Skip to content

Command to SlashCommand Migration

Olivia edited this page May 23, 2021 · 9 revisions

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?

A few methods have been removed in favor of their SlashCommand counterparts. These being:

  • this.aliases and getAliases() - Slash Commands don't support aliases. No alternatives.
  • this.arguments and getArguments() and getArgs() - Removed in favor of Options and OptionData (see migrating arguments below)

Command class conversion

The first thing you will need to is to update your JDA-Chewtils to 1.20 or later.

Next, perform the following changes:

  1. Make your Command class extend com.jagrosh.jdautilities.command.SlashCommand, not com.jagrosh.jdautilities.command.Command.
  2. Change the protected void execute(CommandEvent commandEvent) { to say protected void execute(SlashCommandEvent commandEvent) {
  3. Append .queue(); to any event.reply()s you do.
  4. Remove any of the this.x methods mentioned above.
  5. 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;

        OptionData data = new OptionData(OptionType.STRING, "input", "the input to ask the eight ball")
            .setRequired(true);
        List<OptionData> dataList = new ArrayList<>();
        dataList.add(data);
        this.options = data;
    }
...

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;

        OptionData data = new OptionData(OptionType.STRING, "input", "the input to ask the eight ball")
            .setRequired(true);
        List<OptionData> dataList = new ArrayList<>();
        dataList.add(data);
        this.options = data;
    }

    @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();
    }
}

Telling JDA-Chewtils about our SlashCommands

Because SlashCommands are incompatible with Commands, you will need to add them differently.

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 and, if shut down properly (via JDA#shutdown()), it will be removed when it's shut down.

CommandEvent to SlashCommandEvent

Because the raw event from Discord 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().

If you for whatever reason need to get the message, you need to do: event.getChannel().retrieveMessageById(event.getChannel().getRecentMessageId()).complete();