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/index.js
CHANGED
|
@@ -107,58 +107,57 @@ var Permissions = {
|
|
|
107
107
|
SendPolls: 1n << 49n,
|
|
108
108
|
UseExternalApps: 1n << 50n
|
|
109
109
|
};
|
|
110
|
-
var PermissionFlagsBits
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
})(PermissionFlagsBits ||= {});
|
|
110
|
+
var PermissionFlagsBits = {
|
|
111
|
+
CreateInstantInvite: 1n << 0n,
|
|
112
|
+
KickMembers: 1n << 1n,
|
|
113
|
+
BanMembers: 1n << 2n,
|
|
114
|
+
Administrator: 1n << 3n,
|
|
115
|
+
ManageChannels: 1n << 4n,
|
|
116
|
+
ManageGuild: 1n << 5n,
|
|
117
|
+
AddReactions: 1n << 6n,
|
|
118
|
+
ViewAuditLog: 1n << 7n,
|
|
119
|
+
PrioritySpeaker: 1n << 8n,
|
|
120
|
+
Stream: 1n << 9n,
|
|
121
|
+
ViewChannel: 1n << 10n,
|
|
122
|
+
SendMessages: 1n << 11n,
|
|
123
|
+
SendTTSMessages: 1n << 12n,
|
|
124
|
+
ManageMessages: 1n << 13n,
|
|
125
|
+
EmbedLinks: 1n << 14n,
|
|
126
|
+
AttachFiles: 1n << 15n,
|
|
127
|
+
ReadMessageHistory: 1n << 16n,
|
|
128
|
+
MentionEveryone: 1n << 17n,
|
|
129
|
+
UseExternalEmojis: 1n << 18n,
|
|
130
|
+
ViewGuildInsights: 1n << 19n,
|
|
131
|
+
Connect: 1n << 20n,
|
|
132
|
+
Speak: 1n << 21n,
|
|
133
|
+
MuteMembers: 1n << 22n,
|
|
134
|
+
DeafenMembers: 1n << 23n,
|
|
135
|
+
MoveMembers: 1n << 24n,
|
|
136
|
+
UseVAD: 1n << 25n,
|
|
137
|
+
ChangeNickname: 1n << 26n,
|
|
138
|
+
ManageNicknames: 1n << 27n,
|
|
139
|
+
ManageRoles: 1n << 28n,
|
|
140
|
+
ManageWebhooks: 1n << 29n,
|
|
141
|
+
ManageGuildExpressions: 1n << 30n,
|
|
142
|
+
UseApplicationCommands: 1n << 31n,
|
|
143
|
+
RequestToSpeak: 1n << 32n,
|
|
144
|
+
ManageEvents: 1n << 33n,
|
|
145
|
+
ManageThreads: 1n << 34n,
|
|
146
|
+
CreatePublicThreads: 1n << 35n,
|
|
147
|
+
CreatePrivateThreads: 1n << 36n,
|
|
148
|
+
UseExternalStickers: 1n << 37n,
|
|
149
|
+
SendMessagesInThreads: 1n << 38n,
|
|
150
|
+
UseEmbeddedActivities: 1n << 39n,
|
|
151
|
+
ModerateMembers: 1n << 40n,
|
|
152
|
+
ViewCreatorMonetizationAnalytics: 1n << 41n,
|
|
153
|
+
UseSoundboard: 1n << 42n,
|
|
154
|
+
CreateGuildExpressions: 1n << 43n,
|
|
155
|
+
CreateEvents: 1n << 44n,
|
|
156
|
+
UseExternalSounds: 1n << 45n,
|
|
157
|
+
SendVoiceMessages: 1n << 46n,
|
|
158
|
+
SendPolls: 1n << 49n,
|
|
159
|
+
UseExternalApps: 1n << 50n
|
|
160
|
+
};
|
|
162
161
|
|
|
163
162
|
class PermissionSet {
|
|
164
163
|
bitfield;
|
|
@@ -403,7 +402,7 @@ class PermissionSet {
|
|
|
403
402
|
if (permission in Permissions)
|
|
404
403
|
return Permissions[permission];
|
|
405
404
|
if (permission in PermissionFlagsBits)
|
|
406
|
-
return
|
|
405
|
+
return PermissionFlagsBits[permission];
|
|
407
406
|
const lower = permission.charAt(0).toLowerCase() + permission.slice(1);
|
|
408
407
|
if (lower in Permission)
|
|
409
408
|
return Permission[lower];
|
|
@@ -424,6 +423,8 @@ class PermissionsBitField extends PermissionSet {
|
|
|
424
423
|
var ClientEvent;
|
|
425
424
|
((ClientEvent2) => {
|
|
426
425
|
ClientEvent2["Ready"] = "ready";
|
|
426
|
+
ClientEvent2["Resumed"] = "resumed";
|
|
427
|
+
ClientEvent2["InvalidSession"] = "invalidSession";
|
|
427
428
|
ClientEvent2["Raw"] = "raw";
|
|
428
429
|
ClientEvent2["Error"] = "error";
|
|
429
430
|
ClientEvent2["Open"] = "open";
|
|
@@ -486,6 +487,76 @@ var ClientEvent;
|
|
|
486
487
|
ClientEvent2["TypingStart"] = "typingStart";
|
|
487
488
|
ClientEvent2["InteractionCreate"] = "interactionCreate";
|
|
488
489
|
})(ClientEvent ||= {});
|
|
490
|
+
// packages/core/src/collector.ts
|
|
491
|
+
import { EventEmitter } from "events";
|
|
492
|
+
|
|
493
|
+
class Collector extends EventEmitter {
|
|
494
|
+
collected = new Map;
|
|
495
|
+
options;
|
|
496
|
+
ended = false;
|
|
497
|
+
endReason;
|
|
498
|
+
totalProcessed = 0;
|
|
499
|
+
#timeoutTimer;
|
|
500
|
+
constructor(options = {}) {
|
|
501
|
+
super();
|
|
502
|
+
this.options = options;
|
|
503
|
+
if (options.time && options.time > 0) {
|
|
504
|
+
this.#timeoutTimer = setTimeout(() => {
|
|
505
|
+
this.stop("time");
|
|
506
|
+
}, options.time);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async handle(key, item) {
|
|
510
|
+
if (this.ended)
|
|
511
|
+
return false;
|
|
512
|
+
this.totalProcessed++;
|
|
513
|
+
const reachedProcessed = !!this.options.maxProcessed && this.totalProcessed >= this.options.maxProcessed;
|
|
514
|
+
let passed = true;
|
|
515
|
+
if (this.options.filter)
|
|
516
|
+
passed = !!await this.options.filter(item);
|
|
517
|
+
if (this.ended)
|
|
518
|
+
return false;
|
|
519
|
+
if (!passed) {
|
|
520
|
+
if (reachedProcessed)
|
|
521
|
+
this.stop("processedLimit");
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
this.collected.set(key, item);
|
|
525
|
+
this.emit("collect", item);
|
|
526
|
+
if (this.options.max && this.collected.size >= this.options.max) {
|
|
527
|
+
this.stop("limit");
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
if (reachedProcessed) {
|
|
531
|
+
this.stop("processedLimit");
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
stop(reason = "user") {
|
|
537
|
+
if (this.ended)
|
|
538
|
+
return;
|
|
539
|
+
this.ended = true;
|
|
540
|
+
this.endReason = reason;
|
|
541
|
+
if (this.#timeoutTimer)
|
|
542
|
+
clearTimeout(this.#timeoutTimer);
|
|
543
|
+
this.emit("end", this.collected, reason);
|
|
544
|
+
this.removeAllListeners();
|
|
545
|
+
}
|
|
546
|
+
next() {
|
|
547
|
+
return new Promise((resolve, reject) => {
|
|
548
|
+
if (this.ended) {
|
|
549
|
+
return reject(new Error(`Collector already ended: ${this.endReason ?? "unknown"}`));
|
|
550
|
+
}
|
|
551
|
+
this.once("collect", (item) => {
|
|
552
|
+
resolve(item);
|
|
553
|
+
});
|
|
554
|
+
this.once("end", (_collected, reason) => {
|
|
555
|
+
reject(new Error(`Collector ended before item was collected: ${reason}`));
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
489
560
|
// packages/types/src/index.ts
|
|
490
561
|
var GatewayIntentBits = {
|
|
491
562
|
Guilds: 1 << 0,
|
|
@@ -587,6 +658,14 @@ var ChannelType = {
|
|
|
587
658
|
GuildForum: 15,
|
|
588
659
|
GuildMedia: 16
|
|
589
660
|
};
|
|
661
|
+
var ButtonStyle = {
|
|
662
|
+
Primary: 1,
|
|
663
|
+
Secondary: 2,
|
|
664
|
+
Success: 3,
|
|
665
|
+
Danger: 4,
|
|
666
|
+
Link: 5,
|
|
667
|
+
Premium: 6
|
|
668
|
+
};
|
|
590
669
|
var InteractionResponseType = {
|
|
591
670
|
Pong: 1,
|
|
592
671
|
ChannelMessage: 4,
|
|
@@ -687,10 +766,14 @@ class Cache {
|
|
|
687
766
|
const expiresAt = this.#ttl > 0 ? Date.now() + this.#ttl : 0;
|
|
688
767
|
this.#entries.delete(key);
|
|
689
768
|
this.#entries.set(key, { value, expiresAt });
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
769
|
+
if (this.#maxSize !== Infinity) {
|
|
770
|
+
while (this.#entries.size > this.#maxSize) {
|
|
771
|
+
const oldest = this.#entries.firstKey();
|
|
772
|
+
if (oldest !== undefined)
|
|
773
|
+
this.#entries.delete(oldest);
|
|
774
|
+
else
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
694
777
|
}
|
|
695
778
|
return this;
|
|
696
779
|
}
|
|
@@ -874,6 +957,75 @@ class Collection extends Map {
|
|
|
874
957
|
}
|
|
875
958
|
return result;
|
|
876
959
|
}
|
|
960
|
+
partition(predicate) {
|
|
961
|
+
const pass = new Collection;
|
|
962
|
+
const fail = new Collection;
|
|
963
|
+
for (const [key, value] of this) {
|
|
964
|
+
if (predicate(value, key, this))
|
|
965
|
+
pass.set(key, value);
|
|
966
|
+
else
|
|
967
|
+
fail.set(key, value);
|
|
968
|
+
}
|
|
969
|
+
return [pass, fail];
|
|
970
|
+
}
|
|
971
|
+
tap(fn) {
|
|
972
|
+
fn(this);
|
|
973
|
+
return this;
|
|
974
|
+
}
|
|
975
|
+
flatMap(transform) {
|
|
976
|
+
const result = [];
|
|
977
|
+
for (const [key, value] of this) {
|
|
978
|
+
for (const item of transform(value, key, this))
|
|
979
|
+
result.push(item);
|
|
980
|
+
}
|
|
981
|
+
return result;
|
|
982
|
+
}
|
|
983
|
+
reduce(fn, initialValue) {
|
|
984
|
+
let acc = initialValue;
|
|
985
|
+
for (const [key, value] of this)
|
|
986
|
+
acc = fn(acc, value, key, this);
|
|
987
|
+
return acc;
|
|
988
|
+
}
|
|
989
|
+
sorted(comparator) {
|
|
990
|
+
const decorated = [...this.entries()].map((entry, index) => ({
|
|
991
|
+
entry,
|
|
992
|
+
index
|
|
993
|
+
}));
|
|
994
|
+
if (comparator)
|
|
995
|
+
decorated.sort((x, y) => {
|
|
996
|
+
const comparison = comparator(x.entry[1], y.entry[1], x.entry[0], y.entry[0]);
|
|
997
|
+
return comparison !== 0 ? comparison : x.index - y.index;
|
|
998
|
+
});
|
|
999
|
+
const result = new Collection;
|
|
1000
|
+
for (const { entry } of decorated)
|
|
1001
|
+
result.set(entry[0], entry[1]);
|
|
1002
|
+
return result;
|
|
1003
|
+
}
|
|
1004
|
+
random() {
|
|
1005
|
+
const arr = [...this.values()];
|
|
1006
|
+
if (!arr.length)
|
|
1007
|
+
return;
|
|
1008
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1009
|
+
}
|
|
1010
|
+
randomKey() {
|
|
1011
|
+
const arr = [...this.keys()];
|
|
1012
|
+
if (!arr.length)
|
|
1013
|
+
return;
|
|
1014
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1015
|
+
}
|
|
1016
|
+
at(index) {
|
|
1017
|
+
const arr = [...this.values()];
|
|
1018
|
+
const i = index < 0 ? arr.length + index : index;
|
|
1019
|
+
return arr[i];
|
|
1020
|
+
}
|
|
1021
|
+
keyAt(index) {
|
|
1022
|
+
const arr = [...this.keys()];
|
|
1023
|
+
const i = index < 0 ? arr.length + index : index;
|
|
1024
|
+
return arr[i];
|
|
1025
|
+
}
|
|
1026
|
+
toJSON() {
|
|
1027
|
+
return [...this.entries()];
|
|
1028
|
+
}
|
|
877
1029
|
}
|
|
878
1030
|
|
|
879
1031
|
// packages/managers/src/base.ts
|
|
@@ -945,7 +1097,7 @@ class ResourceManager extends Manager {
|
|
|
945
1097
|
// packages/rest/package.json
|
|
946
1098
|
var package_default = {
|
|
947
1099
|
name: "@lunibee/rest",
|
|
948
|
-
version: "0.1.
|
|
1100
|
+
version: "0.1.7",
|
|
949
1101
|
type: "module",
|
|
950
1102
|
module: "./src/index.ts",
|
|
951
1103
|
types: "./src/index.ts",
|
|
@@ -954,6 +1106,45 @@ var package_default = {
|
|
|
954
1106
|
}
|
|
955
1107
|
};
|
|
956
1108
|
|
|
1109
|
+
// packages/rest/src/store.ts
|
|
1110
|
+
class MemoryRateLimitStore {
|
|
1111
|
+
#buckets = new Map;
|
|
1112
|
+
#routeBuckets = new Map;
|
|
1113
|
+
#globalResetAt = 0;
|
|
1114
|
+
static #PRUNE_INTERVAL = 60000;
|
|
1115
|
+
#lastPruneAt = 0;
|
|
1116
|
+
getBucketHash(route) {
|
|
1117
|
+
return this.#routeBuckets.get(route);
|
|
1118
|
+
}
|
|
1119
|
+
setBucketHash(route, hash) {
|
|
1120
|
+
this.#routeBuckets.set(route, hash);
|
|
1121
|
+
}
|
|
1122
|
+
getBucket(key) {
|
|
1123
|
+
return this.#buckets.get(key);
|
|
1124
|
+
}
|
|
1125
|
+
updateBucket(key, state) {
|
|
1126
|
+
this.#buckets.set(key, state);
|
|
1127
|
+
const now = Date.now();
|
|
1128
|
+
if (now - this.#lastPruneAt >= MemoryRateLimitStore.#PRUNE_INTERVAL) {
|
|
1129
|
+
this.#lastPruneAt = now;
|
|
1130
|
+
this.prune();
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
prune(maxAge = 0) {
|
|
1134
|
+
const cutoff = Date.now() - Math.max(0, maxAge);
|
|
1135
|
+
for (const [key, state] of this.#buckets) {
|
|
1136
|
+
if (state.resetAt <= cutoff)
|
|
1137
|
+
this.#buckets.delete(key);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
getGlobalReset() {
|
|
1141
|
+
return this.#globalResetAt;
|
|
1142
|
+
}
|
|
1143
|
+
setGlobalReset(resetAt) {
|
|
1144
|
+
this.#globalResetAt = resetAt;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
957
1148
|
// packages/rest/src/routes.ts
|
|
958
1149
|
var Routes = {
|
|
959
1150
|
user: () => "/users/@me",
|
|
@@ -999,13 +1190,122 @@ var Routes = {
|
|
|
999
1190
|
guildAutoModerationRule: (guildId, ruleId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules/${snowflake(ruleId, "Rule ID")}`,
|
|
1000
1191
|
voiceRegions: () => "/voice/regions",
|
|
1001
1192
|
gateway: () => "/gateway",
|
|
1002
|
-
gatewayBot: () => "/gateway/bot"
|
|
1193
|
+
gatewayBot: () => "/gateway/bot",
|
|
1194
|
+
guildAuditLog: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/audit-logs`,
|
|
1195
|
+
guildMembers: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members`,
|
|
1196
|
+
guildWelcomeScreen: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/welcome-screen`,
|
|
1197
|
+
guildOnboarding: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/onboarding`,
|
|
1198
|
+
guildPrune: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/prune`,
|
|
1199
|
+
stageInstances: () => "/stage-instances",
|
|
1200
|
+
stageInstanceByChannel: (channelId) => `/stage-instances/${snowflake(channelId, "Channel ID")}`,
|
|
1201
|
+
invite: (code) => `/invites/${encodeURIComponent(code)}`,
|
|
1202
|
+
guildVanity: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/vanity-url`,
|
|
1203
|
+
guildEmoji: (guildId, emojiId) => emojiId ? `/guilds/${snowflake(guildId, "Guild ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/emojis`,
|
|
1204
|
+
applicationEmoji: (applicationId, emojiId) => emojiId ? `/applications/${snowflake(applicationId, "Application ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/emojis`
|
|
1003
1205
|
};
|
|
1004
1206
|
function snowflake(value, field) {
|
|
1005
1207
|
if (!/^\d{1,20}$/.test(value))
|
|
1006
1208
|
throw new TypeError(`${field} must be a valid Discord snowflake.`);
|
|
1007
1209
|
return value;
|
|
1008
1210
|
}
|
|
1211
|
+
// packages/rest/src/redis.ts
|
|
1212
|
+
class RedisRateLimitStore {
|
|
1213
|
+
#client;
|
|
1214
|
+
#prefix;
|
|
1215
|
+
#onErrorCallback;
|
|
1216
|
+
#lastError;
|
|
1217
|
+
constructor(options) {
|
|
1218
|
+
this.#client = options.client;
|
|
1219
|
+
this.#prefix = options.prefix ?? "lunibee:rest:";
|
|
1220
|
+
this.#onErrorCallback = options.onError;
|
|
1221
|
+
}
|
|
1222
|
+
get lastError() {
|
|
1223
|
+
return this.#lastError;
|
|
1224
|
+
}
|
|
1225
|
+
isHealthy() {
|
|
1226
|
+
return this.#lastError === undefined;
|
|
1227
|
+
}
|
|
1228
|
+
#onError(operation, error) {
|
|
1229
|
+
this.#lastError = error;
|
|
1230
|
+
console.warn(`[lunibee/rest] Redis rate-limit store ${operation} failed; ` + `falling back to local limiting for this call.`, error);
|
|
1231
|
+
try {
|
|
1232
|
+
this.#onErrorCallback?.(operation, error);
|
|
1233
|
+
} catch {}
|
|
1234
|
+
}
|
|
1235
|
+
#markHealthy() {
|
|
1236
|
+
this.#lastError = undefined;
|
|
1237
|
+
}
|
|
1238
|
+
async getBucketHash(route) {
|
|
1239
|
+
try {
|
|
1240
|
+
const hash = await this.#client.get(`${this.#prefix}route:${route}`);
|
|
1241
|
+
this.#markHealthy();
|
|
1242
|
+
return hash ?? undefined;
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
this.#onError("getBucketHash", error);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
async setBucketHash(route, hash) {
|
|
1249
|
+
try {
|
|
1250
|
+
await this.#client.set(`${this.#prefix}route:${route}`, hash, "EX", 604800);
|
|
1251
|
+
this.#markHealthy();
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
this.#onError("setBucketHash", error);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async getBucket(key) {
|
|
1257
|
+
try {
|
|
1258
|
+
const data = await this.#client.get(`${this.#prefix}bucket:${key}`);
|
|
1259
|
+
this.#markHealthy();
|
|
1260
|
+
if (!data)
|
|
1261
|
+
return;
|
|
1262
|
+
return JSON.parse(data);
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
this.#onError("getBucket", error);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
async updateBucket(key, state) {
|
|
1269
|
+
const bucketKey = `${this.#prefix}bucket:${key}`;
|
|
1270
|
+
const ttl = Math.ceil((state.resetAt - Date.now()) / 1000);
|
|
1271
|
+
try {
|
|
1272
|
+
if (ttl <= 0) {
|
|
1273
|
+
await this.#client.del(bucketKey);
|
|
1274
|
+
this.#markHealthy();
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
await this.#client.set(bucketKey, JSON.stringify(state), "EX", ttl);
|
|
1278
|
+
this.#markHealthy();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
this.#onError("updateBucket", error);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
async getGlobalReset() {
|
|
1284
|
+
try {
|
|
1285
|
+
const resetAt = await this.#client.get(`${this.#prefix}global`);
|
|
1286
|
+
this.#markHealthy();
|
|
1287
|
+
return resetAt ? Number(resetAt) : 0;
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
this.#onError("getGlobalReset", error);
|
|
1290
|
+
return 0;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
async setGlobalReset(resetAt) {
|
|
1294
|
+
const globalKey = `${this.#prefix}global`;
|
|
1295
|
+
const ttl = Math.ceil((resetAt - Date.now()) / 1000);
|
|
1296
|
+
try {
|
|
1297
|
+
if (ttl <= 0) {
|
|
1298
|
+
await this.#client.del(globalKey);
|
|
1299
|
+
this.#markHealthy();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
await this.#client.set(globalKey, String(resetAt), "EX", ttl);
|
|
1303
|
+
this.#markHealthy();
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
this.#onError("setGlobalReset", error);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1009
1309
|
// packages/rest/src/webhook.ts
|
|
1010
1310
|
class WebhookClient {
|
|
1011
1311
|
id;
|
|
@@ -1030,7 +1330,7 @@ class WebhookClient {
|
|
|
1030
1330
|
const payload = typeof options === "string" ? { content: options } : options;
|
|
1031
1331
|
const { thread_id, ...body } = payload;
|
|
1032
1332
|
const query = thread_id ? `?thread_id=${thread_id}&wait=true` : `?wait=true`;
|
|
1033
|
-
const formattedEmbeds = body.embeds?.map((e) =>
|
|
1333
|
+
const formattedEmbeds = body.embeds?.map((e) => ("toJSON" in e) ? e.toJSON() : e);
|
|
1034
1334
|
return this.#rest.post(`${Routes.webhook(this.id, this.token)}${query}`, {
|
|
1035
1335
|
...body,
|
|
1036
1336
|
embeds: formattedEmbeds
|
|
@@ -1065,6 +1365,49 @@ class RESTError extends Error {
|
|
|
1065
1365
|
var LIBRARY_VERSION = package_default.version;
|
|
1066
1366
|
var LIBRARY_URL = "https://github.com/Ekretos/lunibee";
|
|
1067
1367
|
var USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${package_default.version})`;
|
|
1368
|
+
var REQUEST_DATA_KEYS = new Set([
|
|
1369
|
+
"body",
|
|
1370
|
+
"files",
|
|
1371
|
+
"query",
|
|
1372
|
+
"headers",
|
|
1373
|
+
"reason",
|
|
1374
|
+
"auth",
|
|
1375
|
+
"signal"
|
|
1376
|
+
]);
|
|
1377
|
+
function isRequestData(value) {
|
|
1378
|
+
if (value === null || typeof value !== "object")
|
|
1379
|
+
return false;
|
|
1380
|
+
if (Array.isArray(value))
|
|
1381
|
+
return false;
|
|
1382
|
+
if (typeof FormData !== "undefined" && value instanceof FormData)
|
|
1383
|
+
return false;
|
|
1384
|
+
if (value instanceof Blob || value instanceof Uint8Array || value instanceof ArrayBuffer)
|
|
1385
|
+
return false;
|
|
1386
|
+
const keys = Object.keys(value);
|
|
1387
|
+
if (keys.length === 0)
|
|
1388
|
+
return false;
|
|
1389
|
+
return keys.every((key) => REQUEST_DATA_KEYS.has(key));
|
|
1390
|
+
}
|
|
1391
|
+
function serializeQuery(query) {
|
|
1392
|
+
if (query === undefined)
|
|
1393
|
+
return "";
|
|
1394
|
+
if (typeof query === "string")
|
|
1395
|
+
return query.length === 0 || query.startsWith("?") ? query : `?${query}`;
|
|
1396
|
+
const params = query instanceof URLSearchParams ? query : new URLSearchParams(Object.entries(query).flatMap(([key, value]) => value === undefined || value === null ? [] : [[key, String(value)]]));
|
|
1397
|
+
const serialized = params.toString();
|
|
1398
|
+
return serialized.length === 0 ? "" : `?${serialized}`;
|
|
1399
|
+
}
|
|
1400
|
+
function majorParameter(path) {
|
|
1401
|
+
const match = /^\/(channels|guilds|webhooks)\/(\d+)(?:\/([^/?]+))?/.exec(path);
|
|
1402
|
+
if (!match)
|
|
1403
|
+
return "@none";
|
|
1404
|
+
if (match[1] === "webhooks" && match[3] !== undefined)
|
|
1405
|
+
return `${match[2]}:${match[3]}`;
|
|
1406
|
+
return match[2];
|
|
1407
|
+
}
|
|
1408
|
+
function scopeBucket(hashOrRoute, major) {
|
|
1409
|
+
return `${hashOrRoute}|${major}`;
|
|
1410
|
+
}
|
|
1068
1411
|
function createRetryPolicy(maxRetries = 2) {
|
|
1069
1412
|
if (!Number.isFinite(maxRetries) || maxRetries < 0)
|
|
1070
1413
|
throw new TypeError("maxRetries must be a non-negative finite number.");
|
|
@@ -1087,14 +1430,21 @@ class REST {
|
|
|
1087
1430
|
#timeout;
|
|
1088
1431
|
#retryPolicy;
|
|
1089
1432
|
#token;
|
|
1090
|
-
#
|
|
1091
|
-
#
|
|
1092
|
-
#
|
|
1433
|
+
#store;
|
|
1434
|
+
#localQueues = new Map;
|
|
1435
|
+
#hooks = {};
|
|
1093
1436
|
constructor(options = {}) {
|
|
1094
1437
|
this.#token = options.token;
|
|
1095
1438
|
this.#timeout = Math.max(1, options.timeout ?? 15000);
|
|
1096
1439
|
this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
|
|
1097
1440
|
this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
|
|
1441
|
+
this.#store = options.store ?? new MemoryRateLimitStore;
|
|
1442
|
+
if (options.hooks)
|
|
1443
|
+
this.#hooks = options.hooks;
|
|
1444
|
+
}
|
|
1445
|
+
setHooks(hooks) {
|
|
1446
|
+
this.#hooks = hooks;
|
|
1447
|
+
return this;
|
|
1098
1448
|
}
|
|
1099
1449
|
setToken(token) {
|
|
1100
1450
|
if (!token.trim())
|
|
@@ -1107,56 +1457,91 @@ class REST {
|
|
|
1107
1457
|
throw new TypeError("REST method is required.");
|
|
1108
1458
|
if (!path.startsWith("/"))
|
|
1109
1459
|
throw new TypeError("REST paths must start with '/'.");
|
|
1460
|
+
const files = options.files;
|
|
1461
|
+
if (files && files.length > 0 && !(body instanceof FormData))
|
|
1462
|
+
body = this.#fileForm(body, files);
|
|
1463
|
+
const query = serializeQuery(options.query);
|
|
1464
|
+
const finalPath = query ? `${path}${query}` : path;
|
|
1110
1465
|
const route = `${normalizedMethod}:${this.#normalizeRoute(path)}`;
|
|
1111
|
-
|
|
1112
|
-
let
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
queue: Promise.resolve()
|
|
1116
|
-
};
|
|
1117
|
-
this.#buckets.set(bucketKey, bucket);
|
|
1118
|
-
const previous = bucket.queue;
|
|
1466
|
+
const major = majorParameter(path);
|
|
1467
|
+
let bucketKey = scopeBucket(await this.#store.getBucketHash(route) ?? route, major);
|
|
1468
|
+
const queueKey = bucketKey;
|
|
1469
|
+
const previous = this.#localQueues.get(queueKey) ?? Promise.resolve();
|
|
1119
1470
|
let release;
|
|
1120
|
-
|
|
1471
|
+
const gate = new Promise((resolve) => {
|
|
1121
1472
|
release = resolve;
|
|
1122
1473
|
});
|
|
1474
|
+
this.#localQueues.set(queueKey, gate);
|
|
1123
1475
|
await this.#abortable(previous, options.signal, path);
|
|
1124
1476
|
try {
|
|
1125
1477
|
for (let attempt = 0;attempt <= this.#retryPolicy.maxRetries; attempt++) {
|
|
1126
|
-
await this.#wait(
|
|
1478
|
+
await this.#wait(bucketKey, options.signal, path);
|
|
1127
1479
|
await this.#waitGlobal(options.signal, path);
|
|
1128
1480
|
const controller = new AbortController;
|
|
1129
1481
|
const onAbort = () => controller.abort(options.signal?.reason);
|
|
1130
1482
|
if (options.signal?.aborted)
|
|
1131
1483
|
throw this.#abortError(path, options.signal.reason);
|
|
1132
|
-
options.signal?.addEventListener("abort", onAbort, {
|
|
1484
|
+
options.signal?.addEventListener("abort", onAbort, {
|
|
1485
|
+
once: true
|
|
1486
|
+
});
|
|
1133
1487
|
const timer = setTimeout(() => controller.abort(new DOMException("REST request timeout", "TimeoutError")), this.#timeout);
|
|
1134
1488
|
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
|
|
1135
|
-
const headers = {
|
|
1136
|
-
|
|
1137
|
-
"
|
|
1138
|
-
|
|
1489
|
+
const headers = { ...options.headers };
|
|
1490
|
+
if (options.auth !== false && this.#token)
|
|
1491
|
+
headers["Authorization"] = `Bot ${this.#token}`;
|
|
1492
|
+
headers["User-Agent"] = USER_AGENT;
|
|
1493
|
+
if (options.reason !== undefined)
|
|
1494
|
+
headers["X-Audit-Log-Reason"] = encodeURIComponent(options.reason);
|
|
1139
1495
|
if (!isFormData)
|
|
1140
1496
|
headers["Content-Type"] = "application/json";
|
|
1141
1497
|
const fetchBody = isFormData ? body : body === undefined ? undefined : JSON.stringify(body);
|
|
1142
1498
|
try {
|
|
1143
|
-
|
|
1499
|
+
this.#emit(this.#hooks.onRequest, {
|
|
1500
|
+
method: normalizedMethod,
|
|
1501
|
+
path,
|
|
1502
|
+
attempt
|
|
1503
|
+
});
|
|
1504
|
+
const requestStart = Date.now();
|
|
1505
|
+
const response = await fetch(`${this.#baseURL}${finalPath}`, {
|
|
1144
1506
|
method: normalizedMethod,
|
|
1145
1507
|
headers,
|
|
1146
1508
|
body: fetchBody,
|
|
1147
1509
|
signal: controller.signal
|
|
1148
1510
|
});
|
|
1149
|
-
|
|
1150
|
-
|
|
1511
|
+
this.#emit(this.#hooks.onResponse, {
|
|
1512
|
+
method: normalizedMethod,
|
|
1513
|
+
path,
|
|
1514
|
+
status: response.status,
|
|
1515
|
+
durationMs: Date.now() - requestStart
|
|
1516
|
+
});
|
|
1517
|
+
bucketKey = await this.#update(response, bucketKey, route, major);
|
|
1151
1518
|
const payload = await this.#readPayload(response);
|
|
1152
1519
|
if (response.ok)
|
|
1153
1520
|
return response.status === 204 ? undefined : payload;
|
|
1154
1521
|
const data = this.#errorData(payload);
|
|
1155
1522
|
const retryAfter = response.status === 429 ? this.#retryAfter(response, data) : undefined;
|
|
1156
|
-
if (data.global && retryAfter !== undefined)
|
|
1157
|
-
|
|
1523
|
+
if (data.global && retryAfter !== undefined) {
|
|
1524
|
+
const currentGlobal = await this.#store.getGlobalReset();
|
|
1525
|
+
await this.#store.setGlobalReset(Math.max(currentGlobal, Date.now() + retryAfter * 1000));
|
|
1526
|
+
}
|
|
1527
|
+
if (response.status === 429)
|
|
1528
|
+
this.#emit(this.#hooks.onRateLimit, {
|
|
1529
|
+
method: normalizedMethod,
|
|
1530
|
+
path,
|
|
1531
|
+
retryAfterMs: (retryAfter ?? 1) * 1000,
|
|
1532
|
+
global: data.global ?? false,
|
|
1533
|
+
bucket: await this.#store.getBucketHash(route)
|
|
1534
|
+
});
|
|
1158
1535
|
if (attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, response.status)) {
|
|
1159
|
-
|
|
1536
|
+
const delay = this.#retryPolicy.getDelay(attempt, retryAfter);
|
|
1537
|
+
this.#emit(this.#hooks.onRetry, {
|
|
1538
|
+
method: normalizedMethod,
|
|
1539
|
+
path,
|
|
1540
|
+
attempt,
|
|
1541
|
+
status: response.status,
|
|
1542
|
+
delayMs: delay
|
|
1543
|
+
});
|
|
1544
|
+
await this.#sleep(delay, options.signal, path);
|
|
1160
1545
|
continue;
|
|
1161
1546
|
}
|
|
1162
1547
|
throw new RESTError(data.message ?? response.statusText ?? `Discord REST request failed with status ${response.status}`, response.status, data.code, payload, { method: normalizedMethod, path });
|
|
@@ -1179,22 +1564,29 @@ class REST {
|
|
|
1179
1564
|
throw new RESTError("Discord REST request exhausted its retry attempts", 0, undefined, undefined, { method: normalizedMethod, path });
|
|
1180
1565
|
} finally {
|
|
1181
1566
|
release();
|
|
1567
|
+
if (this.#localQueues.get(queueKey) === gate)
|
|
1568
|
+
this.#localQueues.delete(queueKey);
|
|
1182
1569
|
}
|
|
1183
1570
|
}
|
|
1571
|
+
#dispatch(method, path, bodyOrOptions, options) {
|
|
1572
|
+
if (options === undefined && isRequestData(bodyOrOptions))
|
|
1573
|
+
return this.request(method, path, bodyOrOptions.body, bodyOrOptions);
|
|
1574
|
+
return this.request(method, path, bodyOrOptions, options);
|
|
1575
|
+
}
|
|
1184
1576
|
get(path, options) {
|
|
1185
|
-
return this.request("GET", path,
|
|
1577
|
+
return this.request("GET", path, options?.body, options);
|
|
1186
1578
|
}
|
|
1187
1579
|
post(path, body, options) {
|
|
1188
|
-
return this
|
|
1580
|
+
return this.#dispatch("POST", path, body, options);
|
|
1189
1581
|
}
|
|
1190
1582
|
patch(path, body, options) {
|
|
1191
|
-
return this
|
|
1583
|
+
return this.#dispatch("PATCH", path, body, options);
|
|
1192
1584
|
}
|
|
1193
1585
|
put(path, body, options) {
|
|
1194
|
-
return this
|
|
1586
|
+
return this.#dispatch("PUT", path, body, options);
|
|
1195
1587
|
}
|
|
1196
1588
|
delete(path, options) {
|
|
1197
|
-
return this.request("DELETE", path,
|
|
1589
|
+
return this.request("DELETE", path, options?.body, options);
|
|
1198
1590
|
}
|
|
1199
1591
|
#fileForm(payload, files) {
|
|
1200
1592
|
const form = new FormData;
|
|
@@ -1214,13 +1606,22 @@ class REST {
|
|
|
1214
1606
|
patchWithFiles(path, payload, files, options) {
|
|
1215
1607
|
return this.request("PATCH", path, this.#fileForm(payload, files), options);
|
|
1216
1608
|
}
|
|
1217
|
-
|
|
1609
|
+
#emit(hook, ctx) {
|
|
1610
|
+
if (!hook)
|
|
1611
|
+
return;
|
|
1612
|
+
Promise.resolve().then(() => hook(ctx)).catch(() => {});
|
|
1613
|
+
}
|
|
1614
|
+
async#wait(bucketKey, signal, path) {
|
|
1615
|
+
const bucket = await this.#store.getBucket(bucketKey);
|
|
1616
|
+
if (!bucket)
|
|
1617
|
+
return;
|
|
1218
1618
|
const delay = bucket.resetAt - Date.now();
|
|
1219
1619
|
if (bucket.remaining <= 0 && delay > 0)
|
|
1220
1620
|
await this.#sleep(delay, signal, path);
|
|
1221
1621
|
}
|
|
1222
1622
|
async#waitGlobal(signal, path) {
|
|
1223
|
-
const
|
|
1623
|
+
const globalResetAt = await this.#store.getGlobalReset();
|
|
1624
|
+
const delay = globalResetAt - Date.now();
|
|
1224
1625
|
if (delay > 0)
|
|
1225
1626
|
await this.#sleep(delay, signal, path);
|
|
1226
1627
|
}
|
|
@@ -1284,16 +1685,17 @@ class REST {
|
|
|
1284
1685
|
const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
|
|
1285
1686
|
return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
|
|
1286
1687
|
}
|
|
1287
|
-
#update(response,
|
|
1688
|
+
async#update(response, bucketKey, route, major) {
|
|
1689
|
+
let currentKey = bucketKey;
|
|
1288
1690
|
const serverBucket = response.headers.get("X-RateLimit-Bucket");
|
|
1289
1691
|
if (serverBucket) {
|
|
1290
|
-
this.#
|
|
1291
|
-
|
|
1292
|
-
const shared = this.#buckets.get(serverBucket) ?? bucket;
|
|
1293
|
-
this.#buckets.set(serverBucket, shared);
|
|
1294
|
-
bucket = shared;
|
|
1295
|
-
}
|
|
1692
|
+
await this.#store.setBucketHash(route, serverBucket);
|
|
1693
|
+
currentKey = scopeBucket(serverBucket, major);
|
|
1296
1694
|
}
|
|
1695
|
+
let bucket = await this.#store.getBucket(currentKey) ?? {
|
|
1696
|
+
remaining: 1,
|
|
1697
|
+
resetAt: 0
|
|
1698
|
+
};
|
|
1297
1699
|
const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
|
|
1298
1700
|
const resetAfter = Number(response.headers.get("X-RateLimit-Reset-After"));
|
|
1299
1701
|
const reset = Number(response.headers.get("X-RateLimit-Reset"));
|
|
@@ -1308,12 +1710,15 @@ class REST {
|
|
|
1308
1710
|
if (Number.isFinite(retryAfter))
|
|
1309
1711
|
bucket.resetAt = Math.max(bucket.resetAt, Date.now() + retryAfter * 1000);
|
|
1310
1712
|
}
|
|
1713
|
+
await this.#store.updateBucket(currentKey, bucket);
|
|
1311
1714
|
if (response.headers.get("X-RateLimit-Global") === "true") {
|
|
1312
1715
|
const retryAfter = Number(response.headers.get("Retry-After"));
|
|
1313
|
-
if (Number.isFinite(retryAfter))
|
|
1314
|
-
|
|
1716
|
+
if (Number.isFinite(retryAfter)) {
|
|
1717
|
+
const currentGlobal = await this.#store.getGlobalReset();
|
|
1718
|
+
await this.#store.setGlobalReset(Math.max(currentGlobal, Date.now() + retryAfter * 1000));
|
|
1719
|
+
}
|
|
1315
1720
|
}
|
|
1316
|
-
return
|
|
1721
|
+
return currentKey;
|
|
1317
1722
|
}
|
|
1318
1723
|
}
|
|
1319
1724
|
|
|
@@ -1459,6 +1864,13 @@ class Guild extends BaseStructure {
|
|
|
1459
1864
|
vanityUrlCode;
|
|
1460
1865
|
nsfwLevel;
|
|
1461
1866
|
discoverySplash;
|
|
1867
|
+
rulesChannelId;
|
|
1868
|
+
systemChannelId;
|
|
1869
|
+
systemChannelFlags;
|
|
1870
|
+
maxMembers;
|
|
1871
|
+
mfaLevel;
|
|
1872
|
+
explicitContentFilter;
|
|
1873
|
+
defaultMessageNotifications;
|
|
1462
1874
|
constructor(data) {
|
|
1463
1875
|
super(data.id);
|
|
1464
1876
|
if (!data.name)
|
|
@@ -1480,6 +1892,14 @@ class Guild extends BaseStructure {
|
|
|
1480
1892
|
this.vanityUrlCode = data.vanity_url_code ?? null;
|
|
1481
1893
|
this.nsfwLevel = data.nsfw_level ?? 0;
|
|
1482
1894
|
this.discoverySplash = data.discovery_splash ?? null;
|
|
1895
|
+
const d = data;
|
|
1896
|
+
this.rulesChannelId = d.rules_channel_id ?? null;
|
|
1897
|
+
this.systemChannelId = d.system_channel_id ?? null;
|
|
1898
|
+
this.systemChannelFlags = d.system_channel_flags ?? 0;
|
|
1899
|
+
this.maxMembers = d.max_members ?? 0;
|
|
1900
|
+
this.mfaLevel = d.mfa_level ?? 0;
|
|
1901
|
+
this.explicitContentFilter = d.explicit_content_filter ?? 0;
|
|
1902
|
+
this.defaultMessageNotifications = d.default_message_notifications ?? 0;
|
|
1483
1903
|
}
|
|
1484
1904
|
iconURL(options = {}) {
|
|
1485
1905
|
if (!this.icon)
|
|
@@ -1501,12 +1921,27 @@ class Guild extends BaseStructure {
|
|
|
1501
1921
|
return null;
|
|
1502
1922
|
return cdnURL(`/discovery-splashes/${this.id}`, this.discoverySplash, options);
|
|
1503
1923
|
}
|
|
1924
|
+
widgetImageURL(style = "shield") {
|
|
1925
|
+
return `https://discord.com/api/guilds/${this.id}/widget.png?style=${style}`;
|
|
1926
|
+
}
|
|
1504
1927
|
hasFeature(feature) {
|
|
1505
1928
|
return this.features.includes(feature);
|
|
1506
1929
|
}
|
|
1507
1930
|
get isCommunity() {
|
|
1508
1931
|
return this.hasFeature("COMMUNITY");
|
|
1509
1932
|
}
|
|
1933
|
+
get isPartnered() {
|
|
1934
|
+
return this.hasFeature("PARTNERED");
|
|
1935
|
+
}
|
|
1936
|
+
get isVerified() {
|
|
1937
|
+
return this.hasFeature("VERIFIED");
|
|
1938
|
+
}
|
|
1939
|
+
get isBoosted() {
|
|
1940
|
+
return this.premiumSubscriptionCount > 0;
|
|
1941
|
+
}
|
|
1942
|
+
get boostLevel() {
|
|
1943
|
+
return this.premiumTier;
|
|
1944
|
+
}
|
|
1510
1945
|
get hasVanityUrl() {
|
|
1511
1946
|
return this.vanityUrlCode !== null;
|
|
1512
1947
|
}
|
|
@@ -1517,141 +1952,14 @@ class Guild extends BaseStructure {
|
|
|
1517
1952
|
return this.name;
|
|
1518
1953
|
}
|
|
1519
1954
|
}
|
|
1520
|
-
// packages/structures/src/
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
ManageChannels: 1n << 4n,
|
|
1527
|
-
ManageGuild: 1n << 5n,
|
|
1528
|
-
AddReactions: 1n << 6n,
|
|
1529
|
-
ViewAuditLog: 1n << 7n,
|
|
1530
|
-
PrioritySpeaker: 1n << 8n,
|
|
1531
|
-
Stream: 1n << 9n,
|
|
1532
|
-
ViewChannel: 1n << 10n,
|
|
1533
|
-
SendMessages: 1n << 11n,
|
|
1534
|
-
SendTTSMessages: 1n << 12n,
|
|
1535
|
-
ManageMessages: 1n << 13n,
|
|
1536
|
-
EmbedLinks: 1n << 14n,
|
|
1537
|
-
AttachFiles: 1n << 15n,
|
|
1538
|
-
ReadMessageHistory: 1n << 16n,
|
|
1539
|
-
MentionEveryone: 1n << 17n,
|
|
1540
|
-
UseExternalEmojis: 1n << 18n,
|
|
1541
|
-
ViewGuildInsights: 1n << 19n,
|
|
1542
|
-
Connect: 1n << 20n,
|
|
1543
|
-
Speak: 1n << 21n,
|
|
1544
|
-
MuteMembers: 1n << 22n,
|
|
1545
|
-
DeafenMembers: 1n << 23n,
|
|
1546
|
-
MoveMembers: 1n << 24n,
|
|
1547
|
-
UseVAD: 1n << 25n,
|
|
1548
|
-
ChangeNickname: 1n << 26n,
|
|
1549
|
-
ManageNicknames: 1n << 27n,
|
|
1550
|
-
ManageRoles: 1n << 28n,
|
|
1551
|
-
ManageWebhooks: 1n << 29n,
|
|
1552
|
-
ManageGuildExpressions: 1n << 30n,
|
|
1553
|
-
UseApplicationCommands: 1n << 31n,
|
|
1554
|
-
RequestToSpeak: 1n << 32n,
|
|
1555
|
-
ManageEvents: 1n << 33n,
|
|
1556
|
-
ManageThreads: 1n << 34n,
|
|
1557
|
-
CreatePublicThreads: 1n << 35n,
|
|
1558
|
-
CreatePrivateThreads: 1n << 36n,
|
|
1559
|
-
UseExternalStickers: 1n << 37n,
|
|
1560
|
-
SendMessagesInThreads: 1n << 38n,
|
|
1561
|
-
UseEmbeddedActivities: 1n << 39n,
|
|
1562
|
-
ModerateMembers: 1n << 40n,
|
|
1563
|
-
ViewCreatorMonetizationAnalytics: 1n << 41n,
|
|
1564
|
-
UseSoundboard: 1n << 42n,
|
|
1565
|
-
CreateGuildExpressions: 1n << 43n,
|
|
1566
|
-
CreateEvents: 1n << 44n,
|
|
1567
|
-
UseExternalSounds: 1n << 45n,
|
|
1568
|
-
SendVoiceMessages: 1n << 46n,
|
|
1569
|
-
SendPolls: 1n << 49n,
|
|
1570
|
-
UseExternalApps: 1n << 50n
|
|
1571
|
-
};
|
|
1572
|
-
|
|
1573
|
-
class PermissionSet2 {
|
|
1574
|
-
bitfield;
|
|
1575
|
-
constructor(value = 0n) {
|
|
1576
|
-
try {
|
|
1577
|
-
if (Array.isArray(value)) {
|
|
1578
|
-
let bit = 0n;
|
|
1579
|
-
for (const item of value)
|
|
1580
|
-
bit |= this.#resolve(item);
|
|
1581
|
-
this.bitfield = bit;
|
|
1582
|
-
} else if (value instanceof PermissionSet2) {
|
|
1583
|
-
this.bitfield = value.bitfield;
|
|
1584
|
-
} else {
|
|
1585
|
-
const bitfield = this.#resolve(value);
|
|
1586
|
-
if (bitfield < 0n)
|
|
1587
|
-
throw new RangeError("Permission bitfield cannot be negative.");
|
|
1588
|
-
this.bitfield = bitfield;
|
|
1589
|
-
}
|
|
1590
|
-
} catch (error) {
|
|
1591
|
-
if (error instanceof RangeError)
|
|
1592
|
-
throw error;
|
|
1593
|
-
throw new TypeError("Permission bitfield must be a valid non-negative integer.", { cause: error });
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
has(...permissions) {
|
|
1597
|
-
return permissions.every((permission) => (this.bitfield & this.#resolve(permission)) === this.#resolve(permission));
|
|
1598
|
-
}
|
|
1599
|
-
any(...permissions) {
|
|
1600
|
-
return permissions.some((permission) => (this.bitfield & this.#resolve(permission)) !== 0n);
|
|
1601
|
-
}
|
|
1602
|
-
add(...permissions) {
|
|
1603
|
-
let bitfield = this.bitfield;
|
|
1604
|
-
for (const permission of permissions)
|
|
1605
|
-
bitfield |= this.#resolve(permission);
|
|
1606
|
-
return new PermissionSet2(bitfield);
|
|
1607
|
-
}
|
|
1608
|
-
remove(...permissions) {
|
|
1609
|
-
let bitfield = this.bitfield;
|
|
1610
|
-
for (const permission of permissions)
|
|
1611
|
-
bitfield &= ~this.#resolve(permission);
|
|
1612
|
-
return new PermissionSet2(bitfield);
|
|
1613
|
-
}
|
|
1614
|
-
equals(other) {
|
|
1615
|
-
return this.bitfield === (other instanceof PermissionSet2 ? other.bitfield : new PermissionSet2(other).bitfield);
|
|
1616
|
-
}
|
|
1617
|
-
toString() {
|
|
1618
|
-
return this.bitfield.toString();
|
|
1619
|
-
}
|
|
1620
|
-
toArray() {
|
|
1621
|
-
return Object.keys(Permissions2).filter((permission) => this.has(permission));
|
|
1622
|
-
}
|
|
1623
|
-
#resolve(permission) {
|
|
1624
|
-
if (permission instanceof PermissionSet2)
|
|
1625
|
-
return permission.bitfield;
|
|
1626
|
-
if (typeof permission === "bigint")
|
|
1627
|
-
return permission;
|
|
1628
|
-
if (typeof permission === "number")
|
|
1629
|
-
return BigInt(permission);
|
|
1630
|
-
if (Array.isArray(permission)) {
|
|
1631
|
-
let bit = 0n;
|
|
1632
|
-
for (const p of permission)
|
|
1633
|
-
bit |= this.#resolve(p);
|
|
1634
|
-
return bit;
|
|
1635
|
-
}
|
|
1636
|
-
if (typeof permission === "string") {
|
|
1637
|
-
if (permission in Permissions2)
|
|
1638
|
-
return Permissions2[permission];
|
|
1639
|
-
const matchedKey = Object.keys(Permissions2).find((k) => k.toLowerCase() === permission.toLowerCase());
|
|
1640
|
-
if (matchedKey)
|
|
1641
|
-
return Permissions2[matchedKey];
|
|
1642
|
-
return BigInt(permission);
|
|
1643
|
-
}
|
|
1644
|
-
return 0n;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
class PermissionsBitField2 extends PermissionSet2 {
|
|
1649
|
-
constructor(value = 0n) {
|
|
1650
|
-
super(value);
|
|
1651
|
-
}
|
|
1955
|
+
// packages/structures/src/resources.ts
|
|
1956
|
+
function toDateOrNull(value) {
|
|
1957
|
+
if (!value)
|
|
1958
|
+
return null;
|
|
1959
|
+
const date = new Date(value);
|
|
1960
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
1652
1961
|
}
|
|
1653
1962
|
|
|
1654
|
-
// packages/structures/src/resources.ts
|
|
1655
1963
|
class GuildMember {
|
|
1656
1964
|
user;
|
|
1657
1965
|
guildId;
|
|
@@ -1660,6 +1968,10 @@ class GuildMember {
|
|
|
1660
1968
|
pending;
|
|
1661
1969
|
joinedAt;
|
|
1662
1970
|
permissions;
|
|
1971
|
+
avatarHash;
|
|
1972
|
+
premiumSince;
|
|
1973
|
+
timedOutUntil;
|
|
1974
|
+
flags;
|
|
1663
1975
|
constructor(data) {
|
|
1664
1976
|
this.user = new User(data.user);
|
|
1665
1977
|
if (!/^\d{1,20}$/.test(data.guild_id))
|
|
@@ -1671,11 +1983,26 @@ class GuildMember {
|
|
|
1671
1983
|
this.joinedAt = data.joined_at ? new Date(data.joined_at) : undefined;
|
|
1672
1984
|
if (this.joinedAt?.toString() === "Invalid Date")
|
|
1673
1985
|
throw new RangeError("Member joined_at must be a valid date.");
|
|
1674
|
-
this.permissions = new
|
|
1986
|
+
this.permissions = new PermissionsBitField(data.permissions ?? 0n);
|
|
1987
|
+
this.avatarHash = data.avatar ?? null;
|
|
1988
|
+
this.premiumSince = toDateOrNull(data.premium_since);
|
|
1989
|
+
this.timedOutUntil = toDateOrNull(data.communication_disabled_until);
|
|
1990
|
+
this.flags = data.flags ?? 0;
|
|
1675
1991
|
}
|
|
1676
1992
|
get displayName() {
|
|
1677
1993
|
return this.nickname ?? this.user.displayName;
|
|
1678
1994
|
}
|
|
1995
|
+
get isTimedOut() {
|
|
1996
|
+
return this.timedOutUntil !== null && this.timedOutUntil > new Date;
|
|
1997
|
+
}
|
|
1998
|
+
avatarURL(options = {}) {
|
|
1999
|
+
if (this.avatarHash)
|
|
2000
|
+
return cdnURL(`/guilds/${this.guildId}/users/${this.user.id}/avatars`, this.avatarHash, options);
|
|
2001
|
+
return this.user.avatarURL(options);
|
|
2002
|
+
}
|
|
2003
|
+
displayAvatarURL(options = {}) {
|
|
2004
|
+
return this.avatarURL(options) ?? this.user.displayAvatarURL(options);
|
|
2005
|
+
}
|
|
1679
2006
|
toString() {
|
|
1680
2007
|
return this.user.toString();
|
|
1681
2008
|
}
|
|
@@ -1688,6 +2015,10 @@ class Role extends BaseStructure {
|
|
|
1688
2015
|
permissions;
|
|
1689
2016
|
managed;
|
|
1690
2017
|
mentionable;
|
|
2018
|
+
position;
|
|
2019
|
+
unicodeEmoji;
|
|
2020
|
+
iconHash;
|
|
2021
|
+
tags;
|
|
1691
2022
|
constructor(data) {
|
|
1692
2023
|
super(data.id);
|
|
1693
2024
|
if (!data.name)
|
|
@@ -1695,9 +2026,24 @@ class Role extends BaseStructure {
|
|
|
1695
2026
|
this.name = data.name;
|
|
1696
2027
|
this.color = data.color ?? 0;
|
|
1697
2028
|
this.hoist = data.hoist ?? false;
|
|
1698
|
-
this.permissions = new
|
|
2029
|
+
this.permissions = new PermissionsBitField(data.permissions ?? 0n);
|
|
1699
2030
|
this.managed = data.managed ?? false;
|
|
1700
2031
|
this.mentionable = data.mentionable ?? false;
|
|
2032
|
+
this.position = data.position ?? 0;
|
|
2033
|
+
this.unicodeEmoji = data.unicode_emoji ?? null;
|
|
2034
|
+
this.iconHash = data.icon ?? null;
|
|
2035
|
+
this.tags = data.tags ?? null;
|
|
2036
|
+
}
|
|
2037
|
+
get colorHex() {
|
|
2038
|
+
return `#${this.color.toString(16).padStart(6, "0").toUpperCase()}`;
|
|
2039
|
+
}
|
|
2040
|
+
iconURL(options = {}) {
|
|
2041
|
+
if (!this.iconHash)
|
|
2042
|
+
return null;
|
|
2043
|
+
return cdnURL(`/role-icons/${this.id}`, this.iconHash, options);
|
|
2044
|
+
}
|
|
2045
|
+
isEveryone(guildId) {
|
|
2046
|
+
return this.id === guildId;
|
|
1701
2047
|
}
|
|
1702
2048
|
toString() {
|
|
1703
2049
|
return `<@&${this.id}>`;
|
|
@@ -1712,6 +2058,218 @@ class TextChannel extends Channel {
|
|
|
1712
2058
|
return `<#${this.id}>`;
|
|
1713
2059
|
}
|
|
1714
2060
|
}
|
|
2061
|
+
|
|
2062
|
+
class Invite {
|
|
2063
|
+
code;
|
|
2064
|
+
guildId;
|
|
2065
|
+
channelId;
|
|
2066
|
+
inviter;
|
|
2067
|
+
uses;
|
|
2068
|
+
maxUses;
|
|
2069
|
+
maxAge;
|
|
2070
|
+
temporary;
|
|
2071
|
+
createdAt;
|
|
2072
|
+
expiresAt;
|
|
2073
|
+
constructor(data) {
|
|
2074
|
+
if (!data.code)
|
|
2075
|
+
throw new TypeError("An invite requires a code.");
|
|
2076
|
+
this.code = data.code;
|
|
2077
|
+
this.guildId = data.guild?.id ?? null;
|
|
2078
|
+
this.channelId = data.channel?.id ?? null;
|
|
2079
|
+
this.inviter = data.inviter ? new User(data.inviter) : null;
|
|
2080
|
+
this.uses = data.uses ?? 0;
|
|
2081
|
+
this.maxUses = data.max_uses ?? 0;
|
|
2082
|
+
this.maxAge = data.max_age ?? 0;
|
|
2083
|
+
this.temporary = data.temporary ?? false;
|
|
2084
|
+
this.createdAt = data.created_at ? new Date(data.created_at) : null;
|
|
2085
|
+
this.expiresAt = data.expires_at ? new Date(data.expires_at) : null;
|
|
2086
|
+
}
|
|
2087
|
+
get url() {
|
|
2088
|
+
return `https://discord.gg/${this.code}`;
|
|
2089
|
+
}
|
|
2090
|
+
toString() {
|
|
2091
|
+
return this.url;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
var WebhookType = {
|
|
2095
|
+
Incoming: 1,
|
|
2096
|
+
ChannelFollower: 2,
|
|
2097
|
+
Application: 3
|
|
2098
|
+
};
|
|
2099
|
+
|
|
2100
|
+
class Webhook extends BaseStructure {
|
|
2101
|
+
type;
|
|
2102
|
+
name;
|
|
2103
|
+
avatarHash;
|
|
2104
|
+
channelId;
|
|
2105
|
+
guildId;
|
|
2106
|
+
applicationId;
|
|
2107
|
+
token;
|
|
2108
|
+
constructor(data) {
|
|
2109
|
+
super(data.id);
|
|
2110
|
+
this.type = data.type ?? WebhookType.Incoming;
|
|
2111
|
+
this.name = data.name ?? null;
|
|
2112
|
+
this.avatarHash = data.avatar ?? null;
|
|
2113
|
+
this.channelId = data.channel_id ?? null;
|
|
2114
|
+
this.guildId = data.guild_id ?? null;
|
|
2115
|
+
this.applicationId = data.application_id ?? null;
|
|
2116
|
+
this.token = data.token ?? null;
|
|
2117
|
+
}
|
|
2118
|
+
avatarURL(options = {}) {
|
|
2119
|
+
if (!this.avatarHash)
|
|
2120
|
+
return null;
|
|
2121
|
+
return cdnURL(`/avatars/${this.id}`, this.avatarHash, options);
|
|
2122
|
+
}
|
|
2123
|
+
get url() {
|
|
2124
|
+
return this.token ? `https://discord.com/api/webhooks/${this.id}/${this.token}` : null;
|
|
2125
|
+
}
|
|
2126
|
+
toString() {
|
|
2127
|
+
return this.url ?? this.id;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
class Emoji extends BaseStructure {
|
|
2132
|
+
name;
|
|
2133
|
+
roleIds;
|
|
2134
|
+
user;
|
|
2135
|
+
requireColons;
|
|
2136
|
+
managed;
|
|
2137
|
+
animated;
|
|
2138
|
+
available;
|
|
2139
|
+
constructor(data) {
|
|
2140
|
+
super(data.id ?? "unicode");
|
|
2141
|
+
this.name = data.name;
|
|
2142
|
+
this.roleIds = data.roles ?? [];
|
|
2143
|
+
this.user = data.user ? new User(data.user) : null;
|
|
2144
|
+
this.requireColons = data.require_colons ?? false;
|
|
2145
|
+
this.managed = data.managed ?? false;
|
|
2146
|
+
this.animated = data.animated ?? false;
|
|
2147
|
+
this.available = data.available ?? true;
|
|
2148
|
+
}
|
|
2149
|
+
url(options = {}) {
|
|
2150
|
+
if (!this.id || this.id === "unicode")
|
|
2151
|
+
return null;
|
|
2152
|
+
const ext = options.extension ?? (this.animated ? "gif" : "png");
|
|
2153
|
+
const size = options.size ? `?size=${options.size}` : "";
|
|
2154
|
+
return `${CDN_BASE}/emojis/${this.id}.${ext}${size}`;
|
|
2155
|
+
}
|
|
2156
|
+
toString() {
|
|
2157
|
+
return this.id !== "unicode" ? `<${this.animated ? "a" : ""}:${this.name}:${this.id}>` : this.name ?? "";
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
class AutoModerationRule extends BaseStructure {
|
|
2162
|
+
guildId;
|
|
2163
|
+
name;
|
|
2164
|
+
creatorId;
|
|
2165
|
+
eventType;
|
|
2166
|
+
triggerType;
|
|
2167
|
+
triggerMetadata;
|
|
2168
|
+
actions;
|
|
2169
|
+
enabled;
|
|
2170
|
+
exemptRoles;
|
|
2171
|
+
exemptChannels;
|
|
2172
|
+
constructor(data) {
|
|
2173
|
+
super(data.id);
|
|
2174
|
+
if (!/^\d{1,20}$/.test(data.guild_id))
|
|
2175
|
+
throw new TypeError("AutoModerationRule guild_id must be a valid snowflake.");
|
|
2176
|
+
this.guildId = data.guild_id;
|
|
2177
|
+
this.name = data.name;
|
|
2178
|
+
this.creatorId = data.creator_id;
|
|
2179
|
+
this.eventType = data.event_type;
|
|
2180
|
+
this.triggerType = data.trigger_type;
|
|
2181
|
+
this.triggerMetadata = data.trigger_metadata;
|
|
2182
|
+
this.actions = data.actions;
|
|
2183
|
+
this.enabled = data.enabled;
|
|
2184
|
+
this.exemptRoles = data.exempt_roles;
|
|
2185
|
+
this.exemptChannels = data.exempt_channels;
|
|
2186
|
+
}
|
|
2187
|
+
toJSON() {
|
|
2188
|
+
return {
|
|
2189
|
+
id: this.id,
|
|
2190
|
+
guildId: this.guildId,
|
|
2191
|
+
name: this.name,
|
|
2192
|
+
creatorId: this.creatorId,
|
|
2193
|
+
eventType: this.eventType,
|
|
2194
|
+
triggerType: this.triggerType,
|
|
2195
|
+
triggerMetadata: this.triggerMetadata,
|
|
2196
|
+
actions: this.actions,
|
|
2197
|
+
enabled: this.enabled,
|
|
2198
|
+
exemptRoles: this.exemptRoles,
|
|
2199
|
+
exemptChannels: this.exemptChannels
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
class WelcomeScreenChannel {
|
|
2205
|
+
channelId;
|
|
2206
|
+
description;
|
|
2207
|
+
emojiId;
|
|
2208
|
+
emojiName;
|
|
2209
|
+
constructor(data) {
|
|
2210
|
+
this.channelId = data.channel_id;
|
|
2211
|
+
this.description = data.description;
|
|
2212
|
+
this.emojiId = data.emoji_id;
|
|
2213
|
+
this.emojiName = data.emoji_name;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
class GuildWelcomeScreen {
|
|
2218
|
+
description;
|
|
2219
|
+
channels;
|
|
2220
|
+
constructor(data) {
|
|
2221
|
+
this.description = data.description;
|
|
2222
|
+
this.channels = data.welcome_channels.map((c) => new WelcomeScreenChannel(c));
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
class OnboardingPromptOption extends BaseStructure {
|
|
2227
|
+
channelIds;
|
|
2228
|
+
roleIds;
|
|
2229
|
+
emoji;
|
|
2230
|
+
title;
|
|
2231
|
+
description;
|
|
2232
|
+
constructor(data) {
|
|
2233
|
+
super(data.id);
|
|
2234
|
+
this.channelIds = data.channel_ids;
|
|
2235
|
+
this.roleIds = data.role_ids;
|
|
2236
|
+
this.emoji = data.emoji;
|
|
2237
|
+
this.title = data.title;
|
|
2238
|
+
this.description = data.description;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
class OnboardingPrompt extends BaseStructure {
|
|
2243
|
+
type;
|
|
2244
|
+
options;
|
|
2245
|
+
title;
|
|
2246
|
+
singleSelect;
|
|
2247
|
+
required;
|
|
2248
|
+
inOnboarding;
|
|
2249
|
+
constructor(data) {
|
|
2250
|
+
super(data.id);
|
|
2251
|
+
this.type = data.type;
|
|
2252
|
+
this.options = data.options.map((o) => new OnboardingPromptOption(o));
|
|
2253
|
+
this.title = data.title;
|
|
2254
|
+
this.singleSelect = data.single_select;
|
|
2255
|
+
this.required = data.required;
|
|
2256
|
+
this.inOnboarding = data.in_onboarding;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
class GuildOnboarding extends BaseStructure {
|
|
2261
|
+
prompts;
|
|
2262
|
+
defaultChannelIds;
|
|
2263
|
+
enabled;
|
|
2264
|
+
mode;
|
|
2265
|
+
constructor(data) {
|
|
2266
|
+
super(data.guild_id);
|
|
2267
|
+
this.prompts = data.prompts.map((p) => new OnboardingPrompt(p));
|
|
2268
|
+
this.defaultChannelIds = data.default_channel_ids;
|
|
2269
|
+
this.enabled = data.enabled;
|
|
2270
|
+
this.mode = data.mode;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
1715
2273
|
// packages/structures/src/interactions.ts
|
|
1716
2274
|
var InteractionType = {
|
|
1717
2275
|
Ping: 1,
|
|
@@ -2002,8 +2560,7 @@ class CommandInteraction extends Interaction {
|
|
|
2002
2560
|
super(client, data);
|
|
2003
2561
|
const rawOptions = data.data?.options ?? [];
|
|
2004
2562
|
const resolved = data.data?.resolved ?? {};
|
|
2005
|
-
|
|
2006
|
-
this.options = new CommandOptions(sub?.options ?? rawOptions, resolved);
|
|
2563
|
+
this.options = new CommandOptions(rawOptions, resolved);
|
|
2007
2564
|
}
|
|
2008
2565
|
}
|
|
2009
2566
|
|
|
@@ -2233,8 +2790,38 @@ class Embed {
|
|
|
2233
2790
|
return new Embed(data);
|
|
2234
2791
|
}
|
|
2235
2792
|
}
|
|
2793
|
+
// packages/structures/src/audit-log.ts
|
|
2794
|
+
class AuditLogEntry extends BaseStructure {
|
|
2795
|
+
targetId;
|
|
2796
|
+
userId;
|
|
2797
|
+
actionType;
|
|
2798
|
+
changes;
|
|
2799
|
+
options;
|
|
2800
|
+
reason;
|
|
2801
|
+
constructor(data) {
|
|
2802
|
+
super(data.id);
|
|
2803
|
+
this.targetId = data.target_id;
|
|
2804
|
+
this.userId = data.user_id;
|
|
2805
|
+
this.actionType = data.action_type;
|
|
2806
|
+
this.changes = data.changes ?? [];
|
|
2807
|
+
this.options = data.options ?? null;
|
|
2808
|
+
this.reason = data.reason ?? null;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
class AuditLog {
|
|
2813
|
+
entries;
|
|
2814
|
+
constructor(data) {
|
|
2815
|
+
this.entries = new Map;
|
|
2816
|
+
for (const entryData of data.audit_log_entries) {
|
|
2817
|
+
this.entries.set(entryData.id, new AuditLogEntry(entryData));
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2236
2821
|
|
|
2237
2822
|
// packages/structures/src/index.ts
|
|
2823
|
+
var NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);
|
|
2824
|
+
|
|
2238
2825
|
class Message extends BaseStructure {
|
|
2239
2826
|
content;
|
|
2240
2827
|
author;
|
|
@@ -2280,6 +2867,12 @@ class Message extends BaseStructure {
|
|
|
2280
2867
|
get embedsSuppressed() {
|
|
2281
2868
|
return (this.flags & 4) !== 0;
|
|
2282
2869
|
}
|
|
2870
|
+
get createdTimestamp() {
|
|
2871
|
+
return this.timestamp.getTime();
|
|
2872
|
+
}
|
|
2873
|
+
get createdAt() {
|
|
2874
|
+
return this.timestamp;
|
|
2875
|
+
}
|
|
2283
2876
|
edit(options) {
|
|
2284
2877
|
if (!this.#context)
|
|
2285
2878
|
throw new Error("This message is not attached to a client.");
|
|
@@ -2334,6 +2927,51 @@ class Message extends BaseStructure {
|
|
|
2334
2927
|
throw new Error("This message is not attached to a client.");
|
|
2335
2928
|
return this.#context.unpinMessage(this.channelId, this.id);
|
|
2336
2929
|
}
|
|
2930
|
+
get isEphemeral() {
|
|
2931
|
+
return (this.flags & 64) !== 0;
|
|
2932
|
+
}
|
|
2933
|
+
get isPinned() {
|
|
2934
|
+
return this.pinned;
|
|
2935
|
+
}
|
|
2936
|
+
get isSystemMessage() {
|
|
2937
|
+
return !NON_SYSTEM_MESSAGE_TYPES.has(this.type);
|
|
2938
|
+
}
|
|
2939
|
+
get isCrosspost() {
|
|
2940
|
+
return (this.flags & 2) !== 0;
|
|
2941
|
+
}
|
|
2942
|
+
get hasAttachments() {
|
|
2943
|
+
return this.attachments.length > 0;
|
|
2944
|
+
}
|
|
2945
|
+
get hasEmbeds() {
|
|
2946
|
+
return this.embeds.length > 0;
|
|
2947
|
+
}
|
|
2948
|
+
get hasComponents() {
|
|
2949
|
+
return this.components.length > 0;
|
|
2950
|
+
}
|
|
2951
|
+
suppressEmbeds() {
|
|
2952
|
+
return this.edit({ flags: this.flags | 4 });
|
|
2953
|
+
}
|
|
2954
|
+
toJSON() {
|
|
2955
|
+
return {
|
|
2956
|
+
id: this.id,
|
|
2957
|
+
content: this.content,
|
|
2958
|
+
channelId: this.channelId,
|
|
2959
|
+
guildId: this.guildId,
|
|
2960
|
+
flags: this.flags,
|
|
2961
|
+
pinned: this.pinned,
|
|
2962
|
+
mentionEveryone: this.mentionEveryone,
|
|
2963
|
+
type: this.type,
|
|
2964
|
+
timestamp: this.timestamp.toISOString(),
|
|
2965
|
+
author: { id: this.author.id, username: this.author.username },
|
|
2966
|
+
attachments: [...this.attachments],
|
|
2967
|
+
embeds: [...this.embeds],
|
|
2968
|
+
components: [...this.components],
|
|
2969
|
+
mentions: this.mentions.map((u) => u.id),
|
|
2970
|
+
mentionRoles: [...this.mentionRoles],
|
|
2971
|
+
reference: this.reference,
|
|
2972
|
+
referencedMessage: this.referencedMessage ? this.referencedMessage.id : this.referencedMessage
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2337
2975
|
}
|
|
2338
2976
|
|
|
2339
2977
|
// packages/managers/src/message.ts
|
|
@@ -2425,6 +3063,11 @@ class UserManager extends ResourceManager {
|
|
|
2425
3063
|
}
|
|
2426
3064
|
}
|
|
2427
3065
|
// packages/managers/src/guild.ts
|
|
3066
|
+
function withQuery(path, params) {
|
|
3067
|
+
const suffix = params.toString();
|
|
3068
|
+
return suffix ? `${path}?${suffix}` : path;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
2428
3071
|
class GuildManager extends ResourceManager {
|
|
2429
3072
|
#rest;
|
|
2430
3073
|
constructor(rest) {
|
|
@@ -2454,7 +3097,101 @@ class GuildManager extends ResourceManager {
|
|
|
2454
3097
|
async fetchInvites(id) {
|
|
2455
3098
|
return this.#rest.get(Routes.guildInvites(id));
|
|
2456
3099
|
}
|
|
3100
|
+
async fetchAuditLog(guildId, options = {}) {
|
|
3101
|
+
const params = new URLSearchParams;
|
|
3102
|
+
if (options.userId)
|
|
3103
|
+
params.set("user_id", options.userId);
|
|
3104
|
+
if (options.actionType !== undefined)
|
|
3105
|
+
params.set("action_type", String(options.actionType));
|
|
3106
|
+
if (options.before)
|
|
3107
|
+
params.set("before", options.before);
|
|
3108
|
+
if (options.after)
|
|
3109
|
+
params.set("after", options.after);
|
|
3110
|
+
if (options.limit !== undefined)
|
|
3111
|
+
params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
|
|
3112
|
+
return this.#rest.get(withQuery(Routes.guildAuditLog(guildId), params));
|
|
3113
|
+
}
|
|
3114
|
+
async fetchMembers(guildId, options = {}) {
|
|
3115
|
+
const params = new URLSearchParams;
|
|
3116
|
+
if (options.limit !== undefined)
|
|
3117
|
+
params.set("limit", String(Math.min(1000, Math.max(1, options.limit))));
|
|
3118
|
+
if (options.after)
|
|
3119
|
+
params.set("after", options.after);
|
|
3120
|
+
return this.#rest.get(withQuery(Routes.guildMembers(guildId), params));
|
|
3121
|
+
}
|
|
3122
|
+
async fetchAutoModerationRules(guildId) {
|
|
3123
|
+
const rules = await this.#rest.get(Routes.guildAutoModerationRules(guildId));
|
|
3124
|
+
return rules.map((r) => new AutoModerationRule(r));
|
|
3125
|
+
}
|
|
3126
|
+
async fetchAutoModerationRule(guildId, ruleId) {
|
|
3127
|
+
const rule = await this.#rest.get(Routes.guildAutoModerationRule(guildId, ruleId));
|
|
3128
|
+
return new AutoModerationRule(rule);
|
|
3129
|
+
}
|
|
3130
|
+
async createAutoModerationRule(guildId, options) {
|
|
3131
|
+
const rule = await this.#rest.post(Routes.guildAutoModerationRules(guildId), options);
|
|
3132
|
+
return new AutoModerationRule(rule);
|
|
3133
|
+
}
|
|
3134
|
+
async editAutoModerationRule(guildId, ruleId, options) {
|
|
3135
|
+
const rule = await this.#rest.patch(Routes.guildAutoModerationRule(guildId, ruleId), options);
|
|
3136
|
+
return new AutoModerationRule(rule);
|
|
3137
|
+
}
|
|
3138
|
+
async deleteAutoModerationRule(guildId, ruleId) {
|
|
3139
|
+
await this.#rest.delete(Routes.guildAutoModerationRule(guildId, ruleId));
|
|
3140
|
+
}
|
|
2457
3141
|
}
|
|
3142
|
+
var AuditLogEvent = {
|
|
3143
|
+
GuildUpdate: 1,
|
|
3144
|
+
ChannelCreate: 10,
|
|
3145
|
+
ChannelUpdate: 11,
|
|
3146
|
+
ChannelDelete: 12,
|
|
3147
|
+
ChannelOverwriteCreate: 13,
|
|
3148
|
+
ChannelOverwriteUpdate: 14,
|
|
3149
|
+
ChannelOverwriteDelete: 15,
|
|
3150
|
+
MemberKick: 20,
|
|
3151
|
+
MemberPrune: 21,
|
|
3152
|
+
MemberBanAdd: 22,
|
|
3153
|
+
MemberBanRemove: 23,
|
|
3154
|
+
MemberUpdate: 24,
|
|
3155
|
+
MemberRoleUpdate: 25,
|
|
3156
|
+
MemberMove: 26,
|
|
3157
|
+
MemberDisconnect: 27,
|
|
3158
|
+
BotAdd: 28,
|
|
3159
|
+
RoleCreate: 30,
|
|
3160
|
+
RoleUpdate: 31,
|
|
3161
|
+
RoleDelete: 32,
|
|
3162
|
+
InviteCreate: 40,
|
|
3163
|
+
InviteUpdate: 41,
|
|
3164
|
+
InviteDelete: 42,
|
|
3165
|
+
WebhookCreate: 50,
|
|
3166
|
+
WebhookUpdate: 51,
|
|
3167
|
+
WebhookDelete: 52,
|
|
3168
|
+
EmojiCreate: 60,
|
|
3169
|
+
EmojiUpdate: 61,
|
|
3170
|
+
EmojiDelete: 62,
|
|
3171
|
+
MessageDelete: 72,
|
|
3172
|
+
MessageBulkDelete: 73,
|
|
3173
|
+
MessagePin: 74,
|
|
3174
|
+
MessageUnpin: 75,
|
|
3175
|
+
IntegrationCreate: 80,
|
|
3176
|
+
IntegrationUpdate: 81,
|
|
3177
|
+
IntegrationDelete: 82,
|
|
3178
|
+
StageInstanceCreate: 83,
|
|
3179
|
+
StageInstanceUpdate: 84,
|
|
3180
|
+
StageInstanceDelete: 85,
|
|
3181
|
+
StickerCreate: 90,
|
|
3182
|
+
StickerUpdate: 91,
|
|
3183
|
+
StickerDelete: 92,
|
|
3184
|
+
ScheduledEventCreate: 100,
|
|
3185
|
+
ScheduledEventUpdate: 101,
|
|
3186
|
+
ScheduledEventDelete: 102,
|
|
3187
|
+
ThreadCreate: 110,
|
|
3188
|
+
ThreadUpdate: 111,
|
|
3189
|
+
ThreadDelete: 112,
|
|
3190
|
+
AutoModerationRuleCreate: 140,
|
|
3191
|
+
AutoModerationRuleUpdate: 141,
|
|
3192
|
+
AutoModerationRuleDelete: 142,
|
|
3193
|
+
AutoModerationBlockMessage: 143
|
|
3194
|
+
};
|
|
2458
3195
|
// packages/managers/src/role.ts
|
|
2459
3196
|
class RoleManager extends ResourceManager {
|
|
2460
3197
|
#rest;
|
|
@@ -2575,6 +3312,44 @@ class ApplicationCommandManager {
|
|
|
2575
3312
|
await this.#rest.delete(Routes.guildApplicationCommand(this.#applicationId, guildId, commandId));
|
|
2576
3313
|
}
|
|
2577
3314
|
}
|
|
3315
|
+
// packages/managers/src/emoji.ts
|
|
3316
|
+
class EmojiManager extends Manager {
|
|
3317
|
+
#rest;
|
|
3318
|
+
#guildId;
|
|
3319
|
+
constructor(rest, guildId) {
|
|
3320
|
+
super();
|
|
3321
|
+
this.#rest = rest;
|
|
3322
|
+
this.#guildId = guildId;
|
|
3323
|
+
}
|
|
3324
|
+
async fetch(emojiId) {
|
|
3325
|
+
return this.upsert(await this.#rest.get(Routes.guildEmoji(this.#guildId, emojiId)));
|
|
3326
|
+
}
|
|
3327
|
+
async fetchAll() {
|
|
3328
|
+
const data = await this.#rest.get(Routes.guildEmoji(this.#guildId));
|
|
3329
|
+
return data.map((item) => this.upsert(item));
|
|
3330
|
+
}
|
|
3331
|
+
async create(options) {
|
|
3332
|
+
return this.upsert(await this.#rest.post(Routes.guildEmoji(this.#guildId), options));
|
|
3333
|
+
}
|
|
3334
|
+
async edit(emojiId, options) {
|
|
3335
|
+
return this.upsert(await this.#rest.patch(Routes.guildEmoji(this.#guildId, emojiId), options));
|
|
3336
|
+
}
|
|
3337
|
+
async deleteEmoji(emojiId) {
|
|
3338
|
+
await this.#rest.delete(Routes.guildEmoji(this.#guildId, emojiId));
|
|
3339
|
+
this.delete(emojiId);
|
|
3340
|
+
}
|
|
3341
|
+
upsert(data) {
|
|
3342
|
+
const key = data.id != null ? data.id : `unicode:${data.name ?? ""}`;
|
|
3343
|
+
const existing = this.get(key);
|
|
3344
|
+
const emoji = new Emoji(data);
|
|
3345
|
+
if (existing) {
|
|
3346
|
+
Object.assign(existing, emoji);
|
|
3347
|
+
return existing;
|
|
3348
|
+
}
|
|
3349
|
+
this.set(key, emoji);
|
|
3350
|
+
return emoji;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
2578
3353
|
|
|
2579
3354
|
// packages/managers/src/index.ts
|
|
2580
3355
|
class ChannelManager extends Manager {
|
|
@@ -2637,8 +3412,11 @@ class ChannelManager extends Manager {
|
|
|
2637
3412
|
}
|
|
2638
3413
|
async deleteChannel(channelId) {
|
|
2639
3414
|
await this.#rest.delete(Routes.channel(channelId));
|
|
3415
|
+
this.delete(channelId);
|
|
3416
|
+
}
|
|
3417
|
+
delete(channelId) {
|
|
2640
3418
|
this.#messageManagers.delete(channelId);
|
|
2641
|
-
super.delete(channelId);
|
|
3419
|
+
return super.delete(channelId);
|
|
2642
3420
|
}
|
|
2643
3421
|
send(channelId, options) {
|
|
2644
3422
|
return this.messages(channelId).send(options);
|
|
@@ -2683,6 +3461,8 @@ class ChannelManager extends Manager {
|
|
|
2683
3461
|
}
|
|
2684
3462
|
async bulkDeleteMessages(channelId, messageIds) {
|
|
2685
3463
|
const ids = [...messageIds];
|
|
3464
|
+
if (ids.length < 2 || ids.length > 100)
|
|
3465
|
+
throw new RangeError("bulkDelete requires between 2 and 100 message IDs.");
|
|
2686
3466
|
await this.#rest.post(Routes.channelBulkDelete(channelId), {
|
|
2687
3467
|
messages: ids
|
|
2688
3468
|
});
|
|
@@ -2690,7 +3470,7 @@ class ChannelManager extends Manager {
|
|
|
2690
3470
|
this.messages(channelId).delete(id);
|
|
2691
3471
|
}
|
|
2692
3472
|
async addReaction(channelId, messageId, emoji) {
|
|
2693
|
-
await this.#rest.put(Routes.messageReactions(channelId, messageId, emoji));
|
|
3473
|
+
await this.#rest.put(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
|
|
2694
3474
|
}
|
|
2695
3475
|
async fetchReactions(channelId, messageId, emoji, options = {}) {
|
|
2696
3476
|
const params = new URLSearchParams;
|
|
@@ -2724,9 +3504,21 @@ class ChannelManager extends Manager {
|
|
|
2724
3504
|
createThreadFromMessage(channelId, messageId, options) {
|
|
2725
3505
|
return this.threads(channelId).createFromMessage(messageId, options);
|
|
2726
3506
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
3507
|
+
async fetchPage(channelId, options = {}) {
|
|
3508
|
+
const limit = Math.min(100, Math.max(1, options.limit ?? 50));
|
|
3509
|
+
const messages = await this.fetchMessages(channelId, {
|
|
3510
|
+
...options,
|
|
3511
|
+
limit
|
|
3512
|
+
});
|
|
3513
|
+
return {
|
|
3514
|
+
messages,
|
|
3515
|
+
before: messages[0]?.id,
|
|
3516
|
+
after: messages[messages.length - 1]?.id,
|
|
3517
|
+
hasMore: messages.length === limit
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
async bulkDelete(channelId, messageIds) {
|
|
3521
|
+
return this.bulkDeleteMessages(channelId, messageIds);
|
|
2730
3522
|
}
|
|
2731
3523
|
clear() {
|
|
2732
3524
|
this.#messageManagers.clear();
|
|
@@ -2751,6 +3543,22 @@ var GatewayOpcodes = {
|
|
|
2751
3543
|
Hello: 10,
|
|
2752
3544
|
HeartbeatAck: 11
|
|
2753
3545
|
};
|
|
3546
|
+
var GatewayCloseCodes = {
|
|
3547
|
+
UnknownError: 4000,
|
|
3548
|
+
UnknownOpcode: 4001,
|
|
3549
|
+
DecodeError: 4002,
|
|
3550
|
+
NotAuthenticated: 4003,
|
|
3551
|
+
AuthenticationFailed: 4004,
|
|
3552
|
+
AlreadyAuthenticated: 4005,
|
|
3553
|
+
InvalidSeq: 4007,
|
|
3554
|
+
RateLimited: 4008,
|
|
3555
|
+
SessionTimedOut: 4009,
|
|
3556
|
+
InvalidShard: 4010,
|
|
3557
|
+
ShardingRequired: 4011,
|
|
3558
|
+
InvalidAPIVersion: 4012,
|
|
3559
|
+
InvalidIntents: 4013,
|
|
3560
|
+
DisallowedIntents: 4014
|
|
3561
|
+
};
|
|
2754
3562
|
var GatewayState;
|
|
2755
3563
|
((GatewayState2) => {
|
|
2756
3564
|
GatewayState2["Connect"] = "CONNECT";
|
|
@@ -2763,7 +3571,6 @@ var GatewayState;
|
|
|
2763
3571
|
GatewayState2["Reconnect"] = "RECONNECT";
|
|
2764
3572
|
GatewayState2["Closed"] = "CLOSED";
|
|
2765
3573
|
})(GatewayState ||= {});
|
|
2766
|
-
|
|
2767
3574
|
class GatewayError extends Error {
|
|
2768
3575
|
code;
|
|
2769
3576
|
constructor(message, code, options) {
|
|
@@ -2797,6 +3604,8 @@ class Gateway {
|
|
|
2797
3604
|
#rejectConnect;
|
|
2798
3605
|
#listeners = new Map;
|
|
2799
3606
|
#sendTimestamps = [];
|
|
3607
|
+
ping = -1;
|
|
3608
|
+
#decompressor;
|
|
2800
3609
|
constructor(options) {
|
|
2801
3610
|
if (!options.token?.trim())
|
|
2802
3611
|
throw new TypeError("A Gateway token is required.");
|
|
@@ -2817,7 +3626,12 @@ class Gateway {
|
|
|
2817
3626
|
browser: "Discord Android",
|
|
2818
3627
|
device: "Discord Android"
|
|
2819
3628
|
},
|
|
2820
|
-
presence: {
|
|
3629
|
+
presence: {
|
|
3630
|
+
status: "online",
|
|
3631
|
+
activities: [],
|
|
3632
|
+
afk: false,
|
|
3633
|
+
since: null
|
|
3634
|
+
},
|
|
2821
3635
|
...options
|
|
2822
3636
|
};
|
|
2823
3637
|
if (this.#options.shardId < 0 || this.#options.shardId >= this.#options.shardCount)
|
|
@@ -2832,10 +3646,13 @@ class Gateway {
|
|
|
2832
3646
|
return this.#connectPromise;
|
|
2833
3647
|
this.#closed = false;
|
|
2834
3648
|
this.#setState("CONNECT" /* Connect */);
|
|
3649
|
+
const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}&compress=zlib-stream` : url;
|
|
3650
|
+
if (this.#options.compress)
|
|
3651
|
+
this.#initDecompressor();
|
|
2835
3652
|
this.#connectPromise = new Promise((resolve, reject) => {
|
|
2836
3653
|
this.#resolveConnect = resolve;
|
|
2837
3654
|
this.#rejectConnect = reject;
|
|
2838
|
-
this.#open(
|
|
3655
|
+
this.#open(connectURL);
|
|
2839
3656
|
});
|
|
2840
3657
|
return this.#connectPromise;
|
|
2841
3658
|
}
|
|
@@ -2871,12 +3688,15 @@ class Gateway {
|
|
|
2871
3688
|
send(payload) {
|
|
2872
3689
|
if (!payload || !Number.isInteger(payload.op))
|
|
2873
3690
|
throw new TypeError("Gateway payload must contain an integer opcode.");
|
|
3691
|
+
return this.#dispatch(payload, false);
|
|
3692
|
+
}
|
|
3693
|
+
#dispatch(payload, privileged) {
|
|
2874
3694
|
if (this.#ws?.readyState !== WebSocket.OPEN)
|
|
2875
3695
|
return false;
|
|
2876
3696
|
const now = Date.now();
|
|
2877
3697
|
while (this.#sendTimestamps.length && now - this.#sendTimestamps[0] >= 60000)
|
|
2878
3698
|
this.#sendTimestamps.shift();
|
|
2879
|
-
if (this.#sendTimestamps.length >= 115) {
|
|
3699
|
+
if (!privileged && this.#sendTimestamps.length >= 115) {
|
|
2880
3700
|
this.#emitError(new GatewayError("Gateway send rate budget exhausted."));
|
|
2881
3701
|
return false;
|
|
2882
3702
|
}
|
|
@@ -2947,7 +3767,11 @@ class Gateway {
|
|
|
2947
3767
|
ws.addEventListener("message", (event) => {
|
|
2948
3768
|
this.#lastMessageAt = Date.now();
|
|
2949
3769
|
this.#zombieReported = false;
|
|
2950
|
-
this.#
|
|
3770
|
+
if (this.#options.compress && event.data instanceof ArrayBuffer) {
|
|
3771
|
+
this.#decompress(new Uint8Array(event.data)).then((text) => this.#message(ws, text)).catch((err) => this.#emitError(err));
|
|
3772
|
+
} else {
|
|
3773
|
+
this.#message(ws, String(event.data));
|
|
3774
|
+
}
|
|
2951
3775
|
});
|
|
2952
3776
|
ws.addEventListener("close", (event) => this.#close(ws, event.code));
|
|
2953
3777
|
ws.addEventListener("error", () => this.#emitError(new GatewayError("Gateway WebSocket error")));
|
|
@@ -2955,7 +3779,7 @@ class Gateway {
|
|
|
2955
3779
|
#startZombieDetection() {
|
|
2956
3780
|
if (this.#zombieTimer)
|
|
2957
3781
|
clearInterval(this.#zombieTimer);
|
|
2958
|
-
const interval = Math.max(1, Math.min(
|
|
3782
|
+
const interval = Math.max(1, Math.min(1000, this.#options.heartbeatAckTimeout, this.#options.zombieTimeout / 2));
|
|
2959
3783
|
this.#zombieTimer = setInterval(() => {
|
|
2960
3784
|
if (this.#closed || this.#ws?.readyState !== WebSocket.OPEN || this.#lastMessageAt === 0)
|
|
2961
3785
|
return;
|
|
@@ -3006,18 +3830,24 @@ class Gateway {
|
|
|
3006
3830
|
break;
|
|
3007
3831
|
case GatewayOpcodes.HeartbeatAck:
|
|
3008
3832
|
this.#heartbeatACK = true;
|
|
3833
|
+
this.ping = Date.now() - this.#heartbeatSentAt;
|
|
3009
3834
|
this.#clearHeartbeatAckTimer();
|
|
3010
3835
|
this.#emit("heartbeatAck", payload.d);
|
|
3011
3836
|
break;
|
|
3012
3837
|
case GatewayOpcodes.Reconnect:
|
|
3013
3838
|
ws.close(1001, "Server requested reconnect");
|
|
3014
3839
|
break;
|
|
3015
|
-
case GatewayOpcodes.InvalidSession:
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3840
|
+
case GatewayOpcodes.InvalidSession: {
|
|
3841
|
+
const resumable = payload.d === true;
|
|
3842
|
+
if (!resumable) {
|
|
3843
|
+
this.#sessionId = undefined;
|
|
3844
|
+
this.#sequence = null;
|
|
3845
|
+
this.#resumeURL = undefined;
|
|
3846
|
+
}
|
|
3847
|
+
this.#emit("invalidSession", resumable);
|
|
3848
|
+
ws.close(resumable ? GatewayCloseCodes.UnknownError : 1000, resumable ? "Invalid session (resumable)" : "Invalid session");
|
|
3020
3849
|
break;
|
|
3850
|
+
}
|
|
3021
3851
|
}
|
|
3022
3852
|
}
|
|
3023
3853
|
#handleDispatch(event, data) {
|
|
@@ -3032,6 +3862,10 @@ class Gateway {
|
|
|
3032
3862
|
this.#attempt = 0;
|
|
3033
3863
|
this.#setState("READY" /* Ready */);
|
|
3034
3864
|
this.#emit("ready", data);
|
|
3865
|
+
} else if (event === "RESUMED") {
|
|
3866
|
+
this.#attempt = 0;
|
|
3867
|
+
this.#setState("READY" /* Ready */);
|
|
3868
|
+
this.#emit("resumed", data);
|
|
3035
3869
|
}
|
|
3036
3870
|
this.#emit("RAW", { event: event ?? "UNKNOWN", data });
|
|
3037
3871
|
if (this.#closed)
|
|
@@ -3109,12 +3943,12 @@ class Gateway {
|
|
|
3109
3943
|
#sendHeartbeat() {
|
|
3110
3944
|
this.#heartbeatACK = false;
|
|
3111
3945
|
this.#heartbeatSentAt = Date.now();
|
|
3112
|
-
if (!this
|
|
3946
|
+
if (!this.#dispatch({
|
|
3113
3947
|
op: GatewayOpcodes.Heartbeat,
|
|
3114
3948
|
d: this.#sequence,
|
|
3115
3949
|
s: null,
|
|
3116
3950
|
t: null
|
|
3117
|
-
})) {
|
|
3951
|
+
}, true)) {
|
|
3118
3952
|
this.#emitError(new GatewayError("Unable to send Gateway heartbeat because the WebSocket is not open."));
|
|
3119
3953
|
return;
|
|
3120
3954
|
}
|
|
@@ -3147,14 +3981,23 @@ class Gateway {
|
|
|
3147
3981
|
if (action === "identify") {
|
|
3148
3982
|
this.#sequence = null;
|
|
3149
3983
|
this.#sessionId = undefined;
|
|
3984
|
+
this.#resumeURL = undefined;
|
|
3150
3985
|
}
|
|
3151
3986
|
this.#setState("RECONNECT" /* Reconnect */);
|
|
3152
3987
|
this.#scheduleReconnect();
|
|
3153
3988
|
}
|
|
3154
3989
|
#closeAction(code) {
|
|
3155
|
-
|
|
3990
|
+
const fatal = [
|
|
3991
|
+
GatewayCloseCodes.AuthenticationFailed,
|
|
3992
|
+
GatewayCloseCodes.InvalidShard,
|
|
3993
|
+
GatewayCloseCodes.ShardingRequired,
|
|
3994
|
+
GatewayCloseCodes.InvalidAPIVersion,
|
|
3995
|
+
GatewayCloseCodes.InvalidIntents,
|
|
3996
|
+
GatewayCloseCodes.DisallowedIntents
|
|
3997
|
+
];
|
|
3998
|
+
if (fatal.includes(code))
|
|
3156
3999
|
return "stop";
|
|
3157
|
-
if (
|
|
4000
|
+
if (code === GatewayCloseCodes.InvalidSeq || code === GatewayCloseCodes.SessionTimedOut)
|
|
3158
4001
|
return "identify";
|
|
3159
4002
|
return this.#sessionId && this.#sequence !== null ? "resume" : "identify";
|
|
3160
4003
|
}
|
|
@@ -3167,7 +4010,7 @@ class Gateway {
|
|
|
3167
4010
|
return;
|
|
3168
4011
|
}
|
|
3169
4012
|
const delay = Math.min(this.#options.reconnectMaxDelay, this.#options.reconnectBaseDelay * 2 ** this.#attempt++);
|
|
3170
|
-
const jitter = Math.random() * Math.
|
|
4013
|
+
const jitter = Math.random() * Math.max(1, delay * 0.25);
|
|
3171
4014
|
this.#reconnectTimer = setTimeout(() => {
|
|
3172
4015
|
this.#reconnectTimer = undefined;
|
|
3173
4016
|
this.#open(this.#resumeURL ?? "wss://gateway.discord.gg/?v=10&encoding=json");
|
|
@@ -3237,10 +4080,41 @@ class Gateway {
|
|
|
3237
4080
|
this.state = next;
|
|
3238
4081
|
this.#emit("stateChange", { previous, next });
|
|
3239
4082
|
}
|
|
4083
|
+
#initDecompressor() {
|
|
4084
|
+
const ds = new DecompressionStream("deflate-raw");
|
|
4085
|
+
this.#decompressor = {
|
|
4086
|
+
writer: ds.writable.getWriter(),
|
|
4087
|
+
reader: ds.readable.getReader()
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
async#decompress(chunk) {
|
|
4091
|
+
if (!this.#decompressor)
|
|
4092
|
+
this.#initDecompressor();
|
|
4093
|
+
const { writer, reader } = this.#decompressor;
|
|
4094
|
+
await writer.write(chunk);
|
|
4095
|
+
const parts = [];
|
|
4096
|
+
while (true) {
|
|
4097
|
+
const { done, value } = await Promise.race([
|
|
4098
|
+
reader.read(),
|
|
4099
|
+
new Promise((resolve) => setTimeout(() => resolve({ done: true, value: undefined }), 50))
|
|
4100
|
+
]);
|
|
4101
|
+
if (done || !value)
|
|
4102
|
+
break;
|
|
4103
|
+
parts.push(value);
|
|
4104
|
+
}
|
|
4105
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
4106
|
+
const merged = new Uint8Array(total);
|
|
4107
|
+
let offset = 0;
|
|
4108
|
+
for (const p of parts) {
|
|
4109
|
+
merged.set(p, offset);
|
|
4110
|
+
offset += p.length;
|
|
4111
|
+
}
|
|
4112
|
+
return new TextDecoder().decode(merged);
|
|
4113
|
+
}
|
|
3240
4114
|
}
|
|
3241
4115
|
|
|
3242
4116
|
// packages/core/src/index.ts
|
|
3243
|
-
class
|
|
4117
|
+
class EventEmitter2 {
|
|
3244
4118
|
#listeners = new Map;
|
|
3245
4119
|
on(event, listener) {
|
|
3246
4120
|
if (typeof listener !== "function")
|
|
@@ -3262,6 +4136,9 @@ class EventEmitter {
|
|
|
3262
4136
|
this.#listeners.get(event)?.delete(listener);
|
|
3263
4137
|
return this;
|
|
3264
4138
|
}
|
|
4139
|
+
removeListener(event, listener) {
|
|
4140
|
+
return this.off(event, listener);
|
|
4141
|
+
}
|
|
3265
4142
|
removeAllListeners(event) {
|
|
3266
4143
|
if (event === undefined)
|
|
3267
4144
|
this.#listeners.clear();
|
|
@@ -3298,7 +4175,7 @@ class EventEmitter {
|
|
|
3298
4175
|
}
|
|
3299
4176
|
}
|
|
3300
4177
|
|
|
3301
|
-
class Client extends
|
|
4178
|
+
class Client extends EventEmitter2 {
|
|
3302
4179
|
options;
|
|
3303
4180
|
rest;
|
|
3304
4181
|
users;
|
|
@@ -3314,14 +4191,26 @@ class Client extends EventEmitter {
|
|
|
3314
4191
|
user;
|
|
3315
4192
|
readyAt;
|
|
3316
4193
|
state = "idle";
|
|
4194
|
+
get token() {
|
|
4195
|
+
return this.state === "destroyed" ? null : this.options.token ?? null;
|
|
4196
|
+
}
|
|
3317
4197
|
get uptime() {
|
|
3318
4198
|
return this.readyAt ? Date.now() - this.readyAt.getTime() : null;
|
|
3319
4199
|
}
|
|
4200
|
+
get ping() {
|
|
4201
|
+
return this.#gateway.ping;
|
|
4202
|
+
}
|
|
3320
4203
|
isReady() {
|
|
3321
4204
|
return this.state === "ready";
|
|
3322
4205
|
}
|
|
3323
4206
|
setPresence(data) {
|
|
3324
|
-
return this.#gateway.setPresence
|
|
4207
|
+
return this.#gateway.setPresence(data);
|
|
4208
|
+
}
|
|
4209
|
+
setVoiceState(data) {
|
|
4210
|
+
return this.#gateway.setVoiceState(data);
|
|
4211
|
+
}
|
|
4212
|
+
requestGuildMembers(data) {
|
|
4213
|
+
return this.#gateway.requestGuildMembers(data);
|
|
3325
4214
|
}
|
|
3326
4215
|
#gateway;
|
|
3327
4216
|
#resourceContext;
|
|
@@ -3365,6 +4254,13 @@ class Client extends EventEmitter {
|
|
|
3365
4254
|
this.application.commands = new ApplicationCommandManager(this.rest, appId);
|
|
3366
4255
|
this.emit("ready" /* Ready */, this.user);
|
|
3367
4256
|
});
|
|
4257
|
+
this.#gateway.on("RESUMED", () => {
|
|
4258
|
+
this.state = "ready";
|
|
4259
|
+
this.emit("resumed" /* Resumed */);
|
|
4260
|
+
});
|
|
4261
|
+
this.#gateway.on("invalidSession", (isRecoverable) => {
|
|
4262
|
+
this.emit("invalidSession" /* InvalidSession */, isRecoverable);
|
|
4263
|
+
});
|
|
3368
4264
|
this.#gateway.on("open", () => this.emit("open" /* Open */));
|
|
3369
4265
|
this.#gateway.on("close", (data) => {
|
|
3370
4266
|
if (this.state !== "destroyed")
|
|
@@ -3397,6 +4293,7 @@ class Client extends EventEmitter {
|
|
|
3397
4293
|
this.emit("guildUnavailable" /* GuildUnavailable */, payload);
|
|
3398
4294
|
return;
|
|
3399
4295
|
}
|
|
4296
|
+
const wasCached = this.guilds.has(payload.id);
|
|
3400
4297
|
const guild = new Guild(payload);
|
|
3401
4298
|
this.guilds.set(guild.id, guild);
|
|
3402
4299
|
for (const member of payload.members ?? [])
|
|
@@ -3406,7 +4303,7 @@ class Client extends EventEmitter {
|
|
|
3406
4303
|
...payload.threads ?? []
|
|
3407
4304
|
])
|
|
3408
4305
|
this.channels.set(channelData.id, new Channel(channelData, this.#resourceContext));
|
|
3409
|
-
this.emit("guildCreate" /* GuildCreate */, payload);
|
|
4306
|
+
this.emit(wasCached ? "guildAvailable" /* GuildAvailable */ : "guildCreate" /* GuildCreate */, payload);
|
|
3410
4307
|
});
|
|
3411
4308
|
this.#gateway.on("GUILD_UPDATE", (data) => {
|
|
3412
4309
|
const guild = new Guild(data);
|
|
@@ -3535,6 +4432,47 @@ class Client extends EventEmitter {
|
|
|
3535
4432
|
return Promise.reject(new Error("Client is unauthenticated."));
|
|
3536
4433
|
return this.rest.post(`/webhooks/${this.user.id}/${token}`, data);
|
|
3537
4434
|
}
|
|
4435
|
+
fetchWebhook(id, token) {
|
|
4436
|
+
return this.rest.get(token ? `/webhooks/${id}/${token}` : `/webhooks/${id}`);
|
|
4437
|
+
}
|
|
4438
|
+
fetchGuildPreview(guildId) {
|
|
4439
|
+
return this.rest.get(`/guilds/${guildId}/preview`);
|
|
4440
|
+
}
|
|
4441
|
+
fetchVoiceRegions() {
|
|
4442
|
+
return this.rest.get("/voice/regions");
|
|
4443
|
+
}
|
|
4444
|
+
fetchInvite(code, options) {
|
|
4445
|
+
const query = new URLSearchParams;
|
|
4446
|
+
if (options?.withCounts)
|
|
4447
|
+
query.set("with_counts", "true");
|
|
4448
|
+
if (options?.withExpiration)
|
|
4449
|
+
query.set("with_expiration", "true");
|
|
4450
|
+
if (options?.guildScheduledEventId)
|
|
4451
|
+
query.set("guild_scheduled_event_id", options.guildScheduledEventId);
|
|
4452
|
+
const qs = query.toString();
|
|
4453
|
+
return this.rest.get(`/invites/${code}${qs ? `?${qs}` : ""}`);
|
|
4454
|
+
}
|
|
4455
|
+
fetchSticker(id) {
|
|
4456
|
+
return this.rest.get(`/stickers/${id}`);
|
|
4457
|
+
}
|
|
4458
|
+
fetchPremiumStickerPacks() {
|
|
4459
|
+
return this.rest.get(`/sticker-packs`);
|
|
4460
|
+
}
|
|
4461
|
+
fetchGuildTemplate(code) {
|
|
4462
|
+
return this.rest.get(`/guilds/templates/${code}`);
|
|
4463
|
+
}
|
|
4464
|
+
generateInvite(options) {
|
|
4465
|
+
if (!this.user)
|
|
4466
|
+
throw new Error("Client must be ready to generate invite.");
|
|
4467
|
+
const params = new URLSearchParams({
|
|
4468
|
+
client_id: this.user.id,
|
|
4469
|
+
scopes: (options?.scopes ?? ["bot"]).join(" ")
|
|
4470
|
+
});
|
|
4471
|
+
if (options?.permissions) {
|
|
4472
|
+
params.set("permissions", String(options.permissions));
|
|
4473
|
+
}
|
|
4474
|
+
return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
|
|
4475
|
+
}
|
|
3538
4476
|
}
|
|
3539
4477
|
// packages/formatters/src/index.ts
|
|
3540
4478
|
function userMention(id) {
|
|
@@ -3553,6 +4491,55 @@ function timestamp(unixSeconds, style) {
|
|
|
3553
4491
|
throw new RangeError("Invalid Discord timestamp style.");
|
|
3554
4492
|
return `<t:${Math.floor(unixSeconds)}${style ? `:${style}` : ""}>`;
|
|
3555
4493
|
}
|
|
4494
|
+
function bold(text) {
|
|
4495
|
+
return `**${text}**`;
|
|
4496
|
+
}
|
|
4497
|
+
function italic(text) {
|
|
4498
|
+
return `_${text}_`;
|
|
4499
|
+
}
|
|
4500
|
+
function underline(text) {
|
|
4501
|
+
return `__${text}__`;
|
|
4502
|
+
}
|
|
4503
|
+
function strikethrough(text) {
|
|
4504
|
+
return `~~${text}~~`;
|
|
4505
|
+
}
|
|
4506
|
+
function spoiler(text) {
|
|
4507
|
+
return `||${text}||`;
|
|
4508
|
+
}
|
|
4509
|
+
function masked(text) {
|
|
4510
|
+
return spoiler(text);
|
|
4511
|
+
}
|
|
4512
|
+
function inlineCode(text) {
|
|
4513
|
+
return `\`${text}\``;
|
|
4514
|
+
}
|
|
4515
|
+
function codeBlock(text, language = "") {
|
|
4516
|
+
return `\`\`\`${language}
|
|
4517
|
+
${text}
|
|
4518
|
+
\`\`\``;
|
|
4519
|
+
}
|
|
4520
|
+
function link(label, url, title) {
|
|
4521
|
+
const titlePart = title ? ` "${title}"` : "";
|
|
4522
|
+
return `[${label}](${url}${titlePart})`;
|
|
4523
|
+
}
|
|
4524
|
+
function blockQuote(text) {
|
|
4525
|
+
return text.split(`
|
|
4526
|
+
`).map((line) => `> ${line}`).join(`
|
|
4527
|
+
`);
|
|
4528
|
+
}
|
|
4529
|
+
function heading(text, level = 1) {
|
|
4530
|
+
return `${"#".repeat(level)} ${text}`;
|
|
4531
|
+
}
|
|
4532
|
+
function subtext(text) {
|
|
4533
|
+
return `-# ${text}`;
|
|
4534
|
+
}
|
|
4535
|
+
function orderedList(...items) {
|
|
4536
|
+
return items.map((item, i) => `${i + 1}. ${item}`).join(`
|
|
4537
|
+
`);
|
|
4538
|
+
}
|
|
4539
|
+
function bulletList(...items) {
|
|
4540
|
+
return items.map((item) => `- ${item}`).join(`
|
|
4541
|
+
`);
|
|
4542
|
+
}
|
|
3556
4543
|
function escapeMarkdown(value) {
|
|
3557
4544
|
return value.replace(/[\\*_`~|>]/g, "\\$&");
|
|
3558
4545
|
}
|
|
@@ -3673,8 +4660,155 @@ class ShardBus {
|
|
|
3673
4660
|
}
|
|
3674
4661
|
}
|
|
3675
4662
|
}
|
|
4663
|
+
// packages/sharding/src/cluster.ts
|
|
4664
|
+
import { fork } from "child_process";
|
|
4665
|
+
import { cpus } from "os";
|
|
4666
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4667
|
+
|
|
4668
|
+
class ClusterManager {
|
|
4669
|
+
clusters = new Map;
|
|
4670
|
+
shardCount = 0;
|
|
4671
|
+
#options;
|
|
4672
|
+
#auto;
|
|
4673
|
+
#autoScaleTimer;
|
|
4674
|
+
#spawned = false;
|
|
4675
|
+
constructor(options) {
|
|
4676
|
+
if (!options.token?.trim())
|
|
4677
|
+
throw new TypeError("A Discord bot token is required.");
|
|
4678
|
+
if (!options.script?.trim())
|
|
4679
|
+
throw new TypeError("A script path is required for clustering.");
|
|
4680
|
+
if (options.autoScaleInterval !== undefined && (!Number.isInteger(options.autoScaleInterval) || options.autoScaleInterval < 1000)) {
|
|
4681
|
+
throw new RangeError("autoScaleInterval must be an integer of at least 1000 milliseconds.");
|
|
4682
|
+
}
|
|
4683
|
+
this.#auto = options.shardCount === "auto";
|
|
4684
|
+
this.#options = { ...options };
|
|
4685
|
+
}
|
|
4686
|
+
async fetchRecommendedShardCount() {
|
|
4687
|
+
const response = await fetch("https://discord.com/api/v10/gateway/bot", {
|
|
4688
|
+
headers: {
|
|
4689
|
+
Authorization: `Bot ${this.#options.token}`,
|
|
4690
|
+
"User-Agent": "Lunibee/0.1.0"
|
|
4691
|
+
}
|
|
4692
|
+
});
|
|
4693
|
+
if (!response.ok) {
|
|
4694
|
+
throw new Error(`Gateway discovery failed with status ${response.status}`);
|
|
4695
|
+
}
|
|
4696
|
+
const data = await response.json();
|
|
4697
|
+
if (typeof data.shards !== "number" || !Number.isInteger(data.shards) || data.shards < 1) {
|
|
4698
|
+
throw new Error("Gateway discovery returned an invalid shard count.");
|
|
4699
|
+
}
|
|
4700
|
+
return data.shards;
|
|
4701
|
+
}
|
|
4702
|
+
async spawn() {
|
|
4703
|
+
if (this.#spawned)
|
|
4704
|
+
return;
|
|
4705
|
+
if (typeof process?.versions?.bun === "string") {
|
|
4706
|
+
throw new Error("ClusterManager requires the Node.js runtime; Bun does not support child_process.fork() for this clustering mode.");
|
|
4707
|
+
}
|
|
4708
|
+
this.#spawned = true;
|
|
4709
|
+
const count = this.#options.shardCount === "auto" || this.#options.shardCount === undefined ? await this.fetchRecommendedShardCount() : this.#options.shardCount;
|
|
4710
|
+
this.shardCount = count;
|
|
4711
|
+
const clusterCount = this.#options.clusterCount ?? cpus().length;
|
|
4712
|
+
const chunks = Array.from({ length: clusterCount }, () => []);
|
|
4713
|
+
for (let i = 0;i < count; i++) {
|
|
4714
|
+
chunks[i % clusterCount].push(i);
|
|
4715
|
+
}
|
|
4716
|
+
for (let i = 0;i < clusterCount; i++) {
|
|
4717
|
+
if (chunks[i].length === 0)
|
|
4718
|
+
continue;
|
|
4719
|
+
const child = fork(this.#options.script, [], {
|
|
4720
|
+
env: {
|
|
4721
|
+
...process.env,
|
|
4722
|
+
SHARD_LIST: chunks[i].join(","),
|
|
4723
|
+
SHARD_COUNT: count.toString(),
|
|
4724
|
+
CLUSTER_ID: i.toString()
|
|
4725
|
+
}
|
|
4726
|
+
});
|
|
4727
|
+
this.clusters.set(i, {
|
|
4728
|
+
id: i,
|
|
4729
|
+
process: child,
|
|
4730
|
+
shards: chunks[i]
|
|
4731
|
+
});
|
|
4732
|
+
await sleep(500);
|
|
4733
|
+
}
|
|
4734
|
+
if (this.#options.autoScaleInterval && this.#options.autoScaleInterval > 0) {
|
|
4735
|
+
this.#autoScaleTimer = setInterval(() => {
|
|
4736
|
+
this.checkAutoScale();
|
|
4737
|
+
}, this.#options.autoScaleInterval);
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
async checkAutoScale() {
|
|
4741
|
+
if (!this.#auto)
|
|
4742
|
+
return;
|
|
4743
|
+
try {
|
|
4744
|
+
const recommended = await this.fetchRecommendedShardCount();
|
|
4745
|
+
if (recommended !== this.shardCount) {
|
|
4746
|
+
await this.respawn(recommended);
|
|
4747
|
+
}
|
|
4748
|
+
} catch (error) {
|
|
4749
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
4750
|
+
this.#options.onAutoScaleError?.(normalized);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
async respawn(newShardCount) {
|
|
4754
|
+
this.#options.shardCount = newShardCount;
|
|
4755
|
+
await this.shutdownAll();
|
|
4756
|
+
this.#spawned = false;
|
|
4757
|
+
await this.spawn();
|
|
4758
|
+
}
|
|
4759
|
+
async shutdownAll(timeoutMs = this.#options.shutdownTimeout ?? 5000) {
|
|
4760
|
+
if (this.#autoScaleTimer) {
|
|
4761
|
+
clearInterval(this.#autoScaleTimer);
|
|
4762
|
+
this.#autoScaleTimer = undefined;
|
|
4763
|
+
}
|
|
4764
|
+
await Promise.all([...this.clusters.values()].map((cluster) => this.#shutdownCluster(cluster, timeoutMs)));
|
|
4765
|
+
this.clusters.clear();
|
|
4766
|
+
this.#spawned = false;
|
|
4767
|
+
}
|
|
4768
|
+
async#shutdownCluster(cluster, timeoutMs) {
|
|
4769
|
+
const child = cluster.process;
|
|
4770
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
4771
|
+
return;
|
|
4772
|
+
await new Promise((resolve) => {
|
|
4773
|
+
let timer;
|
|
4774
|
+
const finish = () => {
|
|
4775
|
+
if (timer)
|
|
4776
|
+
clearTimeout(timer);
|
|
4777
|
+
resolve();
|
|
4778
|
+
};
|
|
4779
|
+
child.once("exit", finish);
|
|
4780
|
+
try {
|
|
4781
|
+
child.kill("SIGTERM");
|
|
4782
|
+
} catch {
|
|
4783
|
+
child.off("exit", finish);
|
|
4784
|
+
finish();
|
|
4785
|
+
return;
|
|
4786
|
+
}
|
|
4787
|
+
timer = setTimeout(() => {
|
|
4788
|
+
try {
|
|
4789
|
+
child.kill("SIGKILL");
|
|
4790
|
+
} catch {}
|
|
4791
|
+
}, timeoutMs);
|
|
4792
|
+
});
|
|
4793
|
+
}
|
|
4794
|
+
killAll() {
|
|
4795
|
+
if (this.#autoScaleTimer) {
|
|
4796
|
+
clearInterval(this.#autoScaleTimer);
|
|
4797
|
+
this.#autoScaleTimer = undefined;
|
|
4798
|
+
}
|
|
4799
|
+
for (const cluster of this.clusters.values()) {
|
|
4800
|
+
try {
|
|
4801
|
+
cluster.process.kill();
|
|
4802
|
+
} catch {}
|
|
4803
|
+
}
|
|
4804
|
+
this.clusters.clear();
|
|
4805
|
+
this.#spawned = false;
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
3676
4808
|
|
|
3677
4809
|
// packages/sharding/src/index.ts
|
|
4810
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4811
|
+
|
|
3678
4812
|
class ShardManager {
|
|
3679
4813
|
shards = new Map;
|
|
3680
4814
|
get shardCount() {
|
|
@@ -3683,6 +4817,8 @@ class ShardManager {
|
|
|
3683
4817
|
#options;
|
|
3684
4818
|
#resolved = false;
|
|
3685
4819
|
#destroyed = false;
|
|
4820
|
+
#auto;
|
|
4821
|
+
#autoScaleTimer;
|
|
3686
4822
|
constructor(options) {
|
|
3687
4823
|
if (!options.token?.trim())
|
|
3688
4824
|
throw new TypeError("A Discord bot token is required.");
|
|
@@ -3691,6 +4827,9 @@ class ShardManager {
|
|
|
3691
4827
|
const count = options.shardCount === "auto" || options.shardCount === undefined ? 1 : options.shardCount;
|
|
3692
4828
|
if (!Number.isInteger(count) || count < 1)
|
|
3693
4829
|
throw new RangeError("Shard count must be a positive integer.");
|
|
4830
|
+
if (options.autoScaleInterval !== undefined && (!Number.isInteger(options.autoScaleInterval) || options.autoScaleInterval < 1000))
|
|
4831
|
+
throw new RangeError("autoScaleInterval must be an integer of at least 1000 milliseconds.");
|
|
4832
|
+
this.#auto = options.shardCount === "auto";
|
|
3694
4833
|
this.#options = { ...options };
|
|
3695
4834
|
if (options.shardCount !== "auto")
|
|
3696
4835
|
this.#initialize(count);
|
|
@@ -3710,24 +4849,56 @@ class ShardManager {
|
|
|
3710
4849
|
return data.shards;
|
|
3711
4850
|
}
|
|
3712
4851
|
async connect() {
|
|
4852
|
+
if (this.#autoScaleTimer) {
|
|
4853
|
+
clearInterval(this.#autoScaleTimer);
|
|
4854
|
+
this.#autoScaleTimer = undefined;
|
|
4855
|
+
}
|
|
3713
4856
|
await this.#ensureInitialized();
|
|
3714
4857
|
for (const [id, shard] of this.shards) {
|
|
3715
4858
|
try {
|
|
3716
4859
|
await shard.connect();
|
|
3717
4860
|
} catch (error) {
|
|
3718
4861
|
this.destroy();
|
|
3719
|
-
throw new Error(`Failed to connect shard ${id}.`, {
|
|
4862
|
+
throw new Error(`Failed to connect shard ${id}.`, {
|
|
4863
|
+
cause: error
|
|
4864
|
+
});
|
|
3720
4865
|
}
|
|
3721
4866
|
if (id + 1 < this.shardCount && this.#options.spawnDelay && this.#options.spawnDelay > 0)
|
|
3722
|
-
await
|
|
4867
|
+
await sleep2(this.#options.spawnDelay);
|
|
4868
|
+
}
|
|
4869
|
+
if (this.#options.autoScaleInterval && !this.#autoScaleTimer) {
|
|
4870
|
+
this.#autoScaleTimer = setInterval(() => {
|
|
4871
|
+
this.checkAutoScale();
|
|
4872
|
+
}, this.#options.autoScaleInterval);
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
async checkAutoScale() {
|
|
4876
|
+
if (!this.#auto)
|
|
4877
|
+
return;
|
|
4878
|
+
try {
|
|
4879
|
+
const recommended = await this.fetchRecommendedShardCount();
|
|
4880
|
+
if (recommended !== this.shardCount) {
|
|
4881
|
+
await this.reshard(recommended);
|
|
4882
|
+
}
|
|
4883
|
+
} catch (error) {
|
|
4884
|
+
this.#options.onAutoScaleError?.(error);
|
|
3723
4885
|
}
|
|
3724
4886
|
}
|
|
4887
|
+
async reshard(count) {
|
|
4888
|
+
this.#options.shardCount = count;
|
|
4889
|
+
this.destroy();
|
|
4890
|
+
await this.connect();
|
|
4891
|
+
}
|
|
3725
4892
|
destroy() {
|
|
3726
4893
|
for (const shard of this.shards.values())
|
|
3727
4894
|
shard.close();
|
|
3728
4895
|
this.shards.clear();
|
|
3729
4896
|
this.#resolved = false;
|
|
3730
4897
|
this.#destroyed = true;
|
|
4898
|
+
if (this.#autoScaleTimer) {
|
|
4899
|
+
clearInterval(this.#autoScaleTimer);
|
|
4900
|
+
this.#autoScaleTimer = undefined;
|
|
4901
|
+
}
|
|
3731
4902
|
}
|
|
3732
4903
|
getShardIdForGuild(guildId) {
|
|
3733
4904
|
const id = BigInt(guildId);
|
|
@@ -3765,7 +4936,7 @@ class ShardManager {
|
|
|
3765
4936
|
}
|
|
3766
4937
|
}
|
|
3767
4938
|
// packages/utils/src/index.ts
|
|
3768
|
-
function
|
|
4939
|
+
function sleep3(ms) {
|
|
3769
4940
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3770
4941
|
}
|
|
3771
4942
|
function randomInt(min, max) {
|
|
@@ -3804,6 +4975,7 @@ class VoiceConnection {
|
|
|
3804
4975
|
gateway;
|
|
3805
4976
|
udp;
|
|
3806
4977
|
#listeners = new Map;
|
|
4978
|
+
receiver;
|
|
3807
4979
|
constructor(options) {
|
|
3808
4980
|
const resolved = typeof options === "string" ? { guildId: options } : options;
|
|
3809
4981
|
if (!resolved.guildId?.trim()) {
|
|
@@ -3813,6 +4985,7 @@ class VoiceConnection {
|
|
|
3813
4985
|
this.channelId = resolved.channelId;
|
|
3814
4986
|
this.selfMute = resolved.selfMute ?? false;
|
|
3815
4987
|
this.selfDeaf = resolved.selfDeaf ?? false;
|
|
4988
|
+
this.receiver = new VoiceReceiver(this);
|
|
3816
4989
|
}
|
|
3817
4990
|
on(event, listener) {
|
|
3818
4991
|
if (typeof listener !== "function")
|
|
@@ -3835,6 +5008,13 @@ class VoiceConnection {
|
|
|
3835
5008
|
this.#closeTransports();
|
|
3836
5009
|
this.gateway = gateway;
|
|
3837
5010
|
this.udp = udp;
|
|
5011
|
+
const attachedUdp = udp;
|
|
5012
|
+
if (attachedUdp.onMessage) {
|
|
5013
|
+
attachedUdp.onMessage((packet) => {
|
|
5014
|
+
if (this.udp === attachedUdp)
|
|
5015
|
+
this.receiver.onPacket(packet);
|
|
5016
|
+
});
|
|
5017
|
+
}
|
|
3838
5018
|
return this;
|
|
3839
5019
|
}
|
|
3840
5020
|
connect() {
|
|
@@ -3890,12 +5070,16 @@ class VoiceConnection {
|
|
|
3890
5070
|
try {
|
|
3891
5071
|
this.gateway?.close(1000, "Voice connection replaced or closed");
|
|
3892
5072
|
} catch (error) {
|
|
3893
|
-
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice gateway cleanup failed.", {
|
|
5073
|
+
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice gateway cleanup failed.", {
|
|
5074
|
+
cause: error
|
|
5075
|
+
}));
|
|
3894
5076
|
}
|
|
3895
5077
|
try {
|
|
3896
5078
|
this.udp?.close();
|
|
3897
5079
|
} catch (error) {
|
|
3898
|
-
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice UDP cleanup failed.", {
|
|
5080
|
+
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice UDP cleanup failed.", {
|
|
5081
|
+
cause: error
|
|
5082
|
+
}));
|
|
3899
5083
|
}
|
|
3900
5084
|
this.gateway = undefined;
|
|
3901
5085
|
this.udp = undefined;
|
|
@@ -3909,7 +5093,9 @@ class VoiceConnection {
|
|
|
3909
5093
|
try {
|
|
3910
5094
|
listener(next, previous);
|
|
3911
5095
|
} catch (error) {
|
|
3912
|
-
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice event listener failed.", {
|
|
5096
|
+
this.#emit("error", error instanceof VoiceError ? error : new VoiceError("Voice event listener failed.", {
|
|
5097
|
+
cause: error
|
|
5098
|
+
}));
|
|
3913
5099
|
}
|
|
3914
5100
|
if (this.state !== next && next !== "destroyed" /* Destroyed */)
|
|
3915
5101
|
return;
|
|
@@ -3926,6 +5112,187 @@ class VoiceConnection {
|
|
|
3926
5112
|
this.#assertUsable();
|
|
3927
5113
|
}
|
|
3928
5114
|
}
|
|
5115
|
+
|
|
5116
|
+
class AudioStream {
|
|
5117
|
+
stream;
|
|
5118
|
+
title;
|
|
5119
|
+
duration;
|
|
5120
|
+
constructor(stream, metadata) {
|
|
5121
|
+
this.stream = stream;
|
|
5122
|
+
this.title = metadata?.title;
|
|
5123
|
+
this.duration = metadata?.duration;
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
|
|
5127
|
+
class AudioPlayer {
|
|
5128
|
+
#state = "idle";
|
|
5129
|
+
#stream;
|
|
5130
|
+
#reader;
|
|
5131
|
+
#resumeSignal;
|
|
5132
|
+
#listeners = new Map;
|
|
5133
|
+
get state() {
|
|
5134
|
+
return this.#state;
|
|
5135
|
+
}
|
|
5136
|
+
get current() {
|
|
5137
|
+
return this.#stream;
|
|
5138
|
+
}
|
|
5139
|
+
async play(audioStream) {
|
|
5140
|
+
if (this.#state === "playing" || this.#state === "paused")
|
|
5141
|
+
await this.stop();
|
|
5142
|
+
this.#stream = audioStream;
|
|
5143
|
+
this.#reader = audioStream.stream.getReader();
|
|
5144
|
+
this.#transition("playing");
|
|
5145
|
+
this.#pump().catch((err) => this.#emitError(err));
|
|
5146
|
+
}
|
|
5147
|
+
pause() {
|
|
5148
|
+
if (this.#state !== "playing")
|
|
5149
|
+
return;
|
|
5150
|
+
this.#transition("paused");
|
|
5151
|
+
}
|
|
5152
|
+
resume() {
|
|
5153
|
+
if (this.#state !== "paused")
|
|
5154
|
+
return;
|
|
5155
|
+
this.#transition("playing");
|
|
5156
|
+
const signal = this.#resumeSignal;
|
|
5157
|
+
this.#resumeSignal = undefined;
|
|
5158
|
+
signal?.();
|
|
5159
|
+
}
|
|
5160
|
+
async stop() {
|
|
5161
|
+
if (this.#state === "idle" || this.#state === "stopped")
|
|
5162
|
+
return;
|
|
5163
|
+
const reader = this.#reader;
|
|
5164
|
+
this.#reader = undefined;
|
|
5165
|
+
this.#stream = undefined;
|
|
5166
|
+
this.#transition("stopped");
|
|
5167
|
+
const signal = this.#resumeSignal;
|
|
5168
|
+
this.#resumeSignal = undefined;
|
|
5169
|
+
signal?.();
|
|
5170
|
+
if (reader) {
|
|
5171
|
+
try {
|
|
5172
|
+
await reader.cancel();
|
|
5173
|
+
} catch {}
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
on(event, listener) {
|
|
5177
|
+
let set = this.#listeners.get(event);
|
|
5178
|
+
if (!set) {
|
|
5179
|
+
this.#listeners.set(event, set = new Set);
|
|
5180
|
+
}
|
|
5181
|
+
set.add(listener);
|
|
5182
|
+
return this;
|
|
5183
|
+
}
|
|
5184
|
+
off(event, listener) {
|
|
5185
|
+
this.#listeners.get(event)?.delete(listener);
|
|
5186
|
+
return this;
|
|
5187
|
+
}
|
|
5188
|
+
onChunk(chunk) {}
|
|
5189
|
+
async#pump() {
|
|
5190
|
+
const reader = this.#reader;
|
|
5191
|
+
if (!reader)
|
|
5192
|
+
return;
|
|
5193
|
+
while (true) {
|
|
5194
|
+
if (this.#state === "paused") {
|
|
5195
|
+
await new Promise((resolve) => {
|
|
5196
|
+
this.#resumeSignal = resolve;
|
|
5197
|
+
});
|
|
5198
|
+
}
|
|
5199
|
+
if (this.#state !== "playing")
|
|
5200
|
+
return;
|
|
5201
|
+
const { done, value } = await reader.read();
|
|
5202
|
+
if (done) {
|
|
5203
|
+
if (this.#state === "playing" || this.#state === "paused") {
|
|
5204
|
+
this.#transition("idle");
|
|
5205
|
+
this.#stream = undefined;
|
|
5206
|
+
this.#reader = undefined;
|
|
5207
|
+
this.#emit("finish");
|
|
5208
|
+
}
|
|
5209
|
+
return;
|
|
5210
|
+
}
|
|
5211
|
+
if (this.#state !== "playing" && this.#state !== "paused")
|
|
5212
|
+
return;
|
|
5213
|
+
this.onChunk(value);
|
|
5214
|
+
}
|
|
5215
|
+
}
|
|
5216
|
+
#transition(next) {
|
|
5217
|
+
if (this.#state === next)
|
|
5218
|
+
return;
|
|
5219
|
+
const from = this.#state;
|
|
5220
|
+
this.#state = next;
|
|
5221
|
+
this.#emit("stateChange", from, next);
|
|
5222
|
+
}
|
|
5223
|
+
#emitError(err) {
|
|
5224
|
+
this.#emit("error", err instanceof Error ? err : new VoiceError(String(err)));
|
|
5225
|
+
}
|
|
5226
|
+
#emit(event, ...args) {
|
|
5227
|
+
for (const listener of this.#listeners.get(event) ?? []) {
|
|
5228
|
+
try {
|
|
5229
|
+
listener(...args);
|
|
5230
|
+
} catch {}
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5235
|
+
class VoiceReceiver {
|
|
5236
|
+
connection;
|
|
5237
|
+
#ssrcMap = new Map;
|
|
5238
|
+
#streams = new Map;
|
|
5239
|
+
constructor(connection) {
|
|
5240
|
+
this.connection = connection;
|
|
5241
|
+
}
|
|
5242
|
+
mapSsrc(ssrc, userId) {
|
|
5243
|
+
this.#ssrcMap.set(ssrc, userId);
|
|
5244
|
+
}
|
|
5245
|
+
subscribe(userId) {
|
|
5246
|
+
let controllerRef;
|
|
5247
|
+
const stream = new ReadableStream({
|
|
5248
|
+
start: (controller) => {
|
|
5249
|
+
controllerRef = controller;
|
|
5250
|
+
let set = this.#streams.get(userId);
|
|
5251
|
+
if (!set)
|
|
5252
|
+
this.#streams.set(userId, set = new Set);
|
|
5253
|
+
set.add(controller);
|
|
5254
|
+
},
|
|
5255
|
+
cancel: () => {
|
|
5256
|
+
const set = this.#streams.get(userId);
|
|
5257
|
+
if (set) {
|
|
5258
|
+
set.delete(controllerRef);
|
|
5259
|
+
if (set.size === 0)
|
|
5260
|
+
this.#streams.delete(userId);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
});
|
|
5264
|
+
return new AudioStream(stream, { title: `User ${userId} Audio` });
|
|
5265
|
+
}
|
|
5266
|
+
onPacket(packet) {
|
|
5267
|
+
if (packet.length < 12)
|
|
5268
|
+
return;
|
|
5269
|
+
const view = new DataView(packet.buffer, packet.byteOffset, packet.byteLength);
|
|
5270
|
+
const ssrc = view.getUint32(8, false);
|
|
5271
|
+
const userId = this.#ssrcMap.get(ssrc);
|
|
5272
|
+
if (!userId)
|
|
5273
|
+
return;
|
|
5274
|
+
const controllers = this.#streams.get(userId);
|
|
5275
|
+
if (!controllers || controllers.size === 0)
|
|
5276
|
+
return;
|
|
5277
|
+
const csrcCount = packet[0] & 15;
|
|
5278
|
+
let offset = 12 + csrcCount * 4;
|
|
5279
|
+
const hasExtension = (packet[0] & 16) !== 0;
|
|
5280
|
+
if (hasExtension) {
|
|
5281
|
+
if (packet.length < offset + 4)
|
|
5282
|
+
return;
|
|
5283
|
+
const extWords = view.getUint16(offset + 2, false);
|
|
5284
|
+
offset += 4 + extWords * 4;
|
|
5285
|
+
}
|
|
5286
|
+
if (packet.length < offset)
|
|
5287
|
+
return;
|
|
5288
|
+
const payload = packet.slice(offset);
|
|
5289
|
+
for (const controller of controllers) {
|
|
5290
|
+
try {
|
|
5291
|
+
controller.enqueue(payload);
|
|
5292
|
+
} catch {}
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
3929
5296
|
// packages/builders/src/embed.ts
|
|
3930
5297
|
class EmbedBuilder {
|
|
3931
5298
|
#data = {};
|
|
@@ -3980,16 +5347,20 @@ class EmbedBuilder {
|
|
|
3980
5347
|
return this;
|
|
3981
5348
|
}
|
|
3982
5349
|
addFields(...fields) {
|
|
3983
|
-
|
|
5350
|
+
const resolved = fields.length === 1 && Array.isArray(fields[0]) ? fields[0] : fields;
|
|
5351
|
+
if (!resolved.length)
|
|
3984
5352
|
throw new TypeError("At least one embed field is required.");
|
|
3985
5353
|
const current = this.#data.fields ?? [];
|
|
3986
|
-
if (current.length +
|
|
5354
|
+
if (current.length + resolved.length > 25)
|
|
3987
5355
|
throw new RangeError("An embed cannot contain more than 25 fields.");
|
|
3988
|
-
for (const field of
|
|
5356
|
+
for (const field of resolved) {
|
|
3989
5357
|
validate(field.name, 256, "Embed field name");
|
|
3990
5358
|
validate(field.value, 1024, "Embed field value");
|
|
3991
5359
|
}
|
|
3992
|
-
this.#data.fields = [
|
|
5360
|
+
this.#data.fields = [
|
|
5361
|
+
...current,
|
|
5362
|
+
...resolved.map((field) => ({ ...field }))
|
|
5363
|
+
];
|
|
3993
5364
|
return this;
|
|
3994
5365
|
}
|
|
3995
5366
|
setFields(fields) {
|
|
@@ -4056,13 +5427,6 @@ var ComponentType = {
|
|
|
4056
5427
|
ContentInventoryEntry: 16,
|
|
4057
5428
|
Container: 17
|
|
4058
5429
|
};
|
|
4059
|
-
var ButtonStyle = {
|
|
4060
|
-
Primary: 1,
|
|
4061
|
-
Secondary: 2,
|
|
4062
|
-
Success: 3,
|
|
4063
|
-
Danger: 4,
|
|
4064
|
-
Link: 5
|
|
4065
|
-
};
|
|
4066
5430
|
var TextInputStyle = { Short: 1, Paragraph: 2 };
|
|
4067
5431
|
|
|
4068
5432
|
class ActionRowBuilder {
|
|
@@ -4171,7 +5535,9 @@ class ButtonBuilder {
|
|
|
4171
5535
|
try {
|
|
4172
5536
|
url2 = new URL(value);
|
|
4173
5537
|
} catch (error) {
|
|
4174
|
-
throw new TypeError("Button URL must be a valid URL.", {
|
|
5538
|
+
throw new TypeError("Button URL must be a valid URL.", {
|
|
5539
|
+
cause: error
|
|
5540
|
+
});
|
|
4175
5541
|
}
|
|
4176
5542
|
this.#data.style = ButtonStyle.Link;
|
|
4177
5543
|
delete this.#data.custom_id;
|
|
@@ -4227,7 +5593,7 @@ class EntitySelectBuilder {
|
|
|
4227
5593
|
return this;
|
|
4228
5594
|
}
|
|
4229
5595
|
setDefaultValues(...values) {
|
|
4230
|
-
this.#data.default_values = values;
|
|
5596
|
+
this.#data.default_values = values.map((value) => ({ ...value }));
|
|
4231
5597
|
return this;
|
|
4232
5598
|
}
|
|
4233
5599
|
toJSON() {
|
|
@@ -4318,6 +5684,7 @@ class TextInputBuilder {
|
|
|
4318
5684
|
class ContainerBuilder {
|
|
4319
5685
|
#components = [];
|
|
4320
5686
|
#accentColor;
|
|
5687
|
+
#spoiler;
|
|
4321
5688
|
addComponents(...components) {
|
|
4322
5689
|
if (!components.length)
|
|
4323
5690
|
throw new TypeError("At least one component is required.");
|
|
@@ -4331,12 +5698,20 @@ class ContainerBuilder {
|
|
|
4331
5698
|
this.#accentColor = color;
|
|
4332
5699
|
return this;
|
|
4333
5700
|
}
|
|
5701
|
+
setSpoiler(spoiler2 = true) {
|
|
5702
|
+
this.#spoiler = spoiler2;
|
|
5703
|
+
return this;
|
|
5704
|
+
}
|
|
4334
5705
|
toJSON() {
|
|
4335
|
-
|
|
5706
|
+
const data = {
|
|
4336
5707
|
type: ComponentType.Container,
|
|
4337
|
-
components: this.#components.map((c) => c.toJSON())
|
|
4338
|
-
accent_color: this.#accentColor
|
|
5708
|
+
components: this.#components.map((c) => c.toJSON())
|
|
4339
5709
|
};
|
|
5710
|
+
if (this.#accentColor !== undefined)
|
|
5711
|
+
data.accent_color = this.#accentColor;
|
|
5712
|
+
if (this.#spoiler !== undefined)
|
|
5713
|
+
data.spoiler = this.#spoiler;
|
|
5714
|
+
return data;
|
|
4340
5715
|
}
|
|
4341
5716
|
}
|
|
4342
5717
|
|
|
@@ -4405,8 +5780,8 @@ class FileComponentBuilder {
|
|
|
4405
5780
|
this.#url = url2;
|
|
4406
5781
|
return this;
|
|
4407
5782
|
}
|
|
4408
|
-
setSpoiler(
|
|
4409
|
-
this.#spoiler =
|
|
5783
|
+
setSpoiler(spoiler2 = true) {
|
|
5784
|
+
this.#spoiler = spoiler2;
|
|
4410
5785
|
return this;
|
|
4411
5786
|
}
|
|
4412
5787
|
toJSON() {
|
|
@@ -4447,16 +5822,32 @@ class SeparatorBuilder {
|
|
|
4447
5822
|
|
|
4448
5823
|
class ThumbnailBuilder {
|
|
4449
5824
|
#url = "";
|
|
5825
|
+
#description;
|
|
5826
|
+
#spoiler;
|
|
4450
5827
|
setUrl(url2) {
|
|
4451
5828
|
validateText(url2, 2048, "Thumbnail URL");
|
|
4452
5829
|
this.#url = url2;
|
|
4453
5830
|
return this;
|
|
4454
5831
|
}
|
|
5832
|
+
setDescription(description) {
|
|
5833
|
+
validateText(description, 1024, "Thumbnail description");
|
|
5834
|
+
this.#description = description;
|
|
5835
|
+
return this;
|
|
5836
|
+
}
|
|
5837
|
+
setSpoiler(spoiler2 = true) {
|
|
5838
|
+
this.#spoiler = spoiler2;
|
|
5839
|
+
return this;
|
|
5840
|
+
}
|
|
4455
5841
|
toJSON() {
|
|
4456
|
-
|
|
5842
|
+
const data = {
|
|
4457
5843
|
type: ComponentType.Thumbnail,
|
|
4458
5844
|
url: this.#url
|
|
4459
5845
|
};
|
|
5846
|
+
if (this.#description !== undefined)
|
|
5847
|
+
data.description = this.#description;
|
|
5848
|
+
if (this.#spoiler !== undefined)
|
|
5849
|
+
data.spoiler = this.#spoiler;
|
|
5850
|
+
return data;
|
|
4460
5851
|
}
|
|
4461
5852
|
}
|
|
4462
5853
|
|
|
@@ -4845,6 +6236,46 @@ function validateNumberRange(value, minimum, maximum, field, integerOnly = true)
|
|
|
4845
6236
|
if (!Number.isFinite(value) || integerOnly && !Number.isInteger(value) || value < minimum || value > maximum)
|
|
4846
6237
|
throw new RangeError(`${field} must be ${integerOnly ? "an integer" : "a number"} between ${minimum} and ${maximum}.`);
|
|
4847
6238
|
}
|
|
6239
|
+
|
|
6240
|
+
class ContextMenuCommandBuilder {
|
|
6241
|
+
data;
|
|
6242
|
+
constructor(type) {
|
|
6243
|
+
this.data = { type };
|
|
6244
|
+
}
|
|
6245
|
+
setName(name) {
|
|
6246
|
+
if (typeof name !== "string" || name.trim().length < 1 || name.length > 32)
|
|
6247
|
+
throw new RangeError("Command name must contain 1-32 characters.");
|
|
6248
|
+
this.data.name = name;
|
|
6249
|
+
return this;
|
|
6250
|
+
}
|
|
6251
|
+
setDefaultMemberPermissions(permissions) {
|
|
6252
|
+
this.data.default_member_permissions = permissions === null ? null : BigInt(permissions).toString();
|
|
6253
|
+
return this;
|
|
6254
|
+
}
|
|
6255
|
+
setDMPermission(enabled) {
|
|
6256
|
+
this.data.dm_permission = enabled;
|
|
6257
|
+
return this;
|
|
6258
|
+
}
|
|
6259
|
+
setIntegrationTypes(...types) {
|
|
6260
|
+
this.data.integration_types = [...types];
|
|
6261
|
+
return this;
|
|
6262
|
+
}
|
|
6263
|
+
toJSON() {
|
|
6264
|
+
return structuredClone(this.data);
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
|
|
6268
|
+
class UserCommandBuilder extends ContextMenuCommandBuilder {
|
|
6269
|
+
constructor() {
|
|
6270
|
+
super(2);
|
|
6271
|
+
}
|
|
6272
|
+
}
|
|
6273
|
+
|
|
6274
|
+
class MessageCommandBuilder extends ContextMenuCommandBuilder {
|
|
6275
|
+
constructor() {
|
|
6276
|
+
super(3);
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
4848
6279
|
// packages/builders/src/attachment.ts
|
|
4849
6280
|
import { readFile } from "fs/promises";
|
|
4850
6281
|
import { basename } from "path";
|
|
@@ -4890,7 +6321,7 @@ class AttachmentBuilder {
|
|
|
4890
6321
|
if (typeof Buffer !== "undefined" && Buffer.isBuffer(this.file)) {
|
|
4891
6322
|
return new Uint8Array(this.file);
|
|
4892
6323
|
}
|
|
4893
|
-
|
|
6324
|
+
throw new TypeError("Attachment file must be a supported binary value or file path.");
|
|
4894
6325
|
}
|
|
4895
6326
|
}
|
|
4896
6327
|
export {
|
|
@@ -4900,6 +6331,12 @@ export {
|
|
|
4900
6331
|
ApplicationCommandType,
|
|
4901
6332
|
AttachmentBuilder,
|
|
4902
6333
|
AttachmentOptionBuilder,
|
|
6334
|
+
AudioPlayer,
|
|
6335
|
+
AudioStream,
|
|
6336
|
+
AuditLog,
|
|
6337
|
+
AuditLogEntry,
|
|
6338
|
+
AuditLogEvent,
|
|
6339
|
+
AutoModerationRule,
|
|
4903
6340
|
AutocompleteInteraction,
|
|
4904
6341
|
BaseStructure,
|
|
4905
6342
|
BooleanOptionBuilder,
|
|
@@ -4912,18 +6349,24 @@ export {
|
|
|
4912
6349
|
ChannelType,
|
|
4913
6350
|
Client,
|
|
4914
6351
|
ClientEvent,
|
|
6352
|
+
ClusterManager,
|
|
4915
6353
|
Collection,
|
|
6354
|
+
Collector,
|
|
4916
6355
|
CommandInteraction,
|
|
4917
6356
|
CommandOptions,
|
|
4918
6357
|
ComponentInteraction,
|
|
4919
6358
|
ComponentType,
|
|
4920
6359
|
ContainerBuilder,
|
|
4921
6360
|
ContentInventoryEntryBuilder,
|
|
6361
|
+
RESTError as DiscordAPIError,
|
|
4922
6362
|
Embed,
|
|
4923
6363
|
EmbedBuilder,
|
|
6364
|
+
Emoji,
|
|
6365
|
+
EmojiManager,
|
|
4924
6366
|
EntitySelectBuilder,
|
|
4925
6367
|
FileComponentBuilder,
|
|
4926
6368
|
Gateway,
|
|
6369
|
+
GatewayCloseCodes,
|
|
4927
6370
|
GatewayError,
|
|
4928
6371
|
GatewayIntentBits,
|
|
4929
6372
|
GatewayOpcodes,
|
|
@@ -4932,6 +6375,8 @@ export {
|
|
|
4932
6375
|
GuildManager,
|
|
4933
6376
|
GuildMember,
|
|
4934
6377
|
GuildMemberManager,
|
|
6378
|
+
GuildOnboarding,
|
|
6379
|
+
GuildWelcomeScreen,
|
|
4935
6380
|
HandlerRegistry,
|
|
4936
6381
|
IntegerOptionBuilder,
|
|
4937
6382
|
IntentBits,
|
|
@@ -4940,15 +6385,20 @@ export {
|
|
|
4940
6385
|
InteractionResponse,
|
|
4941
6386
|
InteractionResponseType,
|
|
4942
6387
|
InteractionType,
|
|
6388
|
+
Invite,
|
|
4943
6389
|
Manager,
|
|
4944
6390
|
MediaGalleryBuilder,
|
|
6391
|
+
MemoryRateLimitStore,
|
|
4945
6392
|
MentionableOptionBuilder,
|
|
4946
6393
|
Message,
|
|
6394
|
+
MessageCommandBuilder,
|
|
4947
6395
|
MessageFlags,
|
|
4948
6396
|
MessageManager,
|
|
4949
6397
|
ModalBuilder,
|
|
4950
6398
|
ModalSubmitInteraction,
|
|
4951
6399
|
NumberOptionBuilder,
|
|
6400
|
+
OnboardingPrompt,
|
|
6401
|
+
OnboardingPromptOption,
|
|
4952
6402
|
Permission,
|
|
4953
6403
|
PermissionFlagsBits,
|
|
4954
6404
|
PermissionOverwriteType,
|
|
@@ -4958,6 +6408,7 @@ export {
|
|
|
4958
6408
|
PremiumTier,
|
|
4959
6409
|
REST,
|
|
4960
6410
|
RESTError,
|
|
6411
|
+
RedisRateLimitStore,
|
|
4961
6412
|
ResourceManager,
|
|
4962
6413
|
Role,
|
|
4963
6414
|
RoleManager,
|
|
@@ -4969,6 +6420,7 @@ export {
|
|
|
4969
6420
|
ShardManager,
|
|
4970
6421
|
SlashCommandBuilder,
|
|
4971
6422
|
SpeakingFlags,
|
|
6423
|
+
GatewayState as Status,
|
|
4972
6424
|
StickerFormatType,
|
|
4973
6425
|
StickerType,
|
|
4974
6426
|
StringOptionBuilder,
|
|
@@ -4982,24 +6434,43 @@ export {
|
|
|
4982
6434
|
ThreadManager,
|
|
4983
6435
|
ThumbnailBuilder,
|
|
4984
6436
|
User,
|
|
6437
|
+
UserCommandBuilder,
|
|
4985
6438
|
UserManager,
|
|
4986
6439
|
UserOptionBuilder,
|
|
4987
6440
|
VerificationLevel,
|
|
4988
6441
|
VoiceConnection,
|
|
4989
6442
|
VoiceConnectionState,
|
|
4990
6443
|
VoiceError,
|
|
6444
|
+
VoiceReceiver,
|
|
6445
|
+
Webhook,
|
|
4991
6446
|
WebhookClient,
|
|
6447
|
+
WebhookType,
|
|
6448
|
+
WelcomeScreenChannel,
|
|
6449
|
+
blockQuote,
|
|
6450
|
+
bold,
|
|
6451
|
+
bulletList,
|
|
4992
6452
|
channelMention,
|
|
6453
|
+
codeBlock,
|
|
4993
6454
|
createInteraction,
|
|
4994
6455
|
createRetryPolicy,
|
|
4995
6456
|
escapeMarkdown,
|
|
6457
|
+
heading,
|
|
6458
|
+
inlineCode,
|
|
4996
6459
|
isSnowflake,
|
|
6460
|
+
italic,
|
|
6461
|
+
link,
|
|
6462
|
+
masked,
|
|
6463
|
+
orderedList,
|
|
4997
6464
|
randomInt,
|
|
4998
6465
|
resolveGatewayIntents,
|
|
4999
6466
|
roleMention,
|
|
5000
|
-
sleep,
|
|
6467
|
+
sleep3 as sleep,
|
|
6468
|
+
spoiler,
|
|
6469
|
+
strikethrough,
|
|
6470
|
+
subtext,
|
|
5001
6471
|
timestamp,
|
|
6472
|
+
underline,
|
|
5002
6473
|
userMention
|
|
5003
6474
|
};
|
|
5004
6475
|
|
|
5005
|
-
//# debugId=
|
|
6476
|
+
//# debugId=CB787B7E0E4365B464756E2164756E21
|