diff --git a/docs/Change_Log.md b/docs/Change_Log.md index 474220703c..6f69a7b5e2 100644 --- a/docs/Change_Log.md +++ b/docs/Change_Log.md @@ -1,5 +1,16 @@ # Change Log +## Slash Commands and Interactions +#### December 15, 2020 + +Slash Commands are here! There's a _lot_ to cover, so go check out specific documentation under [Slash Commands](#DOCS_INTERACTIONS_SLASH_COMMANDS/). + +Slash Commands include some new features for webhooks as well: + +- Webhooks can now update previously-sent messages from the same webhook using [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message) and [Delete Webhook Message](#DOCS_RESOURCES_WEBHOOK/delete-webhook-message) + +This PR also documents the `application` field on the `READY` gateway event, which is a partial [application object](#DOCS_TOPICS_OAUTH2/application-object) containing `id` and `flags`. + ## Inline Replies #### November 16, 2020 diff --git a/docs/interactions/Slash_Commands.md b/docs/interactions/Slash_Commands.md new file mode 100644 index 0000000000..15ff2ba072 --- /dev/null +++ b/docs/interactions/Slash_Commands.md @@ -0,0 +1,841 @@ +# Slash Commands + +Slash Commands are the new, exciting way to build and interact with apps on Discord. + +With Slash Commands, all you have to do is type `/` and you're ready to use your favorite bot. Users can learn everything your bot does and easily find new features as you add them. Validation, error states, and helpful UI walks them through your commands, meaning they can get it right the first time, especially on mobile. You now have one more ally in the fight against your phone's autocorrect. + +Slash Commands set your users up for success instead of confusion and frustration. They separate how users think and how your code works, meaning no matter how complex your codebase and commands may get, people who love your bot will find it approachable and easy to use. + +Let's get started! + +## A Quick Note on Limits + +In this documentation you'll find some notes about limits and caps on certain parts of Slash Commands. At a high level, they are as follows: + +- An app can have up to 50 top-level global commands (50 commands with unique names) +- An app can have up to an additional 50 guild commands per guild +- An app can have up to 10 subcommand groups on a top-level command +- An app can have up to 10 subcommands within a subcommand group +- `choices` can have up to 10 values per option +- commands can have up to 10 `options` per command +- Limitations on [command names](#DOCS_INTERACTIONS_SLASH_COMMANDS/registering-a-command) +- Limitations on [nesting subcommands and groups](#DOCS_INTERACTIONS_SLASH_COMMANDS/nested-subcommands-and-groups) + +These are the limits and caps for the initial release, but **they can be subject to change with your feedback.** If you would like to leave feedback about Slash Commands--limits, features, or otherwise--please open a ticket on our [Github Issue Tracker](https://github.com/discord/discord-api-docs/issues) using the `Slash Commands` templates. + +## What is a Slash Command + +A **Slash Command** is a command that you register for your application. They're made up of a name, description, and a block of `options`, which you can think of like arguments to a function. The name and description help users find your command among many others, and the `options` validate user input as they fill out your command. + +Your global commands are available in every guild that adds your application. You can also make commands for a specific guild; they're only available in that guild. + +An **Interaction** is the message that your application receives when a user uses a command. It includes the values that the user submitted, as well as some metadata about this particular instance of the command being used: the `guild_id`, `channel_id`, `member` and other fields. You can find all the values in our [data models](#DOCS_INTERACTIONS_SLASH_COMMANDS/data-models-and-types). + +## Slash Commands, Interactions, and Bot Users + +We're all used to the way that Discord bots have worked for a long time. You make an application in the Dev Portal, you add a bot user to it, and you copy the token. That token can be used to connect to the gateway and to make requests against our API. + +Slash Commands and Interactions bring something entirely new to the table: the ability to interact with an application _without needing a bot user in the guild_. As you read through this documentation, you'll see that bot tokens are only referenced as a helpful alternative to doing a client credentials auth flow. Slash Commands do not depend on a bot user in the guild; responding to interactions does not require a bot token. + +In many cases, you may still need a bot user. If you need to receive gateway events, or need to interact with other parts of our API (like fetching a guild, or a channel, or updating permissions on a user), those actions are all still tied to having a bot token. However, if you don't need any of those things, you never have to add a bot user to your application at all. + +Welcome to the new world. + +## Authorizing Your Application + +There is a new special OAuth2 scope for applications called `applications.commands`. + +> danger +> **In order to make Slash Commands work within a guild, the guild must authorize your application with the `applications.commands` scope. The `bot` scope is not enough.** + +When requesting this scope, we "shortcut" the OAuth2 flow similar to adding a bot. You don't need to complete the flow, exchange for a token, or any of that. + +If your application does not require a bot user within the guild for its commands to work, **you no longer need to add for the bot scope or specific permissions**. To clarify this point even more: + +> info +> If your application only talks to Discord through creating Slash Commands and responding to Interactions, and does not use any other part of our API, **you no longer need to request the bot scope**. + +Who knows, maybe in the future, Interactions tokens will become even smarter. + +## Registering a Command + +> info +> Currently, Slash Commands can only be registered via HTTP endpoint. + +There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means: + +- Your app **cannot** have two global commands with the same name +- Your app **cannot** have two guild commands within the same name **on the same guild** +- Your app **can** have a global and guild command with the same name +- Multiple apps **can** have commands with the same names + +> info +> Apps can have a maximum of 50 global commands, and an additional 50 guild-specific commands per guild + +To make a **global** Slash Command, make an HTTP POST call like this: + +```py +import requests + + +url = "https://discord.com/api/v8/applications//commands" + +json = { + "name": "blep", + "description": "Send a random adorable animal photo", + "options": [ + { + "name": "animal", + "description": "The type of animal", + "type": 3, + "required": True, + "choices": [ + { + "name": "Dog", + "value": "animal_dog" + }, + { + "name": "Cat", + "value": "animal_dog" + }, + { + "name": "Penguin", + "value": "animal_penguin" + } + ] + }, + { + "name": "only_smol", + "description": "Whether to show only baby animals", + "type": 5, + "required": False + } + ] +} + +# For authorization, you can use either your bot token +headers = { + "Authorization": "Bot 123456" +} + +# or a client credentials token for your app with the applications.commmands.update scope +headers = { + "Authorization": "Bearer abcdefg" +} + +r = requests.post(url, headers=headers, json=json) +``` + +This command will be available on _all_ your app's guilds. + +> warn +> Global commands are cached for **1 hour**. That means that new global commands will fan out slowly across all guilds, and will be guaranteed to be updated in an hour. + +To make a **guild** Slash Command, make a similar HTTP POST call, but scope it to a specific `guild_id`: + +```py +import requests + + +url = "https://discord.com/api/v8/applications//guilds//commands" + +json = # ... +``` + +This command will only be available within the guild that you specified. + +> warn +> Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. + +## Updating and Deleting a Command + +Slash Commands can be deleted and updated by making `DELETE` and `PATCH` calls to the command endpoint. Those endpoints are + +- `applications//commands/` for global commands, or +- `applications//guilds//commands/` for guild commands + +Because Slash Commands have unique names within a scope, we treat `POST` requests for new commands as upserts. That means **making a new command with an already-used name for your application will update the existing command**. + +Full documentation of endpoints can be found in [Endpoints](#DOCS_INTERACTIONS_SLASH_COMMANDS/endpoints). + +## Receiving an Interaction + +When a user uses a Slash Command, your app will receive an **Interaction**. Your app can receive an interaction in one of two ways: + +- Via gateway event, `INTERACTION_CREATE` +- Via outgoing webhook + +These two methods are **mutually exclusive**; you can _only_ receive Interactions one of the two ways. The `INTERACTION_CREATE` gateway event will be handled by the library you are using, so we'll just cover outgoing webhooks. + +In your application in the Developer Portal, there is a field on the main page called "Interactions Endpoint URL". If you want to receive Interactions via outgoing webhook, you can set your URL in this field. In order for the URL to be valid, you must be prepared for two things ahead of time: + +> info +> These steps are only necessary for webhook-based Interactions. It is not required for receiving them over the gateway. + +1. Your endpoint must be prepared to ACK a `PING` message +2. Your endpoint must be set up to properly handle signature headers--more on that in [Security and Authorization](#DOCS_INTERACTIONS_SLASH_COMMANDS/security-and-authorization) + +If either of these are not complete, we will not validate your URL and it will fail to save. + +When you attempt to save a URL, we will send a `POST` request to that URL with a `PING` payload. The `PING` payload has a `type: 1`. So, to properly ACK the payload, return a `200` reponse with a payload of `type: 1`: + +```py +@app.route('/', methods=['POST']) +def my_command(): + if request.json["type"] == 1: + return jsonify({ + "type": 1 + }) +``` + +You'll also need to properly set up [Security and Authorization](#DOCS_INTERACTIONS_SLASH_COMMANDS/security-and-authorization) on your endpoint for the URL to be accepted. Once both of those are complete and your URL has been saved, you can start receiving Interactions via webhook! At this point, your app will **no longer receive Interactions over the gateway**. If you want to receive them over the gateway again, simply delete your URL. + +An Interaction includes the `data` that the user sent in the command, as well as some metadata. It looks like this: + +```js +{ + "type": 2, + "token": "A_UNIQUE_TOKEN", + "member": { + "user": { + "id": 53908232506183680, + "username": "Mason", + "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432", + "discriminator": "1337", + "public_flags": 131141 + }, + "roles": ["539082325061836999"], + "premium_since": null, + "permissions": "2147483647", + "pending": false, + "nick": null, + "mute": false, + "joined_at": "2017-03-13T19:19:14.040000+00:00", + "is_pending": false, + "deaf": false + }, + "id": "786008729715212338", + "guild_id": "290926798626357999", + "data": { + "options": [{ + "name": "cardname", + "value": "The Gitrog Monster" + }], + "name": "cardsearch", + "id": "771825006014889984" + }, + "channel_id": "645027906669510667" +} +``` + +An explanation of all the fields can be found in our [data models](#DOCS_INTERACTIONS_SLASH_COMMANDS/data-models-and-types). + +Now that you've gotten the data from the user, it's time to respond to them. + +## Responding to an Interaction + +Interactions--both receiving and responding--are webhooks under the hood. So responding to an Interaction is just like sending a webhook request! + +When responding to an interaction received **via webhook**, your server can simply respond to the received `POST` request. You'll want to respond with a `200` status code (if everything went well), as well as specifying a `type` and `data`, which is an [Interaction Response](#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction-response) object: + +```py +@app.route('/', methods=['POST']) +def my_command(): + if request.json["type"] == 1: + return jsonify({ + "type": 1 + }) + + else: + return jsonify({ + "type": 4, + "data": { + "tts": False, + "content": "Congrats on sending your command!", + "embeds": [], + "allowed_mentions": [] + } + }) +``` + +If you are receiving Interactions over the gateway, you will **also need to respond via HTTP**. Responses to Interactions **are not sent as commands over the gateway**. + +To respond to a gateway Interaction, make a `POST` request like this. `interaction_id` is the unique id of that individual Interaction from the received payload. `interaction_token` is the unique token for that interaction from the received payload. **This endpoint is only valid for Interactions received over the gateway. Otherwise, respond to the `POST` request to issue an initial response.** + +```py +url = "https://discord.com/api/v8/interactions///callback" + +json = { + "type": 4, + "data": { + "content": "Congrats on sending your command!" + } +} +r = requests.post(url, json=json) +``` + +> info +> Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages. + +## Followup Messages + +Sometimes, your bot will want to send followup messages to a user after responding to an interaction. Or, you may want to edit your original response. Whether you receive Interations over the gateway or by outgoing webhook, you can use the following endpoints to edit your initial response or send followup messages: + +- `PATCH /webhooks///messages/@original` to edit your initial response to an Interaction +- `DELETE /webhooks///messages/@original` to delete your initial response to an Interaction +- `POST /webhooks//` to send a new followup message +- `PATCH /webhooks///messages/` to edit a message sent with that `token` + +> info +> Interactions webhooks share the same rate limit properties as normal webhooks + +Interaction tokens are valid for **15 minutes**, meaning you can respond and update to a Slash Command within that amount of time. + +## Security and Authorization + +> info +> Check out our [Community Resources](#DOCS_TOPICS_COMMUNITY_RESOURCES/interactions) for libraries to help you with security in your language of choice + +The internet is a scary place, especially for people hosting open, unauthenticated endpoints. If you are receiving Interactions via outgoing webhook, there are some security steps you **must** take before your app is eligible to receive requests. + +Every Interaction is sent with the following headers: + +- `X-Signature-Ed25519` as a signature +- `X-Signature-Timestamp` as a timestamp + +Using your favorite security library, you **must validate the request each time to you receive an interaction**. If the signature fails validate, respond with a `401` error code. Here's a couple code examples: + +```js +const nacl = require('tweetnacl'); + +// Your public key can be found on your application in the Developer Portal +const PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY'; + +const signature = req.get('X-Signature-Ed25519'); +const timestamp = req.get('X-Signature-Timestamp'); +const body = req.rawBody; // rawBody is expected to be a string, not raw bytes + +const isVerified = nacl.sign.detached.verify( + Buffer.from(timestamp + body), + Buffer.from(signature, 'hex'), + Buffer.from(PUBLIC_KEY, 'hex') +); + +if (!isVerified) { + return res.status(401).end('invalid request signature'); +} + ``` + +```py +from nacl.signing import VerifyKey +from nacl.exceptions import BadSignatureError + +# Your public key can be found on your application in the Developer Portal +PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY' + +verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) + +signature = request.headers["X-Signature-Ed25519"] +timestamp = request.headers["X-Signature-Timestamp"] +body = request.data + +try: + verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature)) +except BadSignatureError: + abort(401, 'invalid request signature') +``` + +If you are not properly validating this signature header, we will not allow you to save your interactions URL in the Dev Portal. We will also do automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your interactions URL in the future and alert you via email and System DM. + +We highly recommend checking out our [Community Resources](#DOCS_TOPICS_COMMUNITY_RESOURCES/interactions) and the two libraries found there. They not only provide typing for Interactions data models, but also include decorators for API frameworks like Flask and Express to make validation easy. + +## Subcommands and Subcommand Groups + +> warn +> Currently, subcommands and subcommand groups all appear at the top level in the command explorer. This may change in the future to include them as nested autocomplete options. + +For those developers looking to make more organized and complex groups of commands, look no further than subcommands and groups. + +**Subcommands** organize your commands by **specifying actions within a command or group**. + +**Subcommand Groups** organize your **subcommands** by **grouping subcommands by similar action or resource within a command**. + +These are not enforced rules. You are free to use subcommands and groups however you'd like; it's just how we think about them. + +> danger +> Using subcommands or subcommand groups will make your base command unusable. You can't send the base `/permissions` command as a valid command if you also have `/permissions add | remove` as subcommands or subcommand groups + +### Nested Subcommands and Groups + +A quick note on nested subcommands and groups. We support nesting one level deep within a group, meaning your top level command can contain subcommand groups, and those groups can contain subcommands. **That is the only kind of nesting supported.** Here's some visual examples: + +``` +VALID + +command +| +|__ subcommand-group + | + |__ subcommand +| +|__ subcommand-group + | + |__ subcommand + + +------- + +INVALID + + +command +| +|__ subcommand-group + | + |__ subcommand-group +| +|__ subcommand-group + | + |__ subcommand-group + +---- + +INVALID + +command +| +|__ subcommand + | + |__ subcommand-group +| +|__ subcommand + | + |__ subcommand-group +``` + +### Example Walkthrough + +Let's look at an example. Let's imagine you run a moderation bot. You want to make a `/permissions` command that can do the following: + +- Get the guild permissions for a user or a role +- Get the permissions for a user or a role on a specific channel +- Change the guild permissions for a user or a role +- Change the permissions for a user or a role on a specific channel + +We'll start by defining the top-level information for `/permissions`: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [] +} +``` + +![A command with no arguments. It says /permissions](command.png) + +Now we have a command named `permissions`. We want this command to be able to affect users and roles. Rather than making two separate commands, we can use subcommand groups. We want to use subcommand groups here because we are grouping commands on a similar resource: `user` or `role`. + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2 // 2 is type SUB_COMMAND_GROUP + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2 + } + ] +} +``` + +You'll notice that a command like this **will not show up** in the command explorer. That's because groups are effectively "folders" for commands, and we've made two empty folders. So let's continue. + +Now that we've effectively made `user` and `role` "folders", we want to be able to either `get` and `edit` permissions. Within the subcommand groups, we can make subcommands for `get` and `edit`: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2, // 2 is type SUB_COMMAND_GROUP + "options": [ + { + "name": "get", + "description": "Get permissions for a user", + "type": 1 // 1 is type SUB_COMMAND + }, + { + "name": "edit", + "description": "Edit permissions for a user", + "type": 1 + } + ] + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2, + "options": [ + { + "name": "get", + "description": "Get permissions for a role", + "type": 1 + }, + { + "name": "edit", + "description": "Edit permissions for a role", + "type": 1 + } + ] + } + ] +} +``` + +![A command with grouped subcommands. It says /permissions user get](command-with-groups-subcommands.png) + +Now, we need some arguments! If we chose `user`, we need to be able to pick a user; if we chose `role`, we need to be able to pick a role. We also want to be able to pick between guild-level permissions and channel-specific permissions. For that, we can use optional arguments: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2, // 2 is type SUB_COMMAND_GROUP + "options": [ + { + "name": "get", + "description": "Get permissions for a user", + "type": 1, // 1 is type SUB_COMMAND + "options": [ + { + "name": "user", + "description": "The user to get", + "type": 6, // 6 is type USER + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to get. If omitted, the guild permissions will be returned", + "type": 7, // 7 is type CHANNEL + "required": false + } + ] + }, + { + "name": "edit", + "description": "Edit permissions for a user", + "type": 1, + "options": [ + { + "name": "user", + "description": "The user to edit", + "type": 6, + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", + "type": 7, + "required": false + } + ] + } + ] + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2, + "options": [ + { + "name": "get", + "description": "Get permissions for a role", + "type": 1, + "options": [ + { + "name": "role", + "description": "The role to get", + "type": 8, // 8 is type ROLE + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to get. If omitted, the guild permissions will be returned", + "type": 7, + "required": false + } + ] + }, + { + "name": "edit", + "description": "Edit permissions for a role", + "type": 1, + "options": [ + { + "name": "role", + "description": "The role to edit", + "type": 8, + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", + "type": 7, + "required": false + } + ] + } + ] + } + ] +} +``` + +And, done! The JSON looks a bit complicated, but what we've ended up with is a single command that can be scoped to multiple actions, and then further scoped to a particular resource, and then even _further_ scope with optional arguments. Here's what it looks like all put together. + +![A command with grouped subcommands and parameters. It says /permissions user get with arguments for a user and a channel.](command-with-groups-subcommands-parameters.png) + +## Endpoints + +> info +> For authoriation, all endpoints take either a bot token or client credentials token for your application + +## Get Global Application Commands % GET /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/commands + +Fetch all of the global commands for your application. Returns an array of [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) objects. + +## Create Global Application Command % POST /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/commands + +> danger +> Creating a command with the same name as an existing command for your application will overwrite the old command. + +Create a new global command. New global commands will be available in all guilds after 1 hour. Returns `200` and an [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) object. + +###### JSON Params + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------------------|--------------------------------| +| name | string | 3-32 character command name | +| description | string | 1-100 character description | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | the parameters for the command | + +## Edit Global Application Command % PATCH /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/commands/{command.id#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand} + +Edit a global command. Updates will be available in all guilds after 1 hour. Returns `200` and an [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) object. + +###### JSON Params + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------------------|--------------------------------| +| name | string | 3-32 character command name | +| description | string | 1-100 character description | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | the parameters for the command | + + +## Delete Global Application Command % DELETE /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/commands/{command.id#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand} + +Deletes a global command. Returns `204`. + +## Get Guild Application Commands % GET /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands + +Fetch all of the guild commands for your application for a specific guild. Returns an array of [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) objects. + +## Create Guild Application Command % POST /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands + +> danger +> Creating a command with the same name as an existing command for your application will overwrite the old command. + +Create a new guild command. New guild commands will be available in the guild immediately. Returns `200` and an [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) object. + +###### JSON Params + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------------------|--------------------------------| +| name | string | 3-32 character command name | +| description | string | 1-100 character description | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | the parameters for the command | + +## Edit Guild Application Command % PATCH /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand} + +Edit a guild command. Updates for guild commands will be available immediately. Returns `200` and an [ApplicationCommand](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand) object. + +###### JSON Params + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------------------|--------------------------------| +| name | string | 3-32 character command name | +| description | string | 1-100 character description | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | the parameters for the command | + + +## Delete Guild Application Command % DELETE /applications/{application.id#DOCS_TOPICS_OAUTH2/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommand} + +Delete a guild command. Returns `204` on success. + +## Create Interaction Response % POST /interactions/{interaction.id#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/callback + +Create a response to an Interaction from the gateway. Takes an [Interaction response](#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction-response). + +## Edit Original Interaction Response % PATCH /webhooks/application.id/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/messages/@original + +Edits the initial Interaction response. Functions the same as [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message). + +## Delete Original Interaction Response % DELETE /webhooks/application.id/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/messages/@original + +Deletes the initial Interaction response. Returns `204` on success. + +## Create Followup Message % POST /webhooks/application.id/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction} + +Create a followup message for an Interaction. Functions the same as [Execute Webhook](#DOCS_RESOURCES_WEBHOOK/execute-webhook) + +## Edit Followup Message % PATCH /webhooks/application.id/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} + +Edits a followup message for an Interaction. Functions the same as [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message). + +## Delete Followup Message % DELETE /webhooks/application.id/{interaction.token#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} + +Deletes a followup message for an Interaction. Returns `204` on success. + +## Data Models and Types + +## ApplicationCommand + +> info +> A command, or each individual subcommand, can have a maximum of 10 `options` + +An application command is the base "command" model that belongs to an application. This is what you are creating when you `POST` a new command. + +| Field | Type | Description | +|----------------|---------------------------------------------------------------------------------------------------|-------------------------------------| +| id | snowflake | unique id of the command | +| application_id | snowflake | unique id of the parent application | +| name | string | 3-32 character name | +| description | string | 1-100 character description | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | the parameters for the command | + +## ApplicationCommandOption + +> info +> You can specify a maximum of 10 `choices` per option + +| Field | Type | Description | +|-------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| +| type | int | value of [ApplicationCommandOptionType](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoptiontype) | +| name | string | 1-32 character name | +| description | string | 1-100 character description | +| default? | bool | the first `required` option for the user to complete--only one option can be `default` | +| required? | bool | if the parameter is required or optional--default `false` | +| choices? | array of [ApplicationCommandOptionChoice](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoptionchoice) | choices for `string` and `int` types for the user to pick from | +| options? | array of [ApplicationCommandOption](#DOCS_INTERACTIONS_SLASH_COMMANDS/applicationcommandoption) | if the option is a subcommand or subcommand group type, this nested options will be the parameters | + +## ApplicationCommandOptionType + +| Name | Value | +|-------------------|-------| +| SUB_COMMAND | 1 | +| SUB_COMMAND_GROUP | 2 | +| STRING | 3 | +| INTEGER | 4 | +| BOOLEAN | 5 | +| USER | 6 | +| CHANNEL | 7 | +| ROLE | 8 | + +## ApplicationCommandOptionChoice + +If you specify `choices` for an option, they are the **only** valid values for a user to pick + +| Field | Type | Description | +|-------|---------------|---------------------| +| name | string | 1-100 character choice name | +| value | string or int | value of the choice | + +## Interaction + +An interaction is the base "thing" that is sent when a user invokes a command, and is the same for Slash Commands and other future interaction types + +| Field | Type | Description | +|------------|-----------------------------------|---------------------------------------------------------| +| id | snowflake | id of the interaction | +| type | InteractionType | the type of interaction | +| data?\* | ApplicationCommandInteractionData | the command data payload | +| guild_id | snowflake | the guild it was sent from | +| channel_id | snowflake | the channel it was sent from | +| member | GuildMember | guild member data for the invoking user | +| token | string | a continuation token for responding to the interaction | +| version | int | read-only property, always `1` | + +\* This is always present on `ApplicationCommand` interaction types. It is optional for future-proofing against new interaction types + +###### InteractionType + +| Name | Value | +|--------------------|-------| +| Ping | 1 | +| ApplicationCommand | 2 | + +###### ApplicationCommandInteractionData + +| Field | Type | Description | +|----------|--------------------------------------------------|-----------------------------------| +| id | snowflake | the ID of the invoked command | +| name | string | the name of the invoked command | +| options? | array of ApplicationCommandInteractionDataOption | the params + values from the user | + +###### ApplicationCommandInteractionDataOption + +All options have names, and an option can either be a parameter and input value--in which case `value` will be set--or it can denote a subcommand or group--in which case it will contain a top-level key and another array of `options`. + +`value` and `options` are mututally exclusive. + +| Field | Type | Description | +|----------|--------------------------------------------------|-------------------------------------------------| +| name | string | the name of the parameter | +| value? | OptionType | the value of the pair | +| options? | array of ApplicationCommandInteractionDataOption | present if this option is a group or subcommand | + +###### Interaction Response + +After receiving an interaction, you must respond to acknowledge it. This may be a `pong` for a `ping`, a message, or simply an acknowledgement that you have received it and will handle the command async. + +Interaction responses may choose to "eat" the user's command input if you do not wish to have their slash command show up as message in chat. This may be helpful for slash commands, or commands whose responses are asynchronous or ephemeral messages. + +| Field | Type | Description | +|-------|-------------------------------------------|------------------------------| +| type | InteractionResponseType | the type of response | +| data? | InteractionApplicationCommandCallbackData | an optional response message | + +###### InteractionResponseType + +| Name | Value | Description | +|--------------------------|-------|-------------------------------------------------------------------| +| Pong | 1 | ACK a `Ping` | +| Acknowledge | 2 | ACK a command without sending a message, eating the user's input | +| ChannelMessage | 3 | respond with a message, eating the user's input | +| ChannelMessageWithSource | 4 | respond with a message, showing the user's input | +| ACKWithSource | 5 | ACK a command without sending a message, showing the user's input | + +###### InteractionApplicationCommandCallbackData + +Not all message fields are currently supported. + +| Name | Value | Description | +|-------------------|------------------|---------------------------------------------------------------------------------------------| +| tts? | bool | is the response TTS | +| content | string | message content | +| embeds? | array of embeds | supports up to 10 embeds | +| allowed_mentions? | allowed mentions | [allowed mentions](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) object | \ No newline at end of file diff --git a/docs/resources/Channel.md b/docs/resources/Channel.md index baa8ecdf9e..dda1bc8af0 100644 --- a/docs/resources/Channel.md +++ b/docs/resources/Channel.md @@ -218,7 +218,7 @@ Represents a message sent in a channel within Discord. ###### Message Types > warn -> Replies only have type `19` in API v8. In v6, they are still type `0`. +> Type `19` and `20` are only in API v8. In v6, they are still type `0`. | Type | Value | |----------------------------------------|-------| @@ -238,6 +238,7 @@ Represents a message sent in a channel within Discord. | GUILD_DISCOVERY_DISQUALIFIED | 14 | | GUILD_DISCOVERY_REQUALIFIED | 15 | | REPLY | 19 | +| APPLICATION_COMMAND | 20 | ###### Message Activity Structure diff --git a/docs/resources/Webhook.md b/docs/resources/Webhook.md index abcf28c038..ab6b112d26 100644 --- a/docs/resources/Webhook.md +++ b/docs/resources/Webhook.md @@ -152,6 +152,8 @@ Add a new webhook to your GitHub repo (in the repo's settings), and use this end ## Edit Webhook Message % PATCH /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} +Edits a previously-sent webhook message from the same token. + > info > All parameters to this endpoint are optional and nullable. @@ -165,4 +167,4 @@ Add a new webhook to your GitHub repo (in the repo's settings), and use this end # Delete Webhook Message % DELETE /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} -Deletes a message that was created by the webhook. Returns a 204 NO CONTENT response on success. +Deletes a message that was created by the webhook. Returns a 204 NO CONTENT response on success. \ No newline at end of file diff --git a/docs/topics/Community_Resources.md b/docs/topics/Community_Resources.md index cf0f93687a..3640eb65ea 100644 --- a/docs/topics/Community_Resources.md +++ b/docs/topics/Community_Resources.md @@ -39,6 +39,13 @@ Many of these libraries are represented in the [unofficial, community-driven Dis | [Sword](https://github.com/Azoy/Sword) | Swift | | [Discordeno](https://github.com/Skillz4Killz/Discordeno) | TypeScript | +## Interactions + +[Interactions and Slash Commands](#DOCS_INTERACTIONS_SLASH_COMMANDS/) are the great, new way of making a Discord bot. The following open-source libraries provide help for the security and authentication checks that are mandatory if you are receiving Interactions via outgoing webhook. They also include some types for the Interactions data models. + +- [discord-interactions-js](https://github.com/discord/discord-interactions-js) +- [discord-interactions-python](https://github.com/discord/discord-interactions-python) + ## Game SDK Tools Discord Game SDK's lobby and networking layer shares similarities with other gaming platforms (i.e. Valve's Steamworks SDK). The following open source library provides developers a uniform interface for these shared features and can simplify developing for multiple platforms. Note: this library is tailored for Unity3D development. diff --git a/docs/topics/Gateway.md b/docs/topics/Gateway.md index 8d37f0e62a..2e9a690ad8 100644 --- a/docs/topics/Gateway.md +++ b/docs/topics/Gateway.md @@ -409,6 +409,7 @@ Events are payloads sent over the socket to a client that correspond to events i | [Voice State Update](#DOCS_TOPICS_GATEWAY/voice-state-update) | someone joined, left, or moved a voice channel | | [Voice Server Update](#DOCS_TOPICS_GATEWAY/voice-server-update) | guild's voice server was updated | | [Webhooks Update](#DOCS_TOPICS_GATEWAY/webhooks-update) | guild channel webhook was created, update, or deleted | +| [Interaction Create](#DOCS_TOPICS_GATEWAY/interaction-create) | user used a [Slash Command](#DOCS_INTERACTIONS_SLASH_COMMANDS/) | ### Event Names @@ -652,6 +653,7 @@ The ready event is dispatched when a client has completed the initial handshake | guilds | array of [Unavailable Guild](#DOCS_RESOURCES_GUILD/unavailable-guild-object) objects | the guilds the user is in | | session_id | string | used for resuming connections | | shard? | array of two integers (shard_id, num_shards) | the [shard information](#DOCS_TOPICS_GATEWAY/sharding) associated with this session, if sent when identifying | +| application | partial [application object](#DOCS_TOPICS_OAUTH2/application-object) | contains `id` and `flags` | #### Resumed @@ -1215,6 +1217,12 @@ Sent when a guild channel's webhook is created, updated, or deleted. | guild_id | snowflake | id of the guild | | channel_id | snowflake | id of the channel | +### Interactions + +### Interaction Create + +Sent when a user in a guild uses a [Slash Command](#DOCS_INTERACTIONS_SLASH_COMMANDS/). Inner payload is an [Interaction](#DOCS_INTERACTIONS_SLASH_COMMANDS/interaction). + ## Get Gateway % GET /gateway > info diff --git a/docs/topics/OAuth2.md b/docs/topics/OAuth2.md index 7ad7848d4d..725b2eeaf4 100644 --- a/docs/topics/OAuth2.md +++ b/docs/topics/OAuth2.md @@ -9,7 +9,7 @@ The first step in implementing OAuth2 is [registering a developer application](# ###### OAuth2 URLs | URL | Description | -| ------------------------------------------- | ----------------------------------------------------------- | +|---------------------------------------------|-------------------------------------------------------------| | https://discord.com/api/oauth2/authorize | Base authorization URL | | https://discord.com/api/oauth2/token | Token URL | | https://discord.com/api/oauth2/token/revoke | [Token Revocation](https://tools.ietf.org/html/rfc7009) URL | @@ -21,27 +21,28 @@ The first step in implementing OAuth2 is [registering a developer application](# These are a list of all the OAuth2 scopes that Discord supports. Scopes that are behind a whitelist cannot be requested unless your application is on said whitelist, and may cause undocumented/error behavior in the OAuth2 flow if you request them from a user. -| Name | Description | -| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| bot | for oauth2 bots, this puts the bot in the user's selected guild by default | -| connections | allows [/users/@me/connections](#DOCS_RESOURCES_USER/get-user-connections) to return linked third-party accounts | -| email | enables [/users/@me](#DOCS_RESOURCES_USER/get-current-user) to return an `email` | -| identify | allows [/users/@me](#DOCS_RESOURCES_USER/get-current-user) without `email` | -| guilds | allows [/users/@me/guilds](#DOCS_RESOURCES_USER/get-current-user-guilds) to return basic information about all of a user's guilds | -| guilds.join | allows [/guilds/{guild.id}/members/{user.id}](#DOCS_RESOURCES_GUILD/add-guild-member) to be used for joining users to a guild | -| gdm.join | allows your app to [join users to a group dm](#DOCS_RESOURCES_CHANNEL/group-dm-add-recipient) | -| messages.read | for local rpc server api access, this allows you to read messages from all client channels (otherwise restricted to channels/guilds your app creates) | -| rpc | for local rpc server access, this allows you to control a user's local Discord client - whitelist only | -| rpc.api | for local rpc server api access, this allows you to access the API as the local user - whitelist only | -| rpc.notifications.read | for local rpc server api access, this allows you to receive notifications pushed out to the user - whitelist only | -| webhook.incoming | this generates a webhook that is returned in the oauth token response for authorization code grants | -| applications.builds.upload | allows your app to upload/update builds for a user's applications - whitelist only | -| applications.builds.read | allows your app to read build data for a user's applications | -| applications.store.update | allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications | -| applications.entitlements | allows your app to read entitlements for a user's applications | -| relationships.read | allows your app to know a user's friends and implicit relationships - whitelist only | -| activities.read | allows your app to fetch data from a user's "Now Playing/Recently Played" list - whitelist only | -| activities.write | allows your app to update a user's activity - whitelist only (NOT REQUIRED FOR [GAMESDK ACTIVITY MANAGER](#DOCS_GAME_SDK_ACTIVITIES/)) | +| Name | Description | +|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| bot | for oauth2 bots, this puts the bot in the user's selected guild by default | +| connections | allows [/users/@me/connections](#DOCS_RESOURCES_USER/get-user-connections) to return linked third-party accounts | +| email | enables [/users/@me](#DOCS_RESOURCES_USER/get-current-user) to return an `email` | +| identify | allows [/users/@me](#DOCS_RESOURCES_USER/get-current-user) without `email` | +| guilds | allows [/users/@me/guilds](#DOCS_RESOURCES_USER/get-current-user-guilds) to return basic information about all of a user's guilds | +| guilds.join | allows [/guilds/{guild.id}/members/{user.id}](#DOCS_RESOURCES_GUILD/add-guild-member) to be used for joining users to a guild | +| gdm.join | allows your app to [join users to a group dm](#DOCS_RESOURCES_CHANNEL/group-dm-add-recipient) | +| messages.read | for local rpc server api access, this allows you to read messages from all client channels (otherwise restricted to channels/guilds your app creates) | +| rpc | for local rpc server access, this allows you to control a user's local Discord client - whitelist only | +| rpc.api | for local rpc server api access, this allows you to access the API as the local user - whitelist only | +| rpc.notifications.read | for local rpc server api access, this allows you to receive notifications pushed out to the user - whitelist only | +| webhook.incoming | this generates a webhook that is returned in the oauth token response for authorization code grants | +| applications.builds.upload | allows your app to upload/update builds for a user's applications - whitelist only | +| applications.builds.read | allows your app to read build data for a user's applications | +| applications.store.update | allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications | +| applications.entitlements | allows your app to read entitlements for a user's applications | +| relationships.read | allows your app to know a user's friends and implicit relationships - whitelist only | +| activities.read | allows your app to fetch data from a user's "Now Playing/Recently Played" list - whitelist only | +| activities.write | allows your app to update a user's activity - whitelist only (NOT REQUIRED FOR [GAMESDK ACTIVITY MANAGER](#DOCS_GAME_SDK_ACTIVITIES/)) | +| applications.commands.update | allows your app to update [Slash Commands](#DOCS_INTERACTIONS_SLASH_COMMANDS/) via this bearer token | > info > `guilds.join` and `bot` require you to have a bot account linked to your application. Also, in order to add a user to a guild, your bot has to already belong to that guild. @@ -243,7 +244,7 @@ Bot authorization is a special server-less and callback-less OAuth2 flow that ma ###### Bot Auth Parameters | name | description | -| -------------------- | --------------------------------------------------------------------- | +|----------------------|-----------------------------------------------------------------------| | client_id | your app's client id | | scope | needs to include `bot` for the bot flow | | permissions | the [permissions](#DOCS_TOPICS_PERMISSIONS/) you're requesting | @@ -365,12 +366,12 @@ Any user that wishes to add your webhook to their channel will need to go throug ## Get Current Application Information % GET /oauth2/applications/@me -Returns the bot's OAuth2 application info. +Returns the bot's OAuth2 [application object](#DOCS_TOPICS_OAUTH2/application-object) without `flags`. -###### Response Structure +### Application Object | Field | Type | Description | -| ---------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +|------------------------|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------| | id | snowflake | the id of the app | | name | string | the name of the app | | icon | ?string | the icon hash of the app | @@ -386,6 +387,7 @@ Returns the bot's OAuth2 application info. | primary_sku_id? | snowflake | if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists | | slug? | string | if this application is a game sold on Discord, this field will be the URL slug that links to the store page | | cover_image? | string | if this application is a game sold on Discord, this field will be the hash of the image on store embeds | +| flags | int | the application's public flags | ###### Example Application Information diff --git a/images/command-with-groups-subcommands-parameters.png b/images/command-with-groups-subcommands-parameters.png new file mode 100644 index 0000000000..9c35a81e3d Binary files /dev/null and b/images/command-with-groups-subcommands-parameters.png differ diff --git a/images/command-with-groups-subcommands.png b/images/command-with-groups-subcommands.png new file mode 100644 index 0000000000..af77d45b2b Binary files /dev/null and b/images/command-with-groups-subcommands.png differ diff --git a/images/command.png b/images/command.png new file mode 100644 index 0000000000..cc0c23e7c6 Binary files /dev/null and b/images/command.png differ