mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-18 20:43:30 +01:00
feat(Client): AsyncIterator-returning method for fetching members (#9771)
* feat(Client): AsyncIterator-returning method for fetching members * fix: requested changes --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { setTimeout } from 'node:timers';
|
import { setTimeout, clearTimeout } from 'node:timers';
|
||||||
import type { REST } from '@discordjs/rest';
|
import type { REST } from '@discordjs/rest';
|
||||||
import { calculateShardId } from '@discordjs/util';
|
import { calculateShardId } from '@discordjs/util';
|
||||||
import { WebSocketShardEvents } from '@discordjs/ws';
|
import { WebSocketShardEvents } from '@discordjs/ws';
|
||||||
@@ -199,42 +199,31 @@ export class Client extends AsyncEventEmitter<ManagerShardEventsMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests guild members from the gateway.
|
* Requests guild members from the gateway and returns an async iterator that yields the data from each guild members chunk event.
|
||||||
*
|
*
|
||||||
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-guild-members}
|
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-guild-members}
|
||||||
* @param options - The options for the request
|
* @param options - The options for the request
|
||||||
* @param timeout - The timeout for waiting for each guild members chunk event
|
* @param timeout - The timeout for waiting for each guild members chunk event
|
||||||
|
* @example
|
||||||
|
* Requesting all members from a guild
|
||||||
|
* ```ts
|
||||||
|
* for await (const { members } of client.requestGuildMembersIterator({ guild_id: '1234567890', query: '', limit: 0 })) {
|
||||||
|
* console.log(members);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
public async requestGuildMembers(options: GatewayRequestGuildMembersData, timeout = 10_000) {
|
public async *requestGuildMembersIterator(options: GatewayRequestGuildMembersData, timeout = 10_000) {
|
||||||
const shardId = calculateShardId(options.guild_id, await this.gateway.getShardCount());
|
const shardId = calculateShardId(options.guild_id, await this.gateway.getShardCount());
|
||||||
const nonce = options.nonce ?? DiscordSnowflake.generate().toString();
|
const nonce = options.nonce ?? DiscordSnowflake.generate().toString();
|
||||||
|
|
||||||
const promise = new Promise<RequestGuildMembersResult>((resolve, reject) => {
|
const controller = new AbortController();
|
||||||
const members: RequestGuildMembersResult['members'] = [];
|
|
||||||
const notFound: RequestGuildMembersResult['notFound'] = [];
|
|
||||||
const presences: RequestGuildMembersResult['presences'] = [];
|
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
const createTimer = () =>
|
||||||
reject(new Error('Request timed out'));
|
setTimeout(() => {
|
||||||
|
controller.abort();
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
||||||
const handler = ({ data }: MappedEvents[GatewayDispatchEvents.GuildMembersChunk][0]) => {
|
let timer: NodeJS.Timeout | undefined = createTimer();
|
||||||
timer.refresh();
|
|
||||||
|
|
||||||
if (data.nonce !== nonce) return;
|
|
||||||
|
|
||||||
members.push(...data.members);
|
|
||||||
if ('presences' in data) presences.push(...data.presences);
|
|
||||||
if ('not_found' in data) notFound.push(...data.not_found);
|
|
||||||
|
|
||||||
if (data.chunk_index >= data.chunk_count - 1) {
|
|
||||||
this.off(GatewayDispatchEvents.GuildMembersChunk, handler);
|
|
||||||
resolve({ members, nonce, notFound, presences });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.on(GatewayDispatchEvents.GuildMembersChunk, handler);
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.gateway.send(shardId, {
|
await this.gateway.send(shardId, {
|
||||||
op: GatewayOpcodes.RequestGuildMembers,
|
op: GatewayOpcodes.RequestGuildMembers,
|
||||||
@@ -245,7 +234,72 @@ export class Client extends AsyncEventEmitter<ManagerShardEventsMap> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
try {
|
||||||
|
const iterator = AsyncEventEmitter.on<
|
||||||
|
typeof this,
|
||||||
|
ManagerShardEventsMap,
|
||||||
|
GatewayDispatchEvents.GuildMembersChunk
|
||||||
|
>(this, GatewayDispatchEvents.GuildMembersChunk, { signal: controller.signal });
|
||||||
|
|
||||||
|
for await (const [{ data }] of iterator) {
|
||||||
|
if (data.nonce !== nonce) continue;
|
||||||
|
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = undefined;
|
||||||
|
|
||||||
|
yield {
|
||||||
|
members: data.members,
|
||||||
|
nonce,
|
||||||
|
notFound: data.not_found ?? null,
|
||||||
|
presences: data.presences ?? null,
|
||||||
|
chunkIndex: data.chunk_index,
|
||||||
|
chunkCount: data.chunk_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.chunk_index >= data.chunk_count - 1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
timer = createTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && error.name === 'AbortError') {
|
||||||
|
throw new Error('Request timed out');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests guild members from the gateway.
|
||||||
|
*
|
||||||
|
* @see {@link https://discord.com/developers/docs/topics/gateway-events#request-guild-members}
|
||||||
|
* @param options - The options for the request
|
||||||
|
* @param timeout - The timeout for waiting for each guild members chunk event
|
||||||
|
* @example
|
||||||
|
* Requesting specific members from a guild
|
||||||
|
* ```ts
|
||||||
|
* const { members } = await client.requestGuildMembers({ guild_id: '1234567890', user_ids: ['9876543210'] });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public async requestGuildMembers(options: GatewayRequestGuildMembersData, timeout = 10_000) {
|
||||||
|
const members: RequestGuildMembersResult['members'] = [];
|
||||||
|
const notFound: RequestGuildMembersResult['notFound'] = [];
|
||||||
|
const presences: RequestGuildMembersResult['presences'] = [];
|
||||||
|
const nonce = options.nonce ?? DiscordSnowflake.generate().toString();
|
||||||
|
|
||||||
|
for await (const data of this.requestGuildMembersIterator({ ...options, nonce }, timeout)) {
|
||||||
|
members.push(...data.members);
|
||||||
|
if (data.presences) presences.push(...data.presences);
|
||||||
|
if (data.notFound) notFound.push(...data.notFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { members, nonce, notFound, presences };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user