Commands represent the main form of interaction that Discord bots have with users. Creating commands is a simple and straightforward process, with the ability to customize different aspects of them.
A slash command is a command that is triggered when the user sends
/command-name in chat. In the library, they are called "chat input commands".
Here is an example of a
/ping command that makes the bot reply with "Pong!":
- A chat input command must have a
@ChatInputCommandannotation that contains the meta-information required by Discord (name of the command, description, defaultPermission, etc), and must implement the
runmethod accepts a
ChatInputInteractionContextthat holds contextual information on the command being executed, such as the original
MessageChannelwhere the interaction happened, the
Userwho initiated the interaction, and a
Localethat may have been adapted to the target user (see Filtering and adapting events).
- Events are automatically acknowledged by default, so you can directly call
reply()will not work unless you disable automatic acknowledgment, see Acknowledging Interactions)
If you are using the Botrino framework, you have nothing else to do, the command will be automatically recognized and registered. Otherwise, you need to manually register it into the
InteractionService like this:
A command may accept one or many options, whether they are required or optional. The library provides
ChatInputCommandGrammar that allows to inject the option values into the fields of a class that is going to be instantiated when the command is executed. Here is an example of a command using options:
- Create a new class that declares the fields in which you want to inject the option values. It is recommended to use an internal class for better code readability, unless you are re-using the same class for several commands. The class must have a no-arg contructor, and must be declared
staticif internal. The fields may be
private, but you can also omit the access modifier to avoid IDE warnings about the field never being assigned a value.
- Use the annotation
@ChatInputCommandGrammar.Optionon the field to declare the properties of the option (the type, the name, the description, whether they are required or not, and the array of value choices, if any).
- Create a new
ChatInputCommandGrammarand pass the class to the
.of()method. You only need to instantiate once, rather than on each command execution.
- In the
run(ChatInputInteractionContext)method, call the
resolve(ChatInputInteractionEvent)method which will read the options, instantiate the class and inject the values in the annotated fields. You can then use the object to conveniently access the values, as show in the example above.
- Override the
ChatInputInteractionListenerand make it return
For reference, here is a table associating each
ApplicationCommandOption.Type with the type of the field carrying the annotation:
|Type of annotated field
long may be used only if
required = true)
double may be used only if
required = true)
boolean may be used only if
required = true)
discord4j.core.object.entity.Member if in a guild)
Optional options will be filled with
null if not specified by the user, which means you cannot use primitive types for
required = false, otherwise you will get
Discord allows to create subcommands and subcommand groups to help in organizing the logic of a complex command. Here is an example of a command using subcommands and subcommand groups:
Here are the notable differences:
- The class carrying the
@ChatInputCommandannotation no longer implements
ChatInputInteractionListener. Indeed, as per Discord's documentation a base command becomes unusable if subcommands are present.
@ChatInputCommandspecifies an array of
@SubcommandGroupwith their own name and description.
- Subcommands specify the class implementing
ChatInputInteractionListenerthat is going to handle them. In this example they are internal classes, but they can as well be external.
Here is how you manually register a command containing subcommands when you control the instance of
If you are using the Botrino framework, the subcommand classes must either have a public no-arg constructor or be declared as a service. If the classes are internal, they must be
Discord currently support two types of context menu commands, one on messages and one on users. It works the same as chat input commands, but you need to use the
@UserCommand annotations with the
UserInteractionListener interfaces, respectively.
Context menu commands are actually less complex than chat input ones, since there is no description, no options, no subcommands... Only a name and a run method:
If you need to do manual registration, it happens via
The following is only applicable if you are using the Botrino framework. See Working with services.
Classes implementing commands can themselves be declared as services without any issues. For example if you need to access the
ConfigContainer in your command, you can do this:
The command above accesses the values in the
config.json to get the gateway intents enabled for the bot. You can notice the use of
@RdiService on top of
@ChatInputCommand, this works totally fine! Don't forget the
@RdiFactory to inject the configuration container, and you're ready to run the bot and try out this command.
If you declare a command as a service this way, you are allowed to do anything with it like any other service, for example inject it in other services, or set up
@RdiFactory to be a reactive static method in case the command needs to perform a reactive task in order to be initialized.