diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs index ee3ea8928..c266da7b2 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs @@ -122,7 +122,7 @@ namespace Discord public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0) { Preconditions.LessThan(row, MaxActionRowCount, nameof(row)); - if (menu.Options.Distinct().Count() != menu.Options.Count) + if (menu.Options is not null && menu.Options.Distinct().Count() != menu.Options.Count) throw new InvalidOperationException("Please make sure that there is no duplicates values."); var builtMenu = menu.Build(); @@ -838,8 +838,6 @@ namespace Discord { if (value != null) Preconditions.AtMost(value.Count, MaxOptionCount, nameof(Options)); - else - throw new ArgumentNullException(nameof(value), $"{nameof(Options)} cannot be null."); _options = value; } @@ -1058,7 +1056,9 @@ namespace Discord /// public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes) { - ChannelTypes = channelTypes.ToList(); + ChannelTypes = channelTypes is null + ? ChannelTypeUtils.AllChannelTypes() + : channelTypes.ToList(); return this; } diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs index a3366f45b..88382ad8e 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IComponentInteractionData.cs @@ -37,6 +37,11 @@ namespace Discord /// IReadOnlyCollection Roles { get; } + /// + /// Gets the guild member(s) of a or interaction response. + /// + IReadOnlyCollection Members { get; } + /// /// Gets the value of a interaction response. /// diff --git a/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs b/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs new file mode 100644 index 000000000..4dd764508 --- /dev/null +++ b/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Discord.Utils; + +public static class ChannelTypeUtils +{ + public static List AllChannelTypes() + => new List() + { + ChannelType.Forum, ChannelType.Category, ChannelType.DM, ChannelType.Group, ChannelType.GuildDirectory, + ChannelType.News, ChannelType.NewsThread, ChannelType.PrivateThread, ChannelType.PublicThread, + ChannelType.Stage, ChannelType.Store, ChannelType.Text, ChannelType.Voice + }; +} diff --git a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs index 0943a5270..3975a8c1e 100644 --- a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs +++ b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs @@ -40,7 +40,7 @@ namespace Discord.API { Type = component.Type; CustomId = component.CustomId; - Options = component.Options.Select(x => new SelectMenuOption(x)).ToArray(); + Options = component.Options?.Select(x => new SelectMenuOption(x)).ToArray(); Placeholder = component.Placeholder; MinValues = component.MinValues; MaxValues = component.MaxValues; diff --git a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs index b484e80ab..2b31990f3 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs @@ -34,6 +34,9 @@ namespace Discord.Rest /// /> public IReadOnlyCollection Roles { get; } + /// /> + public IReadOnlyCollection Members { get; } + #region IComponentInteractionData /// @@ -45,6 +48,9 @@ namespace Discord.Rest /// IReadOnlyCollection IComponentInteractionData.Roles => Roles; + /// + IReadOnlyCollection IComponentInteractionData.Members => Members; + #endregion /// @@ -60,19 +66,25 @@ namespace Discord.Rest if (model.Resolved.IsSpecified) { Users = model.Resolved.Value.Users.IsSpecified - ? model.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)) - .Concat(model.Resolved.Value.Members.IsSpecified - ? model.Resolved.Value.Members.Value.Select(member => RestGuildUser.Create(discord, guild, member.Value)) - : Array.Empty()).ToImmutableArray() + ? model.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)).ToImmutableArray() + : Array.Empty(); + + Members = model.Resolved.Value.Members.IsSpecified + ? model.Resolved.Value.Members.Value.Select(member => + { + member.Value.User = model.Resolved.Value.Users.Value.First(u => u.Key == member.Key).Value; + + return RestGuildUser.Create(discord, guild, member.Value); + }).ToImmutableArray() : null; Channels = model.Resolved.Value.Channels.IsSpecified ? model.Resolved.Value.Channels.Value.Select(channel => RestChannel.Create(discord, channel.Value)).ToImmutableArray() - : null; + : Array.Empty(); Roles = model.Resolved.Value.Roles.IsSpecified ? model.Resolved.Value.Roles.Value.Select(role => RestRole.Create(discord, guild, role.Value)).ToImmutableArray() - : null; + : Array.Empty(); } } @@ -91,10 +103,16 @@ namespace Discord.Rest if (select.Resolved.IsSpecified) { Users = select.Resolved.Value.Users.IsSpecified - ? select.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)) - .Concat(select.Resolved.Value.Members.IsSpecified - ? select.Resolved.Value.Members.Value.Select(member => RestGuildUser.Create(discord, guild, member.Value)) - : Array.Empty()).ToImmutableArray() + ? select.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)).ToImmutableArray() + : null; + + Members = select.Resolved.Value.Members.IsSpecified + ? select.Resolved.Value.Members.Value.Select(member => + { + member.Value.User = select.Resolved.Value.Users.Value.First(u => u.Key == member.Key).Value; + + return RestGuildUser.Create(discord, guild, member.Value); + }).ToImmutableArray() : null; Channels = select.Resolved.Value.Channels.IsSpecified diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 448b57125..465c47a1d 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -1,5 +1,3 @@ -#define DEBUG_PACKETS - using Discord.API.Gateway; using Discord.Net.Queue; using Discord.Net.Rest; diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs index 2a1a67d04..2eaeef221 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs @@ -35,7 +35,7 @@ namespace Discord.WebSocket ? (DataModel)model.Data.Value : null; - Data = new SocketMessageComponentData(dataModel); + Data = new SocketMessageComponentData(dataModel, client, client.State, client.Guilds.FirstOrDefault(x => x.Id == model.GuildId.GetValueOrDefault())); } internal new static SocketMessageComponent Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user) diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs index e462da1aa..d54430095 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs @@ -1,6 +1,9 @@ +using Discord.Rest; using Discord.Utils; +using System; using System.Linq; using System.Collections.Generic; +using System.Collections.Immutable; using Model = Discord.API.MessageComponentInteractionData; namespace Discord.WebSocket @@ -19,27 +22,74 @@ namespace Discord.WebSocket /// public IReadOnlyCollection Values { get; } + /// /> + public IReadOnlyCollection Channels { get; } + + /// /> + public IReadOnlyCollection Users { get; } + + /// /> + public IReadOnlyCollection Roles { get; } + + /// /> + public IReadOnlyCollection Members { get; } + + #region IComponentInteractionData + /// - public IReadOnlyCollection Channels { get; } + IReadOnlyCollection IComponentInteractionData.Channels => Channels; /// - public IReadOnlyCollection Users { get; } + IReadOnlyCollection IComponentInteractionData.Users => Users; /// - public IReadOnlyCollection Roles { get; } + IReadOnlyCollection IComponentInteractionData.Roles => Roles; + /// + IReadOnlyCollection IComponentInteractionData.Members => Members; + + #endregion /// public string Value { get; } - internal SocketMessageComponentData(Model model) + internal SocketMessageComponentData(Model model, DiscordSocketClient discord, ClientState state, SocketGuild guild) { CustomId = model.CustomId; Type = model.ComponentType; Values = model.Values.GetValueOrDefault(); Value = model.Value.GetValueOrDefault(); + + if (model.Resolved.IsSpecified) + { + Users = model.Resolved.Value.Users.IsSpecified + ? model.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)).ToImmutableArray() + : null; + + Members = model.Resolved.Value.Members.IsSpecified + ? model.Resolved.Value.Members.Value.Select(member => + { + member.Value.User = model.Resolved.Value.Users.Value.First(u => u.Key == member.Key).Value; + return SocketGuildUser.Create(guild, state, member.Value); + }).ToImmutableArray() + : null; + + Channels = model.Resolved.Value.Channels.IsSpecified + ? model.Resolved.Value.Channels.Value.Select( + channel => + { + if (channel.Value.Type is ChannelType.DM) + return SocketDMChannel.Create(discord, state, channel.Value); + return (SocketChannel)SocketGuildChannel.Create(guild, state, channel.Value); + }).ToImmutableArray() + : null; + + Roles = model.Resolved.Value.Roles.IsSpecified + ? model.Resolved.Value.Roles.Value.Select(role => SocketRole.Create(guild, state, role.Value)).ToImmutableArray() + : null; + } } - internal SocketMessageComponentData(IMessageComponent component) + internal SocketMessageComponentData(IMessageComponent component, DiscordSocketClient discord, ClientState state, SocketGuild guild) { CustomId = component.CustomId; Type = component.Type; @@ -51,6 +101,35 @@ namespace Discord.WebSocket if (component is API.SelectMenuComponent select) { Values = select.Values.GetValueOrDefault(null); + + if (select.Resolved.IsSpecified) + { + Users = select.Resolved.Value.Users.IsSpecified + ? select.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)).ToImmutableArray() + : null; + + Members = select.Resolved.Value.Members.IsSpecified + ? select.Resolved.Value.Members.Value.Select(member => + { + member.Value.User = select.Resolved.Value.Users.Value.First(u => u.Key == member.Key).Value; + return SocketGuildUser.Create(guild, state, member.Value); + }).ToImmutableArray() + : null; + + Channels = select.Resolved.Value.Channels.IsSpecified + ? select.Resolved.Value.Channels.Value.Select( + channel => + { + if (channel.Value.Type is ChannelType.DM) + return SocketDMChannel.Create(discord, state, channel.Value); + return (SocketChannel)SocketGuildChannel.Create(guild, state, channel.Value); + }).ToImmutableArray() + : null; + + Roles = select.Resolved.Value.Roles.IsSpecified + ? select.Resolved.Value.Roles.Value.Select(role => SocketRole.Create(guild, state, role.Value)).ToImmutableArray() + : null; + } } } } diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs index 647544b48..f03ef54ab 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs @@ -27,8 +27,8 @@ namespace Discord.WebSocket var dataModel = model.Data.IsSpecified ? (DataModel)model.Data.Value : null; - - Data = new SocketModalData(dataModel); + + Data = new SocketModalData(dataModel, client, client.State, client.Guilds.FirstOrDefault(x => x.Id == model.GuildId.GetValueOrDefault())); } internal new static SocketModal Create(DiscordSocketClient client, ModelBase model, ISocketMessageChannel channel, SocketUser user) diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs index 1705433b0..4be4b1015 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs @@ -22,12 +22,12 @@ namespace Discord.WebSocket /// public IReadOnlyCollection Components { get; } - internal SocketModalData(Model model) + internal SocketModalData(Model model, DiscordSocketClient discord, ClientState state, SocketGuild guild) { CustomId = model.CustomId; Components = model.Components .SelectMany(x => x.Components) - .Select(x => new SocketMessageComponentData(x)) + .Select(x => new SocketMessageComponentData(x, discord, state, guild)) .ToArray(); }