feat: shard#requestGuildMember

This commit is contained in:
MARCROCK22 2025-01-18 20:30:48 -04:00
parent d0c7438465
commit ed9c63fb11
5 changed files with 122 additions and 12 deletions

26
src/cache/index.ts vendored
View File

@ -92,7 +92,8 @@ export type CachedEvents =
| 'VOICE_STATE_UPDATE' | 'VOICE_STATE_UPDATE'
| 'STAGE_INSTANCE_CREATE' | 'STAGE_INSTANCE_CREATE'
| 'STAGE_INSTANCE_UPDATE' | 'STAGE_INSTANCE_UPDATE'
| 'STAGE_INSTANCE_DELETE'; | 'STAGE_INSTANCE_DELETE'
| 'GUILD_MEMBERS_CHUNK';
export type DisabledCache = { export type DisabledCache = {
[P in NonGuildBased | GuildBased | GuildRelated | SeyfertBased]?: boolean; [P in NonGuildBased | GuildBased | GuildRelated | SeyfertBased]?: boolean;
@ -554,6 +555,29 @@ export class Cache {
); );
} }
break; break;
case 'GUILD_MEMBERS_CHUNK': {
const data: Parameters<Cache['bulkSet']>[0] = [];
if (this.members) {
for (const member of event.d.members) {
data.push(
[CacheFrom.Gateway, 'members', member, member.user.id, event.d.guild_id],
[CacheFrom.Gateway, 'users', member.user, member.user.id],
);
}
}
if (this.presences && event.d.presences) {
for (const presence of event.d.presences) {
data.push([CacheFrom.Gateway, 'presences', presence, presence.user.id, event.d.guild_id]);
}
}
if (data.length) {
await this.bulkSet(data);
}
break;
}
case 'GUILD_MEMBER_ADD': case 'GUILD_MEMBER_ADD':
case 'GUILD_MEMBER_UPDATE': case 'GUILD_MEMBER_UPDATE':
if (event.d.user) await this.members?.set(CacheFrom.Gateway, event.d.user.id, event.d.guild_id, event.d); if (event.d.user) await this.members?.set(CacheFrom.Gateway, event.d.user.id, event.d.guild_id, event.d);

View File

