mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 12:03:31 +01:00
feat(guide): port legacy guide (#10938)
* feat: initial attempt at porting legacy guide * feat: completion of legacy guide backport * chore: lockfile shenanigans * fix: handle svgs * fix: replace svg with mermaid integration * chore: format * chore: remove unnecssary bullet * chore: cleanup code highlights * chore: explicit return * chore: move display components after interactive components in sidebar * chore: voice * top link should be installation * add docs link to sidebar * feat: subguide-based accent styles * chore: don't list faq twice * chore: mention display components in interactive components * fix: remove unoccs/order rule from guide * chore: redirect to legacy guide instead of /guide root * refactor: use `<kbd>` * refactor: more kbd use * Update apps/guide/content/docs/legacy/app-creation/handling-events.mdx Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com> * chore: fix typos Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: fix typos * chore: fix links regarding secret stores across coding platforms * chore: fix typo * chore: link node method directly Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: typos Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * chore: typo Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * fix: prevent v14 changes from being listed twice * chore: prefer relative links * chore: missed link conversion * chore: missed link conversion * chore: fix link * chore: remove legacy code highlight markers * chore: rephrase and extend contributing guidelines * feat(setup): suggest cli flag over dotenv package * chore: move introduction in sidebar better navigation experience if the 'next page' in intro refers to getting started vs. updating/faq * fix: replace outdated link * fix: update voice dependencies * chore: update node install instructions * fix: list in missing access callout * chore: match bun env file format * chore: restore ffmpeg disclaimer * fix: lockfile conflict * chore: action row typo Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * chore: no longer use at-next for pino --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com> Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
---
|
||||
title: Advanced Command Creation
|
||||
---
|
||||
|
||||
The examples we've covered so far have all been fairly simple commands, such as `ping`, `server`, and `user` which all have standard static responses. However, there's much more you can do with the full suite of slash command tools!
|
||||
|
||||
## Adding options
|
||||
|
||||
Application commands can have additional `options`. Think of these options as arguments to a function, and as a way for the user to provide the additional information the command requires.
|
||||
|
||||
<Callout>
|
||||
If you've already added options to your commands and need to know how to receive and parse them, refer to the [Parsing
|
||||
options](./parsing-options) page in this section of the guide.
|
||||
</Callout>
|
||||
|
||||
Options require at minimum a name and description. The same restrictions apply to option names as slash command names - 1-32 characters containing no capital letters, spaces, or symbols other than `-` and `_`. You can specify them as shown in the `echo` command below, which prompt the user to enter a String for the `input` option.
|
||||
|
||||
```js title="commands/utility/echo.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('echo')
|
||||
.setDescription('Replies with your input!')
|
||||
.addStringOption((option) => option.setName('input').setDescription('The input to echo back'));
|
||||
```
|
||||
|
||||
## Option types
|
||||
|
||||
By specifying the `type` of an `ApplicationCommandOption` using the corresponding method you are able to restrict what the user can provide as input, and for some options, leverage the automatic parsing of options into proper objects by Discord.
|
||||
|
||||
The example above uses `addStringOption`, the simplest form of standard text input with no additional validation. By leveraging additional option types, you could change the behavior of this command in many ways, such as using a Channel option to direct the response to a specific channel:
|
||||
|
||||
```js title="commands/utility/echo.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('echo')
|
||||
.setDescription('Replies with your input!')
|
||||
.addStringOption((option) => option.setName('input').setDescription('The input to echo back'))
|
||||
// [!code ++]
|
||||
.addChannelOption((option) => option.setName('channel').setDescription('The channel to echo into'));
|
||||
```
|
||||
|
||||
Or a Boolean option to give the user control over making the response ephemeral so only the command author can see the response.
|
||||
|
||||
```js title="commands/utility/echo.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('echo')
|
||||
.setDescription('Replies with your input!')
|
||||
.addStringOption((option) => option.setName('input').setDescription('The input to echo back'))
|
||||
// [!code ++:3]
|
||||
.addBooleanOption((option) =>
|
||||
option.setName('ephemeral').setDescription('Whether or not the echo should be ephemeral'),
|
||||
);
|
||||
```
|
||||
|
||||
Listed below is a short description of the different types of options that can be added. For more information, refer to the `add_____Option` methods in the `SlashCommandBuilder` documentation.
|
||||
|
||||
- `String`, `Integer`, `Number` and `Boolean` options all accept primitive values of their associated type.
|
||||
- `Integer` only accepts whole numbers.
|
||||
- `Number` accepts both whole numbers and decimals.
|
||||
- `User`, `Channel`, `Role` and `Mentionable` options will show a selection list in the Discord interface for their associated type, or will accept a Snowflake (id) as input.
|
||||
- `Attachment` options prompt the user to make an upload along with the slash command.
|
||||
- `Subcommand` and `SubcommandGroup` options allow you to have branching pathways of subsequent options for your commands - more on that later on this page.
|
||||
|
||||
<Callout>
|
||||
Refer to the Discord API documentation for detailed explanations on the [`SUB_COMMAND` and `SUB_COMMAND_GROUP` option
|
||||
types](https://discord.com/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups).
|
||||
</Callout>
|
||||
|
||||
## Required options
|
||||
|
||||
With option types covered, you can start looking at additional forms of validation to ensure the data your bot receives is both complete and accurate. The simplest addition is making options required, to ensure the command cannot be executed without a required value. This validation can be applied to options of any type.
|
||||
|
||||
Review the `echo` example again and use `setRequired(true)` to mark the `input` option as required.
|
||||
|
||||
```js title="commands/utility/echo.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('echo')
|
||||
.setDescription('Replies with your input!')
|
||||
// [!code --]
|
||||
.addStringOption((option) => option.setName('input').setDescription('The input to echo back')); // [!code --]
|
||||
// [!code word:setRequired] [!code ++]
|
||||
.addStringOption((option) => option.setName('input').setDescription('The input to echo back').setRequired(true));
|
||||
```
|
||||
|
||||
## Choices
|
||||
|
||||
The `String`, `Number`, and `Integer` option types can have `choices`. If you would prefer users select from predetermined values rather than free entry, `choices` can help you enforce this. This is particularly useful when dealing with external datasets, APIs, and similar, where specific input formats are required.
|
||||
|
||||
<Callout type="warn">
|
||||
If you specify `choices` for an option, they'll be the **only** valid values users can pick!
|
||||
</Callout>
|
||||
|
||||
Specify choices by using the `addChoices()` method from within the option builder, such as `SlashCommandBuilder#addStringOption`. Choices require a `name` which is displayed to the user for selection, and a `value` that your bot will receive when that choice is selected, as if the user had typed it into the option manually.
|
||||
|
||||
The `gif` command example below allows users to select from predetermined categories of gifs to send:
|
||||
|
||||
```js title="commands/fun/gif.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('gif')
|
||||
.setDescription('Sends a random gif!')
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('category')
|
||||
.setDescription('The gif category')
|
||||
.setRequired(true)
|
||||
// [!code focus:5]
|
||||
.addChoices(
|
||||
{ name: 'Funny', value: 'gif_funny' },
|
||||
{ name: 'Meme', value: 'gif_meme' },
|
||||
{ name: 'Movie', value: 'gif_movie' },
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
If you have too many choices to display (the maximum is 25), you may prefer to provide dynamic choices based on what the user has typed so far. This can be achieved using [autocomplete](./autocomplete).
|
||||
|
||||
## Further validation
|
||||
|
||||
Even without predetermined choices, additional restrictions can still be applied on otherwise free inputs.
|
||||
|
||||
- For `String` options, `setMaxLength()` and `setMinLength()` can enforce length limitations.
|
||||
- For `Integer` and `Number` options, `setMaxValue()` and `setMinValue()` can enforce range limitations on the value.
|
||||
- For `Channel` options, `addChannelTypes()` can restrict selection to specific channel types, e.g. `ChannelType.GuildText`.
|
||||
|
||||
We'll use these to show you how to enhance your `echo` command from earlier with extra validation to ensure it won't (or at least shouldn't) break when used:
|
||||
|
||||
```js title="commands/utility/echo.js"
|
||||
const { SlashCommandBuilder, ChannelType } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('echo')
|
||||
.setDescription('Replies with your input!')
|
||||
// [!code focus:13]
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('input')
|
||||
.setDescription('The input to echo back')
|
||||
// Ensure the text will fit in an embed description, if the user chooses that option
|
||||
// [!code ++]
|
||||
.setMaxLength(2_000),
|
||||
)
|
||||
.addChannelOption((option) =>
|
||||
option
|
||||
.setName('channel')
|
||||
.setDescription('The channel to echo into')
|
||||
// Ensure the user can only select a TextChannel for output
|
||||
// [!code ++]
|
||||
.addChannelTypes(ChannelType.GuildText),
|
||||
)
|
||||
.addBooleanOption((option) => option.setName('embed').setDescription('Whether or not the echo should be embedded'));
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
Subcommands are available with the `.addSubcommand()` method. This allows you to branch a single command to require different options depending on the subcommand chosen.
|
||||
|
||||
With this approach, you can merge the `user` and `server` information commands from the previous section into a single `info` command with two subcommands. Additionally, the `user` subcommand has a `User` type option for targeting other users, while the `server` subcommand has no need for this, and would just show info for the current guild.
|
||||
|
||||
```js title="commands/utility/info.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('info')
|
||||
.setDescription('Get info about a user or a server!')
|
||||
// [!code word:addSubcommand]
|
||||
.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'));
|
||||
```
|
||||
|
||||
## Localizations
|
||||
|
||||
The names and descriptions of slash commands can be localized to the user's selected language. You can find the list of accepted locales on the [discord API documentation](https://discord.com/developers/docs/reference#locales).
|
||||
|
||||
Setting localizations with `setNameLocalizations()` and `setDescriptionLocalizations()` takes the format of an object, mapping location codes (e.g. `pl` and `de`) to their localized strings.
|
||||
|
||||
```js title="commands/fun/dog.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('dog')
|
||||
// [!code focus:4]
|
||||
.setNameLocalizations({
|
||||
pl: 'pies',
|
||||
de: 'hund',
|
||||
})
|
||||
.setDescription('Get a cute picture of a dog!')
|
||||
// [!code focus:4]
|
||||
.setDescriptionLocalizations({
|
||||
pl: 'Słodkie zdjęcie pieska!',
|
||||
de: 'Poste ein niedliches Hundebild!',
|
||||
})
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('breed')
|
||||
.setDescription('Breed of dog')
|
||||
// [!code focus:8]
|
||||
.setNameLocalizations({
|
||||
pl: 'rasa',
|
||||
de: 'rasse',
|
||||
})
|
||||
.setDescriptionLocalizations({
|
||||
pl: 'Rasa psa',
|
||||
de: 'Hunderasse',
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
#### Next steps
|
||||
|
||||
For more information on receiving and parsing the different types of options covered on this page, refer to [parsing options](./parsing-options), or for more general information on how you can respond to slash commands, check out [response methods](./response-methods).
|
||||
188
apps/guide/content/docs/legacy/slash-commands/autocomplete.mdx
Normal file
188
apps/guide/content/docs/legacy/slash-commands/autocomplete.mdx
Normal file
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: Autocomplete
|
||||
---
|
||||
|
||||
Autocomplete allows you to dynamically provide a selection of values to the user, based on their input, rather than relying on static choices. In this section we will cover how to add autocomplete support to your commands.
|
||||
|
||||
<Callout>
|
||||
This page is a follow-up to the [slash commands](./advanced-creation) section covering options and option choices.
|
||||
Please carefully read those pages first so that you can understand the methods used in this section.
|
||||
</Callout>
|
||||
|
||||
## Enabling autocomplete
|
||||
|
||||
To use autocomplete with your commands, _instead_ of listing static choices, the option must be set to use autocompletion using `SlashCommandStringOption#setAutocomplete`:
|
||||
|
||||
```js title="commands/utility/guide".js
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('guide')
|
||||
.setDescription('Search discordjs.guide!')
|
||||
// [!code word:setAutocomplete] [!code focus]
|
||||
.addStringOption((option) => option.setName('query').setDescription('Phrase to search for').setAutocomplete(true));
|
||||
```
|
||||
|
||||
## Responding to autocomplete interactions
|
||||
|
||||
To handle an `AutocompleteInteraction`, use the `BaseInteraction#isAutocomplete` type guard to make sure the interaction instance is an autocomplete interaction. You can do this in a separate `interactionCreate` listener:
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, (interaction) => {
|
||||
// [!code word:isAutocomplete]
|
||||
if (!interaction.isAutocomplete()) return;
|
||||
// do autocomplete handling
|
||||
});
|
||||
```
|
||||
|
||||
Or alternatively, by making a small change to your existing [command handler](../app-creation/handling-commands) and adding an additional method to your individual command files.
|
||||
|
||||
The example below shows how this might be applied to a conceptual version of the `guide` command to determine the closest topic to the search input:
|
||||
|
||||
```js tab="Handler" title="index.js / events/interactionCreate.js (if you implemented the event handler)"
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
// command handling...
|
||||
// [!code ++]
|
||||
} else if (interaction.isAutocomplete()) {
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// [!code ++:5]
|
||||
try {
|
||||
await command.autocomplete(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js tab="Command" title="commands/utility/guide.js"
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('guide')
|
||||
.setDescription('Search discordjs.guide!')
|
||||
.addStringOption((option) => option.setName('query').setDescription('Phrase to search for').setAutocomplete(true)),
|
||||
async autocomplete(interaction) {
|
||||
// handle the autocompletion response (more on how to do that below)
|
||||
},
|
||||
async execute(interaction) {
|
||||
// respond to the complete slash command
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The command handling is almost identical, but notice the change from `execute` to `autocomplete` in the new else-if branch. By adding a separate `autocomplete` function to the `module.exports` of commands that require autocompletion, you can safely separate the logic of providing dynamic choices from the code that needs to respond to the slash command once it is complete.
|
||||
|
||||
<Callout>
|
||||
You might have already moved this code to `events/interactionCreate.js` if you followed our [Event
|
||||
handling](../app-creation/handling-events) guide too.
|
||||
</Callout>
|
||||
|
||||
### Sending results
|
||||
|
||||
The `AutocompleteInteraction` class provides the `AutocompleteInteraction#respond` method to send a response. Using this, you can submit an array of `ApplicationCommandOptionChoiceData` objects for the user to choose from. Passing an empty array will show "No options match your search" for the user.
|
||||
|
||||
<Callout type="warn">
|
||||
Unlike static choices, autocompletion **suggestions are not enforced**, and users may still enter free text.
|
||||
</Callout>
|
||||
|
||||
The `CommandInteractionOptionResolver#getFocused` method returns the currently focused option's value, which can be used to apply filtering to the choices presented. For example, to only display options starting with the focused value you can use the `Array#filter()` method, then using `Array#map()`, you can transform the array into an array of `ApplicationCommandOptionChoiceData` objects.
|
||||
|
||||
```js title="commands/utility/guide.js"
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('guide')
|
||||
.setDescription('Search discordjs.guide!')
|
||||
.addStringOption((option) => option.setName('query').setDescription('Phrase to search for').setAutocomplete(true)),
|
||||
// [!code focus:12]
|
||||
async autocomplete(interaction) {
|
||||
// [!code ++:10]
|
||||
const focusedValue = interaction.options.getFocused(); // [!code word:getFocused]
|
||||
const choices = [
|
||||
'Popular Topics: Threads',
|
||||
'Sharding: Getting started',
|
||||
'Library: Voice Connections',
|
||||
'Interactions: Replying to slash commands',
|
||||
'Popular Topics: Embed preview',
|
||||
];
|
||||
const filtered = choices.filter((choice) => choice.startsWith(focusedValue));
|
||||
await interaction.respond(filtered.map((choice) => ({ name: choice, value: choice })));
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Handling multiple autocomplete options
|
||||
|
||||
To distinguish between multiple options, you can pass `true` into `CommandInteractionOptionResolver#getFocused`, which will now return the full focused object instead of just the value. This is used to get the name of the focused option below, allowing for multiple options to each have their own set of suggestions:
|
||||
|
||||
```js title="commands/utility/guide.js"
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('guide')
|
||||
.setDescription('Search discordjs.guide!')
|
||||
.addStringOption((option) => option.setName('query').setDescription('Phrase to search for').setAutocomplete(true))
|
||||
// [!code focus:3] [!code ++:3]
|
||||
.addStringOption((option) =>
|
||||
option.setName('version').setDescription('Version to search in').setAutocomplete(true),
|
||||
),
|
||||
// [!code focus:28]
|
||||
async autocomplete(interaction) {
|
||||
// [!code --:10]
|
||||
const focusedValue = interaction.options.getFocused(); // [!code word:getFocused]
|
||||
const choices = [
|
||||
'Popular Topics: Threads',
|
||||
'Sharding: Getting started',
|
||||
'Library: Voice Connections',
|
||||
'Interactions: Replying to slash commands',
|
||||
'Popular Topics: Embed preview',
|
||||
];
|
||||
const filtered = choices.filter((choice) => choice.startsWith(focusedValue));
|
||||
// [!code ++:18]
|
||||
const focusedOption = interaction.options.getFocused(true);
|
||||
let choices;
|
||||
|
||||
if (focusedOption.name === 'query') {
|
||||
choices = [
|
||||
'Popular Topics: Threads',
|
||||
'Sharding: Getting started',
|
||||
'Library: Voice Connections',
|
||||
'Interactions: Replying to slash commands',
|
||||
'Popular Topics: Embed preview',
|
||||
];
|
||||
}
|
||||
|
||||
if (focusedOption.name === 'version') {
|
||||
choices = ['v9', 'v11', 'v12', 'v13', 'v14'];
|
||||
}
|
||||
|
||||
const filtered = choices.filter((choice) => choice.startsWith(focusedOption.value));
|
||||
await interaction.respond(filtered.map((choice) => ({ name: choice, value: choice })));
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Accessing other values
|
||||
|
||||
In addition to filtering based on the focused value, you may also wish to change the choices displayed based on the value of other arguments in the command. The following methods work the same in `AutocompleteInteraction`:
|
||||
|
||||
```js
|
||||
const string = interaction.options.getString('input');
|
||||
const integer = interaction.options.getInteger('int');
|
||||
const boolean = interaction.options.getBoolean('choice');
|
||||
const number = interaction.options.getNumber('num');
|
||||
```
|
||||
|
||||
However, the `.getUser()`, `.getMember()`, `.getRole()`, `.getChannel()`, `.getMentionable()` and `.getAttachment()` methods are not available to autocomplete interactions. Discord does not send the respective full objects for these methods until the slash command is completed. For these, you can get the Snowflake value using `interaction.options.get('option').value`:
|
||||
|
||||
### Notes
|
||||
|
||||
- As with other application command interactions, autocomplete interactions must receive a response within 3 seconds.
|
||||
- You cannot defer the response to an autocomplete interaction. If you're dealing with asynchronous suggestions, such as from an API, consider keeping a local cache.
|
||||
- After the user selects a value and sends the command, it will be received as a regular `ChatInputCommandInteraction` with the chosen value.
|
||||
- You can only respond with a maximum of 25 choices at a time, though any more than this likely means you should revise your filter to further narrow the selections.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Deleting Commands
|
||||
---
|
||||
|
||||
<Callout>
|
||||
This page is a follow-up to [command deployment](../app-creation/deploying-commands). To delete commands, you need to
|
||||
register them in the first place.
|
||||
</Callout>
|
||||
|
||||
You may have decided that you don't need a command anymore and don't want your users to be confused when they encounter a removed command.
|
||||
|
||||
## Deleting specific commands
|
||||
|
||||
To delete a specific command, you will need its id. Head to **Server Settings -> Integrations -> Bots and Apps** and choose your bot. Then, right click a command and click **Copy ID**.
|
||||
|
||||
<Callout>
|
||||
You need to have [Developer Mode](https://support.discord.com/hc/articles/206346498) enabled for this to show up!
|
||||
</Callout>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Edit your `deploy-commands.js` as shown below, or put it into its own file to clearly discern it from the deploy workflow:
|
||||
|
||||
```js title="deleteCommands.js"
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const { clientId, guildId, token } = require('./config.json');
|
||||
|
||||
const rest = new REST().setToken(token);
|
||||
|
||||
// ...
|
||||
|
||||
// for guild-based commands
|
||||
rest
|
||||
.delete(Routes.applicationGuildCommand(clientId, guildId, 'commandId'))
|
||||
.then(() => console.log('Successfully deleted guild command'))
|
||||
.catch(console.error);
|
||||
|
||||
// for global commands
|
||||
rest
|
||||
.delete(Routes.applicationCommand(clientId, 'commandId'))
|
||||
.then(() => console.log('Successfully deleted application command'))
|
||||
.catch(console.error);
|
||||
```
|
||||
|
||||
Where `'commandId'` is the id of the command you want to delete. Run your deploy script and it will delete the command.
|
||||
|
||||
## Deleting all commands
|
||||
|
||||
To delete all commands in the respective scope (one guild, all global commands) you can pass an empty array when setting commands.
|
||||
|
||||
```js title="deleteAllCommands"
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const { clientId, guildId, token } = require('./config.json');
|
||||
|
||||
const rest = new REST().setToken(token);
|
||||
|
||||
// ...
|
||||
|
||||
// for guild-based commands
|
||||
rest
|
||||
.put(Routes.applicationGuildCommands(clientId, guildId), { body: [] })
|
||||
.then(() => console.log('Successfully deleted all guild commands.'))
|
||||
.catch(console.error);
|
||||
|
||||
// for global commands
|
||||
rest
|
||||
.put(Routes.applicationCommands(clientId), { body: [] })
|
||||
.then(() => console.log('Successfully deleted all application commands.'))
|
||||
.catch(console.error);
|
||||
```
|
||||
|
||||
Discord's API doesn't currently provide an easy way to delete guild-based commands that occur on multiple guilds from all places at once. Each will need a call of the above endpoint, while specifying the respective guild and command id.
|
||||
|
||||
<Callout type="warn">
|
||||
Note, that the same command will have a **different id**, if deployed to a **different guild**!
|
||||
</Callout>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: Parsing Options
|
||||
---
|
||||
|
||||
## Command options
|
||||
|
||||
In this section, we'll cover how to access the values of a command's options. Consider the following `ban` command example with two options:
|
||||
|
||||
```js title="commands/moderation/ban.js"
|
||||
const { InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Select a member and ban them.')
|
||||
// [!code word:setRequired]
|
||||
.addUserOption((option) => option.setName('target').setDescription('The member to ban').setRequired(true))
|
||||
.addStringOption((option) => option.setName('reason').setDescription('The reason for banning'))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.setContexts(InteractionContextType.Guild),
|
||||
};
|
||||
```
|
||||
|
||||
In the execute method, you can retrieve the value of these two options from the `CommandInteractionOptionResolver` as shown below:
|
||||
|
||||
```js title="commands/moderation/ban.js"
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
// [!code ++:7]
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') ?? 'No reason provided';
|
||||
|
||||
await interaction.reply(`Banning ${target.username} for reason: ${reason}`);
|
||||
await interaction.guild.members.ban(target);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Since `reason` isn't a required option, the example above uses the `??` [nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) to set a default value in case the user does not supply a value for `reason`.
|
||||
|
||||
If the target user is still in the guild where the command is being run, you can also use `.getMember('target')` to get their `GuildMember` object.
|
||||
|
||||
<Callout>
|
||||
If you want the id (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 rename](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) the value obtained from the `CommandInteractionOption` structure to avoid identifier name conflicts.
|
||||
</Callout>
|
||||
|
||||
In the same way as the above examples, you can get values of any type using the corresponding `CommandInteractionOptionResolver#get_____()` method. `String`, `Integer`, `Number` and `Boolean` options all provide the respective primitive types, while `User`, `Channel`, `Role`, and `Mentionable` options will provide either the respective discord.js class instance if your application has a bot user in the guild or a raw API structure for commands-only deployments.
|
||||
|
||||
### Choices
|
||||
|
||||
If you specified preset choices for your String, Integer, or Number option, getting the selected choice is exactly the same as the free-entry options above. Consider the [gif command](./advanced-creation#choices) example you looked at earlier:
|
||||
|
||||
```js title="commands/fun/gif.js"
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('gif')
|
||||
.setDescription('Sends a random gif!')
|
||||
.addStringOption((option) =>
|
||||
// [!code focus:5] [!code word:setRequired]
|
||||
option
|
||||
.setName('category')
|
||||
.setDescription('The gif category')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Funny', value: 'gif_funny' },
|
||||
{ name: 'Meme', value: 'gif_meme' },
|
||||
{ name: 'Movie', value: 'gif_movie' },
|
||||
),
|
||||
),
|
||||
|
||||
// [!code focus:4]
|
||||
async execute(interaction) {
|
||||
const category = interaction.options.getString('category');
|
||||
// category must be one of 'gif_funny', 'gif_meme', or 'gif_movie'
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Notice that nothing changes - you still use `getString()` to get the choice value. The only difference is that in this case, you can be sure it's one of only three possible values.
|
||||
|
||||
### Subcommands
|
||||
|
||||
If you have a command that contains subcommands, the `CommandInteractionOptionResolver#getSubcommand()` will tell you which subcommand was used. You can then get any additional options of the selected subcommand using the same methods as above.
|
||||
|
||||
The snippet below uses the same `info` command from the [subcommand creation guide](./advanced-creation#subcommands) to demonstrate how you can control the logic flow when replying to different subcommands:
|
||||
|
||||
```js title="commands/utility/info.js"
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
// [!code focus:15] [!code word:getSubcommand]
|
||||
async execute(interaction) {
|
||||
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}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: Slash Command Permissions
|
||||
---
|
||||
|
||||
Slash commands have their own permissions system. This system allows you to set the default level of permissions required for a user to execute a command when it is first deployed or your bot is added to a new server.
|
||||
|
||||
The slash command permissions for guilds are defaults only and can be altered by guild administrators, allowing them to configure access however best suits their moderation and server roles. Your code should not try to enforce its own permission management, as this can result in a conflict between the server-configured permissions and your bot's code.
|
||||
|
||||
<Callout type="warn">
|
||||
It is **not possible to prevent users with Administrator permissions from using any commands deployed globally or
|
||||
specifically to their guild**. Think twice before creating "dev-only" commands such as `eval` and restrict them to
|
||||
your private, personal dev server.
|
||||
</Callout>
|
||||
|
||||
## Member permissions
|
||||
|
||||
You can use `SlashCommandBuilder#setDefaultMemberPermissions` to set the default permissions required for a member to run the command. Setting it to `0` will prohibit anyone in a guild from using the command unless a specific overwrite is configured or the user has the Administrator permission flag.
|
||||
|
||||
For this, you'll introduce two common and simple moderation commands: `ban` and `kick`. For a ban command, a sensible default is to ensure that users already have the Discord permission `BanMembers` in order to use it.
|
||||
|
||||
```js title="commands/moderation/ban.js"
|
||||
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Select a member and ban them.')
|
||||
.addUserOption((option) => option.setName('target').setDescription('The member to ban').setRequired(true))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers); // [!code ++]
|
||||
```
|
||||
|
||||
For a kick command however, yout can allow members with the `KickMembers` permission to execute the command, so that's why the flag is listed here here.
|
||||
|
||||
<Callout>
|
||||
You can require the user to have all of multiple permissions by merging them with the `|` bitwise OR operator (for example `PermissionFlagsBits.BanMembers | PermissionFlagsBits.KickMembers`).
|
||||
You cannot require any of multiple permissions. Discord evaluates against the combined permission bitfield!
|
||||
|
||||
If you want to learn more about the `|` bitwise OR operator you can check the [Wikipedia](https://en.wikipedia.org/wiki/Bitwise_operation#OR) and [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR) articles on the topic.
|
||||
|
||||
</Callout>
|
||||
|
||||
```js title="commands/moderation/kick.js"
|
||||
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('kick')
|
||||
.setDescription('Select a member and kick them.')
|
||||
.addUserOption((option) => option.setName('target').setDescription('The member to kick').setRequired(true))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers);
|
||||
```
|
||||
|
||||
In reality, you'll probably want to have an additional confirmation step before a ban actually executes. Check out the [button components section](../interactive-components/buttons) of the guide to see how to add confirmation buttons to your command responses, and listen to button clicks.
|
||||
|
||||
## Contexts
|
||||
|
||||
By default, globally-deployed commands are also available for use in DMs. You can pass in [InteractionContextType](https://discord-api-types.dev/api/discord-api-types-v10/enum/InteractionContextType) to the `setContexts` method of the builder to restrict the command to only be available in guilds or DMs.
|
||||
|
||||
It doesn't make much sense for your `ban` command to be available in DMs, so you can add `setContexts(InteractionContextType.Guild)` to the builder so that it is only available in guilds:
|
||||
|
||||
```js title="commands/moderation/ban.js"
|
||||
const { InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
const data = new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Select a member and ban them.')
|
||||
.addUserOption((option) => option.setName('target').setDescription('The member to ban').setRequired(true))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||
.setContexts(InteractionContextType.Guild); // [!code ++]
|
||||
```
|
||||
|
||||
And that's all you need to know on slash command permissions and contexts!
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: Command Responses
|
||||
---
|
||||
|
||||
The most common way of sending a response is by using the `ChatInputCommandInteraction#reply()` method, as you have done in earlier examples. This method acknowledges the interaction and sends a new message in response.
|
||||
|
||||
```js title="commands/utility/ping.js"
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),
|
||||
async execute(interaction) {
|
||||
await interaction.reply('Pong!'); // [!code word:reply]
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
<Callout>
|
||||
Initially an interaction token is only valid for three seconds, so that's the timeframe in which you are able to use
|
||||
the `ChatInputCommandInteraction#reply()` method. Responses that require more time ("Deferred Responses") are
|
||||
explained later on this page.
|
||||
</Callout>
|
||||
|
||||
## Ephemeral responses
|
||||
|
||||
You may not always want everyone who has access to the channel to see a slash command's response. Previously, you would have had to DM the user to achieve this, potentially encountering the high rate limits associated with DM messages, or simply being unable to do so, if the user's DMs were disabled.
|
||||
|
||||
Thankfully, Discord provides a way to hide response messages from everyone but the executor of the slash command. This is called an ephemeral message and can be set by providing `flags: MessageFlags.Ephemeral` in the `InteractionReplyOptions`, as follows:
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!'); // [!code --]
|
||||
await interaction.reply({ content: 'Secret Pong!', flags: MessageFlags.Ephemeral }); // [!code ++] [!code word:Ephemeral]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Ephemeral responses are _only_ available for interaction responses; another great reason to use the new and improved slash command user interface.
|
||||
|
||||
## 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 `ChatInputCommandInteraction#editReply()` method:
|
||||
|
||||
<Callout type="warn">
|
||||
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. You also **cannot** edit the ephemeral state of a message, so make
|
||||
sure that your first response sets this correctly.
|
||||
</Callout>
|
||||
|
||||
```js
|
||||
const wait = require('node:timers/promises').setTimeout;
|
||||
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!');
|
||||
await wait(2_000);
|
||||
await interaction.editReply('Pong again!'); // [!code word:editReply]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In fact, editing your interaction response is necessary to [calculate the ping](../popular-topics/faq#how-do-i-check-the-bots-ping) properly for this command.
|
||||
|
||||
## Deferred responses
|
||||
|
||||
As previously mentioned, Discord requires an acknowledgement from your bot within three seconds that the interaction was received. Otherwise, Discord considers the interaction to have failed and the 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 `ChatInputCommandInteraction#deferReply()` method, which triggers the `<application> is thinking...` message. This also acts as the initial response, to confirm to Discord that the interaction was received successfully and gives you a **15-minute timeframe to complete your tasks** before responding.
|
||||
|
||||
```js
|
||||
const wait = require('node:timers/promises').setTimeout;
|
||||
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.deferReply(); // [!code word:deferReply]
|
||||
// you can take up to 15 minutes! We take 4 seconds to demonstrate this
|
||||
// since it is barely above the 3-second threshold for the initial response
|
||||
await wait(4_000);
|
||||
await interaction.editReply('Pong!'); // [!code word:editReply]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
If you have a command that performs longer tasks, be sure to call `deferReply()` as early as possible.
|
||||
|
||||
Note that if you want your response to be ephemeral, utilize `flags` from `InteractionDeferReplyOptions` here:
|
||||
|
||||
```js
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
```
|
||||
|
||||
It is not possible to edit a reply to change its ephemeral state once sent.
|
||||
|
||||
<Callout>
|
||||
If you want to make a proper ping command, one is available in our
|
||||
[FAQ](../popular-topics/faq#how-do-i-check-the-bot-s-ping).
|
||||
</Callout>
|
||||
|
||||
## Follow-ups
|
||||
|
||||
The `reply()` and `deferReply()` methods are both _initial_ responses, which tell Discord that your bot successfully received the interaction, but cannot be used to send additional messages. This is where follow-up messages come in. After having initially responded to an interaction, you can use `ChatInputCommandInteraction#followUp()` to send additional messages:
|
||||
|
||||
<Callout type="warn">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!');
|
||||
await interaction.followUp('Pong again!');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can also pass the ephemeral flag to the `InteractionReplyOptions`:
|
||||
|
||||
```js
|
||||
await interaction.followUp({ content: 'Pong again!', flags: MessageFlags.Ephemeral });
|
||||
```
|
||||
|
||||
Note that if you use `followUp()` after a `deferReply()`, the first follow-up will edit the `<application> is thinking` message rather than sending a new one.
|
||||
|
||||
That's all, now you know everything there is to know on how to reply to slash commands!
|
||||
|
||||
<Callout>Interaction responses can use masked links (e.g. `[text](http://site.com)`) in the message content.</Callout>
|
||||
|
||||
## Fetching and deleting responses
|
||||
|
||||
In addition to replying to a slash command, you may also want to delete the initial reply. You can use `ChatInputCommandInteraction#deleteReply()` for this:
|
||||
|
||||
```js
|
||||
await interaction.reply('Pong!');
|
||||
await interaction.deleteReply();
|
||||
```
|
||||
|
||||
Lastly, you may require the `Message` object of a reply for various reasons, such as adding reactions. Pass `withResponse: true` to obtain the `InteractionCallbackResponse`. You can then access the `Message` object like so:
|
||||
|
||||
```js
|
||||
const response = await interaction.reply({ content: 'Pong!', withResponse: true });
|
||||
console.log(response.resource.message);
|
||||
```
|
||||
|
||||
You can also use the `ChatInputCommandInteraction#fetchReply()` method to fetch the `Message` instance. Do note that this incurs an extra API call in comparison to `withResponse: true`:
|
||||
|
||||
```js
|
||||
await interaction.reply('Pong!');
|
||||
const message = await interaction.fetchReply();
|
||||
console.log(message);
|
||||
```
|
||||
|
||||
## Localized responses
|
||||
|
||||
In addition to the ability to provide [localized command definitions](./advanced-creation#localizations), you can also localize your responses. To do this, get the locale of the user with `ChatInputCommandInteraction#locale` and respond accordingly:
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, (interaction) => {
|
||||
const locales = {
|
||||
pl: 'Witaj Świecie!',
|
||||
de: 'Hallo Welt!',
|
||||
};
|
||||
interaction.reply(locales[interaction.locale] ?? 'Hello World (default is english)');
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user