| @@ -17,5 +17,8 @@ namespace Discord.API | |||||
| [JsonProperty("resolved")] | [JsonProperty("resolved")] | ||||
| public Optional<ApplicationCommandInteractionDataResolved> Resolved { get; set; } | public Optional<ApplicationCommandInteractionDataResolved> Resolved { get; set; } | ||||
| [JsonProperty("type")] | |||||
| public Optional<ApplicationCommandType> Type { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -16,5 +16,7 @@ namespace Discord.API | |||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public Optional<Dictionary<string, Role>> Roles { get; set; } | public Optional<Dictionary<string, Role>> Roles { get; set; } | ||||
| [JsonProperty("messages")] | |||||
| public Optional<Dictionary<string, Message>> Messages { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -3599,6 +3599,70 @@ | |||||
| <member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetWebhooksAsync(Discord.RequestOptions)"> | <member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetWebhooksAsync(Discord.RequestOptions)"> | ||||
| <inheritdoc /> | <inheritdoc /> | ||||
| </member> | </member> | ||||
| <member name="T:Discord.WebSocket.SocketApplicationMessageCommand"> | |||||
| <summary> | |||||
| Represents a Websocket-based slash command received over the gateway. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="P:Discord.WebSocket.SocketApplicationMessageCommand.Data"> | |||||
| <summary> | |||||
| The data associated with this interaction. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationMessageCommand.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationMessageCommand.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationMessageCommand.DeferAsync(Discord.RequestOptions)"> | |||||
| <summary> | |||||
| Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||||
| </summary> | |||||
| <returns> | |||||
| A task that represents the asynchronous operation of acknowledging the interaction. | |||||
| </returns> | |||||
| </member> | |||||
| <member name="T:Discord.WebSocket.SocketApplicationMessageCommandData"> | |||||
| <summary> | |||||
| Represents the data tied with the <see cref="T:Discord.WebSocket.SocketSlashCommand"/> interaction. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="P:Discord.WebSocket.SocketApplicationMessageCommandData.Name"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="T:Discord.WebSocket.SocketApplicationUserCommand"> | |||||
| <summary> | |||||
| Represents a Websocket-based slash command received over the gateway. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="P:Discord.WebSocket.SocketApplicationUserCommand.Data"> | |||||
| <summary> | |||||
| The data associated with this interaction. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationUserCommand.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationUserCommand.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="M:Discord.WebSocket.SocketApplicationUserCommand.DeferAsync(Discord.RequestOptions)"> | |||||
| <summary> | |||||
| Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||||
| </summary> | |||||
| <returns> | |||||
| A task that represents the asynchronous operation of acknowledging the interaction. | |||||
| </returns> | |||||
| </member> | |||||
| <member name="T:Discord.WebSocket.SocketApplicationUserCommandData"> | |||||
| <summary> | |||||
| Represents the data tied with the <see cref="T:Discord.WebSocket.SocketSlashCommand"/> interaction. | |||||
| </summary> | |||||
| </member> | |||||
| <member name="P:Discord.WebSocket.SocketApplicationUserCommandData.Name"> | |||||
| <inheritdoc/> | |||||
| </member> | |||||
| <member name="T:Discord.WebSocket.SocketMessageComponent"> | <member name="T:Discord.WebSocket.SocketMessageComponent"> | ||||
| <summary> | <summary> | ||||
| Represents a Websocket-based interaction type for Message Components. | Represents a Websocket-based interaction type for Message Components. | ||||
| @@ -0,0 +1,165 @@ | |||||
| using Discord.Rest; | |||||
| using System; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | |||||
| using DataModel = Discord.API.ApplicationCommandInteractionData; | |||||
| using Model = Discord.API.Interaction; | |||||
| namespace Discord.WebSocket | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a Websocket-based slash command received over the gateway. | |||||
| /// </summary> | |||||
| public class SocketApplicationMessageCommand : SocketSlashCommand | |||||
| { | |||||
| /// <summary> | |||||
| /// The data associated with this interaction. | |||||
| /// </summary> | |||||
| new public SocketApplicationMessageCommandData Data { get; } | |||||
| internal SocketApplicationMessageCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | |||||
| : base(client, model, channel) | |||||
| { | |||||
| var dataModel = model.Data.IsSpecified ? | |||||
| (DataModel)model.Data.Value | |||||
| : null; | |||||
| ulong? guildId = null; | |||||
| if (this.Channel is SocketGuildChannel guildChannel) | |||||
| guildId = guildChannel.Guild.Id; | |||||
| Data = SocketApplicationMessageCommandData.Create(client, dataModel, model.Id, guildId); | |||||
| } | |||||
| new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | |||||
| { | |||||
| var entity = new SocketApplicationMessageCommand(client, model, channel); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal override void Update(Model model) | |||||
| { | |||||
| var data = model.Data.IsSpecified ? | |||||
| (DataModel)model.Data.Value | |||||
| : null; | |||||
| this.Data.Update(data); | |||||
| base.Update(model); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public override async Task RespondAsync( | |||||
| string text = null, | |||||
| Embed[] embeds = null, | |||||
| bool isTTS = false, | |||||
| bool ephemeral = false, | |||||
| AllowedMentions allowedMentions = null, | |||||
| RequestOptions options = null, | |||||
| MessageComponent component = null, | |||||
| Embed embed = null) | |||||
| { | |||||
| if (!IsValidToken) | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||||
| if (embeds == null && embed != null) | |||||
| embeds = new[] { embed }; | |||||
| if (Discord.AlwaysAcknowledgeInteractions) | |||||
| { | |||||
| await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component); | |||||
| return; | |||||
| } | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||||
| Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||||
| { | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||||
| allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||||
| allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| } | |||||
| var response = new API.InteractionResponse | |||||
| { | |||||
| Type = InteractionResponseType.ChannelMessageWithSource, | |||||
| Data = new API.InteractionCallbackData | |||||
| { | |||||
| Content = text, | |||||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||||
| Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||||
| TTS = isTTS ? true : Optional<bool>.Unspecified, | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| } | |||||
| }; | |||||
| if (ephemeral) | |||||
| response.Data.Value.Flags = 64; | |||||
| await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public override async Task<RestFollowupMessage> FollowupAsync( | |||||
| string text = null, | |||||
| Embed[] embeds = null, | |||||
| bool isTTS = false, | |||||
| bool ephemeral = false, | |||||
| AllowedMentions allowedMentions = null, | |||||
| RequestOptions options = null, | |||||
| MessageComponent component = null, | |||||
| Embed embed = null) | |||||
| { | |||||
| if (!IsValidToken) | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||||
| if (embeds == null && embed != null) | |||||
| embeds = new[] { embed }; | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||||
| Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||||
| var args = new API.Rest.CreateWebhookMessageParams | |||||
| { | |||||
| Content = text, | |||||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||||
| IsTTS = isTTS, | |||||
| Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| }; | |||||
| if (ephemeral) | |||||
| args.Flags = 64; | |||||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||||
| } | |||||
| /// <summary> | |||||
| /// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous operation of acknowledging the interaction. | |||||
| /// </returns> | |||||
| public override Task DeferAsync(RequestOptions options = null) | |||||
| { | |||||
| var response = new API.InteractionResponse | |||||
| { | |||||
| Type = InteractionResponseType.DeferredChannelMessageWithSource, | |||||
| }; | |||||
| return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,140 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using Model = Discord.API.ApplicationCommandInteractionData; | |||||
| namespace Discord.WebSocket | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents the data tied with the <see cref="SocketSlashCommand"/> interaction. | |||||
| /// </summary> | |||||
| public class SocketApplicationMessageCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData | |||||
| { | |||||
| /// <inheritdoc/> | |||||
| public string Name { get; private set; } | |||||
| public SocketMessage Message { get; private set; } | |||||
| internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; } | |||||
| = new Dictionary<ulong, SocketGuildUser>(); | |||||
| internal Dictionary<ulong, SocketGlobalUser> users { get; private set; } | |||||
| = new Dictionary<ulong, SocketGlobalUser>(); | |||||
| internal Dictionary<ulong, SocketChannel> channels { get; private set; } | |||||
| = new Dictionary<ulong, SocketChannel>(); | |||||
| internal Dictionary<ulong, SocketRole> roles { get; private set; } | |||||
| = new Dictionary<ulong, SocketRole>(); | |||||
| IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionData.Options => throw new System.NotImplementedException(); | |||||
| private ulong? guildId; | |||||
| private ApplicationCommandType Type; | |||||
| internal SocketApplicationMessageCommandData(DiscordSocketClient client, Model model, ulong? guildId) | |||||
| : base(client, model.Id) | |||||
| { | |||||
| this.guildId = guildId; | |||||
| this.Type = (ApplicationCommandType)model.Type; | |||||
| if (model.Resolved.IsSpecified) | |||||
| { | |||||
| var guild = this.guildId.HasValue ? Discord.GetGuild(this.guildId.Value) : null; | |||||
| var resolved = model.Resolved.Value; | |||||
| if (resolved.Users.IsSpecified) | |||||
| { | |||||
| foreach (var user in resolved.Users.Value) | |||||
| { | |||||
| var socketUser = Discord.GetOrCreateUser(this.Discord.State, user.Value); | |||||
| this.users.Add(ulong.Parse(user.Key), socketUser); | |||||
| } | |||||
| } | |||||
| if (resolved.Channels.IsSpecified) | |||||
| { | |||||
| foreach (var channel in resolved.Channels.Value) | |||||
| { | |||||
| SocketChannel socketChannel = guild != null | |||||
| ? guild.GetChannel(channel.Value.Id) | |||||
| : Discord.GetChannel(channel.Value.Id); | |||||
| if (socketChannel == null) | |||||
| { | |||||
| var channelModel = guild != null | |||||
| ? Discord.Rest.ApiClient.GetChannelAsync(guild.Id, channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult() | |||||
| : Discord.Rest.ApiClient.GetChannelAsync(channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult(); | |||||
| socketChannel = guild != null | |||||
| ? SocketGuildChannel.Create(guild, Discord.State, channelModel) | |||||
| : (SocketChannel)SocketChannel.CreatePrivate(Discord, Discord.State, channelModel); | |||||
| } | |||||
| Discord.State.AddChannel(socketChannel); | |||||
| this.channels.Add(ulong.Parse(channel.Key), socketChannel); | |||||
| } | |||||
| } | |||||
| if (resolved.Members.IsSpecified) | |||||
| { | |||||
| foreach (var member in resolved.Members.Value) | |||||
| { | |||||
| member.Value.User = resolved.Users.Value[member.Key]; | |||||
| var user = guild.AddOrUpdateUser(member.Value); | |||||
| this.guildMembers.Add(ulong.Parse(member.Key), user); | |||||
| } | |||||
| } | |||||
| if (resolved.Roles.IsSpecified) | |||||
| { | |||||
| foreach (var role in resolved.Roles.Value) | |||||
| { | |||||
| var socketRole = guild.AddOrUpdateRole(role.Value); | |||||
| this.roles.Add(ulong.Parse(role.Key), socketRole); | |||||
| } | |||||
| } | |||||
| if (resolved.Messages.IsSpecified) | |||||
| { | |||||
| foreach (var msg in resolved.Messages.Value) | |||||
| { | |||||
| var channel = client.GetChannel(msg.Value.ChannelId) as ISocketMessageChannel; | |||||
| SocketUser author; | |||||
| if (guild != null) | |||||
| { | |||||
| if (msg.Value.WebhookId.IsSpecified) | |||||
| author = SocketWebhookUser.Create(guild, client.State, msg.Value.Author.Value, msg.Value.WebhookId.Value); | |||||
| else | |||||
| author = guild.GetUser(msg.Value.Author.Value.Id); | |||||
| } | |||||
| else | |||||
| author = (channel as SocketChannel).GetUser(msg.Value.Author.Value.Id); | |||||
| if (channel == null) | |||||
| { | |||||
| if (!msg.Value.GuildId.IsSpecified) // assume it is a DM | |||||
| { | |||||
| channel = client.CreateDMChannel(msg.Value.ChannelId, msg.Value.Author.Value, client.State); | |||||
| } | |||||
| } | |||||
| this.Message = SocketMessage.Create(client, client.State, author, channel, msg.Value); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| internal static SocketApplicationMessageCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId) | |||||
| { | |||||
| var entity = new SocketApplicationMessageCommandData(client, model, guildId); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal void Update(Model model) | |||||
| { | |||||
| this.Name = model.Name; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,165 @@ | |||||
| using Discord.Rest; | |||||
| using System; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | |||||
| using DataModel = Discord.API.ApplicationCommandInteractionData; | |||||
| using Model = Discord.API.Interaction; | |||||
| namespace Discord.WebSocket | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a Websocket-based slash command received over the gateway. | |||||
| /// </summary> | |||||
| public class SocketApplicationUserCommand : SocketSlashCommand | |||||
| { | |||||
| /// <summary> | |||||
| /// The data associated with this interaction. | |||||
| /// </summary> | |||||
| new public SocketApplicationUserCommandData Data { get; } | |||||
| internal SocketApplicationUserCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | |||||
| : base(client, model, channel) | |||||
| { | |||||
| var dataModel = model.Data.IsSpecified ? | |||||
| (DataModel)model.Data.Value | |||||
| : null; | |||||
| ulong? guildId = null; | |||||
| if (this.Channel is SocketGuildChannel guildChannel) | |||||
| guildId = guildChannel.Guild.Id; | |||||
| Data = SocketApplicationUserCommandData.Create(client, dataModel, model.Id, guildId); | |||||
| } | |||||
| new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | |||||
| { | |||||
| var entity = new SocketApplicationUserCommand(client, model, channel); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal override void Update(Model model) | |||||
| { | |||||
| var data = model.Data.IsSpecified ? | |||||
| (DataModel)model.Data.Value | |||||
| : null; | |||||
| this.Data.Update(data); | |||||
| base.Update(model); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public override async Task RespondAsync( | |||||
| string text = null, | |||||
| Embed[] embeds = null, | |||||
| bool isTTS = false, | |||||
| bool ephemeral = false, | |||||
| AllowedMentions allowedMentions = null, | |||||
| RequestOptions options = null, | |||||
| MessageComponent component = null, | |||||
| Embed embed = null) | |||||
| { | |||||
| if (!IsValidToken) | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||||
| if (embeds == null && embed != null) | |||||
| embeds = new[] { embed }; | |||||
| if (Discord.AlwaysAcknowledgeInteractions) | |||||
| { | |||||
| await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component); | |||||
| return; | |||||
| } | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||||
| Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||||
| { | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||||
| allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||||
| allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| } | |||||
| var response = new API.InteractionResponse | |||||
| { | |||||
| Type = InteractionResponseType.ChannelMessageWithSource, | |||||
| Data = new API.InteractionCallbackData | |||||
| { | |||||
| Content = text, | |||||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||||
| Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||||
| TTS = isTTS ? true : Optional<bool>.Unspecified, | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| } | |||||
| }; | |||||
| if (ephemeral) | |||||
| response.Data.Value.Flags = 64; | |||||
| await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public override async Task<RestFollowupMessage> FollowupAsync( | |||||
| string text = null, | |||||
| Embed[] embeds = null, | |||||
| bool isTTS = false, | |||||
| bool ephemeral = false, | |||||
| AllowedMentions allowedMentions = null, | |||||
| RequestOptions options = null, | |||||
| MessageComponent component = null, | |||||
| Embed embed = null) | |||||
| { | |||||
| if (!IsValidToken) | |||||
| throw new InvalidOperationException("Interaction token is no longer valid"); | |||||
| if (embeds == null && embed != null) | |||||
| embeds = new[] { embed }; | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||||
| Preconditions.AtMost(embeds?.Length ?? 0, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||||
| var args = new API.Rest.CreateWebhookMessageParams | |||||
| { | |||||
| Content = text, | |||||
| AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||||
| IsTTS = isTTS, | |||||
| Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| }; | |||||
| if (ephemeral) | |||||
| args.Flags = 64; | |||||
| return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options); | |||||
| } | |||||
| /// <summary> | |||||
| /// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous operation of acknowledging the interaction. | |||||
| /// </returns> | |||||
| public override Task DeferAsync(RequestOptions options = null) | |||||
| { | |||||
| var response = new API.InteractionResponse | |||||
| { | |||||
| Type = InteractionResponseType.DeferredChannelMessageWithSource, | |||||
| }; | |||||
| return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,113 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using Model = Discord.API.ApplicationCommandInteractionData; | |||||
| namespace Discord.WebSocket | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents the data tied with the <see cref="SocketSlashCommand"/> interaction. | |||||
| /// </summary> | |||||
| public class SocketApplicationUserCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData | |||||
| { | |||||
| /// <inheritdoc/> | |||||
| public string Name { get; private set; } | |||||
| public SocketUser Member { get; private set; } | |||||
| internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; } | |||||
| = new Dictionary<ulong, SocketGuildUser>(); | |||||
| internal Dictionary<ulong, SocketGlobalUser> users { get; private set; } | |||||
| = new Dictionary<ulong, SocketGlobalUser>(); | |||||
| internal Dictionary<ulong, SocketChannel> channels { get; private set; } | |||||
| = new Dictionary<ulong, SocketChannel>(); | |||||
| internal Dictionary<ulong, SocketRole> roles { get; private set; } | |||||
| = new Dictionary<ulong, SocketRole>(); | |||||
| IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionData.Options => throw new System.NotImplementedException(); | |||||
| private ulong? guildId; | |||||
| private ApplicationCommandType Type; | |||||
| internal SocketApplicationUserCommandData(DiscordSocketClient client, Model model, ulong? guildId) | |||||
| : base(client, model.Id) | |||||
| { | |||||
| this.guildId = guildId; | |||||
| this.Type = (ApplicationCommandType)model.Type; | |||||
| if (model.Resolved.IsSpecified) | |||||
| { | |||||
| var guild = this.guildId.HasValue ? Discord.GetGuild(this.guildId.Value) : null; | |||||
| var resolved = model.Resolved.Value; | |||||
| if (resolved.Users.IsSpecified) | |||||
| { | |||||
| foreach (var user in resolved.Users.Value) | |||||
| { | |||||
| var socketUser = Discord.GetOrCreateUser(this.Discord.State, user.Value); | |||||
| this.users.Add(ulong.Parse(user.Key), socketUser); | |||||
| } | |||||
| } | |||||
| if (resolved.Channels.IsSpecified) | |||||
| { | |||||
| foreach (var channel in resolved.Channels.Value) | |||||
| { | |||||
| SocketChannel socketChannel = guild != null | |||||
| ? guild.GetChannel(channel.Value.Id) | |||||
| : Discord.GetChannel(channel.Value.Id); | |||||
| if (socketChannel == null) | |||||
| { | |||||
| var channelModel = guild != null | |||||
| ? Discord.Rest.ApiClient.GetChannelAsync(guild.Id, channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult() | |||||
| : Discord.Rest.ApiClient.GetChannelAsync(channel.Value.Id).ConfigureAwait(false).GetAwaiter().GetResult(); | |||||
| socketChannel = guild != null | |||||
| ? SocketGuildChannel.Create(guild, Discord.State, channelModel) | |||||
| : (SocketChannel)SocketChannel.CreatePrivate(Discord, Discord.State, channelModel); | |||||
| } | |||||
| Discord.State.AddChannel(socketChannel); | |||||
| this.channels.Add(ulong.Parse(channel.Key), socketChannel); | |||||
| } | |||||
| } | |||||
| if (resolved.Members.IsSpecified) | |||||
| { | |||||
| foreach (var member in resolved.Members.Value) | |||||
| { | |||||
| member.Value.User = resolved.Users.Value[member.Key]; | |||||
| var user = guild.AddOrUpdateUser(member.Value); | |||||
| this.guildMembers.Add(ulong.Parse(member.Key), user); | |||||
| this.Member = 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 SocketApplicationUserCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId) | |||||
| { | |||||
| var entity = new SocketApplicationUserCommandData(client, model, guildId); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal void Update(Model model) | |||||
| { | |||||
| this.Name = model.Name; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -2,6 +2,7 @@ using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Interaction; | using Model = Discord.API.Interaction; | ||||
| using DataModel = Discord.API.ApplicationCommandInteractionData; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| @@ -61,6 +62,19 @@ namespace Discord.WebSocket | |||||
| internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | ||||
| { | { | ||||
| if (model.Type == InteractionType.ApplicationCommand) | if (model.Type == InteractionType.ApplicationCommand) | ||||
| if(model.ApplicationId != null) | |||||
| { | |||||
| var dataModel = model.Data.IsSpecified ? | |||||
| (DataModel)model.Data.Value | |||||
| : null; | |||||
| if(dataModel != null) | |||||
| { | |||||
| if (dataModel.Type.Equals(ApplicationCommandType.User)) | |||||
| return SocketApplicationUserCommand.Create(client, model, channel); | |||||
| if (dataModel.Type.Equals(ApplicationCommandType.Message)) | |||||
| return SocketApplicationMessageCommand.Create(client, model, channel); | |||||
| } | |||||
| } | |||||
| return SocketSlashCommand.Create(client, model, channel); | return SocketSlashCommand.Create(client, model, channel); | ||||
| if (model.Type == InteractionType.MessageComponent) | if (model.Type == InteractionType.MessageComponent) | ||||
| return SocketMessageComponent.Create(client, model, channel); | return SocketMessageComponent.Create(client, model, channel); | ||||