Slash commands
Registering slash commands
Discord provides developers with the option to create client-integrated slash commands. In this section, we'll cover how to register these commands using discord.js!
TIP
This page assumes you use the same file structure as our command handling section. The scripts provided are made to function with that setup.
If you already have slash commands set up for your application and want to learn how to respond to them, refer to the following section.
Guild commands
Guild application commands are only available in the guild they were created in, if your application has the applications.commands
scope authorized.
In this section, we'll be using a script that is usable in conjunction with the slash command handler from the command handling section.
First off, install the @discordjs/rest
open in new window and discord-api-types
open in new window by running the following command in your terminal:
npm install @discordjs/rest discord-api-types
yarn add @discordjs/rest discord-api-types
pnpm add @discordjs/rest discord-api-types
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const { token } = require('./config.json');
const fs = require('node:fs');
const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
// Place your client and guild ids here
const clientId = '123456789012345678';
const guildId = '876543210987654321';
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
const rest = new REST({ version: '9' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Running this script will register all your commands to the guild of which the id was passed in above.
Global commands
Global application commands will be available in all the guilds your application has the applications.commands
scope authorized, as well as in DMs.
TIP
Global commands are cached for one hour. New global commands will fan out slowly across all guilds and will only be guaranteed to be updated after an hour. Guild commands update instantly. As such, we recommend you use guild-based commands during development and publish them to global commands when they're ready for public use.
To deploy global commands, you can use the same script from the guild commands section and adjust the route in the script to .applicationCommands(clientId)
.
await rest.put(
Routes.applicationCommands(clientId),
{ body: commands },
);
2
3
4
Options
Application commands can have options
. Think of these options as arguments to a function. You can specify them as shown below:
const { SlashCommandBuilder } = require('@discordjs/builders');
const data = new SlashCommandBuilder()
.setName('echo')
.setDescription('Replies with your input!')
.addStringOption(option =>
option.setName('input')
.setDescription('The input to echo back')
.setRequired(true));
2
3
4
5
6
7
8
9
Notice how .setRequired(true)
is specified within the options builder. Setting this will prevent the user from sending the command without specifying a value for this option!
Option types
As shown in the options example above, you can specify the type
of an ApplicationCommandOption
. Listed below are all the possible values you can pass as ApplicationCommandOptionType
:
TIP
The slash command builder has a method for each of these types respectively. Refer to the Discord API documentation for detailed explanations on the SUB_COMMAND
and SUB_COMMAND_GROUP
option typesopen in new window.
SUB_COMMAND
sets the option to be a subcommandSUB_COMMAND_GROUP
sets the option to be a subcommand groupSTRING
sets the option to require a string valueINTEGER
sets the option to require an integer valueNUMBER
sets the option to require a decimal (also known as a floating point) valueBOOLEAN
sets the option to require a boolean valueUSER
sets the option to require a user or snowflake as valueCHANNEL
sets the option to require a channel or snowflake as valueROLE
sets the option to require a role or snowflake as valueMENTIONABLE
sets the option to require a user, role or snowflake as value
Choices
The STRING
and INTEGER
option types both can have choices
. choices
are a set of predetermined values users can pick from when selecting the option that contains them.
WARNING
If you specify choices
for an option, they'll be the only valid values users can pick!
Specify them by using the addChoice()
method from the slash command builder:
const { SlashCommandBuilder } = require('@discordjs/builders');
const data = new SlashCommandBuilder()
.setName('gif')
.setDescription('Sends a random gif!')
.addStringOption(option =>
option.setName('category')
.setDescription('The gif category')
.setRequired(true)
.addChoice('Funny', 'gif_funny')
.addChoice('Meme', 'gif_meme')
.addChoice('Movie', 'gif_movie'));
2
3
4
5
6
7
8
9
10
11
12
Subcommands
Subcommands are available with the .addSubcommand()
method:
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
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 section.
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('node:timers/promises').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('node:timers/promises').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. 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 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