Registering Chat Input Commands
To register a Chat Input Command (also known as a Slash Command) with Discord, you need to acquire an application
command registry. If you missed how to do it, please see the Acquiring an Application Command Registry
section for more details.
How to register a Chat Input Command
To register a Chat Input Command, implement the registerApplicationCommands
method for the Command
class. The method's parameter will be the command's pre-defined ApplicationCommandRegistry
, on which you can
call registerChatInputCommand
to register your application command.
registerChatInputCommand
This method is responsible for describing how a Chat Input Command will be registered. It takes two parameters:
-
command
: This is the Chat Input Command data that the registry will register with Discord. This can be a@discordjs/builders
SlashCommandBuilder
↗️, a method that returns aSlashCommandBuilder
↗️, or adiscord.js
ApplicationCommandData
↗️ object. -
options
: These are optional options that change how the Chat Input Command will be registered. These are discussed in the Chat Input Command Registry Options section below.
An example looks like the following:
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Send a Slash Command.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
);
}
chatInputRun(interaction) {
// ...
}
}
module.exports = {
SlashCommand
};
import { Command } from '@sapphire/framework';
export class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Send a Slash Command.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
);
}
chatInputRun(interaction) {
// ...
}
}
import { Command } from '@sapphire/framework';
export class SlashCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
super(context, {
...options,
description: 'Send a Slash Command.'
});
}
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
);
}
public override chatInputRun(interaction: Command.ChatInputInteraction) {
// ...
}
}
We're using //
next to builder //
to prevent Prettier from formatting everything on the same line. If you do not use
Prettier, or don't care, you can remove this.
In the example above, we reused the class' name
and description
fields for brevity. However, you may set those to
something else and the application command registry will still route the application command to the command.
We recommend most people use the SlashCommandBuilder
↗️ as the argument for the
registerChatInputCommand
method, since it will ensure the data sent to Discord is properly structured. If you
need more control over the application command, you can write the discord.js
ApplicationCommandData
↗️ JSON object yourself as the argument instead.
Registering Chat Input Commands with options
Chat Input Commands can take in options, which are similar to arguments for a function. These are similar to the Arguments that Sapphire offers for Message Commands, but they are built right into Discord instead!
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
chatInputRun(interaction) {
// ...
}
}
module.exports = {
SlashCommand
};
import { Command } from '@sapphire/framework';
export class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
chatInputRun(interaction) {
// ...
}
}
import { Command } from '@sapphire/framework';
export class SlashCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
public override chatInputRun(interaction: Command.ChatInputInteraction) {
// ...
}
}
In this example, we will add an option that will ask for another user as input. The option can be configured with
setRequired(true)
, which means Discord natively prevents the chat input command from ever being run if it isn't
provided. This is really nice compared to using Sapphire's Arguments for input validation in message
commands.
There are many other types of options that you can add to your Chat Input Command, and you can read all about them on
the discord.js guide
↗️.
Chat Input Command Registry Options
These are options that we can pass into the registerChatInputCommand
method as a second argument, not to be
confused with a Chat Input Command's options in the previous section. Let's take a look at them below!
idHints
At a high level, an application command registry maps application command IDs and/or names to your command. By providing
ID(s) to the idHints
option, we can prevent new commands from accidentally being created.
As mentioned in What is it?
, Discord manages "Application Command Objects" on their end for each of your
application commands. Whenever your bot registers a new application command with Discord, they create and store a new
object like this:
{
"id": "1223334444555554444",
"application_id": "...",
// ...
"name": "foo",
"description": "bar!"
}
Let's say you want to change the application command's name (what users see in Discord). If you don't provide the ID
above to idHints
, then the application command registry will register an entirely new command with the new name, and
the old one will still be there! You can delete an application command through discord.js
or by interacting with
Discord's API directly ↗️, but it's better to prevent this in the first place!
The first time an application command is registered, Sapphire will log this ID at the info
level to the
console. We highly recommend you copy this ID and add it to idHints
option array. If you miss it the first time,
Sapphire will continue to log this ID at the debug
level. If you have configured Sapphire's Logger to only
log errors or fatal messages, please check Configuring Log Level Guide
to learn how to change the
log level visibility.
guildIds
Discord allows you to register application commands either globally or for specific guilds. Providing guild ids to
guildIds
will only register the application command to those guilds.
By default, this is undefined
which registers the application command globally. However, an empty array acts the same
way which may be useful if you're fetching guild ids from another source (i.e. an external API or database).
behaviorWhenNotIdentical
This option defines the registry's behavior when the application command's options in your code are not synced with
Discord, such as a new description. It's set to OVERWRITE
by default, which will always overwrite
Discord's corresponding Application Command Object with the data in your code. If you only want the registry to log
differences instead, you should set this option to LOG_TO_CONSOLE
.
registerCommandIfMissing
This is an option that defaults to true
. If set to false
, the command will not be registered if it is not already
registered in the Discord API.
You will likely never have to touch this option, but it can be useful if you are not using Sapphire to register your commands and instead you're using something external to do so.
Implementing a Chat Input Command
Now that we've covered registering a Chat Input Command with Discord, the last step is to implement the chatInputRun
method for the Command
class so your bot can respond!
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
const { userMention } = require('discord.js');
class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
chatInputRun(interaction) {
const userToGreet = interaction.options.getUser('user', true);
const userToGreetMention = userMention(userToGreet.id);
const interactionUserMention = userMention(interaction.user.id);
return interaction.reply({
content: `Hey ${userToGreetMention}, ${interactionUserMention} says hello!`,
allowedMentions: {
users: [userToGreet.id, interaction.user.id]
}
});
}
}
module.exports = {
SlashCommand
};
import { Command } from '@sapphire/framework';
import { userMention } from 'discord.js';
export class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
chatInputRun(interaction) {
const userToGreet = interaction.options.getUser('user', true);
const userToGreetMention = userMention(userToGreet.id);
const interactionUserMention = userMention(interaction.user.id);
return interaction.reply({
content: `Hey ${userToGreetMention}, ${interactionUserMention} says hello!`,
allowedMentions: {
users: [userToGreet.id, interaction.user.id]
}
});
}
}
import { Command } from '@sapphire/framework';
import { userMention } from 'discord.js';
export class SlashCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
super(context, {
...options,
description: 'Say hello to a user.'
});
}
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
.addUserOption((option) =>
option //
.setName('user')
.setDescription('User to say hello to')
.setRequired(true)
)
);
}
public override chatInputRun(interaction: Command.ChatInputInteraction) {
const userToGreet = interaction.options.getUser('user', true);
const userToGreetMention = userMention(userToGreet.id);
const interactionUserMention = userMention(interaction.user.id);
return interaction.reply({
content: `Hey ${userToGreetMention}, ${interactionUserMention} says hello!`,
allowedMentions: {
users: [userToGreet.id, interaction.user.id]
}
});
}
}
The chatInputRun
example above uses interaction handlers, which are detailed in that section.