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.
Files changed (55) hide show
  1. package/README.md +3 -3
  2. package/dist/builders/commands.d.ts +37 -0
  3. package/dist/builders/components.d.ts +19 -12
  4. package/dist/builders/embed.d.ts +3 -1
  5. package/dist/builders/index.d.ts +1 -1
  6. package/dist/builders/index.js +146 -19
  7. package/dist/builders/index.js.map +8 -7
  8. package/dist/collection/index.d.ts +30 -0
  9. package/dist/collection/index.js +78 -5
  10. package/dist/collection/index.js.map +4 -4
  11. package/dist/core/events.d.ts +2 -0
  12. package/dist/core/index.d.ts +38 -0
  13. package/dist/core/index.js +754 -175
  14. package/dist/core/index.js.map +24 -22
  15. package/dist/core/permissions.d.ts +112 -53
  16. package/dist/formatters/index.d.ts +43 -3
  17. package/dist/formatters/index.js +64 -1
  18. package/dist/formatters/index.js.map +3 -3
  19. package/dist/handlers/index.js.map +2 -2
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1762 -291
  22. package/dist/index.js.map +43 -38
  23. package/dist/managers/emoji.d.ts +38 -0
  24. package/dist/managers/guild.d.ts +112 -1
  25. package/dist/managers/index.d.ts +31 -1
  26. package/dist/managers/index.js +803 -77
  27. package/dist/managers/index.js.map +21 -20
  28. package/dist/rest/index.d.ts +101 -11
  29. package/dist/rest/index.js +301 -45
  30. package/dist/rest/index.js.map +8 -6
  31. package/dist/rest/redis.d.ts +37 -0
  32. package/dist/rest/routes.d.ts +22 -0
  33. package/dist/rest/store.d.ts +38 -0
  34. package/dist/rest/webhook.d.ts +2 -2
  35. package/dist/sharding/cluster.d.ts +61 -0
  36. package/dist/sharding/index.d.ts +8 -0
  37. package/dist/sharding/index.js +286 -18
  38. package/dist/sharding/index.js.map +8 -7
  39. package/dist/structures/audit-log.d.ts +26 -0
  40. package/dist/structures/base.d.ts +32 -0
  41. package/dist/structures/index.d.ts +29 -1
  42. package/dist/structures/index.js +1022 -86
  43. package/dist/structures/index.js.map +14 -10
  44. package/dist/structures/resources.d.ts +192 -4
  45. package/dist/types/index.d.ts +76 -0
  46. package/dist/types/index.js.map +2 -2
  47. package/dist/utils/index.js.map +2 -2
  48. package/dist/voice/index.d.ts +88 -0
  49. package/dist/voice/index.js +204 -5
  50. package/dist/voice/index.js.map +3 -3
  51. package/dist/ws/index.d.ts +40 -1
  52. package/dist/ws/index.js +102 -18
  53. package/dist/ws/index.js.map +4 -4
  54. package/package.json +16 -16
  55. package/dist/structures/permissions.d.ts +0 -73
@@ -107,58 +107,57 @@ var Permissions = {
107
107
  SendPolls: 1n << 49n,
108
108
  UseExternalApps: 1n << 50n
109
109
  };
110
- var PermissionFlagsBits;
111
- ((PermissionFlagsBits2) => {
112
- PermissionFlagsBits2["CreateInstantInvite"] = "1";
113
- PermissionFlagsBits2["KickMembers"] = "2";
114
- PermissionFlagsBits2["BanMembers"] = "4";
115
- PermissionFlagsBits2["Administrator"] = "8";
116
- PermissionFlagsBits2["ManageChannels"] = "16";
117
- PermissionFlagsBits2["ManageGuild"] = "32";
118
- PermissionFlagsBits2["AddReactions"] = "64";
119
- PermissionFlagsBits2["ViewAuditLog"] = "128";
120
- PermissionFlagsBits2["PrioritySpeaker"] = "256";
121
- PermissionFlagsBits2["Stream"] = "512";
122
- PermissionFlagsBits2["ViewChannel"] = "1024";
123
- PermissionFlagsBits2["SendMessages"] = "2048";
124
- PermissionFlagsBits2["SendTTSMessages"] = "4096";
125
- PermissionFlagsBits2["ManageMessages"] = "8192";
126
- PermissionFlagsBits2["EmbedLinks"] = "16384";
127
- PermissionFlagsBits2["AttachFiles"] = "32768";
128
- PermissionFlagsBits2["ReadMessageHistory"] = "65536";
129
- PermissionFlagsBits2["MentionEveryone"] = "131072";
130
- PermissionFlagsBits2["UseExternalEmojis"] = "262144";
131
- PermissionFlagsBits2["ViewGuildInsights"] = "524288";
132
- PermissionFlagsBits2["Connect"] = "1048576";
133
- PermissionFlagsBits2["Speak"] = "2097152";
134
- PermissionFlagsBits2["MuteMembers"] = "4194304";
135
- PermissionFlagsBits2["DeafenMembers"] = "8388608";
136
- PermissionFlagsBits2["MoveMembers"] = "16777216";
137
- PermissionFlagsBits2["UseVAD"] = "33554432";
138
- PermissionFlagsBits2["ChangeNickname"] = "67108864";
139
- PermissionFlagsBits2["ManageNicknames"] = "134217728";
140
- PermissionFlagsBits2["ManageRoles"] = "268435456";
141
- PermissionFlagsBits2["ManageWebhooks"] = "536870912";
142
- PermissionFlagsBits2["ManageGuildExpressions"] = "1073741824";
143
- PermissionFlagsBits2["UseApplicationCommands"] = "2147483648";
144
- PermissionFlagsBits2["RequestToSpeak"] = "4294967296";
145
- PermissionFlagsBits2["ManageEvents"] = "8589934592";
146
- PermissionFlagsBits2["ManageThreads"] = "17179869184";
147
- PermissionFlagsBits2["CreatePublicThreads"] = "34359738368";
148
- PermissionFlagsBits2["CreatePrivateThreads"] = "68719476736";
149
- PermissionFlagsBits2["UseExternalStickers"] = "137438953472";
150
- PermissionFlagsBits2["SendMessagesInThreads"] = "274877906944";
151
- PermissionFlagsBits2["UseEmbeddedActivities"] = "549755813888";
152
- PermissionFlagsBits2["ModerateMembers"] = "1099511627776";
153
- PermissionFlagsBits2["ViewCreatorMonetizationAnalytics"] = "2199023255552";
154
- PermissionFlagsBits2["UseSoundboard"] = "4398046511104";
155
- PermissionFlagsBits2["CreateGuildExpressions"] = "8796093022208";
156
- PermissionFlagsBits2["CreateEvents"] = "17592186044416";
157
- PermissionFlagsBits2["UseExternalSounds"] = "35184372088832";
158
- PermissionFlagsBits2["SendVoiceMessages"] = "70368744177664";
159
- PermissionFlagsBits2["SendPolls"] = "562949953421312";
160
- PermissionFlagsBits2["UseExternalApps"] = "1125899906842624";
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 BigInt(PermissionFlagsBits[permission]);
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,
@@ -716,6 +787,75 @@ class Collection extends Map {
716
787
  }
717
788
  return result;
718
789
  }
790
+ partition(predicate) {
791
+ const pass = new Collection;
792
+ const fail = new Collection;
793
+ for (const [key, value] of this) {
794
+ if (predicate(value, key, this))
795
+ pass.set(key, value);
796
+ else
797
+ fail.set(key, value);
798
+ }
799
+ return [pass, fail];
800
+ }
801
+ tap(fn) {
802
+ fn(this);
803
+ return this;
804
+ }
805
+ flatMap(transform) {
806
+ const result = [];
807
+ for (const [key, value] of this) {
808
+ for (const item of transform(value, key, this))
809
+ result.push(item);
810
+ }
811
+ return result;
812
+ }
813
+ reduce(fn, initialValue) {
814
+ let acc = initialValue;
815
+ for (const [key, value] of this)
816
+ acc = fn(acc, value, key, this);
817
+ return acc;
818
+ }
819
+ sorted(comparator) {
820
+ const decorated = [...this.entries()].map((entry, index) => ({
821
+ entry,
822
+ index
823
+ }));
824
+ if (comparator)
825
+ decorated.sort((x, y) => {
826
+ const comparison = comparator(x.entry[1], y.entry[1], x.entry[0], y.entry[0]);
827
+ return comparison !== 0 ? comparison : x.index - y.index;
828
+ });
829
+ const result = new Collection;
830
+ for (const { entry } of decorated)
831
+ result.set(entry[0], entry[1]);
832
+ return result;
833
+ }
834
+ random() {
835
+ const arr = [...this.values()];
836
+ if (!arr.length)
837
+ return;
838
+ return arr[Math.floor(Math.random() * arr.length)];
839
+ }
840
+ randomKey() {
841
+ const arr = [...this.keys()];
842
+ if (!arr.length)
843
+ return;
844
+ return arr[Math.floor(Math.random() * arr.length)];
845
+ }
846
+ at(index) {
847
+ const arr = [...this.values()];
848
+ const i = index < 0 ? arr.length + index : index;
849
+ return arr[i];
850
+ }
851
+ keyAt(index) {
852
+ const arr = [...this.keys()];
853
+ const i = index < 0 ? arr.length + index : index;
854
+ return arr[i];
855
+ }
856
+ toJSON() {
857
+ return [...this.entries()];
858
+ }
719
859
  }
720
860
 
721
861
  // packages/managers/src/base.ts
@@ -787,7 +927,7 @@ class ResourceManager extends Manager {
787
927
  // packages/rest/package.json
788
928
  var package_default = {
789
929
  name: "@lunibee/rest",
790
- version: "0.1.6",
930
+ version: "0.1.7",
791
931
  type: "module",
792
932
  module: "./src/index.ts",
793
933
  types: "./src/index.ts",
@@ -796,6 +936,45 @@ var package_default = {
796
936
  }
797
937
  };
798
938
 
939
+ // packages/rest/src/store.ts
940
+ class MemoryRateLimitStore {
941
+ #buckets = new Map;
942
+ #routeBuckets = new Map;
943
+ #globalResetAt = 0;
944
+ static #PRUNE_INTERVAL = 60000;
945
+ #lastPruneAt = 0;
946
+ getBucketHash(route) {
947
+ return this.#routeBuckets.get(route);
948
+ }
949
+ setBucketHash(route, hash) {
950
+ this.#routeBuckets.set(route, hash);
951
+ }
952
+ getBucket(key) {
953
+ return this.#buckets.get(key);
954
+ }
955
+ updateBucket(key, state) {
956
+ this.#buckets.set(key, state);
957
+ const now = Date.now();
958
+ if (now - this.#lastPruneAt >= MemoryRateLimitStore.#PRUNE_INTERVAL) {
959
+ this.#lastPruneAt = now;
960
+ this.prune();
961
+ }
962
+ }
963
+ prune(maxAge = 0) {
964
+ const cutoff = Date.now() - Math.max(0, maxAge);
965
+ for (const [key, state] of this.#buckets) {
966
+ if (state.resetAt <= cutoff)
967
+ this.#buckets.delete(key);
968
+ }
969
+ }
970
+ getGlobalReset() {
971
+ return this.#globalResetAt;
972
+ }
973
+ setGlobalReset(resetAt) {
974
+ this.#globalResetAt = resetAt;
975
+ }
976
+ }
977
+
799
978
  // packages/rest/src/routes.ts
800
979
  var Routes = {
801
980
  user: () => "/users/@me",
@@ -841,7 +1020,18 @@ var Routes = {
841
1020
  guildAutoModerationRule: (guildId, ruleId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules/${snowflake(ruleId, "Rule ID")}`,
842
1021
  voiceRegions: () => "/voice/regions",
843
1022
  gateway: () => "/gateway",
844
- gatewayBot: () => "/gateway/bot"
1023
+ gatewayBot: () => "/gateway/bot",
1024
+ guildAuditLog: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/audit-logs`,
1025
+ guildMembers: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members`,
1026
+ guildWelcomeScreen: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/welcome-screen`,
1027
+ guildOnboarding: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/onboarding`,
1028
+ guildPrune: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/prune`,
1029
+ stageInstances: () => "/stage-instances",
1030
+ stageInstanceByChannel: (channelId) => `/stage-instances/${snowflake(channelId, "Channel ID")}`,
1031
+ invite: (code) => `/invites/${encodeURIComponent(code)}`,
1032
+ guildVanity: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/vanity-url`,
1033
+ guildEmoji: (guildId, emojiId) => emojiId ? `/guilds/${snowflake(guildId, "Guild ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/emojis`,
1034
+ applicationEmoji: (applicationId, emojiId) => emojiId ? `/applications/${snowflake(applicationId, "Application ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/emojis`
845
1035
  };
846
1036
  function snowflake(value, field) {
847
1037
  if (!/^\d{1,20}$/.test(value))
@@ -869,6 +1059,49 @@ class RESTError extends Error {
869
1059
  var LIBRARY_VERSION = package_default.version;
870
1060
  var LIBRARY_URL = "https://github.com/Ekretos/lunibee";
871
1061
  var USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${package_default.version})`;
1062
+ var REQUEST_DATA_KEYS = new Set([
1063
+ "body",
1064
+ "files",
1065
+ "query",
1066
+ "headers",
1067
+ "reason",
1068
+ "auth",
1069
+ "signal"
1070
+ ]);
1071
+ function isRequestData(value) {
1072
+ if (value === null || typeof value !== "object")
1073
+ return false;
1074
+ if (Array.isArray(value))
1075
+ return false;
1076
+ if (typeof FormData !== "undefined" && value instanceof FormData)
1077
+ return false;
1078
+ if (value instanceof Blob || value instanceof Uint8Array || value instanceof ArrayBuffer)
1079
+ return false;
1080
+ const keys = Object.keys(value);
1081
+ if (keys.length === 0)
1082
+ return false;
1083
+ return keys.every((key) => REQUEST_DATA_KEYS.has(key));
1084
+ }
1085
+ function serializeQuery(query) {
1086
+ if (query === undefined)
1087
+ return "";
1088
+ if (typeof query === "string")
1089
+ return query.length === 0 || query.startsWith("?") ? query : `?${query}`;
1090
+ const params = query instanceof URLSearchParams ? query : new URLSearchParams(Object.entries(query).flatMap(([key, value]) => value === undefined || value === null ? [] : [[key, String(value)]]));
1091
+ const serialized = params.toString();
1092
+ return serialized.length === 0 ? "" : `?${serialized}`;
1093
+ }
1094
+ function majorParameter(path) {
1095
+ const match = /^\/(channels|guilds|webhooks)\/(\d+)(?:\/([^/?]+))?/.exec(path);
1096
+ if (!match)
1097
+ return "@none";
1098
+ if (match[1] === "webhooks" && match[3] !== undefined)
1099
+ return `${match[2]}:${match[3]}`;
1100
+ return match[2];
1101
+ }
1102
+ function scopeBucket(hashOrRoute, major) {
1103
+ return `${hashOrRoute}|${major}`;
1104
+ }
872
1105
  function createRetryPolicy(maxRetries = 2) {
873
1106
  if (!Number.isFinite(maxRetries) || maxRetries < 0)
874
1107
  throw new TypeError("maxRetries must be a non-negative finite number.");
@@ -891,14 +1124,21 @@ class REST {
891
1124
  #timeout;
892
1125
  #retryPolicy;
893
1126
  #token;
894
- #globalResetAt = 0;
895
- #buckets = new Map;
896
- #routeBuckets = new Map;
1127
+ #store;
1128
+ #localQueues = new Map;
1129
+ #hooks = {};
897
1130
  constructor(options = {}) {
898
1131
  this.#token = options.token;
899
1132
  this.#timeout = Math.max(1, options.timeout ?? 15000);
900
1133
  this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
901
1134
  this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
1135
+ this.#store = options.store ?? new MemoryRateLimitStore;
1136
+ if (options.hooks)
1137
+ this.#hooks = options.hooks;
1138
+ }
1139
+ setHooks(hooks) {
1140
+ this.#hooks = hooks;
1141
+ return this;
902
1142
  }
903
1143
  setToken(token) {
904
1144
  if (!token.trim())
@@ -911,56 +1151,91 @@ class REST {
911
1151
  throw new TypeError("REST method is required.");
912
1152
  if (!path.startsWith("/"))
913
1153
  throw new TypeError("REST paths must start with '/'.");
1154
+ const files = options.files;
1155
+ if (files && files.length > 0 && !(body instanceof FormData))
1156
+ body = this.#fileForm(body, files);
1157
+ const query = serializeQuery(options.query);
1158
+ const finalPath = query ? `${path}${query}` : path;
914
1159
  const route = `${normalizedMethod}:${this.#normalizeRoute(path)}`;
915
- let bucketKey = this.#routeBuckets.get(route) ?? route;
916
- let bucket = this.#buckets.get(bucketKey) ?? {
917
- remaining: 1,
918
- resetAt: 0,
919
- queue: Promise.resolve()
920
- };
921
- this.#buckets.set(bucketKey, bucket);
922
- const previous = bucket.queue;
1160
+ const major = majorParameter(path);
1161
+ let bucketKey = scopeBucket(await this.#store.getBucketHash(route) ?? route, major);
1162
+ const queueKey = bucketKey;
1163
+ const previous = this.#localQueues.get(queueKey) ?? Promise.resolve();
923
1164
  let release;
924
- bucket.queue = new Promise((resolve) => {
1165
+ const gate = new Promise((resolve) => {
925
1166
  release = resolve;
926
1167
  });
1168
+ this.#localQueues.set(queueKey, gate);
927
1169
  await this.#abortable(previous, options.signal, path);
928
1170
  try {
929
1171
  for (let attempt = 0;attempt <= this.#retryPolicy.maxRetries; attempt++) {
930
- await this.#wait(bucket, options.signal, path);
1172
+ await this.#wait(bucketKey, options.signal, path);
931
1173
  await this.#waitGlobal(options.signal, path);
932
1174
  const controller = new AbortController;
933
1175
  const onAbort = () => controller.abort(options.signal?.reason);
934
1176
  if (options.signal?.aborted)
935
1177
  throw this.#abortError(path, options.signal.reason);
936
- options.signal?.addEventListener("abort", onAbort, { once: true });
1178
+ options.signal?.addEventListener("abort", onAbort, {
1179
+ once: true
1180
+ });
937
1181
  const timer = setTimeout(() => controller.abort(new DOMException("REST request timeout", "TimeoutError")), this.#timeout);
938
1182
  const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
939
- const headers = {
940
- ...this.#token ? { Authorization: `Bot ${this.#token}` } : {},
941
- "User-Agent": USER_AGENT
942
- };
1183
+ const headers = { ...options.headers };
1184
+ if (options.auth !== false && this.#token)
1185
+ headers["Authorization"] = `Bot ${this.#token}`;
1186
+ headers["User-Agent"] = USER_AGENT;
1187
+ if (options.reason !== undefined)
1188
+ headers["X-Audit-Log-Reason"] = encodeURIComponent(options.reason);
943
1189
  if (!isFormData)
944
1190
  headers["Content-Type"] = "application/json";
945
1191
  const fetchBody = isFormData ? body : body === undefined ? undefined : JSON.stringify(body);
946
1192
  try {
947
- const response = await fetch(`${this.#baseURL}${path}`, {
1193
+ this.#emit(this.#hooks.onRequest, {
1194
+ method: normalizedMethod,
1195
+ path,
1196
+ attempt
1197
+ });
1198
+ const requestStart = Date.now();
1199
+ const response = await fetch(`${this.#baseURL}${finalPath}`, {
948
1200
  method: normalizedMethod,
949
1201
  headers,
950
1202
  body: fetchBody,
951
1203
  signal: controller.signal
952
1204
  });
953
- bucket = this.#update(response, bucket, route, bucketKey);
954
- bucketKey = this.#routeBuckets.get(route) ?? bucketKey;
1205
+ this.#emit(this.#hooks.onResponse, {
1206
+ method: normalizedMethod,
1207
+ path,
1208
+ status: response.status,
1209
+ durationMs: Date.now() - requestStart
1210
+ });
1211
+ bucketKey = await this.#update(response, bucketKey, route, major);
955
1212
  const payload = await this.#readPayload(response);
956
1213
  if (response.ok)
957
1214
  return response.status === 204 ? undefined : payload;
958
1215
  const data = this.#errorData(payload);
959
1216
  const retryAfter = response.status === 429 ? this.#retryAfter(response, data) : undefined;
960
- if (data.global && retryAfter !== undefined)
961
- this.#globalResetAt = Date.now() + retryAfter * 1000;
1217
+ if (data.global && retryAfter !== undefined) {
1218
+ const currentGlobal = await this.#store.getGlobalReset();
1219
+ await this.#store.setGlobalReset(Math.max(currentGlobal, Date.now() + retryAfter * 1000));
1220
+ }
1221
+ if (response.status === 429)
1222
+ this.#emit(this.#hooks.onRateLimit, {
1223
+ method: normalizedMethod,
1224
+ path,
1225
+ retryAfterMs: (retryAfter ?? 1) * 1000,
1226
+ global: data.global ?? false,
1227
+ bucket: await this.#store.getBucketHash(route)
1228
+ });
962
1229
  if (attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, response.status)) {
963
- await this.#sleep(this.#retryPolicy.getDelay(attempt, retryAfter), options.signal, path);
1230
+ const delay = this.#retryPolicy.getDelay(attempt, retryAfter);
1231
+ this.#emit(this.#hooks.onRetry, {
1232
+ method: normalizedMethod,
1233
+ path,
1234
+ attempt,
1235
+ status: response.status,
1236
+ delayMs: delay
1237
+ });
1238
+ await this.#sleep(delay, options.signal, path);
964
1239
  continue;
965
1240
  }
966
1241
  throw new RESTError(data.message ?? response.statusText ?? `Discord REST request failed with status ${response.status}`, response.status, data.code, payload, { method: normalizedMethod, path });
@@ -983,22 +1258,29 @@ class REST {
983
1258
  throw new RESTError("Discord REST request exhausted its retry attempts", 0, undefined, undefined, { method: normalizedMethod, path });
984
1259
  } finally {
985
1260
  release();
1261
+ if (this.#localQueues.get(queueKey) === gate)
1262
+ this.#localQueues.delete(queueKey);
986
1263
  }
987
1264
  }
1265
+ #dispatch(method, path, bodyOrOptions, options) {
1266
+ if (options === undefined && isRequestData(bodyOrOptions))
1267
+ return this.request(method, path, bodyOrOptions.body, bodyOrOptions);
1268
+ return this.request(method, path, bodyOrOptions, options);
1269
+ }
988
1270
  get(path, options) {
989
- return this.request("GET", path, undefined, options);
1271
+ return this.request("GET", path, options?.body, options);
990
1272
  }
991
1273
  post(path, body, options) {
992
- return this.request("POST", path, body, options);
1274
+ return this.#dispatch("POST", path, body, options);
993
1275
  }
994
1276
  patch(path, body, options) {
995
- return this.request("PATCH", path, body, options);
1277
+ return this.#dispatch("PATCH", path, body, options);
996
1278
  }
997
1279
  put(path, body, options) {
998
- return this.request("PUT", path, body, options);
1280
+ return this.#dispatch("PUT", path, body, options);
999
1281
  }
1000
1282
  delete(path, options) {
1001
- return this.request("DELETE", path, undefined, options);
1283
+ return this.request("DELETE", path, options?.body, options);
1002
1284
  }
1003
1285
  #fileForm(payload, files) {
1004
1286
  const form = new FormData;
@@ -1018,13 +1300,22 @@ class REST {
1018
1300
  patchWithFiles(path, payload, files, options) {
1019
1301
  return this.request("PATCH", path, this.#fileForm(payload, files), options);
1020
1302
  }
1021
- async#wait(bucket, signal, path) {
1303
+ #emit(hook, ctx) {
1304
+ if (!hook)
1305
+ return;
1306
+ Promise.resolve().then(() => hook(ctx)).catch(() => {});
1307
+ }
1308
+ async#wait(bucketKey, signal, path) {
1309
+ const bucket = await this.#store.getBucket(bucketKey);
1310
+ if (!bucket)
1311
+ return;
1022
1312
  const delay = bucket.resetAt - Date.now();
1023
1313
  if (bucket.remaining <= 0 && delay > 0)
1024
1314
  await this.#sleep(delay, signal, path);
1025
1315
  }
1026
1316
  async#waitGlobal(signal, path) {
1027
- const delay = this.#globalResetAt - Date.now();
1317
+ const globalResetAt = await this.#store.getGlobalReset();
1318
+ const delay = globalResetAt - Date.now();
1028
1319
  if (delay > 0)
1029
1320
  await this.#sleep(delay, signal, path);
1030
1321
  }
@@ -1088,16 +1379,17 @@ class REST {
1088
1379
  const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
1089
1380
  return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
1090
1381
  }
1091
- #update(response, bucket, route, bucketKey) {
1382
+ async#update(response, bucketKey, route, major) {
1383
+ let currentKey = bucketKey;
1092
1384
  const serverBucket = response.headers.get("X-RateLimit-Bucket");
1093
1385
  if (serverBucket) {
1094
- this.#routeBuckets.set(route, serverBucket);
1095
- if (serverBucket !== bucketKey) {
1096
- const shared = this.#buckets.get(serverBucket) ?? bucket;
1097
- this.#buckets.set(serverBucket, shared);
1098
- bucket = shared;
1099
- }
1386
+ await this.#store.setBucketHash(route, serverBucket);
1387
+ currentKey = scopeBucket(serverBucket, major);
1100
1388
  }
1389
+ let bucket = await this.#store.getBucket(currentKey) ?? {
1390
+ remaining: 1,
1391
+ resetAt: 0
1392
+ };
1101
1393
  const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
1102
1394
  const resetAfter = Number(response.headers.get("X-RateLimit-Reset-After"));
1103
1395
  const reset = Number(response.headers.get("X-RateLimit-Reset"));
@@ -1112,12 +1404,15 @@ class REST {
1112
1404
  if (Number.isFinite(retryAfter))
1113
1405
  bucket.resetAt = Math.max(bucket.resetAt, Date.now() + retryAfter * 1000);
1114
1406
  }
1407
+ await this.#store.updateBucket(currentKey, bucket);
1115
1408
  if (response.headers.get("X-RateLimit-Global") === "true") {
1116
1409
  const retryAfter = Number(response.headers.get("Retry-After"));
1117
- if (Number.isFinite(retryAfter))
1118
- this.#globalResetAt = Math.max(this.#globalResetAt, Date.now() + retryAfter * 1000);
1410
+ if (Number.isFinite(retryAfter)) {
1411
+ const currentGlobal = await this.#store.getGlobalReset();
1412
+ await this.#store.setGlobalReset(Math.max(currentGlobal, Date.now() + retryAfter * 1000));
1413
+ }
1119
1414
  }
1120
- return bucket;
1415
+ return currentKey;
1121
1416
  }
1122
1417
  }
1123
1418
 
@@ -1263,6 +1558,13 @@ class Guild extends BaseStructure {
1263
1558
  vanityUrlCode;
1264
1559
  nsfwLevel;
1265
1560
  discoverySplash;
1561
+ rulesChannelId;
1562
+ systemChannelId;
1563
+ systemChannelFlags;
1564
+ maxMembers;
1565
+ mfaLevel;
1566
+ explicitContentFilter;
1567
+ defaultMessageNotifications;
1266
1568
  constructor(data) {
1267
1569
  super(data.id);
1268
1570
  if (!data.name)
@@ -1284,6 +1586,14 @@ class Guild extends BaseStructure {
1284
1586
  this.vanityUrlCode = data.vanity_url_code ?? null;
1285
1587
  this.nsfwLevel = data.nsfw_level ?? 0;
1286
1588
  this.discoverySplash = data.discovery_splash ?? null;
1589
+ const d = data;
1590
+ this.rulesChannelId = d.rules_channel_id ?? null;
1591
+ this.systemChannelId = d.system_channel_id ?? null;
1592
+ this.systemChannelFlags = d.system_channel_flags ?? 0;
1593
+ this.maxMembers = d.max_members ?? 0;
1594
+ this.mfaLevel = d.mfa_level ?? 0;
1595
+ this.explicitContentFilter = d.explicit_content_filter ?? 0;
1596
+ this.defaultMessageNotifications = d.default_message_notifications ?? 0;
1287
1597
  }
1288
1598
  iconURL(options = {}) {
1289
1599
  if (!this.icon)
@@ -1305,12 +1615,27 @@ class Guild extends BaseStructure {
1305
1615
  return null;
1306
1616
  return cdnURL(`/discovery-splashes/${this.id}`, this.discoverySplash, options);
1307
1617
  }
1618
+ widgetImageURL(style = "shield") {
1619
+ return `https://discord.com/api/guilds/${this.id}/widget.png?style=${style}`;
1620
+ }
1308
1621
  hasFeature(feature) {
1309
1622
  return this.features.includes(feature);
1310
1623
  }
1311
1624
  get isCommunity() {
1312
1625
  return this.hasFeature("COMMUNITY");
1313
1626
  }
1627
+ get isPartnered() {
1628
+ return this.hasFeature("PARTNERED");
1629
+ }
1630
+ get isVerified() {
1631
+ return this.hasFeature("VERIFIED");
1632
+ }
1633
+ get isBoosted() {
1634
+ return this.premiumSubscriptionCount > 0;
1635
+ }
1636
+ get boostLevel() {
1637
+ return this.premiumTier;
1638
+ }
1314
1639
  get hasVanityUrl() {
1315
1640
  return this.vanityUrlCode !== null;
1316
1641
  }
@@ -1321,58 +1646,49 @@ class Guild extends BaseStructure {
1321
1646
  return this.name;
1322
1647
  }
1323
1648
  }
1324
- // packages/structures/src/permissions.ts
1325
- var Permissions2 = {
1326
- CreateInstantInvite: 1n << 0n,
1327
- KickMembers: 1n << 1n,
1328
- BanMembers: 1n << 2n,
1329
- Administrator: 1n << 3n,
1330
- ManageChannels: 1n << 4n,
1331
- ManageGuild: 1n << 5n,
1332
- AddReactions: 1n << 6n,
1333
- ViewAuditLog: 1n << 7n,
1334
- PrioritySpeaker: 1n << 8n,
1335
- Stream: 1n << 9n,
1336
- ViewChannel: 1n << 10n,
1337
- SendMessages: 1n << 11n,
1338
- SendTTSMessages: 1n << 12n,
1339
- ManageMessages: 1n << 13n,
1340
- EmbedLinks: 1n << 14n,
1341
- AttachFiles: 1n << 15n,
1342
- ReadMessageHistory: 1n << 16n,
1343
- MentionEveryone: 1n << 17n,
1344
- UseExternalEmojis: 1n << 18n,
1345
- ViewGuildInsights: 1n << 19n,
1346
- Connect: 1n << 20n,
1347
- Speak: 1n << 21n,
1348
- MuteMembers: 1n << 22n,
1349
- DeafenMembers: 1n << 23n,
1350
- MoveMembers: 1n << 24n,
1351
- UseVAD: 1n << 25n,
1352
- ChangeNickname: 1n << 26n,
1353
- ManageNicknames: 1n << 27n,
1354
- ManageRoles: 1n << 28n,
1355
- ManageWebhooks: 1n << 29n,
1356
- ManageGuildExpressions: 1n << 30n,
1357
- UseApplicationCommands: 1n << 31n,
1358
- RequestToSpeak: 1n << 32n,
1359
- ManageEvents: 1n << 33n,
1360
- ManageThreads: 1n << 34n,
1361
- CreatePublicThreads: 1n << 35n,
1362
- CreatePrivateThreads: 1n << 36n,
1363
- UseExternalStickers: 1n << 37n,
1364
- SendMessagesInThreads: 1n << 38n,
1365
- UseEmbeddedActivities: 1n << 39n,
1366
- ModerateMembers: 1n << 40n,
1367
- ViewCreatorMonetizationAnalytics: 1n << 41n,
1368
- UseSoundboard: 1n << 42n,
1369
- CreateGuildExpressions: 1n << 43n,
1370
- CreateEvents: 1n << 44n,
1371
- UseExternalSounds: 1n << 45n,
1372
- SendVoiceMessages: 1n << 46n,
1373
- SendPolls: 1n << 49n,
1374
- UseExternalApps: 1n << 50n
1375
- };
1649
+ // packages/structures/src/resources.ts
1650
+ class AutoModerationRule extends BaseStructure {
1651
+ guildId;
1652
+ name;
1653
+ creatorId;
1654
+ eventType;
1655
+ triggerType;
1656
+ triggerMetadata;
1657
+ actions;
1658
+ enabled;
1659
+ exemptRoles;
1660
+ exemptChannels;
1661
+ constructor(data) {
1662
+ super(data.id);
1663
+ if (!/^\d{1,20}$/.test(data.guild_id))
1664
+ throw new TypeError("AutoModerationRule guild_id must be a valid snowflake.");
1665
+ this.guildId = data.guild_id;
1666
+ this.name = data.name;
1667
+ this.creatorId = data.creator_id;
1668
+ this.eventType = data.event_type;
1669
+ this.triggerType = data.trigger_type;
1670
+ this.triggerMetadata = data.trigger_metadata;
1671
+ this.actions = data.actions;
1672
+ this.enabled = data.enabled;
1673
+ this.exemptRoles = data.exempt_roles;
1674
+ this.exemptChannels = data.exempt_channels;
1675
+ }
1676
+ toJSON() {
1677
+ return {
1678
+ id: this.id,
1679
+ guildId: this.guildId,
1680
+ name: this.name,
1681
+ creatorId: this.creatorId,
1682
+ eventType: this.eventType,
1683
+ triggerType: this.triggerType,
1684
+ triggerMetadata: this.triggerMetadata,
1685
+ actions: this.actions,
1686
+ enabled: this.enabled,
1687
+ exemptRoles: this.exemptRoles,
1688
+ exemptChannels: this.exemptChannels
1689
+ };
1690
+ }
1691
+ }
1376
1692
  // packages/structures/src/interactions.ts
1377
1693
  var InteractionType = {
1378
1694
  Ping: 1,
@@ -1663,8 +1979,7 @@ class CommandInteraction extends Interaction {
1663
1979
  super(client, data);
1664
1980
  const rawOptions = data.data?.options ?? [];
1665
1981
  const resolved = data.data?.resolved ?? {};
1666
- const sub = rawOptions.find((o) => o.type === 1 || o.type === 2);
1667
- this.options = new CommandOptions(sub?.options ?? rawOptions, resolved);
1982
+ this.options = new CommandOptions(rawOptions, resolved);
1668
1983
  }
1669
1984
  }
1670
1985
 
@@ -1758,6 +2073,8 @@ function createInteraction(client, data) {
1758
2073
  }
1759
2074
 
1760
2075
  // packages/structures/src/index.ts
2076
+ var NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);
2077
+
1761
2078
  class Message extends BaseStructure {
1762
2079
  content;
1763
2080
  author;
@@ -1803,6 +2120,12 @@ class Message extends BaseStructure {
1803
2120
  get embedsSuppressed() {
1804
2121
  return (this.flags & 4) !== 0;
1805
2122
  }
2123
+ get createdTimestamp() {
2124
+ return this.timestamp.getTime();
2125
+ }
2126
+ get createdAt() {
2127
+ return this.timestamp;
2128
+ }
1806
2129
  edit(options) {
1807
2130
  if (!this.#context)
1808
2131
  throw new Error("This message is not attached to a client.");
@@ -1857,6 +2180,51 @@ class Message extends BaseStructure {
1857
2180
  throw new Error("This message is not attached to a client.");
1858
2181
  return this.#context.unpinMessage(this.channelId, this.id);
1859
2182
  }
2183
+ get isEphemeral() {
2184
+ return (this.flags & 64) !== 0;
2185
+ }
2186
+ get isPinned() {
2187
+ return this.pinned;
2188
+ }
2189
+ get isSystemMessage() {
2190
+ return !NON_SYSTEM_MESSAGE_TYPES.has(this.type);
2191
+ }
2192
+ get isCrosspost() {
2193
+ return (this.flags & 2) !== 0;
2194
+ }
2195
+ get hasAttachments() {
2196
+ return this.attachments.length > 0;
2197
+ }
2198
+ get hasEmbeds() {
2199
+ return this.embeds.length > 0;
2200
+ }
2201
+ get hasComponents() {
2202
+ return this.components.length > 0;
2203
+ }
2204
+ suppressEmbeds() {
2205
+ return this.edit({ flags: this.flags | 4 });
2206
+ }
2207
+ toJSON() {
2208
+ return {
2209
+ id: this.id,
2210
+ content: this.content,
2211
+ channelId: this.channelId,
2212
+ guildId: this.guildId,
2213
+ flags: this.flags,
2214
+ pinned: this.pinned,
2215
+ mentionEveryone: this.mentionEveryone,
2216
+ type: this.type,
2217
+ timestamp: this.timestamp.toISOString(),
2218
+ author: { id: this.author.id, username: this.author.username },
2219
+ attachments: [...this.attachments],
2220
+ embeds: [...this.embeds],
2221
+ components: [...this.components],
2222
+ mentions: this.mentions.map((u) => u.id),
2223
+ mentionRoles: [...this.mentionRoles],
2224
+ reference: this.reference,
2225
+ referencedMessage: this.referencedMessage ? this.referencedMessage.id : this.referencedMessage
2226
+ };
2227
+ }
1860
2228
  }
1861
2229
 
1862
2230
  // packages/managers/src/message.ts
@@ -1948,6 +2316,11 @@ class UserManager extends ResourceManager {
1948
2316
  }
1949
2317
  }
1950
2318
  // packages/managers/src/guild.ts
2319
+ function withQuery(path, params) {
2320
+ const suffix = params.toString();
2321
+ return suffix ? `${path}?${suffix}` : path;
2322
+ }
2323
+
1951
2324
  class GuildManager extends ResourceManager {
1952
2325
  #rest;
1953
2326
  constructor(rest) {
@@ -1977,6 +2350,47 @@ class GuildManager extends ResourceManager {
1977
2350
  async fetchInvites(id) {
1978
2351
  return this.#rest.get(Routes.guildInvites(id));
1979
2352
  }
2353
+ async fetchAuditLog(guildId, options = {}) {
2354
+ const params = new URLSearchParams;
2355
+ if (options.userId)
2356
+ params.set("user_id", options.userId);
2357
+ if (options.actionType !== undefined)
2358
+ params.set("action_type", String(options.actionType));
2359
+ if (options.before)
2360
+ params.set("before", options.before);
2361
+ if (options.after)
2362
+ params.set("after", options.after);
2363
+ if (options.limit !== undefined)
2364
+ params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
2365
+ return this.#rest.get(withQuery(Routes.guildAuditLog(guildId), params));
2366
+ }
2367
+ async fetchMembers(guildId, options = {}) {
2368
+ const params = new URLSearchParams;
2369
+ if (options.limit !== undefined)
2370
+ params.set("limit", String(Math.min(1000, Math.max(1, options.limit))));
2371
+ if (options.after)
2372
+ params.set("after", options.after);
2373
+ return this.#rest.get(withQuery(Routes.guildMembers(guildId), params));
2374
+ }
2375
+ async fetchAutoModerationRules(guildId) {
2376
+ const rules = await this.#rest.get(Routes.guildAutoModerationRules(guildId));
2377
+ return rules.map((r) => new AutoModerationRule(r));
2378
+ }
2379
+ async fetchAutoModerationRule(guildId, ruleId) {
2380
+ const rule = await this.#rest.get(Routes.guildAutoModerationRule(guildId, ruleId));
2381
+ return new AutoModerationRule(rule);
2382
+ }
2383
+ async createAutoModerationRule(guildId, options) {
2384
+ const rule = await this.#rest.post(Routes.guildAutoModerationRules(guildId), options);
2385
+ return new AutoModerationRule(rule);
2386
+ }
2387
+ async editAutoModerationRule(guildId, ruleId, options) {
2388
+ const rule = await this.#rest.patch(Routes.guildAutoModerationRule(guildId, ruleId), options);
2389
+ return new AutoModerationRule(rule);
2390
+ }
2391
+ async deleteAutoModerationRule(guildId, ruleId) {
2392
+ await this.#rest.delete(Routes.guildAutoModerationRule(guildId, ruleId));
2393
+ }
1980
2394
  }
1981
2395
  // packages/managers/src/application.ts
1982
2396
  class ApplicationCommandManager {
@@ -2079,8 +2493,11 @@ class ChannelManager extends Manager {
2079
2493
  }
2080
2494
  async deleteChannel(channelId) {
2081
2495
  await this.#rest.delete(Routes.channel(channelId));
2496
+ this.delete(channelId);
2497
+ }
2498
+ delete(channelId) {
2082
2499
  this.#messageManagers.delete(channelId);
2083
- super.delete(channelId);
2500
+ return super.delete(channelId);
2084
2501
  }
2085
2502
  send(channelId, options) {
2086
2503
  return this.messages(channelId).send(options);
@@ -2125,6 +2542,8 @@ class ChannelManager extends Manager {
2125
2542
  }
2126
2543
  async bulkDeleteMessages(channelId, messageIds) {
2127
2544
  const ids = [...messageIds];
2545
+ if (ids.length < 2 || ids.length > 100)
2546
+ throw new RangeError("bulkDelete requires between 2 and 100 message IDs.");
2128
2547
  await this.#rest.post(Routes.channelBulkDelete(channelId), {
2129
2548
  messages: ids
2130
2549
  });
@@ -2132,7 +2551,7 @@ class ChannelManager extends Manager {
2132
2551
  this.messages(channelId).delete(id);
2133
2552
  }
2134
2553
  async addReaction(channelId, messageId, emoji) {
2135
- await this.#rest.put(Routes.messageReactions(channelId, messageId, emoji));
2554
+ await this.#rest.put(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
2136
2555
  }
2137
2556
  async fetchReactions(channelId, messageId, emoji, options = {}) {
2138
2557
  const params = new URLSearchParams;
@@ -2166,9 +2585,21 @@ class ChannelManager extends Manager {
2166
2585
  createThreadFromMessage(channelId, messageId, options) {
2167
2586
  return this.threads(channelId).createFromMessage(messageId, options);
2168
2587
  }
2169
- delete(channelId) {
2170
- this.#messageManagers.delete(channelId);
2171
- return super.delete(channelId);
2588
+ async fetchPage(channelId, options = {}) {
2589
+ const limit = Math.min(100, Math.max(1, options.limit ?? 50));
2590
+ const messages = await this.fetchMessages(channelId, {
2591
+ ...options,
2592
+ limit
2593
+ });
2594
+ return {
2595
+ messages,
2596
+ before: messages[0]?.id,
2597
+ after: messages[messages.length - 1]?.id,
2598
+ hasMore: messages.length === limit
2599
+ };
2600
+ }
2601
+ async bulkDelete(channelId, messageIds) {
2602
+ return this.bulkDeleteMessages(channelId, messageIds);
2172
2603
  }
2173
2604
  clear() {
2174
2605
  this.#messageManagers.clear();
@@ -2193,6 +2624,22 @@ var GatewayOpcodes = {
2193
2624
  Hello: 10,
2194
2625
  HeartbeatAck: 11
2195
2626
  };
2627
+ var GatewayCloseCodes = {
2628
+ UnknownError: 4000,
2629
+ UnknownOpcode: 4001,
2630
+ DecodeError: 4002,
2631
+ NotAuthenticated: 4003,
2632
+ AuthenticationFailed: 4004,
2633
+ AlreadyAuthenticated: 4005,
2634
+ InvalidSeq: 4007,
2635
+ RateLimited: 4008,
2636
+ SessionTimedOut: 4009,
2637
+ InvalidShard: 4010,
2638
+ ShardingRequired: 4011,
2639
+ InvalidAPIVersion: 4012,
2640
+ InvalidIntents: 4013,
2641
+ DisallowedIntents: 4014
2642
+ };
2196
2643
  class GatewayError extends Error {
2197
2644
  code;
2198
2645
  constructor(message, code, options) {
@@ -2226,6 +2673,8 @@ class Gateway {
2226
2673
  #rejectConnect;
2227
2674
  #listeners = new Map;
2228
2675
  #sendTimestamps = [];
2676
+ ping = -1;
2677
+ #decompressor;
2229
2678
  constructor(options) {
2230
2679
  if (!options.token?.trim())
2231
2680
  throw new TypeError("A Gateway token is required.");
@@ -2246,7 +2695,12 @@ class Gateway {
2246
2695
  browser: "Discord Android",
2247
2696
  device: "Discord Android"
2248
2697
  },
2249
- presence: { status: "online", activities: [], afk: false, since: null },
2698
+ presence: {
2699
+ status: "online",
2700
+ activities: [],
2701
+ afk: false,
2702
+ since: null
2703
+ },
2250
2704
  ...options
2251
2705
  };
2252
2706
  if (this.#options.shardId < 0 || this.#options.shardId >= this.#options.shardCount)
@@ -2261,10 +2715,13 @@ class Gateway {
2261
2715
  return this.#connectPromise;
2262
2716
  this.#closed = false;
2263
2717
  this.#setState("CONNECT" /* Connect */);
2718
+ const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}&compress=zlib-stream` : url;
2719
+ if (this.#options.compress)
2720
+ this.#initDecompressor();
2264
2721
  this.#connectPromise = new Promise((resolve, reject) => {
2265
2722
  this.#resolveConnect = resolve;
2266
2723
  this.#rejectConnect = reject;
2267
- this.#open(url);
2724
+ this.#open(connectURL);
2268
2725
  });
2269
2726
  return this.#connectPromise;
2270
2727
  }
@@ -2300,12 +2757,15 @@ class Gateway {
2300
2757
  send(payload) {
2301
2758
  if (!payload || !Number.isInteger(payload.op))
2302
2759
  throw new TypeError("Gateway payload must contain an integer opcode.");
2760
+ return this.#dispatch(payload, false);
2761
+ }
2762
+ #dispatch(payload, privileged) {
2303
2763
  if (this.#ws?.readyState !== WebSocket.OPEN)
2304
2764
  return false;
2305
2765
  const now = Date.now();
2306
2766
  while (this.#sendTimestamps.length && now - this.#sendTimestamps[0] >= 60000)
2307
2767
  this.#sendTimestamps.shift();
2308
- if (this.#sendTimestamps.length >= 115) {
2768
+ if (!privileged && this.#sendTimestamps.length >= 115) {
2309
2769
  this.#emitError(new GatewayError("Gateway send rate budget exhausted."));
2310
2770
  return false;
2311
2771
  }
@@ -2376,7 +2836,11 @@ class Gateway {
2376
2836
  ws.addEventListener("message", (event) => {
2377
2837
  this.#lastMessageAt = Date.now();
2378
2838
  this.#zombieReported = false;
2379
- this.#message(ws, String(event.data));
2839
+ if (this.#options.compress && event.data instanceof ArrayBuffer) {
2840
+ this.#decompress(new Uint8Array(event.data)).then((text) => this.#message(ws, text)).catch((err) => this.#emitError(err));
2841
+ } else {
2842
+ this.#message(ws, String(event.data));
2843
+ }
2380
2844
  });
2381
2845
  ws.addEventListener("close", (event) => this.#close(ws, event.code));
2382
2846
  ws.addEventListener("error", () => this.#emitError(new GatewayError("Gateway WebSocket error")));
@@ -2384,7 +2848,7 @@ class Gateway {
2384
2848
  #startZombieDetection() {
2385
2849
  if (this.#zombieTimer)
2386
2850
  clearInterval(this.#zombieTimer);
2387
- const interval = Math.max(1, Math.min(250, this.#options.heartbeatAckTimeout, this.#options.zombieTimeout / 2));
2851
+ const interval = Math.max(1, Math.min(1000, this.#options.heartbeatAckTimeout, this.#options.zombieTimeout / 2));
2388
2852
  this.#zombieTimer = setInterval(() => {
2389
2853
  if (this.#closed || this.#ws?.readyState !== WebSocket.OPEN || this.#lastMessageAt === 0)
2390
2854
  return;
@@ -2435,18 +2899,24 @@ class Gateway {
2435
2899
  break;
2436
2900
  case GatewayOpcodes.HeartbeatAck:
2437
2901
  this.#heartbeatACK = true;
2902
+ this.ping = Date.now() - this.#heartbeatSentAt;
2438
2903
  this.#clearHeartbeatAckTimer();
2439
2904
  this.#emit("heartbeatAck", payload.d);
2440
2905
  break;
2441
2906
  case GatewayOpcodes.Reconnect:
2442
2907
  ws.close(1001, "Server requested reconnect");
2443
2908
  break;
2444
- case GatewayOpcodes.InvalidSession:
2445
- this.#sessionId = undefined;
2446
- this.#sequence = null;
2447
- this.#emit("invalidSession", payload.d);
2448
- ws.close(1000, "Invalid session");
2909
+ case GatewayOpcodes.InvalidSession: {
2910
+ const resumable = payload.d === true;
2911
+ if (!resumable) {
2912
+ this.#sessionId = undefined;
2913
+ this.#sequence = null;
2914
+ this.#resumeURL = undefined;
2915
+ }
2916
+ this.#emit("invalidSession", resumable);
2917
+ ws.close(resumable ? GatewayCloseCodes.UnknownError : 1000, resumable ? "Invalid session (resumable)" : "Invalid session");
2449
2918
  break;
2919
+ }
2450
2920
  }
2451
2921
  }
2452
2922
  #handleDispatch(event, data) {
@@ -2461,6 +2931,10 @@ class Gateway {
2461
2931
  this.#attempt = 0;
2462
2932
  this.#setState("READY" /* Ready */);
2463
2933
  this.#emit("ready", data);
2934
+ } else if (event === "RESUMED") {
2935
+ this.#attempt = 0;
2936
+ this.#setState("READY" /* Ready */);
2937
+ this.#emit("resumed", data);
2464
2938
  }
2465
2939
  this.#emit("RAW", { event: event ?? "UNKNOWN", data });
2466
2940
  if (this.#closed)
@@ -2538,12 +3012,12 @@ class Gateway {
2538
3012
  #sendHeartbeat() {
2539
3013
  this.#heartbeatACK = false;
2540
3014
  this.#heartbeatSentAt = Date.now();
2541
- if (!this.send({
3015
+ if (!this.#dispatch({
2542
3016
  op: GatewayOpcodes.Heartbeat,
2543
3017
  d: this.#sequence,
2544
3018
  s: null,
2545
3019
  t: null
2546
- })) {
3020
+ }, true)) {
2547
3021
  this.#emitError(new GatewayError("Unable to send Gateway heartbeat because the WebSocket is not open."));
2548
3022
  return;
2549
3023
  }
@@ -2576,14 +3050,23 @@ class Gateway {
2576
3050
  if (action === "identify") {
2577
3051
  this.#sequence = null;
2578
3052
  this.#sessionId = undefined;
3053
+ this.#resumeURL = undefined;
2579
3054
  }
2580
3055
  this.#setState("RECONNECT" /* Reconnect */);
2581
3056
  this.#scheduleReconnect();
2582
3057
  }
2583
3058
  #closeAction(code) {
2584
- if ([4004, 4010, 4011, 4012, 4013, 4014].includes(code))
3059
+ const fatal = [
3060
+ GatewayCloseCodes.AuthenticationFailed,
3061
+ GatewayCloseCodes.InvalidShard,
3062
+ GatewayCloseCodes.ShardingRequired,
3063
+ GatewayCloseCodes.InvalidAPIVersion,
3064
+ GatewayCloseCodes.InvalidIntents,
3065
+ GatewayCloseCodes.DisallowedIntents
3066
+ ];
3067
+ if (fatal.includes(code))
2585
3068
  return "stop";
2586
- if ([4007, 4009].includes(code))
3069
+ if (code === GatewayCloseCodes.InvalidSeq || code === GatewayCloseCodes.SessionTimedOut)
2587
3070
  return "identify";
2588
3071
  return this.#sessionId && this.#sequence !== null ? "resume" : "identify";
2589
3072
  }
@@ -2596,7 +3079,7 @@ class Gateway {
2596
3079
  return;
2597
3080
  }
2598
3081
  const delay = Math.min(this.#options.reconnectMaxDelay, this.#options.reconnectBaseDelay * 2 ** this.#attempt++);
2599
- const jitter = Math.random() * Math.min(250, Math.max(1, delay * 0.25));
3082
+ const jitter = Math.random() * Math.max(1, delay * 0.25);
2600
3083
  this.#reconnectTimer = setTimeout(() => {
2601
3084
  this.#reconnectTimer = undefined;
2602
3085
  this.#open(this.#resumeURL ?? "wss://gateway.discord.gg/?v=10&encoding=json");
@@ -2666,10 +3149,41 @@ class Gateway {
2666
3149
  this.state = next;
2667
3150
  this.#emit("stateChange", { previous, next });
2668
3151
  }
3152
+ #initDecompressor() {
3153
+ const ds = new DecompressionStream("deflate-raw");
3154
+ this.#decompressor = {
3155
+ writer: ds.writable.getWriter(),
3156
+ reader: ds.readable.getReader()
3157
+ };
3158
+ }
3159
+ async#decompress(chunk) {
3160
+ if (!this.#decompressor)
3161
+ this.#initDecompressor();
3162
+ const { writer, reader } = this.#decompressor;
3163
+ await writer.write(chunk);
3164
+ const parts = [];
3165
+ while (true) {
3166
+ const { done, value } = await Promise.race([
3167
+ reader.read(),
3168
+ new Promise((resolve) => setTimeout(() => resolve({ done: true, value: undefined }), 50))
3169
+ ]);
3170
+ if (done || !value)
3171
+ break;
3172
+ parts.push(value);
3173
+ }
3174
+ const total = parts.reduce((n, p) => n + p.length, 0);
3175
+ const merged = new Uint8Array(total);
3176
+ let offset = 0;
3177
+ for (const p of parts) {
3178
+ merged.set(p, offset);
3179
+ offset += p.length;
3180
+ }
3181
+ return new TextDecoder().decode(merged);
3182
+ }
2669
3183
  }
2670
3184
 
2671
3185
  // packages/core/src/index.ts
2672
- class EventEmitter {
3186
+ class EventEmitter2 {
2673
3187
  #listeners = new Map;
2674
3188
  on(event, listener) {
2675
3189
  if (typeof listener !== "function")
@@ -2691,6 +3205,9 @@ class EventEmitter {
2691
3205
  this.#listeners.get(event)?.delete(listener);
2692
3206
  return this;
2693
3207
  }
3208
+ removeListener(event, listener) {
3209
+ return this.off(event, listener);
3210
+ }
2694
3211
  removeAllListeners(event) {
2695
3212
  if (event === undefined)
2696
3213
  this.#listeners.clear();
@@ -2727,7 +3244,7 @@ class EventEmitter {
2727
3244
  }
2728
3245
  }
2729
3246
 
2730
- class Client extends EventEmitter {
3247
+ class Client extends EventEmitter2 {
2731
3248
  options;
2732
3249
  rest;
2733
3250
  users;
@@ -2743,14 +3260,26 @@ class Client extends EventEmitter {
2743
3260
  user;
2744
3261
  readyAt;
2745
3262
  state = "idle";
3263
+ get token() {
3264
+ return this.state === "destroyed" ? null : this.options.token ?? null;
3265
+ }
2746
3266
  get uptime() {
2747
3267
  return this.readyAt ? Date.now() - this.readyAt.getTime() : null;
2748
3268
  }
3269
+ get ping() {
3270
+ return this.#gateway.ping;
3271
+ }
2749
3272
  isReady() {
2750
3273
  return this.state === "ready";
2751
3274
  }
2752
3275
  setPresence(data) {
2753
- return this.#gateway.setPresence?.(data) ?? false;
3276
+ return this.#gateway.setPresence(data);
3277
+ }
3278
+ setVoiceState(data) {
3279
+ return this.#gateway.setVoiceState(data);
3280
+ }
3281
+ requestGuildMembers(data) {
3282
+ return this.#gateway.requestGuildMembers(data);
2754
3283
  }
2755
3284
  #gateway;
2756
3285
  #resourceContext;
@@ -2794,6 +3323,13 @@ class Client extends EventEmitter {
2794
3323
  this.application.commands = new ApplicationCommandManager(this.rest, appId);
2795
3324
  this.emit("ready" /* Ready */, this.user);
2796
3325
  });
3326
+ this.#gateway.on("RESUMED", () => {
3327
+ this.state = "ready";
3328
+ this.emit("resumed" /* Resumed */);
3329
+ });
3330
+ this.#gateway.on("invalidSession", (isRecoverable) => {
3331
+ this.emit("invalidSession" /* InvalidSession */, isRecoverable);
3332
+ });
2797
3333
  this.#gateway.on("open", () => this.emit("open" /* Open */));
2798
3334
  this.#gateway.on("close", (data) => {
2799
3335
  if (this.state !== "destroyed")
@@ -2826,6 +3362,7 @@ class Client extends EventEmitter {
2826
3362
  this.emit("guildUnavailable" /* GuildUnavailable */, payload);
2827
3363
  return;
2828
3364
  }
3365
+ const wasCached = this.guilds.has(payload.id);
2829
3366
  const guild = new Guild(payload);
2830
3367
  this.guilds.set(guild.id, guild);
2831
3368
  for (const member of payload.members ?? [])
@@ -2835,7 +3372,7 @@ class Client extends EventEmitter {
2835
3372
  ...payload.threads ?? []
2836
3373
  ])
2837
3374
  this.channels.set(channelData.id, new Channel(channelData, this.#resourceContext));
2838
- this.emit("guildCreate" /* GuildCreate */, payload);
3375
+ this.emit(wasCached ? "guildAvailable" /* GuildAvailable */ : "guildCreate" /* GuildCreate */, payload);
2839
3376
  });
2840
3377
  this.#gateway.on("GUILD_UPDATE", (data) => {
2841
3378
  const guild = new Guild(data);
@@ -2964,10 +3501,52 @@ class Client extends EventEmitter {
2964
3501
  return Promise.reject(new Error("Client is unauthenticated."));
2965
3502
  return this.rest.post(`/webhooks/${this.user.id}/${token}`, data);
2966
3503
  }
3504
+ fetchWebhook(id, token) {
3505
+ return this.rest.get(token ? `/webhooks/${id}/${token}` : `/webhooks/${id}`);
3506
+ }
3507
+ fetchGuildPreview(guildId) {
3508
+ return this.rest.get(`/guilds/${guildId}/preview`);
3509
+ }
3510
+ fetchVoiceRegions() {
3511
+ return this.rest.get("/voice/regions");
3512
+ }
3513
+ fetchInvite(code, options) {
3514
+ const query = new URLSearchParams;
3515
+ if (options?.withCounts)
3516
+ query.set("with_counts", "true");
3517
+ if (options?.withExpiration)
3518
+ query.set("with_expiration", "true");
3519
+ if (options?.guildScheduledEventId)
3520
+ query.set("guild_scheduled_event_id", options.guildScheduledEventId);
3521
+ const qs = query.toString();
3522
+ return this.rest.get(`/invites/${code}${qs ? `?${qs}` : ""}`);
3523
+ }
3524
+ fetchSticker(id) {
3525
+ return this.rest.get(`/stickers/${id}`);
3526
+ }
3527
+ fetchPremiumStickerPacks() {
3528
+ return this.rest.get(`/sticker-packs`);
3529
+ }
3530
+ fetchGuildTemplate(code) {
3531
+ return this.rest.get(`/guilds/templates/${code}`);
3532
+ }
3533
+ generateInvite(options) {
3534
+ if (!this.user)
3535
+ throw new Error("Client must be ready to generate invite.");
3536
+ const params = new URLSearchParams({
3537
+ client_id: this.user.id,
3538
+ scopes: (options?.scopes ?? ["bot"]).join(" ")
3539
+ });
3540
+ if (options?.permissions) {
3541
+ params.set("permissions", String(options.permissions));
3542
+ }
3543
+ return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
3544
+ }
2967
3545
  }
2968
3546
  export {
2969
3547
  Client,
2970
3548
  ClientEvent,
3549
+ Collector,
2971
3550
  GatewayIntentBits,
2972
3551
  IntentBits,
2973
3552
  Intents,
@@ -2980,4 +3559,4 @@ export {
2980
3559
  resolveGatewayIntents
2981
3560
  };
2982
3561
 
2983
- //# debugId=813519968046205864756E2164756E21
3562
+ //# debugId=5F8825194034E27864756E2164756E21