| @@ -8,7 +8,7 @@ | |||||
| <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
| <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
| <PackageId>Discord.Net.Labs.Core</PackageId> | <PackageId>Discord.Net.Labs.Core</PackageId> | ||||
| <Version>2.3.8</Version> | |||||
| <Version>2.3.9-pre</Version> | |||||
| <Product>Discord.Net.Labs.Core</Product> | <Product>Discord.Net.Labs.Core</Product> | ||||
| <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
| <PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
| @@ -3932,6 +3932,11 @@ | |||||
| A <see cref="T:Discord.IRole"/>. | A <see cref="T:Discord.IRole"/>. | ||||
| </summary> | </summary> | ||||
| </member> | </member> | ||||
| <member name="F:Discord.ApplicationCommandOptionType.Mentionable"> | |||||
| <summary> | |||||
| </summary> | |||||
| </member> | |||||
| <member name="T:Discord.ApplicationCommandProperties"> | <member name="T:Discord.ApplicationCommandProperties"> | ||||
| <summary> | <summary> | ||||
| Provides properties that are used to modify a <see cref="T:Discord.IApplicationCommand" /> with the specified changes. | Provides properties that are used to modify a <see cref="T:Discord.IApplicationCommand" /> with the specified changes. | ||||
| @@ -4032,6 +4037,11 @@ | |||||
| </note> | </note> | ||||
| </summary> | </summary> | ||||
| </member> | </member> | ||||
| <member name="P:Discord.IApplicationCommandInteractionDataOption.Type"> | |||||
| <summary> | |||||
| The type of this data's option. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="P:Discord.IApplicationCommandInteractionDataOption.Options"> | <member name="P:Discord.IApplicationCommandInteractionDataOption.Options"> | ||||
| <summary> | <summary> | ||||
| Present if this option is a group or subcommand. | Present if this option is a group or subcommand. | ||||
| @@ -49,6 +49,11 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// A <see cref="IRole"/>. | /// A <see cref="IRole"/>. | ||||
| /// </summary> | /// </summary> | ||||
| Role = 8 | |||||
| Role = 8, | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| Mentionable = 9 | |||||
| } | } | ||||
| } | } | ||||
| @@ -24,6 +24,11 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| object Value { get; } | object Value { get; } | ||||
| /// <summary> | |||||
| /// The type of this data's option. | |||||
| /// </summary> | |||||
| ApplicationCommandOptionType Type { get; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Present if this option is a group or subcommand. | /// Present if this option is a group or subcommand. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -341,7 +341,7 @@ namespace Discord | |||||
| Default = this.Default, | Default = this.Default, | ||||
| Required = this.Required, | Required = this.Required, | ||||
| Type = this.Type, | Type = this.Type, | ||||
| Options = new List<ApplicationCommandOptionProperties>(this.Options.Select(x => x.Build())), | |||||
| Options = this.Options?.Count > 0 ? new List<ApplicationCommandOptionProperties>(this.Options.Select(x => x.Build())) : null, | |||||
| Choices = this.Choices | Choices = this.Choices | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -12,6 +12,10 @@ namespace Discord.API | |||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("options")] | [JsonProperty("options")] | ||||
| public List<ApplicationCommandInteractionDataOption> Options { get; set; } = new(); | |||||
| public List<ApplicationCommandInteractionDataOption> Options { get; set; } | |||||
| [JsonProperty("resolved")] | |||||
| public Optional<ApplicationCommandInteractionDataResolved> Resolved { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,15 +6,15 @@ namespace Discord.API | |||||
| internal class ApplicationCommandInteractionDataResolved | internal class ApplicationCommandInteractionDataResolved | ||||
| { | { | ||||
| [JsonProperty("users")] | [JsonProperty("users")] | ||||
| public Optional<Dictionary<ulong, User>> Users { get; set; } | |||||
| public Optional<Dictionary<string, User>> Users { get; set; } | |||||
| [JsonProperty("members")] | [JsonProperty("members")] | ||||
| public Optional<Dictionary<ulong, GuildMember>> Members { get; set; } | |||||
| public Optional<Dictionary<string, GuildMember>> Members { get; set; } | |||||
| [JsonProperty("channels")] | [JsonProperty("channels")] | ||||
| public Optional<Dictionary<ulong, Channel>> Channels { get; set; } | |||||
| public Optional<Dictionary<string, Channel>> Channels { get; set; } | |||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public Optional<Dictionary<ulong, Role>> Roles { get; set; } | |||||
| public Optional<Dictionary<string, Role>> Roles { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -9,7 +9,7 @@ | |||||
| <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
| <PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
| <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | ||||
| <Version>2.3.8</Version> | |||||
| <Version>2.3.9-pre</Version> | |||||
| <PackageId>Discord.Net.Labs.Rest</PackageId> | <PackageId>Discord.Net.Labs.Rest</PackageId> | ||||
| <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
| <AssemblyVersion>2.3.4</AssemblyVersion> | <AssemblyVersion>2.3.4</AssemblyVersion> | ||||
| @@ -10,20 +10,20 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class InteractionHelper | internal static class InteractionHelper | ||||
| { | { | ||||
| internal static async Task<RestInteractionMessage> SendInteractionResponse(BaseDiscordClient client, IMessageChannel channel, InteractionResponse response, | |||||
| internal static Task SendInteractionResponse(BaseDiscordClient client, IMessageChannel channel, InteractionResponse response, | |||||
| ulong interactionId, string interactionToken, RequestOptions options = null) | ulong interactionId, string interactionToken, RequestOptions options = null) | ||||
| { | { | ||||
| await client.ApiClient.CreateInteractionResponse(response, interactionId, interactionToken, options).ConfigureAwait(false); | |||||
| // get the original message | |||||
| var msg = await client.ApiClient.GetInteractionResponse(interactionToken).ConfigureAwait(false); | |||||
| var entity = RestInteractionMessage.Create(client, msg, interactionToken, channel); | |||||
| return client.ApiClient.CreateInteractionResponse(response, interactionId, interactionToken, options); | |||||
| } | |||||
| return entity; | |||||
| internal static async Task<RestInteractionMessage> GetOriginalResponseAsync(BaseDiscordClient client, IMessageChannel channel, | |||||
| IDiscordInteraction interaction, RequestOptions options = null) | |||||
| { | |||||
| var model = await client.ApiClient.GetInteractionResponse(interaction.Token, options).ConfigureAwait(false); | |||||
| return RestInteractionMessage.Create(client, model, interaction.Token, channel); | |||||
| } | } | ||||
| internal static async Task<RestFollowupMessage> SendFollowupAsync(BaseDiscordClient client, API.Rest.CreateWebhookMessageParams args, | |||||
| internal static async Task<RestFollowupMessage> SendFollowupAsync(BaseDiscordClient client, CreateWebhookMessageParams args, | |||||
| string token, IMessageChannel channel, RequestOptions options = null) | string token, IMessageChannel channel, RequestOptions options = null) | ||||
| { | { | ||||
| var model = await client.ApiClient.CreateInteractionFollowupMessage(args, token, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateInteractionFollowupMessage(args, token, options).ConfigureAwait(false); | ||||
| @@ -8,7 +8,7 @@ | |||||
| <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
| <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> | ||||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
| <Version>2.3.8</Version> | |||||
| <Version>2.3.9-pre</Version> | |||||
| <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | <RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl> | ||||
| <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | <PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl> | ||||
| <PackageIcon>Temporary.png</PackageIcon> | <PackageIcon>Temporary.png</PackageIcon> | ||||
| @@ -3299,41 +3299,17 @@ | |||||
| The data associated with this interaction. | The data associated with this interaction. | ||||
| </summary> | </summary> | ||||
| </member> | </member> | ||||
| <member name="M:Discord.WebSocket.SocketSlashCommand.RespondAsync(System.String,System.Boolean,Discord.Embed,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||||
| <member name="M:Discord.WebSocket.SocketSlashCommand.GetOriginalResponse"> | |||||
| <summary> | <summary> | ||||
| Responds to an Interaction. | |||||
| <para> | |||||
| If you have <see cref="P:Discord.WebSocket.DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||||
| <see cref="!:FollowupAsync(string, bool, Embed, InteractionResponseType, AllowedMentions, RequestOptions)"/> instead. | |||||
| </para> | |||||
| Gets the original response to this slash command. | |||||
| </summary> | </summary> | ||||
| <param name="text">The text of the message to be sent.</param> | |||||
| <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||||
| <param name="embed">A <see cref="T:Discord.Embed"/> to send with this response.</param> | |||||
| <param name="type">The type of response to this Interaction.</param> | |||||
| <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||||
| <param name="allowedMentions">The allowed mentions for this response.</param> | |||||
| <param name="options">The request options for this response.</param> | |||||
| <returns> | |||||
| The <see cref="T:Discord.IMessage"/> sent as the response. If this is the first acknowledgement, it will return null. | |||||
| </returns> | |||||
| <exception cref="T:System.ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| <exception cref="T:System.InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||||
| <returns>A <see cref="T:Discord.Rest.RestInteractionMessage"/> that represents the initial response to this interaction.</returns> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketSlashCommand.RespondAsync(System.String,System.Boolean,Discord.Embed,Discord.InteractionResponseType,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | |||||
| <inheritdoc/> | |||||
| </member> | </member> | ||||
| <member name="M:Discord.WebSocket.SocketSlashCommand.FollowupAsync(System.String,System.Boolean,Discord.Embed,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | <member name="M:Discord.WebSocket.SocketSlashCommand.FollowupAsync(System.String,System.Boolean,Discord.Embed,System.Boolean,Discord.InteractionResponseType,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent)"> | ||||
| <summary> | |||||
| Sends a followup message for this interaction. | |||||
| </summary> | |||||
| <param name="text">The text of the message to be sent</param> | |||||
| <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||||
| <param name="embed">A <see cref="T:Discord.Embed"/> to send with this response.</param> | |||||
| <param name="type">The type of response to this Interaction.</param> | |||||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||||
| <param name="allowedMentions">The allowed mentions for this response.</param> | |||||
| <param name="options">The request options for this response.</param> | |||||
| <returns> | |||||
| The sent message. | |||||
| </returns> | |||||
| <inheritdoc/> | |||||
| </member> | </member> | ||||
| <member name="T:Discord.WebSocket.SocketSlashCommandData"> | <member name="T:Discord.WebSocket.SocketSlashCommandData"> | ||||
| <summary> | <summary> | ||||
| @@ -3359,6 +3335,9 @@ | |||||
| <member name="P:Discord.WebSocket.SocketSlashCommandDataOption.Value"> | <member name="P:Discord.WebSocket.SocketSlashCommandDataOption.Value"> | ||||
| <inheritdoc/> | <inheritdoc/> | ||||
| </member> | </member> | ||||
| <member name="P:Discord.WebSocket.SocketSlashCommandDataOption.Type"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="P:Discord.WebSocket.SocketSlashCommandDataOption.Options"> | <member name="P:Discord.WebSocket.SocketSlashCommandDataOption.Options"> | ||||
| <summary> | <summary> | ||||
| The sub command options received for this sub command group. | The sub command options received for this sub command group. | ||||
| @@ -3420,9 +3399,6 @@ | |||||
| <param name="allowedMentions">The allowed mentions for this response.</param> | <param name="allowedMentions">The allowed mentions for this response.</param> | ||||
| <param name="options">The request options for this response.</param> | <param name="options">The request options for this response.</param> | ||||
| <param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param> | <param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param> | ||||
| <returns> | |||||
| The <see cref="T:Discord.IMessage"/> sent as the response. If this is the first acknowledgement, it will return null. | |||||
| </returns> | |||||
| <exception cref="T:System.ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>.</exception> | <exception cref="T:System.ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="F:Discord.DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| <exception cref="T:System.InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | <exception cref="T:System.InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | ||||
| </member> | </member> | ||||
| @@ -3442,6 +3418,13 @@ | |||||
| The sent message. | The sent message. | ||||
| </returns> | </returns> | ||||
| </member> | </member> | ||||
| <member name="M:Discord.WebSocket.SocketInteraction.GetOriginalResponseAsync(Discord.RequestOptions)"> | |||||
| <summary> | |||||
| Gets the original response for this interaction. | |||||
| </summary> | |||||
| <param name="options">The request options for this async request.</param> | |||||
| <returns>A <see cref="T:Discord.Rest.RestInteractionMessage"/> that represents the intitial response, or <see langword="null"/> if there is no response.</returns> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketInteraction.AcknowledgeAsync(Discord.RequestOptions)"> | <member name="M:Discord.WebSocket.SocketInteraction.AcknowledgeAsync(Discord.RequestOptions)"> | ||||
| <summary> | <summary> | ||||
| Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | ||||
| @@ -25,7 +25,7 @@ namespace Discord.WebSocket | |||||
| public partial class DiscordSocketClient : BaseSocketClient, IDiscordClient | public partial class DiscordSocketClient : BaseSocketClient, IDiscordClient | ||||
| { | { | ||||
| private readonly ConcurrentQueue<ulong> _largeGuilds; | private readonly ConcurrentQueue<ulong> _largeGuilds; | ||||
| private readonly JsonSerializer _serializer; | |||||
| internal readonly JsonSerializer _serializer; | |||||
| private readonly DiscordShardedClient _shardedClient; | private readonly DiscordShardedClient _shardedClient; | ||||
| private readonly DiscordSocketClient _parentClient; | private readonly DiscordSocketClient _parentClient; | ||||
| private readonly ConcurrentQueue<long> _heartbeatTimes; | private readonly ConcurrentQueue<long> _heartbeatTimes; | ||||
| @@ -794,6 +794,16 @@ namespace Discord.WebSocket | |||||
| return null; | return null; | ||||
| } | } | ||||
| internal SocketRole AddOrUpdateRole(RoleModel model) | |||||
| { | |||||
| if (_roles.TryGetValue(model.Id, out SocketRole role)) | |||||
| _roles[model.Id].Update(this.Discord.State, model); | |||||
| else | |||||
| role = AddRole(model); | |||||
| return role; | |||||
| } | |||||
| //Users | //Users | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestGuildUser> AddGuildUserAsync(ulong id, string accessToken, Action<AddGuildUserProperties> func = null, RequestOptions options = null) | public Task<RestGuildUser> AddGuildUserAsync(ulong id, string accessToken, Action<AddGuildUserProperties> func = null, RequestOptions options = null) | ||||
| @@ -92,7 +92,7 @@ namespace Discord.WebSocket | |||||
| /// </returns> | /// </returns> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | ||||
| public override async Task<RestUserMessage> RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||||
| public override async 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) | bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | ||||
| { | { | ||||
| if (type == InteractionResponseType.Pong) | if (type == InteractionResponseType.Pong) | ||||
| @@ -102,7 +102,10 @@ namespace Discord.WebSocket | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
| if (Discord.AlwaysAcknowledgeInteractions) | if (Discord.AlwaysAcknowledgeInteractions) | ||||
| return await FollowupAsync(text, isTTS, embed, ephemeral, type, allowedMentions, options); | |||||
| { | |||||
| await FollowupAsync(text, isTTS, embed, ephemeral, type, allowedMentions, options); | |||||
| return; | |||||
| } | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | 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(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | ||||
| @@ -141,7 +144,7 @@ namespace Discord.WebSocket | |||||
| if (ephemeral) | if (ephemeral) | ||||
| response.Data.Value.Flags = 64; | response.Data.Value.Flags = 64; | ||||
| return await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||||
| await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -22,10 +22,14 @@ namespace Discord.WebSocket | |||||
| : base(client, model.Id, channel) | : base(client, model.Id, channel) | ||||
| { | { | ||||
| var dataModel = model.Data.IsSpecified ? | var dataModel = model.Data.IsSpecified ? | ||||
| (model.Data.Value as JToken).ToObject<DataModel>() | |||||
| (model.Data.Value as JToken).ToObject<DataModel>(client._serializer) | |||||
| : null; | : null; | ||||
| Data = SocketSlashCommandData.Create(client, dataModel, model.Id); | |||||
| ulong? guildId = null; | |||||
| if (this.Channel is SocketGuildChannel guildChannel) | |||||
| guildId = guildChannel.Guild.Id; | |||||
| Data = SocketSlashCommandData.Create(client, dataModel, model.Id, guildId); | |||||
| } | } | ||||
| new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | ||||
| @@ -38,7 +42,7 @@ namespace Discord.WebSocket | |||||
| internal override void Update(Model model) | internal override void Update(Model model) | ||||
| { | { | ||||
| var data = model.Data.IsSpecified ? | var data = model.Data.IsSpecified ? | ||||
| (model.Data.Value as JToken).ToObject<DataModel>() | |||||
| (model.Data.Value as JToken).ToObject<DataModel>(Discord._serializer) | |||||
| : null; | : null; | ||||
| this.Data.Update(data); | this.Data.Update(data); | ||||
| @@ -47,26 +51,21 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Responds to an Interaction. | |||||
| /// <para> | |||||
| /// If you have <see cref="DiscordSocketConfig.AlwaysAcknowledgeInteractions"/> set to <see langword="true"/>, You should use | |||||
| /// <see cref="FollowupAsync(string, bool, Embed, InteractionResponseType, AllowedMentions, RequestOptions)"/> instead. | |||||
| /// </para> | |||||
| /// Gets the original response to this slash command. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="text">The text of the message to be sent.</param> | |||||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||||
| /// <param name="embed">A <see cref="Embed"/> to send with this response.</param> | |||||
| /// <param name="type">The type of response to this Interaction.</param> | |||||
| /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||||
| /// <param name="options">The request options for this response.</param> | |||||
| /// <returns> | |||||
| /// The <see cref="IMessage"/> sent as the response. If this is the first acknowledgement, it will return null. | |||||
| /// </returns> | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||||
| public override async Task<RestUserMessage> RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||||
| /// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response to this interaction.</returns> | |||||
| public async Task<RestInteractionMessage> GetOriginalResponse() | |||||
| { | |||||
| // get the original message | |||||
| var msg = await Discord.ApiClient.GetInteractionResponse(this.Token).ConfigureAwait(false); | |||||
| var entity = RestInteractionMessage.Create(Discord, msg, this.Token, this.Channel); | |||||
| return entity; | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public override async 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) | bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | ||||
| { | { | ||||
| if (type == InteractionResponseType.Pong) | if (type == InteractionResponseType.Pong) | ||||
| @@ -79,7 +78,10 @@ namespace Discord.WebSocket | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | throw new InvalidOperationException("Interaction token is no longer valid"); | ||||
| if (Discord.AlwaysAcknowledgeInteractions) | if (Discord.AlwaysAcknowledgeInteractions) | ||||
| return await FollowupAsync(text, isTTS, embed, ephemeral, type, allowedMentions, options); // The arguments should be passed? What was i thinking... | |||||
| { | |||||
| await FollowupAsync(text, isTTS, embed, ephemeral, type, allowedMentions, options); | |||||
| return; | |||||
| } | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | 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(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | ||||
| @@ -118,22 +120,10 @@ namespace Discord.WebSocket | |||||
| if (ephemeral) | if (ephemeral) | ||||
| response.Data.Value.Flags = 64; | response.Data.Value.Flags = 64; | ||||
| return await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||||
| await InteractionHelper.SendInteractionResponse(this.Discord, this.Channel, response, this.Id, Token, options); | |||||
| } | } | ||||
| /// <summary> | |||||
| /// Sends a followup message for this interaction. | |||||
| /// </summary> | |||||
| /// <param name="text">The text of the message to be sent</param> | |||||
| /// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||||
| /// <param name="embed">A <see cref="Embed"/> to send with this response.</param> | |||||
| /// <param name="type">The type of response to this Interaction.</param> | |||||
| /// /// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | |||||
| /// <param name="options">The request options for this response.</param> | |||||
| /// <returns> | |||||
| /// The sent message. | |||||
| /// </returns> | |||||
| /// <inheritdoc/> | |||||
| public override async Task<RestFollowupMessage> FollowupAsync(string text = null, bool isTTS = false, Embed embed = null, bool ephemeral = false, | public override async Task<RestFollowupMessage> FollowupAsync(string text = null, bool isTTS = false, Embed embed = null, bool ephemeral = false, | ||||
| InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | ||||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null) | ||||
| @@ -18,15 +18,71 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | ||||
| internal SocketSlashCommandData(DiscordSocketClient client, ulong id) | |||||
| : base(client, id) | |||||
| internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; } = new(); | |||||
| internal Dictionary<ulong, SocketGlobalUser> users { get; private set; } = new(); | |||||
| internal Dictionary<ulong, SocketChannel> channels { get; private set; } = new(); | |||||
| internal Dictionary<ulong, SocketRole> roles { get; private set; } = new(); | |||||
| private ulong? guildId; | |||||
| internal SocketSlashCommandData(DiscordSocketClient client, Model model, ulong? guildId) | |||||
| : base(client, model.Id) | |||||
| { | { | ||||
| this.guildId = guildId; | |||||
| 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 = channel.Value.GuildId.IsSpecified | |||||
| ? SocketGuildChannel.Create(Discord.GetGuild(channel.Value.GuildId.Value), Discord.State, channel.Value) | |||||
| : SocketDMChannel.Create(Discord, Discord.State, channel.Value); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| internal static SocketSlashCommandData Create(DiscordSocketClient client, Model model, ulong id) | |||||
| internal static SocketSlashCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId) | |||||
| { | { | ||||
| var entity = new SocketSlashCommandData(client, model.Id); | |||||
| var entity = new SocketSlashCommandData(client, model, guildId); | |||||
| entity.Update(model); | entity.Update(model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| @@ -35,7 +91,7 @@ namespace Discord.WebSocket | |||||
| this.Name = model.Name; | this.Name = model.Name; | ||||
| this.Options = model.Options.Any() | this.Options = model.Options.Any() | ||||
| ? model.Options.Select(x => new SocketSlashCommandDataOption(x, this.Discord)).ToImmutableArray() | |||||
| ? model.Options.Select(x => new SocketSlashCommandDataOption(this, x)).ToImmutableArray() | |||||
| : null; | : null; | ||||
| } | } | ||||
| @@ -16,22 +16,25 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public object Value { get; private set; } | public object Value { get; private set; } | ||||
| /// <inheritdoc/> | |||||
| public ApplicationCommandOptionType Type { get; private set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// The sub command options received for this sub command group. | /// The sub command options received for this sub command group. | ||||
| /// </summary> | /// </summary> | ||||
| public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | public IReadOnlyCollection<SocketSlashCommandDataOption> Options { get; private set; } | ||||
| private DiscordSocketClient discord; | |||||
| private SocketSlashCommandData data; | |||||
| internal SocketSlashCommandDataOption() { } | internal SocketSlashCommandDataOption() { } | ||||
| internal SocketSlashCommandDataOption(Model model, DiscordSocketClient discord) | |||||
| internal SocketSlashCommandDataOption(SocketSlashCommandData data, Model model) | |||||
| { | { | ||||
| this.Name = model.Name; | this.Name = model.Name; | ||||
| this.Value = model.Value.IsSpecified ? model.Value.Value : null; | this.Value = model.Value.IsSpecified ? model.Value.Value : null; | ||||
| this.discord = discord; | |||||
| this.Options = model.Options.Any() | this.Options = model.Options.Any() | ||||
| ? model.Options.Select(x => new SocketSlashCommandDataOption(x, discord)).ToImmutableArray() | |||||
| ? model.Options.Select(x => new SocketSlashCommandDataOption(data, x)).ToImmutableArray() | |||||
| : null; | : null; | ||||
| } | } | ||||
| @@ -43,16 +46,12 @@ namespace Discord.WebSocket | |||||
| public static explicit operator string(SocketSlashCommandDataOption option) | public static explicit operator string(SocketSlashCommandDataOption option) | ||||
| => option.Value.ToString(); | => option.Value.ToString(); | ||||
| public static explicit operator SocketGuildChannel(SocketSlashCommandDataOption option) | |||||
| public static explicit operator SocketChannel(SocketSlashCommandDataOption option) | |||||
| { | { | ||||
| if (option.Value is ulong id) | |||||
| if(ulong.TryParse(option.Value.ToString(), out ulong id)) | |||||
| { | { | ||||
| var guild = option.discord.GetGuild(id); | |||||
| if (guild == null) | |||||
| return null; | |||||
| return guild.GetChannel(id); | |||||
| if (option.data.channels.TryGetValue(id, out var channel)) | |||||
| return channel; | |||||
| } | } | ||||
| return null; | return null; | ||||
| @@ -60,34 +59,35 @@ namespace Discord.WebSocket | |||||
| public static explicit operator SocketRole(SocketSlashCommandDataOption option) | public static explicit operator SocketRole(SocketSlashCommandDataOption option) | ||||
| { | { | ||||
| if (option.Value is ulong id) | |||||
| if (ulong.TryParse(option.Value.ToString(), out ulong id)) | |||||
| { | { | ||||
| var guild = option.discord.GetGuild(id); | |||||
| if (guild == null) | |||||
| return null; | |||||
| return guild.GetRole(id); | |||||
| if (option.data.roles.TryGetValue(id, out var role)) | |||||
| return role; | |||||
| } | } | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static explicit operator SocketGuildUser(SocketSlashCommandDataOption option) | |||||
| public static explicit operator SocketUser(SocketSlashCommandDataOption option) | |||||
| { | { | ||||
| if(option.Value is ulong id) | |||||
| if (ulong.TryParse(option.Value.ToString(), out ulong id)) | |||||
| { | { | ||||
| var guild = option.discord.GetGuild(id); | |||||
| if (option.data.users.TryGetValue(id, out var user)) | |||||
| return user; | |||||
| } | |||||
| if (guild == null) | |||||
| return null; | |||||
| return null; | |||||
| } | |||||
| return guild.GetUser(id); | |||||
| } | |||||
| public static explicit operator SocketGuildUser(SocketSlashCommandDataOption option) | |||||
| { | |||||
| if (option.Value as SocketUser is SocketGuildUser guildUser) | |||||
| return guildUser; | |||||
| return null; | return null; | ||||
| } | } | ||||
| IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionDataOption.Options => this.Options; | IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionDataOption.Options => this.Options; | ||||
| } | } | ||||
| } | } | ||||
| @@ -110,13 +110,10 @@ namespace Discord.WebSocket | |||||
| /// <param name="allowedMentions">The allowed mentions for this response.</param> | /// <param name="allowedMentions">The allowed mentions for this response.</param> | ||||
| /// <param name="options">The request options for this response.</param> | /// <param name="options">The request options for this response.</param> | ||||
| /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | /// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param> | ||||
| /// <returns> | |||||
| /// The <see cref="IMessage"/> sent as the response. If this is the first acknowledgement, it will return null. | |||||
| /// </returns> | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | ||||
| public abstract Task<RestUserMessage> RespondAsync(string text = null, bool isTTS = false, Embed embed = null, InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | |||||
| public abstract 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); | bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -137,6 +134,16 @@ namespace Discord.WebSocket | |||||
| InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | InteractionResponseType type = InteractionResponseType.ChannelMessageWithSource, | ||||
| AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); | AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null); | ||||
| /// <summary> | |||||
| /// Gets the original response for this interaction. | |||||
| /// </summary> | |||||
| /// <param name="options">The request options for this async request.</param> | |||||
| /// <returns>A <see cref="RestInteractionMessage"/> that represents the intitial response, or <see langword="null"/> if there is no response.</returns> | |||||
| public Task<RestInteractionMessage> GetOriginalResponseAsync(RequestOptions options = null) | |||||
| { | |||||
| return InteractionHelper.GetOriginalResponseAsync(this.Discord, this.Channel, this, options); | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | /// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | ||||
| /// </summary> | /// </summary> | ||||