diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs
new file mode 100644
index 000000000..9b4e435cf
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Represents a for making slash commands.
+ ///
+ public class ApplicationCommandOptionProperties
+ {
+ private string _name;
+ private string _description;
+
+ ///
+ /// The name of this option.
+ ///
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ if (value?.Length > 32)
+ throw new ArgumentException("Name length must be less than or equal to 32");
+ _name = value;
+ }
+ }
+
+ ///
+ /// The description of this option.
+ ///
+ public string Description
+ {
+ get => _description;
+ set
+ {
+ if (value?.Length > 100)
+ throw new ArgumentException("Name length must be less than or equal to 32");
+ _description = value;
+ }
+ }
+
+ ///
+ /// The type of this option.
+ ///
+ public ApplicationCommandOptionType Type { get; set; }
+
+ ///
+ /// The first required option for the user to complete. only one option can be default.
+ ///
+ public bool? Default { get; set; }
+
+ ///
+ /// if this option is required for this command, otherwise .
+ ///
+ public bool? Required { get; set; }
+
+ ///
+ /// choices for string and int types for the user to pick from
+ ///
+ public List Choices { get; set; }
+
+ ///
+ /// If the option is a subcommand or subcommand group type, this nested options will be the parameters.
+ ///
+ public List Options { get; set; }
+
+
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs
new file mode 100644
index 000000000..99e3f66d6
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// Represents a choice for a . This class is used when making new commands
+ ///
+ public class ApplicationCommandOptionChoiceProperties
+ {
+ private string _name;
+ private object _value;
+ ///
+ /// The name of this choice
+ ///
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ if(value?.Length > 100)
+ throw new ArgumentException("Name length must be less than or equal to 100");
+ _name = value;
+ }
+ }
+
+ // Note: discord allows strings & ints as values. how should that be handled?
+ // should we make this an object and then just type check it?
+ ///
+ /// The value of this choice
+ ///
+ public object Value
+ {
+ get => _value;
+ set
+ {
+ if(value != null)
+ {
+ if(!(value is int) && !(value is string))
+ throw new ArgumentException("The value of a choice must be a string or int!");
+ }
+ _value = value;
+ }
+ }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
index 990fe2e5e..594a0f370 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
@@ -11,41 +11,20 @@ namespace Discord
///
public class ApplicationCommandProperties
{
- private string _name { get; set; }
- private string _description { get; set; }
-
///
/// Gets or sets the name of this command.
///
- public string Name
- {
- get => _name;
- set
- {
- if(value.Length > 32)
- throw new ArgumentException("Name length must be less than or equal to 32");
- _name = value;
- }
- }
+ public Optional Name { get; set; }
///
/// Gets or sets the discription of this command.
///
- public string Description
- {
- get => _description;
- set
- {
- if (value.Length > 100)
- throw new ArgumentException("Description length must be less than or equal to 100");
- _description = value;
- }
- }
+ public Optional Description { get; set; }
///
/// Gets or sets the options for this command.
///
- public Optional> Options { get; set; }
+ public Optional> Options { get; set; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandInteractionDataOption.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandInteractionDataOption.cs
index 871eb4ae6..8ea8378e7 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandInteractionDataOption.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandInteractionDataOption.cs
@@ -22,7 +22,7 @@ namespace Discord
/// This objects type can be any one of the option types in
///
///
- object? Value { get; }
+ object Value { get; }
///
/// Present if this option is a group or subcommand.
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
index 960d1571d..25e3c7dfc 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
@@ -42,7 +42,7 @@ namespace Discord
IReadOnlyCollection? Choices { get; }
///
- /// if the option is a subcommand or subcommand group type, this nested options will be the parameters.
+ /// If the option is a subcommand or subcommand group type, this nested options will be the parameters.
///
IReadOnlyCollection? Options { get; }
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
index 3b4c7c249..1f0540656 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
@@ -19,7 +19,7 @@ namespace Discord
///
/// value of the choice.
///
- string Value { get; }
+ object Value { get; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/SlashCommandCreationProperties.cs b/src/Discord.Net.Core/Entities/Interactions/SlashCommandCreationProperties.cs
new file mode 100644
index 000000000..0facc02a1
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/SlashCommandCreationProperties.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ ///
+ /// A class used to create slash commands
+ ///
+ public class SlashCommandCreationProperties
+ {
+ ///
+ /// The name of this command.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// The discription of this command.
+ ///
+ public string Description { get; set; }
+
+
+ ///
+ /// Gets or sets the options for this command.
+ ///
+ public Optional> Options { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs b/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs
index ad7748594..789c5549d 100644
--- a/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs
+++ b/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs
@@ -53,5 +53,28 @@ namespace Discord.API
this.Type = cmd.Type;
this.Description = cmd.Description;
}
+ public ApplicationCommandOption(Discord.ApplicationCommandOptionProperties option)
+ {
+ this.Choices = option.Choices != null
+ ? option.Choices.Select(x => new ApplicationCommandOptionChoice()
+ {
+ Name = x.Name,
+ Value = x.Value
+ }).ToArray()
+ : Optional.Unspecified;
+
+ this.Options = option.Options != null
+ ? option.Options.Select(x => new ApplicationCommandOption(x)).ToArray()
+ : Optional.Unspecified;
+
+ this.Required = option.Required.Value;
+ this.Default = option.Default.HasValue
+ ? option.Default.Value
+ : Optional.Unspecified;
+
+ this.Name = option.Name;
+ this.Type = option.Type;
+ this.Description = option.Description;
+ }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/ApplicationCommandOptionChoice.cs b/src/Discord.Net.Rest/API/Common/ApplicationCommandOptionChoice.cs
index 00179dde1..b847fceba 100644
--- a/src/Discord.Net.Rest/API/Common/ApplicationCommandOptionChoice.cs
+++ b/src/Discord.Net.Rest/API/Common/ApplicationCommandOptionChoice.cs
@@ -13,6 +13,6 @@ namespace Discord.API
public string Name { get; set; }
[JsonProperty("value")]
- public string Value { get; set; }
+ public object Value { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Rest/ApplicationCommandParams.cs b/src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs
similarity index 74%
rename from src/Discord.Net.Rest/API/Rest/ApplicationCommandParams.cs
rename to src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs
index a674093c5..4ab6f5a9d 100644
--- a/src/Discord.Net.Rest/API/Rest/ApplicationCommandParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Discord.API.Rest
{
- internal class ApplicationCommandParams
+ internal class CreateApplicationCommandParams
{
[JsonProperty("name")]
public string Name { get; set; }
@@ -19,8 +19,8 @@ namespace Discord.API.Rest
[JsonProperty("options")]
public Optional Options { get; set; }
- public ApplicationCommandParams() { }
- public ApplicationCommandParams(string name, string description, ApplicationCommandOption[] options = null)
+ public CreateApplicationCommandParams() { }
+ public CreateApplicationCommandParams(string name, string description, ApplicationCommandOption[] options = null)
{
this.Name = name;
this.Description = description;
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyApplicationCommandParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyApplicationCommandParams.cs
new file mode 100644
index 000000000..141932955
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/ModifyApplicationCommandParams.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.API.Rest
+{
+ internal class ModifyApplicationCommandParams
+ {
+ [JsonProperty("name")]
+ public Optional Name { get; set; }
+
+ [JsonProperty("description")]
+ public Optional Description { get; set; }
+
+ [JsonProperty("options")]
+ public Optional Options { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/ClientHelper.cs b/src/Discord.Net.Rest/ClientHelper.cs
index d39fcd7a2..4666d645d 100644
--- a/src/Discord.Net.Rest/ClientHelper.cs
+++ b/src/Discord.Net.Rest/ClientHelper.cs
@@ -202,23 +202,23 @@ namespace Discord.Rest
};
}
- public static async Task GetGlobalApplicationCommands(BaseDiscordClient client, RequestOptions options)
+ public static async Task> GetGlobalApplicationCommands(BaseDiscordClient client, RequestOptions options)
{
var response = await client.ApiClient.GetGlobalApplicationCommandsAsync(options).ConfigureAwait(false);
if (!response.Any())
- return null;
+ return new RestGlobalCommand[0];
return response.Select(x => RestGlobalCommand.Create(client, x)).ToArray();
}
- public static async Task GetGuildApplicationCommands(BaseDiscordClient client, ulong guildId, RequestOptions options)
+ public static async Task> GetGuildApplicationCommands(BaseDiscordClient client, ulong guildId, RequestOptions options)
{
var response = await client.ApiClient.GetGuildApplicationCommandAsync(guildId, options).ConfigureAwait(false);
if (!response.Any())
- return null;
+ return new RestGuildCommand[0].ToImmutableArray();
- return response.Select(x => RestGuildCommand.Create(client, x, guildId)).ToArray();
+ return response.Select(x => RestGuildCommand.Create(client, x, guildId)).ToImmutableArray();
}
}
}
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index d9da6503a..af33360f4 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -46,7 +46,7 @@ namespace Discord.API
internal IRestClient RestClient { get; private set; }
internal ulong? CurrentUserId { get; set; }
public RateLimitPrecision RateLimitPrecision { get; private set; }
- internal bool UseSystemClock { get; set; }
+ internal bool UseSystemClock { get; set; }
internal JsonSerializer Serializer => _serializer;
/// Unknown OAuth token type.
@@ -58,7 +58,7 @@ namespace Discord.API
DefaultRetryMode = defaultRetryMode;
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
RateLimitPrecision = rateLimitPrecision;
- UseSystemClock = useSystemClock;
+ UseSystemClock = useSystemClock;
RequestQueue = new RequestQueue();
_stateLock = new SemaphoreSlim(1, 1);
@@ -262,8 +262,8 @@ namespace Discord.API
CheckState();
if (request.Options.RetryMode == null)
request.Options.RetryMode = DefaultRetryMode;
- if (request.Options.UseSystemClock == null)
- request.Options.UseSystemClock = UseSystemClock;
+ if (request.Options.UseSystemClock == null)
+ request.Options.UseSystemClock = UseSystemClock;
var stopwatch = Stopwatch.StartNew();
var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false);
@@ -787,9 +787,14 @@ namespace Discord.API
//Interactions
public async Task GetGlobalApplicationCommandsAsync(RequestOptions options = null)
- => await SendAsync("GET", $"applications/{this.CurrentUserId}/commands", options: options).ConfigureAwait(false);
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ return await SendAsync("GET", () => $"applications/{this.CurrentUserId}/commands", new BucketIds(), options: options).ConfigureAwait(false);
+ }
- public async Task CreateGlobalApplicationCommandAsync(ApplicationCommandParams command, RequestOptions options = null)
+
+ public async Task CreateGlobalApplicationCommandAsync(CreateApplicationCommandParams command, RequestOptions options = null)
{
Preconditions.NotNull(command, nameof(command));
Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
@@ -797,24 +802,45 @@ namespace Discord.API
Preconditions.AtMost(command.Description.Length, 100, nameof(command.Description));
Preconditions.AtLeast(command.Description.Length, 1, nameof(command.Description));
- return await SendJsonAsync("POST", $"applications/{this.CurrentUserId}/commands", command, options: options).ConfigureAwait(false);
+ options = RequestOptions.CreateOrClone(options);
+
+ return await SendJsonAsync("POST", () => $"applications/{this.CurrentUserId}/commands", command, new BucketIds(), options: options).ConfigureAwait(false);
}
- public async Task ModifyGlobalApplicationCommandAsync(ApplicationCommandParams command, ulong commandId, RequestOptions options = null)
+ public async Task ModifyGlobalApplicationCommandAsync(ModifyApplicationCommandParams command, ulong commandId, RequestOptions options = null)
{
Preconditions.NotNull(command, nameof(command));
- Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
- Preconditions.AtLeast(command.Name.Length, 3, nameof(command.Name));
- Preconditions.AtMost(command.Description.Length, 100, nameof(command.Description));
- Preconditions.AtLeast(command.Description.Length, 1, nameof(command.Description));
- return await SendJsonAsync("PATCH", $"applications/{this.CurrentUserId}/commands/{commandId}", command, options: options).ConfigureAwait(false);
+ if (command.Name.IsSpecified)
+ {
+ Preconditions.AtMost(command.Name.Value.Length, 32, nameof(command.Name));
+ Preconditions.AtLeast(command.Name.Value.Length, 3, nameof(command.Name));
+ }
+ if (command.Description.IsSpecified)
+ {
+ Preconditions.AtMost(command.Description.Value.Length, 100, nameof(command.Description));
+ Preconditions.AtLeast(command.Description.Value.Length, 1, nameof(command.Description));
+ }
+
+ options = RequestOptions.CreateOrClone(options);
+
+ return await SendJsonAsync("PATCH", () => $"applications/{this.CurrentUserId}/commands/{commandId}", command, new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task DeleteGlobalApplicationCommandAsync(ulong commandId, RequestOptions options = null)
- => await SendAsync("DELETE", $"applications/{this.CurrentUserId}/commands/{commandId}", options: options).ConfigureAwait(false);
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ await SendAsync("DELETE", () => $"applications/{this.CurrentUserId}/commands/{commandId}", new BucketIds(), options: options).ConfigureAwait(false);
+ }
public async Task GetGuildApplicationCommandAsync(ulong guildId, RequestOptions options = null)
- => await SendAsync("GET", $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", options: options).ConfigureAwait(false);
- public async Task CreateGuildApplicationCommandAsync(ApplicationCommandParams command, ulong guildId, RequestOptions options = null)
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ var bucket = new BucketIds(guildId: guildId);
+
+ return await SendAsync("GET", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", bucket, options: options).ConfigureAwait(false);
+ }
+ public async Task CreateGuildApplicationCommandAsync(CreateApplicationCommandParams command, ulong guildId, RequestOptions options = null)
{
Preconditions.NotNull(command, nameof(command));
Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
@@ -822,20 +848,41 @@ namespace Discord.API
Preconditions.AtMost(command.Description.Length, 100, nameof(command.Description));
Preconditions.AtLeast(command.Description.Length, 1, nameof(command.Description));
- return await SendJsonAsync("POST", $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, options: options).ConfigureAwait(false);
+ options = RequestOptions.CreateOrClone(options);
+
+ var bucket = new BucketIds(guildId: guildId);
+
+ return await SendJsonAsync("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options).ConfigureAwait(false);
}
- public async Task ModifyGuildApplicationCommandAsync(ApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
+ public async Task ModifyGuildApplicationCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
Preconditions.NotNull(command, nameof(command));
- Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
- Preconditions.AtLeast(command.Name.Length, 3, nameof(command.Name));
- Preconditions.AtMost(command.Description.Length, 100, nameof(command.Description));
- Preconditions.AtLeast(command.Description.Length, 1, nameof(command.Description));
- return await SendJsonAsync("PATCH", $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, options: options).ConfigureAwait(false);
+ if (command.Name.IsSpecified)
+ {
+ Preconditions.AtMost(command.Name.Value.Length, 32, nameof(command.Name));
+ Preconditions.AtLeast(command.Name.Value.Length, 3, nameof(command.Name));
+ }
+ if (command.Description.IsSpecified)
+ {
+ Preconditions.AtMost(command.Description.Value.Length, 100, nameof(command.Description));
+ Preconditions.AtLeast(command.Description.Value.Length, 1, nameof(command.Description));
+ }
+
+ options = RequestOptions.CreateOrClone(options);
+
+ var bucket = new BucketIds(guildId: guildId);
+
+ return await SendJsonAsync("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options).ConfigureAwait(false);
}
public async Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId, RequestOptions options = null)
- => await SendAsync("DELETE", $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", options: options).ConfigureAwait(false);
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ var bucket = new BucketIds(guildId: guildId);
+
+ await SendAsync("DELETE", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", bucket, options: options).ConfigureAwait(false);
+ }
//Interaction Responses
public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null)
@@ -845,12 +892,20 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
- await SendJsonAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response, new BucketIds(), options: options);
+ await SendJsonAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response, new BucketIds(), options: options);
}
public async Task ModifyInteractionResponse(ModifyInteractionResponseParams args, string interactionToken, RequestOptions options = null)
- => await SendJsonAsync("POST", $"webhooks/{this.CurrentUserId}/{interactionToken}/messages/@original", args, options: options);
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ await SendJsonAsync("POST", () => $"webhooks/{this.CurrentUserId}/{interactionToken}/messages/@original", args, new BucketIds(), options: options);
+ }
public async Task DeleteInteractionResponse(string interactionToken, RequestOptions options = null)
- => await SendAsync("DELETE", $"webhooks/{this.CurrentUserId}/{interactionToken}/messages/@original", options: options);
+ {
+ options = RequestOptions.CreateOrClone(options);
+
+ await SendAsync("DELETE", () => $"webhooks/{this.CurrentUserId}/{interactionToken}/messages/@original", new BucketIds(), options: options);
+ }
public async Task CreateInteractionFollowupMessage(CreateWebhookMessageParams args, string token, RequestOptions options = null)
{
@@ -862,7 +917,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
- return await SendJsonAsync("POST", $"webhooks/{CurrentUserId}/{token}?wait=true", args, options: options).ConfigureAwait(false);
+ return await SendJsonAsync("POST", () => $"webhooks/{CurrentUserId}/{token}?wait=true", args, new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task ModifyInteractionFollowupMessage(CreateWebhookMessageParams args, ulong id, string token, RequestOptions options = null)
@@ -875,7 +930,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
- return await SendJsonAsync("PATCH", $"webhooks/{CurrentUserId}/{token}/messages/{id}", args, options: options).ConfigureAwait(false);
+ return await SendJsonAsync("PATCH", () => $"webhooks/{CurrentUserId}/{token}/messages/{id}", args, new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task DeleteInteractionFollowupMessage(ulong id, string token, RequestOptions options = null)
@@ -884,7 +939,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
- await SendAsync("DELETE", $"webhooks/{CurrentUserId}/{token}/messages/{id}", options: options).ConfigureAwait(false);
+ await SendAsync("DELETE", () => $"webhooks/{CurrentUserId}/{token}/messages/{id}", new BucketIds(), options: options).ConfigureAwait(false);
}
//Guilds
diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs
index 79fb1a5c2..d62a83d2d 100644
--- a/src/Discord.Net.Rest/DiscordRestClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestClient.cs
@@ -107,13 +107,17 @@ namespace Discord.Rest
=> ClientHelper.GetVoiceRegionAsync(this, id, options);
public Task GetWebhookAsync(ulong id, RequestOptions options = null)
=> ClientHelper.GetWebhookAsync(this, id, options);
- public Task CreateGobalCommand(Action func, RequestOptions options = null)
+ public Task CreateGlobalCommand(SlashCommandCreationProperties properties, RequestOptions options = null)
+ => InteractionHelper.CreateGlobalCommand(this, properties, options);
+ public Task CreateGlobalCommand(Action func, RequestOptions options = null)
=> InteractionHelper.CreateGlobalCommand(this, func, options);
- public Task CreateGuildCommand(Action func, ulong guildId, RequestOptions options = null)
+ public Task CreateGuildCommand(SlashCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
+ => InteractionHelper.CreateGuildCommand(this, guildId, properties, options);
+ public Task CreateGuildCommand(Action func, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildCommand(this, guildId, func, options);
- public Task GetGlobalApplicationCommands(RequestOptions options = null)
+ public Task> GetGlobalApplicationCommands(RequestOptions options = null)
=> ClientHelper.GetGlobalApplicationCommands(this, options);
- public Task GetGuildApplicationCommands(ulong guildId, RequestOptions options = null)
+ public Task> GetGuildApplicationCommands(ulong guildId, RequestOptions options = null)
=> ClientHelper.GetGuildApplicationCommands(this, guildId, options);
//IDiscordClient
diff --git a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
index 341087cb4..46d92f815 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
@@ -21,30 +21,35 @@ namespace Discord.Rest
// Global commands
internal static async Task CreateGlobalCommand(BaseDiscordClient client,
- Action func, RequestOptions options = null)
+ Action func, RequestOptions options = null)
{
- var args = new ApplicationCommandProperties();
+ var args = new SlashCommandCreationProperties();
func(args);
-
+ return await CreateGlobalCommand(client, args, options).ConfigureAwait(false);
+ }
+ internal static async Task CreateGlobalCommand(BaseDiscordClient client,
+ SlashCommandCreationProperties args, RequestOptions options = null)
+ {
if (args.Options.IsSpecified)
{
if (args.Options.Value.Count > 10)
throw new ArgumentException("Option count must be 10 or less");
}
- var model = new ApplicationCommandParams()
+
+
+ var model = new CreateApplicationCommandParams()
{
Name = args.Name,
Description = args.Description,
Options = args.Options.IsSpecified
- ? args.Options.Value.Select(x => new ApplicationCommandOption(x)).ToArray()
- : Optional.Unspecified
+ ? args.Options.Value.Select(x => new Discord.API.ApplicationCommandOption(x)).ToArray()
+ : Optional.Unspecified
};
var cmd = await client.ApiClient.CreateGlobalApplicationCommandAsync(model, options).ConfigureAwait(false);
return RestGlobalCommand.Create(client, cmd);
}
-
internal static async Task ModifyGlobalCommand(BaseDiscordClient client, RestGlobalCommand command,
Action func, RequestOptions options = null)
{
@@ -57,13 +62,13 @@ namespace Discord.Rest
throw new ArgumentException("Option count must be 10 or less");
}
- var model = new Discord.API.Rest.ApplicationCommandParams()
+ var model = new Discord.API.Rest.ModifyApplicationCommandParams()
{
Name = args.Name,
Description = args.Description,
Options = args.Options.IsSpecified
- ? args.Options.Value.Select(x => new ApplicationCommandOption(x)).ToArray()
- : Optional.Unspecified
+ ? args.Options.Value.Select(x => new Discord.API.ApplicationCommandOption(x)).ToArray()
+ : Optional.Unspecified
};
var msg = await client.ApiClient.ModifyGlobalApplicationCommandAsync(model, command.Id, options).ConfigureAwait(false);
@@ -82,30 +87,44 @@ namespace Discord.Rest
// Guild Commands
internal static async Task CreateGuildCommand(BaseDiscordClient client, ulong guildId,
- Action func, RequestOptions options = null)
+ Action func, RequestOptions options = null)
{
- var args = new ApplicationCommandProperties();
+ var args = new SlashCommandCreationProperties();
func(args);
+ return await CreateGuildCommand(client, guildId, args, options).ConfigureAwait(false);
+ }
+ internal static async Task CreateGuildCommand(BaseDiscordClient client, ulong guildId,
+ SlashCommandCreationProperties args, RequestOptions options = null)
+ {
+ Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
+ Preconditions.NotNullOrEmpty(args.Description, nameof(args.Description));
+
+
if (args.Options.IsSpecified)
{
if (args.Options.Value.Count > 10)
throw new ArgumentException("Option count must be 10 or less");
+
+ foreach(var item in args.Options.Value)
+ {
+ Preconditions.NotNullOrEmpty(item.Name, nameof(item.Name));
+ Preconditions.NotNullOrEmpty(item.Description, nameof(item.Description));
+ }
}
- var model = new ApplicationCommandParams()
+ var model = new CreateApplicationCommandParams()
{
Name = args.Name,
Description = args.Description,
Options = args.Options.IsSpecified
- ? args.Options.Value.Select(x => new ApplicationCommandOption(x)).ToArray()
- : Optional.Unspecified
+ ? args.Options.Value.Select(x => new Discord.API.ApplicationCommandOption(x)).ToArray()
+ : Optional.Unspecified
};
var cmd = await client.ApiClient.CreateGuildApplicationCommandAsync(model, guildId, options).ConfigureAwait(false);
return RestGuildCommand.Create(client, cmd, guildId);
}
-
internal static async Task ModifyGuildCommand(BaseDiscordClient client, RestGuildCommand command,
Action func, RequestOptions options = null)
{
@@ -118,16 +137,16 @@ namespace Discord.Rest
throw new ArgumentException("Option count must be 10 or less");
}
- var model = new Discord.API.Rest.ApplicationCommandParams()
+ var model = new Discord.API.Rest.ModifyApplicationCommandParams()
{
Name = args.Name,
Description = args.Description,
Options = args.Options.IsSpecified
- ? args.Options.Value.Select(x => new ApplicationCommandOption(x)).ToArray()
- : Optional.Unspecified
+ ? args.Options.Value.Select(x => new Discord.API.ApplicationCommandOption(x)).ToArray()
+ : Optional.Unspecified
};
- var msg = await client.ApiClient.ModifyGuildApplicationCommandAsync(model, command.Id, command.GuildId, options).ConfigureAwait(false);
+ var msg = await client.ApiClient.ModifyGuildApplicationCommandAsync(model, command.GuildId, command.Id, options).ConfigureAwait(false);
command.Update(msg);
return command;
}
@@ -137,7 +156,7 @@ namespace Discord.Rest
Preconditions.NotNull(command, nameof(command));
Preconditions.NotEqual(command.Id, 0, nameof(command.Id));
- await client.ApiClient.DeleteGuildApplicationCommandAsync(command.Id, command.GuildId, options).ConfigureAwait(false);
+ await client.ApiClient.DeleteGuildApplicationCommandAsync(command.GuildId, command.Id, options).ConfigureAwait(false);
}
}
}
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs
index c52d69619..5e0852b29 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs
@@ -19,9 +19,9 @@ namespace Discord.Rest
public string Description { get; private set; }
- public IReadOnlyCollection Options { get; private set; }
+ public IReadOnlyCollection Options { get; private set; }
- public RestApplicationCommandType CommandType { get; private set; }
+ public RestApplicationCommandType CommandType { get; internal set; }
public DateTimeOffset CreatedAt
=> SnowflakeUtils.FromSnowflake(this.Id);
@@ -47,12 +47,15 @@ namespace Discord.Rest
{
this.ApplicationId = model.ApplicationId;
this.Name = model.Name;
+ this.Description = model.Description;
this.Options = model.Options.IsSpecified
? model.Options.Value.Select(x => RestApplicationCommandOption.Create(x)).ToImmutableArray()
: null;
}
+ IReadOnlyCollection IApplicationCommand.Options => Options;
+
public virtual Task DeleteAsync(RequestOptions options = null) => throw new NotImplementedException();
}
}
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandChoice.cs b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandChoice.cs
index 211d2fe12..f9ab50f60 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandChoice.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandChoice.cs
@@ -11,7 +11,7 @@ namespace Discord.Rest
{
public string Name { get; }
- public string Value { get; }
+ public object Value { get; }
internal RestApplicationCommandChoice(Model model)
{
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs
index 946319112..120fe0c29 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs
@@ -20,9 +20,9 @@ namespace Discord.Rest
public bool? Required { get; private set; }
- public IReadOnlyCollection Choices { get; private set; }
+ public IReadOnlyCollection Choices { get; private set; }
- public IReadOnlyCollection Options { get; private set; }
+ public IReadOnlyCollection Options { get; private set; }
internal RestApplicationCommandOption() { }
@@ -53,5 +53,8 @@ namespace Discord.Rest
? model.Choices.Value.Select(x => new RestApplicationCommandChoice(x)).ToImmutableArray()
: null;
}
+
+ IReadOnlyCollection IApplicationCommandOption.Options => Options;
+ IReadOnlyCollection IApplicationCommandOption.Choices => Choices;
}
}
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestGlobalCommand.cs b/src/Discord.Net.Rest/Entities/Interactions/RestGlobalCommand.cs
index d44e6819d..5223c069c 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestGlobalCommand.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestGlobalCommand.cs
@@ -15,7 +15,7 @@ namespace Discord.Rest
internal RestGlobalCommand(BaseDiscordClient client, ulong id)
: base(client, id)
{
-
+ this.CommandType = RestApplicationCommandType.GlobalCommand;
}
internal static RestGlobalCommand Create(BaseDiscordClient client, Model model)
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestGuildCommand.cs b/src/Discord.Net.Rest/Entities/Interactions/RestGuildCommand.cs
index 5bf386051..73737dcd2 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestGuildCommand.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestGuildCommand.cs
@@ -13,6 +13,7 @@ namespace Discord.Rest
internal RestGuildCommand(BaseDiscordClient client, ulong id, ulong guildId)
: base(client, id)
{
+ this.CommandType = RestApplicationCommandType.GuildCommand;
this.GuildId = guildId;
}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/ApplicationCommandCreatedUpdatedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/ApplicationCommandCreatedUpdatedEvent.cs
new file mode 100644
index 000000000..94b3470e7
--- /dev/null
+++ b/src/Discord.Net.WebSocket/API/Gateway/ApplicationCommandCreatedUpdatedEvent.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.API.Gateway
+{
+ internal class ApplicationCommandCreatedUpdatedEvent
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("application_id")]
+ public ulong ApplicationId { get; set; }
+
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
+
+ [JsonProperty("options")]
+ public List Options { get; set; }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
index 7b8a245cc..af1a9f147 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
@@ -45,7 +45,8 @@ namespace Discord.WebSocket
///
///
- public event Func ChannelDestroyed {
+ public event Func ChannelDestroyed
+ {
add { _channelDestroyedEvent.Add(value); }
remove { _channelDestroyedEvent.Remove(value); }
}
@@ -67,7 +68,8 @@ namespace Discord.WebSocket
///
///
- public event Func ChannelUpdated {
+ public event Func ChannelUpdated
+ {
add { _channelUpdatedEvent.Add(value); }
remove { _channelUpdatedEvent.Remove(value); }
}
@@ -92,7 +94,8 @@ namespace Discord.WebSocket
///
///
- public event Func MessageReceived {
+ public event Func MessageReceived
+ {
add { _messageReceivedEvent.Add(value); }
remove { _messageReceivedEvent.Remove(value); }
}
@@ -124,7 +127,8 @@ namespace Discord.WebSocket
///
///
- public event Func, ISocketMessageChannel, Task> MessageDeleted {
+ public event Func, ISocketMessageChannel, Task> MessageDeleted
+ {
add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); }
}
@@ -182,7 +186,8 @@ namespace Discord.WebSocket
/// parameter.
///
///
- public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated {
+ public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated
+ {
add { _messageUpdatedEvent.Add(value); }
remove { _messageUpdatedEvent.Remove(value); }
}
@@ -217,19 +222,22 @@ namespace Discord.WebSocket
///
///
- public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded {
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded
+ {
add { _reactionAddedEvent.Add(value); }
remove { _reactionAddedEvent.Remove(value); }
}
internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
/// Fired when a reaction is removed from a message.
- public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved {
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved
+ {
add { _reactionRemovedEvent.Add(value); }
remove { _reactionRemovedEvent.Remove(value); }
}
internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
/// Fired when all reactions to a message are cleared.
- public event Func, ISocketMessageChannel, Task> ReactionsCleared {
+ public event Func, ISocketMessageChannel, Task> ReactionsCleared
+ {
add { _reactionsClearedEvent.Add(value); }
remove { _reactionsClearedEvent.Remove(value); }
}
@@ -259,19 +267,22 @@ namespace Discord.WebSocket
//Roles
/// Fired when a role is created.
- public event Func RoleCreated {
+ public event Func RoleCreated
+ {
add { _roleCreatedEvent.Add(value); }
remove { _roleCreatedEvent.Remove(value); }
}
internal readonly AsyncEvent> _roleCreatedEvent = new AsyncEvent>();
/// Fired when a role is deleted.
- public event Func RoleDeleted {
+ public event Func RoleDeleted
+ {
add { _roleDeletedEvent.Add(value); }
remove { _roleDeletedEvent.Remove(value); }
}
internal readonly AsyncEvent> _roleDeletedEvent = new AsyncEvent>();
/// Fired when a role is updated.
- public event Func RoleUpdated {
+ public event Func RoleUpdated
+ {
add { _roleUpdatedEvent.Add(value); }
remove { _roleUpdatedEvent.Remove(value); }
}
@@ -279,37 +290,43 @@ namespace Discord.WebSocket
//Guilds
/// Fired when the connected account joins a guild.
- public event Func JoinedGuild {
+ public event Func JoinedGuild
+ {
add { _joinedGuildEvent.Add(value); }
remove { _joinedGuildEvent.Remove(value); }
}
internal readonly AsyncEvent> _joinedGuildEvent = new AsyncEvent>();
/// Fired when the connected account leaves a guild.
- public event Func LeftGuild {
+ public event Func LeftGuild
+ {
add { _leftGuildEvent.Add(value); }
remove { _leftGuildEvent.Remove(value); }
}
internal readonly AsyncEvent> _leftGuildEvent = new AsyncEvent>();
/// Fired when a guild becomes available.
- public event Func GuildAvailable {
+ public event Func GuildAvailable
+ {
add { _guildAvailableEvent.Add(value); }
remove { _guildAvailableEvent.Remove(value); }
}
internal readonly AsyncEvent> _guildAvailableEvent = new AsyncEvent>();
/// Fired when a guild becomes unavailable.
- public event Func GuildUnavailable {
+ public event Func GuildUnavailable
+ {
add { _guildUnavailableEvent.Add(value); }
remove { _guildUnavailableEvent.Remove(value); }
}
internal readonly AsyncEvent> _guildUnavailableEvent = new AsyncEvent>();
/// Fired when offline guild members are downloaded.
- public event Func GuildMembersDownloaded {
+ public event Func GuildMembersDownloaded
+ {
add { _guildMembersDownloadedEvent.Add(value); }
remove { _guildMembersDownloadedEvent.Remove(value); }
}
internal readonly AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>();
/// Fired when a guild is updated.
- public event Func GuildUpdated {
+ public event Func GuildUpdated
+ {
add { _guildUpdatedEvent.Add(value); }
remove { _guildUpdatedEvent.Remove(value); }
}
@@ -317,43 +334,50 @@ namespace Discord.WebSocket
//Users
/// Fired when a user joins a guild.
- public event Func UserJoined {
+ public event Func UserJoined
+ {
add { _userJoinedEvent.Add(value); }
remove { _userJoinedEvent.Remove(value); }
}
internal readonly AsyncEvent> _userJoinedEvent = new AsyncEvent>();
/// Fired when a user leaves a guild.
- public event Func UserLeft {
+ public event Func UserLeft
+ {
add { _userLeftEvent.Add(value); }
remove { _userLeftEvent.Remove(value); }
}
internal readonly AsyncEvent> _userLeftEvent = new AsyncEvent>();
/// Fired when a user is banned from a guild.
- public event Func UserBanned {
+ public event Func UserBanned
+ {
add { _userBannedEvent.Add(value); }
remove { _userBannedEvent.Remove(value); }
}
internal readonly AsyncEvent> _userBannedEvent = new AsyncEvent>();
/// Fired when a user is unbanned from a guild.
- public event Func UserUnbanned {
+ public event Func UserUnbanned
+ {
add { _userUnbannedEvent.Add(value); }
remove { _userUnbannedEvent.Remove(value); }
}
internal readonly AsyncEvent> _userUnbannedEvent = new AsyncEvent>();
/// Fired when a user is updated.
- public event Func UserUpdated {
+ public event Func UserUpdated
+ {
add { _userUpdatedEvent.Add(value); }
remove { _userUpdatedEvent.Remove(value); }
}
internal readonly AsyncEvent> _userUpdatedEvent = new AsyncEvent>();
/// Fired when a guild member is updated, or a member presence is updated.
- public event Func GuildMemberUpdated {
+ public event Func GuildMemberUpdated
+ {
add { _guildMemberUpdatedEvent.Add(value); }
remove { _guildMemberUpdatedEvent.Remove(value); }
}
internal readonly AsyncEvent> _guildMemberUpdatedEvent = new AsyncEvent>();
/// Fired when a user joins, leaves, or moves voice channels.
- public event Func UserVoiceStateUpdated {
+ public event Func UserVoiceStateUpdated
+ {
add { _userVoiceStateUpdatedEvent.Add(value); }
remove { _userVoiceStateUpdatedEvent.Remove(value); }
}
@@ -366,25 +390,29 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent> _voiceServerUpdatedEvent = new AsyncEvent>();
/// Fired when the connected account is updated.
- public event Func CurrentUserUpdated {
+ public event Func CurrentUserUpdated
+ {
add { _selfUpdatedEvent.Add(value); }
remove { _selfUpdatedEvent.Remove(value); }
}
internal readonly AsyncEvent> _selfUpdatedEvent = new AsyncEvent>();
/// Fired when a user starts typing.
- public event Func UserIsTyping {
+ public event Func UserIsTyping
+ {
add { _userIsTypingEvent.Add(value); }
remove { _userIsTypingEvent.Remove(value); }
}
internal readonly AsyncEvent> _userIsTypingEvent = new AsyncEvent>();
/// Fired when a user joins a group channel.
- public event Func RecipientAdded {
+ public event Func RecipientAdded
+ {
add { _recipientAddedEvent.Add(value); }
remove { _recipientAddedEvent.Remove(value); }
}
internal readonly AsyncEvent> _recipientAddedEvent = new AsyncEvent>();
/// Fired when a user is removed from a group channel.
- public event Func RecipientRemoved {
+ public event Func RecipientRemoved
+ {
add { _recipientRemovedEvent.Add(value); }
remove { _recipientRemovedEvent.Remove(value); }
}
@@ -452,5 +480,70 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent> _interactionCreatedEvent = new AsyncEvent>();
+ ///
+ /// Fired when a guild application command is created.
+ ///
+ ///
+ ///
+ /// This event is fired when an application command is created. The event handler must return a
+ /// and accept a as its parameter.
+ ///
+ ///
+ /// The command that was deleted will be passed into the parameter.
+ ///
+ ///
+ /// This event is an undocumented discord event and may break at any time, its not recommended to rely on this event
+ ///
+ ///
+ public event Func ApplicationCommandCreated
+ {
+ add { _applicationCommandCreated.Add(value); }
+ remove { _applicationCommandCreated.Remove(value); }
+ }
+ internal readonly AsyncEvent> _applicationCommandCreated = new AsyncEvent>();
+
+ ///
+ /// Fired when a guild application command is updated.
+ ///
+ ///
+ ///
+ /// This event is fired when an application command is updated. The event handler must return a
+ /// and accept a as its parameter.
+ ///
+ ///
+ /// The command that was deleted will be passed into the parameter.
+ ///
+ ///
+ /// This event is an undocumented discord event and may break at any time, its not recommended to rely on this event
+ ///
+ ///
+ public event Func ApplicationCommandUpdated
+ {
+ add { _applicationCommandUpdated.Add(value); }
+ remove { _applicationCommandUpdated.Remove(value); }
+ }
+ internal readonly AsyncEvent> _applicationCommandUpdated = new AsyncEvent>();
+
+ ///
+ /// Fired when a guild application command is deleted.
+ ///
+ ///
+ ///
+ /// This event is fired when an application command is deleted. The event handler must return a
+ /// and accept a as its parameter.
+ ///
+ ///
+ /// The command that was deleted will be passed into the parameter.
+ ///
+ ///
+ /// This event is an undocumented discord event and may break at any time, its not recommended to rely on this event
+ ///
+ ///
+ public event Func ApplicationCommandDeleted
+ {
+ add { _applicationCommandDeleted.Add(value); }
+ remove { _applicationCommandDeleted.Remove(value); }
+ }
+ internal readonly AsyncEvent> _applicationCommandDeleted = new AsyncEvent>();
}
}
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index e2029e471..115367e6f 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -613,7 +613,7 @@ namespace Discord.WebSocket
}
else if (_connection.CancelToken.IsCancellationRequested)
return;
-
+
if (BaseConfig.AlwaysDownloadUsers)
_ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers));
@@ -1808,6 +1808,60 @@ namespace Discord.WebSocket
}
}
break;
+ case "APPLICATION_COMMAND_CREATE":
+ {
+ await _gatewayLogger.DebugAsync("Received Dispatch (APPLICATION_COMMAND_CREATE)").ConfigureAwait(false);
+
+ var data = (payload as JToken).ToObject(_serializer);
+
+ var guild = State.GetGuild(data.GuildId);
+ if(guild == null)
+ {
+ await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false);
+ return;
+ }
+
+ var applicationCommand = SocketApplicationCommand.Create(this, data);
+
+ await TimedInvokeAsync(_applicationCommandCreated, nameof(ApplicationCommandCreated), applicationCommand).ConfigureAwait(false);
+ }
+ break;
+ case "APPLICATION_COMMAND_UPDATE":
+ {
+ await _gatewayLogger.DebugAsync("Received Dispatch (APPLICATION_COMMAND_UPDATE)").ConfigureAwait(false);
+
+ var data = (payload as JToken).ToObject(_serializer);
+
+ var guild = State.GetGuild(data.GuildId);
+ if (guild == null)
+ {
+ await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false);
+ return;
+ }
+
+ var applicationCommand = SocketApplicationCommand.Create(this, data);
+
+ await TimedInvokeAsync(_applicationCommandUpdated, nameof(ApplicationCommandUpdated), applicationCommand).ConfigureAwait(false);
+ }
+ break;
+ case "APPLICATION_COMMAND_DELETE":
+ {
+ await _gatewayLogger.DebugAsync("Received Dispatch (APPLICATION_COMMAND_DELETE)").ConfigureAwait(false);
+
+ var data = (payload as JToken).ToObject(_serializer);
+
+ var guild = State.GetGuild(data.GuildId);
+ if (guild == null)
+ {
+ await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false);
+ return;
+ }
+
+ var applicationCommand = SocketApplicationCommand.Create(this, data);
+
+ await TimedInvokeAsync(_applicationCommandDeleted, nameof(ApplicationCommandDeleted), applicationCommand).ConfigureAwait(false);
+ }
+ break;
//Ignored (User only)
case "CHANNEL_PINS_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false);
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index e9e535998..35797dc78 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -1006,6 +1006,18 @@ namespace Discord.WebSocket
public Task> GetWebhooksAsync(RequestOptions options = null)
=> GuildHelper.GetWebhooksAsync(this, Discord, options);
+ //Interactions
+ ///
+ /// Gets this guilds slash commands commands
+ ///
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous get operation. The task result contains a read-only collection
+ /// of application commands found within the guild.
+ ///
+ public async Task> GetApplicationCommandsAsync(RequestOptions options = null)
+ => await Discord.Rest.GetGuildApplicationCommands(this.Id, options);
+
//Emotes
///
public Task GetEmoteAsync(ulong id, RequestOptions options = null)
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommand.cs
new file mode 100644
index 000000000..c8cc4d3a2
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommand.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.Gateway.ApplicationCommandCreatedUpdatedEvent;
+
+namespace Discord.WebSocket
+{
+ public class SocketApplicationCommand : SocketEntity, IApplicationCommand
+ {
+ public ulong ApplicationId { get; private set; }
+
+ public string Name { get; private set; }
+
+ public string Description { get; private set; }
+
+ public IReadOnlyCollection Options { get; private set; }
+
+ public DateTimeOffset CreatedAt
+ => SnowflakeUtils.FromSnowflake(this.Id);
+
+ public SocketGuild Guild
+ => Discord.GetGuild(this.GuildId);
+ private ulong GuildId { get; set; }
+
+ internal SocketApplicationCommand(DiscordSocketClient client, ulong id)
+ : base(client, id)
+ {
+
+ }
+ internal static SocketApplicationCommand Create(DiscordSocketClient client, Model model)
+ {
+ var entity = new SocketApplicationCommand(client, model.Id);
+ entity.Update(model);
+ return entity;
+ }
+
+ internal void Update(Model model)
+ {
+ this.ApplicationId = model.ApplicationId;
+ this.Description = model.Description;
+ this.Name = model.Name;
+ this.GuildId = model.GuildId;
+
+ this.Options = model.Options.Any()
+ ? model.Options.Select(x => SocketApplicationCommandOption.Create(x)).ToImmutableArray()
+ : new ImmutableArray();
+ }
+
+ public Task DeleteAsync(RequestOptions options = null) => throw new NotImplementedException();
+ IReadOnlyCollection IApplicationCommand.Options => Options;
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandChoice.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandChoice.cs
new file mode 100644
index 000000000..f82f9e6c4
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandChoice.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.ApplicationCommandOptionChoice;
+
+namespace Discord.WebSocket
+{
+ ///
+ /// Represents a choice for a
+ ///
+ public class SocketApplicationCommandChoice : IApplicationCommandOptionChoice
+ {
+ public string Name { get; private set; }
+
+ public object Value { get; private set; }
+
+ internal SocketApplicationCommandChoice() { }
+ internal static SocketApplicationCommandChoice Create(Model model)
+ {
+ var entity = new SocketApplicationCommandChoice();
+ entity.Update(model);
+ return entity;
+ }
+ internal void Update(Model model)
+ {
+ this.Name = model.Name;
+ this.Value = model.Value;
+ }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandOption.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandOption.cs
new file mode 100644
index 000000000..e856bce42
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketApplicationCommandOption.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.ApplicationCommandOption;
+
+namespace Discord.WebSocket
+{
+ ///
+ /// Represents an option for a
+ ///
+ public class SocketApplicationCommandOption : IApplicationCommandOption
+ {
+ public string Name { get; private set; }
+
+ public ApplicationCommandOptionType Type { get; private set; }
+
+ public string Description { get; private set; }
+
+ public bool? Default { get; private set; }
+
+ public bool? Required { get; private set; }
+
+ ///
+ /// Choices for string and int types for the user to pick from.
+ ///
+ public IReadOnlyCollection Choices { get; private set; }
+
+ ///
+ /// If the option is a subcommand or subcommand group type, this nested options will be the parameters.
+ ///
+ public IReadOnlyCollection Options { get; private set; }
+
+ internal SocketApplicationCommandOption() { }
+ internal static SocketApplicationCommandOption Create(Model model)
+ {
+ var entity = new SocketApplicationCommandOption();
+ entity.Update(model);
+ return entity;
+ }
+
+ internal void Update(Model model)
+ {
+ this.Name = model.Name;
+ this.Type = model.Type;
+ this.Description = model.Description;
+
+ this.Default = model.Default.IsSpecified
+ ? model.Default.Value
+ : null;
+
+ this.Required = model.Required.IsSpecified
+ ? model.Required.Value
+ : null;
+
+ this.Choices = model.Choices.IsSpecified
+ ? model.Choices.Value.Select(x => SocketApplicationCommandChoice.Create(x)).ToImmutableArray()
+ : new ImmutableArray();
+
+ this.Options = model.Options.IsSpecified
+ ? model.Options.Value.Select(x => SocketApplicationCommandOption.Create(x)).ToImmutableArray()
+ : new ImmutableArray();
+ }
+
+ IReadOnlyCollection IApplicationCommandOption.Choices => Choices;
+ IReadOnlyCollection IApplicationCommandOption.Options => Options;
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
index f51d0a8f8..f8c2c62bd 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
@@ -39,7 +39,7 @@ namespace Discord.WebSocket
///
/// The data associated with this interaction
///
- public IApplicationCommandInteractionData Data { get; private set; }
+ public SocketInteractionData Data { get; private set; }
///
/// The token used to respond to this interaction
@@ -209,5 +209,7 @@ namespace Discord.WebSocket
await Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, Token, options).ConfigureAwait(false);
}
+
+ IApplicationCommandInteractionData IDiscordInteraction.Data => Data;
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteractionData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteractionData.cs
index 4ff582480..b6dfd2f8e 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteractionData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteractionData.cs
@@ -11,7 +11,7 @@ namespace Discord.WebSocket
public class SocketInteractionData : SocketEntity, IApplicationCommandInteractionData
{
public string Name { get; private set; }
- public IReadOnlyCollection Options { get; private set; }
+ public IReadOnlyCollection Options { get; private set; }
internal SocketInteractionData(DiscordSocketClient client, ulong id)
: base(client, id)
@@ -33,5 +33,7 @@ namespace Discord.WebSocket
: null;
}
+
+ IReadOnlyCollection IApplicationCommandInteractionData.Options => Options;
}
}