diff --git a/docs/guides/commands/application-commands.md b/docs/guides/commands/application-commands.md new file mode 100644 index 000000000..a1247417a --- /dev/null +++ b/docs/guides/commands/application-commands.md @@ -0,0 +1,216 @@ +# Application commands + +Application commands are a new feature thats still a work in progress, this guide will show you how to make the best of em. + + + + +## Getting started + +### Configuring + +There is a new configuration setting for your DiscordSocketClient called `AlwaysAcknowledgeInteractions`, It's default value is true. +Interactions work off of the Recieve -> Respond pipeline, meaning if you dont acknowledge the interaction within 3 seconds its gone forever. +With `AlwaysAcknowledgeInteractions` set to true, the client will automatically acknowledge the interaction as its recieved, +letting you wait up to 15 minutes before responding with a message. + +With `AlwaysAcknowledgeInteractions` set to false you will have to acknowledge the interaction yourself via the `InteractionCreated` event + +### Registering commands + +While there is no "easy" way to register command right now, in the future I plan to write a command service to help with that, but right now you have to use the rest +client to create your command: + +```cs +_client.Ready += RegisterCommands + +... + +private async Task RegisterCommands() +{ + // Creating a global command + var myGlobalCommand = await _client.Rest.CreateGlobalCommand(new Discord.SlashCommandCreationProperties() + { + Name = "example", + Description = "Runs the example command", + Options = new List() + { + new ApplicationCommandOptionProperties() + { + Name = "Example option", + Required = false, + Description = "Option Description", + Type = Discord.ApplicationCommandOptionType.String, + } + } + }); + + // Creating a guild command + var myGuildCommand = await _client.Rest.CreateGuildCommand(new Discord.SlashCommandCreationProperties() + { + Name = "guildExample", + Description = "Runs the guild example command", + Options = new List() + { + new ApplicationCommandOptionProperties() + { + Name = "Guild example option", + Required = false, + Description = "Guild option description", + Type = Discord.ApplicationCommandOptionType.String, + } + } + }, 1234567890); // <- the guild id +} +``` +CreateGuildCommand returns a `RestGuildCommand` class which can be used to modify/delete your command on the fly, it also contains details about your command. +CreateGlobalCOmmand returns a `RestGlobalCommand` class which can be used to modify/delete your command on the fly, it also contains details about your command. + +### Getting a list of all your commands +You can fetch a list of all your global commands via rest: +```cs +var commands = _client.Rest.GetGlobalApplicationCommands(); +``` +This returns a `IReadOnlyCollection`. + +You can also fetch guild specific commands: +```cs +var commands = _client.Rest.GetGuildApplicationCommands(1234567890) +``` +This returns all the application commands in that guild. + +### Responding + +First thing we want to do is listen to the `InteractionCreated` event. This event is fired when a socket interaction is recieved via the gateway, It looks somthing like this +```cs +_client.InteractionCreated += MyEventHandler; + +... + +private async Task MyEventHandler(SocketInteraction arg) +{ + // handle the interaction here +} +``` + +A socket interaction is made up of these properties and methods: + +| Name | Description | +|--------|--------------| +| Guild | The `SocketGuild` this interaction was used in | +| Channel | The `SocketTextChannel` this interaction was used in | +| Member | The `SocketGuildUser` that executed the interaction | +| Type | The [InteractionType](https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype) of this interaction | +| Data | The `SocketInteractionData` associated with this interaction | +| Token | The token used to respond to this interaction | +| Version | The version of this interaction | +| CreatedAt | The time this interaction was created | +| IsValidToken | Whether or not the token to respond to this interaction is still valid | +| RespondAsync | Responds to the interaction | +| FollowupAsync | Sends a followup message to the interaction | + + + +#### Whats the difference between `FollowupAsync` and `RespondAsync`? +RespondAsync is the initial responce to the interaction, its used to "capture" the interaction, while followup is used to send more messages to the interaction. +Basically, you want to first use `RespondAsync` to acknowledge the interaction, then if you need to send anything else regarding that interaction you would use `FollowupAsync` +If you have `AlwaysAcknowledgeInteractions` set to true in your client config then it will automatically acknowledge the interaction without sending a message, +in this case you can use either or to respond. + +#### Example ping pong command +```cs +_client.InteractionCreated += MyEventHandler; +_client.Ready += CreateCommands + +... + +private async Task CreateCommands() +{ + await _client.Rest.CreateGlobalCommand(new Discord.SlashCommandCreationProperties() + { + Name = "ping", + Description = "ping for a pong!", + }); +} + +private async Task MyEventHandler(SocketInteraction arg) +{ + switch(arg.Type) // We want to check the type of this interaction + { + case InteractionType.ApplicationCommand: // If it is a command + await MySlashCommandHandler(arg); // Handle the command somewhere + break; + default: // We dont support it + Console.WriteLine("Unsupported interaction type: " + arg.Type); + break; + } +} + +private async Task MySlashCommandHandler(SocketInteraction arg) +{ + switch(arg.Name) + { + case "ping": + await arg.RespondAsync("Pong!"); + break; + } +} +``` + +#### Example hug command +```cs +_client.InteractionCreated += MyEventHandler; +_client.Ready += CreateCommands; + +... + +private async Task CreateCommands() +{ + await _client.Rest.CreateGlobalCommand(new Discord.SlashCommandCreationProperties() + { + Name = "hug", + Description = "Hugs a user!", + Options = new List() + { + new ApplicationCommandOptionProperties() + { + Name = "User", + Required = true, + Description = "The user to hug", + Type = Discord.ApplicationCommandOptionType.User, + } + } + }); +} + +private async Task MyEventHandler(SocketInteraction arg) +{ + switch(arg.Type) // We want to check the type of this interaction + { + case InteractionType.ApplicationCommand: // If it is a command + await MySlashCommandHandler(arg); // Handle the command somewhere + break; + default: // We dont support it + Console.WriteLine("Unsupported interaction type: " + arg.Type); + break; + } +} + +private async Task MySlashCommandHandler(SocketInteraction arg) +{ + switch(arg.Name) + { + case "hug": + // Get the user argument + var option = arg.Data.Options.First(x => x.Name == "user"); + // We know that the options value must be a user + if(option.Value is SocketGuildUser user) + { + await arg.RespondAsync($"Hugged {user.Mention}"); + } + break; + } +} +``` + +