lunibee 0.1.6 → 0.1.7
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/README.md +3 -3
- package/dist/builders/commands.d.ts +37 -0
- package/dist/builders/components.d.ts +19 -12
- package/dist/builders/embed.d.ts +3 -1
- package/dist/builders/index.d.ts +1 -1
- package/dist/builders/index.js +146 -19
- package/dist/builders/index.js.map +8 -7
- package/dist/collection/index.d.ts +30 -0
- package/dist/collection/index.js +78 -5
- package/dist/collection/index.js.map +4 -4
- package/dist/core/events.d.ts +2 -0
- package/dist/core/index.d.ts +38 -0
- package/dist/core/index.js +754 -175
- package/dist/core/index.js.map +24 -22
- package/dist/core/permissions.d.ts +112 -53
- package/dist/formatters/index.d.ts +43 -3
- package/dist/formatters/index.js +64 -1
- package/dist/formatters/index.js.map +3 -3
- package/dist/handlers/index.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1762 -291
- package/dist/index.js.map +43 -38
- package/dist/managers/emoji.d.ts +38 -0
- package/dist/managers/guild.d.ts +112 -1
- package/dist/managers/index.d.ts +31 -1
- package/dist/managers/index.js +803 -77
- package/dist/managers/index.js.map +21 -20
- package/dist/rest/index.d.ts +101 -11
- package/dist/rest/index.js +301 -45
- package/dist/rest/index.js.map +8 -6
- package/dist/rest/redis.d.ts +37 -0
- package/dist/rest/routes.d.ts +22 -0
- package/dist/rest/store.d.ts +38 -0
- package/dist/rest/webhook.d.ts +2 -2
- package/dist/sharding/cluster.d.ts +61 -0
- package/dist/sharding/index.d.ts +8 -0
- package/dist/sharding/index.js +286 -18
- package/dist/sharding/index.js.map +8 -7
- package/dist/structures/audit-log.d.ts +26 -0
- package/dist/structures/base.d.ts +32 -0
- package/dist/structures/index.d.ts +29 -1
- package/dist/structures/index.js +1022 -86
- package/dist/structures/index.js.map +14 -10
- package/dist/structures/resources.d.ts +192 -4
- package/dist/types/index.d.ts +76 -0
- package/dist/types/index.js.map +2 -2
- package/dist/utils/index.js.map +2 -2
- package/dist/voice/index.d.ts +88 -0
- package/dist/voice/index.js +204 -5
- package/dist/voice/index.js.map +3 -3
- package/dist/ws/index.d.ts +40 -1
- package/dist/ws/index.js +102 -18
- package/dist/ws/index.js.map +4 -4
- package/package.json +16 -16
- package/dist/structures/permissions.d.ts +0 -73
package/dist/ws/index.js
CHANGED
|
@@ -88,6 +88,22 @@ var GatewayOpcodes = {
|
|
|
88
88
|
Hello: 10,
|
|
89
89
|
HeartbeatAck: 11
|
|
90
90
|
};
|
|
91
|
+
var GatewayCloseCodes = {
|
|
92
|
+
UnknownError: 4000,
|
|
93
|
+
UnknownOpcode: 4001,
|
|
94
|
+
DecodeError: 4002,
|
|
95
|
+
NotAuthenticated: 4003,
|
|
96
|
+
AuthenticationFailed: 4004,
|
|
97
|
+
AlreadyAuthenticated: 4005,
|
|
98
|
+
InvalidSeq: 4007,
|
|
99
|
+
RateLimited: 4008,
|
|
100
|
+
SessionTimedOut: 4009,
|
|
101
|
+
InvalidShard: 4010,
|
|
102
|
+
ShardingRequired: 4011,
|
|
103
|
+
InvalidAPIVersion: 4012,
|
|
104
|
+
InvalidIntents: 4013,
|
|
105
|
+
DisallowedIntents: 4014
|
|
106
|
+
};
|
|
91
107
|
var GatewayState;
|
|
92
108
|
((GatewayState2) => {
|
|
93
109
|
GatewayState2["Connect"] = "CONNECT";
|
|
@@ -100,7 +116,6 @@ var GatewayState;
|
|
|
100
116
|
GatewayState2["Reconnect"] = "RECONNECT";
|
|
101
117
|
GatewayState2["Closed"] = "CLOSED";
|
|
102
118
|
})(GatewayState ||= {});
|
|
103
|
-
|
|
104
119
|
class GatewayError extends Error {
|
|
105
120
|
code;
|
|
106
121
|
constructor(message, code, options) {
|
|
@@ -134,6 +149,8 @@ class Gateway {
|
|
|
134
149
|
#rejectConnect;
|
|
135
150
|
#listeners = new Map;
|
|
136
151
|
#sendTimestamps = [];
|
|
152
|
+
ping = -1;
|
|
153
|
+
#decompressor;
|
|
137
154
|
constructor(options) {
|
|
138
155
|
if (!options.token?.trim())
|
|
139
156
|
throw new TypeError("A Gateway token is required.");
|
|
@@ -154,7 +171,12 @@ class Gateway {
|
|
|
154
171
|
browser: "Discord Android",
|
|
155
172
|
device: "Discord Android"
|
|
156
173
|
},
|
|
157
|
-
presence: {
|
|
174
|
+
presence: {
|
|
175
|
+
status: "online",
|
|
176
|
+
activities: [],
|
|
177
|
+
afk: false,
|
|
178
|
+
since: null
|
|
179
|
+
},
|
|
158
180
|
...options
|
|
159
181
|
};
|
|
160
182
|
if (this.#options.shardId < 0 || this.#options.shardId >= this.#options.shardCount)
|
|
@@ -169,10 +191,13 @@ class Gateway {
|
|
|
169
191
|
return this.#connectPromise;
|
|
170
192
|
this.#closed = false;
|
|
171
193
|
this.#setState("CONNECT" /* Connect */);
|
|
194
|
+
const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}&compress=zlib-stream` : url;
|
|
195
|
+
if (this.#options.compress)
|
|
196
|
+
this.#initDecompressor();
|
|
172
197
|
this.#connectPromise = new Promise((resolve, reject) => {
|
|
173
198
|
this.#resolveConnect = resolve;
|
|
174
199
|
this.#rejectConnect = reject;
|
|
175
|
-
this.#open(
|
|
200
|
+
this.#open(connectURL);
|
|
176
201
|
});
|
|
177
202
|
return this.#connectPromise;
|
|
178
203
|
}
|
|
@@ -208,12 +233,15 @@ class Gateway {
|
|
|
208
233
|
send(payload) {
|
|
209
234
|
if (!payload || !Number.isInteger(payload.op))
|
|
210
235
|
throw new TypeError("Gateway payload must contain an integer opcode.");
|
|
236
|
+
return this.#dispatch(payload, false);
|
|
237
|
+
}
|
|
238
|
+
#dispatch(payload, privileged) {
|
|
211
239
|
if (this.#ws?.readyState !== WebSocket.OPEN)
|
|
212
240
|
return false;
|
|
213
241
|
const now = Date.now();
|
|
214
242
|
while (this.#sendTimestamps.length && now - this.#sendTimestamps[0] >= 60000)
|
|
215
243
|
this.#sendTimestamps.shift();
|
|
216
|
-
if (this.#sendTimestamps.length >= 115) {
|
|
244
|
+
if (!privileged && this.#sendTimestamps.length >= 115) {
|
|
217
245
|
this.#emitError(new GatewayError("Gateway send rate budget exhausted."));
|
|
218
246
|
return false;
|
|
219
247
|
}
|
|
@@ -284,7 +312,11 @@ class Gateway {
|
|
|
284
312
|
ws.addEventListener("message", (event) => {
|
|
285
313
|
this.#lastMessageAt = Date.now();
|
|
286
314
|
this.#zombieReported = false;
|
|
287
|
-
this.#
|
|
315
|
+
if (this.#options.compress && event.data instanceof ArrayBuffer) {
|
|
316
|
+
this.#decompress(new Uint8Array(event.data)).then((text) => this.#message(ws, text)).catch((err) => this.#emitError(err));
|
|
317
|
+
} else {
|
|
318
|
+
this.#message(ws, String(event.data));
|
|
319
|
+
}
|
|
288
320
|
});
|
|
289
321
|
ws.addEventListener("close", (event) => this.#close(ws, event.code));
|
|
290
322
|
ws.addEventListener("error", () => this.#emitError(new GatewayError("Gateway WebSocket error")));
|
|
@@ -292,7 +324,7 @@ class Gateway {
|
|
|
292
324
|
#startZombieDetection() {
|
|
293
325
|
if (this.#zombieTimer)
|
|
294
326
|
clearInterval(this.#zombieTimer);
|
|
295
|
-
const interval = Math.max(1, Math.min(
|
|
327
|
+
const interval = Math.max(1, Math.min(1000, this.#options.heartbeatAckTimeout, this.#options.zombieTimeout / 2));
|
|
296
328
|
this.#zombieTimer = setInterval(() => {
|
|
297
329
|
if (this.#closed || this.#ws?.readyState !== WebSocket.OPEN || this.#lastMessageAt === 0)
|
|
298
330
|
return;
|
|
@@ -343,18 +375,24 @@ class Gateway {
|
|
|
343
375
|
break;
|
|
344
376
|
case GatewayOpcodes.HeartbeatAck:
|
|
345
377
|
this.#heartbeatACK = true;
|
|
378
|
+
this.ping = Date.now() - this.#heartbeatSentAt;
|
|
346
379
|
this.#clearHeartbeatAckTimer();
|
|
347
380
|
this.#emit("heartbeatAck", payload.d);
|
|
348
381
|
break;
|
|
349
382
|
case GatewayOpcodes.Reconnect:
|
|
350
383
|
ws.close(1001, "Server requested reconnect");
|
|
351
384
|
break;
|
|
352
|
-
case GatewayOpcodes.InvalidSession:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
385
|
+
case GatewayOpcodes.InvalidSession: {
|
|
386
|
+
const resumable = payload.d === true;
|
|
387
|
+
if (!resumable) {
|
|
388
|
+
this.#sessionId = undefined;
|
|
389
|
+
this.#sequence = null;
|
|
390
|
+
this.#resumeURL = undefined;
|
|
391
|
+
}
|
|
392
|
+
this.#emit("invalidSession", resumable);
|
|
393
|
+
ws.close(resumable ? GatewayCloseCodes.UnknownError : 1000, resumable ? "Invalid session (resumable)" : "Invalid session");
|
|
357
394
|
break;
|
|
395
|
+
}
|
|
358
396
|
}
|
|
359
397
|
}
|
|
360
398
|
#handleDispatch(event, data) {
|
|
@@ -369,6 +407,10 @@ class Gateway {
|
|
|
369
407
|
this.#attempt = 0;
|
|
370
408
|
this.#setState("READY" /* Ready */);
|
|
371
409
|
this.#emit("ready", data);
|
|
410
|
+
} else if (event === "RESUMED") {
|
|
411
|
+
this.#attempt = 0;
|
|
412
|
+
this.#setState("READY" /* Ready */);
|
|
413
|
+
this.#emit("resumed", data);
|
|
372
414
|
}
|
|
373
415
|
this.#emit("RAW", { event: event ?? "UNKNOWN", data });
|
|
374
416
|
if (this.#closed)
|
|
@@ -446,12 +488,12 @@ class Gateway {
|
|
|
446
488
|
#sendHeartbeat() {
|
|
447
489
|
this.#heartbeatACK = false;
|
|
448
490
|
this.#heartbeatSentAt = Date.now();
|
|
449
|
-
if (!this
|
|
491
|
+
if (!this.#dispatch({
|
|
450
492
|
op: GatewayOpcodes.Heartbeat,
|
|
451
493
|
d: this.#sequence,
|
|
452
494
|
s: null,
|
|
453
495
|
t: null
|
|
454
|
-
})) {
|
|
496
|
+
}, true)) {
|
|
455
497
|
this.#emitError(new GatewayError("Unable to send Gateway heartbeat because the WebSocket is not open."));
|
|
456
498
|
return;
|
|
457
499
|
}
|
|
@@ -484,14 +526,23 @@ class Gateway {
|
|
|
484
526
|
if (action === "identify") {
|
|
485
527
|
this.#sequence = null;
|
|
486
528
|
this.#sessionId = undefined;
|
|
529
|
+
this.#resumeURL = undefined;
|
|
487
530
|
}
|
|
488
531
|
this.#setState("RECONNECT" /* Reconnect */);
|
|
489
532
|
this.#scheduleReconnect();
|
|
490
533
|
}
|
|
491
534
|
#closeAction(code) {
|
|
492
|
-
|
|
535
|
+
const fatal = [
|
|
536
|
+
GatewayCloseCodes.AuthenticationFailed,
|
|
537
|
+
GatewayCloseCodes.InvalidShard,
|
|
538
|
+
GatewayCloseCodes.ShardingRequired,
|
|
539
|
+
GatewayCloseCodes.InvalidAPIVersion,
|
|
540
|
+
GatewayCloseCodes.InvalidIntents,
|
|
541
|
+
GatewayCloseCodes.DisallowedIntents
|
|
542
|
+
];
|
|
543
|
+
if (fatal.includes(code))
|
|
493
544
|
return "stop";
|
|
494
|
-
if (
|
|
545
|
+
if (code === GatewayCloseCodes.InvalidSeq || code === GatewayCloseCodes.SessionTimedOut)
|
|
495
546
|
return "identify";
|
|
496
547
|
return this.#sessionId && this.#sequence !== null ? "resume" : "identify";
|
|
497
548
|
}
|
|
@@ -504,7 +555,7 @@ class Gateway {
|
|
|
504
555
|
return;
|
|
505
556
|
}
|
|
506
557
|
const delay = Math.min(this.#options.reconnectMaxDelay, this.#options.reconnectBaseDelay * 2 ** this.#attempt++);
|
|
507
|
-
const jitter = Math.random() * Math.
|
|
558
|
+
const jitter = Math.random() * Math.max(1, delay * 0.25);
|
|
508
559
|
this.#reconnectTimer = setTimeout(() => {
|
|
509
560
|
this.#reconnectTimer = undefined;
|
|
510
561
|
this.#open(this.#resumeURL ?? "wss://gateway.discord.gg/?v=10&encoding=json");
|
|
@@ -574,12 +625,45 @@ class Gateway {
|
|
|
574
625
|
this.state = next;
|
|
575
626
|
this.#emit("stateChange", { previous, next });
|
|
576
627
|
}
|
|
628
|
+
#initDecompressor() {
|
|
629
|
+
const ds = new DecompressionStream("deflate-raw");
|
|
630
|
+
this.#decompressor = {
|
|
631
|
+
writer: ds.writable.getWriter(),
|
|
632
|
+
reader: ds.readable.getReader()
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
async#decompress(chunk) {
|
|
636
|
+
if (!this.#decompressor)
|
|
637
|
+
this.#initDecompressor();
|
|
638
|
+
const { writer, reader } = this.#decompressor;
|
|
639
|
+
await writer.write(chunk);
|
|
640
|
+
const parts = [];
|
|
641
|
+
while (true) {
|
|
642
|
+
const { done, value } = await Promise.race([
|
|
643
|
+
reader.read(),
|
|
644
|
+
new Promise((resolve) => setTimeout(() => resolve({ done: true, value: undefined }), 50))
|
|
645
|
+
]);
|
|
646
|
+
if (done || !value)
|
|
647
|
+
break;
|
|
648
|
+
parts.push(value);
|
|
649
|
+
}
|
|
650
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
651
|
+
const merged = new Uint8Array(total);
|
|
652
|
+
let offset = 0;
|
|
653
|
+
for (const p of parts) {
|
|
654
|
+
merged.set(p, offset);
|
|
655
|
+
offset += p.length;
|
|
656
|
+
}
|
|
657
|
+
return new TextDecoder().decode(merged);
|
|
658
|
+
}
|
|
577
659
|
}
|
|
578
660
|
export {
|
|
579
661
|
Gateway,
|
|
662
|
+
GatewayCloseCodes,
|
|
580
663
|
GatewayError,
|
|
581
664
|
GatewayOpcodes,
|
|
582
|
-
GatewayState
|
|
665
|
+
GatewayState,
|
|
666
|
+
GatewayState as Status
|
|
583
667
|
};
|
|
584
668
|
|
|
585
|
-
//# debugId=
|
|
669
|
+
//# debugId=267C8623751A500664756E2164756E21
|
package/dist/ws/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../packages/types/src/index.ts", "../../packages/ws/src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/** Discord API snowflake identifier. */\r\nexport type Snowflake = string;\r\n\r\n// ─── Gateway Intents ─────────────────────────────────────────────────────────\r\n\r\n/** Gateway intent bit flags supported by Discord (PascalCase). */\r\nexport const GatewayIntentBits = {\r\n Guilds: 1 << 0,\r\n GuildMembers: 1 << 1,\r\n GuildModeration: 1 << 2,\r\n GuildExpressions: 1 << 3,\r\n GuildIntegrations: 1 << 4,\r\n GuildWebhooks: 1 << 5,\r\n GuildInvites: 1 << 6,\r\n GuildVoiceStates: 1 << 7,\r\n GuildPresences: 1 << 8,\r\n GuildMessages: 1 << 9,\r\n GuildMessageReactions: 1 << 10,\r\n GuildMessageTyping: 1 << 11,\r\n DirectMessages: 1 << 12,\r\n DirectMessageReactions: 1 << 13,\r\n DirectMessageTyping: 1 << 14,\r\n MessageContent: 1 << 15,\r\n GuildScheduledEvents: 1 << 16,\r\n AutoModerationConfiguration: 1 << 20,\r\n AutoModerationExecution: 1 << 21,\r\n GuildMessagePolls: 1 << 24,\r\n DirectMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\r\nexport const IntentBits = {\r\n guild: 1 << 0,\r\n guilds: 1 << 0,\r\n guildMembers: 1 << 1,\r\n guildModeration: 1 << 2,\r\n guildBans: 1 << 2,\r\n guildExpressions: 1 << 3,\r\n guildEmojis: 1 << 3,\r\n guildEmojisAndStickers: 1 << 3,\r\n guildIntegrations: 1 << 4,\r\n guildWebhooks: 1 << 5,\r\n guildInvites: 1 << 6,\r\n guildVoiceStates: 1 << 7,\r\n guildPresences: 1 << 8,\r\n guildMessages: 1 << 9,\r\n guildMessage: 1 << 9,\r\n guildMessageReactions: 1 << 10,\r\n guildMessageTyping: 1 << 11,\r\n directMessages: 1 << 12,\r\n directMessageReactions: 1 << 13,\r\n directMessageTyping: 1 << 14,\r\n messageContent: 1 << 15,\r\n guildScheduledEvents: 1 << 16,\r\n autoModerationConfiguration: 1 << 20,\r\n autoModerationExecution: 1 << 21,\r\n guildMessagePolls: 1 << 24,\r\n directMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Alias for IntentBits. */\r\nexport const Intents = IntentBits;\r\n\r\n/** Gateway intent resolvable value (single bitfield, enum key, or array of bitfields/strings). */\r\nexport type GatewayIntentResolvable =\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | (\r\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\r\n )[];\r\n\r\n/** Resolves any GatewayIntentResolvable into a raw bitfield integer. */\r\nexport function resolveGatewayIntents(\r\n intents: GatewayIntentResolvable,\r\n): number {\r\n if (typeof intents === \"number\") return intents;\r\n if (Array.isArray(intents)) {\r\n return intents.reduce<number>((acc, intent) => {\r\n return acc | resolveGatewayIntents(intent as any);\r\n }, 0);\r\n }\r\n if (typeof intents === \"string\") {\r\n if (intents in GatewayIntentBits)\r\n return (GatewayIntentBits as Record<string, number>)[intents]!;\r\n if (intents in IntentBits)\r\n return (IntentBits as Record<string, number>)[intents]!;\r\n const lower = intents.charAt(0).toLowerCase() + intents.slice(1);\r\n if (lower in IntentBits)\r\n return (IntentBits as Record<string, number>)[lower]!;\r\n const num = Number(intents);\r\n if (!Number.isNaN(num)) return num;\r\n }\r\n return 0;\r\n}\r\n\r\n// ─── Gateway Config ───────────────────────────────────────────────────────────\r\n\r\n/** Gateway connection properties (OS, browser, device). */\r\nexport interface GatewayProperties {\r\n os?: string;\r\n browser?: string;\r\n device?: string;\r\n [key: string]: unknown;\r\n}\r\n/** Gateway presence activity. */\r\nexport interface GatewayPresenceActivity {\r\n name: string;\r\n type?: number;\r\n url?: string;\r\n state?: string;\r\n}\r\n/** Gateway presence data. */\r\nexport interface GatewayPresence {\r\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\r\n activities?: GatewayPresenceActivity[];\r\n afk?: boolean;\r\n since?: number | null;\r\n}\r\n/** Client configuration. */\r\nexport interface ClientOptions {\r\n token: string;\r\n intents: GatewayIntentResolvable;\r\n gateway?: GatewayOptions;\r\n rest?: RESTOptions;\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n intents?: GatewayIntentResolvable;\r\n reconnect?: boolean;\r\n maxReconnectAttempts?: number;\r\n reconnectBaseDelay?: number;\r\n reconnectMaxDelay?: number;\r\n shardId?: number;\r\n shardCount?: number;\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n}\r\n\r\n/** REST request configuration. */\r\nexport interface RESTOptions {\r\n timeout?: number;\r\n retries?: number;\r\n baseURL?: string;\r\n}\r\n\r\n// ─── Core Discord Objects ─────────────────────────────────────────────────────\r\n\r\n/** Discord user object. */\r\nexport interface UserData {\r\n id: Snowflake;\r\n username: string;\r\n discriminator?: string;\r\n global_name?: string | null;\r\n avatar?: string | null;\r\n bot?: boolean;\r\n system?: boolean;\r\n public_flags?: number;\r\n}\r\n/** Current authenticated bot user. */\r\nexport type ClientUser = UserData;\r\n/** Discord Gateway payload envelope. */\r\nexport interface GatewayPayload<T = unknown> {\r\n op: number;\r\n d: T | null;\r\n s: number | null;\r\n t: string | null;\r\n}\r\n/** Generic Discord API error payload. */\r\nexport interface APIError {\r\n code?: number;\r\n message?: string;\r\n errors?: Record<string, unknown>;\r\n}\r\n\r\n// ─── Discord Flags & Enums ────────────────────────────────────────────────────\r\n\r\n/** Discord message flags. */\r\nexport const MessageFlags = {\r\n Crossposted: 1,\r\n IsCrosspost: 2,\r\n SuppressEmbeds: 4,\r\n SourceMessageDeleted: 8,\r\n Urgent: 16,\r\n HasThread: 32,\r\n Ephemeral: 64,\r\n Loading: 128,\r\n IsComponentsV2: 32768,\r\n} as const;\r\n\r\n/** Discord channel types. */\r\nexport const ChannelType = {\r\n GuildText: 0,\r\n DM: 1,\r\n GuildVoice: 2,\r\n GroupDM: 3,\r\n GuildCategory: 4,\r\n GuildAnnouncement: 5,\r\n AnnouncementThread: 10,\r\n PublicThread: 11,\r\n PrivateThread: 12,\r\n GuildStageVoice: 13,\r\n GuildDirectory: 14,\r\n GuildForum: 15,\r\n GuildMedia: 16,\r\n} as const;\r\n\r\n/** Discord component types. */\r\nexport const ComponentType = {\r\n ActionRow: 1,\r\n Button: 2,\r\n StringSelect: 3,\r\n TextInput: 4,\r\n UserSelect: 5,\r\n RoleSelect: 6,\r\n MentionableSelect: 7,\r\n ChannelSelect: 8,\r\n Section: 9,\r\n TextDisplay: 10,\r\n Thumbnail: 11,\r\n MediaGallery: 12,\r\n File: 13,\r\n Separator: 14,\r\n ContentInventoryEntry: 16,\r\n Container: 17,\r\n} as const;\r\n\r\n/** Discord button style types. */\r\nexport const ButtonStyle = {\r\n Primary: 1,\r\n Secondary: 2,\r\n Success: 3,\r\n Danger: 4,\r\n Link: 5,\r\n Premium: 6,\r\n} as const;\r\n\r\n/** Discord text input style types. */\r\nexport const TextInputStyle = {\r\n Short: 1,\r\n Paragraph: 2,\r\n} as const;\r\n\r\n/** Discord interaction response types. */\r\nexport const InteractionResponseType = {\r\n Pong: 1,\r\n ChannelMessage: 4,\r\n DeferredChannelMessage: 5,\r\n DeferredMessageUpdate: 6,\r\n MessageUpdate: 7,\r\n Autocomplete: 8,\r\n Modal: 9,\r\n} as const;\r\n\r\n/** Discord sticker format types. */\r\nexport const StickerFormatType = {\r\n PNG: 1,\r\n APNG: 2,\r\n Lottie: 3,\r\n GIF: 4,\r\n} as const;\r\n\r\n/** Discord sticker types. */\r\nexport const StickerType = {\r\n Standard: 1,\r\n Guild: 2,\r\n} as const;\r\n\r\n/** Discord verification levels. */\r\nexport const VerificationLevel = {\r\n None: 0,\r\n Low: 1,\r\n Medium: 2,\r\n High: 3,\r\n VeryHigh: 4,\r\n} as const;\r\n\r\n/** Discord premium tiers. */\r\nexport const PremiumTier = {\r\n None: 0,\r\n Tier1: 1,\r\n Tier2: 2,\r\n Tier3: 3,\r\n} as const;\r\n\r\n// ─── Embeds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord embed footer object. */\r\nexport interface APIEmbedFooter {\r\n text: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed image/thumbnail/video object. */\r\nexport interface APIEmbedMedia {\r\n url: string;\r\n proxy_url?: string;\r\n height?: number;\r\n width?: number;\r\n}\r\n/** Raw Discord embed provider object. */\r\nexport interface APIEmbedProvider {\r\n name?: string;\r\n url?: string;\r\n}\r\n/** Raw Discord embed author object. */\r\nexport interface APIEmbedAuthor {\r\n name: string;\r\n url?: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed field object. */\r\nexport interface APIEmbedField {\r\n name: string;\r\n value: string;\r\n inline?: boolean;\r\n}\r\n/** Raw Discord embed object. */\r\nexport interface APIEmbed {\r\n title?: string;\r\n type?: string;\r\n description?: string;\r\n url?: string;\r\n timestamp?: string;\r\n color?: number;\r\n footer?: APIEmbedFooter;\r\n image?: APIEmbedMedia;\r\n thumbnail?: APIEmbedMedia;\r\n video?: APIEmbedMedia;\r\n provider?: APIEmbedProvider;\r\n author?: APIEmbedAuthor;\r\n fields?: APIEmbedField[];\r\n}\r\n\r\n// ─── Message Components ───────────────────────────────────────────────────────\r\n\r\n/** Raw Discord select menu option. */\r\nexport interface APISelectMenuOption {\r\n label: string;\r\n value: string;\r\n description?: string;\r\n emoji?: APIPartialEmoji;\r\n default?: boolean;\r\n}\r\n/** Raw Discord emoji (partial, as used in components). */\r\nexport interface APIPartialEmoji {\r\n id?: Snowflake | null;\r\n name?: string | null;\r\n animated?: boolean;\r\n}\r\n/** Raw Discord button component. */\r\nexport interface APIButtonComponent {\r\n type: 2;\r\n style: number;\r\n label?: string;\r\n emoji?: APIPartialEmoji;\r\n custom_id?: string;\r\n url?: string;\r\n sku_id?: string;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord select menu component. */\r\nexport interface APISelectMenuComponent {\r\n type: 3 | 5 | 6 | 7 | 8;\r\n custom_id: string;\r\n options?: APISelectMenuOption[];\r\n channel_types?: number[];\r\n placeholder?: string;\r\n min_values?: number;\r\n max_values?: number;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord text input component. */\r\nexport interface APITextInputComponent {\r\n type: 4;\r\n custom_id: string;\r\n style: number;\r\n label: string;\r\n min_length?: number;\r\n max_length?: number;\r\n required?: boolean;\r\n value?: string;\r\n placeholder?: string;\r\n}\r\n/** Union of all raw message component types. */\r\nexport type APIMessageActionRowComponent =\r\n APIButtonComponent | APISelectMenuComponent | APITextInputComponent;\r\n/** Raw Discord action row component. */\r\nexport interface APIActionRowComponent {\r\n type: 1;\r\n id?: number;\r\n components: APIMessageActionRowComponent[];\r\n}\r\n/** Union including action rows and V2 components. */\r\nexport type APIMessageComponent = APIActionRowComponent | { type: Exclude<number, 1>; [key: string]: any };\r\n\r\n// ─── Attachments ─────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord attachment object. */\r\nexport interface APIAttachment {\r\n id: Snowflake;\r\n filename: string;\r\n description?: string | null;\r\n content_type?: string;\r\n size: number;\r\n url: string;\r\n proxy_url: string;\r\n height?: number | null;\r\n width?: number | null;\r\n ephemeral?: boolean;\r\n duration_secs?: number;\r\n waveform?: string;\r\n flags?: number;\r\n}\r\n\r\n// ─── Stickers ────────────────────────────────────────────────────────────────\r\n\r\n/** Compact sticker item as included in messages. */\r\nexport interface APIStickerItem {\r\n id: Snowflake;\r\n name: string;\r\n format_type: number;\r\n}\r\n/** Full Discord sticker object. */\r\nexport interface APISticker {\r\n id: Snowflake;\r\n pack_id?: Snowflake;\r\n name: string;\r\n description?: string | null;\r\n tags?: string;\r\n type: number;\r\n format_type: number;\r\n available?: boolean;\r\n guild_id?: Snowflake;\r\n user?: UserData;\r\n sort_value?: number;\r\n}\r\n\r\n// ─── Messages ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord message reference. */\r\nexport interface APIMessageReference {\r\n type?: number;\r\n message_id?: Snowflake;\r\n channel_id?: Snowflake;\r\n guild_id?: Snowflake;\r\n fail_if_not_exists?: boolean;\r\n}\r\n/** Raw Discord message reaction. */\r\nexport interface APIMessageReaction {\r\n count: number;\r\n count_details?: Record<string, number>;\r\n me?: boolean;\r\n me_burst?: boolean;\r\n emoji: APIPartialEmoji;\r\n burst_colors?: string[];\r\n}\r\n/** Raw Discord message object. */\r\nexport interface APIMessage {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n author: UserData;\r\n content: string;\r\n timestamp?: string;\r\n edited_timestamp?: string | null;\r\n tts?: boolean;\r\n mention_everyone?: boolean;\r\n mentions?: UserData[];\r\n mention_roles?: Snowflake[];\r\n mention_channels?: Array<Record<string, unknown>>;\r\n attachments?: APIAttachment[];\r\n embeds?: APIEmbed[];\r\n reactions?: APIMessageReaction[];\r\n nonce?: string | number | null;\r\n pinned?: boolean;\r\n webhook_id?: Snowflake;\r\n type?: number;\r\n activity?: Record<string, unknown>;\r\n application?: Record<string, unknown>;\r\n application_id?: Snowflake;\r\n message_reference?: APIMessageReference;\r\n flags?: number;\r\n referenced_message?: APIMessage | null;\r\n interaction_metadata?: Record<string, unknown>;\r\n thread?: Record<string, unknown>;\r\n components?: APIMessageComponent[];\r\n sticker_items?: APIStickerItem[];\r\n stickers?: APISticker[];\r\n position?: number;\r\n role_subscription_data?: Record<string, unknown>;\r\n purchase_notification?: Record<string, unknown>;\r\n poll?: Record<string, unknown>;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Channels ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord channel object. */\r\nexport interface APIChannel {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake;\r\n position?: number;\r\n permission_overwrites?: Array<Record<string, unknown>>;\r\n name?: string | null;\r\n topic?: string | null;\r\n nsfw?: boolean;\r\n last_message_id?: Snowflake | null;\r\n bitrate?: number;\r\n user_limit?: number;\r\n rate_limit_per_user?: number;\r\n recipients?: UserData[];\r\n icon?: string | null;\r\n owner_id?: Snowflake;\r\n application_id?: Snowflake;\r\n parent_id?: Snowflake | null;\r\n last_pin_timestamp?: string | null;\r\n rtc_region?: string | null;\r\n video_quality_mode?: number;\r\n message_count?: number;\r\n member_count?: number;\r\n thread_metadata?: APIThreadMetadata;\r\n default_auto_archive_duration?: number;\r\n flags?: number;\r\n total_message_sent?: number;\r\n available_tags?: APIForumTag[];\r\n default_reaction_emoji?: APIPartialEmoji | null;\r\n default_thread_rate_limit_per_user?: number;\r\n default_sort_order?: number | null;\r\n default_forum_layout?: number;\r\n}\r\n\r\n/** Raw Discord thread metadata. */\r\nexport interface APIThreadMetadata {\r\n archived: boolean;\r\n auto_archive_duration: number;\r\n archive_timestamp: string;\r\n locked: boolean;\r\n invitable?: boolean;\r\n create_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord forum tag. */\r\nexport interface APIForumTag {\r\n id: Snowflake;\r\n name: string;\r\n moderated: boolean;\r\n emoji_id?: Snowflake | null;\r\n emoji_name?: string | null;\r\n}\r\n\r\n/** Raw Discord thread member. */\r\nexport interface APIThreadMember {\r\n id?: Snowflake;\r\n user_id?: Snowflake;\r\n join_timestamp: string;\r\n flags: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Roles ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord role object. */\r\nexport interface APIRole {\r\n id: Snowflake;\r\n name: string;\r\n color: number;\r\n hoist: boolean;\r\n icon?: string | null;\r\n unicode_emoji?: string | null;\r\n position: number;\r\n permissions: string;\r\n managed: boolean;\r\n mentionable: boolean;\r\n tags?: Record<string, unknown>;\r\n flags?: number;\r\n}\r\n\r\n// ─── Emoji ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord emoji object. */\r\nexport interface APIEmoji {\r\n id: Snowflake | null;\r\n name: string | null;\r\n roles?: Snowflake[];\r\n user?: UserData;\r\n require_colons?: boolean;\r\n managed?: boolean;\r\n animated?: boolean;\r\n available?: boolean;\r\n}\r\n\r\n// ─── Guilds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild object. */\r\nexport interface APIGuild {\r\n id: Snowflake;\r\n name: string;\r\n icon?: string | null;\r\n icon_hash?: string | null;\r\n splash?: string | null;\r\n discovery_splash?: string | null;\r\n owner?: boolean;\r\n owner_id?: Snowflake;\r\n permissions?: string;\r\n afk_channel_id?: Snowflake | null;\r\n afk_timeout?: number;\r\n widget_enabled?: boolean;\r\n widget_channel_id?: Snowflake | null;\r\n verification_level?: number;\r\n default_message_notifications?: number;\r\n explicit_content_filter?: number;\r\n roles?: APIRole[];\r\n emojis?: APIEmoji[];\r\n stickers?: APISticker[];\r\n features?: string[];\r\n mfa_level?: number;\r\n application_id?: Snowflake | null;\r\n system_channel_id?: Snowflake | null;\r\n system_channel_flags?: number;\r\n rules_channel_id?: Snowflake | null;\r\n max_presences?: number | null;\r\n max_members?: number;\r\n vanity_url_code?: string | null;\r\n description?: string | null;\r\n banner?: string | null;\r\n premium_tier?: number;\r\n premium_subscription_count?: number;\r\n preferred_locale?: string;\r\n public_updates_channel_id?: Snowflake | null;\r\n max_video_channel_users?: number;\r\n approximate_member_count?: number;\r\n approximate_presence_count?: number;\r\n nsfw_level?: number;\r\n safety_alerts_channel_id?: Snowflake | null;\r\n member_count?: number;\r\n}\r\n\r\n/** Raw Discord guild member object. */\r\nexport interface APIGuildMember {\r\n user: UserData;\r\n nick?: string | null;\r\n avatar?: string | null;\r\n roles: Snowflake[];\r\n joined_at: string;\r\n premium_since?: string | null;\r\n deaf?: boolean;\r\n mute?: boolean;\r\n flags?: number;\r\n pending?: boolean;\r\n permissions?: string;\r\n communication_disabled_until?: string | null;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Voice ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord voice state object. */\r\nexport interface APIVoiceState {\r\n guild_id?: Snowflake;\r\n channel_id?: Snowflake | null;\r\n user_id: Snowflake;\r\n member?: APIGuildMember;\r\n session_id: string;\r\n deaf: boolean;\r\n mute: boolean;\r\n self_deaf: boolean;\r\n self_mute: boolean;\r\n self_stream?: boolean;\r\n self_video: boolean;\r\n suppress: boolean;\r\n request_to_speak_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord voice server update event. */\r\nexport interface APIVoiceServerUpdate {\r\n token: string;\r\n guild_id: Snowflake;\r\n endpoint?: string | null;\r\n}\r\n\r\n// ─── Presence ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord activity object. */\r\nexport interface APIActivity {\r\n name: string;\r\n type: number;\r\n url?: string | null;\r\n created_at: number;\r\n state?: string | null;\r\n details?: string | null;\r\n emoji?: APIPartialEmoji | null;\r\n}\r\n\r\n/** Raw Discord presence update event. */\r\nexport interface APIPresenceUpdate {\r\n user: { id: Snowflake } & Partial<UserData>;\r\n guild_id: Snowflake;\r\n status: string;\r\n activities: APIActivity[];\r\n client_status: { desktop?: string; mobile?: string; web?: string };\r\n}\r\n\r\n// ─── Typing ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord typing start event. */\r\nexport interface APITypingStart {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n user_id: Snowflake;\r\n timestamp: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Guilds ──────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild preview. */\r\nexport interface APIGuildPreview {\r\n id: Snowflake;\r\n name: string;\r\n icon: string | null;\r\n splash: string | null;\r\n discovery_splash: string | null;\r\n emojis: unknown[];\r\n features: string[];\r\n approximate_member_count: number;\r\n approximate_presence_count: number;\r\n description: string | null;\r\n stickers: unknown[];\r\n}\r\n\r\n// ─── Invites ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord invite. */\r\nexport interface APIInvite {\r\n code: string;\r\n guild?: Partial<APIGuild>;\r\n channel: Partial<APIChannel>;\r\n inviter?: UserData;\r\n target_type?: number;\r\n target_user?: UserData;\r\n approximate_presence_count?: number;\r\n approximate_member_count?: number;\r\n}\r\n\r\n/** Raw Discord invite create event. */\r\nexport interface APIInviteCreate {\r\n channel_id: Snowflake;\r\n code: string;\r\n created_at: string;\r\n guild_id?: Snowflake;\r\n inviter?: UserData;\r\n max_age: number;\r\n max_uses: number;\r\n target_type?: number;\r\n target_user?: UserData;\r\n temporary: boolean;\r\n uses: number;\r\n}\r\n\r\n/** Raw Discord invite delete event. */\r\nexport interface APIInviteDelete {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n code: string;\r\n}\r\n\r\n// ─── Webhooks ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord webhooks update event. */\r\nexport interface APIWebhooksUpdate {\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord webhook object. */\r\nexport interface APIWebhook {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake | null;\r\n channel_id?: Snowflake | null;\r\n user?: UserData;\r\n name?: string | null;\r\n avatar?: string | null;\r\n token?: string;\r\n application_id?: Snowflake | null;\r\n url?: string;\r\n}\r\n\r\n// ─── AutoMod ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord AutoMod action. */\r\nexport interface APIAutoModerationAction {\r\n type: number;\r\n metadata?: {\r\n channel_id?: Snowflake;\r\n duration_seconds?: number;\r\n custom_message?: string;\r\n };\r\n}\r\n\r\n/** Raw Discord AutoMod rule object. */\r\nexport interface APIAutoModerationRule {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n name: string;\r\n creator_id: Snowflake;\r\n event_type: number;\r\n trigger_type: number;\r\n trigger_metadata: Record<string, unknown>;\r\n actions: APIAutoModerationAction[];\r\n enabled: boolean;\r\n exempt_roles: Snowflake[];\r\n exempt_channels: Snowflake[];\r\n}\r\n\r\n/** Raw Discord AutoMod action execution event. */\r\nexport interface APIAutoModerationActionExecution {\r\n guild_id: Snowflake;\r\n action: APIAutoModerationAction;\r\n rule_id: Snowflake;\r\n rule_trigger_type: number;\r\n user_id: Snowflake;\r\n channel_id?: Snowflake;\r\n message_id?: Snowflake;\r\n alert_system_message_id?: Snowflake;\r\n content: string;\r\n matched_keyword?: string | null;\r\n matched_content?: string | null;\r\n}\r\n\r\n// ─── Scheduled Events ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild scheduled event entity metadata. */\r\nexport interface APIGuildScheduledEventEntityMetadata {\r\n location?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event object. */\r\nexport interface APIGuildScheduledEvent {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id?: Snowflake | null;\r\n creator_id?: Snowflake | null;\r\n name: string;\r\n description?: string | null;\r\n scheduled_start_time: string;\r\n scheduled_end_time?: string | null;\r\n privacy_level: number;\r\n status: number;\r\n entity_type: number;\r\n entity_id?: Snowflake | null;\r\n entity_metadata?: APIGuildScheduledEventEntityMetadata | null;\r\n creator?: UserData;\r\n user_count?: number;\r\n image?: string | null;\r\n}\r\n\r\n// ─── Stage Instances ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord stage instance object. */\r\nexport interface APIStageInstance {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n topic: string;\r\n privacy_level: number;\r\n discoverable_disabled?: boolean;\r\n guild_scheduled_event_id?: Snowflake | null;\r\n}\r\n\r\n// ─── Application Commands ─────────────────────────────────────────────────────\r\n\r\n/** Discord application command option types. */\r\nexport const ApplicationCommandOptionType = {\r\n SubCommand: 1,\r\n SubCommandGroup: 2,\r\n String: 3,\r\n Integer: 4,\r\n Boolean: 5,\r\n User: 6,\r\n Channel: 7,\r\n Role: 8,\r\n Mentionable: 9,\r\n Number: 10,\r\n Attachment: 11,\r\n} as const;\r\n\r\n/** Discord application command types. */\r\nexport const ApplicationCommandType = {\r\n ChatInput: 1,\r\n User: 2,\r\n Message: 3,\r\n} as const;\r\n\r\n/** Raw Discord application command option. */\r\nexport interface APIApplicationCommandOption {\r\n type: number;\r\n name: string;\r\n description: string;\r\n required?: boolean;\r\n choices?: Array<{ name: string; value: string | number }>;\r\n options?: APIApplicationCommandOption[];\r\n min_value?: number;\r\n max_value?: number;\r\n min_length?: number;\r\n max_length?: number;\r\n autocomplete?: boolean;\r\n channel_types?: number[];\r\n}\r\n\r\n/** Raw Discord application command object (from API). */\r\nexport interface APIApplicationCommand {\r\n id: Snowflake;\r\n type?: number;\r\n application_id: Snowflake;\r\n guild_id?: Snowflake;\r\n name: string;\r\n description: string;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n version: Snowflake;\r\n}\r\n\r\n/** Data for creating/overwriting an application command. */\r\nexport interface ApplicationCommandData {\r\n name: string;\r\n description: string;\r\n type?: number;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n}\r\n\r\n// ─── Gateway Events ───────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord READY event payload. */\r\nexport interface APIReadyEvent {\r\n v: number;\r\n user: UserData;\r\n guilds: Array<{ id: Snowflake; unavailable?: boolean }>;\r\n session_id: string;\r\n resume_gateway_url: string;\r\n shard?: [number, number];\r\n application?: { id: Snowflake; flags: number; [key: string]: unknown };\r\n}\r\n\r\n/** Raw Discord reaction event payload. */\r\nexport interface APIMessageReactionEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n member?: APIGuildMember;\r\n emoji: APIPartialEmoji;\r\n message_author_id?: Snowflake;\r\n burst?: boolean;\r\n type?: number;\r\n}\r\n\r\n/** Raw Discord reaction remove emoji event payload. */\r\nexport interface APIMessageReactionRemoveEmojiEvent {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n message_id: Snowflake;\r\n emoji: APIPartialEmoji;\r\n}\r\n\r\n/** Raw Discord message deletion event payload. */\r\nexport interface APIMessageDeleteEvent {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord bulk message deletion event payload. */\r\nexport interface APIMessageDeleteBulkEvent {\r\n ids: Snowflake[];\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord channel deletion event payload. */\r\nexport interface APIChannelDeleteEvent extends APIChannel {}\r\n\r\n/** Raw Discord guild deletion event payload. */\r\nexport interface APIGuildDeleteEvent {\r\n id: Snowflake;\r\n unavailable?: boolean;\r\n}\r\n\r\n/** Raw Discord thread lifecycle payload. */\r\nexport interface APIThreadEvent extends APIChannel {\r\n guild_id: Snowflake;\r\n member?: APIThreadMember;\r\n}\r\n\r\n/** Raw Discord thread list sync event. */\r\nexport interface APIThreadListSync {\r\n guild_id: Snowflake;\r\n channel_ids?: Snowflake[];\r\n threads: APIChannel[];\r\n members: APIThreadMember[];\r\n}\r\n\r\n/** Raw Discord thread members update event. */\r\nexport interface APIThreadMembersUpdate {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n member_count: number;\r\n added_members?: APIThreadMember[];\r\n removed_member_ids?: Snowflake[];\r\n}\r\n\r\n/** Raw Discord guild role event payload. */\r\nexport interface APIGuildRoleEvent {\r\n guild_id: Snowflake;\r\n role: APIRole;\r\n}\r\n\r\n/** Raw Discord guild role delete event payload. */\r\nexport interface APIGuildRoleDeleteEvent {\r\n guild_id: Snowflake;\r\n role_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord guild ban event payload. */\r\nexport interface APIGuildBanEvent {\r\n guild_id: Snowflake;\r\n user: UserData;\r\n}\r\n\r\n/** Raw Discord guild emojis update event payload. */\r\nexport interface APIGuildEmojisUpdateEvent {\r\n guild_id: Snowflake;\r\n emojis: APIEmoji[];\r\n}\r\n\r\n/** Raw Discord guild stickers update event payload. */\r\nexport interface APIGuildStickersUpdateEvent {\r\n guild_id: Snowflake;\r\n stickers: APISticker[];\r\n}\r\n\r\n/** Raw Discord channel pins update event. */\r\nexport interface APIChannelPinsUpdate {\r\n guild_id?: Snowflake;\r\n channel_id: Snowflake;\r\n last_pin_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord guild members chunk event. */\r\nexport interface APIGuildMembersChunk {\r\n guild_id: Snowflake;\r\n members: APIGuildMember[];\r\n chunk_index: number;\r\n chunk_count: number;\r\n not_found?: Snowflake[];\r\n presences?: APIPresenceUpdate[];\r\n nonce?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event user add/remove event. */\r\nexport interface APIGuildScheduledEventUserEvent {\r\n guild_scheduled_event_id: Snowflake;\r\n user_id: Snowflake;\r\n guild_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord message poll vote add/remove event. */\r\nexport interface APIMessagePollVoteEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n answer_id: number;\r\n}\r\n",
|
|
6
|
-
"import {\r\n resolveGatewayIntents,\r\n type GatewayPayload,\r\n type GatewayProperties,\r\n type GatewayPresence,\r\n type GatewayIntentResolvable,\r\n} from \"@lunibee/types\";\r\n\r\n/** Discord Gateway opcodes. */\r\nexport const GatewayOpcodes = {\r\n Dispatch: 0,\r\n Heartbeat: 1,\r\n Identify: 2,\r\n PresenceUpdate: 3,\r\n VoiceStateUpdate: 4,\r\n Resume: 6,\r\n Reconnect: 7,\r\n RequestGuildMembers: 8,\r\n InvalidSession: 9,\r\n Hello: 10,\r\n HeartbeatAck: 11,\r\n} as const;\r\n/** Gateway connection lifecycle states. */\r\nexport enum GatewayState {\r\n /** Initial connection state. */ Connect = \"CONNECT\",\r\n /** Gateway HELLO received state. */ Hello = \"HELLO\",\r\n /** IDENTIFY operation in progress. */ Identify = \"IDENTIFY\",\r\n /** RESUME operation in progress. */ Resume = \"RESUME\",\r\n /** Gateway READY state. */ Ready = \"READY\",\r\n /** Gateway dispatch processing state. */ Dispatch = \"DISPATCH\",\r\n /** Heartbeat processing state. */ Heartbeat = \"HEARTBEAT\",\r\n /** Reconnect in progress. */ Reconnect = \"RECONNECT\",\r\n /** Gateway is permanently closed. */ Closed = \"CLOSED\",\r\n}\r\n/** Gateway protocol error. */\r\nexport class GatewayError extends Error {\r\n /** Gateway close/error code. */ public readonly code?: number;\r\n /** Creates a Gateway error. @param message Error message. @param code Optional Gateway code. @param options Optional error metadata. */ public constructor(\r\n message: string,\r\n code?: number,\r\n options?: ErrorOptions,\r\n ) {\r\n super(message, options);\r\n this.name = \"GatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n /** Authentication token. */ token: string;\r\n /** Gateway intent bitfield or resolvable. */ intents: GatewayIntentResolvable;\r\n /** Shard identifier. */ shardId?: number;\r\n /** Total shard count. */ shardCount?: number;\r\n /** Whether automatic reconnect is enabled. */ reconnect?: boolean;\r\n /** Maximum reconnect attempts. */ maxReconnectAttempts?: number;\r\n /** Initial reconnect delay. */ reconnectBaseDelay?: number;\r\n /** Maximum reconnect delay. */ reconnectMaxDelay?: number;\r\n /** Heartbeat acknowledgement timeout. */ heartbeatAckTimeout?: number;\r\n /** Zombie connection timeout. */ zombieTimeout?: number;\r\n /** Identification properties. */ properties?: GatewayProperties;\r\n /** Presence data. */ presence?: GatewayPresence;\r\n}\r\n/** Gateway event listener. */\r\ntype GatewayListener = (data: unknown) => unknown;\r\n/** Manages a Discord Gateway connection. */\r\nexport class Gateway {\r\n /** Current Gateway lifecycle state. */\r\n public state: GatewayState = GatewayState.Connect;\r\n #options: Required<Omit<GatewayOptions, \"properties\" | \"presence\">> & {\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n };\r\n #ws?: WebSocket;\r\n #sequence: number | null = null;\r\n #sessionId?: string;\r\n #resumeURL?: string;\r\n #heartbeatTimer?: ReturnType<typeof setInterval>;\r\n #initialHeartbeat?: ReturnType<typeof setTimeout>;\r\n #heartbeatAckTimer?: ReturnType<typeof setTimeout>;\r\n #heartbeatACK = true;\r\n #heartbeatInterval = 0;\r\n #heartbeatSentAt = 0;\r\n #lastMessageAt = 0;\r\n #zombieTimer?: ReturnType<typeof setInterval>;\r\n #zombieReported = false;\r\n #closed = false;\r\n #attempt = 0;\r\n #reconnectTimer?: ReturnType<typeof setTimeout>;\r\n #connectPromise?: Promise<void>;\r\n #resolveConnect?: () => void;\r\n #rejectConnect?: (error: GatewayError) => void;\r\n readonly #listeners = new Map<string, Set<GatewayListener>>();\r\n readonly #sendTimestamps: number[] = [];\r\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\r\n public constructor(options: GatewayOptions) {\r\n if (!options.token?.trim())\r\n throw new TypeError(\"A Gateway token is required.\");\r\n const resolvedIntents = resolveGatewayIntents(options.intents);\r\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\r\n throw new TypeError(\"Gateway intents must be a non-negative integer.\");\r\n this.#options = {\r\n shardId: 0,\r\n shardCount: 1,\r\n reconnect: true,\r\n maxReconnectAttempts: Infinity,\r\n reconnectBaseDelay: 1000,\r\n reconnectMaxDelay: 30000,\r\n heartbeatAckTimeout: 10000,\r\n zombieTimeout: 30000,\r\n properties: {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n },\r\n presence: { status: \"online\", activities: [], afk: false, since: null },\r\n ...options,\r\n };\r\n if (\r\n this.#options.shardId < 0 ||\r\n this.#options.shardId >= this.#options.shardCount\r\n )\r\n throw new RangeError(\r\n \"Gateway shardId must be within the configured shard count.\",\r\n );\r\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\r\n throw new RangeError(\r\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\r\n );\r\n }\r\n\r\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens. @throws {GatewayError} If permanently closed or unable to connect. */\r\n public connect(\r\n url = \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n ): Promise<void> {\r\n if (this.state === GatewayState.Closed)\r\n throw new GatewayError(\"Gateway has been permanently closed.\");\r\n if (this.#connectPromise) return this.#connectPromise;\r\n this.#closed = false;\r\n this.#setState(GatewayState.Connect);\r\n this.#connectPromise = new Promise<void>((resolve, reject) => {\r\n this.#resolveConnect = resolve;\r\n this.#rejectConnect = reject;\r\n this.#open(url);\r\n });\r\n return this.#connectPromise;\r\n }\r\n /** Permanently closes the Gateway connection. */\r\n public close(): void {\r\n this.#closed = true;\r\n this.#clearTimers();\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway connection closed before socket open.\"),\r\n );\r\n const ws = this.#ws;\r\n this.#ws = undefined;\r\n try {\r\n ws?.close(1000, \"Client closed connection\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n this.#setState(GatewayState.Closed);\r\n }\r\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\r\n public emit(event: string, data?: unknown): void {\r\n this.#emit(event, data);\r\n }\r\n public on(event: string, listener: GatewayListener): this {\r\n if (!event || typeof listener !== \"function\")\r\n throw new TypeError(\"Gateway event and listener are required.\");\r\n let listeners = this.#listeners.get(event);\r\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\r\n listeners.add(listener);\r\n return this;\r\n }\r\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n public off(event: string, listener: GatewayListener): this {\r\n this.#listeners.get(event)?.delete(listener);\r\n return this;\r\n }\r\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\r\n public send(payload: GatewayPayload): boolean {\r\n if (!payload || !Number.isInteger(payload.op))\r\n throw new TypeError(\"Gateway payload must contain an integer opcode.\");\r\n if (this.#ws?.readyState !== WebSocket.OPEN) return false;\r\n const now = Date.now();\r\n while (\r\n this.#sendTimestamps.length &&\r\n now - this.#sendTimestamps[0]! >= 60000\r\n )\r\n this.#sendTimestamps.shift();\r\n if (this.#sendTimestamps.length >= 115) {\r\n this.#emitError(new GatewayError(\"Gateway send rate budget exhausted.\"));\r\n return false;\r\n }\r\n try {\r\n this.#ws.send(JSON.stringify(payload));\r\n this.#sendTimestamps.push(now);\r\n return true;\r\n } catch (error) {\r\n this.#emitError(error);\r\n return false;\r\n }\r\n }\r\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\r\n public setPresence(data: GatewayPresence): boolean {\r\n this.#options.presence = { ...this.#options.presence, ...data };\r\n const payload = {\r\n since: this.#options.presence.since ?? null,\r\n activities: this.#options.presence.activities ?? [],\r\n status: this.#options.presence.status ?? \"online\",\r\n afk: Boolean(this.#options.presence.afk),\r\n };\r\n return this.send({\r\n op: GatewayOpcodes.PresenceUpdate,\r\n d: payload,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\r\n public setVoiceState(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.VoiceStateUpdate,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\r\n public requestGuildMembers(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.RequestGuildMembers,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #open(url: string): void {\r\n if (this.#closed) return;\r\n let ws: WebSocket;\r\n try {\r\n ws = new WebSocket(url);\r\n } catch (error) {\r\n const failure = this.#normalizeError(error);\r\n this.#emitError(failure);\r\n if (this.#options.reconnect) {\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n } else this.#settleConnect(failure);\r\n return;\r\n }\r\n this.#ws = ws;\r\n ws.addEventListener(\"open\", () => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n this.#startZombieDetection();\r\n this.#emit(\"open\", undefined);\r\n this.#settleConnect();\r\n });\r\n ws.addEventListener(\"message\", (event) => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n this.#message(ws, String(event.data));\r\n });\r\n ws.addEventListener(\"close\", (event) => this.#close(ws, event.code));\r\n ws.addEventListener(\"error\", () =>\r\n this.#emitError(new GatewayError(\"Gateway WebSocket error\")),\r\n );\r\n }\r\n #startZombieDetection(): void {\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n const interval = Math.max(\r\n 1,\r\n Math.min(\r\n 250,\r\n this.#options.heartbeatAckTimeout,\r\n this.#options.zombieTimeout / 2,\r\n ),\r\n );\r\n this.#zombieTimer = setInterval(() => {\r\n if (\r\n this.#closed ||\r\n this.#ws?.readyState !== WebSocket.OPEN ||\r\n this.#lastMessageAt === 0\r\n )\r\n return;\r\n const silentFor = Date.now() - this.#lastMessageAt;\r\n const deadline = Math.max(\r\n this.#options.zombieTimeout,\r\n this.#heartbeatInterval + this.#options.heartbeatAckTimeout,\r\n );\r\n if (silentFor < deadline || this.#zombieReported) return;\r\n this.#zombieReported = true;\r\n const error = new GatewayError(\r\n `Gateway connection appears stale after ${silentFor}ms without traffic.`,\r\n );\r\n this.#emit(\"zombie\", { silentFor, timeout: deadline });\r\n this.#emitError(error);\r\n try {\r\n this.#ws.close(1001, \"Zombie Gateway connection\");\r\n } catch (closeError) {\r\n this.#emitError(closeError);\r\n }\r\n }, interval);\r\n }\r\n #message(ws: WebSocket, raw: string): void {\r\n let payload: GatewayPayload;\r\n try {\r\n payload = JSON.parse(raw) as GatewayPayload;\r\n } catch (error) {\r\n this.#emitError(\r\n new GatewayError(\"Gateway returned invalid JSON\", undefined, {\r\n cause: error,\r\n }),\r\n );\r\n ws.close(1002, \"Invalid JSON\");\r\n return;\r\n }\r\n if (!payload || typeof payload.op !== \"number\") {\r\n this.#emitError(new GatewayError(\"Gateway returned an invalid payload\"));\r\n ws.close(1002, \"Invalid payload\");\r\n return;\r\n }\r\n if (typeof payload.s === \"number\") this.#sequence = payload.s;\r\n switch (payload.op) {\r\n case GatewayOpcodes.Dispatch:\r\n this.#setState(GatewayState.Dispatch);\r\n this.#handleDispatch(payload.t, payload.d);\r\n break;\r\n case GatewayOpcodes.Hello:\r\n this.#handleHello(payload.d);\r\n break;\r\n case GatewayOpcodes.Heartbeat:\r\n this.#setState(GatewayState.Heartbeat);\r\n this.#sendHeartbeat();\r\n break;\r\n case GatewayOpcodes.HeartbeatAck:\r\n this.#heartbeatACK = true;\r\n this.#clearHeartbeatAckTimer();\r\n this.#emit(\"heartbeatAck\", payload.d);\r\n break;\r\n case GatewayOpcodes.Reconnect:\r\n ws.close(1001, \"Server requested reconnect\");\r\n break;\r\n case GatewayOpcodes.InvalidSession:\r\n this.#sessionId = undefined;\r\n this.#sequence = null;\r\n this.#emit(\"invalidSession\", payload.d);\r\n ws.close(1000, \"Invalid session\");\r\n break;\r\n }\r\n }\r\n #handleDispatch(event: string | null, data: unknown): void {\r\n if (event === \"READY\") {\r\n const ready = data as {\r\n session_id?: unknown;\r\n resume_gateway_url?: unknown;\r\n };\r\n if (\r\n typeof ready?.session_id !== \"string\" ||\r\n typeof ready?.resume_gateway_url !== \"string\"\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway READY payload is missing session information\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#sessionId = ready.session_id;\r\n this.#resumeURL = ready.resume_gateway_url;\r\n this.#attempt = 0;\r\n this.#setState(GatewayState.Ready);\r\n this.#emit(\"ready\", data);\r\n }\r\n this.#emit(\"RAW\", { event: event ?? \"UNKNOWN\", data });\r\n if (this.#closed) return;\r\n this.#emit(event ?? \"dispatch\", data);\r\n }\r\n #handleHello(data: unknown): void {\r\n const interval = (data as { heartbeat_interval?: unknown })\r\n ?.heartbeat_interval;\r\n if (\r\n typeof interval !== \"number\" ||\r\n !Number.isFinite(interval) ||\r\n interval <= 0\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway HELLO payload contains an invalid heartbeat interval\",\r\n ),\r\n );\r\n this.#ws?.close(1002, \"Invalid heartbeat interval\");\r\n return;\r\n }\r\n this.#heartbeatInterval = interval;\r\n this.#setState(\r\n this.#sessionId && this.#sequence !== null && this.#resumeURL\r\n ? GatewayState.Resume\r\n : GatewayState.Hello,\r\n );\r\n this.#startHeartbeat(interval);\r\n this.#identifyOrResume();\r\n }\r\n #identifyOrResume(): void {\r\n if (this.#sessionId && this.#sequence !== null && this.#resumeURL) {\r\n this.#setState(GatewayState.Resume);\r\n this.send({\r\n op: GatewayOpcodes.Resume,\r\n d: {\r\n token: this.#options.token,\r\n session_id: this.#sessionId,\r\n seq: this.#sequence,\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n return;\r\n }\r\n this.#setState(GatewayState.Identify);\r\n const props = this.#options.properties ?? {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n };\r\n const os = props.os ?? \"Android\";\r\n const browser = props.browser ?? \"Discord Android\";\r\n const device = props.device ?? \"Discord Android\";\r\n this.send({\r\n op: GatewayOpcodes.Identify,\r\n d: {\r\n token: this.#options.token,\r\n intents: this.#options.intents,\r\n properties: {\r\n os,\r\n browser,\r\n device,\r\n $os: os,\r\n $browser: browser,\r\n $device: device,\r\n ...props,\r\n },\r\n presence: {\r\n since: this.#options.presence?.since ?? null,\r\n activities: this.#options.presence?.activities ?? [],\r\n status: this.#options.presence?.status ?? \"online\",\r\n afk: Boolean(this.#options.presence?.afk),\r\n },\r\n shard: [this.#options.shardId, this.#options.shardCount],\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #startHeartbeat(interval: number): void {\r\n this.#clearHeartbeatTimers();\r\n this.#heartbeatACK = true;\r\n this.#initialHeartbeat = setTimeout(\r\n () => this.#sendHeartbeat(),\r\n Math.random() * interval,\r\n );\r\n this.#heartbeatTimer = setInterval(() => this.#sendHeartbeat(), interval);\r\n }\r\n #sendHeartbeat(): void {\r\n this.#heartbeatACK = false;\r\n this.#heartbeatSentAt = Date.now();\r\n if (\r\n !this.send({\r\n op: GatewayOpcodes.Heartbeat,\r\n d: this.#sequence,\r\n s: null,\r\n t: null,\r\n })\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Unable to send Gateway heartbeat because the WebSocket is not open.\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatAckTimer = setTimeout(() => {\r\n if (!this.#heartbeatACK) {\r\n const elapsed = Date.now() - this.#heartbeatSentAt;\r\n this.#emitError(\r\n new GatewayError(\r\n `Gateway heartbeat acknowledgement timed out after ${elapsed}ms.`,\r\n ),\r\n );\r\n try {\r\n this.#ws?.close(1001, \"Heartbeat timeout\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n }\r\n }, this.#options.heartbeatAckTimeout);\r\n }\r\n #close(ws: WebSocket, code: number): void {\r\n if (this.#ws !== ws) return;\r\n this.#ws = undefined;\r\n this.#clearTimers();\r\n const action = this.#closeAction(code);\r\n this.#emit(\"close\", { code, action });\r\n if (this.#closed || !this.#options.reconnect || action === \"stop\") {\r\n this.#setState(this.#closed ? GatewayState.Closed : GatewayState.Connect);\r\n if (this.#connectPromise)\r\n this.#settleConnect(\r\n new GatewayError(`Gateway closed before READY (code ${code}).`, code),\r\n );\r\n return;\r\n }\r\n if (action === \"identify\") {\r\n this.#sequence = null;\r\n this.#sessionId = undefined;\r\n }\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n }\r\n #closeAction(code: number): \"resume\" | \"identify\" | \"stop\" {\r\n if ([4004, 4010, 4011, 4012, 4013, 4014].includes(code)) return \"stop\";\r\n if ([4007, 4009].includes(code)) return \"identify\";\r\n return this.#sessionId && this.#sequence !== null ? \"resume\" : \"identify\";\r\n }\r\n #scheduleReconnect(): void {\r\n if (this.#closed || !this.#options.reconnect || this.#reconnectTimer)\r\n return;\r\n if (this.#attempt >= this.#options.maxReconnectAttempts) {\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\r\n );\r\n this.#setState(GatewayState.Connect);\r\n return;\r\n }\r\n const delay = Math.min(\r\n this.#options.reconnectMaxDelay,\r\n this.#options.reconnectBaseDelay * 2 ** this.#attempt++,\r\n );\r\n const jitter = Math.random() * Math.min(250, Math.max(1, delay * 0.25));\r\n this.#reconnectTimer = setTimeout(() => {\r\n this.#reconnectTimer = undefined;\r\n this.#open(\r\n this.#resumeURL ?? \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n );\r\n }, delay + jitter);\r\n }\r\n #settleConnect(error?: GatewayError): void {\r\n const resolve = this.#resolveConnect;\r\n const reject = this.#rejectConnect;\r\n this.#resolveConnect = undefined;\r\n this.#rejectConnect = undefined;\r\n this.#connectPromise = undefined;\r\n if (error) reject?.(error);\r\n else resolve?.();\r\n }\r\n #clearHeartbeatAckTimer(): void {\r\n if (this.#heartbeatAckTimer) clearTimeout(this.#heartbeatAckTimer);\r\n this.#heartbeatAckTimer = undefined;\r\n }\r\n #clearHeartbeatTimers(): void {\r\n if (this.#heartbeatTimer) clearInterval(this.#heartbeatTimer);\r\n if (this.#initialHeartbeat) clearTimeout(this.#initialHeartbeat);\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatTimer = undefined;\r\n this.#initialHeartbeat = undefined;\r\n }\r\n #clearTimers(): void {\r\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n this.#reconnectTimer = undefined;\r\n this.#zombieTimer = undefined;\r\n this.#clearHeartbeatTimers();\r\n }\r\n #normalizeError(error: unknown): GatewayError {\r\n return error instanceof GatewayError\r\n ? error\r\n : new GatewayError(\"Gateway connection failed.\", undefined, {\r\n cause: error,\r\n });\r\n }\r\n #emitError(error: unknown): void {\r\n this.#emit(\r\n \"error\",\r\n error instanceof Error ? error : new GatewayError(String(error)),\r\n );\r\n }\r\n #emit(event: string, data: unknown): void {\r\n for (const listener of this.#listeners.get(event) ?? []) {\r\n try {\r\n const result = listener(data);\r\n if (\r\n result &&\r\n typeof (result as PromiseLike<unknown>).then === \"function\"\r\n )\r\n void Promise.resolve(result).catch((error) => {\r\n if (event !== \"error\") this.#emitError(error);\r\n });\r\n } catch (error) {\r\n if (event !== \"error\") this.#emitError(error);\r\n }\r\n }\r\n }\r\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\r\n #setState(next: GatewayState): void {\r\n if (this.state === next) return;\r\n const previous = this.state;\r\n this.state = next;\r\n this.#emit(\"stateChange\", { previous, next });\r\n }\r\n}\r\n"
|
|
5
|
+
"/** Discord API snowflake identifier. */\r\nexport type Snowflake = string;\r\n\r\n// ─── Gateway Intents ─────────────────────────────────────────────────────────\r\n\r\n/** Gateway intent bit flags supported by Discord (PascalCase). */\r\nexport const GatewayIntentBits = {\r\n Guilds: 1 << 0,\r\n GuildMembers: 1 << 1,\r\n GuildModeration: 1 << 2,\r\n GuildExpressions: 1 << 3,\r\n GuildIntegrations: 1 << 4,\r\n GuildWebhooks: 1 << 5,\r\n GuildInvites: 1 << 6,\r\n GuildVoiceStates: 1 << 7,\r\n GuildPresences: 1 << 8,\r\n GuildMessages: 1 << 9,\r\n GuildMessageReactions: 1 << 10,\r\n GuildMessageTyping: 1 << 11,\r\n DirectMessages: 1 << 12,\r\n DirectMessageReactions: 1 << 13,\r\n DirectMessageTyping: 1 << 14,\r\n MessageContent: 1 << 15,\r\n GuildScheduledEvents: 1 << 16,\r\n AutoModerationConfiguration: 1 << 20,\r\n AutoModerationExecution: 1 << 21,\r\n GuildMessagePolls: 1 << 24,\r\n DirectMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\r\nexport const IntentBits = {\r\n guild: 1 << 0,\r\n guilds: 1 << 0,\r\n guildMembers: 1 << 1,\r\n guildModeration: 1 << 2,\r\n guildBans: 1 << 2,\r\n guildExpressions: 1 << 3,\r\n guildEmojis: 1 << 3,\r\n guildEmojisAndStickers: 1 << 3,\r\n guildIntegrations: 1 << 4,\r\n guildWebhooks: 1 << 5,\r\n guildInvites: 1 << 6,\r\n guildVoiceStates: 1 << 7,\r\n guildPresences: 1 << 8,\r\n guildMessages: 1 << 9,\r\n guildMessage: 1 << 9,\r\n guildMessageReactions: 1 << 10,\r\n guildMessageTyping: 1 << 11,\r\n directMessages: 1 << 12,\r\n directMessageReactions: 1 << 13,\r\n directMessageTyping: 1 << 14,\r\n messageContent: 1 << 15,\r\n guildScheduledEvents: 1 << 16,\r\n autoModerationConfiguration: 1 << 20,\r\n autoModerationExecution: 1 << 21,\r\n guildMessagePolls: 1 << 24,\r\n directMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Alias for IntentBits. */\r\nexport const Intents = IntentBits;\r\n\r\n/** Gateway intent resolvable value (single bitfield, enum key, or array of bitfields/strings). */\r\nexport type GatewayIntentResolvable =\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | (\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | string\r\n )[];\r\n\r\n/** Resolves any GatewayIntentResolvable into a raw bitfield integer. */\r\nexport function resolveGatewayIntents(\r\n intents: GatewayIntentResolvable,\r\n): number {\r\n if (typeof intents === \"number\") return intents;\r\n if (Array.isArray(intents)) {\r\n return intents.reduce<number>((acc, intent) => {\r\n return acc | resolveGatewayIntents(intent as any);\r\n }, 0);\r\n }\r\n if (typeof intents === \"string\") {\r\n if (intents in GatewayIntentBits)\r\n return (GatewayIntentBits as Record<string, number>)[intents]!;\r\n if (intents in IntentBits)\r\n return (IntentBits as Record<string, number>)[intents]!;\r\n const lower = intents.charAt(0).toLowerCase() + intents.slice(1);\r\n if (lower in IntentBits)\r\n return (IntentBits as Record<string, number>)[lower]!;\r\n const num = Number(intents);\r\n if (!Number.isNaN(num)) return num;\r\n }\r\n return 0;\r\n}\r\n\r\n// ─── Gateway Config ───────────────────────────────────────────────────────────\r\n\r\n/** Gateway connection properties (OS, browser, device). */\r\nexport interface GatewayProperties {\r\n os?: string;\r\n browser?: string;\r\n device?: string;\r\n [key: string]: unknown;\r\n}\r\n/** Gateway presence activity. */\r\nexport interface GatewayPresenceActivity {\r\n name: string;\r\n type?: number;\r\n url?: string;\r\n state?: string;\r\n}\r\n/** Gateway presence data. */\r\nexport interface GatewayPresence {\r\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\r\n activities?: GatewayPresenceActivity[];\r\n afk?: boolean;\r\n since?: number | null;\r\n}\r\n/** Client configuration. */\r\nexport interface ClientOptions {\r\n token: string;\r\n intents: GatewayIntentResolvable;\r\n gateway?: GatewayOptions;\r\n rest?: RESTOptions;\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n intents?: GatewayIntentResolvable;\r\n reconnect?: boolean;\r\n maxReconnectAttempts?: number;\r\n reconnectBaseDelay?: number;\r\n reconnectMaxDelay?: number;\r\n shardId?: number;\r\n shardCount?: number;\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n}\r\n\r\n/** REST request configuration. */\r\nexport interface RESTOptions {\r\n timeout?: number;\r\n retries?: number;\r\n baseURL?: string;\r\n}\r\n\r\n// ─── Core Discord Objects ─────────────────────────────────────────────────────\r\n\r\n/** Discord user object. */\r\nexport interface UserData {\r\n id: Snowflake;\r\n username: string;\r\n discriminator?: string;\r\n global_name?: string | null;\r\n avatar?: string | null;\r\n bot?: boolean;\r\n system?: boolean;\r\n public_flags?: number;\r\n}\r\n/** Current authenticated bot user. */\r\nexport type ClientUser = UserData;\r\n/** Discord Gateway payload envelope. */\r\nexport interface GatewayPayload<T = unknown> {\r\n op: number;\r\n d: T | null;\r\n s: number | null;\r\n t: string | null;\r\n}\r\n/** Generic Discord API error payload. */\r\nexport interface APIError {\r\n code?: number;\r\n message?: string;\r\n errors?: Record<string, unknown>;\r\n}\r\n\r\n// ─── Discord Flags & Enums ────────────────────────────────────────────────────\r\n\r\n/** Discord message flags. */\r\nexport const MessageFlags = {\r\n Crossposted: 1,\r\n IsCrosspost: 2,\r\n SuppressEmbeds: 4,\r\n SourceMessageDeleted: 8,\r\n Urgent: 16,\r\n HasThread: 32,\r\n Ephemeral: 64,\r\n Loading: 128,\r\n IsComponentsV2: 32768,\r\n} as const;\r\n\r\n/** Discord channel types. */\r\nexport const ChannelType = {\r\n GuildText: 0,\r\n DM: 1,\r\n GuildVoice: 2,\r\n GroupDM: 3,\r\n GuildCategory: 4,\r\n GuildAnnouncement: 5,\r\n AnnouncementThread: 10,\r\n PublicThread: 11,\r\n PrivateThread: 12,\r\n GuildStageVoice: 13,\r\n GuildDirectory: 14,\r\n GuildForum: 15,\r\n GuildMedia: 16,\r\n} as const;\r\n\r\n/** Discord component types. */\r\nexport const ComponentType = {\r\n ActionRow: 1,\r\n Button: 2,\r\n StringSelect: 3,\r\n TextInput: 4,\r\n UserSelect: 5,\r\n RoleSelect: 6,\r\n MentionableSelect: 7,\r\n ChannelSelect: 8,\r\n Section: 9,\r\n TextDisplay: 10,\r\n Thumbnail: 11,\r\n MediaGallery: 12,\r\n File: 13,\r\n Separator: 14,\r\n ContentInventoryEntry: 16,\r\n Container: 17,\r\n} as const;\r\n\r\n/** Discord button style types. */\r\nexport const ButtonStyle = {\r\n Primary: 1,\r\n Secondary: 2,\r\n Success: 3,\r\n Danger: 4,\r\n Link: 5,\r\n Premium: 6,\r\n} as const;\r\n\r\n/** Discord text input style types. */\r\nexport const TextInputStyle = {\r\n Short: 1,\r\n Paragraph: 2,\r\n} as const;\r\n\r\n/** Discord interaction response types. */\r\nexport const InteractionResponseType = {\r\n Pong: 1,\r\n ChannelMessage: 4,\r\n DeferredChannelMessage: 5,\r\n DeferredMessageUpdate: 6,\r\n MessageUpdate: 7,\r\n Autocomplete: 8,\r\n Modal: 9,\r\n} as const;\r\n\r\n/** Discord sticker format types. */\r\nexport const StickerFormatType = {\r\n PNG: 1,\r\n APNG: 2,\r\n Lottie: 3,\r\n GIF: 4,\r\n} as const;\r\n\r\n/** Discord sticker types. */\r\nexport const StickerType = {\r\n Standard: 1,\r\n Guild: 2,\r\n} as const;\r\n\r\n/** Discord verification levels. */\r\nexport const VerificationLevel = {\r\n None: 0,\r\n Low: 1,\r\n Medium: 2,\r\n High: 3,\r\n VeryHigh: 4,\r\n} as const;\r\n\r\n/** Discord premium tiers. */\r\nexport const PremiumTier = {\r\n None: 0,\r\n Tier1: 1,\r\n Tier2: 2,\r\n Tier3: 3,\r\n} as const;\r\n\r\n// ─── Embeds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord embed footer object. */\r\nexport interface APIEmbedFooter {\r\n text: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed image/thumbnail/video object. */\r\nexport interface APIEmbedMedia {\r\n url: string;\r\n proxy_url?: string;\r\n height?: number;\r\n width?: number;\r\n}\r\n/** Raw Discord embed provider object. */\r\nexport interface APIEmbedProvider {\r\n name?: string;\r\n url?: string;\r\n}\r\n/** Raw Discord embed author object. */\r\nexport interface APIEmbedAuthor {\r\n name: string;\r\n url?: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed field object. */\r\nexport interface APIEmbedField {\r\n name: string;\r\n value: string;\r\n inline?: boolean;\r\n}\r\n/** Raw Discord embed object. */\r\nexport interface APIEmbed {\r\n title?: string;\r\n type?: string;\r\n description?: string;\r\n url?: string;\r\n timestamp?: string;\r\n color?: number;\r\n footer?: APIEmbedFooter;\r\n image?: APIEmbedMedia;\r\n thumbnail?: APIEmbedMedia;\r\n video?: APIEmbedMedia;\r\n provider?: APIEmbedProvider;\r\n author?: APIEmbedAuthor;\r\n fields?: APIEmbedField[];\r\n}\r\n\r\n// ─── Message Components ───────────────────────────────────────────────────────\r\n\r\n/** Raw Discord select menu option. */\r\nexport interface APISelectMenuOption {\r\n label: string;\r\n value: string;\r\n description?: string;\r\n emoji?: APIPartialEmoji;\r\n default?: boolean;\r\n}\r\n/** Raw Discord emoji (partial, as used in components). */\r\nexport interface APIPartialEmoji {\r\n id?: Snowflake | null;\r\n name?: string | null;\r\n animated?: boolean;\r\n}\r\n/** Raw Discord button component. */\r\nexport interface APIButtonComponent {\r\n type: 2;\r\n style: number;\r\n label?: string;\r\n emoji?: APIPartialEmoji;\r\n custom_id?: string;\r\n url?: string;\r\n sku_id?: string;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord select menu component. */\r\nexport interface APISelectMenuComponent {\r\n type: 3 | 5 | 6 | 7 | 8;\r\n custom_id: string;\r\n options?: APISelectMenuOption[];\r\n channel_types?: number[];\r\n placeholder?: string;\r\n min_values?: number;\r\n max_values?: number;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord text input component. */\r\nexport interface APITextInputComponent {\r\n type: 4;\r\n custom_id: string;\r\n style: number;\r\n label: string;\r\n min_length?: number;\r\n max_length?: number;\r\n required?: boolean;\r\n value?: string;\r\n placeholder?: string;\r\n}\r\n/** Union of all raw message component types. */\r\nexport type APIMessageActionRowComponent =\r\n APIButtonComponent | APISelectMenuComponent | APITextInputComponent;\r\n/** Raw Discord action row component. */\r\nexport interface APIActionRowComponent {\r\n type: 1;\r\n id?: number;\r\n components: APIMessageActionRowComponent[];\r\n}\r\n/** Union including action rows and V2 components. */\r\nexport type APIMessageComponent =\r\n APIActionRowComponent | { type: Exclude<number, 1>; [key: string]: any };\r\n\r\n// ─── Attachments ─────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord attachment object. */\r\nexport interface APIAttachment {\r\n id: Snowflake;\r\n filename: string;\r\n description?: string | null;\r\n content_type?: string;\r\n size: number;\r\n url: string;\r\n proxy_url: string;\r\n height?: number | null;\r\n width?: number | null;\r\n ephemeral?: boolean;\r\n duration_secs?: number;\r\n waveform?: string;\r\n flags?: number;\r\n}\r\n\r\n// ─── Stickers ────────────────────────────────────────────────────────────────\r\n\r\n/** Compact sticker item as included in messages. */\r\nexport interface APIStickerItem {\r\n id: Snowflake;\r\n name: string;\r\n format_type: number;\r\n}\r\n/** Full Discord sticker object. */\r\nexport interface APISticker {\r\n id: Snowflake;\r\n pack_id?: Snowflake;\r\n name: string;\r\n description?: string | null;\r\n tags?: string;\r\n type: number;\r\n format_type: number;\r\n available?: boolean;\r\n guild_id?: Snowflake;\r\n user?: UserData;\r\n sort_value?: number;\r\n}\r\n\r\n// ─── Messages ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord message reference. */\r\nexport interface APIMessageReference {\r\n type?: number;\r\n message_id?: Snowflake;\r\n channel_id?: Snowflake;\r\n guild_id?: Snowflake;\r\n fail_if_not_exists?: boolean;\r\n}\r\n/** Raw Discord message reaction. */\r\nexport interface APIMessageReaction {\r\n count: number;\r\n count_details?: Record<string, number>;\r\n me?: boolean;\r\n me_burst?: boolean;\r\n emoji: APIPartialEmoji;\r\n burst_colors?: string[];\r\n}\r\n/** Raw Discord message object. */\r\nexport interface APIMessage {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n author: UserData;\r\n content: string;\r\n timestamp?: string;\r\n edited_timestamp?: string | null;\r\n tts?: boolean;\r\n mention_everyone?: boolean;\r\n mentions?: UserData[];\r\n mention_roles?: Snowflake[];\r\n mention_channels?: Array<Record<string, unknown>>;\r\n attachments?: APIAttachment[];\r\n embeds?: APIEmbed[];\r\n reactions?: APIMessageReaction[];\r\n nonce?: string | number | null;\r\n pinned?: boolean;\r\n webhook_id?: Snowflake;\r\n type?: number;\r\n activity?: Record<string, unknown>;\r\n application?: Record<string, unknown>;\r\n application_id?: Snowflake;\r\n message_reference?: APIMessageReference;\r\n flags?: number;\r\n referenced_message?: APIMessage | null;\r\n interaction_metadata?: Record<string, unknown>;\r\n thread?: Record<string, unknown>;\r\n components?: APIMessageComponent[];\r\n sticker_items?: APIStickerItem[];\r\n stickers?: APISticker[];\r\n position?: number;\r\n role_subscription_data?: Record<string, unknown>;\r\n purchase_notification?: Record<string, unknown>;\r\n poll?: Record<string, unknown>;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Channels ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord channel object. */\r\nexport interface APIChannel {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake;\r\n position?: number;\r\n permission_overwrites?: Array<Record<string, unknown>>;\r\n name?: string | null;\r\n topic?: string | null;\r\n nsfw?: boolean;\r\n last_message_id?: Snowflake | null;\r\n bitrate?: number;\r\n user_limit?: number;\r\n rate_limit_per_user?: number;\r\n recipients?: UserData[];\r\n icon?: string | null;\r\n owner_id?: Snowflake;\r\n application_id?: Snowflake;\r\n parent_id?: Snowflake | null;\r\n last_pin_timestamp?: string | null;\r\n rtc_region?: string | null;\r\n video_quality_mode?: number;\r\n message_count?: number;\r\n member_count?: number;\r\n thread_metadata?: APIThreadMetadata;\r\n default_auto_archive_duration?: number;\r\n flags?: number;\r\n total_message_sent?: number;\r\n available_tags?: APIForumTag[];\r\n default_reaction_emoji?: APIPartialEmoji | null;\r\n default_thread_rate_limit_per_user?: number;\r\n default_sort_order?: number | null;\r\n default_forum_layout?: number;\r\n}\r\n\r\n/** Raw Discord thread metadata. */\r\nexport interface APIThreadMetadata {\r\n archived: boolean;\r\n auto_archive_duration: number;\r\n archive_timestamp: string;\r\n locked: boolean;\r\n invitable?: boolean;\r\n create_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord forum tag. */\r\nexport interface APIForumTag {\r\n id: Snowflake;\r\n name: string;\r\n moderated: boolean;\r\n emoji_id?: Snowflake | null;\r\n emoji_name?: string | null;\r\n}\r\n\r\n/** Raw Discord thread member. */\r\nexport interface APIThreadMember {\r\n id?: Snowflake;\r\n user_id?: Snowflake;\r\n join_timestamp: string;\r\n flags: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Roles ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord role object. */\r\nexport interface APIRole {\r\n id: Snowflake;\r\n name: string;\r\n color: number;\r\n hoist: boolean;\r\n icon?: string | null;\r\n unicode_emoji?: string | null;\r\n position: number;\r\n permissions: string;\r\n managed: boolean;\r\n mentionable: boolean;\r\n tags?: Record<string, unknown>;\r\n flags?: number;\r\n}\r\n\r\n// ─── Emoji ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord emoji object. */\r\nexport interface APIEmoji {\r\n id: Snowflake | null;\r\n name: string | null;\r\n roles?: Snowflake[];\r\n user?: UserData;\r\n require_colons?: boolean;\r\n managed?: boolean;\r\n animated?: boolean;\r\n available?: boolean;\r\n}\r\n\r\n// ─── Guilds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild object. */\r\nexport interface APIGuild {\r\n id: Snowflake;\r\n name: string;\r\n icon?: string | null;\r\n icon_hash?: string | null;\r\n splash?: string | null;\r\n discovery_splash?: string | null;\r\n owner?: boolean;\r\n owner_id?: Snowflake;\r\n permissions?: string;\r\n afk_channel_id?: Snowflake | null;\r\n afk_timeout?: number;\r\n widget_enabled?: boolean;\r\n widget_channel_id?: Snowflake | null;\r\n verification_level?: number;\r\n default_message_notifications?: number;\r\n explicit_content_filter?: number;\r\n roles?: APIRole[];\r\n emojis?: APIEmoji[];\r\n stickers?: APISticker[];\r\n features?: string[];\r\n mfa_level?: number;\r\n application_id?: Snowflake | null;\r\n system_channel_id?: Snowflake | null;\r\n system_channel_flags?: number;\r\n rules_channel_id?: Snowflake | null;\r\n max_presences?: number | null;\r\n max_members?: number;\r\n vanity_url_code?: string | null;\r\n description?: string | null;\r\n banner?: string | null;\r\n premium_tier?: number;\r\n premium_subscription_count?: number;\r\n preferred_locale?: string;\r\n public_updates_channel_id?: Snowflake | null;\r\n max_video_channel_users?: number;\r\n approximate_member_count?: number;\r\n approximate_presence_count?: number;\r\n nsfw_level?: number;\r\n safety_alerts_channel_id?: Snowflake | null;\r\n member_count?: number;\r\n}\r\n\r\n// ─── Welcome Screen ───────────────────────────────────────────────────────────\r\n\r\nexport interface APIWelcomeScreenChannel {\r\n channel_id: Snowflake;\r\n description: string;\r\n emoji_id: Snowflake | null;\r\n emoji_name: string | null;\r\n}\r\n\r\nexport interface APIGuildWelcomeScreen {\r\n description: string | null;\r\n welcome_channels: APIWelcomeScreenChannel[];\r\n}\r\n\r\n// ─── Guild Onboarding ─────────────────────────────────────────────────────────\r\n\r\nexport interface APIOnboardingPromptOption {\r\n id: Snowflake;\r\n channel_ids: Snowflake[];\r\n role_ids: Snowflake[];\r\n emoji?: APIPartialEmoji;\r\n title: string;\r\n description: string | null;\r\n}\r\n\r\nexport interface APIOnboardingPrompt {\r\n id: Snowflake;\r\n type: number;\r\n options: APIOnboardingPromptOption[];\r\n title: string;\r\n single_select: boolean;\r\n required: boolean;\r\n in_onboarding: boolean;\r\n}\r\n\r\nexport interface APIGuildOnboarding {\r\n guild_id: Snowflake;\r\n prompts: APIOnboardingPrompt[];\r\n default_channel_ids: Snowflake[];\r\n enabled: boolean;\r\n mode: number;\r\n}\r\n\r\n/** Raw Discord guild member object. */\r\nexport interface APIGuildMember {\r\n user: UserData;\r\n nick?: string | null;\r\n avatar?: string | null;\r\n roles: Snowflake[];\r\n joined_at: string;\r\n premium_since?: string | null;\r\n deaf?: boolean;\r\n mute?: boolean;\r\n flags?: number;\r\n pending?: boolean;\r\n permissions?: string;\r\n communication_disabled_until?: string | null;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Voice ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord voice state object. */\r\nexport interface APIVoiceState {\r\n guild_id?: Snowflake;\r\n channel_id?: Snowflake | null;\r\n user_id: Snowflake;\r\n member?: APIGuildMember;\r\n session_id: string;\r\n deaf: boolean;\r\n mute: boolean;\r\n self_deaf: boolean;\r\n self_mute: boolean;\r\n self_stream?: boolean;\r\n self_video: boolean;\r\n suppress: boolean;\r\n request_to_speak_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord voice server update event. */\r\nexport interface APIVoiceServerUpdate {\r\n token: string;\r\n guild_id: Snowflake;\r\n endpoint?: string | null;\r\n}\r\n\r\n// ─── Presence ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord activity object. */\r\nexport interface APIActivity {\r\n name: string;\r\n type: number;\r\n url?: string | null;\r\n created_at: number;\r\n state?: string | null;\r\n details?: string | null;\r\n emoji?: APIPartialEmoji | null;\r\n}\r\n\r\n/** Raw Discord presence update event. */\r\nexport interface APIPresenceUpdate {\r\n user: { id: Snowflake } & Partial<UserData>;\r\n guild_id: Snowflake;\r\n status: string;\r\n activities: APIActivity[];\r\n client_status: { desktop?: string; mobile?: string; web?: string };\r\n}\r\n\r\n// ─── Typing ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord typing start event. */\r\nexport interface APITypingStart {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n user_id: Snowflake;\r\n timestamp: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Guilds ──────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild preview. */\r\nexport interface APIGuildPreview {\r\n id: Snowflake;\r\n name: string;\r\n icon: string | null;\r\n splash: string | null;\r\n discovery_splash: string | null;\r\n emojis: unknown[];\r\n features: string[];\r\n approximate_member_count: number;\r\n approximate_presence_count: number;\r\n description: string | null;\r\n stickers: unknown[];\r\n}\r\n\r\n// ─── Invites ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord invite. */\r\nexport interface APIInvite {\r\n code: string;\r\n guild?: Partial<APIGuild>;\r\n channel: Partial<APIChannel>;\r\n inviter?: UserData;\r\n target_type?: number;\r\n target_user?: UserData;\r\n approximate_presence_count?: number;\r\n approximate_member_count?: number;\r\n}\r\n\r\n/** Raw Discord invite create event. */\r\nexport interface APIInviteCreate {\r\n channel_id: Snowflake;\r\n code: string;\r\n created_at: string;\r\n guild_id?: Snowflake;\r\n inviter?: UserData;\r\n max_age: number;\r\n max_uses: number;\r\n target_type?: number;\r\n target_user?: UserData;\r\n temporary: boolean;\r\n uses: number;\r\n}\r\n\r\n/** Raw Discord invite delete event. */\r\nexport interface APIInviteDelete {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n code: string;\r\n}\r\n\r\n// ─── Webhooks ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord webhooks update event. */\r\nexport interface APIWebhooksUpdate {\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord webhook object. */\r\nexport interface APIWebhook {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake | null;\r\n channel_id?: Snowflake | null;\r\n user?: UserData;\r\n name?: string | null;\r\n avatar?: string | null;\r\n token?: string;\r\n application_id?: Snowflake | null;\r\n url?: string;\r\n}\r\n\r\n// ─── AutoMod ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord AutoMod action. */\r\nexport interface APIAutoModerationAction {\r\n type: number;\r\n metadata?: {\r\n channel_id?: Snowflake;\r\n duration_seconds?: number;\r\n custom_message?: string;\r\n };\r\n}\r\n\r\n// ─── Audit Logs ──────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord audit log change object. */\r\nexport interface APIAuditLogChange {\r\n new_value?: unknown;\r\n old_value?: unknown;\r\n key: string;\r\n}\r\n\r\n/** Raw Discord audit log entry options object. */\r\nexport interface APIAuditLogOptions {\r\n app_id?: Snowflake;\r\n auto_moderation_rule_name?: string;\r\n auto_moderation_rule_trigger_type?: string;\r\n channel_id?: Snowflake;\r\n count?: string;\r\n delete_member_days?: string;\r\n id?: Snowflake;\r\n members_removed?: string;\r\n message_id?: Snowflake;\r\n role_name?: string;\r\n type?: string;\r\n integration_type?: string;\r\n}\r\n\r\n/** Raw Discord audit log entry object. */\r\nexport interface APIAuditLogEntry {\r\n target_id: string | null;\r\n changes?: APIAuditLogChange[];\r\n user_id: Snowflake | null;\r\n id: Snowflake;\r\n action_type: number;\r\n options?: APIAuditLogOptions;\r\n reason?: string;\r\n}\r\n\r\n/** Raw Discord audit log object. */\r\nexport interface APIAuditLog {\r\n audit_log_entries: APIAuditLogEntry[];\r\n guild_scheduled_events: APIGuildScheduledEvent[];\r\n integrations: Array<Record<string, unknown>>;\r\n threads: APIChannel[];\r\n users: UserData[];\r\n webhooks: APIWebhook[];\r\n application_commands: APIApplicationCommand[];\r\n auto_moderation_rules: APIAutoModerationRule[];\r\n}\r\n\r\n/** Raw Discord AutoMod rule object. */\r\nexport interface APIAutoModerationRule {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n name: string;\r\n creator_id: Snowflake;\r\n event_type: number;\r\n trigger_type: number;\r\n trigger_metadata: Record<string, unknown>;\r\n actions: APIAutoModerationAction[];\r\n enabled: boolean;\r\n exempt_roles: Snowflake[];\r\n exempt_channels: Snowflake[];\r\n}\r\n\r\n/** Raw Discord AutoMod action execution event. */\r\nexport interface APIAutoModerationActionExecution {\r\n guild_id: Snowflake;\r\n action: APIAutoModerationAction;\r\n rule_id: Snowflake;\r\n rule_trigger_type: number;\r\n user_id: Snowflake;\r\n channel_id?: Snowflake;\r\n message_id?: Snowflake;\r\n alert_system_message_id?: Snowflake;\r\n content: string;\r\n matched_keyword?: string | null;\r\n matched_content?: string | null;\r\n}\r\n\r\n// ─── Scheduled Events ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild scheduled event entity metadata. */\r\nexport interface APIGuildScheduledEventEntityMetadata {\r\n location?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event object. */\r\nexport interface APIGuildScheduledEvent {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id?: Snowflake | null;\r\n creator_id?: Snowflake | null;\r\n name: string;\r\n description?: string | null;\r\n scheduled_start_time: string;\r\n scheduled_end_time?: string | null;\r\n privacy_level: number;\r\n status: number;\r\n entity_type: number;\r\n entity_id?: Snowflake | null;\r\n entity_metadata?: APIGuildScheduledEventEntityMetadata | null;\r\n creator?: UserData;\r\n user_count?: number;\r\n image?: string | null;\r\n}\r\n\r\n// ─── Stage Instances ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord stage instance object. */\r\nexport interface APIStageInstance {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n topic: string;\r\n privacy_level: number;\r\n discoverable_disabled?: boolean;\r\n guild_scheduled_event_id?: Snowflake | null;\r\n}\r\n\r\n// ─── Application Commands ─────────────────────────────────────────────────────\r\n\r\n/** Discord application command option types. */\r\nexport const ApplicationCommandOptionType = {\r\n SubCommand: 1,\r\n SubCommandGroup: 2,\r\n String: 3,\r\n Integer: 4,\r\n Boolean: 5,\r\n User: 6,\r\n Channel: 7,\r\n Role: 8,\r\n Mentionable: 9,\r\n Number: 10,\r\n Attachment: 11,\r\n} as const;\r\n\r\n/** Discord application command types. */\r\nexport const ApplicationCommandType = {\r\n ChatInput: 1,\r\n User: 2,\r\n Message: 3,\r\n} as const;\r\n\r\n/** Raw Discord application command option. */\r\nexport interface APIApplicationCommandOption {\r\n type: number;\r\n name: string;\r\n description: string;\r\n required?: boolean;\r\n choices?: Array<{ name: string; value: string | number }>;\r\n options?: APIApplicationCommandOption[];\r\n min_value?: number;\r\n max_value?: number;\r\n min_length?: number;\r\n max_length?: number;\r\n autocomplete?: boolean;\r\n channel_types?: number[];\r\n}\r\n\r\n/** Raw Discord application command object (from API). */\r\nexport interface APIApplicationCommand {\r\n id: Snowflake;\r\n type?: number;\r\n application_id: Snowflake;\r\n guild_id?: Snowflake;\r\n name: string;\r\n description: string;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n version: Snowflake;\r\n}\r\n\r\n/** Data for creating/overwriting an application command. */\r\nexport interface ApplicationCommandData {\r\n name: string;\r\n description: string;\r\n type?: number;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n}\r\n\r\n// ─── Gateway Events ───────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord READY event payload. */\r\nexport interface APIReadyEvent {\r\n v: number;\r\n user: UserData;\r\n guilds: Array<{ id: Snowflake; unavailable?: boolean }>;\r\n session_id: string;\r\n resume_gateway_url: string;\r\n shard?: [number, number];\r\n application?: { id: Snowflake; flags: number; [key: string]: unknown };\r\n}\r\n\r\n/** Raw Discord reaction event payload. */\r\nexport interface APIMessageReactionEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n member?: APIGuildMember;\r\n emoji: APIPartialEmoji;\r\n message_author_id?: Snowflake;\r\n burst?: boolean;\r\n type?: number;\r\n}\r\n\r\n/** Raw Discord reaction remove emoji event payload. */\r\nexport interface APIMessageReactionRemoveEmojiEvent {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n message_id: Snowflake;\r\n emoji: APIPartialEmoji;\r\n}\r\n\r\n/** Raw Discord message deletion event payload. */\r\nexport interface APIMessageDeleteEvent {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord bulk message deletion event payload. */\r\nexport interface APIMessageDeleteBulkEvent {\r\n ids: Snowflake[];\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord channel deletion event payload. */\r\nexport interface APIChannelDeleteEvent extends APIChannel {}\r\n\r\n/** Raw Discord guild deletion event payload. */\r\nexport interface APIGuildDeleteEvent {\r\n id: Snowflake;\r\n unavailable?: boolean;\r\n}\r\n\r\n/** Raw Discord thread lifecycle payload. */\r\nexport interface APIThreadEvent extends APIChannel {\r\n guild_id: Snowflake;\r\n member?: APIThreadMember;\r\n}\r\n\r\n/** Raw Discord thread list sync event. */\r\nexport interface APIThreadListSync {\r\n guild_id: Snowflake;\r\n channel_ids?: Snowflake[];\r\n threads: APIChannel[];\r\n members: APIThreadMember[];\r\n}\r\n\r\n/** Raw Discord thread members update event. */\r\nexport interface APIThreadMembersUpdate {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n member_count: number;\r\n added_members?: APIThreadMember[];\r\n removed_member_ids?: Snowflake[];\r\n}\r\n\r\n/** Raw Discord guild role event payload. */\r\nexport interface APIGuildRoleEvent {\r\n guild_id: Snowflake;\r\n role: APIRole;\r\n}\r\n\r\n/** Raw Discord guild role delete event payload. */\r\nexport interface APIGuildRoleDeleteEvent {\r\n guild_id: Snowflake;\r\n role_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord guild ban event payload. */\r\nexport interface APIGuildBanEvent {\r\n guild_id: Snowflake;\r\n user: UserData;\r\n}\r\n\r\n/** Raw Discord guild emojis update event payload. */\r\nexport interface APIGuildEmojisUpdateEvent {\r\n guild_id: Snowflake;\r\n emojis: APIEmoji[];\r\n}\r\n\r\n/** Raw Discord guild stickers update event payload. */\r\nexport interface APIGuildStickersUpdateEvent {\r\n guild_id: Snowflake;\r\n stickers: APISticker[];\r\n}\r\n\r\n/** Raw Discord channel pins update event. */\r\nexport interface APIChannelPinsUpdate {\r\n guild_id?: Snowflake;\r\n channel_id: Snowflake;\r\n last_pin_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord guild members chunk event. */\r\nexport interface APIGuildMembersChunk {\r\n guild_id: Snowflake;\r\n members: APIGuildMember[];\r\n chunk_index: number;\r\n chunk_count: number;\r\n not_found?: Snowflake[];\r\n presences?: APIPresenceUpdate[];\r\n nonce?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event user add/remove event. */\r\nexport interface APIGuildScheduledEventUserEvent {\r\n guild_scheduled_event_id: Snowflake;\r\n user_id: Snowflake;\r\n guild_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord message poll vote add/remove event. */\r\nexport interface APIMessagePollVoteEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n answer_id: number;\r\n}\r\n",
|
|
6
|
+
"import {\r\n resolveGatewayIntents,\r\n type GatewayPayload,\r\n type GatewayProperties,\r\n type GatewayPresence,\r\n type GatewayIntentResolvable,\r\n} from \"@lunibee/types\";\r\n\r\n/** Discord Gateway opcodes. */\r\nexport const GatewayOpcodes = {\r\n Dispatch: 0,\r\n Heartbeat: 1,\r\n Identify: 2,\r\n PresenceUpdate: 3,\r\n VoiceStateUpdate: 4,\r\n Resume: 6,\r\n Reconnect: 7,\r\n RequestGuildMembers: 8,\r\n InvalidSession: 9,\r\n Hello: 10,\r\n HeartbeatAck: 11,\r\n} as const;\r\n/**\r\n * Discord Gateway close codes.\r\n *\r\n * Discord.js-familiar names and numeric values, matching the Discord Gateway\r\n * protocol. Exposed so consumers can branch on named codes instead of magic\r\n * numbers; {@link Gateway} uses them internally to decide resume/identify/stop.\r\n */\r\nexport const GatewayCloseCodes = {\r\n UnknownError: 4000,\r\n UnknownOpcode: 4001,\r\n DecodeError: 4002,\r\n NotAuthenticated: 4003,\r\n AuthenticationFailed: 4004,\r\n AlreadyAuthenticated: 4005,\r\n InvalidSeq: 4007,\r\n RateLimited: 4008,\r\n SessionTimedOut: 4009,\r\n InvalidShard: 4010,\r\n ShardingRequired: 4011,\r\n InvalidAPIVersion: 4012,\r\n InvalidIntents: 4013,\r\n DisallowedIntents: 4014,\r\n} as const;\r\n/** Gateway connection lifecycle states. */\r\nexport enum GatewayState {\r\n /** Initial connection state. */ Connect = \"CONNECT\",\r\n /** Gateway HELLO received state. */ Hello = \"HELLO\",\r\n /** IDENTIFY operation in progress. */ Identify = \"IDENTIFY\",\r\n /** RESUME operation in progress. */ Resume = \"RESUME\",\r\n /** Gateway READY state. */ Ready = \"READY\",\r\n /** Gateway dispatch processing state. */ Dispatch = \"DISPATCH\",\r\n /** Heartbeat processing state. */ Heartbeat = \"HEARTBEAT\",\r\n /** Reconnect in progress. */ Reconnect = \"RECONNECT\",\r\n /** Gateway is permanently closed. */ Closed = \"CLOSED\",\r\n}\r\n/**\r\n * Discord.js-familiar alias for {@link GatewayState}.\r\n *\r\n * Discord.js exposes connection status via a `Status` enum. Lunibee's canonical\r\n * name is {@link GatewayState}; this is an additive alias so `discord.js` users\r\n * find the expected name. Note the *values* remain Lunibee's string states\r\n * (e.g. `\"READY\"`), not Discord.js's numeric `Status` members — an intentional\r\n * divergence documented in the compatibility matrix.\r\n */\r\nexport { GatewayState as Status };\r\n/** Gateway protocol error. */\r\nexport class GatewayError extends Error {\r\n /** Gateway close/error code. */ public readonly code?: number;\r\n /** Creates a Gateway error. @param message Error message. @param code Optional Gateway code. @param options Optional error metadata. */ public constructor(\r\n message: string,\r\n code?: number,\r\n options?: ErrorOptions,\r\n ) {\r\n super(message, options);\r\n this.name = \"GatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n /** Authentication token. */ token: string;\r\n /** Gateway intent bitfield or resolvable. */ intents: GatewayIntentResolvable;\r\n /** Shard identifier. */ shardId?: number;\r\n /** Total shard count. */ shardCount?: number;\r\n /** Whether automatic reconnect is enabled. */ reconnect?: boolean;\r\n /** Maximum reconnect attempts. */ maxReconnectAttempts?: number;\r\n /** Initial reconnect delay. */ reconnectBaseDelay?: number;\r\n /** Maximum reconnect delay. */ reconnectMaxDelay?: number;\r\n /** Heartbeat acknowledgement timeout. */ heartbeatAckTimeout?: number;\r\n /** Zombie connection timeout. */ zombieTimeout?: number;\r\n /** Identification properties. */ properties?: GatewayProperties;\r\n /** Presence data. */ presence?: GatewayPresence;\r\n /**\r\n * Whether to enable zlib-stream transport compression.\r\n * Uses Bun's native `DecompressionStream` — no external dependencies.\r\n * When enabled, appends `&compress=zlib-stream` to the Gateway URL.\r\n */\r\n compress?: boolean;\r\n}\r\n/** Gateway event listener. */\r\ntype GatewayListener = (data: unknown) => unknown;\r\n/** Manages a Discord Gateway connection. */\r\nexport class Gateway {\r\n /** Current Gateway lifecycle state. */\r\n public state: GatewayState = GatewayState.Connect;\r\n #options: Required<\r\n Omit<GatewayOptions, \"properties\" | \"presence\" | \"compress\">\r\n > & {\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n compress?: boolean;\r\n };\r\n #ws?: WebSocket;\r\n #sequence: number | null = null;\r\n #sessionId?: string;\r\n #resumeURL?: string;\r\n #heartbeatTimer?: ReturnType<typeof setInterval>;\r\n #initialHeartbeat?: ReturnType<typeof setTimeout>;\r\n #heartbeatAckTimer?: ReturnType<typeof setTimeout>;\r\n #heartbeatACK = true;\r\n #heartbeatInterval = 0;\r\n #heartbeatSentAt = 0;\r\n #lastMessageAt = 0;\r\n #zombieTimer?: ReturnType<typeof setInterval>;\r\n #zombieReported = false;\r\n #closed = false;\r\n #attempt = 0;\r\n #reconnectTimer?: ReturnType<typeof setTimeout>;\r\n #connectPromise?: Promise<void>;\r\n #resolveConnect?: () => void;\r\n #rejectConnect?: (error: GatewayError) => void;\r\n readonly #listeners = new Map<string, Set<GatewayListener>>();\r\n readonly #sendTimestamps: number[] = [];\r\n public ping: number = -1;\r\n /** Pending zlib-stream decompressor, initialised lazily when compress is enabled. */\r\n #decompressor?: {\r\n writer: WritableStreamDefaultWriter<BufferSource>;\r\n reader: ReadableStreamDefaultReader<Uint8Array>;\r\n };\r\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\r\n public constructor(options: GatewayOptions) {\r\n if (!options.token?.trim())\r\n throw new TypeError(\"A Gateway token is required.\");\r\n const resolvedIntents = resolveGatewayIntents(options.intents);\r\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\r\n throw new TypeError(\r\n \"Gateway intents must be a non-negative integer.\",\r\n );\r\n this.#options = {\r\n shardId: 0,\r\n shardCount: 1,\r\n reconnect: true,\r\n maxReconnectAttempts: Infinity,\r\n reconnectBaseDelay: 1000,\r\n reconnectMaxDelay: 30000,\r\n heartbeatAckTimeout: 10000,\r\n zombieTimeout: 30000,\r\n properties: {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n },\r\n presence: {\r\n status: \"online\",\r\n activities: [],\r\n afk: false,\r\n since: null,\r\n },\r\n ...options,\r\n };\r\n if (\r\n this.#options.shardId < 0 ||\r\n this.#options.shardId >= this.#options.shardCount\r\n )\r\n throw new RangeError(\r\n \"Gateway shardId must be within the configured shard count.\",\r\n );\r\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\r\n throw new RangeError(\r\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\r\n );\r\n }\r\n\r\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens. @throws {GatewayError} If permanently closed or unable to connect. */\r\n public connect(\r\n url = \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n ): Promise<void> {\r\n if (this.state === GatewayState.Closed)\r\n throw new GatewayError(\"Gateway has been permanently closed.\");\r\n if (this.#connectPromise) return this.#connectPromise;\r\n this.#closed = false;\r\n this.#setState(GatewayState.Connect);\r\n // Append compression parameter if enabled\r\n const connectURL = this.#options.compress\r\n ? url.includes(\"compress=\")\r\n ? url\r\n : `${url}&compress=zlib-stream`\r\n : url;\r\n if (this.#options.compress) this.#initDecompressor();\r\n this.#connectPromise = new Promise<void>((resolve, reject) => {\r\n this.#resolveConnect = resolve;\r\n this.#rejectConnect = reject;\r\n this.#open(connectURL);\r\n });\r\n return this.#connectPromise;\r\n }\r\n /** Permanently closes the Gateway connection. */\r\n public close(): void {\r\n this.#closed = true;\r\n this.#clearTimers();\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway connection closed before socket open.\"),\r\n );\r\n const ws = this.#ws;\r\n this.#ws = undefined;\r\n try {\r\n ws?.close(1000, \"Client closed connection\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n this.#setState(GatewayState.Closed);\r\n }\r\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\r\n public emit(event: string, data?: unknown): void {\r\n this.#emit(event, data);\r\n }\r\n public on(event: string, listener: GatewayListener): this {\r\n if (!event || typeof listener !== \"function\")\r\n throw new TypeError(\"Gateway event and listener are required.\");\r\n let listeners = this.#listeners.get(event);\r\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\r\n listeners.add(listener);\r\n return this;\r\n }\r\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n public off(event: string, listener: GatewayListener): this {\r\n this.#listeners.get(event)?.delete(listener);\r\n return this;\r\n }\r\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\r\n public send(payload: GatewayPayload): boolean {\r\n if (!payload || !Number.isInteger(payload.op))\r\n throw new TypeError(\r\n \"Gateway payload must contain an integer opcode.\",\r\n );\r\n return this.#dispatch(payload, false);\r\n }\r\n /**\r\n * Writes a payload to the socket. Heartbeats pass `privileged` so they are\r\n * never starved by application traffic: the 115 budget sits below\r\n * Discord's real 120/60s limit precisely to leave room for them, so\r\n * spending that headroom on heartbeats is what it is reserved for. They\r\n * are still recorded, keeping the true total under Discord's limit.\r\n */\r\n #dispatch(payload: GatewayPayload, privileged: boolean): boolean {\r\n if (this.#ws?.readyState !== WebSocket.OPEN) return false;\r\n const now = Date.now();\r\n while (\r\n this.#sendTimestamps.length &&\r\n now - this.#sendTimestamps[0]! >= 60000\r\n )\r\n this.#sendTimestamps.shift();\r\n if (!privileged && this.#sendTimestamps.length >= 115) {\r\n this.#emitError(\r\n new GatewayError(\"Gateway send rate budget exhausted.\"),\r\n );\r\n return false;\r\n }\r\n try {\r\n this.#ws.send(JSON.stringify(payload));\r\n this.#sendTimestamps.push(now);\r\n return true;\r\n } catch (error) {\r\n this.#emitError(error);\r\n return false;\r\n }\r\n }\r\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\r\n public setPresence(data: GatewayPresence): boolean {\r\n this.#options.presence = { ...this.#options.presence, ...data };\r\n const payload = {\r\n since: this.#options.presence.since ?? null,\r\n activities: this.#options.presence.activities ?? [],\r\n status: this.#options.presence.status ?? \"online\",\r\n afk: Boolean(this.#options.presence.afk),\r\n };\r\n return this.send({\r\n op: GatewayOpcodes.PresenceUpdate,\r\n d: payload,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\r\n public setVoiceState(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.VoiceStateUpdate,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\r\n public requestGuildMembers(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.RequestGuildMembers,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #open(url: string): void {\r\n if (this.#closed) return;\r\n let ws: WebSocket;\r\n try {\r\n ws = new WebSocket(url);\r\n } catch (error) {\r\n const failure = this.#normalizeError(error);\r\n this.#emitError(failure);\r\n if (this.#options.reconnect) {\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n } else this.#settleConnect(failure);\r\n return;\r\n }\r\n this.#ws = ws;\r\n ws.addEventListener(\"open\", () => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n this.#startZombieDetection();\r\n this.#emit(\"open\", undefined);\r\n this.#settleConnect();\r\n });\r\n ws.addEventListener(\"message\", (event) => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n if (this.#options.compress && event.data instanceof ArrayBuffer) {\r\n this.#decompress(new Uint8Array(event.data))\r\n .then((text) => this.#message(ws, text))\r\n .catch((err) => this.#emitError(err));\r\n } else {\r\n this.#message(ws, String(event.data));\r\n }\r\n });\r\n ws.addEventListener(\"close\", (event) => this.#close(ws, event.code));\r\n ws.addEventListener(\"error\", () =>\r\n this.#emitError(new GatewayError(\"Gateway WebSocket error\")),\r\n );\r\n }\r\n #startZombieDetection(): void {\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n // Poll granularity only needs to be a fraction of the staleness\r\n // deadline (heartbeatInterval + heartbeatAckTimeout, ~51s in\r\n // practice). A 1s ceiling keeps detection latency negligible while\r\n // costing one wakeup per second per shard instead of four.\r\n const interval = Math.max(\r\n 1,\r\n Math.min(\r\n 1000,\r\n this.#options.heartbeatAckTimeout,\r\n this.#options.zombieTimeout / 2,\r\n ),\r\n );\r\n this.#zombieTimer = setInterval(() => {\r\n if (\r\n this.#closed ||\r\n this.#ws?.readyState !== WebSocket.OPEN ||\r\n this.#lastMessageAt === 0\r\n )\r\n return;\r\n const silentFor = Date.now() - this.#lastMessageAt;\r\n const deadline = Math.max(\r\n this.#options.zombieTimeout,\r\n this.#heartbeatInterval + this.#options.heartbeatAckTimeout,\r\n );\r\n if (silentFor < deadline || this.#zombieReported) return;\r\n this.#zombieReported = true;\r\n const error = new GatewayError(\r\n `Gateway connection appears stale after ${silentFor}ms without traffic.`,\r\n );\r\n this.#emit(\"zombie\", { silentFor, timeout: deadline });\r\n this.#emitError(error);\r\n try {\r\n this.#ws.close(1001, \"Zombie Gateway connection\");\r\n } catch (closeError) {\r\n this.#emitError(closeError);\r\n }\r\n }, interval);\r\n }\r\n #message(ws: WebSocket, raw: string): void {\r\n let payload: GatewayPayload;\r\n try {\r\n payload = JSON.parse(raw) as GatewayPayload;\r\n } catch (error) {\r\n this.#emitError(\r\n new GatewayError(\"Gateway returned invalid JSON\", undefined, {\r\n cause: error,\r\n }),\r\n );\r\n ws.close(1002, \"Invalid JSON\");\r\n return;\r\n }\r\n if (!payload || typeof payload.op !== \"number\") {\r\n this.#emitError(\r\n new GatewayError(\"Gateway returned an invalid payload\"),\r\n );\r\n ws.close(1002, \"Invalid payload\");\r\n return;\r\n }\r\n if (typeof payload.s === \"number\") this.#sequence = payload.s;\r\n switch (payload.op) {\r\n case GatewayOpcodes.Dispatch:\r\n this.#setState(GatewayState.Dispatch);\r\n this.#handleDispatch(payload.t, payload.d);\r\n break;\r\n case GatewayOpcodes.Hello:\r\n this.#handleHello(payload.d);\r\n break;\r\n case GatewayOpcodes.Heartbeat:\r\n this.#setState(GatewayState.Heartbeat);\r\n this.#sendHeartbeat();\r\n break;\r\n case GatewayOpcodes.HeartbeatAck:\r\n this.#heartbeatACK = true;\r\n this.ping = Date.now() - this.#heartbeatSentAt;\r\n this.#clearHeartbeatAckTimer();\r\n this.#emit(\"heartbeatAck\", payload.d);\r\n break;\r\n case GatewayOpcodes.Reconnect:\r\n ws.close(1001, \"Server requested reconnect\");\r\n break;\r\n case GatewayOpcodes.InvalidSession: {\r\n // Op 9 `d` is a boolean: true = the session is resumable, so we\r\n // keep session state and RESUME on reconnect; false = the\r\n // session is dead and we must re-IDENTIFY from scratch. Matches\r\n // Discord/Discord.js semantics rather than always re-identifying.\r\n const resumable = payload.d === true;\r\n if (!resumable) {\r\n this.#sessionId = undefined;\r\n this.#sequence = null;\r\n this.#resumeURL = undefined;\r\n }\r\n this.#emit(\"invalidSession\", resumable);\r\n ws.close(\r\n resumable ? GatewayCloseCodes.UnknownError : 1000,\r\n resumable\r\n ? \"Invalid session (resumable)\"\r\n : \"Invalid session\",\r\n );\r\n break;\r\n }\r\n }\r\n }\r\n #handleDispatch(event: string | null, data: unknown): void {\r\n if (event === \"READY\") {\r\n const ready = data as {\r\n session_id?: unknown;\r\n resume_gateway_url?: unknown;\r\n };\r\n if (\r\n typeof ready?.session_id !== \"string\" ||\r\n typeof ready?.resume_gateway_url !== \"string\"\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway READY payload is missing session information\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#sessionId = ready.session_id;\r\n this.#resumeURL = ready.resume_gateway_url;\r\n this.#attempt = 0;\r\n this.#setState(GatewayState.Ready);\r\n this.#emit(\"ready\", data);\r\n } else if (event === \"RESUMED\") {\r\n // A successful RESUME ends the reconnect cycle just like READY:\r\n // reset the backoff counter so a later disconnect starts from the\r\n // base delay, mark the connection READY, and surface a Discord.js-\r\n // familiar `resumed` event.\r\n this.#attempt = 0;\r\n this.#setState(GatewayState.Ready);\r\n this.#emit(\"resumed\", data);\r\n }\r\n this.#emit(\"RAW\", { event: event ?? \"UNKNOWN\", data });\r\n if (this.#closed) return;\r\n this.#emit(event ?? \"dispatch\", data);\r\n }\r\n #handleHello(data: unknown): void {\r\n const interval = (data as { heartbeat_interval?: unknown })\r\n ?.heartbeat_interval;\r\n if (\r\n typeof interval !== \"number\" ||\r\n !Number.isFinite(interval) ||\r\n interval <= 0\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway HELLO payload contains an invalid heartbeat interval\",\r\n ),\r\n );\r\n this.#ws?.close(1002, \"Invalid heartbeat interval\");\r\n return;\r\n }\r\n this.#heartbeatInterval = interval;\r\n this.#setState(\r\n this.#sessionId && this.#sequence !== null && this.#resumeURL\r\n ? GatewayState.Resume\r\n : GatewayState.Hello,\r\n );\r\n this.#startHeartbeat(interval);\r\n this.#identifyOrResume();\r\n }\r\n #identifyOrResume(): void {\r\n if (this.#sessionId && this.#sequence !== null && this.#resumeURL) {\r\n this.#setState(GatewayState.Resume);\r\n this.send({\r\n op: GatewayOpcodes.Resume,\r\n d: {\r\n token: this.#options.token,\r\n session_id: this.#sessionId,\r\n seq: this.#sequence,\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n return;\r\n }\r\n this.#setState(GatewayState.Identify);\r\n const props = this.#options.properties ?? {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n };\r\n const os = props.os ?? \"Android\";\r\n const browser = props.browser ?? \"Discord Android\";\r\n const device = props.device ?? \"Discord Android\";\r\n this.send({\r\n op: GatewayOpcodes.Identify,\r\n d: {\r\n token: this.#options.token,\r\n intents: this.#options.intents,\r\n properties: {\r\n os,\r\n browser,\r\n device,\r\n $os: os,\r\n $browser: browser,\r\n $device: device,\r\n ...props,\r\n },\r\n presence: {\r\n since: this.#options.presence?.since ?? null,\r\n activities: this.#options.presence?.activities ?? [],\r\n status: this.#options.presence?.status ?? \"online\",\r\n afk: Boolean(this.#options.presence?.afk),\r\n },\r\n shard: [this.#options.shardId, this.#options.shardCount],\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #startHeartbeat(interval: number): void {\r\n this.#clearHeartbeatTimers();\r\n this.#heartbeatACK = true;\r\n this.#initialHeartbeat = setTimeout(\r\n () => this.#sendHeartbeat(),\r\n Math.random() * interval,\r\n );\r\n this.#heartbeatTimer = setInterval(\r\n () => this.#sendHeartbeat(),\r\n interval,\r\n );\r\n }\r\n #sendHeartbeat(): void {\r\n this.#heartbeatACK = false;\r\n this.#heartbeatSentAt = Date.now();\r\n if (\r\n !this.#dispatch(\r\n {\r\n op: GatewayOpcodes.Heartbeat,\r\n d: this.#sequence,\r\n s: null,\r\n t: null,\r\n },\r\n true,\r\n )\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Unable to send Gateway heartbeat because the WebSocket is not open.\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatAckTimer = setTimeout(() => {\r\n if (!this.#heartbeatACK) {\r\n const elapsed = Date.now() - this.#heartbeatSentAt;\r\n this.#emitError(\r\n new GatewayError(\r\n `Gateway heartbeat acknowledgement timed out after ${elapsed}ms.`,\r\n ),\r\n );\r\n try {\r\n this.#ws?.close(1001, \"Heartbeat timeout\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n }\r\n }, this.#options.heartbeatAckTimeout);\r\n }\r\n #close(ws: WebSocket, code: number): void {\r\n if (this.#ws !== ws) return;\r\n this.#ws = undefined;\r\n this.#clearTimers();\r\n const action = this.#closeAction(code);\r\n this.#emit(\"close\", { code, action });\r\n if (this.#closed || !this.#options.reconnect || action === \"stop\") {\r\n this.#setState(\r\n this.#closed ? GatewayState.Closed : GatewayState.Connect,\r\n );\r\n if (this.#connectPromise)\r\n this.#settleConnect(\r\n new GatewayError(\r\n `Gateway closed before READY (code ${code}).`,\r\n code,\r\n ),\r\n );\r\n return;\r\n }\r\n if (action === \"identify\") {\r\n // Fresh IDENTIFY: drop the resumable session AND its resume URL so\r\n // the reconnect targets the main Gateway, not a stale resume host.\r\n this.#sequence = null;\r\n this.#sessionId = undefined;\r\n this.#resumeURL = undefined;\r\n }\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n }\r\n #closeAction(code: number): \"resume\" | \"identify\" | \"stop\" {\r\n // Fatal codes: the connection cannot recover by reconnecting.\r\n const fatal: number[] = [\r\n GatewayCloseCodes.AuthenticationFailed,\r\n GatewayCloseCodes.InvalidShard,\r\n GatewayCloseCodes.ShardingRequired,\r\n GatewayCloseCodes.InvalidAPIVersion,\r\n GatewayCloseCodes.InvalidIntents,\r\n GatewayCloseCodes.DisallowedIntents,\r\n ];\r\n if (fatal.includes(code)) return \"stop\";\r\n // Sequence/session invalidated: reconnect but IDENTIFY afresh.\r\n if (\r\n code === GatewayCloseCodes.InvalidSeq ||\r\n code === GatewayCloseCodes.SessionTimedOut\r\n )\r\n return \"identify\";\r\n // Otherwise resume when we still hold a live session + sequence.\r\n return this.#sessionId && this.#sequence !== null\r\n ? \"resume\"\r\n : \"identify\";\r\n }\r\n #scheduleReconnect(): void {\r\n if (this.#closed || !this.#options.reconnect || this.#reconnectTimer)\r\n return;\r\n if (this.#attempt >= this.#options.maxReconnectAttempts) {\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\r\n );\r\n this.#setState(GatewayState.Connect);\r\n return;\r\n }\r\n const delay = Math.min(\r\n this.#options.reconnectMaxDelay,\r\n this.#options.reconnectBaseDelay * 2 ** this.#attempt++,\r\n );\r\n // Jitter must scale with the delay: a fixed ceiling would reconnect\r\n // every shard inside the same narrow window after a gateway-wide\r\n // restart, which is exactly when decorrelation matters.\r\n const jitter = Math.random() * Math.max(1, delay * 0.25);\r\n this.#reconnectTimer = setTimeout(() => {\r\n this.#reconnectTimer = undefined;\r\n this.#open(\r\n this.#resumeURL ??\r\n \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n );\r\n }, delay + jitter);\r\n }\r\n #settleConnect(error?: GatewayError): void {\r\n const resolve = this.#resolveConnect;\r\n const reject = this.#rejectConnect;\r\n this.#resolveConnect = undefined;\r\n this.#rejectConnect = undefined;\r\n this.#connectPromise = undefined;\r\n if (error) reject?.(error);\r\n else resolve?.();\r\n }\r\n #clearHeartbeatAckTimer(): void {\r\n if (this.#heartbeatAckTimer) clearTimeout(this.#heartbeatAckTimer);\r\n this.#heartbeatAckTimer = undefined;\r\n }\r\n #clearHeartbeatTimers(): void {\r\n if (this.#heartbeatTimer) clearInterval(this.#heartbeatTimer);\r\n if (this.#initialHeartbeat) clearTimeout(this.#initialHeartbeat);\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatTimer = undefined;\r\n this.#initialHeartbeat = undefined;\r\n }\r\n #clearTimers(): void {\r\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n this.#reconnectTimer = undefined;\r\n this.#zombieTimer = undefined;\r\n this.#clearHeartbeatTimers();\r\n }\r\n #normalizeError(error: unknown): GatewayError {\r\n return error instanceof GatewayError\r\n ? error\r\n : new GatewayError(\"Gateway connection failed.\", undefined, {\r\n cause: error,\r\n });\r\n }\r\n #emitError(error: unknown): void {\r\n this.#emit(\r\n \"error\",\r\n error instanceof Error ? error : new GatewayError(String(error)),\r\n );\r\n }\r\n #emit(event: string, data: unknown): void {\r\n for (const listener of this.#listeners.get(event) ?? []) {\r\n try {\r\n const result = listener(data);\r\n if (\r\n result &&\r\n typeof (result as PromiseLike<unknown>).then === \"function\"\r\n )\r\n void Promise.resolve(result).catch((error) => {\r\n if (event !== \"error\") this.#emitError(error);\r\n });\r\n } catch (error) {\r\n if (event !== \"error\") this.#emitError(error);\r\n }\r\n }\r\n }\r\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\r\n #setState(next: GatewayState): void {\r\n if (this.state === next) return;\r\n const previous = this.state;\r\n this.state = next;\r\n this.#emit(\"stateChange\", { previous, next });\r\n }\r\n /** Initialises a fresh zlib-stream decompressor using Bun's native DecompressionStream. */\r\n #initDecompressor(): void {\r\n const ds = new DecompressionStream(\"deflate-raw\");\r\n this.#decompressor = {\r\n writer: ds.writable.getWriter() as WritableStreamDefaultWriter<BufferSource>,\r\n reader: ds.readable.getReader() as ReadableStreamDefaultReader<Uint8Array>,\r\n };\r\n }\r\n /** Decompresses a zlib-stream chunk and returns the decoded JSON string. */\r\n async #decompress(chunk: Uint8Array): Promise<string> {\r\n if (!this.#decompressor) this.#initDecompressor();\r\n const { writer, reader } = this.#decompressor!;\r\n await writer.write(chunk as unknown as BufferSource);\r\n const parts: Uint8Array[] = [];\r\n // Drain all available output frames\r\n while (true) {\r\n const { done, value } = await Promise.race([\r\n reader.read(),\r\n // 50 ms fence so we don't hang on a partial payload\r\n new Promise<{ done: true; value: undefined }>((resolve) =>\r\n setTimeout(\r\n () => resolve({ done: true, value: undefined }),\r\n 50,\r\n ),\r\n ),\r\n ]);\r\n if (done || !value) break;\r\n parts.push(value);\r\n }\r\n const total = parts.reduce((n, p) => n + p.length, 0);\r\n const merged = new Uint8Array(total);\r\n let offset = 0;\r\n for (const p of parts) {\r\n merged.set(p, offset);\r\n offset += p.length;\r\n }\r\n return new TextDecoder().decode(merged);\r\n }\r\n}\r\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "267C8623751A500664756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|