diff --git a/src/Discord.Net.Core/Discord.Net.Core.xml b/src/Discord.Net.Core/Discord.Net.Core.xml index db3bbe3aa..ad4a34eb8 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.xml +++ b/src/Discord.Net.Core/Discord.Net.Core.xml @@ -7129,7 +7129,7 @@ Properties that are used to modify an with the specified changes. - The content of a message can be cleared with if and only if an + The content of a message can be cleared with if and only if an is present. @@ -7142,9 +7142,9 @@ This must be less than the constant defined by . - + - Gets or sets the embed the message should display. + Gets or sets the embeds of the message. diff --git a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs index b2423796d..9317c0e64 100644 --- a/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs @@ -120,7 +120,8 @@ namespace Discord } /// - /// Adds a button to the specified row. + /// Adds a with specified parameters to the at the specific row. + /// If the row cannot accept the component then it will add it to a row that can. /// /// The label text for the newly added button. /// The style of this newly added button. @@ -131,8 +132,8 @@ namespace Discord /// The row the button should be placed on. /// The current builder. public ComponentBuilder WithButton( - string label, - string customId, + string label = null, + string customId = null, ButtonStyle style = ButtonStyle.Primary, IEmote emote = null, string url = null, @@ -374,7 +375,7 @@ namespace Discord /// The custom ID of this button /// The emote of this button /// Disabled this button or not - public ButtonBuilder(string label, string customId, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool disabled = false) + public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool disabled = false) { this.CustomId = customId; this.Style = style; @@ -400,47 +401,52 @@ namespace Discord /// /// Creates a button with the style. /// - /// The label to use on the newly created link button. + /// The label for this link button. /// The url for this link button to go to. + /// The emote for this link button /// A builder with the newly created button. - public static ButtonBuilder CreateLinkButton(string label, string url) - => new ButtonBuilder(label, null, ButtonStyle.Link, url); + public static ButtonBuilder CreateLinkButton(string label, string url, IEmote emote = null) + => new ButtonBuilder(label, null, ButtonStyle.Link, url, emote: emote); /// /// Creates a button with the style. /// /// The label for this danger button. /// The custom id for this danger button. + /// The emote for this danger button /// A builder with the newly created button. - public static ButtonBuilder CreateDangerButton(string label, string customId) - => new ButtonBuilder(label, customId, ButtonStyle.Danger); + public static ButtonBuilder CreateDangerButton(string label, string customId, IEmote emote = null) + => new ButtonBuilder(label, customId, ButtonStyle.Danger, emote: emote); /// /// Creates a button with the style. /// /// The label for this primary button. /// The custom id for this primary button. + /// The emote for this primary button /// A builder with the newly created button. - public static ButtonBuilder CreatePrimaryButton(string label, string customId) - => new ButtonBuilder(label, customId); + public static ButtonBuilder CreatePrimaryButton(string label, string customId, IEmote emote = null) + => new ButtonBuilder(label, customId, emote: emote); /// /// Creates a button with the style. /// /// The label for this secondary button. /// The custom id for this secondary button. + /// The emote for this secondary button /// A builder with the newly created button. - public static ButtonBuilder CreateSecondaryButton(string label, string customId) - => new ButtonBuilder(label, customId, ButtonStyle.Secondary); + public static ButtonBuilder CreateSecondaryButton(string label, string customId, IEmote emote = null) + => new ButtonBuilder(label, customId, ButtonStyle.Secondary, emote: emote); /// /// Creates a button with the style. /// /// The label for this success button. /// The custom id for this success button. + /// The emote for this success button /// A builder with the newly created button. - public static ButtonBuilder CreateSuccessButton(string label, string customId) - => new ButtonBuilder(label, customId, ButtonStyle.Success); + public static ButtonBuilder CreateSuccessButton(string label, string customId, IEmote emote = null) + => new ButtonBuilder(label, customId, ButtonStyle.Success, emote: emote); /// /// Sets the current buttons label to the specified text. diff --git a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs index c71d29520..f4971b69e 100644 --- a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs +++ b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs @@ -4,7 +4,7 @@ namespace Discord /// Properties that are used to modify an with the specified changes. /// /// - /// The content of a message can be cleared with if and only if an + /// The content of a message can be cleared with if and only if an /// is present. /// /// @@ -17,10 +17,11 @@ namespace Discord /// This must be less than the constant defined by . /// public Optional Content { get; set; } + /// - /// Gets or sets the embed the message should display. + /// Gets or sets the embeds of the message. /// - public Optional Embed { get; set; } + public Optional Embeds { get; set; } /// /// Gets or sets the components for this message. diff --git a/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs b/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs index f6e3dcbee..f03cb8870 100644 --- a/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs +++ b/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs @@ -11,7 +11,7 @@ namespace Discord.API public Optional Content { get; set; } [JsonProperty("embeds")] - public Optional Embeds { get; set; } + public Optional Embeds { get; set; } [JsonProperty("allowed_mentions")] public Optional AllowedMentions { get; set; } diff --git a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs index 528bf8e07..bd4cc1a6c 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs @@ -7,7 +7,7 @@ namespace Discord.API.Rest internal class CreateWebhookMessageParams { [JsonProperty("content")] - public string Content { get; } + public string Content { get; set; } [JsonProperty("nonce")] public Optional Nonce { get; set; } @@ -32,10 +32,5 @@ namespace Discord.API.Rest [JsonProperty("components")] public Optional Components { get; set; } - - public CreateWebhookMessageParams(string content) - { - Content = content; - } } } diff --git a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs index 69d962767..21ed1a418 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs @@ -8,8 +8,8 @@ namespace Discord.API.Rest { [JsonProperty("content")] public Optional Content { get; set; } - [JsonProperty("embed")] - public Optional Embed { get; set; } + [JsonProperty("embeds")] + public Optional Embeds { get; set; } [JsonProperty("components")] public Optional Components { get; set; } [JsonProperty("flags")] diff --git a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs index 7c5de2ee9..14516137d 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs @@ -4,7 +4,6 @@ using Discord.Net; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Discord.Rest @@ -21,7 +20,7 @@ namespace Discord.Rest return client.ApiClient.BulkOverwriteGlobalApplicationCommands(new CreateApplicationCommandParams[0], options); } - public static Task SendInteractionResponse(BaseDiscordClient client, IMessageChannel channel, InteractionResponse response, + public static Task SendInteractionResponse(BaseDiscordClient client, InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null) { return client.ApiClient.CreateInteractionResponse(response, interactionId, interactionToken, options); @@ -298,14 +297,14 @@ namespace Discord.Rest func(args); bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(message.Content); - bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : message.Embeds.Any(); + bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : message.Embeds.Any(); if (!hasText && !hasEmbed) Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); var apiArgs = new API.Rest.ModifyInteractionResponseParams { Content = args.Content, - Embeds = args.Embed.IsSpecified ? new API.Embed[] { args.Embed.Value.ToModel() } : Optional.Create(), + Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Create(), AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Unspecified, Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified, }; @@ -316,26 +315,21 @@ namespace Discord.Rest public static async Task DeleteFollowupMessage(BaseDiscordClient client, RestFollowupMessage message, RequestOptions options = null) => await client.ApiClient.DeleteInteractionFollowupMessage(message.Id, message.Token, options); - public static async Task ModifyInteractionResponse(BaseDiscordClient client, RestInteractionMessage message, Action func, + public static async Task ModifyInteractionResponse(BaseDiscordClient client, string token, Action func, RequestOptions options = null) { var args = new MessageProperties(); func(args); - bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(message.Content); - bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : message.Embeds.Any(); - if (!hasText && !hasEmbed) - Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); - - var apiArgs = new API.Rest.ModifyInteractionResponseParams + var apiArgs = new ModifyInteractionResponseParams { Content = args.Content, - Embeds = args.Embed.IsSpecified ? new API.Embed[] { args.Embed.Value.ToModel() } : Optional.Create(), - AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Unspecified, + Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, + AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional.Unspecified, Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified, }; - return await client.ApiClient.ModifyInteractionResponse(apiArgs, message.Token, options).ConfigureAwait(false); + return await client.ApiClient.ModifyInteractionResponse(apiArgs, token, options).ConfigureAwait(false); } public static async Task DeletedInteractionResponse(BaseDiscordClient client, RestInteractionMessage message, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 2062759ff..5812a7316 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -30,11 +30,11 @@ namespace Discord.Rest var args = new MessageProperties(); func(args); - if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embed.IsSpecified || args.AllowedMentions.IsSpecified)) + if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embeds.IsSpecified || args.AllowedMentions.IsSpecified)) throw new InvalidOperationException("Only the author of a message may modify the message content, embed, or allowed mentions."); bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(msg.Content); - bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : msg.Embeds.Any(); + bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : msg.Embeds.Any(); if (!hasText && !hasEmbed) Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); @@ -61,13 +61,13 @@ namespace Discord.Rest } } - var apiArgs = new API.Rest.ModifyMessageParams + var apiArgs = new ModifyMessageParams { Content = args.Content, - Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create(), + Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified, - Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create(), - AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create(), + Flags = args.Flags, + AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Unspecified, }; return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } @@ -78,7 +78,7 @@ namespace Discord.Rest var args = new MessageProperties(); func(args); - if ((args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value)) && (args.Embed.IsSpecified && args.Embed.Value == null)) + if (args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value) && args.Embeds.IsSpecified && args.Embeds.Value == null) Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); if (args.AllowedMentions.IsSpecified) @@ -86,6 +86,7 @@ namespace Discord.Rest AllowedMentions allowedMentions = args.AllowedMentions.Value; 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(args.Embeds.Value?.Length ?? 0, 10, nameof(args.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) @@ -107,7 +108,7 @@ namespace Discord.Rest var apiArgs = new API.Rest.ModifyMessageParams { Content = args.Content, - Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create(), + Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Create(), Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create(), AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create(), }; diff --git a/src/Discord.Net.Rest/Entities/Messages/RestInteractionMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestInteractionMessage.cs index 6733966ad..3574143fc 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestInteractionMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestInteractionMessage.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Model = Discord.API.Message; @@ -64,7 +61,7 @@ namespace Discord.Rest { try { - var model = await InteractionHelper.ModifyInteractionResponse(Discord, this, func, options).ConfigureAwait(false); + var model = await InteractionHelper.ModifyInteractionResponse(Discord, this.Token, func, options).ConfigureAwait(false); this.Update(model); } catch (Discord.Net.HttpException x) diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml index 9b285f33f..f223faa49 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml @@ -3223,16 +3223,24 @@ The message that contained the trigger for this interaction. - + - + + + Updates the original message of the component on which the interaction was received on. + + A delegate containing the properties to modify the message with. + The request options for this async request. + + - + Acknowledges this interaction with the . + The request options for this async request. A task that represents the asynchronous operation of acknowledging the interaction. @@ -3341,14 +3349,19 @@ The data associated with this interaction. - + - + - - + + + Acknowledges this interaction with the . + + + A task that represents the asynchronous operation of acknowledging the interaction. + @@ -3425,18 +3438,17 @@ if the token is valid for replying to, otherwise . - + - Responds to an Interaction. + Responds to an Interaction with type . If you have set to , You should use - instead. + instead. The text of the message to be sent. + A array of embeds to send with this response. Max 10 if the message should be read out by a text-to-speech reader, otherwise . - A to send with this response. - The type of response to this Interaction. if the response should be hidden to everyone besides the invoker of the command, otherwise . The allowed mentions for this response. The request options for this response. @@ -3444,49 +3456,13 @@ Message content is too long, length must be less or equal to . The parameters provided were invalid or the token was invalid. - + Sends a followup message for this interaction. The text of the message to be sent - if the message should be read out by a text-to-speech reader, otherwise . - A to send with this response - The type of response to this Interaction. - if the response should be hidden to everyone besides the invoker of the command, otherwise . - The allowed mentions for this response. - The request options for this response. - A to be sent with this response - - The sent message. - - - - - Responds to an Interaction. - - If you have set to , You should use - instead. - - - The text of the message to be sent. - if the message should be read out by a text-to-speech reader, otherwise . A array of embeds to send with this response. Max 10 - The type of response to this Interaction. - if the response should be hidden to everyone besides the invoker of the command, otherwise . - The allowed mentions for this response. - The request options for this response. - A to be sent with this response - Message content is too long, length must be less or equal to . - The parameters provided were invalid or the token was invalid. - - - - Sends a followup message for this interaction. - - The text of the message to be sent if the message should be read out by a text-to-speech reader, otherwise . - A array of embeds to send with this response. Max 10 - The type of response to this Interaction. if the response should be hidden to everyone besides the invoker of the command, otherwise . The allowed mentions for this response. The request options for this response. @@ -3500,11 +3476,19 @@ Gets the original response for this interaction. The request options for this async request. - A that represents the intitial response, or if there is no response. + A that represents the initial response. - + - Acknowledges this interaction with the . + Edits original response for this interaction. + + A delegate containing the properties to modify the message with. + The request options for this async request. + A that represents the initial response. + + + + Acknowledges this interaction. A task that represents the asynchronous operation of acknowledging the interaction. diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index f9eaeb005..2c5b26016 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1904,7 +1904,7 @@ namespace Discord.WebSocket var interaction = SocketInteraction.Create(this, data, channel as ISocketMessageChannel); if (this.AlwaysAcknowledgeInteractions) - await interaction.AcknowledgeAsync().ConfigureAwait(false); + await interaction.DeferAsync().ConfigureAwait(false); await TimedInvokeAsync(_interactionCreatedEvent, nameof(InteractionCreated), interaction).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs index 8cbfc362e..338ef52a0 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Model = Discord.API.Interaction; using DataModel = Discord.API.MessageComponentInteractionData; -using Newtonsoft.Json.Linq; using Discord.Rest; namespace Discord.WebSocket @@ -71,18 +68,21 @@ namespace Discord.WebSocket } /// - public override async Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) + 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) { - if (type == InteractionResponseType.Pong) - throw new InvalidOperationException($"Cannot use {Type} on a send message function"); - if (!IsValidToken) throw new InvalidOperationException("Interaction token is no longer valid"); if (Discord.AlwaysAcknowledgeInteractions) { - await FollowupAsync(embeds, text, isTTS, ephemeral, type, allowedMentions, options); + await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options); return; } @@ -109,13 +109,13 @@ namespace Discord.WebSocket var response = new API.InteractionResponse { - Type = type, + Type = InteractionResponseType.ChannelMessageWithSource, Data = new API.InteractionCallbackData { Content = text ?? Optional.Unspecified, AllowedMentions = allowedMentions?.ToModel(), Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified, - TTS = type == InteractionResponseType.ChannelMessageWithSource ? isTTS : Optional.Unspecified, + TTS = isTTS, Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified } }; @@ -123,17 +123,78 @@ namespace Discord.WebSocket if (ephemeral) response.Data.Value.Flags = 64; - await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); + await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); } - /// - public override async Task FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, - InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) + /// + /// Updates the message which this component resides in with the type + /// + /// A delegate containing the properties to modify the message with. + /// The request options for this async request. + /// A task that represents the asynchronous operation of updating the message. + public async Task UpdateAsync(Action func, RequestOptions options = null) { - if (type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.Pong) - throw new InvalidOperationException($"Cannot use {type} on a slash command!"); + var args = new MessageProperties(); + func(args); + + if (!IsValidToken) + throw new InvalidOperationException("Interaction token is no longer valid"); + + if (args.AllowedMentions.IsSpecified) + { + var allowedMentions = args.AllowedMentions.Value; + Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 role Ids are allowed."); + Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 user Ids are allowed."); + } + + if (args.Embeds.IsSpecified) + Preconditions.AtMost(args.Embeds.Value?.Length ?? 0, 10, nameof(args.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 (args.AllowedMentions.IsSpecified && args.AllowedMentions.Value != null && args.AllowedMentions.Value.AllowedTypes.HasValue) + { + var allowedMentions = args.AllowedMentions.Value; + 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(args.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(args.AllowedMentions)); + } + } + + var response = new API.InteractionResponse + { + Type = InteractionResponseType.UpdateMessage, + Data = new API.InteractionCallbackData + { + Content = args.Content, + AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional.Unspecified, + Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, + Components = args.Components.IsSpecified + ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() + : Optional.Unspecified, + Flags = args.Flags.IsSpecified ? (int?)args.Flags.Value ?? Optional.Unspecified : Optional.Unspecified + } + }; + + await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, this.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) + { if (!IsValidToken) throw new InvalidOperationException("Interaction token is no longer valid"); @@ -141,13 +202,12 @@ namespace Discord.WebSocket 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(text) + var args = new API.Rest.CreateWebhookMessageParams { - AllowedMentions = allowedMentions?.ToModel(), + Content = text, + AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, - Embeds = embeds != null - ? embeds.Select(x => x.ToModel()).ToArray() - : Optional.Unspecified, + Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified, Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; @@ -160,10 +220,11 @@ namespace Discord.WebSocket /// /// Acknowledges this interaction with the . /// + /// The request options for this async request. /// /// A task that represents the asynchronous operation of acknowledging the interaction. /// - public override Task AcknowledgeAsync(RequestOptions options = null) + public override Task DeferAsync(RequestOptions options = null) { var response = new API.InteractionResponse() { 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 e3596ec50..81518cd95 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs @@ -1,5 +1,4 @@ using Discord.Rest; -using Newtonsoft.Json.Linq; using System; using System.Linq; using System.Threading.Tasks; @@ -16,7 +15,7 @@ namespace Discord.WebSocket /// /// The data associated with this interaction. /// - new public SocketSlashCommandData Data { get; private set; } + new public SocketSlashCommandData Data { get; } internal SocketSlashCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel) : base(client, model.Id, channel) @@ -51,21 +50,21 @@ namespace Discord.WebSocket } /// - public override async Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) + 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) { - if (type == InteractionResponseType.Pong) - throw new InvalidOperationException($"Cannot use {Type} on a send message function"); - - if(type == InteractionResponseType.DeferredUpdateMessage || type == InteractionResponseType.UpdateMessage) - throw new InvalidOperationException($"Cannot use {Type} on a slash command!"); - if (!IsValidToken) throw new InvalidOperationException("Interaction token is no longer valid"); if (Discord.AlwaysAcknowledgeInteractions) { - await FollowupAsync(embeds, text, isTTS, ephemeral, type, allowedMentions, options, component); + await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component); return; } @@ -92,11 +91,11 @@ namespace Discord.WebSocket var response = new API.InteractionResponse { - Type = type, + Type = InteractionResponseType.ChannelMessageWithSource, Data = new API.InteractionCallbackData { - Content = text ?? Optional.Unspecified, - AllowedMentions = allowedMentions?.ToModel(), + 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 @@ -106,17 +105,19 @@ namespace Discord.WebSocket if (ephemeral) response.Data.Value.Flags = 64; - await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); + await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); } /// - public override async Task FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, - InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) + 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) { - if (type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.DeferredChannelMessageWithSource || type == InteractionResponseType.Pong || type == InteractionResponseType.DeferredUpdateMessage || type == InteractionResponseType.UpdateMessage) - throw new InvalidOperationException($"Cannot use {type} on a slash command!"); - if (!IsValidToken) throw new InvalidOperationException("Interaction token is no longer valid"); @@ -124,13 +125,12 @@ namespace Discord.WebSocket 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(text) + var args = new API.Rest.CreateWebhookMessageParams { - AllowedMentions = allowedMentions?.ToModel(), + Content = text, + AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, - Embeds = embeds != null - ? embeds.Select(x => x.ToModel()).ToArray() - : Optional.Unspecified, + Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional.Unspecified, Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; @@ -140,10 +140,15 @@ namespace Discord.WebSocket return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); } - /// - public override Task AcknowledgeAsync(RequestOptions options = null) + /// + /// 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() + var response = new API.InteractionResponse { Type = InteractionResponseType.DeferredChannelMessageWithSource, }; diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs index e6a5a9d34..e49205d7c 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs @@ -1,8 +1,5 @@ using Discord.Rest; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Model = Discord.API.Interaction; @@ -96,71 +93,30 @@ namespace Discord.WebSocket } /// - /// Responds to an Interaction. + /// Responds to an Interaction with type . /// /// If you have set to , You should use - /// instead. + /// instead. /// /// /// The text of the message to be sent. - /// if the message should be read out by a text-to-speech reader, otherwise . - /// A to send with this response. - /// The type of response to this Interaction. - /// if the response should be hidden to everyone besides the invoker of the command, otherwise . - /// The allowed mentions for this response. - /// The request options for this response. - /// A to be sent with this response - /// Message content is too long, length must be less or equal to . - /// The parameters provided were invalid or the token was invalid. - public Task RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) - => RespondAsync(embed != null ? new Embed[] { embed } : null, text, isTTS, type, ephemeral, allowedMentions, options, component); - - /// - /// Sends a followup message for this interaction. - /// - /// The text of the message to be sent - /// if the message should be read out by a text-to-speech reader, otherwise . - /// A to send with this response - /// The type of response to this Interaction. - /// if the response should be hidden to everyone besides the invoker of the command, otherwise . - /// The allowed mentions for this response. - /// The request options for this response. - /// A to be sent with this response - /// - /// The sent message. - /// - public Task FollowupAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, - bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) - => FollowupAsync(embed != null ? new Embed[] { embed } : null, text, isTTS, ephemeral, type, allowedMentions, options, component); - /// - /// Responds to an Interaction. - /// - /// If you have set to , You should use - /// instead. - /// - /// - /// The text of the message to be sent. - /// if the message should be read out by a text-to-speech reader, otherwise . /// A array of embeds to send with this response. Max 10 - /// The type of response to this Interaction. + /// if the message should be read out by a text-to-speech reader, otherwise . /// if the response should be hidden to everyone besides the invoker of the command, otherwise . /// The allowed mentions for this response. /// The request options for this response. /// A to be sent with this response /// Message content is too long, length must be less or equal to . /// The parameters provided were invalid or the token was invalid. - - public abstract Task RespondAsync(Embed[] embeds = null, string text = null, bool isTTS = false, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, + public abstract Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); /// /// Sends a followup message for this interaction. /// /// The text of the message to be sent - /// if the message should be read out by a text-to-speech reader, otherwise . /// A array of embeds to send with this response. Max 10 - /// The type of response to this Interaction. + /// if the message should be read out by a text-to-speech reader, otherwise . /// if the response should be hidden to everyone besides the invoker of the command, otherwise . /// The allowed mentions for this response. /// The request options for this response. @@ -168,25 +124,45 @@ namespace Discord.WebSocket /// /// The sent message. /// - public abstract Task FollowupAsync(Embed[] embeds = null, string text = null, bool isTTS = false, bool ephemeral = false, - InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, + public abstract Task FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); /// /// Gets the original response for this interaction. /// /// The request options for this async request. - /// A that represents the intitial response, or if there is no response. + /// A that represents the initial response. public Task GetOriginalResponseAsync(RequestOptions options = null) => InteractionHelper.GetOriginalResponseAsync(this.Discord, this.Channel, this, options); /// - /// Acknowledges this interaction with the . + /// Edits original response for this interaction. + /// + /// A delegate containing the properties to modify the message with. + /// The request options for this async request. + /// A that represents the initial response. + public async Task ModifyOriginalResponseAsync(Action func, RequestOptions options = null) + { + var model = await InteractionHelper.ModifyInteractionResponse(this.Discord, this.Token, func, options); + return RestInteractionMessage.Create(this.Discord, model, this.Token, this.Channel); + } + + /// + /// Acknowledges this interaction. + /// + /// + /// A task that represents the asynchronous operation of acknowledging the interaction. + /// + [Obsolete("This method deprecated, please use DeferAsync instead")] + public Task AcknowledgeAsync(RequestOptions options = null) => DeferAsync(options); + + /// + /// Acknowledges this interaction. /// /// /// A task that represents the asynchronous operation of acknowledging the interaction. /// - public abstract Task AcknowledgeAsync(RequestOptions options = null); + public abstract Task DeferAsync(RequestOptions options = null); private bool CheckToken() { diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs index 886ff234d..528848f7f 100644 --- a/src/Discord.Net.Webhook/WebhookClientHelper.cs +++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs @@ -20,10 +20,15 @@ namespace Discord.Webhook throw new InvalidOperationException("Could not find a webhook with the supplied credentials."); return RestInternalWebhook.Create(client, model); } - public static async Task SendMessageAsync(DiscordWebhookClient client, + public static async Task SendMessageAsync(DiscordWebhookClient client, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options) { - var args = new CreateWebhookMessageParams(text) { IsTTS = isTTS }; + var args = new CreateWebhookMessageParams + { + Content = text, + IsTTS = isTTS + }; + if (embeds != null) args.Embeds = embeds.Select(x => x.ToModel()).ToArray(); if (username != null)