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<Discord.ApplicationCommandOptionProperties>()
{
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<Discord.ApplicationCommandOptionProperties>()
{
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<RestGlobalCommand>`.
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
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,
/// The option type of the Slash command parameter, See <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptiontype">the discord docs</see>.
/// The base command model that belongs to an application. see <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommand"/>
/// </summary>
public interface IApplicationCommand : ISnowflakeEntity
{
/// <summary>
/// Gets the unique id of the command.
/// </summary>
ulong Id { get; }
/// <summary>
/// Gets the unique id of the parent application.
/// </summary>
ulong ApplicationId { get; }
/// <summary>
/// The name of the command.
/// </summary>
string Name { get; }
/// <summary>
/// The description of the command.
/// </summary>
string Description { get; }
/// <summary>
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters.
/// Represents data of an Interaction Command, see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondata"/>
/// </summary>
public interface IApplicationCommandInteractionData
/// Represents a option group for a command, see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondataoption"/>
/// </summary>
public interface IApplicationCommandInteractionDataOption
{
/// <summary>
/// The name of the parameter.
/// </summary>
string Name { get; }
/// <summary>
/// The value of the pair.
/// <note>
/// This objects type can be any one of the option types in <see cref="ApplicationCommandOptionType"/>
/// </note>
/// </summary>
object Value { get; }
/// <summary>
/// Present if this option is a group or subcommand.
/// Options for the <see cref="IApplicationCommand"/>, see <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoption"/>The docs</see>.
/// </summary>
public interface IApplicationCommandOption
{
/// <summary>
/// The type of this <see cref="IApplicationCommandOption"/>.
/// </summary>
ApplicationCommandOptionType Type { get; }
/// <summary>
/// The name of this command option, 1-32 character name.
/// </summary>
string Name { get; }
/// <summary>
/// The discription of this command option, 1-100 character description.
/// </summary>
string Description { get; }
/// <summary>
/// The first required option for the user to complete--only one option can be default.
/// </summary>
bool? Default { get; }
/// <summary>
/// If the parameter is required or optional, default is <see langword="false"/>.
/// </summary>
bool? Required { get; }
/// <summary>
/// Choices for string and int types for the user to pick from.
if (args.Content?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
if (args.Content?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
/// <param name="text">The text of the message to be sent</param>
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/></param>
/// <param name="embed">A <see cref="Embed"/> to send with this response</param>
/// <param name="Type">The type of response to this Interaction</param>
/// <param name="allowedMentions">The allowed mentions for this response</param>
/// <param name="options">The request options for this response</param>
/// <returns>
/// The <see cref="IMessage"/> sent as the response. If this is the first acknowledgement, it will return null;
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid</exception>
public async Task<IMessage> RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType Type = InteractionResponseType.ChannelMessageWithSource, AllowedMentions allowedMentions = null, RequestOptions options = null)
{
if (Type == InteractionResponseType.Pong)
throw new InvalidOperationException($"Cannot use {Type} on a send message function");
if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");
if (Discord.AlwaysAcknowledgeInteractions)
return await FollowupAsync();
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
// check that user flag and user Id list are exclusive, same with role flag and role Id list
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
{
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.