Replying to slash commands
Discord provides developers the option to create client-integrated slash commands. In this section, we'll cover how to respond to these commands using discord.js!
TIP
You need at least one slash command registered on your application to continue with the instructions on this page. If you haven't done that yet, refer to the previous page.
Receiving interactions
Every slash command is an interaction
, so to respond to a command, you need to set up an event listener that will execute code when your application receives an interaction:
client.on('interactionCreate', interaction => {
console.log(interaction);
});
2
3
However, not every interaction is a slash command (e.g. MessageComponent
s). Make sure to only receive slash commands by making use of the CommandInteraction#isCommand()
method:
client.on('interactionCreate', interaction => {
if (!interaction.isCommand()) return;
console.log(interaction);
});
2
3
4
Responding to a command
There are multiple ways of responding to a slash command, each of these are covered in the following segments. The most common way of sending a response is by using the CommandInteraction#reply()
method:
WARNING
Initially an interaction token is only valid for three seconds, so that's the timeframe in which you are able to use the CommandInteraction#reply()
method. Responses that require more time ("Deferred Responses") are explained later in this page.
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
}
});
2
3
4
5
6
7
Restart your bot and then send the command to a channel your bot has access to. If all goes well, you should see something like this:
You've successfully sent a response to a slash command! This is only the beginning, there's more to look out for so let's move on to further ways of replying to a command!
Ephemeral responses
You may not always want everyone who has access to the channel to see a slash command's response. Thankfully, Discord implemented a way to hide messages from everyone but the executor of the slash command. This type of message is called ephemeral
and can be set by using ephemeral: true
in the InteractionReplyOptions
, as follows:
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply({ content: 'Pong!', ephemeral: true });
}
});
2
3
4
5
6
7
Now when you run your command again, you should see something like this:
Editing responses
After you've sent an initial response, you may want to edit that response for various reasons. This can be achieved with the CommandInteraction#editReply()
method:
WARNING
After the initial response, an interaction token is valid for 15 minutes, so this is the timeframe in which you can edit the response and send follow-up messages.
const wait = require('util').promisify(setTimeout);
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
await wait(2000);
await interaction.editReply('Pong again!');
}
});
2
3
4
5
6
7
8
9
10
11
Deferred responses
As previously mentioned, you have three seconds to respond to an interaction before its token becomes invalid. But what if you have a command that performs a task which takes longer than three seconds before being able to reply?
In this case, you can make use of the CommandInteraction#deferReply()
method, which triggers the <application> is thinking...
message and also acts as initial response. This allows you 15 minutes to complete your tasks before responding.
const wait = require('util').promisify(setTimeout);
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.deferReply();
await wait(4000);
await interaction.editReply('Pong!');
}
});
2
3
4
5
6
7
8
9
10
11
If you have a command that performs longer tasks, be sure to call deferReply()
as early as possible.
You can also pass an ephemeral
flag to the InteractionDeferOptions
:
await interaction.deferReply({ ephemeral: true });
Follow-ups
Replying to slash commands is great and all, but what if you want to send multiple responses instead of just one? Follow-up messages got you covered, you can use CommandInteraction#followUp()
to send multiple responses:
WARNING
After the initial response, an interaction token is valid for 15 minutes, so this is the timeframe in which you can edit the response and send follow-up messages.
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
await interaction.followUp('Pong again!');
}
});
2
3
4
5
6
7
8
If you run this code you should end up having something that looks like this:
You can also pass an ephemeral
flag to the InteractionReplyOptions
:
await interaction.followUp({ content: 'Pong again!', ephemeral: true });
That's all, now you know everything there is to know on how to reply to slash commands!
TIP
Interaction responses can use masked links (e.g. [text](http://site.com)
) and global emojis in the message content.
Parsing options
Command options
In this section, we'll cover how to access the values of a command's options. Let's assume you have a command that contains the following options:
const { SlashCommandBuilder } = require('@discordjs/builders');
const data = new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!')
.addStringOption(option => option.setName('input').setDescription('Enter a string'))
.addIntegerOption(option => option.setName('int').setDescription('Enter an integer'))
.addNumberOption(option => option.setName('num').setDescription('Enter a number'))
.addBooleanOption(option => option.setName('choice').setDescription('Select a boolean'))
.addUserOption(option => option.setName('target').setDescription('Select a user'))
.addChannelOption(option => option.setName('destination').setDescription('Select a channel'))
.addRoleOption(option => option.setName('muted').setDescription('Select a role'))
.addMentionableOption(option => option.setName('mentionable').setDescription('Mention something'));
2
3
4
5
6
7
8
9
10
11
12
13
You can get()
these options from the CommandInteractionOptionResolver
as shown below:
const string = interaction.options.getString('input');
const integer = interaction.options.getInteger('int');
const number = interaction.options.getNumber('num');
const boolean = interaction.options.getBoolean('choice');
const user = interaction.options.getUser('target');
const member = interaction.options.getMember('target');
const channel = interaction.options.getChannel('destination');
const role = interaction.options.getRole('muted');
const mentionable = interaction.options.getMentionable('mentionable');
console.log([string, integer, boolean, user, member, channel, role, mentionable]);
2
3
4
5
6
7
8
9
10
11
TIP
If you want the Snowflake of a structure instead, grab the option via get()
and access the Snowflake via the value
property. Note that you should use const { value: name } = ...
here to destructure and renameopen in new window the value obtained from the CommandInteractionOption
open in new window structure to avoid identifier name conflicts.
Subcommands
If you have a command that contains subcommands, you can parse them in a very similar way as to the above examples. Let's say your command looks like this:
const { SlashCommandBuilder } = require('@discordjs/builders');
const data = new SlashCommandBuilder()
.setName('info')
.setDescription('Get info about a user or a server!')
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('Info about a user')
.addUserOption(option => option.setName('target').setDescription('The user')))
.addSubcommand(subcommand =>
subcommand
.setName('server')
.setDescription('Info about the server'));
2
3
4
5
6
7
8
9
10
11
12
13
14
The following snippet details the logic needed to parse the subcommands and respond accordingly using the CommandInteractionOptionResolver#getSubcommand()
method:
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'info') {
if (interaction.options.getSubcommand() === 'user') {
const user = interaction.options.getUser('target');
if (user) {
await interaction.reply(`Username: ${user.username}\nID: ${user.id}`);
} else {
await interaction.reply(`Your username: ${interaction.user.username}\nYour ID: ${interaction.user.id}`);
}
} else if (interaction.options.getSubcommand() === 'server') {
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Fetching and deleting responses
DANGER
You cannot fetch nor delete an ephemeral message.
In addition to replying to a slash command, you may also want to delete the initial reply. You can use CommandInteraction#deleteReply()
for this:
await interaction.reply('Pong!');
await interaction.deleteReply();
2
Lastly, you may require the Message
object of a reply for various reasons, such as adding reactions. You can use the CommandInteraction#fetchReply()
method to fetch the Message
instance of an initial response:
await interaction.reply('Pong!');
const message = await interaction.fetchReply();
console.log(message);
2
3