diff --git a/src/helpers/members/search_members.ts b/src/helpers/members/search_members.ts new file mode 100644 index 000000000..f9fb1b76b --- /dev/null +++ b/src/helpers/members/search_members.ts @@ -0,0 +1,51 @@ +import { cacheHandlers } from "../../cache.ts"; +import { rest } from "../../rest/rest.ts"; +import { DiscordenoMember } from "../../structures/member.ts"; +import { structures } from "../../structures/mod.ts"; +import { DiscordGuildMemberWithUser } from "../../types/guilds/guild_member.ts"; +import { SearchGuildMembers } from "../../types/members/search_guild_members.ts"; +import { Errors } from "../../types/misc/errors.ts"; +import { Collection } from "../../util/collection.ts"; +import { endpoints } from "../../util/constants.ts"; + +/** + * ⚠️ BEGINNER DEVS!! YOU SHOULD ALMOST NEVER NEED THIS AND YOU CAN GET FROM cache.members.filter() + * @param query Query string to match username(s) and nickname(s) against + */ +export async function searchMembers( + guildId: string, + query: string, + options?: Omit & { cache?: boolean }, +) { + if (options?.limit) { + if (options.limit < 1) throw new Error(Errors.MEMBER_SEARCH_LIMIT_TOO_LOW); + if (options.limit > 1000) { + throw new Error(Errors.MEMBER_SEARCH_LIMIT_TOO_HIGH); + } + } + + const result = await rest.runMethod( + "get", + endpoints.GUILD_MEMBERS_SEARCH(guildId), + { + ...options, + query, + }, + ); + + const members = await Promise.all(result.map(async (member) => { + const discordenoMember = await structures.createDiscordenoMember( + member, + guildId, + ); + if (options?.cache) { + await cacheHandlers.set("members", discordenoMember.id, discordenoMember); + } + + return discordenoMember; + })); + + return new Collection( + members.map((member) => [member.id, member]), + ); +} diff --git a/src/types/members/search_guild_members.ts b/src/types/members/search_guild_members.ts new file mode 100644 index 000000000..6a984618d --- /dev/null +++ b/src/types/members/search_guild_members.ts @@ -0,0 +1,9 @@ +export interface SearchGuildMembers { + /** Query string to match username(s) and nickname(s) against */ + query: string; + /** Max number of members to return (1-1000). Default: 1 */ + limit?: number; +} + +/** https://discord.com/developers/docs/resources/guild#search-guild-members-query-string-params */ +export type DiscordSearchGuildMembers = SearchGuildMembers; diff --git a/src/types/misc/errors.ts b/src/types/misc/errors.ts index cf8354c1d..f8e441b72 100644 --- a/src/types/misc/errors.ts +++ b/src/types/misc/errors.ts @@ -14,6 +14,8 @@ export enum Errors { GUILD_NOT_FOUND = "GUILD_NOT_FOUND", MEMBER_NOT_FOUND = "MEMBER_NOT_FOUND", MEMBER_NOT_IN_VOICE_CHANNEL = "MEMBER_NOT_IN_VOICE_CHANNEL", + MEMBER_SEARCH_LIMIT_TOO_HIGH = "MEMBER_SEARCH_LIMIT_TOO_HIGH", + MEMBER_SEARCH_LIMIT_TOO_LOW = "MEMBER_SEARCH_LIMIT_TOO_LOW", PRUNE_MAX_DAYS = "PRUNE_MAX_DAYS", ROLE_NOT_FOUND = "ROLE_NOT_FOUND", // Message Delete Errors diff --git a/src/types/mod.ts b/src/types/mod.ts index e65dab797..dd18a8ec4 100644 --- a/src/types/mod.ts +++ b/src/types/mod.ts @@ -130,6 +130,7 @@ export * from "./members/guild_members_chunk.ts"; export * from "./members/guild_member_add.ts"; export * from "./members/guild_member_remove.ts"; export * from "./members/guild_member_update.ts"; +export * from "./members/search_guild_members.ts"; export * from "./messages/allowed_mentions.ts"; export * from "./messages/allowed_mentions_types.ts"; export * from "./messages/attachment.ts"; diff --git a/src/util/constants.ts b/src/util/constants.ts index 16ef3e07f..4c1e47ab4 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -106,6 +106,8 @@ export const endpoints = { GUILD_MEMBERS: (guildId: string) => `${GUILDS_BASE(guildId)}/members`, GUILD_MEMBER_ROLE: (guildId: string, memberId: string, roleId: string) => `${GUILDS_BASE(guildId)}/members/${memberId}/roles/${roleId}`, + GUILD_MEMBERS_SEARCH: (guildId: string) => + `${GUILDS_BASE(guildId)}/members/search`, GUILD_PRUNE: (guildId: string) => `${GUILDS_BASE(guildId)}/prune`, GUILD_REGIONS: (guildId: string) => `${GUILDS_BASE(guildId)}/regions`, GUILD_ROLE: (guildId: string, roleId: string) => diff --git a/tests/members/search_members.ts b/tests/members/search_members.ts new file mode 100644 index 000000000..789b8e288 --- /dev/null +++ b/tests/members/search_members.ts @@ -0,0 +1,29 @@ +import { defaultTestOptions, tempData } from "../ws/start_bot.ts"; +import { assertEquals, assertExists } from "../deps.ts"; +import { searchMembers } from "../../src/helpers/members/search_members.ts"; +import { botId, cache } from "../../mod.ts"; + +async function ifItFailsBlameWolf() { + const botMember = cache.members.get(botId); + + // Assertions + assertExists(botMember); + + const foundMembers = await searchMembers( + tempData.guildId, + botMember!.username.substring(0, 4), + { + limit: 1, + }, + ); + + assertEquals(foundMembers.size, 1); +} + +Deno.test({ + name: "[members] search guild members", + async fn() { + await ifItFailsBlameWolf(); + }, + ...defaultTestOptions, +}); diff --git a/tests/mod.ts b/tests/mod.ts index 4dcd6f57e..76fd9bced 100644 --- a/tests/mod.ts +++ b/tests/mod.ts @@ -66,6 +66,9 @@ import "./roles/delete_role.ts"; import "./roles/edit_role.ts"; import "./roles/remove_role.ts"; +// Members tests +import "./members/search_members.ts"; + // Final cleanup import "./guilds/delete_server.ts"; import "./ws/ws_close.ts";