@@ -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(); | |||
} | |||