@ -4,7 +4,7 @@ import { WorkerAdapter } from '../cache';
import { type DeepPartial, LogLevels, type MakeRequired, type When, lazyLoadPackage } from '../common'; import { type DeepPartial, LogLevels, type MakeRequired, type When, lazyLoadPackage } from '../common';
import { EventHandler } from '../events'; import { EventHandler } from '../events';
import type { GatewayDispatchPayload, GatewaySendPayload } from '../types'; import type { GatewayDispatchPayload, GatewaySendPayload } from '../types';
import { Shard, type ShardManagerOptions, type WorkerData, properties } from '../websocket'; import { Shard, type ShardManagerOptions, ShardSocketCloseCodes, type WorkerData, properties } from '../websocket';
import type { import type {
WorkerDisconnectedAllShardsResharding, WorkerDisconnectedAllShardsResharding,
WorkerMessages, WorkerMessages,
@ -350,7 +350,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
case 'DISCONNECT_ALL_SHARDS_RESHARDING': case 'DISCONNECT_ALL_SHARDS_RESHARDING':
{ {
for (const i of this.shards.values()) { for (const i of this.shards.values()) {
await i.disconnect(); await i.disconnect(ShardSocketCloseCodes.Resharding);
} }
this.postMessage({ this.postMessage({
type: 'DISCONNECTED_ALL_SHARDS_RESHARDING', type: 'DISCONNECTED_ALL_SHARDS_RESHARDING',

View File

@ -1,13 +1,19 @@
import { inflateSync } from 'node:zlib'; import { inflateSync } from 'node:zlib';
import { LogLevels, Logger, type MakeRequired, MergeOptions, hasIntent } from '../../common'; import { LogLevels, Logger, type MakeRequired, MergeOptions, hasIntent } from '../../common';
import { import {
type APIGuildMember,
GatewayCloseCodes, GatewayCloseCodes,
GatewayDispatchEvents, GatewayDispatchEvents,
type GatewayDispatchPayload, type GatewayDispatchPayload,
type GatewayGuildMembersChunkPresence,
GatewayOpcodes, GatewayOpcodes,
type GatewayReceivePayload, type GatewayReceivePayload,
type GatewaySendPayload, type GatewaySendPayload,
} from '../../types'; } from '../../types';
import type {
GatewayRequestGuildMembersDataWithQuery,
GatewayRequestGuildMembersDataWithUserIds,
} from '../../types/gateway';
import { properties } from '../constants'; import { properties } from '../constants';
import { DynamicBucket } from '../structures'; import { DynamicBucket } from '../structures';
import { ConnectTimeout } from '../structures/timeout'; import { ConnectTimeout } from '../structures/timeout';
@ -42,6 +48,18 @@ export class Shard {
pendingGuilds = new Set<string>(); pendingGuilds = new Set<string>();
options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions'>; options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions'>;
isReady = false; isReady = false;
private requestGuildMembersChunk = new Map<
string,
{
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
resolve: (value: {
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
}) => void;
reject: (reason?: any) => void;
}
>();
constructor( constructor(
public id: number, public id: number,
@ -198,14 +216,14 @@ export class Shard {
); );
} }
disconnect() { disconnect(code = ShardSocketCloseCodes.Shutdown) {
this.debugger?.info(`[Shard #${this.id}] Disconnecting`); this.debugger?.info(`[Shard #${this.id}] Disconnecting`);
this.close(ShardSocketCloseCodes.Shutdown, 'Shard down request'); this.close(code, 'Shard down request');
} }
async reconnect() { async reconnect() {
this.debugger?.info(`[Shard #${this.id}] Reconnecting`); this.debugger?.info(`[Shard #${this.id}] Reconnecting`);
this.disconnect(); this.disconnect(ShardSocketCloseCodes.Reconnect);
await this.connect(); await this.connect();
} }
@ -300,6 +318,29 @@ export class Shard {
this.options.handlePayload(this.id, packet); this.options.handlePayload(this.id, packet);
} }
break; break;
case GatewayDispatchEvents.GuildMembersChunk:
{
if (!packet.d.nonce) {
this.options.handlePayload(this.id, packet);
break;
}
const guildMemberChunk = this.requestGuildMembersChunk.get(packet.d.nonce);
if (!guildMemberChunk) {
this.options.handlePayload(this.id, packet);
break;
}
guildMemberChunk.members.push(...packet.d.members);
if (packet.d.presences) guildMemberChunk.presences.push(...packet.d.presences);
if (packet.d.chunk_index + 1 === packet.d.chunk_count) {
this.requestGuildMembersChunk.delete(packet.d.nonce);
guildMemberChunk.resolve({
members: guildMemberChunk.members,
presences: guildMemberChunk.presences,
});
}
this.options.handlePayload(this.id, packet);
}
break;
default: default:
this.options.handlePayload(this.id, packet); this.options.handlePayload(this.id, packet);
break; break;
@ -309,6 +350,48 @@ export class Shard {
} }
} }
async requestGuildMember(
options:
| Omit<GatewayRequestGuildMembersDataWithQuery, 'nonce'>
| Omit<GatewayRequestGuildMembersDataWithUserIds, 'nonce'>,
) {
const nonce = Date.now().toString() + Math.random().toString(36);
let resolve: (value: {
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
}) => void = () => {
//
};
let reject: (reason?: any) => void = () => {
//
};
const promise = new Promise<{
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
}>((res, rej) => {
resolve = res;
reject = rej;
});
this.requestGuildMembersChunk.set(nonce, {
members: [],
presences: [],
reject,
resolve,
});
this.send(false, {
op: GatewayOpcodes.RequestGuildMembers,
d: {
...options,
nonce,
},
});
return promise;
}
protected async handleClosed(close: { code: number; reason: string }) { protected async handleClosed(close: { code: number; reason: string }) {
this.isReady = false; this.isReady = false;
clearInterval(this.heart.nodeInterval); clearInterval(this.heart.nodeInterval);

View File

@ -19,7 +19,7 @@ import { ShardManagerDefaults } from '../constants';
import { DynamicBucket } from '../structures'; import { DynamicBucket } from '../structures';
import { ConnectQueue } from '../structures/timeout'; import { ConnectQueue } from '../structures/timeout';
import { Shard } from './shard'; import { Shard } from './shard';
import type { ShardData, ShardManagerOptions, WorkerData } from './shared'; import { type ShardData, type ShardManagerOptions, ShardSocketCloseCodes, type WorkerData } from './shared';
let parentPort: import('node:worker_threads').MessagePort; let parentPort: import('node:worker_threads').MessagePort;
let workerData: WorkerData; let workerData: WorkerData;
@ -159,7 +159,7 @@ export class ShardManager extends Map<number, Shard> {
handlePayload = () => { handlePayload = () => {
// //
}; };
this.disconnectAll(); this.disconnectAll(ShardSocketCloseCodes.Resharding);
this.clear(); this.clear();
this.options.totalShards = this.options.shardEnd = this.options.info.shards = info.shards; this.options.totalShards = this.options.shardEnd = this.options.info.shards = info.shards;
@ -220,14 +220,14 @@ export class ShardManager extends Map<number, Shard> {
return this.create(shardId).identify(); return this.create(shardId).identify();
} }
disconnect(shardId: number) { disconnect(shardId: number, code?: ShardSocketCloseCodes) {
this.debugger?.info(`Shard #${shardId} force disconnect`); this.debugger?.info(`Shard #${shardId} force disconnect`);
return this.get(shardId)?.disconnect(); return this.get(shardId)?.disconnect(code);
} }
disconnectAll() { disconnectAll(code = ShardSocketCloseCodes.ShutdownAll) {
this.debugger?.info('Disconnect all shards'); this.debugger?.info('Disconnect all shards');
this.forEach(shard => shard.disconnect()); this.forEach(shard => shard.disconnect(code));
} }
setShardPresence(shardId: number, payload: GatewayUpdatePresence['d']) { setShardPresence(shardId: number, payload: GatewayUpdatePresence['d']) {

View File

@ -133,6 +133,9 @@ export interface ShardOptions extends ShardDetails {
export enum ShardSocketCloseCodes { export enum ShardSocketCloseCodes {
Shutdown = 3000, Shutdown = 3000,
ZombiedConnection = 3010, ZombiedConnection = 3010,
Reconnect = 3020,
Resharding = 3030,
ShutdownAll = 3040,
} }
export interface WorkerData { export interface WorkerData {