diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
index c2af7b358..742bebac0 100644
--- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
+++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
@@ -3609,23 +3609,9 @@
The data associated with this interaction.
-
-
-
-
-
-
-
-
- Acknowledges this interaction with the .
-
-
- A task that represents the asynchronous operation of acknowledging the interaction.
-
-
- Represents the data tied with the interaction.
+ Represents the data tied with the interaction.
@@ -3641,23 +3627,9 @@
The data associated with this interaction.
-
-
-
-
-
-
-
-
- Acknowledges this interaction with the .
-
-
- A task that represents the asynchronous operation of acknowledging the interaction.
-
-
- Represents the data tied with the interaction.
+ Represents the data tied with the interaction.
@@ -3721,6 +3693,48 @@
The value(s) of a interaction response.
+
+
+ Represents a Websocket-based slash command received over the gateway.
+
+
+
+
+ The data associated with this interaction.
+
+
+
+
+ Represents the data tied with the interaction.
+
+
+
+
+
+
+
+ The 's received with this interaction.
+
+
+
+
+ Represents a Websocket-based recieved by the gateway
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The sub command options received for this sub command group.
+
+
Represends a Websocket-based recieved over the gateway.
@@ -3798,23 +3812,18 @@
If the option is a subcommand or subcommand group type, this nested options will be the parameters.
-
-
- Represents a Websocket-based slash command received over the gateway.
-
-
-
+
The data associated with this interaction.
-
+
-
+
-
+
Acknowledges this interaction with the .
@@ -3822,34 +3831,13 @@
A task that represents the asynchronous operation of acknowledging the interaction.
-
-
- Represents the data tied with the interaction.
-
-
-
-
-
-
-
- The 's received with this interaction.
-
-
-
-
- Represents a Websocket-based recieved by the gateway
-
-
-
-
-
-
+
-
+
-
+
The sub command options received for this sub command group.
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommand.cs
index 0a483b2bf..828ed14bb 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommand.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommand.cs
@@ -10,7 +10,7 @@ namespace Discord.WebSocket
///
/// Represents a Websocket-based slash command received over the gateway.
///
- public class SocketApplicationMessageCommand : SocketSlashCommand
+ public class SocketApplicationMessageCommand : SocketCommandBase
{
///
/// The data associated with this interaction.
@@ -37,129 +37,5 @@ namespace Discord.WebSocket
entity.Update(model);
return entity;
}
-
- internal override void Update(Model model)
- {
- var data = model.Data.IsSpecified ?
- (DataModel)model.Data.Value
- : null;
-
- this.Data.Update(data);
-
- base.Update(model);
- }
-
- ///
- public override async Task RespondAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
-
- if (Discord.AlwaysAcknowledgeInteractions)
- {
- await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component);
- return;
- }
-
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds 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) &&
- allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
- {
- throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
- }
-
- if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
- allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
- {
- throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
- }
- }
-
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.ChannelMessageWithSource,
- Data = new API.InteractionCallbackData
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- TTS = isTTS ? true : Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- }
- };
-
- if (ephemeral)
- response.Data.Value.Flags = 64;
-
- await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
- }
-
- ///
- public override async Task FollowupAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
-
- var args = new API.Rest.CreateWebhookMessageParams
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- IsTTS = isTTS,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- };
-
- if (ephemeral)
- args.Flags = 64;
-
- return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
- }
-
- ///
- /// Acknowledges this interaction with the .
- ///
- ///
- /// A task that represents the asynchronous operation of acknowledging the interaction.
- ///
- public override Task DeferAsync(RequestOptions options = null)
- {
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.DeferredChannelMessageWithSource,
- };
-
- return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);
- }
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommandData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommandData.cs
index e548e4cf7..ead330ef6 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommandData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommandData.cs
@@ -6,7 +6,7 @@ using Model = Discord.API.ApplicationCommandInteractionData;
namespace Discord.WebSocket
{
///
- /// Represents the data tied with the interaction.
+ /// Represents the data tied with the interaction.
///
public class SocketApplicationMessageCommandData : SocketEntity, IApplicationCommandInteractionData
{
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommand.cs
index 37bdd40bd..603a11397 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommand.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommand.cs
@@ -10,7 +10,7 @@ namespace Discord.WebSocket
///
/// Represents a Websocket-based slash command received over the gateway.
///
- public class SocketApplicationUserCommand : SocketSlashCommand
+ public class SocketApplicationUserCommand : SocketCommandBase
{
///
/// The data associated with this interaction.
@@ -36,130 +36,6 @@ namespace Discord.WebSocket
var entity = new SocketApplicationUserCommand(client, model, channel);
entity.Update(model);
return entity;
- }
-
- internal override void Update(Model model)
- {
- var data = model.Data.IsSpecified ?
- (DataModel)model.Data.Value
- : null;
-
- this.Data.Update(data);
-
- base.Update(model);
- }
-
- ///
- public override async Task RespondAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
-
- if (Discord.AlwaysAcknowledgeInteractions)
- {
- await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component);
- return;
- }
-
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds 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) &&
- allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
- {
- throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
- }
-
- if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
- allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
- {
- throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
- }
- }
-
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.ChannelMessageWithSource,
- Data = new API.InteractionCallbackData
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- TTS = isTTS ? true : Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- }
- };
-
- if (ephemeral)
- response.Data.Value.Flags = 64;
-
- await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
- }
-
- ///
- public override async Task FollowupAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
-
- var args = new API.Rest.CreateWebhookMessageParams
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- IsTTS = isTTS,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- };
-
- if (ephemeral)
- args.Flags = 64;
-
- return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
- }
-
- ///
- /// Acknowledges this interaction with the .
- ///
- ///
- /// A task that represents the asynchronous operation of acknowledging the interaction.
- ///
- public override Task DeferAsync(RequestOptions options = null)
- {
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.DeferredChannelMessageWithSource,
- };
-
- return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);
- }
+ }
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommandData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommandData.cs
index 0c5a540c7..a6eb24ca5 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommandData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommandData.cs
@@ -6,7 +6,7 @@ using Model = Discord.API.ApplicationCommandInteractionData;
namespace Discord.WebSocket
{
///
- /// Represents the data tied with the interaction.
+ /// Represents the data tied with the interaction.
///
public class SocketApplicationUserCommandData : SocketEntity, IApplicationCommandInteractionData
{
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
index 245274613..3013099c7 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
@@ -10,7 +10,7 @@ namespace Discord.WebSocket
///
/// Represents a Websocket-based slash command received over the gateway.
///
- public class SocketSlashCommand : SocketInteraction
+ public class SocketSlashCommand : SocketCommandBase
{
///
/// The data associated with this interaction.
@@ -18,7 +18,7 @@ namespace Discord.WebSocket
new public SocketSlashCommandData Data { get; }
internal SocketSlashCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
- : base(client, model.Id, channel)
+ : base(client, model, channel)
{
var dataModel = model.Data.IsSpecified ?
(DataModel)model.Data.Value
@@ -36,130 +36,6 @@ namespace Discord.WebSocket
var entity = new SocketSlashCommand(client, model, channel);
entity.Update(model);
return entity;
- }
-
- internal override void Update(Model model)
- {
- var data = model.Data.IsSpecified ?
- (DataModel)model.Data.Value
- : null;
-
- this.Data.Update(data);
-
- base.Update(model);
- }
-
- ///
- public override async Task RespondAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
-
- if (Discord.AlwaysAcknowledgeInteractions)
- {
- await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component);
- return;
- }
-
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds 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) &&
- allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
- {
- throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
- }
-
- if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
- allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
- {
- throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
- }
- }
-
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.ChannelMessageWithSource,
- Data = new API.InteractionCallbackData
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- TTS = isTTS ? true : Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- }
- };
-
- if (ephemeral)
- response.Data.Value.Flags = 64;
-
- await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
- }
-
- ///
- public override async Task FollowupAsync(
- string text = null,
- Embed[] embeds = null,
- bool isTTS = false,
- bool ephemeral = false,
- AllowedMentions allowedMentions = null,
- RequestOptions options = null,
- MessageComponent component = null,
- Embed embed = null)
- {
- if (!IsValidToken)
- throw new InvalidOperationException("Interaction token is no longer valid");
-
- if (embeds == null && embed != null)
- embeds = new[] { embed };
- 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.");
- Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
-
- var args = new API.Rest.CreateWebhookMessageParams
- {
- Content = text,
- AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
- IsTTS = isTTS,
- Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
- Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
- };
-
- if (ephemeral)
- args.Flags = 64;
-
- return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
- }
-
- ///
- /// Acknowledges this interaction with the .
- ///
- ///
- /// A task that represents the asynchronous operation of acknowledging the interaction.
- ///
- public override Task DeferAsync(RequestOptions options = null)
- {
- var response = new API.InteractionResponse
- {
- Type = InteractionResponseType.DeferredChannelMessageWithSource,
- };
-
- return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);
- }
+ }
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommand.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs
similarity index 100%
rename from src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommand.cs
rename to src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommandChoice.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandChoice.cs
similarity index 100%
rename from src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommandChoice.cs
rename to src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandChoice.cs
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommandOption.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs
similarity index 100%
rename from src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketApplicationCommandOption.cs
rename to src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
new file mode 100644
index 000000000..9fd25ac5d
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
@@ -0,0 +1,164 @@
+using Discord.Rest;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DataModel = Discord.API.ApplicationCommandInteractionData;
+using Model = Discord.API.Interaction;
+
+namespace Discord.WebSocket
+{
+ public class SocketCommandBase : SocketInteraction
+ {
+ ///
+ /// The data associated with this interaction.
+ ///
+ new internal SocketCommandBaseData Data { get; }
+
+ internal SocketCommandBase(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
+ : base(client, model.Id, channel)
+ {
+ var dataModel = model.Data.IsSpecified ?
+ (DataModel)model.Data.Value
+ : null;
+
+ ulong? guildId = null;
+ if (this.Channel is SocketGuildChannel guildChannel)
+ guildId = guildChannel.Guild.Id;
+
+ Data = SocketCommandBaseData.Create(client, dataModel, model.Id, guildId);
+ }
+
+ new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
+ {
+ var entity = new SocketSlashCommand(client, model, channel);
+ entity.Update(model);
+ return entity;
+ }
+
+ internal override void Update(Model model)
+ {
+ var data = model.Data.IsSpecified ?
+ (DataModel)model.Data.Value
+ : null;
+
+ this.Data.Update(data);
+
+ base.Update(model);
+ }
+
+ ///
+ public override async Task RespondAsync(
+ string text = null,
+ Embed[] embeds = null,
+ bool isTTS = false,
+ bool ephemeral = false,
+ AllowedMentions allowedMentions = null,
+ RequestOptions options = null,
+ MessageComponent component = null,
+ Embed embed = null)
+ {
+ if (!IsValidToken)
+ throw new InvalidOperationException("Interaction token is no longer valid");
+
+ if (embeds == null && embed != null)
+ embeds = new[] { embed };
+
+ if (Discord.AlwaysAcknowledgeInteractions)
+ {
+ await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component);
+ return;
+ }
+
+ 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.");
+ Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds 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) &&
+ allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+ {
+ throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
+ }
+
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
+ allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+ {
+ throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
+ }
+ }
+
+ var response = new API.InteractionResponse
+ {
+ Type = InteractionResponseType.ChannelMessageWithSource,
+ Data = new API.InteractionCallbackData
+ {
+ Content = text,
+ AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
+ Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
+ TTS = isTTS ? true : Optional.Unspecified,
+ Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
+ }
+ };
+
+ if (ephemeral)
+ response.Data.Value.Flags = 64;
+
+ await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
+ }
+
+ ///
+ public override async Task FollowupAsync(
+ string text = null,
+ Embed[] embeds = null,
+ bool isTTS = false,
+ bool ephemeral = false,
+ AllowedMentions allowedMentions = null,
+ RequestOptions options = null,
+ MessageComponent component = null,
+ Embed embed = null)
+ {
+ if (!IsValidToken)
+ throw new InvalidOperationException("Interaction token is no longer valid");
+
+ if (embeds == null && embed != null)
+ embeds = new[] { embed };
+ 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.");
+ Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed.");
+
+ var args = new API.Rest.CreateWebhookMessageParams
+ {
+ Content = text,
+ AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified,
+ IsTTS = isTTS,
+ Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified,
+ Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified
+ };
+
+ if (ephemeral)
+ args.Flags = 64;
+
+ return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
+ }
+
+ ///
+ /// Acknowledges this interaction with the .
+ ///
+ ///
+ /// A task that represents the asynchronous operation of acknowledging the interaction.
+ ///
+ public override Task DeferAsync(RequestOptions options = null)
+ {
+ var response = new API.InteractionResponse
+ {
+ Type = InteractionResponseType.DeferredChannelMessageWithSource,
+ };
+
+ return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);
+ }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseData.cs
new file mode 100644
index 000000000..7d7dbbce7
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseData.cs
@@ -0,0 +1,145 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Model = Discord.API.ApplicationCommandInteractionData;
+
+namespace Discord.WebSocket
+{
+ public class SocketCommandBaseData : SocketEntity, IApplicationCommandInteractionData
+ {
+ public string Name { get; private set; }
+
+ public IReadOnlyCollection Options { get; private set; }
+ // id
+ // type
+ internal Dictionary guildMembers { get; private set; }
+ = new Dictionary();
+ internal Dictionary users { get; private set; }
+ = new Dictionary();
+ internal Dictionary channels { get; private set; }
+ = new Dictionary();
+ internal Dictionary roles { get; private set; }
+ = new Dictionary();
+
+ private ulong? guildId;
+
+ internal SocketMessage Message { get; private set; }
+
+ private ApplicationCommandType Type { get; set; }
+
+ internal SocketCommandBaseData(DiscordSocketClient client, Model model, ulong? guildId)
+ : base(client, model.Id)
+ {
+ this.guildId = guildId;
+
+ this.Type = (ApplicationCommandType)model.Type;
+
+ if (model.Resolved.IsSpecified)
+ {
+ var guild = this.guildId.HasValue ? Discord.GetGuild(this.guildId.Value) : null;
+
+ var resolved = model.Resolved.Value;
+
+ if (resolved.Users.IsSpecified)
+ {
+ foreach (var user in resolved.Users.Value)
+ {
+ var socketUser = Discord.GetOrCreateUser(this.Discord.State, user.Value);
+
+ this.users.Add(ulong.Parse(user.Key), socketUser);
+ }
+ }
+
+ if (resolved.Channels.IsSpecified)
+ {
+ foreach (var channel in resolved.Channels.Value)
+ {
+ SocketChannel socketChannel = guild != null
+ ? guild.GetChannel(channel.Value.Id)
+ : Discord.GetChannel(channel.Value.Id);
+
+ if (socketChannel == null)
+ {
+ var channelModel = guild != null
+ ? Discord.Rest.ApiClient.GetChannelAsync(guild.Id, channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult()
+ : Discord.Rest.ApiClient.GetChannelAsync(channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult();
+
+ socketChannel = guild != null
+ ? SocketGuildChannel.Create(guild, Discord.State, channelModel)
+ : (SocketChannel)SocketChannel.CreatePrivate(Discord, Discord.State, channelModel);
+ }
+
+ Discord.State.AddChannel(socketChannel);
+ this.channels.Add(ulong.Parse(channel.Key), socketChannel);
+ }
+ }
+
+ if (resolved.Members.IsSpecified)
+ {
+ foreach (var member in resolved.Members.Value)
+ {
+ member.Value.User = resolved.Users.Value[member.Key];
+ var user = guild.AddOrUpdateUser(member.Value);
+ this.guildMembers.Add(ulong.Parse(member.Key), user);
+ }
+ }
+
+ if (resolved.Roles.IsSpecified)
+ {
+ foreach (var role in resolved.Roles.Value)
+ {
+ var socketRole = guild.AddOrUpdateRole(role.Value);
+ this.roles.Add(ulong.Parse(role.Key), socketRole);
+ }
+ }
+
+ if (resolved.Messages.IsSpecified)
+ {
+ foreach (var msg in resolved.Messages.Value)
+ {
+ var channel = client.GetChannel(msg.Value.ChannelId) as ISocketMessageChannel;
+
+ SocketUser author;
+ if (guild != null)
+ {
+ if (msg.Value.WebhookId.IsSpecified)
+ author = SocketWebhookUser.Create(guild, client.State, msg.Value.Author.Value, msg.Value.WebhookId.Value);
+ else
+ author = guild.GetUser(msg.Value.Author.Value.Id);
+ }
+ else
+ author = (channel as SocketChannel).GetUser(msg.Value.Author.Value.Id);
+
+ if (channel == null)
+ {
+ if (!msg.Value.GuildId.IsSpecified) // assume it is a DM
+ {
+ channel = client.CreateDMChannel(msg.Value.ChannelId, msg.Value.Author.Value, client.State);
+ }
+ }
+
+ this.Message = SocketMessage.Create(client, client.State, author, channel, msg.Value);
+ }
+ }
+ }
+ }
+
+ internal static SocketCommandBaseData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId)
+ {
+ var entity = new SocketCommandBaseData(client, model, guildId);
+ entity.Update(model);
+ return entity;
+ }
+
+ internal void Update(Model model)
+ {
+ this.Name = model.Name;
+
+ this.Options = model.Options.IsSpecified
+ ? model.Options.Value.Select(x => new SocketCommandBaseDataOption(this, x)).ToImmutableArray()
+ : null;
+ }
+
+ IReadOnlyCollection IApplicationCommandInteractionData.Options => Options;
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseDataOption.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseDataOption.cs
new file mode 100644
index 000000000..369f1f868
--- /dev/null
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseDataOption.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.ApplicationCommandInteractionDataOption;
+
+namespace Discord.WebSocket
+{
+ public class SocketCommandBaseDataOption : IApplicationCommandInteractionDataOption
+ {
+ public string Name { get; private set; }
+
+ ///
+ public object Value { get; private set; }
+
+ ///
+ public ApplicationCommandOptionType Type { get; private set; }
+
+ ///
+ /// The sub command options received for this sub command group.
+ ///
+ public IReadOnlyCollection Options { get; private set; }
+
+ internal SocketCommandBaseDataOption() { }
+ internal SocketCommandBaseDataOption(SocketCommandBaseData data, Model model)
+ {
+ this.Name = model.Name;
+ this.Type = model.Type;
+
+ if (model.Value.IsSpecified)
+ {
+ switch (Type)
+ {
+ case ApplicationCommandOptionType.User:
+ case ApplicationCommandOptionType.Role:
+ case ApplicationCommandOptionType.Channel:
+ case ApplicationCommandOptionType.Mentionable:
+ if (ulong.TryParse($"{model.Value.Value}", out var valueId))
+ {
+ switch (this.Type)
+ {
+ case ApplicationCommandOptionType.User:
+ {
+ var guildUser = data.guildMembers.FirstOrDefault(x => x.Key == valueId).Value;
+
+ if (guildUser != null)
+ this.Value = guildUser;
+ else
+ this.Value = data.users.FirstOrDefault(x => x.Key == valueId).Value;
+ }
+ break;
+ case ApplicationCommandOptionType.Channel:
+ this.Value = data.channels.FirstOrDefault(x => x.Key == valueId).Value;
+ break;
+ case ApplicationCommandOptionType.Role:
+ this.Value = data.roles.FirstOrDefault(x => x.Key == valueId).Value;
+ break;
+ case ApplicationCommandOptionType.Mentionable:
+ {
+ if (data.guildMembers.Any(x => x.Key == valueId) || data.users.Any(x => x.Key == valueId))
+ {
+ var guildUser = data.guildMembers.FirstOrDefault(x => x.Key == valueId).Value;
+
+ if (guildUser != null)
+ this.Value = guildUser;
+ else
+ this.Value = data.users.FirstOrDefault(x => x.Key == valueId).Value;
+ }
+ else if (data.roles.Any(x => x.Key == valueId))
+ {
+ this.Value = data.roles.FirstOrDefault(x => x.Key == valueId).Value;
+ }
+ }
+ break;
+ default:
+ this.Value = model.Value.Value;
+ break;
+ }
+ }
+ break;
+ case ApplicationCommandOptionType.String:
+ this.Value = model.Value.ToString();
+ break;
+ case ApplicationCommandOptionType.Integer:
+ {
+ if (model.Value.Value is int val)
+ this.Value = val;
+ else if (int.TryParse(model.Value.Value.ToString(), out int res))
+ this.Value = res;
+ }
+ break;
+ case ApplicationCommandOptionType.Boolean:
+ {
+ if (model.Value.Value is bool val)
+ this.Value = val;
+ else if (bool.TryParse(model.Value.Value.ToString(), out bool res))
+ this.Value = res;
+ }
+ break;
+ case ApplicationCommandOptionType.Number:
+ {
+ if (model.Value.Value is int val)
+ this.Value = val;
+ else if (double.TryParse(model.Value.Value.ToString(), out double res))
+ this.Value = res;
+ }
+ break;
+ }
+
+ }
+
+ this.Options = model.Options.IsSpecified
+ ? model.Options.Value.Select(x => new SocketCommandBaseDataOption(data, x)).ToImmutableArray()
+ : null;
+ }
+
+ // Converters
+ public static explicit operator bool(SocketCommandBaseDataOption option)
+ => (bool)option.Value;
+ public static explicit operator int(SocketCommandBaseDataOption option)
+ => (int)option.Value;
+ public static explicit operator string(SocketCommandBaseDataOption option)
+ => option.Value.ToString();
+
+ IReadOnlyCollection IApplicationCommandInteractionDataOption.Options => this.Options;
+ }
+}
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
index 01b3874f7..59bae6f08 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
@@ -62,12 +62,13 @@ namespace Discord.WebSocket
internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
{
if (model.Type == InteractionType.ApplicationCommand)
- if(model.ApplicationId != null)
+ {
+ if (model.ApplicationId != null)
{
var dataModel = model.Data.IsSpecified ?
(DataModel)model.Data.Value
: null;
- if(dataModel != null)
+ if (dataModel != null)
{
if (dataModel.Type.Equals(ApplicationCommandType.User))
return SocketApplicationUserCommand.Create(client, model, channel);
@@ -76,6 +77,7 @@ namespace Discord.WebSocket
}
}
return SocketSlashCommand.Create(client, model, channel);
+ }
if (model.Type == InteractionType.MessageComponent)
return SocketMessageComponent.Create(client, model, channel);
else