-
-
Notifications
You must be signed in to change notification settings - Fork 24
Command to SlashCommand Migration
With the introduction of SlashCommands, it's all the new hype and everyone wants to do them. How do you convert your existing bot over?
Because SlashCommand extends Command, you are still able to support both kinds of replies. You can do this by having two methods in your command class:
protected void execute(CommandEvent event) {}
protected void execute(SlashCommandEvent event) {}
Depending on how it was executed, it will run one or the other.
If you want to support both, it's recommended to do all of your logic in a different method, and just return a valid response and only do replying in your execute methods.
There are a few things to keep in mind regarding the SlashCommand class, though.
A few methods are unsupported and have no effect as a SlashCommand. These being:
-
this.aliases
andgetAliases()
- Slash Commands don't support aliases. -
this.arguments
andgetArguments()
- These don't have any effect, and you will need to use this.options to properly handle arguments. -
event.getArgs()
- This command doesn't exist in SlashCommandEvent, so you will not be able to use it. You MUST use Options.
A few methods had their functionality change, as well:
-
this.guildOnly
- This now specifies whether or not to mark this as a Global command instead of a Server command. -
this.guildId
- Has been added as a helper forthis.guildOnly
. This accepts a String and is used for specifying the Server to add this command to. In order for this to matter,guildOnly
MUST betrue
.
A sample migration may end up looking like this: https://github.com/Chew/ChanServ/commit/00314934bd4cbf8e045672f731b1be5bfe8296a0
The first thing you will need to is to update your JDA-Chewtils to 1.20 or later, then you need to make your Command class extend com.jagrosh.jdautilities.command.SlashCommand
, not com.jagrosh.jdautilities.command.Command
.
If you're wanting to scrap normal responses entirely in favor of SlashCommand, do the following steps below.
- Change the
protected void execute(CommandEvent commandEvent) {
to sayprotected void execute(SlashCommandEvent commandEvent) {
- Append
.queue();
to anyevent.reply()
s you do. - Remove any of the
this.x
methods mentioned above. - If you don't already have a
this.help = "";
, you should add one since it shows up in the client to users. They would otherwise get "no help available."
If you have no errors, great! You are done with the initial command class conversion, but you may run into even more issues.
Because getArgs() is gone, you will need to convert any arguments you plan on handing over to the new this.options
and OptionData included in JDA.
For example, if you accept one argument, such as an input for an 8ball command, you would have something like this:
public class EightBallCommand extends Command {
public EightBallCommand() {
this.name = "8ball";
this.help = "Ask the 8 ball a question!";
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
this.guildOnly = false;
}
@Override
protected void execute(CommandEvent commandEvent) {
// Get the question, doesn't matter what they said but we'll send it back to them
String question = commandEvent.getArgs();
// Pick a number between 0 and 2 inclusive
int response = rand.nextInt(3);
EmbedBuilder e = new EmbedBuilder();
e.setTitle(":question: Question");
e.setDescription(question);
String answer = null;
// Finish and send embed
e.addField(":8ball: 8ball says", "My Response Here", false);
commandEvent.reply(e.build());
}
}
(Full code found here: https://github.com/Chewbotcca/Discord/blob/a9e585a672224306d7e7812ea433fc4990e7a8f8/src/main/java/pw/chew/chewbotcca/commands/fun/EightBallCommand.java)
As you can tell, we use commandEvent.getArgs()
to get the argument. This won't work anymore, instead, we will need to instantiate an OptionData object and set the details there.
OptionData data = new OptionData(OptionType.STRING, "input", "the input to ask the eight ball")
.setRequired(true);
This adds a new input called "input" and with the description that will show below the input. Additionally, it makes it required, not to let the user use the command without providing input.
To add this, we would put it in the constructor:
...
public EightBallCommand() {
this.name = "8ball";
this.help = "Ask the 8 ball a question!";
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
this.guildOnly = false;
this.options = Collections.singletonList(
new OptionData(OptionType.STRING, "input", "the input to ask the eight ball")
.setRequired(true)
);
}
...
Then, later on in our code, we can grab the input.
String question = commandEvent.getArgs();
becomes:
String question = commandEvent.getOption("input").getAsString();
Then the rest of the command function would work fine.
Our final code should look like this:
public class EightBallCommand extends SlashCommand {
public EightBallCommand() {
this.name = "8ball";
this.help = "Ask the 8 ball a question!";
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
this.guildOnly = false;
this.options = Collections.singletonList(
new OptionData(OptionType.STRING, "input", "the input to ask the eight ball")
.setRequired(true)
);
}
@Override
protected void execute(CommandEvent commandEvent) {
// Get the question, doesn't matter what they said but we'll send it back to them
String question = commandEvent.getOption("input").getAsString();
// Pick a number between 0 and 2 inclusive
int response = rand.nextInt(3);
EmbedBuilder e = new EmbedBuilder();
e.setTitle(":question: Question");
e.setDescription(question);
String answer = null;
// Finish and send embed
e.addField(":8ball: 8ball says", "My Response Here", false);
commandEvent.reply(e.build()).queue();
}
}
You will need to add your Commands to a new method if you want them to be treated as Slash Commands.
Instead of doing something like this:
client.addCommand(new EightBallCommand());
We need to add it using the new addSlashCommand function:
client.addSlashCommand(new EightBallCommand());
This will upsert the command on boot.
If you end up doing both CommandEvent and SlashCommandEvent, you must add your command to both of these.
Because the raw event from JDA is no longer wrapped, you lose all of the methods provided by the CommandEvent class. See the table below for conversion
CommandEvent Method | Alternative |
---|---|
getArgs() | Deprecated as shown above. See above for alternatives. |
getEvent() | No longer needed because this would've returned the raw event, just use the parameter |
getClient() | This is now included in the command itself, so remove the event. part of the call. |
linkId() | No alternatives yet. |
Any reply() | You should be able to just append .queue() to resolve these issues. |
async() | No alternatives yet. |
getSelfUser() | Replace with event.getJDA().getSelfUser();
|
getSelfMember() | Replace with event.getGuild() == null ? null : event.getGuild().getSelfMember();
|
isOwner() | You will need to implement this manually with getClient() |
All of the other methods call event.method() anyway, which should work because of how the event works, except for getMessage().