| @@ -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 | |||
| /// </returns> | |||
| public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes) | |||
| { | |||
| ChannelTypes = channelTypes.ToList(); | |||
| ChannelTypes = channelTypes is null | |||
| ? ChannelTypeUtils.AllChannelTypes() | |||
| : channelTypes.ToList(); | |||
| return this; | |||
| } | |||
| @@ -37,6 +37,11 @@ namespace Discord | |||
| /// </summary> | |||
| IReadOnlyCollection<IRole> Roles { get; } | |||
| /// <summary> | |||
| /// Gets the guild member(s) of a <see cref="ComponentType.UserSelect"/> or <see cref="ComponentType.MentionableSelect"/> interaction response. | |||
| /// </summary> | |||
| IReadOnlyCollection<IGuildUser> Members { get; } | |||
| /// <summary> | |||
| /// Gets the value of a <see cref="ComponentType.TextInput"/> interaction response. | |||
| /// </summary> | |||
| @@ -0,0 +1,14 @@ | |||
| using System.Collections.Generic; | |||
| namespace Discord.Utils; | |||
| public static class ChannelTypeUtils | |||
| { | |||
| public static List<ChannelType> AllChannelTypes() | |||
| => new List<ChannelType>() | |||
| { | |||
| 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 | |||
| }; | |||
| } | |||
| @@ -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; | |||
| @@ -34,6 +34,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc cref="IComponentInteractionData.Roles"/>/> | |||
| public IReadOnlyCollection<RestRole> Roles { get; } | |||
| /// <inheritdoc cref="IComponentInteractionData.Members"/>/> | |||
| public IReadOnlyCollection<RestGuildUser> Members { get; } | |||
| #region IComponentInteractionData | |||
| /// <inheritdoc/> | |||
| @@ -45,6 +48,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc/> | |||
| IReadOnlyCollection<IRole> IComponentInteractionData.Roles => Roles; | |||
| /// <inheritdoc/> | |||
| IReadOnlyCollection<IGuildUser> IComponentInteractionData.Members => Members; | |||
| #endregion | |||
| /// <inheritdoc/> | |||
| @@ -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<RestGuildUser>()).ToImmutableArray() | |||
| ? model.Resolved.Value.Users.Value.Select(user => RestUser.Create(discord, user.Value)).ToImmutableArray() | |||
| : Array.Empty<RestUser>(); | |||
| 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<RestChannel>(); | |||
| Roles = model.Resolved.Value.Roles.IsSpecified | |||
| ? model.Resolved.Value.Roles.Value.Select(role => RestRole.Create(discord, guild, role.Value)).ToImmutableArray() | |||
| : null; | |||
| : Array.Empty<RestRole>(); | |||
| } | |||
| } | |||
| @@ -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<RestGuildUser>()).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 | |||
| @@ -1,5 +1,3 @@ | |||
| #define DEBUG_PACKETS | |||
| using Discord.API.Gateway; | |||
| using Discord.Net.Queue; | |||
| using Discord.Net.Rest; | |||
| @@ -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) | |||
| @@ -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 | |||
| /// <inheritdoc /> | |||
| public IReadOnlyCollection<string> Values { get; } | |||
| /// <inheritdoc cref="IComponentInteractionData.Channels"/>/> | |||
| public IReadOnlyCollection<SocketChannel> Channels { get; } | |||
| /// <inheritdoc cref="IComponentInteractionData.Users"/>/> | |||
| public IReadOnlyCollection<RestUser> Users { get; } | |||
| /// <inheritdoc cref="IComponentInteractionData.Roles"/>/> | |||
| public IReadOnlyCollection<SocketRole> Roles { get; } | |||
| /// <inheritdoc cref="IComponentInteractionData.Members"/>/> | |||
| public IReadOnlyCollection<SocketGuildUser> Members { get; } | |||
| #region IComponentInteractionData | |||
| /// <inheritdoc /> | |||
| public IReadOnlyCollection<IChannel> Channels { get; } | |||
| IReadOnlyCollection<IChannel> IComponentInteractionData.Channels => Channels; | |||
| /// <inheritdoc /> | |||
| public IReadOnlyCollection<IUser> Users { get; } | |||
| IReadOnlyCollection<IUser> IComponentInteractionData.Users => Users; | |||
| /// <inheritdoc /> | |||
| public IReadOnlyCollection<IRole> Roles { get; } | |||
| IReadOnlyCollection<IRole> IComponentInteractionData.Roles => Roles; | |||
| /// <inheritdoc /> | |||
| IReadOnlyCollection<IGuildUser> IComponentInteractionData.Members => Members; | |||
| #endregion | |||
| /// <inheritdoc /> | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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) | |||
| @@ -22,12 +22,12 @@ namespace Discord.WebSocket | |||
| /// </summary> | |||
| public IReadOnlyCollection<SocketMessageComponentData> 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(); | |||
| } | |||