From e18915ac96dd702ae9e23681ea116296169e371a Mon Sep 17 00:00:00 2001 From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> Date: Sat, 20 Jul 2024 23:37:22 +0300 Subject: [PATCH] [Feature] Member search v2 (#2931) * initial commit * tweak some things * a * Update JoinSourceType.cs * ill just hope it works * yup * docz --- .../Entities/Guilds/IGuild.cs | 6 + .../Entities/Guilds/JoinSourceType.cs | 47 +++++ .../Guilds/MemberSearchPropertiesV2.cs | 183 +++++++++++++++++ .../Entities/Guilds/MemberSearchResult.cs | 45 +++++ .../Entities/Guilds/MemberSearchV2SortType.cs | 27 +++ .../API/Common/GuildMemberSearchResponse.cs | 18 ++ .../API/Common/GuildSearchMemberData.cs | 18 ++ .../API/Rest/SearchGuildMembersParamsV2.cs | 91 +++++++++ src/Discord.Net.Rest/DiscordRestApiClient.cs | 10 + .../Entities/Guilds/GuildHelper.cs | 186 ++++++++++++++++++ .../Entities/Guilds/RestGuild.cs | 4 + .../Entities/Guilds/SocketGuild.cs | 4 + 12 files changed, 639 insertions(+) create mode 100644 src/Discord.Net.Core/Entities/Guilds/JoinSourceType.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/MemberSearchPropertiesV2.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/MemberSearchResult.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/MemberSearchV2SortType.cs create mode 100644 src/Discord.Net.Rest/API/Common/GuildMemberSearchResponse.cs create mode 100644 src/Discord.Net.Rest/API/Common/GuildSearchMemberData.cs create mode 100644 src/Discord.Net.Rest/API/Rest/SearchGuildMembersParamsV2.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 708e5830..afa169c3 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -1087,6 +1087,7 @@ namespace Discord /// be or has been removed from this guild. /// Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null, IEnumerable includeRoleIds = null); + /// /// Gets a collection of users in this guild that the name or nickname starts with the /// provided at . @@ -1104,6 +1105,11 @@ namespace Discord /// Task> SearchUsersAsync(string query, int limit = DiscordConfig.MaxUsersPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// + /// Gets a collection of users in this guild that match the provided search criteria. + /// + Task SearchUsersAsyncV2(int limit = DiscordConfig.MaxUsersPerBatch, MemberSearchPropertiesV2 args = null, RequestOptions options = null); + /// /// Gets the specified number of audit log entries for this guild. /// diff --git a/src/Discord.Net.Core/Entities/Guilds/JoinSourceType.cs b/src/Discord.Net.Core/Entities/Guilds/JoinSourceType.cs new file mode 100644 index 00000000..d9dabbf8 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/JoinSourceType.cs @@ -0,0 +1,47 @@ +namespace Discord; + +/// +/// Represents the source of a user joining a guild. +/// +public enum JoinSourceType +{ + /// + /// Unknown source. + /// + Unknown = 0, + + /// + /// The user was invited by a bot. + /// + BotInvite = 1, + + /// + /// The user was invited by an integration. + /// + Integration = 2, + + /// + /// The user joined via server discovery. + /// + ServerDiscovery = 3, + + /// + /// The user joined via the student hub. + /// + StudentHub = 4, + + /// + /// The user joined via an invite code. + /// + InviteCode = 5, + + /// + /// The user joined via a vanity URL. + /// + VanityUrl = 6, + + /// + /// The user was manually verified + /// + ManualVerification = 7 +} diff --git a/src/Discord.Net.Core/Entities/Guilds/MemberSearchPropertiesV2.cs b/src/Discord.Net.Core/Entities/Guilds/MemberSearchPropertiesV2.cs new file mode 100644 index 00000000..75c0dac2 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/MemberSearchPropertiesV2.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Discord; + + +/// +/// Represents the properties for searching members in a guild. +/// +public class MemberSearchPropertiesV2 +{ + /// + /// Gets or sets the after property for the search. + /// + public MemberSearchPropertiesV2After After { get; set; } + + /// + /// Gets or sets the sort type for the search. + /// + public MemberSearchV2SortType? Sort { get; set; } + + /// + /// Gets or sets the and query for the search. + /// + public MemberSearchV2QueryParams? AndQuery { get; set; } + + /// + /// Gets or sets the or query for the search. + /// + public MemberSearchV2QueryParams? OrQuery { get; set; } +} + + +/// +/// Represents the after property for searching members in a guild. +/// +public struct MemberSearchPropertiesV2After +{ + /// + /// Gets or sets the user ID to search after. + /// + public ulong UserId { get; set; } + + /// + /// Gets or sets the guild joined at timestamp to search after. + /// + public long GuildJoinedAt { get; set; } + + public MemberSearchPropertiesV2After(ulong userId, long guildJoinedAt) + { + UserId = userId; + GuildJoinedAt = guildJoinedAt; + } + + public MemberSearchPropertiesV2After(ulong userId, DateTimeOffset guildJoinedAt) + { + UserId = userId; + GuildJoinedAt = guildJoinedAt.ToUnixTimeMilliseconds(); + } +} + + +/// +/// Represents the query parameters for searching members in a guild. +/// +public struct MemberSearchV2QueryParams +{ + /// + /// Gets or sets the safety signal search properties. + /// + public MemberSearchV2SafetySignalsProperties? SafetySignals { get; set; } + + /// + /// Gets or sets the role IDs to search for. + /// + public MemberSearchV2QueryProperties? RoleIds { get; set; } + + /// + /// Gets or sets the range to search for the user ID. + /// + public MemberSearchV2RangeProperties? UserId { get; set; } + + /// + /// Gets or sets the range to search for the user's guild joined at timestamp. + /// + public MemberSearchV2RangeProperties? GuildJoinedAt { get; set; } + + /// + /// Gets or sets the source invite code to search for. + /// + public MemberSearchV2QueryProperties? SourceInviteCode { get; set; } + + /// + /// Gets or sets the join source type to search for. + /// + public MemberSearchV2QueryProperties? JoinSourceType { get; set; } +} + + +/// +/// Represents the safety signal properties for searching members in a guild. +/// +public struct MemberSearchV2SafetySignalsProperties +{ + /// + /// Gets or sets the unusual DM activity until property for the search. + /// + public MemberSearchV2SafetySignalProperties? UnusualDmActivityUntil { get; set; } + + /// + /// Gets or sets the communication disabled until property for the search. + /// + public MemberSearchV2SafetySignalProperties? CommunicationDisabledUntil { get; set; } + + /// + /// Gets or sets the unusual account activity property for the search. + /// + public bool? UnusualAccountActivity { get; set; } + + /// + /// Gets or sets the automod quarantined username property for the search. + /// + public bool? AutomodQuarantinedUsername { get; set; } +} + + +/// +/// Represents the query properties for searching members in a guild. +/// +public readonly struct MemberSearchV2QueryProperties +{ + /// + /// Gets the and query for the search. + /// + public Dictionary AndQuery { get; } + + /// + /// Gets the or query for the search. + /// + public Dictionary OrQuery { get; } + + public MemberSearchV2QueryProperties(Dictionary andQuery, Dictionary orQuery) + { + AndQuery = andQuery.Select(x => new KeyValuePair(x.Key, x.Value)).ToDictionary(); + OrQuery = orQuery.Select(x => new KeyValuePair(x.Key, x.Value)).ToDictionary(); + } + + public MemberSearchV2QueryProperties(Dictionary andQuery, Dictionary orQuery) + { + AndQuery = andQuery.Select(x => new KeyValuePair(x.Key, x.Value)).ToDictionary(); + OrQuery = orQuery.Select(x => new KeyValuePair(x.Key, x.Value)).ToDictionary(); + } +} + + +/// +/// Represents the safety signal properties for searching members in a guild. +/// +public struct MemberSearchV2SafetySignalProperties +{ + /// + /// Gets or sets the range for the search. + /// + public MemberSearchV2RangeProperties Range { get; set; } +} + + +/// +/// Represents the range properties for searching members in a guild. +/// +public struct MemberSearchV2RangeProperties +{ + /// + /// Gets or sets the less than property for the search. + /// + public long? LessThanOrEqual { get; set; } + + /// + /// Gets or sets the greater than property for the search. + /// + public long? GreaterThanOrEqual { get; set; } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/MemberSearchResult.cs b/src/Discord.Net.Core/Entities/Guilds/MemberSearchResult.cs new file mode 100644 index 00000000..6baaa935 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/MemberSearchResult.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace Discord; + +public struct MemberSearchResult +{ + public ulong GuildId { get; } + + public IReadOnlyCollection Members { get; } + + public int PageResultCount { get; } + + public int TotalResultCount { get; } + + public MemberSearchResult(ulong guildId, IReadOnlyCollection members, int pageResultCount, int totalResultCount) + { + GuildId = guildId; + Members = members; + PageResultCount = pageResultCount; + TotalResultCount = totalResultCount; + } +} + +[DebuggerDisplay(@"{DebuggerDisplay,nq}")] +public readonly struct MemberSearchData +{ + public IGuildUser User { get; } + + public string SourceInviteCode { get; } + + public JoinSourceType JoinSourceType { get; } + + public ulong? InviterId { get; } + + public MemberSearchData(IGuildUser user, string sourceInviteCode, JoinSourceType joinSourceType, ulong? inviterId) + { + User = user; + SourceInviteCode = sourceInviteCode; + JoinSourceType = joinSourceType; + InviterId = inviterId; + } + + private string DebuggerDisplay => $"{User.Username} ({User.Id})"; +} diff --git a/src/Discord.Net.Core/Entities/Guilds/MemberSearchV2SortType.cs b/src/Discord.Net.Core/Entities/Guilds/MemberSearchV2SortType.cs new file mode 100644 index 00000000..39fce29b --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/MemberSearchV2SortType.cs @@ -0,0 +1,27 @@ +namespace Discord; + +/// +/// Represents the sort type for searching members in a guild. +/// +public enum MemberSearchV2SortType +{ + /// + /// Sort by member since newest first. + /// + MemberSinceNewestFirst = 1, + + /// + /// Sort by member since oldest first. + /// + MemberSinceOldestFirst = 2, + + /// + /// Sort by joined discord since newest first. + /// + JoinedDiscordNewestFirst = 3, + + /// + /// Sort by joined discord since oldest first. + /// + JoinedDiscordOldestFirst = 4, +} diff --git a/src/Discord.Net.Rest/API/Common/GuildMemberSearchResponse.cs b/src/Discord.Net.Rest/API/Common/GuildMemberSearchResponse.cs new file mode 100644 index 00000000..46b866c6 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/GuildMemberSearchResponse.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Discord.API; + +internal class GuildMemberSearchResponse +{ + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + + [JsonProperty("members")] + public GuildSearchMemberData[] Members { get; set; } + + [JsonProperty("page_result_count")] + public int PageResultCount { get; set; } + + [JsonProperty("total_result_count")] + public int TotalResultCount { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Common/GuildSearchMemberData.cs b/src/Discord.Net.Rest/API/Common/GuildSearchMemberData.cs new file mode 100644 index 00000000..ad77db14 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/GuildSearchMemberData.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Discord.API; + +internal class GuildSearchMemberData +{ + [JsonProperty("member")] + public GuildMember Member { get; set; } + + [JsonProperty("source_invite_code")] + public string InviteCode { get; set; } + + [JsonProperty("join_source_type")] + public JoinSourceType JoinSourceType { get; set; } + + [JsonProperty("inviter_id")] + public ulong? InviterId { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Rest/SearchGuildMembersParamsV2.cs b/src/Discord.Net.Rest/API/Rest/SearchGuildMembersParamsV2.cs new file mode 100644 index 00000000..709e8542 --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/SearchGuildMembersParamsV2.cs @@ -0,0 +1,91 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Discord.API.Rest; + +internal class SearchGuildMembersParamsV2 +{ + [JsonProperty("limit")] + public Optional Limit { get; set; } + + [JsonProperty("and_query")] + public Optional AndQuery { get; set; } + + [JsonProperty("or_query")] + public Optional OrQuery { get; set; } + + [JsonProperty("after")] + public Optional After { get; set; } + + [JsonProperty("sort")] + public Optional Sort { get; set; } +} + +internal class SearchParamsAfter +{ + [JsonProperty("guild_joined_at")] + public long GuildJoinedAt { get; set; } + + [JsonProperty("user_id")] + public ulong UserId { get; set; } +} + +internal class SearchQueryParams +{ + [JsonProperty("safety_signals")] + public Optional SafetySignals { get; set; } + + [JsonProperty("role_ids")] + public Optional RoleIds { get; set; } + + [JsonProperty("user_id")] + public Optional UserId { get; set; } + + [JsonProperty("guild_joined_at")] + public Optional GuildJoinedAt { get; set; } + + [JsonProperty("source_invite_code")] + public Optional SourceInviteCode { get; set; } + + [JsonProperty("join_source_type")] + public Optional JoinSourceType { get; set; } +} + +internal class SearchQueryProperties +{ + [JsonProperty("and_query")] + public Optional> AndQuery { get; set; } + + [JsonProperty("or_query")] + public Optional> OrQuery { get; set; } +} + +internal class SafetySignalsProperties +{ + [JsonProperty("unusual_dm_activity_until")] + public Optional UnusualDMActivityUntil { get; set; } + + [JsonProperty("communication_disabled_until")] + public Optional CommunicationDisabledUntil { get; set; } + + [JsonProperty("unusual_account_activity")] + public Optional UnusualAccountActivity { get; set; } + + [JsonProperty("automod_quarantined_username")] + public Optional AutomodQuarantinedUsername { get; set; } +} + +internal class SafetySignalProperties +{ + [JsonProperty("range")] + public SearchRangeProperties Until { get; set; } +} + +internal class SearchRangeProperties +{ + [JsonProperty("gte")] + public Optional GreaterThanOrEqual { get; set; } + + [JsonProperty("lte")] + public Optional LessThanOrEqual { get; set; } +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index beaf0155..407a4447 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -2038,6 +2038,16 @@ namespace Discord.API Expression> endpoint = () => $"guilds/{guildId}/members/search?limit={limit}&query={query}"; return SendAsync>("GET", endpoint, ids, options: options); } + + public Task SearchGuildMembersAsyncV2(ulong guildId, SearchGuildMembersParamsV2 args, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return SendJsonAsync("POST", () => $"guilds/{guildId}/members-search", args, ids, options: options); + } + #endregion #region Guild Roles diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 6245d20c..563bf32a 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -763,6 +763,192 @@ namespace Discord.Rest var models = await client.ApiClient.SearchGuildMembersAsync(guild.Id, apiArgs, options).ConfigureAwait(false); return models.Select(x => RestGuildUser.Create(client, guild, x)).ToImmutableArray(); } + + public static async Task SearchUsersAsyncV2(IGuild guild, BaseDiscordClient client, int limit, MemberSearchPropertiesV2 args, + RequestOptions options) + { + var apiArgs = new SearchGuildMembersParamsV2 + { + Limit = limit, + After = args is null + ? null + : new SearchParamsAfter + { + UserId = args.After.UserId, + GuildJoinedAt = args.After.GuildJoinedAt + }, + Sort = args?.Sort ?? Optional.Unspecified, + OrQuery = args is null + ? Optional.Unspecified + : args.OrQuery is null + ? Optional.Unspecified + : new SearchQueryParams + { + GuildJoinedAt = args.OrQuery.Value.GuildJoinedAt is null + ? Optional.Unspecified + : new SearchRangeProperties + { + GreaterThanOrEqual = args.OrQuery.Value.GuildJoinedAt.Value.GreaterThanOrEqual is null + ? Optional.Unspecified + : args.OrQuery.Value.GuildJoinedAt.Value.GreaterThanOrEqual.Value, + LessThanOrEqual = args.OrQuery.Value.GuildJoinedAt.Value.LessThanOrEqual is null + ? Optional.Unspecified + : args.OrQuery.Value.GuildJoinedAt.Value.LessThanOrEqual.Value, + }, + JoinSourceType = args.OrQuery.Value.JoinSourceType is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.OrQuery.Value.JoinSourceType.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.OrQuery.Value.JoinSourceType.Value.OrQuery ?? Optional>.Unspecified + }, + RoleIds = args.OrQuery.Value.RoleIds is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.OrQuery.Value.RoleIds.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.OrQuery.Value.RoleIds.Value.OrQuery ?? Optional>.Unspecified + }, + SourceInviteCode = args.OrQuery.Value.SourceInviteCode is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.OrQuery.Value.SourceInviteCode.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.OrQuery.Value.SourceInviteCode.Value.OrQuery ?? Optional>.Unspecified + }, + SafetySignals = args.OrQuery.Value.SafetySignals is null + ? Optional.Unspecified + : new SafetySignalsProperties + { + AutomodQuarantinedUsername = args.OrQuery.Value.SafetySignals.Value.AutomodQuarantinedUsername is null + ? Optional.Unspecified + : args.OrQuery.Value.SafetySignals.Value.AutomodQuarantinedUsername.Value, + UnusualAccountActivity = args.OrQuery.Value.SafetySignals.Value.UnusualAccountActivity is null + ? Optional.Unspecified + : args.OrQuery.Value.SafetySignals.Value.UnusualAccountActivity.Value, + CommunicationDisabledUntil = args.OrQuery.Value.SafetySignals.Value.CommunicationDisabledUntil is null + ? Optional.Unspecified + : new SafetySignalProperties + { + Until = new SearchRangeProperties + { + GreaterThanOrEqual = args.OrQuery.Value.SafetySignals.Value.CommunicationDisabledUntil.Value.Range.GreaterThanOrEqual ?? Optional.Unspecified, + LessThanOrEqual = args.OrQuery.Value.SafetySignals.Value.CommunicationDisabledUntil.Value.Range.LessThanOrEqual ?? Optional.Unspecified, + } + }, + UnusualDMActivityUntil = args.OrQuery.Value.SafetySignals.Value.UnusualDmActivityUntil is null + ? Optional.Unspecified + : new SafetySignalProperties + { + Until = new SearchRangeProperties + { + GreaterThanOrEqual = args.OrQuery.Value.SafetySignals.Value.UnusualDmActivityUntil.Value.Range.GreaterThanOrEqual ?? Optional.Unspecified, + LessThanOrEqual = args.OrQuery.Value.SafetySignals.Value.UnusualDmActivityUntil.Value.Range.LessThanOrEqual ?? Optional.Unspecified, + } + }, + }, + UserId = args.OrQuery.Value.UserId is null + ? Optional.Unspecified + : new SearchRangeProperties + { + LessThanOrEqual = args.OrQuery.Value.UserId.Value.LessThanOrEqual ?? Optional.Unspecified, + GreaterThanOrEqual = args.OrQuery.Value.UserId.Value.GreaterThanOrEqual ?? Optional.Unspecified, + }, + }, + AndQuery = args is null + ? Optional.Unspecified + : args.AndQuery is null + ? Optional.Unspecified + : new SearchQueryParams + { + GuildJoinedAt = args.AndQuery.Value.GuildJoinedAt is null + ? Optional.Unspecified + : new SearchRangeProperties + { + GreaterThanOrEqual = args.AndQuery.Value.GuildJoinedAt.Value.GreaterThanOrEqual is null + ? Optional.Unspecified + : args.AndQuery.Value.GuildJoinedAt.Value.GreaterThanOrEqual.Value, + LessThanOrEqual = args.AndQuery.Value.GuildJoinedAt.Value.LessThanOrEqual is null + ? Optional.Unspecified + : args.AndQuery.Value.GuildJoinedAt.Value.LessThanOrEqual.Value, + }, + JoinSourceType = args.AndQuery.Value.JoinSourceType is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.AndQuery.Value.JoinSourceType.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.AndQuery.Value.JoinSourceType.Value.OrQuery ?? Optional>.Unspecified + }, + RoleIds = args.AndQuery.Value.RoleIds is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.AndQuery.Value.RoleIds.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.AndQuery.Value.RoleIds.Value.OrQuery ?? Optional>.Unspecified + }, + SourceInviteCode = args.AndQuery.Value.SourceInviteCode is null + ? Optional.Unspecified + : new SearchQueryProperties + { + AndQuery = args.AndQuery.Value.SourceInviteCode.Value.AndQuery ?? Optional>.Unspecified, + OrQuery = args.AndQuery.Value.SourceInviteCode.Value.OrQuery ?? Optional>.Unspecified + }, + SafetySignals = args.AndQuery.Value.SafetySignals is null + ? Optional.Unspecified + : new SafetySignalsProperties + { + AutomodQuarantinedUsername = args.AndQuery.Value.SafetySignals.Value.AutomodQuarantinedUsername is null + ? Optional.Unspecified + : args.AndQuery.Value.SafetySignals.Value.AutomodQuarantinedUsername.Value, + UnusualAccountActivity = args.AndQuery.Value.SafetySignals.Value.UnusualAccountActivity is null + ? Optional.Unspecified + : args.AndQuery.Value.SafetySignals.Value.UnusualAccountActivity.Value, + CommunicationDisabledUntil = args.AndQuery.Value.SafetySignals.Value.CommunicationDisabledUntil is null + ? Optional.Unspecified + : new SafetySignalProperties + { + Until = new SearchRangeProperties + { + GreaterThanOrEqual = args.AndQuery.Value.SafetySignals.Value.CommunicationDisabledUntil.Value.Range.GreaterThanOrEqual ?? Optional.Unspecified, + LessThanOrEqual = args.AndQuery.Value.SafetySignals.Value.CommunicationDisabledUntil.Value.Range.LessThanOrEqual ?? Optional.Unspecified, + } + }, + UnusualDMActivityUntil = args.AndQuery.Value.SafetySignals.Value.UnusualDmActivityUntil is null + ? Optional.Unspecified + : new SafetySignalProperties + { + Until = new SearchRangeProperties + { + GreaterThanOrEqual = args.AndQuery.Value.SafetySignals.Value.UnusualDmActivityUntil.Value.Range.GreaterThanOrEqual ?? Optional.Unspecified, + LessThanOrEqual = args.AndQuery.Value.SafetySignals.Value.UnusualDmActivityUntil.Value.Range.LessThanOrEqual ?? Optional.Unspecified, + } + }, + }, + UserId = args.AndQuery.Value.UserId is null + ? Optional.Unspecified + : new SearchRangeProperties + { + LessThanOrEqual = args.AndQuery.Value.UserId.Value.LessThanOrEqual ?? Optional.Unspecified, + GreaterThanOrEqual = args.AndQuery.Value.UserId.Value.GreaterThanOrEqual ?? Optional.Unspecified, + }, + }, + }; + var model = await client.ApiClient.SearchGuildMembersAsyncV2(guild.Id, apiArgs, options); + + return new MemberSearchResult( + model.GuildId, + model.Members.Select(x => + new MemberSearchData( + RestGuildUser.Create(client, guild, x.Member), + x.InviteCode, + x.JoinSourceType, + x.InviterId) + ).ToImmutableArray(), + model.PageResultCount, + model.TotalResultCount + ); + } + #endregion #region Audit logs diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 6818ecde..f0ca5093 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -983,6 +983,10 @@ namespace Discord.Rest /// public Task> SearchUsersAsync(string query, int limit = DiscordConfig.MaxUsersPerBatch, RequestOptions options = null) => GuildHelper.SearchUsersAsync(this, Discord, query, limit, options); + + /// + public Task SearchUsersAsyncV2(int limit = DiscordConfig.MaxUsersPerBatch, MemberSearchPropertiesV2 args = null, RequestOptions options = null) + => GuildHelper.SearchUsersAsyncV2(this, Discord, limit, args, options); #endregion #region Audit logs diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 86335d0a..bb6c3748 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -1336,6 +1336,10 @@ namespace Discord.WebSocket /// public Task> SearchUsersAsync(string query, int limit = DiscordConfig.MaxUsersPerBatch, RequestOptions options = null) => GuildHelper.SearchUsersAsync(this, Discord, query, limit, options); + + /// + public Task SearchUsersAsyncV2(int limit = DiscordConfig.MaxUsersPerBatch, MemberSearchPropertiesV2 args = null, RequestOptions options = null) + => GuildHelper.SearchUsersAsyncV2(this, Discord, limit, args, options); #endregion #region Guild Events