Skip to content

06) Command Parameters

BreadMoirai edited this page Jan 5, 2021 · 4 revisions

You can put anything as a Command Parameter.

Flags

Note that you can pass flags to the parser as a hint. For Members and Channels, passing a flag of 1 indicates that it should only map mentions, otherwise will map mentions, ids, and names. You can either set this value programmatically with CommandParameterBuilder#setFlags or with the @Flags annotation.

Primitives

When passing primitives to a Command, their boxed forms should be used or their Optional forms such as Integer, or OptionalInt.

Parameters

The parameters in a method are mapped from the first parameter to the last parameter. If one parameter is mapped from a substring in the input, that substring is considered consumed and unavailable for the next parameters.
Take the following method declaration

@Command("c")
public void myCommand(String quote, String author, Integer age) {...}

Here is some example input.


!c "The path is short" Randy 13  -> quote="The path is short", author="Randy", age=13. 
!c The path is short Randy 13    -> quote="The",               author="path",  age=13. 
!c Randy 13 "The path is short"  -> quote="Randy",             author="13",    age=null. 

Width

Each parameter has a default width of 1 meaning that they will consume exactly 1 token. If the width is set to a positive number, it will consume exactly that many number of tokens otherwise it is set to null. If the width is set to 0, It will try all combination of contiguous tokens until it matches. If the width is set to a negative number, It will only try the largest combination of contiguous tokens.

Index

The index of a parameter is by default 0. This is the default behavior where it will attempt to map the argument at each index. If the index is set, It will attempt to parse that token where the index of the first token is 0. If the index is out of bounds (larger than the number of arguments passed in the event), the parameter will be passed null. If the index is negative, it will count backwards from the last argument where the last argument is -1.

Nullable

If a command requires a parameter in order to execute, you can do CommandParameterBuilder#setRequired or @Required. If the parameter could not be mapped from the arguments passed, the command will not be executed. If you would like to set some action to be run when an argument is missing you can use CommandParameterBuilder#setOnAbsentArgument which takes in a AbsentArgumentHandler which is a consumer that takes in the CommandEvent and the CommandParameter which was not found. There also exists an annotation @HandleAbsentArgument which takes in a class that extends AbsentArgumentHandler.

Example

@Command("ex")
@Name("example")
public String exampleCommand(@Required @Index(-1) Integer lint,
                             @Index(3) String third,                             
                             @Width(0) String start) {
  return "lint=" + lint +
         ", third=" + third +
         ", start=" + start;
         
}

@ConfigureCommand("example")
public static void configureCommand(CommandHandleBuilder command) {
  command.getParameter(0)
    .setOnAbsentArgument( (event, param) -> {
        event.replyFormat("Error: required [%s] but not found", param.getName())});
}
"!ex hello 1"         -> "lint=1, third=null, start=hello"
"!ex hhel lo 3i wo 6" -> "lint=6, third=3i, start=hhel lo"
"!ex is i2noi 2i4 sz" -> "Error: required [arg0] but not found"
//using 'javac -parameters'
"!ex is i2noi 2i4 sz" -> "Error: required [lint] but not found"

When using Gradle, you can use -parameters with the following

tasks.withType(JavaCompile) {
   options.compilerArgs << '-parameters'
}

Collections

Supported Collection Types

  • Stream
  • List
  • Queue
  • Deque

How a collection works is that it's generic parameter is used to map as many arguments as possible. Specifying the contiguous property specifies whether the arguments must be adjacent to one another.

public void test(List<Integer> ints, List<String> strings) {...}

"a b 3 d e 5" -> ints={3, 5}, strings={"a", "b", "d", "e"}
//where ints is contiguous
              -> ints={3}, strings={"a", "b", "d", "e", "5"}
//where strings is contiguous
              -> ints={3, 5}, strings={"a", "b"}

Custom Parameters

Here is an example.

BreadBotClient bread = new BreadBotClientBuilder()
  .registerParameterTypeFlagless(
     MathCommand.Operator.class, //type
     null, //optional predicate to test for type
     arg -> { //mapper
       switch (arg.getArgument()) {
         case "+":
           return new MathCommand.AddOperator();
         case "-":
           return new MathCommand.SubtractOperator();
         case "/":
           return new MathCommand.DivideOperator();
         case "*":
           return new MathCommand.MultiplyOperator();
         default:
            throw new RuntimeException();
       }
     })
  .addCommand(MathCommand.class)
  .build();
...
public class MathCommand {

  @MainCommand
  @Description("This command can only use the 4 basic operators" + 
               "and evaluates expressions left to right" +
               "disregarding any order of operations")
  public String math(CommandEvent event, 
                     Deque<Double> operands,
                     Deque<Operator> operators) {
    if (operands.size() != operators.size() + 1) {
      return "invalid";
    } else {
      while (!operators.isEmpty()) {
        Double op1 = operands.pop();
        Double op2 = operands.pop();
        Operator o = operators.pop();
        operands.push(o.calculate(op1, op2));
      }
    }
    return String.valueOf(operands.pop());
  }

  public static abstract class Operator {
    public abstract double calculate(double op1, double op2);
  }

  public static class AddOperator extends Operator {

    @Override
    public double calculate(double op1, double op2) {
      return op1 + op2;
    }
  }

  public static class SubtractOperator extends Operator {

    @Override
    public double calculate(double op1, double op2) {
      return op1 - op2;
    }
  }

  public static class MultiplyOperator extends Operator {

    @Override
    public double calculate(double op1, double op2) {
      return op1 * op2;
    }
  }

  public static class DivideOperator extends Operator {

    @Override
    public double calculate(double op1, double op2) {
      return op1 / op2;
    }
  }
}