@@ -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> | ||||