seedcord 0.16.0-next.3 → 0.16.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -27,11 +27,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
 
28
28
  //#endregion
29
29
  require("reflect-metadata");
30
+ let _seedcord_core = require("@seedcord/core");
31
+ let _seedcord_core_internal = require("@seedcord/core/internal");
32
+ let discord_js = require("discord.js");
30
33
  let _seedcord_errors = require("@seedcord/errors");
31
34
  let _seedcord_errors_internal = require("@seedcord/errors/internal");
32
- let discord_js = require("discord.js");
33
- let _seedcord_core_internal = require("@seedcord/core/internal");
34
- let _seedcord_core = require("@seedcord/core");
35
35
  let _seedcord_utils = require("@seedcord/utils");
36
36
  let _seedcord_services = require("@seedcord/services");
37
37
  let chalk = require("chalk");
@@ -41,60 +41,12 @@ let _seedcord_utils_internal = require("@seedcord/utils/internal");
41
41
  let node_fs = require("node:fs");
42
42
  let node_path = require("node:path");
43
43
  let node_url = require("node:url");
44
+ let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
44
45
  let _seedcord_types_internal = require("@seedcord/types/internal");
45
46
  let envapt = require("envapt");
46
47
  let node_crypto = require("node:crypto");
47
48
  node_crypto = __toESM(node_crypto, 1);
48
49
 
49
- //#region src/metadataKeys.ts
50
- /** @internal */
51
- const InteractionMetadataKey = Symbol("seedcord:interaction:metadata");
52
- /** @internal */
53
- const InteractionRouteKeys = {
54
- ["slash"]: Symbol("seedcord:interaction:slash"),
55
- ["button"]: Symbol("seedcord:interaction:button"),
56
- ["modal"]: Symbol("seedcord:interaction:modal"),
57
- ["stringMenu"]: Symbol("seedcord:interaction:stringMenu"),
58
- ["userMenu"]: Symbol("seedcord:interaction:userMenu"),
59
- ["roleMenu"]: Symbol("seedcord:interaction:roleMenu"),
60
- ["channelMenu"]: Symbol("seedcord:interaction:channelMenu"),
61
- ["mentionableMenu"]: Symbol("seedcord:interaction:mentionableMenu"),
62
- ["messageContextMenu"]: Symbol("seedcord:interaction:messageContextMenu"),
63
- ["userContextMenu"]: Symbol("seedcord:interaction:userContextMenu"),
64
- ["autocomplete"]: Symbol("seedcord:interaction:autocomplete")
65
- };
66
- /** @internal */
67
- const MiddlewareMetadataKey = Symbol("seedcord:middleware:metadata");
68
- /** @internal */
69
- const EventMetadataKey = Symbol("seedcord:event:metadata");
70
- /** @internal */
71
- const CommandMetadataKey = Symbol("seedcord:command:metadata");
72
- /** @internal */
73
- const GatedMetadataKey = Symbol("seedcord:gated:metadata");
74
- /** @internal */
75
- const SubscribeMetadataKey = Symbol("seedcord:subscribe:metadata");
76
-
77
- //#endregion
78
- //#region src/bot/decorators/Command.ts
79
- function RegisterCommand(scope, guilds = []) {
80
- return (ctor) => {
81
- const meta = scope === "global" ? { scope } : {
82
- scope,
83
- guilds
84
- };
85
- const existingMeta = Reflect.getOwnMetadata(CommandMetadataKey, ctor);
86
- if (existingMeta) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorCommandAlreadyRegistered, [
87
- ctor.name,
88
- existingMeta.scope,
89
- scope
90
- ]);
91
- if (scope === "global" && guilds.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);
92
- if (scope === "guild" && (!Array.isArray(guilds) || guilds.length === 0)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);
93
- Reflect.defineMetadata(CommandMetadataKey, meta, ctor);
94
- };
95
- }
96
-
97
- //#endregion
98
50
  //#region src/bot/decorators/Events.ts
99
51
  /**
100
52
  * Registers an event handler class with one or more Discord.js events.
@@ -138,126 +90,12 @@ function RegisterCommand(scope, guilds = []) {
138
90
  */
139
91
  function RegisterEvent(...defs) {
140
92
  return function(constructor) {
141
- const existing = Reflect.getMetadata(EventMetadataKey, constructor) ?? [];
93
+ const existing = Reflect.getMetadata(_seedcord_core_internal.EventMetadataKey, constructor) ?? [];
142
94
  const entries = defs.map(([event, options]) => ({
143
95
  event,
144
96
  frequency: options?.frequency ?? "on"
145
97
  }));
146
- Reflect.defineMetadata(EventMetadataKey, [...existing, ...entries], constructor);
147
- };
148
- }
149
-
150
- //#endregion
151
- //#region src/miscellaneous/deriveEventActor.ts
152
- function deriveEventActor(args) {
153
- const tuple = Array.isArray(args) ? args : [args];
154
- for (const arg of tuple) {
155
- if (arg instanceof discord_js.Message) return {
156
- guild: arg.guild,
157
- member: arg.member,
158
- user: arg.author,
159
- channelId: arg.channelId
160
- };
161
- if (arg instanceof discord_js.GuildMember) return {
162
- guild: arg.guild,
163
- member: arg,
164
- user: arg.user,
165
- channelId: null
166
- };
167
- if (arg instanceof discord_js.User) return {
168
- guild: null,
169
- member: null,
170
- user: arg,
171
- channelId: null
172
- };
173
- }
174
- return {
175
- guild: null,
176
- member: null,
177
- user: null,
178
- channelId: null
179
- };
180
- }
181
-
182
- //#endregion
183
- //#region src/bot/gates/effects.ts
184
- const commitQueues = /* @__PURE__ */ new WeakMap();
185
- function isEffectGate(gate) {
186
- return typeof gate.check === "function" && typeof gate.commit === "function";
187
- }
188
- /**
189
- * Runs a gate's check and, when it passes and carries an effect, queues its commit against the context. A
190
- * refusal throws here before the queue, so a refused effect gate never commits. The combinators and the
191
- * runner both go through this, so an effect gate inside `and`/`or` queues the same way a top-level one does.
192
- */
193
- async function runCheck(gate, ctx) {
194
- await gate.check(ctx);
195
- if (!isEffectGate(gate)) return;
196
- let queue = commitQueues.get(ctx);
197
- if (!queue) {
198
- queue = [];
199
- commitQueues.set(ctx, queue);
200
- }
201
- queue.push(() => gate.commit(ctx));
202
- }
203
- /** Drains the queued commits once the whole set has passed. */
204
- async function runCommits(ctx) {
205
- const queue = commitQueues.get(ctx);
206
- if (!queue) return;
207
- for (const commit of queue) await commit();
208
- }
209
- /** Drops a context's queue. The runner calls this after every run, so a refused or drained run leaves no stale queue behind. */
210
- function discardCommits(ctx) {
211
- commitQueues.delete(ctx);
212
- }
213
- /** The current queued-commit count, a mark an `or` arm rolls back to if it refuses. */
214
- function markCommits(ctx) {
215
- return commitQueues.get(ctx)?.length ?? 0;
216
- }
217
- /** Drops commits queued after `mark`, so an `or` arm that queued an effect then refused leaves nothing for the winning arm to carry. */
218
- function rollbackCommits(ctx, mark) {
219
- const queue = commitQueues.get(ctx);
220
- if (queue && queue.length > mark) queue.length = mark;
221
- }
222
-
223
- //#endregion
224
- //#region src/bot/gates/runGates.ts
225
- /**
226
- * Runs each gate's check in order, so the first refusal propagates to the dispatcher boundary. An effect
227
- * gate's commit runs once the whole set passes.
228
- */
229
- async function runGates(gates, ctx) {
230
- try {
231
- for (const gate of gates) await runCheck(gate, ctx);
232
- await runCommits(ctx);
233
- } finally {
234
- discardCommits(ctx);
235
- }
236
- }
237
- function interactionGateContext(interaction, core) {
238
- return {
239
- kind: "interaction",
240
- interaction,
241
- core,
242
- user: interaction.user,
243
- guild: interaction.guild,
244
- member: interaction.member instanceof discord_js.GuildMember ? interaction.member : null,
245
- guildId: interaction.guildId,
246
- channelId: interaction.channelId
247
- };
248
- }
249
- function eventGateContext(eventName, args, core) {
250
- const actor = deriveEventActor(args);
251
- return {
252
- kind: "event",
253
- core,
254
- eventName,
255
- payload: args,
256
- user: actor.user,
257
- guild: actor.guild,
258
- member: actor.member,
259
- guildId: actor.guild?.id ?? null,
260
- channelId: actor.channelId
98
+ Reflect.defineMetadata(_seedcord_core_internal.EventMetadataKey, [...existing, ...entries], constructor);
261
99
  };
262
100
  }
263
101
 
@@ -296,22 +134,10 @@ function eventGateContext(eventName, args, core) {
296
134
  */
297
135
  function Gated(...gates) {
298
136
  return function(ctor) {
299
- const existing = Reflect.getMetadata(GatedMetadataKey, ctor) ?? [];
300
- Reflect.defineMetadata(GatedMetadataKey, [...existing, ...gates], ctor);
137
+ const existing = Reflect.getMetadata(_seedcord_core_internal.GatedMetadataKey, ctor) ?? [];
138
+ Reflect.defineMetadata(_seedcord_core_internal.GatedMetadataKey, [...existing, ...gates], ctor);
301
139
  };
302
140
  }
303
- /**
304
- * Runs the gates a handler was decorated with against the given context. The dispatcher calls this before
305
- * `execute`, inside the boundary, so a refusal renders or drops.
306
- *
307
- * @param handlerCtor - The handler class whose attached gates are read from metadata.
308
- * @param ctx - The context passed to each gate, supplying what the gate checks against.
309
- */
310
- async function runHandlerGates(handlerCtor, ctx) {
311
- const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor);
312
- if (!gates) return;
313
- await runGates(gates, ctx);
314
- }
315
141
 
316
142
  //#endregion
317
143
  //#region src/miscellaneous/areRoutes.ts
@@ -368,7 +194,7 @@ let SelectMenuKind = /* @__PURE__ */ function(SelectMenuKind) {
368
194
  */
369
195
  function SlashRoute(...routes) {
370
196
  return function(constructor) {
371
- storeMetadata("slash", routes, constructor);
197
+ storeMetadata(_seedcord_core_internal.InteractionRoutes.Slash, routes, constructor);
372
198
  };
373
199
  }
374
200
  /**
@@ -384,7 +210,7 @@ function SlashRoute(...routes) {
384
210
  */
385
211
  function ButtonRoute(...defs) {
386
212
  return function(constructor) {
387
- storeComponentRoute("button", defs, constructor);
213
+ storeComponentRoute(_seedcord_core_internal.InteractionRoutes.Button, defs, constructor);
388
214
  };
389
215
  }
390
216
  /**
@@ -400,7 +226,7 @@ function ButtonRoute(...defs) {
400
226
  */
401
227
  function ModalRoute(...defs) {
402
228
  return function(constructor) {
403
- storeComponentRoute("modal", defs, constructor);
229
+ storeComponentRoute(_seedcord_core_internal.InteractionRoutes.Modal, defs, constructor);
404
230
  };
405
231
  }
406
232
  /**
@@ -426,7 +252,7 @@ function ModalRoute(...defs) {
426
252
  */
427
253
  function ContextMenuRoute(kind, ...names) {
428
254
  return function(constructor) {
429
- storeMetadata(kind === discord_js.ApplicationCommandType.User ? "userContextMenu" : "messageContextMenu", names, constructor);
255
+ storeMetadata(kind === discord_js.ApplicationCommandType.User ? _seedcord_core_internal.InteractionRoutes.UserContextMenu : _seedcord_core_internal.InteractionRoutes.MessageContextMenu, names, constructor);
430
256
  };
431
257
  }
432
258
  /**
@@ -451,7 +277,7 @@ function ContextMenuRoute(kind, ...names) {
451
277
  */
452
278
  function AutocompleteRoute(...routes) {
453
279
  return function(constructor) {
454
- storeMetadata("autocomplete", routes, constructor);
280
+ storeMetadata(_seedcord_core_internal.InteractionRoutes.Autocomplete, routes, constructor);
455
281
  };
456
282
  }
457
283
  /**
@@ -476,11 +302,11 @@ function AutocompleteRoute(...routes) {
476
302
  function SelectMenuRoute(type, ...defs) {
477
303
  return function(constructor) {
478
304
  storeComponentRoute({
479
- ["string"]: "stringMenu",
480
- ["user"]: "userMenu",
481
- ["role"]: "roleMenu",
482
- ["channel"]: "channelMenu",
483
- ["mentionable"]: "mentionableMenu"
305
+ ["string"]: _seedcord_core_internal.InteractionRoutes.StringMenu,
306
+ ["user"]: _seedcord_core_internal.InteractionRoutes.UserMenu,
307
+ ["role"]: _seedcord_core_internal.InteractionRoutes.RoleMenu,
308
+ ["channel"]: _seedcord_core_internal.InteractionRoutes.ChannelMenu,
309
+ ["mentionable"]: _seedcord_core_internal.InteractionRoutes.MentionableMenu
484
310
  }[type], defs, constructor);
485
311
  };
486
312
  }
@@ -489,12 +315,12 @@ function storeComponentRoute(route, defs, constructor) {
489
315
  Reflect.defineMetadata(_seedcord_core_internal.ComponentDefsKey, defs, constructor);
490
316
  }
491
317
  function storeMetadata(route, routes, constructor) {
492
- const key = InteractionRouteKeys[route];
318
+ const key = _seedcord_core_internal.InteractionRouteKeys[route];
493
319
  const savedRoutes = Reflect.getMetadata(key, constructor);
494
320
  const existing = areRoutes(savedRoutes) ? savedRoutes : [];
495
321
  const toStore = Array.isArray(routes) ? routes : [routes];
496
322
  Reflect.defineMetadata(key, [...existing, ...toStore], constructor);
497
- Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
323
+ Reflect.defineMetadata(_seedcord_core_internal.InteractionMetadataKey, true, constructor);
498
324
  }
499
325
 
500
326
  //#endregion
@@ -536,111 +362,34 @@ function Middleware(type, priority = 0, options = {}) {
536
362
  type,
537
363
  ...options.events && options.events.length > 0 && { events: options.events }
538
364
  };
539
- Reflect.defineMetadata(MiddlewareMetadataKey, metadata, ctor);
365
+ Reflect.defineMetadata(_seedcord_core_internal.MiddlewareMetadataKey, metadata, ctor);
540
366
  };
541
367
  }
542
368
 
543
369
  //#endregion
544
- //#region src/bot/gates/Gate.ts
370
+ //#region src/bot/gates/catalog/IgnoreBots.ts
545
371
  /**
546
- * Builds a {@link Gate} from a check. The check refuses by throwing a Notice or a Silence and passes
547
- * by returning. The required context is inferred from `ctx`, so annotate it as narrowly as the fields
548
- * the check reads. No annotation defaults to {@link GateContextBase}, an agnostic gate that fits every
549
- * handler.
550
- *
551
- * @typeParam Name - The gate's name captured as a literal, so a mismatch error can name the gate.
552
- * @typeParam Ctx - The context the check reads, inferred from the `ctx` annotation.
553
- *
554
- * @param name - The gate's name, used in mismatch errors.
555
- * @param fn - The check, which refuses by throwing a Notice or a Silence and passes by returning.
372
+ * Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
373
+ * Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
374
+ * the decorator line. Takes no options, so attach it directly without calling it.
556
375
  *
557
- * @see {@link defineEffectGate}
558
376
  * @see {@link Gated}
559
377
  *
560
378
  * @example
561
379
  * ```ts
562
- * // a gate factory that takes its own option and closes over it
563
- * function MinRoleCount(min: number): Gate<InteractionGateContext, 'MinRoleCount'> {
564
- * return defineGate('MinRoleCount', (ctx: InteractionGateContext) => {
565
- * if (ctx.member && ctx.member.roles.cache.size < min) throw new NotEnoughRoles(min);
566
- * });
567
- * }
568
- * ```
569
- *
570
- * @example
571
- * ```ts
572
- * // agnostic gate (no ctx annotation): fits any handler
573
- * const Owner = defineGate('owner', (ctx) => {
574
- * if (!ctx.user) throw new NotOwner();
575
- * });
576
- * ```
577
- *
578
- * @example
579
- * ```ts
580
- * // interaction-only gate: annotate ctx to narrow the required context
581
- * const SlashGate = defineGate('slash', (ctx: InteractionGateContext) => {
582
- * void ctx.interaction;
583
- * });
584
- * ```
585
- *
586
- * @example
587
- * ```ts
588
380
  * import { Events } from 'discord.js';
589
381
  *
590
- * // event-only gate keyed to one event
591
- * const OnMessage = defineGate('msg', (ctx: EventGateContext<Events.MessageCreate>) => {
592
- * void ctx.payload;
593
- * });
594
- * ```
595
- */
596
- function defineGate(name, fn) {
597
- return {
598
- name,
599
- check: async (ctx) => fn(ctx)
600
- };
601
- }
602
- /**
603
- * Builds an {@link EffectGate} from a `check` and a `commit`. `check` peeks and refuses by throwing, and
604
- * `commit` applies the side effect, running only once the whole gate set passes so a later refusal never
605
- * commits. In an `or`, a refusing arm's queued commit is rolled back. This is how {@link Cooldown} peeks
606
- * in `check` and charges the slot in `commit`.
607
- *
608
- * @typeParam Name - The gate's name captured as a literal, so a mismatch error can name the gate.
609
- * @typeParam Ctx - The context both `check` and `commit` read, inferred from the `ctx` annotation.
610
- *
611
- * @param name - The gate's name, used in mismatch errors.
612
- * @param check - Peeks and refuses by throwing, without applying the side effect.
613
- * @param commit - Applies the side effect, running only after the whole gate set passes.
614
- *
615
- * @see {@link defineGate}
616
- * @see {@link Gated}
617
- * @see {@link Cooldown}
618
- *
619
- * @example
620
- * ```ts
621
- * // an effect-gate factory that takes its own limit and closes over it
622
- * function UsesPerDay(max: number): EffectGate<GateContextBase, 'UsesPerDay'> {
623
- * return defineEffectGate(
624
- * 'UsesPerDay',
625
- * (ctx) => {
626
- * // peek and refuse by throwing, do not mutate yet
627
- * if (usedToday(ctx.user) >= max) throw new OutOfUsesNotice(max);
628
- * },
629
- * (ctx) => {
630
- * // runs only after the whole gate set passes
631
- * recordUse(ctx.user);
632
- * }
633
- * );
382
+ * \@Gated(IgnoreBots)
383
+ * class OnMessage extends EventHandler<Events.MessageCreate> {
384
+ * async execute() {
385
+ * // ...
386
+ * }
634
387
  * }
635
388
  * ```
636
389
  */
637
- function defineEffectGate(name, check, commit) {
638
- return {
639
- name,
640
- check: async (ctx) => check(ctx),
641
- commit: async (ctx) => commit(ctx)
642
- };
643
- }
390
+ const IgnoreBots = (0, _seedcord_core.defineGate)("IgnoreBots", (ctx) => {
391
+ if (ctx.user?.bot) throw new _seedcord_core.Silence("actor is a bot");
392
+ });
644
393
 
645
394
  //#endregion
646
395
  //#region src/bot/notices/utils.ts
@@ -659,63 +408,18 @@ function labelFor(subject) {
659
408
 
660
409
  //#endregion
661
410
  //#region src/bot/notices/index.ts
662
- var GateNotice = class extends _seedcord_core.Notice {
663
- render() {
664
- return { components: [new _seedcord_core_internal.NoticeCard(this.message).component] };
665
- }
666
- };
667
- var NotOwner = class extends GateNotice {
668
- constructor(message = "Only the bot owner can use this.") {
669
- super(message);
670
- }
671
- };
672
- var NotInGuild = class extends GateNotice {
673
- constructor(message = "This can only be used in a server.") {
674
- super(message);
675
- }
676
- };
677
- var NotInDm = class extends GateNotice {
678
- constructor(message = "This can only be used in a direct message.") {
679
- super(message);
680
- }
681
- };
682
- var NotNsfw = class extends GateNotice {
411
+ var NotNsfw = class extends _seedcord_core_internal.GateNotice {
683
412
  constructor(message = "This can only be used in an age-restricted channel.") {
684
413
  super(message);
685
414
  }
686
415
  };
687
- var OnCooldown = class extends GateNotice {
688
- expires;
689
- constructor(expires, message) {
690
- super(message ?? `You are doing that too fast. Try again <t:${(0, _seedcord_utils.toEpochSeconds)(expires)}:R>.`);
691
- this.expires = expires;
692
- }
693
- };
694
- var MissingRole = class extends GateNotice {
416
+ var MissingRole = class extends _seedcord_core_internal.GateNotice {
695
417
  role;
696
418
  constructor(message, role) {
697
419
  super(message ?? (role ? `You need the ${role.name} role to use this.` : "You do not have the required role."));
698
420
  this.role = role;
699
421
  }
700
422
  };
701
- var NotAllowed = class extends _seedcord_core.Notice {
702
- constructor() {
703
- super("not allowed");
704
- }
705
- render() {
706
- return { components: [new _seedcord_core_internal.NoticeCard("You are not allowed to use this command.").component] };
707
- }
708
- };
709
- var NeedsAny = class extends _seedcord_core.Notice {
710
- summaries;
711
- constructor(summaries) {
712
- super("not allowed");
713
- this.summaries = summaries;
714
- }
715
- render() {
716
- return { components: [new _seedcord_core_internal.NoticeCard(`You need any of:\n${this.summaries.map((summary) => `• ${summary}`).join("\n")}`).component] };
717
- }
718
- };
719
423
  var RoleHigherThanMe = class extends _seedcord_core.Notice {
720
424
  role;
721
425
  botRole;
@@ -801,241 +505,6 @@ var CouldNotFindChannel = class extends _seedcord_core.Notice {
801
505
  }
802
506
  };
803
507
 
804
- //#endregion
805
- //#region src/bot/gates/combinators.ts
806
- function and(...gates) {
807
- return defineGate("and", async (ctx) => {
808
- for (const gate of gates) await runCheck(gate, ctx);
809
- });
810
- }
811
- function isOrOptions(arg) {
812
- return !("check" in arg);
813
- }
814
- function or(...args) {
815
- const last = args.at(-1);
816
- const options = last !== void 0 && isOrOptions(last) ? last : void 0;
817
- const gates = options ? args.slice(0, -1) : args;
818
- return defineGate("or", async (ctx) => {
819
- const summaries = [];
820
- let everyArmHasSummary = true;
821
- for (const gate of gates) {
822
- const mark = markCommits(ctx);
823
- try {
824
- await runCheck(gate, ctx);
825
- return;
826
- } catch (error) {
827
- if (error instanceof _seedcord_core.Notice && !error.report) {
828
- rollbackCommits(ctx, mark);
829
- if (error.summary === void 0) everyArmHasSummary = false;
830
- else summaries.push(error.summary);
831
- continue;
832
- }
833
- throw error;
834
- }
835
- }
836
- if (options) throw typeof options.fail === "function" ? options.fail(ctx) : options.fail;
837
- if (everyArmHasSummary && summaries.length > 0) throw new NeedsAny(summaries);
838
- throw new NotAllowed();
839
- });
840
- }
841
-
842
- //#endregion
843
- //#region src/bot/gates/catalog/options.ts
844
- function pickNotice(options, makeDefault) {
845
- return options?.notice ?? makeDefault(options?.message);
846
- }
847
-
848
- //#endregion
849
- //#region src/bot/gates/catalog/access.ts
850
- /**
851
- * Passes only for a user id listed in `config.ownerIds`, else refuses.
852
- *
853
- * Agnostic, so it attaches to interaction and event handlers alike. With no `ownerIds` configured it refuses every caller. Pass {@link GateNoticeOptions} to reword or replace the refusal.
854
- *
855
- * @param options - Reword the default refusal with `message`, or replace it with `notice`.
856
- *
857
- * @see {@link Gated}
858
- *
859
- * @example
860
- * ```ts
861
- * \@Gated(OwnerOnly())
862
- * \@SlashRoute('shutdown')
863
- * class ShutdownHandler extends SlashHandler<'shutdown'> {
864
- * async execute() {
865
- * // ...
866
- * }
867
- * }
868
- * ```
869
- *
870
- * @example
871
- * ```ts
872
- * // reword the refusal, keeping the embed styling
873
- * OwnerOnly({ message: 'Owners only.' });
874
- * ```
875
- */
876
- function OwnerOnly(options) {
877
- return defineGate("OwnerOnly", (ctx) => {
878
- const owners = ctx.core.config.ownerIds ?? [];
879
- if (ctx.user && owners.includes(ctx.user.id)) return;
880
- throw pickNotice(options, (message) => new NotOwner(message));
881
- });
882
- }
883
- /**
884
- * Passes inside a guild, else refuses.
885
- *
886
- * Agnostic, so it attaches to any handler kind. Often paired with {@link RequirePermissions} in an {@link and}.
887
- *
888
- * @param options - Reword the default refusal with `message`, or replace it with `notice`.
889
- *
890
- * @see {@link Gated}
891
- *
892
- * @example
893
- * ```ts
894
- * import { PermissionFlagsBits } from 'discord.js';
895
- *
896
- * \@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.Administrator]))
897
- * \@SlashRoute('maintenance')
898
- * class Maintenance extends SlashHandler<'maintenance'> {
899
- * async execute() {
900
- * // ...
901
- * }
902
- * }
903
- * ```
904
- */
905
- function GuildOnly(options) {
906
- return defineGate("GuildOnly", (ctx) => {
907
- if (ctx.guild) return;
908
- throw pickNotice(options, (message) => new NotInGuild(message));
909
- });
910
- }
911
- /**
912
- * Passes in a direct message, else refuses.
913
- *
914
- * Agnostic, so it attaches to any handler kind. The inverse of {@link GuildOnly}, so combining the two in an {@link and} can never pass.
915
- *
916
- * @param options - Reword the default refusal with `message`, or replace it with `notice`.
917
- *
918
- * @see {@link Gated}
919
- *
920
- * @example
921
- * ```ts
922
- * \@Gated(DmOnly())
923
- * \@SlashRoute('verify')
924
- * class VerifyHandler extends SlashHandler<'verify'> {
925
- * async execute() {
926
- * // ...
927
- * }
928
- * }
929
- * ```
930
- */
931
- function DmOnly(options) {
932
- return defineGate("DmOnly", (ctx) => {
933
- if (!ctx.guild) return;
934
- throw pickNotice(options, (message) => new NotInDm(message));
935
- });
936
- }
937
-
938
- //#endregion
939
- //#region src/bot/gates/catalog/Cooldown.ts
940
- let bucketSeq = 0;
941
- function scopeValue(ctx, per) {
942
- if (per === "guild") return ctx.guildId ?? "global";
943
- if (per === "channel") return ctx.channelId ?? "global";
944
- return ctx.user?.id ?? "global";
945
- }
946
- /**
947
- * Allows `limit` uses per window, scoped by `per`. A number `duration` is **seconds**, a string is
948
- * a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError** at construction. The
949
- * slot is charged in commit, only after the whole gate set passes, so a later refusal never burns the cooldown.
950
- * Each call gets its own bucket, so two handlers never share a window. Reword the refusal with {@link CooldownOptions.message}
951
- * or replace it with {@link CooldownOptions.notice}.
952
- *
953
- * @param duration - A number is seconds, a string is a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError**.
954
- * @param options - Sets the scope with `per`, the uses per window with `limit`, and the refusal text with `message` or `notice`.
955
- *
956
- * @see {@link Gated}
957
- * @see {@link RateLimiter}
958
- *
959
- * @example
960
- * ```ts
961
- * // a number argument is SECONDS
962
- * \@Gated(Cooldown(5))
963
- * \@SlashRoute('daily')
964
- * class DailyHandler extends SlashHandler<'daily'> {
965
- * async execute() {
966
- * // ...
967
- * }
968
- * }
969
- * ```
970
- *
971
- * @example
972
- * ```ts
973
- * // a string is a duration, here scoped per guild instead of per user
974
- * Cooldown('30m', { per: 'guild' });
975
- * ```
976
- *
977
- * @example
978
- * ```ts
979
- * // a burst, 3 uses per 10 minutes per user, then it refuses until a slot frees
980
- * \@Gated(Cooldown('10m', { limit: 3 }))
981
- * \@SlashRoute('report')
982
- * class ReportHandler extends SlashHandler<'report'> {
983
- * async execute() {
984
- * // ...
985
- * }
986
- * }
987
- * ```
988
- */
989
- function Cooldown(duration, options) {
990
- let delay;
991
- if (typeof duration === "number") delay = duration * 1e3;
992
- else {
993
- const parsed = (0, _seedcord_utils.parseDuration)(duration);
994
- if (parsed === null) throw new _seedcord_errors_internal.SeedcordTypeError(_seedcord_errors.SeedcordErrorCode.GateInvalidCooldownDuration, [duration]);
995
- delay = parsed;
996
- }
997
- const bucket = `cooldown:${bucketSeq++}`;
998
- const per = options?.per ?? "user";
999
- const window = options?.limit === void 0 ? { delay } : {
1000
- delay,
1001
- limit: options.limit
1002
- };
1003
- const keyOf = (ctx) => `${bucket}:${scopeValue(ctx, per)}`;
1004
- return defineEffectGate("Cooldown", (ctx) => {
1005
- const result = ctx.core.rateLimiter.peek(keyOf(ctx), window);
1006
- if (!result.limited) return;
1007
- if (options?.notice) throw options.notice(result.expires);
1008
- throw new OnCooldown(result.expires, options?.message?.(result.expires));
1009
- }, (ctx) => {
1010
- ctx.core.rateLimiter.hit(keyOf(ctx), window);
1011
- });
1012
- }
1013
-
1014
- //#endregion
1015
- //#region src/bot/gates/catalog/IgnoreBots.ts
1016
- /**
1017
- * Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
1018
- * Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
1019
- * the decorator line. Takes no options, so attach it directly without calling it.
1020
- *
1021
- * @see {@link Gated}
1022
- *
1023
- * @example
1024
- * ```ts
1025
- * import { Events } from 'discord.js';
1026
- *
1027
- * \@Gated(IgnoreBots)
1028
- * class OnMessage extends EventHandler<Events.MessageCreate> {
1029
- * async execute() {
1030
- * // ...
1031
- * }
1032
- * }
1033
- * ```
1034
- */
1035
- const IgnoreBots = defineGate("IgnoreBots", (ctx) => {
1036
- if (ctx.user?.bot) throw new _seedcord_core.Silence("actor is a bot");
1037
- });
1038
-
1039
508
  //#endregion
1040
509
  //#region src/bot/gates/catalog/Nsfw.ts
1041
510
  function channelIsNsfw(channel) {
@@ -1064,9 +533,9 @@ function channelIsNsfw(channel) {
1064
533
  * ```
1065
534
  */
1066
535
  function Nsfw(options) {
1067
- return defineGate("Nsfw", (ctx) => {
536
+ return (0, _seedcord_core.defineGate)("Nsfw", (ctx) => {
1068
537
  if (channelIsNsfw(ctx.interaction.channel)) return;
1069
- throw pickNotice(options, (message) => new NotNsfw(message));
538
+ throw (0, _seedcord_core_internal.pickNotice)(options, (message) => new NotNsfw(message));
1070
539
  });
1071
540
  }
1072
541
 
@@ -1100,6 +569,7 @@ function checkPermissions(a, b, c, d, e) {
1100
569
 
1101
570
  //#endregion
1102
571
  //#region src/bot/gates/catalog/permissions.ts
572
+ const UNCACHED_MEMBER = "Your server member data could not be resolved. Try again.";
1103
573
  /**
1104
574
  * Requires the caller to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
1105
575
  *
@@ -1124,15 +594,16 @@ function checkPermissions(a, b, c, d, e) {
1124
594
  * ```
1125
595
  */
1126
596
  function RequirePermissions(scope, options) {
1127
- return defineGate("RequirePermissions", (ctx) => {
1128
- if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
597
+ return (0, _seedcord_core.defineGate)("RequirePermissions", (ctx) => {
598
+ if (!ctx.guild) throw (0, _seedcord_core_internal.pickNotice)(options?.notInGuild, (message) => new _seedcord_core_internal.NotInGuild(message));
599
+ if (!ctx.member) throw (0, _seedcord_core_internal.pickNotice)(options?.notInGuild, (message) => new _seedcord_core_internal.NotInGuild(message ?? UNCACHED_MEMBER));
1129
600
  checkPermissions(ctx.member, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
1130
601
  });
1131
602
  }
1132
603
  /**
1133
604
  * Requires the bot to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
1134
605
  *
1135
- * Checks the bot's own member, so unlike {@link RequirePermissions} it attaches to a modal handler too.
606
+ * It reads the bot's own member, so it attaches to modal handlers as well.
1136
607
  *
1137
608
  * @param scope - The permission flag bits the bot must all hold.
1138
609
  * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
@@ -1153,9 +624,9 @@ function RequirePermissions(scope, options) {
1153
624
  * ```
1154
625
  */
1155
626
  function RequireBotPermissions(scope, options) {
1156
- return defineGate("RequireBotPermissions", (ctx) => {
627
+ return (0, _seedcord_core.defineGate)("RequireBotPermissions", (ctx) => {
1157
628
  const botMember = ctx.guild?.members.me;
1158
- if (!ctx.guild || !botMember) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
629
+ if (!ctx.guild || !botMember) throw (0, _seedcord_core_internal.pickNotice)(options?.notInGuild, (message) => new _seedcord_core_internal.NotInGuild(message));
1159
630
  checkPermissions(botMember, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
1160
631
  });
1161
632
  }
@@ -1182,11 +653,12 @@ function RequireBotPermissions(scope, options) {
1182
653
  * ```
1183
654
  */
1184
655
  function RequireRole(roleId, options) {
1185
- return defineGate("RequireRole", (ctx) => {
1186
- if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
656
+ return (0, _seedcord_core.defineGate)("RequireRole", (ctx) => {
657
+ if (!ctx.guild) throw (0, _seedcord_core_internal.pickNotice)(options?.notInGuild, (message) => new _seedcord_core_internal.NotInGuild(message));
658
+ if (!ctx.member) throw (0, _seedcord_core_internal.pickNotice)(options?.notInGuild, (message) => new _seedcord_core_internal.NotInGuild(message ?? UNCACHED_MEMBER));
1187
659
  if (ctx.member.roles.cache.has(roleId)) return;
1188
660
  const role = ctx.guild.roles.cache.get(roleId) ?? null;
1189
- throw pickNotice(options?.missingRole, (message) => new MissingRole(message, role));
661
+ throw (0, _seedcord_core_internal.pickNotice)(options?.missingRole, (message) => new MissingRole(message, role));
1190
662
  });
1191
663
  }
1192
664
 
@@ -2691,7 +2163,7 @@ function Subscribe(subscriber, options) {
2691
2163
  subscriber,
2692
2164
  frequency: options?.frequency
2693
2165
  };
2694
- Reflect.defineMetadata(SubscribeMetadataKey, meta, constructor);
2166
+ Reflect.defineMetadata(_seedcord_core_internal.SubscribeMetadataKey, meta, constructor);
2695
2167
  };
2696
2168
  }
2697
2169
 
@@ -3003,10 +2475,10 @@ var CommandRegistry = class {
3003
2475
  }
3004
2476
  isCommandClass(obj) {
3005
2477
  if (typeof obj !== "function") return false;
3006
- return obj.prototype instanceof _seedcord_core.BuilderComponent && Reflect.hasMetadata(CommandMetadataKey, obj);
2478
+ return obj.prototype instanceof _seedcord_core.BuilderComponent && Reflect.hasMetadata(_seedcord_core_internal.CommandMetadataKey, obj);
3007
2479
  }
3008
2480
  registerCommand(ctor, rel) {
3009
- const meta = Reflect.getMetadata(CommandMetadataKey, ctor);
2481
+ const meta = Reflect.getMetadata(_seedcord_core_internal.CommandMetadataKey, ctor);
3010
2482
  if (!meta) return;
3011
2483
  const comp = new ctor().component;
3012
2484
  const commandType = comp instanceof _discordjs_builders.SlashCommandBuilder ? "slash command" : "context menu command";
@@ -3082,6 +2554,75 @@ var CommandRegistry = class {
3082
2554
  }
3083
2555
  };
3084
2556
 
2557
+ //#endregion
2558
+ //#region src/miscellaneous/deriveEventActor.ts
2559
+ function deriveEventActor(args) {
2560
+ const tuple = Array.isArray(args) ? args : [args];
2561
+ for (const arg of tuple) {
2562
+ if (arg instanceof discord_js.Message) return {
2563
+ guild: arg.guild,
2564
+ member: arg.member,
2565
+ user: arg.author,
2566
+ channelId: arg.channelId
2567
+ };
2568
+ if (arg instanceof discord_js.GuildMember) return {
2569
+ guild: arg.guild,
2570
+ member: arg,
2571
+ user: arg.user,
2572
+ channelId: null
2573
+ };
2574
+ if (arg instanceof discord_js.User) return {
2575
+ guild: null,
2576
+ member: null,
2577
+ user: arg,
2578
+ channelId: null
2579
+ };
2580
+ }
2581
+ return {
2582
+ guild: null,
2583
+ member: null,
2584
+ user: null,
2585
+ channelId: null
2586
+ };
2587
+ }
2588
+
2589
+ //#endregion
2590
+ //#region src/bot/gates/runGates.ts
2591
+ function interactionGateContext(interaction, core) {
2592
+ const rawMember = interaction.member;
2593
+ const memberRoleIds = rawMember instanceof discord_js.GuildMember ? [...rawMember.roles.cache.keys()].filter((id) => id !== interaction.guildId) : rawMember?.roles ?? [];
2594
+ return {
2595
+ kind: "interaction",
2596
+ interaction,
2597
+ core,
2598
+ user: interaction.user,
2599
+ guild: interaction.guild,
2600
+ member: rawMember instanceof discord_js.GuildMember ? rawMember : null,
2601
+ userId: interaction.user.id,
2602
+ guildId: interaction.guildId,
2603
+ channelId: interaction.channelId,
2604
+ memberRoleIds,
2605
+ memberPermissions: interaction.memberPermissions?.bitfield ?? null
2606
+ };
2607
+ }
2608
+ function eventGateContext(eventName, args, core) {
2609
+ const actor = deriveEventActor(args);
2610
+ return {
2611
+ kind: "event",
2612
+ core,
2613
+ eventName,
2614
+ payload: args,
2615
+ user: actor.user,
2616
+ guild: actor.guild,
2617
+ member: actor.member,
2618
+ userId: actor.user?.id ?? null,
2619
+ guildId: actor.guild?.id ?? null,
2620
+ channelId: actor.channelId,
2621
+ memberRoleIds: actor.member ? [...actor.member.roles.cache.keys()].filter((id) => id !== actor.guild?.id) : [],
2622
+ memberPermissions: actor.member?.permissions.bitfield ?? null
2623
+ };
2624
+ }
2625
+
3085
2626
  //#endregion
3086
2627
  //#region src/miscellaneous/FaultThrottle.ts
3087
2628
  /**
@@ -3391,7 +2932,7 @@ var EventDispatcher = class {
3391
2932
  if (index !== -1) this.middlewares.splice(index, 1);
3392
2933
  }
3393
2934
  registerMiddleware(middlewareCtor, relativePath) {
3394
- const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
2935
+ const metadata = Reflect.getMetadata(_seedcord_core_internal.MiddlewareMetadataKey, middlewareCtor);
3395
2936
  if (metadata?.type !== "event") return;
3396
2937
  if (this.middlewares.some((entry) => entry.ctor === middlewareCtor)) return;
3397
2938
  this.middlewares.push({
@@ -3416,14 +2957,14 @@ var EventDispatcher = class {
3416
2957
  }
3417
2958
  isEventHandlerClass(obj) {
3418
2959
  if (typeof obj !== "function") return false;
3419
- return obj.prototype instanceof EventHandler && Reflect.hasMetadata(EventMetadataKey, obj);
2960
+ return obj.prototype instanceof EventHandler && Reflect.hasMetadata(_seedcord_core_internal.EventMetadataKey, obj);
3420
2961
  }
3421
2962
  isMiddlewareClass(obj) {
3422
2963
  if (typeof obj !== "function") return false;
3423
- return obj.prototype instanceof EventMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
2964
+ return obj.prototype instanceof EventMiddleware && Reflect.hasMetadata(_seedcord_core_internal.MiddlewareMetadataKey, obj);
3424
2965
  }
3425
2966
  registerHandler(handlerClass, relativePath) {
3426
- const raw = Reflect.getMetadata(EventMetadataKey, handlerClass);
2967
+ const raw = Reflect.getMetadata(_seedcord_core_internal.EventMetadataKey, handlerClass);
3427
2968
  const register = (key, frequency) => {
3428
2969
  let handlers = this.eventMap.get(key);
3429
2970
  if (!handlers) {
@@ -3479,7 +3020,7 @@ var EventDispatcher = class {
3479
3020
  try {
3480
3021
  this.logger.debug(`Processing ${chalk.default.bold.green(eventName)} with ${chalk.default.gray(ctor.name)}`);
3481
3022
  const handler = new ctor(args, this.core, eventName);
3482
- await runHandlerGates(ctor, eventGateContext(eventName, args, this.core));
3023
+ await (0, _seedcord_core_internal.runHandlerGates)(ctor, eventGateContext(eventName, args, this.core));
3483
3024
  await handler.execute();
3484
3025
  } catch (caught) {
3485
3026
  handleEventFault(caught, String(eventName), ctor.name, args, this.core);
@@ -3571,17 +3112,17 @@ var InteractionDispatcher = class {
3571
3112
  middlewares = [];
3572
3113
  hmrHandler;
3573
3114
  routeTypes = [
3574
- ["slash", this.slashMap],
3575
- ["button", this.buttonMap],
3576
- ["modal", this.modalMap],
3577
- ["stringMenu", this.stringSelectMap],
3578
- ["userMenu", this.userSelectMap],
3579
- ["roleMenu", this.roleSelectMap],
3580
- ["channelMenu", this.channelSelectMap],
3581
- ["mentionableMenu", this.mentionableSelectMap],
3582
- ["messageContextMenu", this.messageContextMenuMap],
3583
- ["userContextMenu", this.userContextMenuMap],
3584
- ["autocomplete", this.autocompleteMap]
3115
+ [_seedcord_core_internal.InteractionRoutes.Slash, this.slashMap],
3116
+ [_seedcord_core_internal.InteractionRoutes.Button, this.buttonMap],
3117
+ [_seedcord_core_internal.InteractionRoutes.Modal, this.modalMap],
3118
+ [_seedcord_core_internal.InteractionRoutes.StringMenu, this.stringSelectMap],
3119
+ [_seedcord_core_internal.InteractionRoutes.UserMenu, this.userSelectMap],
3120
+ [_seedcord_core_internal.InteractionRoutes.RoleMenu, this.roleSelectMap],
3121
+ [_seedcord_core_internal.InteractionRoutes.ChannelMenu, this.channelSelectMap],
3122
+ [_seedcord_core_internal.InteractionRoutes.MentionableMenu, this.mentionableSelectMap],
3123
+ [_seedcord_core_internal.InteractionRoutes.MessageContextMenu, this.messageContextMenuMap],
3124
+ [_seedcord_core_internal.InteractionRoutes.UserContextMenu, this.userContextMenuMap],
3125
+ [_seedcord_core_internal.InteractionRoutes.Autocomplete, this.autocompleteMap]
3585
3126
  ];
3586
3127
  constructor(core) {
3587
3128
  this.core = core;
@@ -3626,7 +3167,7 @@ var InteractionDispatcher = class {
3626
3167
  getArtifacts(handlerClass) {
3627
3168
  const artifacts = [];
3628
3169
  for (const [routeType] of this.routeTypes) {
3629
- const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3170
+ const meta = Reflect.getMetadata(_seedcord_core_internal.InteractionRouteKeys[routeType], handlerClass);
3630
3171
  if (areRoutes(meta)) artifacts.push({
3631
3172
  routeType,
3632
3173
  routes: meta
@@ -3682,7 +3223,7 @@ var InteractionDispatcher = class {
3682
3223
  }, this.logger);
3683
3224
  }
3684
3225
  registerMiddleware(middlewareCtor, relativePath) {
3685
- const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
3226
+ const metadata = Reflect.getMetadata(_seedcord_core_internal.MiddlewareMetadataKey, middlewareCtor);
3686
3227
  if (metadata?.type !== "interaction") return;
3687
3228
  if (this.middlewares.some((entry) => entry.ctor === middlewareCtor)) return;
3688
3229
  if (this.middlewares.some((entry) => entry.ctor.name === middlewareCtor.name)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateMiddleware, [middlewareCtor.name]);
@@ -3695,16 +3236,16 @@ var InteractionDispatcher = class {
3695
3236
  }
3696
3237
  isHandlerClass(obj) {
3697
3238
  if (typeof obj !== "function") return false;
3698
- return obj.prototype instanceof InteractionHandler && Reflect.hasMetadata(InteractionMetadataKey, obj) || obj.prototype instanceof AutocompleteHandler && Reflect.hasMetadata(InteractionMetadataKey, obj);
3239
+ return obj.prototype instanceof InteractionHandler && Reflect.hasMetadata(_seedcord_core_internal.InteractionMetadataKey, obj) || obj.prototype instanceof AutocompleteHandler && Reflect.hasMetadata(_seedcord_core_internal.InteractionMetadataKey, obj);
3699
3240
  }
3700
3241
  isMiddlewareClass(obj) {
3701
3242
  if (typeof obj !== "function") return false;
3702
- return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
3243
+ return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(_seedcord_core_internal.MiddlewareMetadataKey, obj);
3703
3244
  }
3704
3245
  registerHandler(handlerClass, relativePath) {
3705
3246
  const writes = [];
3706
3247
  for (const [routeType, map] of this.routeTypes) {
3707
- const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3248
+ const meta = Reflect.getMetadata(_seedcord_core_internal.InteractionRouteKeys[routeType], handlerClass);
3708
3249
  if (!areRoutes(meta)) continue;
3709
3250
  for (const route of meta) {
3710
3251
  const existing = map.get(route);
@@ -3732,7 +3273,7 @@ var InteractionDispatcher = class {
3732
3273
  return;
3733
3274
  }
3734
3275
  for (const [routeType, map] of this.routeTypes) {
3735
- const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
3276
+ const meta = Reflect.getMetadata(_seedcord_core_internal.InteractionRouteKeys[routeType], handlerClass);
3736
3277
  if (!areRoutes(meta)) continue;
3737
3278
  meta.forEach((route) => map.delete(route));
3738
3279
  }
@@ -3767,7 +3308,7 @@ var InteractionDispatcher = class {
3767
3308
  }
3768
3309
  this.logger.debug(`Processing ${chalk.default.bold.green(key)} with ${chalk.default.gray(HandlerCtor.name)}`);
3769
3310
  const handler = new HandlerCtor(interaction, this.core);
3770
- if (!interaction.isAutocomplete()) await runHandlerGates(HandlerCtor, interactionGateContext(interaction, this.core));
3311
+ if (!interaction.isAutocomplete()) await (0, _seedcord_core_internal.runHandlerGates)(HandlerCtor, interactionGateContext(interaction, this.core));
3771
3312
  await handler.execute();
3772
3313
  } catch (caught) {
3773
3314
  await handleInteractionFault(caught, interaction, this.core);
@@ -4197,7 +3738,7 @@ var Bus = class extends Plugin {
4197
3738
  }
4198
3739
  }
4199
3740
  getArtifacts(ctor) {
4200
- return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
3741
+ return [Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, ctor).subscriber];
4201
3742
  }
4202
3743
  /** @internal */
4203
3744
  async init() {
@@ -4231,7 +3772,7 @@ var Bus = class extends Plugin {
4231
3772
  }, this.logger);
4232
3773
  }
4233
3774
  registerSubscriber(handler) {
4234
- const options = Reflect.getMetadata(SubscribeMetadataKey, handler);
3775
+ const options = Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, handler);
4235
3776
  let handlers = this.subscribersMap.get(options.subscriber);
4236
3777
  if (!handlers) {
4237
3778
  handlers = [];
@@ -4253,7 +3794,7 @@ var Bus = class extends Plugin {
4253
3794
  }
4254
3795
  isSubscriber(obj) {
4255
3796
  if (typeof obj !== "function") return false;
4256
- return obj.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, obj);
3797
+ return obj.prototype instanceof Subscriber && Reflect.hasMetadata(_seedcord_core_internal.SubscribeMetadataKey, obj);
4257
3798
  }
4258
3799
  /**
4259
3800
  * Publishes an event to its subscribers and native listeners.
@@ -4287,6 +3828,11 @@ var Bus = class extends Plugin {
4287
3828
  }
4288
3829
  };
4289
3830
 
3831
+ //#endregion
3832
+ //#region src/version.ts
3833
+ /** Package version */
3834
+ const version = "0.16.0-next.4";
3835
+
4290
3836
  //#endregion
4291
3837
  //#region src/Seedcord.ts
4292
3838
  /**
@@ -4303,6 +3849,8 @@ var Seedcord = class Seedcord extends Pluggable {
4303
3849
  * @internal
4304
3850
  * */
4305
3851
  [_seedcord_types_internal.SeedcordBrand] = true;
3852
+ /** The framework package version this instance runs on. */
3853
+ version = version;
4306
3854
  static isInstantiated = false;
4307
3855
  /** @see {@link CoordinatedShutdown} */
4308
3856
  shutdown;
@@ -4312,7 +3860,7 @@ var Seedcord = class Seedcord extends Pluggable {
4312
3860
  bus;
4313
3861
  /** @see {@link Bot} */
4314
3862
  bot;
4315
- /** @see {@link RateLimiter} */
3863
+ /** @see {@link IRateLimiter} */
4316
3864
  rateLimiter;
4317
3865
  /** @see {@link HealthCheck} */
4318
3866
  healthCheck;
@@ -4338,14 +3886,11 @@ var Seedcord = class Seedcord extends Pluggable {
4338
3886
  this.hmrManager.init();
4339
3887
  this.bus = new Bus(this);
4340
3888
  this.bot = new Bot(this);
4341
- this.rateLimiter = new _seedcord_services.RateLimiter();
3889
+ this.rateLimiter = new _seedcord_rate_limiter.MemoryRateLimiter();
4342
3890
  this.healthCheck = new _seedcord_services.HealthCheck(this.shutdown, config.healthCheck);
4343
3891
  this.registerStartupTasks();
4344
3892
  }
4345
- /**
4346
- * Resets the singleton state.
4347
- * @internal
4348
- */
3893
+ /** @internal */
4349
3894
  static reset() {
4350
3895
  Seedcord.isInstantiated = false;
4351
3896
  }
@@ -4390,11 +3935,6 @@ var Seedcord = class Seedcord extends Pluggable {
4390
3935
  }
4391
3936
  };
4392
3937
 
4393
- //#endregion
4394
- //#region src/index.ts
4395
- /** Package version */
4396
- const version = "0.16.0-next.3";
4397
-
4398
3938
  //#endregion
4399
3939
  exports.ArraySource = ArraySource;
4400
3940
  exports.AutocompleteHandler = AutocompleteHandler;
@@ -4404,14 +3944,11 @@ exports.ButtonRoute = ButtonRoute;
4404
3944
  exports.CommandMentions = CommandMentions;
4405
3945
  exports.ContextMenuHandler = ContextMenuHandler;
4406
3946
  exports.ContextMenuRoute = ContextMenuRoute;
4407
- exports.Cooldown = Cooldown;
4408
3947
  exports.CursorSource = CursorSource;
4409
- exports.DmOnly = DmOnly;
4410
3948
  exports.Emojis = Emojis;
4411
3949
  exports.EventHandler = EventHandler;
4412
3950
  exports.EventMiddleware = EventMiddleware;
4413
3951
  exports.Gated = Gated;
4414
- exports.GuildOnly = GuildOnly;
4415
3952
  exports.HmrModuleHandler = HmrModuleHandler;
4416
3953
  exports.IgnoreBots = IgnoreBots;
4417
3954
  exports.InteractionHandler = InteractionHandler;
@@ -4421,11 +3958,15 @@ exports.MiddlewareType = MiddlewareType;
4421
3958
  exports.ModalHandler = ModalHandler;
4422
3959
  exports.ModalRoute = ModalRoute;
4423
3960
  exports.Nsfw = Nsfw;
4424
- exports.OwnerOnly = OwnerOnly;
4425
3961
  exports.Paginator = Paginator;
4426
3962
  exports.Pluggable = Pluggable;
4427
3963
  exports.Plugin = Plugin;
4428
- exports.RegisterCommand = RegisterCommand;
3964
+ Object.defineProperty(exports, 'RegisterCommand', {
3965
+ enumerable: true,
3966
+ get: function () {
3967
+ return _seedcord_core.RegisterCommand;
3968
+ }
3969
+ });
4429
3970
  exports.RegisterEvent = RegisterEvent;
4430
3971
  exports.ReplySender = ReplySender;
4431
3972
  exports.RequireBotPermissions = RequireBotPermissions;
@@ -4440,11 +3981,8 @@ exports.SlashRoute = SlashRoute;
4440
3981
  exports.Subscribe = Subscribe;
4441
3982
  exports.Subscriber = Subscriber;
4442
3983
  exports.WebhookLog = WebhookLog;
4443
- exports.and = and;
4444
3984
  exports.checkBotPermissions = checkBotPermissions;
4445
3985
  exports.checkPermissions = checkPermissions;
4446
- exports.defineEffectGate = defineEffectGate;
4447
- exports.defineGate = defineGate;
4448
3986
  exports.fetchGuildMember = fetchGuildMember;
4449
3987
  exports.fetchManyGuildMembers = fetchManyGuildMembers;
4450
3988
  exports.fetchManyUsers = fetchManyUsers;
@@ -4454,7 +3992,6 @@ exports.fetchUser = fetchUser;
4454
3992
  exports.getBotRole = getBotRole;
4455
3993
  exports.getConfirmation = getConfirmation;
4456
3994
  exports.hasPermsToAssign = hasPermsToAssign;
4457
- exports.or = or;
4458
3995
  exports.updateMemberRoles = updateMemberRoles;
4459
3996
  exports.version = version;
4460
3997
  Object.keys(_seedcord_core).forEach(function (k) {
@@ -4471,6 +4008,13 @@ Object.keys(_seedcord_errors).forEach(function (k) {
4471
4008
  });
4472
4009
  });
4473
4010
 
4011
+ Object.keys(_seedcord_rate_limiter).forEach(function (k) {
4012
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4013
+ enumerable: true,
4014
+ get: function () { return _seedcord_rate_limiter[k]; }
4015
+ });
4016
+ });
4017
+
4474
4018
  Object.keys(_seedcord_services).forEach(function (k) {
4475
4019
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4476
4020
  enumerable: true,