Creating commands
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.
#
Chat input (aka "slash") commands#
Basic commandA 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
@ChatInputCommand
annotation that contains the meta-information required by Discord (name of the command, description, defaultPermission, etc), and must implement theChatInputInteractionListener
interface. - The
run
method accepts aChatInputInteractionContext
that holds contextual information on the command being executed, such as the originalChatInputInteractionEvent
, theMessageChannel
where the interaction happened, theUser
who initiated the interaction, and aLocale
that may have been adapted to the target user (see Filtering and adapting events). - Events are automatically acknowledged by default, so you can directly call
createFollowup()
without usingdeferReply()
first (reply()
will not work unless you disable automatic acknowledgment, see Acknowledging Interactions)
info
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:
#
Command optionsA 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
static
if internal. The fields may beprivate
, but you can also omit the access modifier to avoid IDE warnings about the field never being assigned a value. - Use the annotation
@ChatInputCommandGrammar.Option
on 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
ChatInputCommandGrammar
and 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 theresolve(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
options()
method fromChatInputInteractionListener
and make it returnChatInputCommandGrammar#toOptions()
.
For reference, here is a table associating each ApplicationCommandOption.Type
with the type of the field carrying the annotation:
Option type | Type of annotated field |
---|---|
STRING | java.lang.String |
INTEGER | java.lang.Long (primitive long may be used only if required = true ) |
NUMBER | java.lang.Double (primitive double may be used only if required = true ) |
BOOLEAN | java.lang.Boolean (primitive boolean may be used only if required = true ) |
USER | discord4j.core.object.entity.User (or discord4j.core.object.entity.Member if in a guild) |
CHANNEL | discord4j.core.object.entity.channel.Channel |
ROLE | discord4j.core.object.entity.Role |
MENTIONABLE | discord4j.common.util.Snowflake |
caution
Optional options will be filled with null
if not specified by the user, which means you cannot use primitive types for INTEGER
, NUMBER
and BOOLEAN
if required = false
, otherwise you will get NullPointerException
s.
#
Subcommands and subcommand groupsDiscord 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
@ChatInputCommand
annotation no longer implementsChatInputInteractionListener
. Indeed, as per Discord's documentation a base command becomes unusable if subcommands are present. - The
@ChatInputCommand
specifies an array of@Subcommand
and@SubcommandGroup
with their own name and description. - Subcommands specify the class implementing
ChatInputInteractionListener
that is going to handle them. In this example they are internal classes, but they can as well be external.
info
Here is how you manually register a command containing subcommands when you control the instance of InteractionService
:
caution
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 static
.
#
Context menu commandsDiscord 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 @MessageCommand
and @UserCommand
annotations with the MessageInteractionListener
and 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:
info
If you need to do manual registration, it happens via InteractionService#registerMessageCommand(MessageInteractionListener)
and InteractionService#registerUserCommand(MessageInteractionListener)
:
#
Commands as a serviceinfo
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.
tip
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.