@discordeno/gateway 19.0.0-next.a6c3ffe → 19.0.0-next.a8f9e5e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Shard.d.ts +9 -89
- package/dist/Shard.d.ts.map +1 -1
- package/dist/Shard.js +54 -182
- package/dist/Shard.js.map +1 -1
- package/dist/manager.d.ts +9 -3
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +103 -38
- package/dist/manager.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
package/dist/Shard.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type {
|
|
5
|
-
import type { BotStatusUpdate, ShardEvents, ShardGatewayConfig, ShardHeart, ShardSocketRequest, StatusUpdate, UpdateVoiceState } from './types.js';
|
|
1
|
+
import type { DiscordGatewayPayload } from '@discordeno/types';
|
|
2
|
+
import { LeakyBucket } from '@discordeno/utils';
|
|
3
|
+
import NodeWebSocket from 'ws';
|
|
4
|
+
import type { BotStatusUpdate, ShardEvents, ShardGatewayConfig, ShardHeart, ShardSocketRequest } from './types.js';
|
|
6
5
|
import { ShardState } from './types.js';
|
|
7
6
|
export declare class DiscordenoShard {
|
|
8
7
|
/** The id of the shard */
|
|
@@ -20,7 +19,7 @@ export declare class DiscordenoShard {
|
|
|
20
19
|
/** Current session id of the shard if present. */
|
|
21
20
|
sessionId?: string;
|
|
22
21
|
/** This contains the WebSocket connection to Discord, if currently connected. */
|
|
23
|
-
socket?:
|
|
22
|
+
socket?: NodeWebSocket;
|
|
24
23
|
/** Current internal state of the this. */
|
|
25
24
|
state: ShardState;
|
|
26
25
|
/** The url provided by discord to use when resuming a connection for this this. */
|
|
@@ -33,22 +32,10 @@ export declare class DiscordenoShard {
|
|
|
33
32
|
resolves: Map<"READY" | "RESUMED" | "INVALID_SESSION", (payload: DiscordGatewayPayload) => void>;
|
|
34
33
|
/** Shard bucket. Only access this if you know what you are doing. Bucket for handling shard request rate limits. */
|
|
35
34
|
bucket: LeakyBucket;
|
|
36
|
-
/** This managers cache related settings. */
|
|
37
|
-
cache: {
|
|
38
|
-
requestMembers: {
|
|
39
|
-
/**
|
|
40
|
-
* Whether or not request member requests should be cached.
|
|
41
|
-
* @default false
|
|
42
|
-
*/
|
|
43
|
-
enabled: boolean;
|
|
44
|
-
/** The pending requests. */
|
|
45
|
-
pending: Collection<string, RequestMemberRequest>;
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
35
|
constructor(options: ShardCreateOptions);
|
|
49
36
|
/** The gateway configuration which is used to connect to Discord. */
|
|
50
37
|
get gatewayConfig(): ShardGatewayConfig;
|
|
51
|
-
/** The url to connect to.
|
|
38
|
+
/** The url to connect to. Initially this is the discord gateway url, and then is switched to resume gateway url once a READY is received. */
|
|
52
39
|
get connectionUrl(): string;
|
|
53
40
|
/** Calculate the amount of requests which can safely be made per rate limit interval, before the gateway gets disconnected due to an exceeded rate limit. */
|
|
54
41
|
calculateSafeRequests(): number;
|
|
@@ -70,11 +57,12 @@ export declare class DiscordenoShard {
|
|
|
70
57
|
/** Shutdown the this. Forcefully disconnect the shard from Discord. The shard may not attempt to reconnect with Discord. */
|
|
71
58
|
shutdown(): Promise<void>;
|
|
72
59
|
/** Handle a gateway connection close. */
|
|
73
|
-
handleClose(close:
|
|
60
|
+
handleClose(close: NodeWebSocket.CloseEvent): Promise<void>;
|
|
74
61
|
/** Handles a incoming gateway packet. */
|
|
75
62
|
handleDiscordPacket(packet: DiscordGatewayPayload): Promise<void>;
|
|
63
|
+
forwardToBot(packet: DiscordGatewayPayload): void;
|
|
76
64
|
/** Handle an incoming gateway message. */
|
|
77
|
-
handleMessage(message:
|
|
65
|
+
handleMessage(message: NodeWebSocket.MessageEvent): Promise<void>;
|
|
78
66
|
/**
|
|
79
67
|
* Override in order to make the shards presence.
|
|
80
68
|
* async in case devs create the presence based on eg. database values.
|
|
@@ -89,74 +77,6 @@ export declare class DiscordenoShard {
|
|
|
89
77
|
startHeartbeating(interval: number): void;
|
|
90
78
|
/** Stop the heartbeating process with discord. */
|
|
91
79
|
stopHeartbeating(): void;
|
|
92
|
-
/**
|
|
93
|
-
* Connects the bot user to a voice or stage channel.
|
|
94
|
-
*
|
|
95
|
-
* This function sends the _Update Voice State_ gateway command over the gateway behind the scenes.
|
|
96
|
-
*
|
|
97
|
-
* @param guildId - The ID of the guild the voice channel to leave is in.
|
|
98
|
-
* @param channelId - The ID of the channel you want to join.
|
|
99
|
-
*
|
|
100
|
-
* @remarks
|
|
101
|
-
* Requires the `CONNECT` permission.
|
|
102
|
-
*
|
|
103
|
-
* Fires a _Voice State Update_ gateway event.
|
|
104
|
-
*
|
|
105
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state}
|
|
106
|
-
*/
|
|
107
|
-
joinVoiceChannel(guildId: BigString, channelId: BigString, options?: AtLeastOne<Omit<UpdateVoiceState, 'guildId' | 'channelId'>>): Promise<void>;
|
|
108
|
-
/**
|
|
109
|
-
* Edits the bot status in all shards that this gateway manages.
|
|
110
|
-
*
|
|
111
|
-
* @param data The status data to set the bots status to.
|
|
112
|
-
* @returns Promise<void>
|
|
113
|
-
*/
|
|
114
|
-
editBotStatus(data: StatusUpdate): Promise<void>;
|
|
115
|
-
/**
|
|
116
|
-
* Edits the bot's status on one shard.
|
|
117
|
-
*
|
|
118
|
-
* @param shardId The shard id to edit the status for.
|
|
119
|
-
* @param data The status data to set the bots status to.
|
|
120
|
-
* @returns Promise<void>
|
|
121
|
-
*/
|
|
122
|
-
editShardStatus(data: StatusUpdate): Promise<void>;
|
|
123
|
-
/**
|
|
124
|
-
* Fetches the list of members for a guild over the gateway.
|
|
125
|
-
*
|
|
126
|
-
* @param guildId - The ID of the guild to get the list of members for.
|
|
127
|
-
* @param options - The parameters for the fetching of the members.
|
|
128
|
-
*
|
|
129
|
-
* @remarks
|
|
130
|
-
* If requesting the entire member list:
|
|
131
|
-
* - Requires the `GUILD_MEMBERS` intent.
|
|
132
|
-
*
|
|
133
|
-
* If requesting presences ({@link RequestGuildMembers.presences | presences} set to `true`):
|
|
134
|
-
* - Requires the `GUILD_PRESENCES` intent.
|
|
135
|
-
*
|
|
136
|
-
* If requesting a prefix ({@link RequestGuildMembers.query | query} non-`undefined`):
|
|
137
|
-
* - Returns a maximum of 100 members.
|
|
138
|
-
*
|
|
139
|
-
* If requesting a users by ID ({@link RequestGuildMembers.userIds | userIds} non-`undefined`):
|
|
140
|
-
* - Returns a maximum of 100 members.
|
|
141
|
-
*
|
|
142
|
-
* Fires a _Guild Members Chunk_ gateway event for every 1000 members fetched.
|
|
143
|
-
*
|
|
144
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#request-guild-members}
|
|
145
|
-
*/
|
|
146
|
-
requestMembers(guildId: BigString, options?: Omit<RequestGuildMembers, 'guildId'>): Promise<Camelize<DiscordMember[]>>;
|
|
147
|
-
/**
|
|
148
|
-
* Leaves the voice channel the bot user is currently in.
|
|
149
|
-
*
|
|
150
|
-
* This function sends the _Update Voice State_ gateway command over the gateway behind the scenes.
|
|
151
|
-
*
|
|
152
|
-
* @param guildId - The ID of the guild the voice channel to leave is in.
|
|
153
|
-
*
|
|
154
|
-
* @remarks
|
|
155
|
-
* Fires a _Voice State Update_ gateway event.
|
|
156
|
-
*
|
|
157
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state}
|
|
158
|
-
*/
|
|
159
|
-
leaveVoiceChannel(guildId: BigString): Promise<void>;
|
|
160
80
|
}
|
|
161
81
|
export interface ShardCreateOptions {
|
|
162
82
|
/** The shard id */
|
package/dist/Shard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Shard.d.ts","sourceRoot":"","sources":["../src/Shard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Shard.d.ts","sourceRoot":"","sources":["../src/Shard.ts"],"names":[],"mappings":"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"}
|
package/dist/Shard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-confusing-void-expression */ import { GatewayCloseEventCodes,
|
|
2
|
-
import { camelize,
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-confusing-void-expression */ import { GatewayCloseEventCodes, GatewayOpcodes } from '@discordeno/types';
|
|
2
|
+
import { camelize, delay, LeakyBucket, logger } from '@discordeno/utils';
|
|
3
3
|
import { inflateSync } from 'node:zlib';
|
|
4
|
-
import
|
|
4
|
+
import NodeWebSocket from 'ws';
|
|
5
5
|
import { ShardSocketCloseCodes, ShardState } from './types.js';
|
|
6
6
|
export class DiscordenoShard {
|
|
7
7
|
constructor(options){
|
|
@@ -13,15 +13,6 @@ export class DiscordenoShard {
|
|
|
13
13
|
/** The shard related event handlers. */ this.events = {};
|
|
14
14
|
/** Cache for pending gateway requests which should have been send while the gateway went offline. */ this.offlineSendQueue = [];
|
|
15
15
|
/** Resolve internal waiting states. Mapped by SelectedEvents => ResolveFunction */ this.resolves = new Map();
|
|
16
|
-
/** This managers cache related settings. */ this.cache = {
|
|
17
|
-
requestMembers: {
|
|
18
|
-
/**
|
|
19
|
-
* Whether or not request member requests should be cached.
|
|
20
|
-
* @default false
|
|
21
|
-
*/ enabled: false,
|
|
22
|
-
/** The pending requests. */ pending: new Collection()
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
16
|
this.id = options.id;
|
|
26
17
|
this.connection = options.connection;
|
|
27
18
|
this.events = options.events;
|
|
@@ -40,7 +31,7 @@ export class DiscordenoShard {
|
|
|
40
31
|
/** The gateway configuration which is used to connect to Discord. */ get gatewayConfig() {
|
|
41
32
|
return this.connection;
|
|
42
33
|
}
|
|
43
|
-
/** The url to connect to.
|
|
34
|
+
/** The url to connect to. Initially this is the discord gateway url, and then is switched to resume gateway url once a READY is received. */ get connectionUrl() {
|
|
44
35
|
// Use || and not ?? here. ?? will cause a bug.
|
|
45
36
|
return this.resumeGatewayUrl || this.gatewayConfig.url;
|
|
46
37
|
}
|
|
@@ -59,7 +50,7 @@ export class DiscordenoShard {
|
|
|
59
50
|
}
|
|
60
51
|
}
|
|
61
52
|
/** Close the socket connection to discord if present. */ close(code, reason) {
|
|
62
|
-
if (this.socket?.readyState !==
|
|
53
|
+
if (this.socket?.readyState !== NodeWebSocket.OPEN) return;
|
|
63
54
|
this.socket?.close(code, reason);
|
|
64
55
|
}
|
|
65
56
|
/** Connect the shard with the gateway and start heartbeating. This will not identify the shard to the gateway. */ async connect() {
|
|
@@ -75,7 +66,8 @@ export class DiscordenoShard {
|
|
|
75
66
|
const url = new URL(this.connectionUrl);
|
|
76
67
|
url.searchParams.set('v', this.gatewayConfig.version.toString());
|
|
77
68
|
url.searchParams.set('encoding', 'json');
|
|
78
|
-
const socket =
|
|
69
|
+
const socket = // @ts-expect-error Deno
|
|
70
|
+
globalThis.Deno !== undefined && Reflect.has(globalThis, 'Deno') ? new WebSocket(url.toString()) : new NodeWebSocket(url.toString());
|
|
79
71
|
this.socket = socket;
|
|
80
72
|
// TODO: proper event handling
|
|
81
73
|
socket.onerror = (event)=>console.log({
|
|
@@ -145,13 +137,14 @@ export class DiscordenoShard {
|
|
|
145
137
|
});
|
|
146
138
|
}
|
|
147
139
|
/** Check whether the connection to Discord is currently open. */ isOpen() {
|
|
148
|
-
return this.socket?.readyState ===
|
|
140
|
+
return this.socket?.readyState === NodeWebSocket.OPEN;
|
|
149
141
|
}
|
|
150
142
|
/** Attempt to resume the previous shards session with the gateway. */ async resume() {
|
|
151
143
|
logger.debug(`[Gateway] Resuming Shard #${this.id}`);
|
|
152
144
|
// It has been requested to resume the Shards session.
|
|
153
145
|
// It's possible that the shard is still connected with Discord's gateway therefore we need to forcefully close it.
|
|
154
146
|
if (this.isOpen()) {
|
|
147
|
+
logger.debug(`[Gateway] Resuming Shard #${this.id} in isOpen`);
|
|
155
148
|
this.close(ShardSocketCloseCodes.ResumeClosingOldConnection, 'Reconnecting the shard, closing old connection.');
|
|
156
149
|
}
|
|
157
150
|
// Shard has never identified, so we cannot resume.
|
|
@@ -160,8 +153,11 @@ export class DiscordenoShard {
|
|
|
160
153
|
return await this.identify();
|
|
161
154
|
}
|
|
162
155
|
this.state = ShardState.Resuming;
|
|
156
|
+
logger.debug(`[Gateway] Resuming Shard #${this.id}, before connecting`);
|
|
163
157
|
// Before we can resume, we need to create a new connection with Discord's gateway.
|
|
164
158
|
await this.connect();
|
|
159
|
+
logger.debug(// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
160
|
+
`[Gateway] Resuming Shard #${this.id}, after connecting. ${this.gatewayConfig.token} | ${this.sessionId} | ${this.previousSequenceNumber}`);
|
|
165
161
|
this.send({
|
|
166
162
|
op: GatewayOpcodes.Resume,
|
|
167
163
|
d: {
|
|
@@ -170,6 +166,7 @@ export class DiscordenoShard {
|
|
|
170
166
|
seq: this.previousSequenceNumber ?? 0
|
|
171
167
|
}
|
|
172
168
|
}, true);
|
|
169
|
+
logger.debug(`[Gateway] Resuming Shard #${this.id} after send resumg`);
|
|
173
170
|
return await new Promise((resolve)=>{
|
|
174
171
|
this.resolves.set('RESUMED', ()=>resolve());
|
|
175
172
|
// If it is attempted to resume with an invalid session id,
|
|
@@ -249,6 +246,7 @@ export class DiscordenoShard {
|
|
|
249
246
|
case GatewayCloseEventCodes.AlreadyAuthenticated:
|
|
250
247
|
default:
|
|
251
248
|
{
|
|
249
|
+
logger.info(`[Shard] closed shard #${this.id}. Resuming...`);
|
|
252
250
|
this.state = ShardState.Resuming;
|
|
253
251
|
this.events.disconnected?.(this);
|
|
254
252
|
return await this.resume();
|
|
@@ -282,6 +280,7 @@ export class DiscordenoShard {
|
|
|
282
280
|
case GatewayOpcodes.Hello:
|
|
283
281
|
{
|
|
284
282
|
const interval = packet.d.heartbeat_interval;
|
|
283
|
+
logger.debug(`[Gateway] Hello on Shard #${this.id}`);
|
|
285
284
|
this.startHeartbeating(interval);
|
|
286
285
|
if (this.state !== ShardState.Resuming) {
|
|
287
286
|
const currentQueue = [
|
|
@@ -333,25 +332,33 @@ export class DiscordenoShard {
|
|
|
333
332
|
break;
|
|
334
333
|
}
|
|
335
334
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
335
|
+
switch(packet.t){
|
|
336
|
+
case 'RESUMED':
|
|
337
|
+
this.state = ShardState.Connected;
|
|
338
|
+
this.events.resumed?.(this);
|
|
339
|
+
// Continue the requests which have been queued since the shard went offline.
|
|
340
|
+
this.offlineSendQueue.map((resolve)=>resolve());
|
|
341
|
+
this.resolves.get('RESUMED')?.(packet);
|
|
342
|
+
this.resolves.delete('RESUMED');
|
|
343
|
+
break;
|
|
344
|
+
case 'READY':
|
|
345
|
+
{
|
|
346
|
+
// Important for future resumes.
|
|
347
|
+
const payload = packet.d;
|
|
348
|
+
this.resumeGatewayUrl = payload.resume_gateway_url;
|
|
349
|
+
this.sessionId = payload.session_id;
|
|
350
|
+
this.state = ShardState.Connected;
|
|
351
|
+
// Continue the requests which have been queued since the shard went offline.
|
|
352
|
+
// Important when this is a re-identify
|
|
353
|
+
this.offlineSendQueue.map((resolve)=>resolve());
|
|
354
|
+
this.resolves.get('READY')?.(packet);
|
|
355
|
+
this.resolves.delete('READY');
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
case 'GUILD_MEMBERS_CHUNK':
|
|
359
|
+
{
|
|
360
|
+
this.events.guildMemberChunk?.(packet.d);
|
|
361
|
+
}
|
|
355
362
|
}
|
|
356
363
|
// Update the sequence number if it is present
|
|
357
364
|
// `s` can be either `null` or a `number`.
|
|
@@ -359,6 +366,9 @@ export class DiscordenoShard {
|
|
|
359
366
|
if (packet.s !== null) {
|
|
360
367
|
this.previousSequenceNumber = packet.s;
|
|
361
368
|
}
|
|
369
|
+
this.forwardToBot(packet);
|
|
370
|
+
}
|
|
371
|
+
forwardToBot(packet) {
|
|
362
372
|
// The necessary handling required for the Shards connection has been finished.
|
|
363
373
|
// Now the event can be safely forwarded.
|
|
364
374
|
this.events.message?.(this, camelize(packet));
|
|
@@ -385,6 +395,7 @@ export class DiscordenoShard {
|
|
|
385
395
|
/** This function communicates with the management process, in order to know whether its free to identify. When this function resolves, this means that the shard is allowed to send an identify payload to discord. */ async requestIdentify() {}
|
|
386
396
|
/** This function communicates with the management process, in order to tell it can identify the next shard. */ async shardIsReady() {}
|
|
387
397
|
/** Start sending heartbeat payloads to Discord in the provided interval. */ startHeartbeating(interval) {
|
|
398
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id}`);
|
|
388
399
|
// If old heartbeast exist like after resume, clear the old ones.
|
|
389
400
|
if (this.heart.intervalId) clearInterval(this.heart.intervalId);
|
|
390
401
|
if (this.heart.timeoutId) clearTimeout(this.heart.timeoutId);
|
|
@@ -395,6 +406,7 @@ export class DiscordenoShard {
|
|
|
395
406
|
ShardState.Disconnected,
|
|
396
407
|
ShardState.Offline
|
|
397
408
|
].includes(this.state)) {
|
|
409
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} a`);
|
|
398
410
|
this.state = ShardState.Unidentified;
|
|
399
411
|
}
|
|
400
412
|
// The first heartbeat needs to be send with a random delay between `0` and `interval`
|
|
@@ -403,17 +415,22 @@ export class DiscordenoShard {
|
|
|
403
415
|
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating
|
|
404
416
|
const jitter = Math.ceil(this.heart.interval * (Math.random() || 0.5));
|
|
405
417
|
this.heart.timeoutId = setTimeout(()=>{
|
|
418
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} b`);
|
|
406
419
|
if (!this.isOpen()) return;
|
|
420
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} c ${this.previousSequenceNumber}`);
|
|
407
421
|
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
|
408
422
|
this.socket?.send(JSON.stringify({
|
|
409
423
|
op: GatewayOpcodes.Heartbeat,
|
|
410
424
|
d: this.previousSequenceNumber
|
|
411
425
|
}));
|
|
426
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} d`);
|
|
412
427
|
this.heart.lastBeat = Date.now();
|
|
413
428
|
this.heart.acknowledged = false;
|
|
414
429
|
// After the random heartbeat jitter we can start a normal interval.
|
|
415
430
|
this.heart.intervalId = setInterval(async ()=>{
|
|
431
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} e`);
|
|
416
432
|
if (!this.isOpen()) return;
|
|
433
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} f`);
|
|
417
434
|
// gateway.debug("GW DEBUG", `Running setInterval in heartbeat file. Shard: ${shardId}`);
|
|
418
435
|
// gateway.debug("GW HEARTBEATING", { shardId, shard: currentShard });
|
|
419
436
|
// The Shard did not receive a heartbeat ACK from Discord in time,
|
|
@@ -426,11 +443,13 @@ export class DiscordenoShard {
|
|
|
426
443
|
return await this.identify();
|
|
427
444
|
}
|
|
428
445
|
this.heart.acknowledged = false;
|
|
446
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} g`);
|
|
429
447
|
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
|
430
448
|
this.socket?.send(JSON.stringify({
|
|
431
449
|
op: GatewayOpcodes.Heartbeat,
|
|
432
450
|
d: this.previousSequenceNumber
|
|
433
451
|
}));
|
|
452
|
+
logger.debug(`[Gateway] Start Heartbeating Shard #${this.id} h`);
|
|
434
453
|
this.heart.lastBeat = Date.now();
|
|
435
454
|
this.events.heartbeat?.(this);
|
|
436
455
|
}, this.heart.interval);
|
|
@@ -443,153 +462,6 @@ export class DiscordenoShard {
|
|
|
443
462
|
// To go safe we should clear the related timeout too.
|
|
444
463
|
clearTimeout(this.heart.timeoutId);
|
|
445
464
|
}
|
|
446
|
-
/**
|
|
447
|
-
* Connects the bot user to a voice or stage channel.
|
|
448
|
-
*
|
|
449
|
-
* This function sends the _Update Voice State_ gateway command over the gateway behind the scenes.
|
|
450
|
-
*
|
|
451
|
-
* @param guildId - The ID of the guild the voice channel to leave is in.
|
|
452
|
-
* @param channelId - The ID of the channel you want to join.
|
|
453
|
-
*
|
|
454
|
-
* @remarks
|
|
455
|
-
* Requires the `CONNECT` permission.
|
|
456
|
-
*
|
|
457
|
-
* Fires a _Voice State Update_ gateway event.
|
|
458
|
-
*
|
|
459
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state}
|
|
460
|
-
*/ async joinVoiceChannel(guildId, channelId, options) {
|
|
461
|
-
logger.debug(`[Shard] joinVoiceChannel guildId: ${guildId} channelId: ${channelId}`);
|
|
462
|
-
await this.send({
|
|
463
|
-
op: GatewayOpcodes.VoiceStateUpdate,
|
|
464
|
-
d: {
|
|
465
|
-
guild_id: guildId.toString(),
|
|
466
|
-
channel_id: channelId.toString(),
|
|
467
|
-
self_mute: Boolean(options?.selfMute),
|
|
468
|
-
self_deaf: options?.selfDeaf ?? true
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Edits the bot status in all shards that this gateway manages.
|
|
474
|
-
*
|
|
475
|
-
* @param data The status data to set the bots status to.
|
|
476
|
-
* @returns Promise<void>
|
|
477
|
-
*/ async editBotStatus(data) {
|
|
478
|
-
logger.debug(`[Shard] editBotStatus data: ${JSON.stringify(data)}`);
|
|
479
|
-
await this.editShardStatus(data);
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Edits the bot's status on one shard.
|
|
483
|
-
*
|
|
484
|
-
* @param shardId The shard id to edit the status for.
|
|
485
|
-
* @param data The status data to set the bots status to.
|
|
486
|
-
* @returns Promise<void>
|
|
487
|
-
*/ async editShardStatus(data) {
|
|
488
|
-
logger.debug(`[Shard] editShardStatus shardId: ${this.id} -> data: ${JSON.stringify(data)}`);
|
|
489
|
-
await this.send({
|
|
490
|
-
op: GatewayOpcodes.PresenceUpdate,
|
|
491
|
-
d: {
|
|
492
|
-
since: null,
|
|
493
|
-
afk: false,
|
|
494
|
-
activities: data.activities,
|
|
495
|
-
status: data.status
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Fetches the list of members for a guild over the gateway.
|
|
501
|
-
*
|
|
502
|
-
* @param guildId - The ID of the guild to get the list of members for.
|
|
503
|
-
* @param options - The parameters for the fetching of the members.
|
|
504
|
-
*
|
|
505
|
-
* @remarks
|
|
506
|
-
* If requesting the entire member list:
|
|
507
|
-
* - Requires the `GUILD_MEMBERS` intent.
|
|
508
|
-
*
|
|
509
|
-
* If requesting presences ({@link RequestGuildMembers.presences | presences} set to `true`):
|
|
510
|
-
* - Requires the `GUILD_PRESENCES` intent.
|
|
511
|
-
*
|
|
512
|
-
* If requesting a prefix ({@link RequestGuildMembers.query | query} non-`undefined`):
|
|
513
|
-
* - Returns a maximum of 100 members.
|
|
514
|
-
*
|
|
515
|
-
* If requesting a users by ID ({@link RequestGuildMembers.userIds | userIds} non-`undefined`):
|
|
516
|
-
* - Returns a maximum of 100 members.
|
|
517
|
-
*
|
|
518
|
-
* Fires a _Guild Members Chunk_ gateway event for every 1000 members fetched.
|
|
519
|
-
*
|
|
520
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#request-guild-members}
|
|
521
|
-
*/ async requestMembers(guildId, options) {
|
|
522
|
-
// You can request 1 member without the intent
|
|
523
|
-
// Check if intents is not 0 as proxy ws won't set intents in other instances
|
|
524
|
-
if (this.connection.intents && (!options?.limit || options.limit > 1) && !(this.connection.intents & GatewayIntents.GuildMembers)) {
|
|
525
|
-
throw new Error('MISSING_INTENT_GUILD_MEMBERS');
|
|
526
|
-
}
|
|
527
|
-
if (options?.userIds?.length) {
|
|
528
|
-
logger.debug(`[Shard] requestMembers guildId: ${guildId} -> setting user limit based on userIds length: ${options.userIds.length}`);
|
|
529
|
-
options.limit = options.userIds.length;
|
|
530
|
-
}
|
|
531
|
-
const nonce = `${guildId}-${Date.now()}`;
|
|
532
|
-
// Gateway does not require caching these requests so directly send and return
|
|
533
|
-
if (!this.cache.requestMembers?.enabled) {
|
|
534
|
-
logger.debug(`[Shard] requestMembers guildId: ${guildId} -> skipping cache -> options ${JSON.stringify(options)}`);
|
|
535
|
-
await this.send({
|
|
536
|
-
op: GatewayOpcodes.RequestGuildMembers,
|
|
537
|
-
d: {
|
|
538
|
-
guild_id: guildId.toString(),
|
|
539
|
-
// If a query is provided use it, OR if a limit is NOT provided use ""
|
|
540
|
-
query: options?.query ?? (options?.limit ? undefined : ''),
|
|
541
|
-
limit: options?.limit ?? 0,
|
|
542
|
-
presences: options?.presences ?? false,
|
|
543
|
-
user_ids: options?.userIds?.map((id)=>id.toString()),
|
|
544
|
-
nonce
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
return [];
|
|
548
|
-
}
|
|
549
|
-
return await new Promise((resolve)=>{
|
|
550
|
-
this.cache.requestMembers?.pending.set(nonce, {
|
|
551
|
-
nonce,
|
|
552
|
-
resolve,
|
|
553
|
-
members: []
|
|
554
|
-
});
|
|
555
|
-
logger.debug(`[Shard] requestMembers guildId: ${guildId} -> requesting members -> data: ${JSON.stringify(options)}`);
|
|
556
|
-
this.send({
|
|
557
|
-
op: GatewayOpcodes.RequestGuildMembers,
|
|
558
|
-
d: {
|
|
559
|
-
guild_id: guildId.toString(),
|
|
560
|
-
// If a query is provided use it, OR if a limit is NOT provided use ""
|
|
561
|
-
query: options?.query ?? (options?.limit ? undefined : ''),
|
|
562
|
-
limit: options?.limit ?? 0,
|
|
563
|
-
presences: options?.presences ?? false,
|
|
564
|
-
user_ids: options?.userIds?.map((id)=>id.toString()),
|
|
565
|
-
nonce
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Leaves the voice channel the bot user is currently in.
|
|
572
|
-
*
|
|
573
|
-
* This function sends the _Update Voice State_ gateway command over the gateway behind the scenes.
|
|
574
|
-
*
|
|
575
|
-
* @param guildId - The ID of the guild the voice channel to leave is in.
|
|
576
|
-
*
|
|
577
|
-
* @remarks
|
|
578
|
-
* Fires a _Voice State Update_ gateway event.
|
|
579
|
-
*
|
|
580
|
-
* @see {@link https://discord.com/developers/docs/topics/gateway#update-voice-state}
|
|
581
|
-
*/ async leaveVoiceChannel(guildId) {
|
|
582
|
-
logger.debug(`[Shard] leaveVoiceChannel guildId: ${guildId} Shard ${this.id}`);
|
|
583
|
-
await this.send({
|
|
584
|
-
op: GatewayOpcodes.VoiceStateUpdate,
|
|
585
|
-
d: {
|
|
586
|
-
guild_id: guildId.toString(),
|
|
587
|
-
channel_id: null,
|
|
588
|
-
self_mute: false,
|
|
589
|
-
self_deaf: false
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
465
|
}
|
|
594
466
|
export default DiscordenoShard;
|
|
595
467
|
|