-
-
Notifications
You must be signed in to change notification settings - Fork 24
SlashCommands in JDA Chewtils
JDA-Chewtils 1.20 added support for Discord's inbuilt Slash commands, adding an easy-to-use framework for the otherwise complicated command system.
Please note that this Guide is a deeper breakdown of Slash commands. For a simple migration Guide from JDA-Utilities Command-System to this one, check out the Migration page.
Before getting started, if you plan to make server-specific slash commands, be sure to add the applications.commands
scope when inviting it.
To get started, should you create a new class for your command. In our example, we will create a /ping
command and create a PingCommand
class for it.
Once created, you will need to extend
the SlashCommand
class from JDA-Chewtils and add the execute
void.
If you've done everything correctly, should your class look something like this now:
public class PingCommand extends SlashCommand {
@Override
public void execute(SlashCommandEvent event) {
}
}
You now have the class set up, but we haven't set a command name, description, or other information yet.
You will need to set up a constructor and set the different values using this.x
where x
is the option you want to override.
Example of setting a name and description:
public class PingCommand extends SlashCommand {
public PingCommand() {
this.name = "ping"; // This has to be lowercase
this.help = "Performs a ping to see the bot's delay"; // Describe the command here
}
@Override
public void execute(SlashCommandEvent event) {
}
}
The above would already be enough to count as a valid command, but many more options are available, which are listed below.
Option | Type | Description | Default |
---|---|---|---|
children |
SlashCommand[] |
Array of SlashCommand instances count as children/subcommands of the main one. Sub-commands may not have their own children set. |
new SlashCommand[0] |
options |
List<OptionData> |
List of OptionData to use for the commands. For a normal command would this be /command <optiondata> and for subcommands /command <subcommand> <optiondata>
|
new ArrayList<>() |
subcommandGroup |
SubcommandGroupData |
Subcommand/child group the command is associated with. This only works in a child/subcommand. | null |
Keep in mind that the above list only contains the protected fields of the SlashCommand
class. The class extends Command
, meaning you can also set and use values from the Command
class. There is no guarantee that the class will support all options.
Slash commands are a bit special in that they need to be "acknowledged" by the bot within 3 seconds after receiving it. Otherwise, the command will be treated as "no response" by Discord and result in an "bot did not reply" in the client.
To acknowledge a Slash Command, there are various options from JDA that you can use.
If you, for example, just want to give a simple response to a command you can use reply(String)
.
Example:
public class PingCommand extends SlashCommand {
public PingCommand() {
this.name = "ping"; // This has to be lowercase
this.help = "Performs a ping to see the bot's delay";
}
@Override
public void execute(SlashCommandEvent event) {
event.reply("Pong!").queue();
}
}
If you, for whatever reason, need to delay your response, but don't want the command to fail, you can use one of the deferX
methods to "pretend" sending a response.
Example:
public class PingCommand extends SlashCommand {
public PingCommand() {
this.name = "ping"; // This has to be lowercase
this.help = "Performs a ping to see the bot's delay";
}
@Override
public void execute(SlashCommandEvent event) {
// Sends a "<bot> is thinking..." response and allows you a delayed response.
event.deferReply().queue(
hook -> hook.editOriginal("Pong!").queueAfter(5, TimeUnit.SECONDS)
);
}
}
Now that your command is properly set up you need to tell JDA-Chewtils to load and register it in Discord.
To do this, you will need to create an instance of the CommandClientBuilder
, add then Commands using addSlashCommand
or addSlashCommands
, and finally build it and register it as an event listener in JDA to work.
Example:
CommandClientBuilder builder = new CommandClientBuilder();
// Load the Slash command
builder.addSlashCommand(new PingCommand());
// Build the CommandClient instance
CommandClient commandClient = builder.build();
// Add it as an event listener to JDA
JDA jda = JDABuilder.createDefault("my.secret.token")
.addEventListeners(
commandClient,
// Any other events you have
).build();
By default, Slash commands are global, which can take up to an hour for them to be loaded and/or updated for all clients.
If you only use the bot and its commands in a single server, or would like to test your bot, you should consider using the forceGuildOnly
method to set the command as server-specific. Server-specific commands are loaded and updated nearly instantly, but only for that server.
Developing slash commands is a lot different from testing normal commands. Because they need to be upserted, it's highly suggested to utilize the forceGuildOnly(String guildId)
method in the CommandClientBuilder
.
In development, you should always use the forceGuildOnly(String guildId)
method in CommandClientBuilder
. Upserting commands to a specific server will update the commands cache instantly, instead of taking up to an hour on global commands.
Example:
CommandClientBuilder builder = new CommandClientBuilder();
// ...
builder.forceGuildOnly("134445052805120001");
TIP: Also keep this on for server-specific bots in production so updates can take immediately.
Here are some more advanced explanations regarding Slash Commands.
You may want to allow users to put their own values in a command to execute, like the text in a say command.
For that does JDA-Chewtils offer the options
field that can be changed through the Constructor.
options
is a List
of OptionData
instances, so you have to provide an ArrayList of those.
Example:
public class SayCommand extends SlashCommand {
public SayCommand() {
this.name = "say";
this.help = "Makes the bot say something";
List<OptionData> options = new ArrayList<>();
options.add(new OptionData(OptionType.STRING, "text", "The text to say.").setRequired(true));
this.options = options;
}
@Override
public void execute(SlashCommandEvent event) {
}
}
The above example would work fine, but making a new ArrayList for a single entry is a bit of overkill here.
Fortunately, Java does offer a Collections.singletonList(T)
method which allows having a list with a single entry, making the above code look like this now:
public class SayCommand extends SlashCommand {
public SayCommand() {
this.name = "say";
this.help = "Makes the bot say something";
this.options = Collections.singletonList(new OptionData(OptionType.STRING, "text", "The text to say.").setRequired(true));
}
@Override
public void execute(SlashCommandEvent event) {
}
}
Now comes the question: how do we use the provided value?
This is simple. JDA offers a getOption(String)
method to get an OptionMapping
instance with the provided key name.
The returned OptionMapping
instance can then, after doing some null checks, be converted into whatever value was previously set to be allowed.
Example:
public class SayCommand extends SlashCommand {
public SayCommand() {
this.name = "say";
this.help = "Makes the bot say something";
this.options = Collections.singeltonList(new OptionData(OptionType.STRING, "text", "The text to say.").setRequired(true))
}
@Override
public void execute(SlashCommandEvent event) {
OptionMapping option = event.getOption("text"); // string must match the name we set above for the option.
// getOption is nullable!
if (option == null) {
// Send a response only the command executor can see.
event.reply("Could not execute say command. Text option was null!").setEphemeral(true).queue();
return;
}
event.reply(option.getAsString()).queue();
}
}
Sometimes you may want to simplify things, or have some default value used when the option couldn't be found.
For that does JDA-Chewtils offer the OptionHelper
class. This class has many useful methods to get a specific option as a particular value (i.e. String) or get a default returned when it couldn't be found.
Using the OptionHelper
class would turn the above example into this:
public class SayCommand extends SlashCommand {
public SayCommand() {
this.name = "say";
this.help = "Makes the bot say something";
this.options = Collections.singeltonList(new OptionData(OptionType.STRING, "text", "The text to say.").setRequired(true))
}
@Override
public void execute(SlashCommandEvent event) {
// If you're using JDA-Chewtils 1.x
String message = OptionHelper.optString(event, "text"); // Would return null if the option was not found.
// If you're using JDA-Chewtils 2.x
String message = event.optString("text"); // Would return null if the option was not found.
// message may be null.
if (message == null) {
// Send a response only the command executor can see.
event.reply("Could not execute say command. Text option was null!").setEphemeral(true).queue();
return;
}
event.reply(message).queue();
}
}
With the above setup would the bot now respond with whatever text you provided.
For example, would /say I am a bot
result in the bot saying I am a bot
Sometimes you may want to have a sub-command. For example an add
subcommand for your /tag
command to add new tags.
In this case, you can use the children
option of SlashCommand
. This option allows you to set an array of classes, which extend the SlashCommand
class, to be added and treated as sub-commands.
- Once a child is added, the
execute
void of the main command will no longer be executable. - Classes that are the child of a command cannot use the
children
option. - If you have children, you cannot use
this.options
in the main command, and vice versa.
The below example creates a /tag
command with an add
subcommand:
public class TagCommand extends SlashCommand {
public TagCommand() {
this.name = "tag";
this.help = "Let's you set new tags";
this.children = new SlashCommand[]{new Add()};
}
// This void won't be executed anymore.
@Override
public void execute(SlashCommandEvent event) {
}
// It's recommended to keep subcommands as nested classes.
private static class Add extends SlashCommand {
public Add() {
this.name = "add"; // Will be used as the sub command.
this.help = "Adds a new tag";
// Creating options name and text for /tag add <name> <text>
List<OptionData> options = new ArrayList<>();
options.add(new OptionData(DataType.STRING, "name", "The name of the new tag").setRequired(true));
options.add(new OptionData(DataType.STRING, "text", "The text of the new tag").setRequired(true));
this.options = options;
}
@Override
public void execute(SlashCommandEvent event) {
// Handle the command as described before.
}
}
This simple setup would now result in /tag add <name> <text>
being created.
- SlashCommands in JDA-Chewtils
- Context Menus in JDA-Chewtils (Chewtils 2.0+)
- User-Installed Apps (Chewtils 2.1-SNAPSHOT)