[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:
Misha133
2022-12-25 15:41:15 +03:00
committed by GitHub
parent c67642acfa
commit 48fb1b5df4
25 changed files with 470 additions and 95 deletions

View File

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

View File

@@ -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)

View File

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

View File

@@ -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)

View File

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

View File

@@ -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: