[Feature] Selects v2 support (#2507)
* Initial support for new select types * Merge branch 'dev' of https://github.com/discord-net/Discord.Net into dev * some component&action row builder additions * remove redundant code * changes1 * maybe working rest part? * working-ish state? * fix some xml docs & small rework * typos * fix `ActionRowBuilder` * update DefaultArrayComponentConverter to accomodate new select-v2 types * now supports dm channels in channel selects * add a note to IF docs * add notes about nullable properties * <see langword="null"/> * update Modal.cs Co-authored-by: cat <lumitydev@gmail.com> Co-authored-by: Cenngo <cenk.ergen1@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ using Discord.Net.Converters;
|
||||
using Discord.Net.Udp;
|
||||
using Discord.Net.WebSockets;
|
||||
using Discord.Rest;
|
||||
using Discord.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
@@ -2394,7 +2395,7 @@ namespace Discord.WebSocket
|
||||
await TimedInvokeAsync(_slashCommandExecuted, nameof(SlashCommandExecuted), slashCommand).ConfigureAwait(false);
|
||||
break;
|
||||
case SocketMessageComponent messageComponent:
|
||||
if (messageComponent.Data.Type == ComponentType.SelectMenu)
|
||||
if (messageComponent.Data.Type.IsSelectType())
|
||||
await TimedInvokeAsync(_selectMenuExecuted, nameof(SelectMenuExecuted), messageComponent).ConfigureAwait(false);
|
||||
if (messageComponent.Data.Type == ComponentType.Button)
|
||||
await TimedInvokeAsync(_buttonExecuted, nameof(ButtonExecuted), messageComponent).ConfigureAwait(false);
|
||||
|
||||
@@ -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()), model.User.GetValueOrDefault());
|
||||
}
|
||||
|
||||
internal new static SocketMessageComponent Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel, SocketUser user)
|
||||
|
||||
@@ -1,4 +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
|
||||
@@ -8,35 +13,84 @@ namespace Discord.WebSocket
|
||||
/// </summary>
|
||||
public class SocketMessageComponentData : IComponentInteractionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the components Custom Id that was clicked.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string CustomId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the component clicked.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public ComponentType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value(s) of a <see cref="SelectMenuComponent"/> interaction response.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<string> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a <see cref="TextInputComponent"/> interaction response.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IComponentInteractionData.Channels"/>
|
||||
public IReadOnlyCollection<SocketChannel> Channels { get; }
|
||||
|
||||
/// <inheritdoc cref="IComponentInteractionData.Users"/>
|
||||
/// <remarks>Returns <see cref="SocketUser"/> if user is cached, <see cref="RestUser"/> otherwise.</remarks>
|
||||
public IReadOnlyCollection<IUser> Users { get; }
|
||||
|
||||
/// <inheritdoc cref="IComponentInteractionData.Roles"/>
|
||||
public IReadOnlyCollection<SocketRole> Roles { get; }
|
||||
|
||||
/// <inheritdoc cref="IComponentInteractionData.Members"/>
|
||||
public IReadOnlyCollection<SocketGuildUser> Members { get; }
|
||||
|
||||
#region IComponentInteractionData
|
||||
|
||||
/// <inheritdoc />
|
||||
IReadOnlyCollection<IChannel> IComponentInteractionData.Channels => Channels;
|
||||
|
||||
/// <inheritdoc />
|
||||
IReadOnlyCollection<IUser> IComponentInteractionData.Users => Users;
|
||||
|
||||
/// <inheritdoc />
|
||||
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, API.User dmUser)
|
||||
{
|
||||
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 => (IUser)state.GetUser(user.Value.Id) ?? 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.Id, dmUser);
|
||||
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, API.User dmUser)
|
||||
{
|
||||
CustomId = component.CustomId;
|
||||
Type = component.Type;
|
||||
@@ -45,9 +99,39 @@ namespace Discord.WebSocket
|
||||
? (component as API.TextInputComponent).Value.Value
|
||||
: null;
|
||||
|
||||
Values = component.Type == ComponentType.SelectMenu
|
||||
? (component as API.SelectMenuComponent).Values.Value
|
||||
: null;
|
||||
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 => (IUser)state.GetUser(user.Value.Id) ?? 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.Id, dmUser);
|
||||
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.State.GetGuild(model.GuildId.GetValueOrDefault()), model.User.GetValueOrDefault());
|
||||
}
|
||||
|
||||
internal new static SocketModal Create(DiscordSocketClient client, ModelBase model, ISocketMessageChannel channel, SocketUser user)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Discord.WebSocket
|
||||
/// <summary>
|
||||
/// Represents data sent from a <see cref="InteractionType.ModalSubmit"/>.
|
||||
/// </summary>
|
||||
public class SocketModalData : IDiscordInteractionData, IModalInteractionData
|
||||
public class SocketModalData : IModalInteractionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Modal"/>'s Custom Id.
|
||||
@@ -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, API.User dmUser)
|
||||
{
|
||||
CustomId = model.CustomId;
|
||||
Components = model.Components
|
||||
.SelectMany(x => x.Components)
|
||||
.Select(x => new SocketMessageComponentData(x))
|
||||
.Select(x => new SocketMessageComponentData(x, discord, state, guild, dmUser))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Discord.WebSocket
|
||||
/// <returns>
|
||||
/// Collection of WebSocket-based users.
|
||||
/// </returns>
|
||||
public IReadOnlyCollection<SocketUser> MentionedUsers => _userMentions;
|
||||
public IReadOnlyCollection<SocketUser> MentionedUsers => _userMentions;
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);
|
||||
|
||||
@@ -226,7 +226,9 @@ namespace Discord.WebSocket
|
||||
parsed.Placeholder.GetValueOrDefault(),
|
||||
parsed.MinValues,
|
||||
parsed.MaxValues,
|
||||
parsed.Disabled
|
||||
parsed.Disabled,
|
||||
parsed.Type,
|
||||
parsed.ChannelTypes.GetValueOrDefault()
|
||||
);
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user