@seedcord/gateway 0.1.0-next.2 → 0.1.0-next.3

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.d.mts CHANGED
@@ -1,20 +1,20 @@
1
- import { BuilderComponent, CoreBase, DispatchContext, Gate, GateContextBase, GateNoticeOptions, MessageContextMenuRegistry, Notice, OptionKind, PageView, RegisterCommand, RequiredOf, SlashOptionRegistry, UserContextMenuRegistry } from "@seedcord/core";
2
- import { AnyCustomId, DecodedParams, GateFitsWith, HasComponentDefs, PageCursor } from "@seedcord/core/internal";
3
- import { APIInteractionGuildMember, APIMessageComponentEmoji, AnySelectMenuInteraction, ApplicationCommand, ApplicationCommandOptionChoiceData, ApplicationCommandType, ApplicationEmoji, AutocompleteInteraction, BooleanCache, ButtonInteraction, ButtonStyle, CacheType, CacheTypeReducer, ChannelSelectMenuInteraction, ChannelType, ChatInputCommandInteraction, Client, ClientEvents, ClientOptions, Collection, CommandInteractionOption, ContextMenuCommandInteraction, Events, Guild, GuildEmoji, GuildMember, Interaction, MentionableSelectMenuInteraction, Message, MessageContextMenuCommandInteraction, ModalSubmitInteraction, Role, RoleSelectMenuInteraction, Snowflake, StringSelectMenuInteraction, TextChannel, TextChannelResolvable, User, UserContextMenuCommandInteraction, UserSelectMenuInteraction, WebhookClient } from "discord.js";
1
+ import { AnyCustomId, AutocompletableNames, BaseReplySender, ChoiceValueOf, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, MatchArms, ModalLike, PageCursor, SingleParams, SlashOptions as SlashOptions$1 } from "@seedcord/core/internal";
2
+ import { AutocompleteOptions, BaseHandler, BuilderComponent, CoreBase, DispatchContext, Gate, GateContextBase, GateNoticeOptions, GuildPermissionsContext, Notice, PageView, PermissionScope, RepliableHandler as RepliableHandler$1, RequiredOf, SelectMenuKind, SlashOptionRegistry } from "@seedcord/core";
4
3
  import { Logger } from "@seedcord/logger";
5
4
  import { ActionRowBuilder, ButtonBuilder, ContextMenuCommandBuilder, SlashCommandBuilder } from "@discordjs/builders";
5
+ import { APIInteractionGuildMember, APIMessageComponentEmoji, APIModalInteractionResponseCallbackData, AnySelectMenuInteraction, ApplicationCommand, ApplicationCommandOptionChoiceData, ApplicationCommandType, ApplicationEmoji, AutocompleteInteraction, BooleanCache, ButtonInteraction, ButtonStyle, CacheType, CacheTypeReducer, ChannelSelectMenuInteraction, ChatInputCommandInteraction, Client, ClientEvents, ClientOptions, Collection, CommandInteractionOption, ContextMenuCommandInteraction, Events, Guild, GuildEmoji, GuildMember, Interaction, MentionableSelectMenuInteraction, Message, MessageContextMenuCommandInteraction, ModalSubmitInteraction, Role, RoleSelectMenuInteraction, Snowflake, StringSelectMenuInteraction, TextChannel, TextChannelResolvable, User, UserContextMenuCommandInteraction, UserSelectMenuInteraction } from "discord.js";
6
+ import { UUID } from "node:crypto";
6
7
  import { EventMap, NoEvents, TypedEventEmitter } from "@seedcord/event-emitter";
7
- import { CoordinatedShutdown, CoordinatedStartup, StartupPhase } from "@seedcord/services";
8
+ import { APIMessageTopLevelComponent } from "discord-api-types/v10";
9
+ import { CoordinatedShutdown, CoordinatedStartup, ShutdownPhase, StartupPhase, StartupPhase as StartupPhase$1 } from "@seedcord/core/node";
8
10
  import { SeedcordBrand, SeedcordInstance } from "@seedcord/types/internal";
9
- import { UUID } from "node:crypto";
10
- import { BotConfig, Config, EmojiMap, HmrAware, HmrUpdateEvent, IRateLimiter, Nullable, ReplyResponse, Tail, V2Component } from "@seedcord/types";
11
- export * from "@seedcord/core";
11
+ import { BotConfig, Config, DeferOpts, EmojiMap, HmrAware, HmrUpdateEvent, IRateLimiter, ReplyResponse, SendOpts, Tail, V2Component } from "@seedcord/types";
12
12
  export * from "@seedcord/errors";
13
- export * from "@seedcord/utils";
13
+ export * from "@seedcord/core";
14
14
  export * from "@seedcord/logger";
15
15
  export * from "@seedcord/event-emitter";
16
+ export * from "@seedcord/utils";
16
17
  export * from "@seedcord/rate-limiter";
17
- export * from "@seedcord/services";
18
18
  export type * from "@seedcord/types";
19
19
 
20
20
  //#region src/bot/utilities/miscellaneous/contextMenuLeaves.d.ts
@@ -138,7 +138,7 @@ declare class Pluggable<TPluggableEvents extends EventMap<TPluggableEvents> = No
138
138
  * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
139
139
  * ```
140
140
  */
141
- attach<Key extends string, Ctor extends PluginCtor>(this: this, key: Key, Plugin: Ctor, startupPhase: StartupPhase, ...args: PluginArgs<Ctor>): this & Record<Key, InstanceType<Ctor>>;
141
+ attach<Key extends string, Ctor extends PluginCtor>(this: this, key: Key, Plugin: Ctor, startupPhase: StartupPhase$1, ...args: PluginArgs<Ctor>): this & Record<Key, InstanceType<Ctor>>;
142
142
  /** @internal */
143
143
  removeListener<TEventKey extends Extract<keyof TPluggableEvents, string | symbol>>(event: TEventKey, listener: (...args: TPluggableEvents[TEventKey]) => void): this;
144
144
  /** @internal */
@@ -147,7 +147,7 @@ declare class Pluggable<TPluggableEvents extends EventMap<TPluggableEvents> = No
147
147
  //#endregion
148
148
  //#region src/subscribers/types/Subscriptions.d.ts
149
149
  /**
150
- * Where a reported fault came from. A union over `kind`.
150
+ * Where a reported fault came from.
151
151
  */
152
152
  type FaultSource = InteractionFaultSource | EventFaultSource;
153
153
  /**
@@ -155,19 +155,16 @@ type FaultSource = InteractionFaultSource | EventFaultSource;
155
155
  */
156
156
  interface InteractionFaultSource {
157
157
  kind: 'interaction';
158
- /** Which interaction kind produced the fault. */
159
158
  interactionKind: 'slash' | 'context-menu' | 'button' | 'select' | 'modal';
160
159
  /** Slash or context-menu command name, null for component and modal interactions. */
161
160
  command: string | null;
162
161
  /** Component or modal customId, null for slash and context-menu interactions. */
163
162
  customId: string | null;
164
- /** User who triggered the interaction. */
165
163
  userId: string;
166
164
  /** Guild the interaction came from, null in DMs. */
167
165
  guildId: string | null;
168
166
  /** Channel the interaction came from, null when unavailable. */
169
167
  channelId: string | null;
170
- /** The interaction's own id. */
171
168
  interactionId: string;
172
169
  /** The raw interaction, made JSON-safe at serialization with filterCirculars. */
173
170
  raw: unknown;
@@ -180,25 +177,26 @@ interface EventFaultSource {
180
177
  kind: 'event';
181
178
  eventName: string;
182
179
  handler: string;
183
- /** Actor derived from the event args, null when none is present. */
184
180
  userId: string | null;
185
- /** Guild derived from the event args, null outside a guild. */
181
+ /** Null outside a guild. */
186
182
  guildId: string | null;
187
- /** Channel derived from the event args, null when none is present. */
188
183
  channelId: string | null;
189
184
  /** The raw event args, made JSON-safe at serialization with filterCirculars. */
190
185
  raw: unknown;
191
186
  }
192
- /**
193
- * Default subscribers that are always available in the framework.
194
- */
195
187
  interface DefaultSubscriptions {
196
188
  /** Triggered when an unhandled exception (a raw non-Notice throw) occurs */
197
189
  unknownException: {
198
190
  uuid: UUID;
199
191
  error: Error;
200
- guild: Nullable<Guild>;
201
- user: Nullable<User>;
192
+ guild?: {
193
+ id: string;
194
+ name: string;
195
+ } | undefined;
196
+ user?: {
197
+ id: string;
198
+ username: string;
199
+ } | undefined;
202
200
  metadata?: unknown;
203
201
  };
204
202
  /** Triggered when a reported Notice (`report: true`) is caught */
@@ -209,51 +207,35 @@ interface DefaultSubscriptions {
209
207
  };
210
208
  }
211
209
  /**
212
- * Custom subscribers defined by the application.
213
- *
214
- * This interface can be augmented via declaration merging to add
215
- * type-safe custom subscriber definitions for emitting custom subscribers anywhere in the application.
210
+ * Custom subscribers defined by the application. Augment it via declaration merging to type your
211
+ * own subscriber payloads.
216
212
  *
217
213
  * @example
218
214
  * ```typescript
219
215
  * declare module '@seedcord/gateway' {
220
216
  * interface Subscriptions {
221
- * 'userJoin': { user: User; guild: Guild };
222
- * 'levelUp': { user: User; level: number; guild: Guild };
217
+ * 'userJoin': { userId: string; guildId: string };
218
+ * 'levelUp': { userId: string; level: number };
223
219
  * }
224
220
  * }
225
221
  * ```
226
222
  */
227
223
  interface Subscriptions {}
228
- /**
229
- * Combined subscribers interface containing both default and custom subscribers.
230
- */
231
224
  interface AllSubscriptions extends DefaultSubscriptions, Subscriptions {}
232
- /**
233
- * Helper type to extract all available subscriber event names.
234
- */
225
+ /** All subscriber event names available to publish and augment. */
235
226
  type SubscriptionKey = keyof AllSubscriptions;
236
227
  /**
237
- * Helper type to get parameters for a specific subscriber event.
228
+ * The payload type for a subscriber event.
238
229
  *
239
230
  * @typeParam KeyOfSubscribers - The subscriber event name
240
231
  */
241
232
  type SubscriptionData<KeyOfSubscribers extends SubscriptionKey> = AllSubscriptions[KeyOfSubscribers];
242
- /**
243
- * Event map for Bus, compatible with TypedEventEmitter.
244
- *
245
- * @internal
246
- */
247
233
  type SubscriptionTuples = { [K in SubscriptionKey]: [AllSubscriptions[K]] };
248
234
  //#endregion
249
235
  //#region src/subscribers/Bus.d.ts
250
236
  /**
251
- * Manages application subscribers and event handling
252
- *
253
- * Provides a centralized system for registering and executing custom subscribers
254
- * throughout the application lifecycle. Bus subscribers are loaded from configured directories
255
- * and can be triggered programmatically or by framework events. Accessed via `core.bus`. Do not
256
- * construct it directly.
237
+ * Registers and dispatches subscribers. Subscribers load from the configured directories and run
238
+ * on a programmatic or framework-event publish. Accessed via `core.bus`. Do not construct it directly.
257
239
  */
258
240
  declare class Bus extends Plugin<SubscriptionTuples> {
259
241
  protected core: Core;
@@ -263,12 +245,15 @@ declare class Bus extends Plugin<SubscriptionTuples> {
263
245
  private isInitialized;
264
246
  private readonly subscribersMap;
265
247
  private readonly executedOnceHandlers;
248
+ private readonly webhookProbes;
266
249
  private readonly hmrHandler?;
267
250
  constructor(core: Core);
268
251
  private getArtifacts;
269
252
  /** @internal */
270
253
  init(): Promise<void>;
271
- /** @internal For use in dev mode */
254
+ /** @internal Exposed for tests. */
255
+ verifyWebhooks(): Promise<void>;
256
+ /** @internal */
272
257
  onHmr(event: HmrUpdateEvent): Promise<void>;
273
258
  private loadSubscribers;
274
259
  private registerSubscriber;
@@ -400,6 +385,8 @@ interface BotEvents {
400
385
  'any:event': { [K in keyof ClientEvents]: [K, ...ClientEvents[K]] }[keyof ClientEvents];
401
386
  /** Emitted for any interaction */
402
387
  'any:interaction': [interaction: Interaction];
388
+ /** @internal */
389
+ ready: [];
403
390
  }
404
391
  /**
405
392
  * Discord bot implementation that manages client and controllers
@@ -457,30 +444,19 @@ declare class Bot extends Plugin<BotEvents> {
457
444
  protected onListenerError(error: unknown, event: string | symbol): void;
458
445
  }
459
446
  //#endregion
460
- //#region src/handlers/BaseHandler.d.ts
447
+ //#region src/handlers/interactionTypes.d.ts
461
448
  type ValidInteractionTypes = ChatInputCommandInteraction | ButtonInteraction | ModalSubmitInteraction | AutocompleteInteraction | AnySelectMenuInteraction | ContextMenuCommandInteraction;
462
449
  type ValidNonInteractionKeys = Exclude<keyof ClientEvents, Events.InteractionCreate>;
463
450
  type ValidEventTypes = ValidInteractionTypes | ClientEvents[ValidNonInteractionKeys];
464
451
  type Repliables = Exclude<ValidInteractionTypes, AutocompleteInteraction>;
465
452
  type NonModalInteraction = Exclude<Repliables, ModalSubmitInteraction>;
466
- /** @internal */
467
- interface Handler {
468
- execute(): Promise<void>;
469
- }
453
+ //#endregion
454
+ //#region src/handlers/BaseHandler.d.ts
470
455
  /**
471
- * Base class for a Discord event or interaction handler. Don't register handlers directly, use the more specific handler subclasses.
456
+ * The gateway base, fixes the core base's `TCore` to the gateway {@link Core} and adds the `getEvent`
457
+ * gate hook.
472
458
  */
473
- declare abstract class BaseHandler<ValidEvent extends ValidEventTypes> implements Handler {
474
- readonly core: Core;
475
- protected readonly event: ValidEvent;
476
- protected readonly logger: Logger;
477
- protected readonly dispatch?: DispatchContext | undefined;
478
- protected constructor(event: ValidEvent, core: Core, dispatch?: DispatchContext);
479
- /**
480
- * Holds the main logic of your handler. The dispatcher calls it after the handler's gates pass, so a
481
- * gate that refuses stops `execute()` from running.
482
- */
483
- abstract execute(): Promise<void>;
459
+ declare abstract class BaseHandler$1<ValidEvent extends ValidEventTypes> extends BaseHandler<ValidEvent, Core> {
484
460
  /** @internal */
485
461
  getEvent(): ValidEvent;
486
462
  }
@@ -683,7 +659,7 @@ type EventMatchArms<Names extends ValidNonInteractionKeys, Ret> = { [Name in Nam
683
659
  * }
684
660
  * ```
685
661
  */
686
- declare abstract class EventHandler<Names extends ValidNonInteractionKeys> extends BaseHandler<ClientEvents[Names]> implements Handler {
662
+ declare abstract class EventHandler<Names extends ValidNonInteractionKeys> extends BaseHandler$1<ClientEvents[Names]> {
687
663
  private readonly firedEvent;
688
664
  constructor(event: ClientEvents[Names], core: Core, eventName?: Names);
689
665
  protected readonly event: SingleEventPayload<Names>;
@@ -727,7 +703,7 @@ declare abstract class EventHandler<Names extends ValidNonInteractionKeys> exten
727
703
  *
728
704
  * @typeParam EventName - One or more `ClientEvents` keys. Defaults to every event (catchall).
729
705
  */
730
- declare abstract class EventMiddleware<in out EventName extends ValidNonInteractionKeys = ValidNonInteractionKeys> extends BaseHandler<ClientEvents[EventName]> implements Handler {
706
+ declare abstract class EventMiddleware<in out EventName extends ValidNonInteractionKeys = ValidNonInteractionKeys> extends BaseHandler$1<ClientEvents[EventName]> {
731
707
  private readonly firedEvent;
732
708
  constructor(event: ClientEvents[EventName], core: Core, eventName?: EventName);
733
709
  protected readonly event: SingleEventPayload<EventName>;
@@ -801,8 +777,8 @@ declare function RegisterEvent<const Defs extends readonly EventSpec<ValidNonInt
801
777
  //#endregion
802
778
  //#region src/bot/gates/Gate.d.ts
803
779
  /**
804
- * The interaction arm. It extends the scalar {@link GateContextBase} with the live interaction and
805
- * the rich djs objects the gateway cache provides. `Repliable` is the interaction type the gate
780
+ * The interaction arm. It extends {@link GuildPermissionsContext} with the live interaction and
781
+ * the djs objects resolved from the gateway cache. `Repliable` is the interaction type the gate
806
782
  * supports, inferred from the `ctx` annotation, so a gate reading button-specific fields is rejected
807
783
  * on a slash handler. `interaction` is the reply target, so `user` is always present. Use
808
784
  * {@link NonModalInteraction} to exclude ModalSubmit when the gate needs a reliable caller member or
@@ -820,8 +796,8 @@ declare function RegisterEvent<const Defs extends readonly EventSpec<ValidNonInt
820
796
  * });
821
797
  * ```
822
798
  */
823
- interface InteractionGateContext<Repliable extends Repliables = Repliables> extends GateContextBase {
824
- /** Marks the interaction arm, narrow on it before reading `interaction`. */
799
+ interface InteractionGateContext<Repliable extends Repliables = Repliables> extends GuildPermissionsContext {
800
+ /** The literal `'interaction'`. Narrow on it before reading `interaction`. */
825
801
  readonly kind: 'interaction';
826
802
  /** The running framework, with the gateway's bot and bus. */
827
803
  readonly core: Core;
@@ -835,7 +811,7 @@ interface InteractionGateContext<Repliable extends Repliables = Repliables> exte
835
811
  readonly member: GuildMember | null;
836
812
  }
837
813
  /**
838
- * The event arm. It extends the scalar {@link GateContextBase} with the event payload and the djs
814
+ * The event arm. It extends {@link GuildPermissionsContext} with the event payload and the djs
839
815
  * objects derived from it. `Names` is the event(s) the gate supports, inferred from the `ctx`
840
816
  * annotation, so a gate that reads one event's `payload` is rejected on a handler for a different
841
817
  * event. The default supports every event.
@@ -852,8 +828,8 @@ interface InteractionGateContext<Repliable extends Repliables = Repliables> exte
852
828
  * });
853
829
  * ```
854
830
  */
855
- interface EventGateContext<Names extends ValidNonInteractionKeys = ValidNonInteractionKeys> extends GateContextBase {
856
- /** Marks the event arm, narrow on it before reading `payload`. */
831
+ interface EventGateContext<Names extends ValidNonInteractionKeys = ValidNonInteractionKeys> extends GuildPermissionsContext {
832
+ /** The literal `'event'`, narrow on it before reading `payload`. */
857
833
  readonly kind: 'event';
858
834
  /** The running framework, with the gateway's bot and bus. */
859
835
  readonly core: Core;
@@ -886,18 +862,68 @@ interface EventGateContext<Names extends ValidNonInteractionKeys = ValidNonInter
886
862
  */
887
863
  type GateContext = InteractionGateContext | EventGateContext;
888
864
  //#endregion
865
+ //#region src/bot/ReplySender.d.ts
866
+ /** The created message a gateway reply resolves to. */
867
+ type SentMessage = Message;
868
+ /**
869
+ * Writes interaction responses through the discord.js interaction. Construction is internal to the repliable
870
+ * handler bases and the dispatcher.
871
+ */
872
+ declare class ReplySender extends BaseReplySender<SentMessage> {
873
+ private readonly interaction;
874
+ constructor(interaction: Repliables, routeId: string);
875
+ protected writeReply(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
876
+ protected writeDefer(opts?: DeferOpts): Promise<void>;
877
+ protected writeDeferUpdate(): Promise<void>;
878
+ protected writeUpdate(response: ReplyResponse | string): Promise<SentMessage | undefined>;
879
+ protected writeFollowUp(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage>;
880
+ protected writeEditOriginal(response: ReplyResponse | string): Promise<SentMessage>;
881
+ protected writeEditTarget(targetId: string, response: ReplyResponse | string): Promise<SentMessage>;
882
+ protected writeDeleteOriginal(): Promise<void>;
883
+ protected writeDeleteTarget(targetId: string): Promise<void>;
884
+ protected writeModal(data: APIModalInteractionResponseCallbackData): Promise<void>;
885
+ protected guardModalSource(): void;
886
+ private rejectModalSubmit;
887
+ private sourceInteraction;
888
+ private replyOptions;
889
+ private editOptions;
890
+ private updateOptions;
891
+ private createdMessage;
892
+ }
893
+ //#endregion
894
+ //#region src/handlers/RepliableHandler.d.ts
895
+ /**
896
+ * Shared base the repliable interaction handlers extend.
897
+ *
898
+ * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
899
+ * {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base
900
+ * to the gateway sender.
901
+ *
902
+ * @typeParam Event - The repliable interaction type this handler processes
903
+ */
904
+ declare abstract class RepliableHandler<Event extends Repliables> extends RepliableHandler$1<Event, Core, SentMessage, ReplySender> {
905
+ /** @internal */
906
+ getEvent(): Event;
907
+ protected buildSender(event: Event, _core: Core, routeId: string): ReplySender;
908
+ }
909
+ //#endregion
889
910
  //#region src/handlers/interaction/InteractionHandler.d.ts
890
911
  /**
891
912
  * Shared base the typed interaction handlers extend.
892
913
  *
893
- * Not a public entry point. You should be using {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
894
- * or {@link SelectMenuHandler} instead. This class only carries the repliable-event plumbing those bases share,
895
- * so DO NOT use it directly.
914
+ * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
915
+ * or {@link SelectMenuHandler} instead. This class adds `showModal` on top of the reply members those bases
916
+ * share.
896
917
  *
897
918
  * @typeParam Repliable - The interaction type this handler processes
898
919
  */
899
- declare abstract class InteractionHandler<Repliable extends Repliables> extends BaseHandler<Repliable> implements Handler {
920
+ declare abstract class InteractionHandler<Repliable extends Repliables> extends RepliableHandler<Repliable> {
900
921
  constructor(event: Repliable, core: Core, dispatch?: DispatchContext);
922
+ /**
923
+ * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
924
+ * compile time (Discord forbids a modal in response to a modal).
925
+ */
926
+ protected showModal(this: InteractionHandler<NonModalInteraction>, modal: ModalLike): Promise<void>;
901
927
  }
902
928
  //#endregion
903
929
  //#region src/bot/gates/matching.d.ts
@@ -947,189 +973,28 @@ type FitAll<TCtor extends AnyHandlerCtor, Gates extends readonly Gate<GateContex
947
973
  */
948
974
  declare function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>(...gates: Gates): <TCtor extends AnyHandlerCtor>(ctor: FitAll<TCtor, Gates> extends Gates ? TCtor : FitAll<TCtor, Gates>) => void;
949
975
  //#endregion
950
- //#region src/inputs/AutocompleteOptions.d.ts
951
- type Row$2<Route extends keyof SlashOptionRegistry> = SlashOptionRegistry[Route];
952
- type EntryFor$1<Route extends keyof SlashOptionRegistry, Name extends PropertyKey> = Route extends unknown ? Name extends keyof Row$2<Route> ? Row$2<Route>[Name] : never : never;
953
- type NamesOfKind$1<Route extends keyof SlashOptionRegistry, Kind extends OptionKind> = Route extends unknown ? { [Name in keyof Row$2<Route>]: Row$2<Route>[Name] extends {
954
- kind: Kind;
955
- } ? Name : never }[keyof Row$2<Route>] : never;
956
- type ResolvedValue$1<Entry> = Entry extends {
957
- choices: readonly (infer Choice)[];
958
- } ? Choice : Entry extends {
959
- kind: 'string';
960
- } ? string : Entry extends {
961
- kind: 'integer' | 'number';
962
- } ? number : Entry extends {
963
- kind: 'boolean';
964
- } ? boolean : never;
965
- type Getter$1<Route extends keyof SlashOptionRegistry, Kind extends OptionKind, Method extends string> = IsNever<NamesOfKind$1<Route, Kind>> extends true ? unknown : Record<Method, <Name extends NamesOfKind$1<Route, Kind>>(name: Name) => ResolvedValue$1<EntryFor$1<Route, Name>> | null>;
966
- /**
967
- * The sibling-option view for an autocomplete handler. Only the four kinds Discord resolves during autocomplete
968
- * appear (string, integer, number, boolean), every read is `T | null` because a sibling is partial while the
969
- * user types the focused field, and a getter appears only when a registered command has an option of that kind.
970
- * Across several registered commands the view is the union of their options, so a getter for an option absent on
971
- * the firing command returns null. Read the focused field from `this.focused`.
972
- *
973
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}.
974
- */
975
- type AutocompleteOptions<Route extends keyof SlashOptionRegistry> = Getter$1<Route, 'string', 'getString'> & Getter$1<Route, 'integer', 'getInteger'> & Getter$1<Route, 'number', 'getNumber'> & Getter$1<Route, 'boolean', 'getBoolean'>;
976
- //#endregion
977
- //#region src/handlers/interaction/AutocompleteHandler.d.ts
978
- type Row$1<Route extends keyof SlashOptionRegistry> = SlashOptionRegistry[Route];
979
- type AutocompletableNames<Route extends keyof SlashOptionRegistry> = Route extends unknown ? { [Name in keyof Row$1<Route>]: Row$1<Route>[Name] extends {
980
- autocomplete: true;
981
- } ? Name : never }[keyof Row$1<Route>] : never;
982
- type EntryFor<Route extends keyof SlashOptionRegistry, Name extends PropertyKey> = Route extends unknown ? Name extends keyof Row$1<Route> ? Row$1<Route>[Name] : never : never;
983
- type ChoiceValueOf<Entry> = Entry extends {
984
- kind: 'string';
985
- } ? string : Entry extends {
986
- kind: 'integer' | 'number';
987
- } ? number : never;
988
- /** The focused option. `value` is the raw partial Discord delivers mid-type, always a string regardless of kind. */
989
- interface FocusedField<Route extends keyof SlashOptionRegistry> {
990
- name: AutocompletableNames<Route>;
991
- value: string;
992
- }
993
- /** One arm per autocompletable field, `value` is the focused partial, `respond` is pinned to the field's choice type. */
994
- type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly ApplicationCommandOptionChoiceData<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret> };
995
- /**
996
- * Base class for a Discord autocomplete handler.
997
- *
998
- * Pass the command route(s) from the generated registry as the generic, the same string(s) as
999
- * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
1000
- * with `this.options`, and find which command fired with `this.route`.
1001
- *
1002
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'` or `'search' | 'find'`.
1003
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1004
- *
1005
- * @example
1006
- * ```ts
1007
- * \@AutocompleteRoute('search')
1008
- * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1009
- * async execute() {
1010
- * await this.match({
1011
- * query: (value, respond) => respond([{ name: value, value }])
1012
- * });
1013
- * }
1014
- * }
1015
- * ```
1016
- */
1017
- declare abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends BaseHandler<AutocompleteInteraction<Cache>> implements Handler {
1018
- constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch?: DispatchContext);
1019
- /**
1020
- * The focused option for this interaction. `value` is always the raw partial string the user is typing,
1021
- * even for an integer or number option, so coerce it yourself when you need a number.
1022
- */
1023
- private decodedFocused?;
1024
- protected get focused(): FocusedField<Route>;
1025
- /**
1026
- * Run the arm for the focused field. An autocomplete always dispatches by which field is focused, so
1027
- * match is how you read it. There is no single-field shortcut the way slash/component handlers have one.
1028
- *
1029
- * Provide one arm per autocompletable field across the registered commands, keyed by field name. Each
1030
- * arm receives the focused partial value and a `respond` pinned to that field's choice value type. The
1031
- * arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A
1032
- * focused field with no arm, only reachable from a stale-deployed command, throws at runtime.
1033
- *
1034
- * @param arms - One handler per autocompletable field, keyed by field name.
1035
- * @returns The result of the arm that ran.
1036
- *
1037
- * @example
1038
- * ```ts
1039
- * \@AutocompleteRoute('search')
1040
- * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1041
- * async execute() {
1042
- * await this.match({
1043
- * query: (value, respond) => respond(titles(value).map((s) => ({ name: s, value: s }))),
1044
- * tag: (value, respond) => respond(tags(value).map((s) => ({ name: s, value: s })))
1045
- * });
1046
- * }
1047
- * }
1048
- * ```
1049
- */
1050
- protected match<Ret>(arms: FocusedArms<Route, Ret>): Promise<Ret>;
1051
- /** The firing command route, for a field whose completion differs per registered command. */
1052
- protected get route(): Route;
1053
- /**
1054
- * The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete
1055
- * (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user
1056
- * types the focused field. Read the focused field from `this.focused`, not here.
1057
- */
1058
- protected get options(): AutocompleteOptions<Route>;
1059
- }
1060
- //#endregion
1061
- //#region src/handlers/interaction/ContextMenuHandler.d.ts
1062
- type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;
1063
- type InteractionFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? UserContextMenuCommandInteraction<Cache> : MessageContextMenuCommandInteraction<Cache>;
1064
- type TargetFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? User : Message<BooleanCache<Cache>>;
1065
- type TargetMemberFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null : never;
976
+ //#region src/handlers/interaction/InteractionMiddleware.d.ts
1066
977
  /**
1067
- * Base class for a context-menu command handler (right-click a user or a message).
1068
- *
1069
- * Pass the kind from `discord.js`'s {@link ApplicationCommandType} as the generic, the same value as
1070
- * `@ContextMenuRoute`. Read the right-clicked entity from `this.target`, a `User` for a user menu and a
1071
- * `Message` for a message menu. Context menus carry no options, so a handler registered for several names
1072
- * reads `this.target` uniformly with no per-name branch.
978
+ * Base class for interaction middleware
1073
979
  *
1074
- * @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
1075
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
980
+ * Middleware runs before interaction handlers and can modify behavior or block execution.
981
+ * Unlike handlers, middleware should not send responses directly.
1076
982
  *
1077
- * @example
1078
- * ```ts
1079
- * \@ContextMenuRoute(ApplicationCommandType.Message, 'Report Message')
1080
- * class ReportMessage extends ContextMenuHandler<ApplicationCommandType.Message> {
1081
- * async execute() {
1082
- * const message = this.target;
1083
- * }
1084
- * }
1085
- * ```
983
+ * @typeParam Repliable - The interaction type this middleware processes
1086
984
  */
1087
- declare abstract class ContextMenuHandler<Kind extends ContextMenuKind, Cache extends CacheType = 'cached'> extends InteractionHandler<InteractionFor<Kind, Cache>> {
1088
- protected get target(): TargetFor<Kind, Cache>;
1089
- /**
1090
- * The invoking guild member, resolved only on user menus. Reading it on a message menu is a compile
1091
- * error since the type is `never` there.
1092
- */
1093
- protected get targetMember(): TargetMemberFor<Kind, Cache>;
985
+ declare abstract class InteractionMiddleware<Repliable extends Repliables> extends BaseHandler$1<Repliable> {
986
+ constructor(event: Repliable, core: Core);
1094
987
  }
1095
988
  //#endregion
1096
989
  //#region src/inputs/SlashOptions.d.ts
1097
- type Row<Route extends keyof SlashOptionRegistry> = SlashOptionRegistry[Route];
1098
- type NamesOfKind<Route extends keyof SlashOptionRegistry, Kind extends OptionKind> = { [Name in keyof Row<Route>]: Row<Route>[Name] extends {
1099
- kind: Kind;
1100
- } ? Name : never }[keyof Row<Route>];
1101
- type ChannelWireType<Types extends number> = Types extends ChannelType.PublicThread | ChannelType.AnnouncementThread ? ChannelType.PublicThread | ChannelType.AnnouncementThread : Types;
1102
- type ResolvedChannel<Cache extends CacheType, Entry> = Entry extends {
1103
- channelTypes: readonly (infer Types extends number)[];
1104
- } ? Extract<NonNullable<CommandInteractionOption<Cache>['channel']>, {
1105
- type: ChannelWireType<Types>;
1106
- }> : NonNullable<CommandInteractionOption<Cache>['channel']>;
1107
- type ResolvedValue<Cache extends CacheType, Entry> = Entry extends {
1108
- choices: readonly (infer Choice)[];
1109
- } ? Choice : Entry extends {
1110
- kind: 'string';
1111
- } ? string : Entry extends {
1112
- kind: 'integer' | 'number';
1113
- } ? number : Entry extends {
1114
- kind: 'boolean';
1115
- } ? boolean : Entry extends {
1116
- kind: 'user';
1117
- } ? NonNullable<CommandInteractionOption<Cache>['user']> : Entry extends {
1118
- kind: 'channel';
1119
- } ? ResolvedChannel<Cache, Entry> : Entry extends {
1120
- kind: 'role';
1121
- } ? NonNullable<CommandInteractionOption<Cache>['role']> : Entry extends {
1122
- kind: 'mentionable';
1123
- } ? NonNullable<CommandInteractionOption<Cache>['member' | 'role' | 'user']> : Entry extends {
1124
- kind: 'attachment';
1125
- } ? NonNullable<CommandInteractionOption<Cache>['attachment']> : never;
1126
- type Returned<Cache extends CacheType, Entry> = Entry extends {
1127
- required: true;
1128
- } ? ResolvedValue<Cache, Entry> : ResolvedValue<Cache, Entry> | null;
1129
- type Getter<Route extends keyof SlashOptionRegistry, Cache extends CacheType, Kind extends OptionKind, Method extends string> = IsNever<NamesOfKind<Route, Kind>> extends true ? unknown : Record<Method, <Name extends NamesOfKind<Route, Kind>>(name: Name) => Returned<Cache, Row<Route>[Name]>>;
1130
- type MemberGetter<Route extends keyof SlashOptionRegistry, Cache extends CacheType> = IsNever<NamesOfKind<Route, 'user'>> extends true ? unknown : {
1131
- getMember: <Name extends NamesOfKind<Route, 'user'>>(name: Name) => NonNullable<CommandInteractionOption<Cache>['member']> | null;
1132
- };
990
+ interface GatewayLens<Cache extends CacheType> {
991
+ user: NonNullable<CommandInteractionOption<Cache>['user']>;
992
+ member: NonNullable<CommandInteractionOption<Cache>['member']>;
993
+ channel: NonNullable<CommandInteractionOption<Cache>['channel']>;
994
+ role: NonNullable<CommandInteractionOption<Cache>['role']>;
995
+ mentionable: NonNullable<CommandInteractionOption<Cache>['member' | 'role' | 'user']>;
996
+ attachment: NonNullable<CommandInteractionOption<Cache>['attachment']>;
997
+ }
1133
998
  /**
1134
999
  * The typed view over a chat-input command's options for one route. Each getter mirrors the discord.js
1135
1000
  * resolver method but restricts its `name` to that route's options of the matching kind, drops the null
@@ -1142,7 +1007,7 @@ type MemberGetter<Route extends keyof SlashOptionRegistry, Cache extends CacheTy
1142
1007
  * @typeParam Route - A route key from the generated {@link SlashOptionRegistry}.
1143
1008
  * @typeParam Cache - The interaction cache state, `'cached'` by default.
1144
1009
  */
1145
- type SlashOptions<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> = Getter<Route, Cache, 'string', 'getString'> & Getter<Route, Cache, 'integer', 'getInteger'> & Getter<Route, Cache, 'number', 'getNumber'> & Getter<Route, Cache, 'boolean', 'getBoolean'> & Getter<Route, Cache, 'user', 'getUser'> & Getter<Route, Cache, 'channel', 'getChannel'> & Getter<Route, Cache, 'role', 'getRole'> & Getter<Route, Cache, 'mentionable', 'getMentionable'> & Getter<Route, Cache, 'attachment', 'getAttachment'> & MemberGetter<Route, Cache>;
1010
+ type SlashOptions<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> = SlashOptions$1<Route, GatewayLens<Cache>>;
1146
1011
  //#endregion
1147
1012
  //#region src/handlers/interaction/SlashHandler.d.ts
1148
1013
  type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends CacheType, Ret> = { [Key in Route]: (options: SlashOptions<Key, Cache>) => Promisable<Ret> };
@@ -1168,6 +1033,8 @@ type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends Cache
1168
1033
  * ```
1169
1034
  */
1170
1035
  declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
1036
+ /** @internal */
1037
+ readonly __slashRoute?: Route;
1171
1038
  /**
1172
1039
  * The typed options for this command's route. Required options drop the null, choices narrow to their
1173
1040
  * literal union, and only the getters for kinds this command actually uses appear. Use `this.event.options`
@@ -1176,7 +1043,7 @@ declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cac
1176
1043
  protected get options(): SlashOptions<Route, Cache>;
1177
1044
  /**
1178
1045
  * Run the arm for whichever command fired. Use this only when the handler is registered for several
1179
- * routes. A single-route handler reads `this.options` directly instead, match buys nothing there.
1046
+ * routes.
1180
1047
  *
1181
1048
  * Provide one arm per registered route, keyed by its route string, and each arm receives that route's
1182
1049
  * own narrowed options. The arms must cover every route in the generic, a missing route or an unknown
@@ -1201,211 +1068,122 @@ declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cac
1201
1068
  protected match<Ret>(arms: SlashMatchArms<Route, Cache, Ret>): Promise<Ret>;
1202
1069
  }
1203
1070
  //#endregion
1204
- //#region src/bot/decorators/Interactions.d.ts
1205
- /**
1206
- * Types of select menus supported for routing
1207
- */
1208
- declare enum SelectMenuKind {
1209
- String = "string",
1210
- User = "user",
1211
- Role = "role",
1212
- Channel = "channel",
1213
- Mentionable = "mentionable"
1214
- }
1215
- /** @internal */
1216
- type HandlerEventType<TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = InstanceType<TCtor> extends InteractionHandler<infer TEvent> ? TEvent : never;
1217
- /**
1218
- * Compile-time assertion that the required event type(s) `TRequired` are included in the handler event union.
1219
- *
1220
- * @internal
1221
- */
1222
- type AssertHandles<TRequired, TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = Extract<HandlerEventType<TCtor>, TRequired> extends never ? Constructor<['Handler event generic must include', TRequired]> : TCtor;
1223
- /**
1224
- * The slash route(s) a handler serves, read off its `SlashHandler` generic.
1225
- *
1226
- * @internal
1227
- */
1228
- type SlashRouteOf<TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = InstanceType<TCtor> extends SlashHandler<infer Route, CacheType> ? Route : never;
1229
- /**
1230
- * On a route/generic mismatch, resolves to a non-constructor type so applying the decorator is a TS1238.
1231
- *
1232
- * @internal
1233
- */
1234
- type AssertSlashRoute<Route extends string, TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = [Route] extends [SlashRouteOf<TCtor>] ? [SlashRouteOf<TCtor>] extends [Route] ? TCtor : Constructor<['SlashHandler declares a route the SlashRoute decorator does not list', SlashRouteOf<TCtor>]> : Constructor<['SlashRoute does not match the SlashHandler generic', Route]>;
1235
- /**
1236
- * Routes one or more slash commands to a {@link SlashHandler}.
1237
- *
1238
- * Pass the same route string(s) as the handler's generic. A single command reads `this.options`, several
1239
- * commands branch with `this.match`. The decorator routes and the generic must match exactly, so listing
1240
- * fewer or more than the handler declares is a compile error. Subcommands use a slash path.
1241
- *
1242
- * @param routes - The route string(s) this handler serves, e.g. `'ban'`, `'ban', 'kick'`, or `'demo/setup'`.
1243
- * @decorator
1244
- * @example
1245
- * ```ts
1246
- * \@SlashRoute('ban')
1247
- * class BanHandler extends SlashHandler<'ban'> {
1248
- * async execute() {
1249
- * const target = this.options.getUser('target');
1250
- * }
1251
- * }
1252
- * ```
1253
- *
1254
- * @example
1255
- * ```ts
1256
- * \@SlashRoute('ban', 'kick')
1257
- * class ModerationHandler extends SlashHandler<'ban' | 'kick'> {
1258
- * async execute() {
1259
- * await this.match({ ban: (o) => o.getUser('target'), kick: (o) => o.getUser('member') });
1260
- * }
1261
- * }
1262
- * ```
1263
- */
1264
- declare function SlashRoute<const Route extends keyof SlashOptionRegistry>(...routes: Route[]): <TCtor extends new (...args: any[]) => InteractionHandler<Repliables>>(constructor: AssertSlashRoute<Route, TCtor>) => void;
1265
- /**
1266
- * Routes button interactions to handler classes.
1267
- *
1268
- * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's
1269
- * generic. Passing different definitions to the decorator and the generic is a compile error. Routing
1270
- * matches the stable prefix, so a wire minted from an older shape still reaches the handler, where
1271
- * reading this.params throws StaleCustomId and the controller boundary turns it into a reply.
1272
- *
1273
- * @param defs - The customId definition(s) this handler decodes, one per route.
1274
- * @decorator
1275
- */
1276
- declare function ButtonRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs): <TCtor extends new (...args: any[]) => InteractionHandler<Repliables> & HasComponentDefs<Defs>>(constructor: AssertHandles<ButtonInteraction, TCtor>) => void;
1277
- /**
1278
- * Routes modal submissions to handler classes.
1279
- *
1280
- * Pass the {@link CustomId} definition(s) this handler decodes and list the same ones in the handler's
1281
- * generic. Passing different definitions to the decorator and the generic is a compile error. Routing
1282
- * matches the stable prefix, so an older-shape wire still reaches the handler and throws StaleCustomId
1283
- * on read, the same as a button route.
1284
- *
1285
- * @param defs - The customId definition(s) this handler decodes, one per route.
1286
- * @decorator
1287
- */
1288
- declare function ModalRoute<const Defs extends readonly AnyCustomId[]>(...defs: Defs): <TCtor extends new (...args: any[]) => InteractionHandler<Repliables> & HasComponentDefs<Defs>>(constructor: AssertHandles<ModalSubmitInteraction, TCtor>) => void;
1289
- /** The context-menu command names valid for a kind, read off the matching registry. @internal */
1290
- type NamesFor<Kind extends ApplicationCommandType.User | ApplicationCommandType.Message> = Kind extends ApplicationCommandType.User ? keyof UserContextMenuRegistry : keyof MessageContextMenuRegistry;
1291
- /**
1292
- * The context-menu kind a handler serves, read off its event's `commandType` discriminant. Direct
1293
- * inference from the `ContextMenuHandler` generic widens the kind to the whole union because the handler's
1294
- * conditional members blur it, so this uses the event literal to keep it narrow.
1295
- *
1296
- * @internal
1297
- */
1298
- type ContextMenuKindOf<TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = InstanceType<TCtor> extends ContextMenuHandler<infer Kind, CacheType> ? [Kind] extends [ApplicationCommandType.User | ApplicationCommandType.Message] ? HandlerEventType<TCtor> extends {
1299
- commandType: infer K;
1300
- } ? Extract<K, ApplicationCommandType.User | ApplicationCommandType.Message> : never : never : never;
1071
+ //#region src/handlers/interaction/ContextMenuHandler.d.ts
1072
+ type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;
1073
+ type InteractionFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? UserContextMenuCommandInteraction<Cache> : MessageContextMenuCommandInteraction<Cache>;
1074
+ type TargetFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? User : Message<BooleanCache<Cache>>;
1075
+ type TargetMemberFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null : never;
1301
1076
  /**
1302
- * On a kind mismatch between the decorator and the handler generic, resolves to a non-constructor type so
1303
- * applying the decorator is a TS1238. Cross-checks both directions, mirroring {@link AssertSlashRoute}.
1077
+ * Base class for a context-menu command handler (right-click a user or a message).
1304
1078
  *
1305
- * @internal
1306
- */
1307
- type AssertContextMenuRoute<Kind extends ApplicationCommandType.User | ApplicationCommandType.Message, TCtor extends new (...args: any[]) => InteractionHandler<Repliables>> = [Kind] extends [ContextMenuKindOf<TCtor>] ? [ContextMenuKindOf<TCtor>] extends [Kind] ? TCtor : Constructor<['ContextMenuHandler declares a kind the ContextMenuRoute decorator does not match', ContextMenuKindOf<TCtor>]> : Constructor<['ContextMenuRoute does not match the ContextMenuHandler generic', Kind]>;
1308
- /**
1309
- * Routes one or more context-menu commands to a {@link ContextMenuHandler}.
1079
+ * Pass the kind from `discord.js`'s {@link ApplicationCommandType} as the generic, the same value as
1080
+ * `@ContextMenuRoute`. Read the right-clicked entity from `this.target`, a `User` for a user menu and a
1081
+ * `Message` for a message menu. Context menus carry no options, so a handler registered for several names
1082
+ * reads `this.target` uniformly with no per-name branch.
1310
1083
  *
1311
- * Pass the kind (`ApplicationCommandType.User` or `ApplicationCommandType.Message`) and the command name(s),
1312
- * each checked against that kind's registry, so a typo is a compile error. The same kind must sit on the
1313
- * handler's generic, cross-checked both directions. Context menus carry no options, so a multi-name handler
1314
- * reads `this.target` uniformly with no branch.
1084
+ * @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
1085
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1315
1086
  *
1316
- * @param kind - The context-menu kind, `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
1317
- * @param names - The command name(s) this handler serves, keyed off the matching registry.
1318
- * @decorator
1319
1087
  * @example
1320
1088
  * ```ts
1321
- * \@ContextMenuRoute(ApplicationCommandType.User, 'View Profile')
1322
- * class ViewProfile extends ContextMenuHandler<ApplicationCommandType.User> {
1089
+ * \@ContextMenuRoute(ApplicationCommandType.Message, 'Report Message')
1090
+ * class ReportMessage extends ContextMenuHandler<ApplicationCommandType.Message> {
1323
1091
  * async execute() {
1324
- * const user = this.target;
1092
+ * const message = this.target;
1325
1093
  * }
1326
1094
  * }
1327
1095
  * ```
1328
1096
  */
1329
- declare function ContextMenuRoute<const Kind extends ApplicationCommandType.User | ApplicationCommandType.Message>(kind: Kind, ...names: NamesFor<Kind>[]): <TCtor extends new (...args: any[]) => InteractionHandler<Repliables>>(constructor: AssertContextMenuRoute<Kind, TCtor>) => void;
1330
- /**
1331
- * The command route(s) an autocomplete handler serves, read off its `AutocompleteHandler` generic.
1332
- *
1333
- * @internal
1334
- */
1335
- type AutocompleteRouteOf<TCtor extends new (...args: any[]) => BaseHandler<AutocompleteInteraction<CacheType>>> = InstanceType<TCtor> extends AutocompleteHandler<infer Route, CacheType> ? Route : never;
1097
+ declare abstract class ContextMenuHandler<Kind extends ContextMenuKind, Cache extends CacheType = 'cached'> extends InteractionHandler<InteractionFor<Kind, Cache>> {
1098
+ /** @internal */
1099
+ readonly __ctxKind?: Kind;
1100
+ protected get target(): TargetFor<Kind, Cache>;
1101
+ /**
1102
+ * The invoking guild member, resolved only on user menus. Reading it on a message menu is a compile
1103
+ * error since the type is `never` there.
1104
+ */
1105
+ protected get targetMember(): TargetMemberFor<Kind, Cache>;
1106
+ }
1107
+ //#endregion
1108
+ //#region src/handlers/interaction/AutocompleteHandler.d.ts
1109
+ type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly ApplicationCommandOptionChoiceData<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret> };
1336
1110
  /**
1337
- * On a route/generic mismatch, resolves to a non-constructor type so applying the decorator is a TS1238.
1111
+ * Base class for a Discord autocomplete handler.
1338
1112
  *
1339
- * @internal
1340
- */
1341
- type AssertAutocompleteRoute<Route extends keyof SlashOptionRegistry, TCtor extends new (...args: any[]) => BaseHandler<AutocompleteInteraction<CacheType>>> = [Route] extends [AutocompleteRouteOf<TCtor>] ? [AutocompleteRouteOf<TCtor>] extends [Route] ? TCtor : Constructor<['AutocompleteHandler declares a command the AutocompleteRoute decorator does not list', AutocompleteRouteOf<TCtor>]> : Constructor<['AutocompleteRoute does not match the AutocompleteHandler generic', Route]>;
1342
- /**
1343
- * Routes one or more commands' autocomplete to an {@link AutocompleteHandler}.
1113
+ * Pass the command route(s) from the generated registry as the generic, the same string(s) as
1114
+ * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
1115
+ * with `this.options`, and find which command fired with `this.route`.
1344
1116
  *
1345
- * Pass the same command route string(s) as the handler's generic. One command branches with `this.match`
1346
- * over its autocompletable fields, several commands share one handler whose arms span every command's
1347
- * fields. The decorator routes and the generic must match exactly, so listing fewer or more than the
1348
- * handler declares is a compile error.
1117
+ * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'` or `'search' | 'find'`.
1118
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1349
1119
  *
1350
- * @param routes - The command route string(s) this handler serves, e.g. `'search'` or `'search', 'find'`.
1351
- * @decorator
1352
1120
  * @example
1353
1121
  * ```ts
1354
1122
  * \@AutocompleteRoute('search')
1355
1123
  * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1356
1124
  * async execute() {
1357
- * await this.match({ query: (value, respond) => respond([{ name: value, value }]) });
1125
+ * await this.match({
1126
+ * query: (value, respond) => respond([{ name: value, value }])
1127
+ * });
1358
1128
  * }
1359
1129
  * }
1360
1130
  * ```
1361
1131
  */
1362
- declare function AutocompleteRoute<const Route extends keyof SlashOptionRegistry>(...routes: Route[]): <TCtor extends new (...args: any[]) => BaseHandler<AutocompleteInteraction<CacheType>>>(constructor: AssertAutocompleteRoute<Route, TCtor>) => void;
1363
- /** @internal */
1364
- type SelectMenuInteractionFor<SelectMenu extends SelectMenuKind, Cache extends CacheType = CacheType> = SelectMenu extends SelectMenuKind.String ? StringSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.User ? UserSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Role ? RoleSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Channel ? ChannelSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Mentionable ? MentionableSelectMenuInteraction<Cache> : never;
1365
- /**
1366
- * Routes select menu interactions to handler classes.
1367
- *
1368
- * Pass the select kind and the {@link CustomId} definition(s) this handler decodes. The handler's
1369
- * generic must list the same definitions, and its second generic argument must be the matching select
1370
- * interaction type, or it is a compile error.
1371
- *
1372
- * @param type - Select menu kind from {@link SelectMenuKind}.
1373
- * @param defs - The customId definition(s) this handler decodes, one per route.
1374
- * @decorator
1375
- *
1376
- * @example
1377
- * ```typescript
1378
- * \@SelectMenuRoute(SelectMenuKind.User, AssignId)
1379
- * class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {
1380
- * // handles user select menus minted from AssignId
1381
- * }
1382
- * ```
1383
- */
1384
- declare function SelectMenuRoute<SelectMenu extends SelectMenuKind, const Defs extends readonly AnyCustomId[]>(type: SelectMenu, ...defs: Defs): <TCtor extends new (...args: any[]) => InteractionHandler<Repliables> & HasComponentDefs<Defs>>(constructor: AssertHandles<SelectMenuInteractionFor<SelectMenu>, TCtor>) => void;
1385
- //#endregion
1386
- //#region src/handlers/interaction/InteractionMiddleware.d.ts
1387
- /**
1388
- * Base class for interaction middleware
1389
- *
1390
- * Middleware runs before interaction handlers and can modify behavior or block execution.
1391
- * Unlike handlers, middleware should not send responses directly.
1392
- *
1393
- * @typeParam Repliable - The interaction type this middleware processes
1394
- */
1395
- declare abstract class InteractionMiddleware<Repliable extends Repliables> extends BaseHandler<Repliable> implements Handler {
1396
- constructor(event: Repliable, core: Core);
1132
+ declare abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends BaseHandler$1<AutocompleteInteraction<Cache>> {
1133
+ /** @internal */
1134
+ readonly __autocompleteRoute?: Route;
1135
+ constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch?: DispatchContext);
1136
+ private decodedFocused?;
1137
+ /**
1138
+ * The focused option for this interaction. `value` is always the raw partial string the user is typing,
1139
+ * even for an integer or number option, so coerce it yourself when you need a number.
1140
+ */
1141
+ protected get focused(): FocusedField<Route>;
1142
+ /**
1143
+ * Run the arm for the focused field. An autocomplete always dispatches by which field is focused, so
1144
+ * match is how you read it. There is no single-field shortcut the way slash/component handlers have one.
1145
+ *
1146
+ * Provide one arm per autocompletable field across the registered commands, keyed by field name. Each
1147
+ * arm receives the focused partial value and a `respond` typed to that field's choice value. The
1148
+ * arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A
1149
+ * focused field with no arm, only reachable from a stale-deployed command, throws at runtime.
1150
+ *
1151
+ * @param arms - One handler per autocompletable field, keyed by field name.
1152
+ * @returns The result of the arm that ran.
1153
+ *
1154
+ * @example
1155
+ * ```ts
1156
+ * \@AutocompleteRoute('search')
1157
+ * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1158
+ * async execute() {
1159
+ * await this.match({
1160
+ * query: (value, respond) => respond(titles(value).map((s) => ({ name: s, value: s }))),
1161
+ * tag: (value, respond) => respond(tags(value).map((s) => ({ name: s, value: s })))
1162
+ * });
1163
+ * }
1164
+ * }
1165
+ * ```
1166
+ */
1167
+ protected match<Ret>(arms: FocusedArms<Route, Ret>): Promise<Ret>;
1168
+ /** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
1169
+ protected respond(choices: readonly ApplicationCommandOptionChoiceData[]): Promise<void>;
1170
+ /** The firing command route, for a field whose completion differs per registered command. */
1171
+ protected get route(): Route;
1172
+ /**
1173
+ * The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete
1174
+ * (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user
1175
+ * types the focused field. The focused field is on `this.focused`.
1176
+ */
1177
+ protected get options(): AutocompleteOptions<Route>;
1397
1178
  }
1398
1179
  //#endregion
1399
1180
  //#region src/handlers/interaction/components/ComponentHandler.d.ts
1400
1181
  type ComponentInteraction = ButtonInteraction | ModalSubmitInteraction | AnySelectMenuInteraction;
1401
- type SingleParams<Defs extends readonly AnyCustomId[]> = Defs extends readonly [infer One extends AnyCustomId] ? DecodedParams<One['shape']> : never;
1402
- type MatchArms<Defs extends readonly AnyCustomId[], Ret> = { [Def in Defs[number] as Def['prefix']]: (params: DecodedParams<Def['shape']>) => Promisable<Ret> };
1403
1182
  /**
1404
1183
  * Shared base the customId-routed component handlers extend.
1405
1184
  *
1406
- * Not a public entry point. You should be using {@link ButtonHandler}, {@link SelectMenuHandler}, or
1407
- * {@link ModalHandler} instead. This class only carries the customId decode and route-matching plumbing
1408
- * those bases share, so DO NOT use it directly.
1185
+ * Not a public entry point. Extend {@link ButtonHandler}, {@link SelectMenuHandler}, or {@link ModalHandler}
1186
+ * instead. This class defines the customId decode and the route matching those bases share.
1409
1187
  *
1410
1188
  * @typeParam Event - The component interaction type this handler processes
1411
1189
  * @typeParam Defs - The customId route definitions registered on the concrete handler
@@ -1413,7 +1191,10 @@ type MatchArms<Defs extends readonly AnyCustomId[], Ret> = { [Def in Defs[number
1413
1191
  declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs extends readonly AnyCustomId[]> extends InteractionHandler<Event> implements HasComponentDefs<Defs> {
1414
1192
  /** @internal */
1415
1193
  readonly __componentDefs?: Defs;
1416
- private get registeredDefs();
1194
+ /** Rewrite the source message this component interaction came from. */
1195
+ protected update(response: ReplyResponse | string): Promise<SentMessage>;
1196
+ /** Acknowledge the component without changing the source message. */
1197
+ protected deferUpdate(): Promise<void>;
1417
1198
  private decoded?;
1418
1199
  private get route();
1419
1200
  /**
@@ -1431,10 +1212,11 @@ declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs
1431
1212
  * handler `this.params` is `never`, so match is the only way to read the decoded params.
1432
1213
  *
1433
1214
  * Provide one arm per registered route, keyed by its prefix, and each arm receives that route's own
1434
- * decoded params. The arms must cover every registered def, a missing prefix or an unknown key is a
1435
- * compile error. Decoding runs before any arm, so a stale or corrupt wire throws before an arm body executes.
1215
+ * decoded params. The arms cover every registered route prefix, checked at compile time, and a prefix
1216
+ * unmatched at runtime throws `CustomIdMatchArmMissing`. Decoding runs before any arm, so a stale or
1217
+ * corrupt wire throws before an arm body executes.
1436
1218
  *
1437
- * @param arms - One handler per registered route, keyed by prefix.
1219
+ * @param arms - One callback per registered route, keyed by prefix.
1438
1220
  * @returns The result of the arm that ran.
1439
1221
  *
1440
1222
  * @example
@@ -1443,8 +1225,8 @@ declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs
1443
1225
  * class ReviewButtons extends ButtonHandler<[typeof Approve, typeof Reject]> {
1444
1226
  * async execute() {
1445
1227
  * await this.match({
1446
- * approve: ({ userId }) => this.event.reply(`approved <@${userId}>`),
1447
- * reject: ({ userId }) => this.event.reply(`rejected <@${userId}>`)
1228
+ * approve: ({ userId }) => this.reply(`approved <@${userId}>`),
1229
+ * reject: ({ userId }) => this.reply(`rejected <@${userId}>`)
1448
1230
  * });
1449
1231
  * }
1450
1232
  * }
@@ -1458,8 +1240,9 @@ declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs
1458
1240
  * Base class for a button interaction handler.
1459
1241
  *
1460
1242
  * Register the customId definitions this handler decodes with `@ButtonRoute`, list the same ones in the
1461
- * generic, then read `this.params` for a single route or `this.match` for several. Passing different
1462
- * definitions to the decorator and the generic is a compile error.
1243
+ * generic, then read `this.params` for a single route or `this.match` for several. Reply through the handler
1244
+ * members, or rewrite the source message with `this.update`. Passing different definitions to the decorator
1245
+ * and the generic is a compile error.
1463
1246
  *
1464
1247
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ApproveId]`.
1465
1248
  * @typeParam Cache - The interaction cache state, `'cached'` by default.
@@ -1470,12 +1253,15 @@ declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs
1470
1253
  * class ApproveButton extends ButtonHandler<[typeof ApproveId]> {
1471
1254
  * async execute() {
1472
1255
  * const { userId } = this.params;
1473
- * await this.event.reply(`approved <@${userId}>`);
1256
+ * await this.reply(`approved <@${userId}>`);
1474
1257
  * }
1475
1258
  * }
1476
1259
  * ```
1477
1260
  */
1478
- declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ButtonInteraction<Cache>, Defs> {}
1261
+ declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ButtonInteraction<Cache>, Defs> {
1262
+ /** @internal */
1263
+ readonly __component?: 'button';
1264
+ }
1479
1265
  //#endregion
1480
1266
  //#region src/handlers/interaction/components/ModalHandler.d.ts
1481
1267
  /**
@@ -1483,7 +1269,8 @@ declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache
1483
1269
  *
1484
1270
  * Register the customId definitions this handler decodes with `@ModalRoute`, list the same ones in the
1485
1271
  * generic, then read `this.params` for a single route or `this.match` for several. Read the submitted
1486
- * inputs from `this.event.fields`.
1272
+ * inputs from `this.event.fields`, and reply through the handler members. Discord forbids opening a modal in
1273
+ * response to a modal, so `showModal` fails compilation on this kind.
1487
1274
  *
1488
1275
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
1489
1276
  * @typeParam Cache - The interaction cache state, `'cached'` by default.
@@ -1495,20 +1282,25 @@ declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache
1495
1282
  * async execute() {
1496
1283
  * const { guildId } = this.params;
1497
1284
  * const name = this.event.fields.getTextInputValue('name');
1498
- * await this.event.reply(`saved ${name} for ${guildId}`);
1285
+ * await this.reply(`saved ${name} for ${guildId}`);
1499
1286
  * }
1500
1287
  * }
1501
1288
  * ```
1502
1289
  */
1503
- declare abstract class ModalHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ModalSubmitInteraction<Cache>, Defs> {}
1290
+ declare abstract class ModalHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ModalSubmitInteraction<Cache>, Defs> {
1291
+ /** @internal */
1292
+ readonly __component?: 'modal';
1293
+ }
1504
1294
  //#endregion
1505
1295
  //#region src/handlers/interaction/components/SelectMenuHandler.d.ts
1296
+ type SelectMenuInteractionFor<SelectMenu extends SelectMenuKind, Cache extends CacheType = CacheType> = SelectMenu extends SelectMenuKind.String ? StringSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.User ? UserSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Role ? RoleSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Channel ? ChannelSelectMenuInteraction<Cache> : SelectMenu extends SelectMenuKind.Mentionable ? MentionableSelectMenuInteraction<Cache> : never;
1506
1297
  /**
1507
1298
  * Base class for a select menu handler.
1508
1299
  *
1509
1300
  * Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,
1510
1301
  * so `this.event` and `this.event.values` are narrowed to that kind. Read `this.params` for a single
1511
- * route or `this.match` for several.
1302
+ * route or `this.match` for several, and reply through the handler members or rewrite the source message
1303
+ * with `this.update`.
1512
1304
  *
1513
1305
  * @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
1514
1306
  * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
@@ -1520,12 +1312,15 @@ declare abstract class ModalHandler<Defs extends readonly AnyCustomId[], Cache e
1520
1312
  * class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {
1521
1313
  * async execute() {
1522
1314
  * const { roleId } = this.params;
1523
- * await this.event.reply(`assigning ${this.event.values.length} member(s) to <@&${roleId}>`);
1315
+ * await this.reply(`assigning ${this.event.values.length} member(s) to <@&${roleId}>`);
1524
1316
  * }
1525
1317
  * }
1526
1318
  * ```
1527
1319
  */
1528
- declare abstract class SelectMenuHandler<Kind extends SelectMenuKind, Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<SelectMenuInteractionFor<Kind, Cache>, Defs> {}
1320
+ declare abstract class SelectMenuHandler<Kind extends SelectMenuKind, Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<SelectMenuInteractionFor<Kind, Cache>, Defs> {
1321
+ /** @internal */
1322
+ readonly __component?: Kind;
1323
+ }
1529
1324
  //#endregion
1530
1325
  //#region src/bot/decorators/Middlewares.d.ts
1531
1326
  /**
@@ -1613,36 +1408,37 @@ declare const IgnoreBots: import("@seedcord/core").Gate<EventGateContext<ValidNo
1613
1408
  */
1614
1409
  declare function Nsfw(options?: GateNoticeOptions): Gate<InteractionGateContext<NonModalInteraction>, 'Nsfw'>;
1615
1410
  //#endregion
1616
- //#region src/bot/notices/utils.d.ts
1617
- /** Whose permissions a check ran against, used to mention and label the subject in a refusal. */
1618
- type PermSubject = Role | TextChannel | Guild | GuildMember;
1619
- //#endregion
1620
- //#region src/bot/utilities/permissions/checkPermissions.d.ts
1411
+ //#region src/bot/utilities/channels/fetchText.d.ts
1621
1412
  /**
1622
- * Array of permission bits for permission validation.
1623
- * Import any of the predefined sets or pass a custom bigint[].
1413
+ * Fetches and validates a text channel. Refuses when the channel doesn't exist or isn't a text channel.
1414
+ *
1415
+ * @param client - The Discord client instance
1416
+ * @param channelId - Channel ID or TextChannel instance
1417
+ * @returns Promise resolving to the text channel
1624
1418
  */
1625
- type BotPermissionScope = readonly bigint[];
1419
+ declare function fetchText(client: Client, channelId: TextChannelResolvable): Promise<TextChannel>;
1420
+ //#endregion
1421
+ //#region src/bot/utilities/permissions/checkPermissions.d.ts
1626
1422
  /**
1627
1423
  * Optional custom error constructors for {@link CheckPermissionOptions}.
1628
1424
  */
1629
- interface PermissionErrorCtors {
1630
- missing?: new (message: string, where: PermSubject, missingPerms: string[]) => Notice;
1631
- dangerous?: new (message: string, target: PermSubject, dangerousPerms: string[]) => Notice;
1425
+ interface PermissionErrorNoticeOverrides {
1426
+ missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;
1427
+ dangerousNotice?: new (message: string | undefined, subject: string, dangerousPerms: readonly string[]) => Notice;
1632
1428
  }
1633
1429
  /**
1634
1430
  * Options for {@link checkPermissions}.
1635
1431
  *
1636
- * @see {@link PermissionErrorCtors}
1432
+ * @see {@link PermissionErrorNoticeOverrides}
1637
1433
  */
1638
- interface CheckPermissionOptions extends PermissionErrorCtors {
1434
+ interface CheckPermissionOptions extends PermissionErrorNoticeOverrides {
1639
1435
  /** Role or member whose permissions will be checked */
1640
1436
  for: Role | GuildMember;
1641
- /** Context where permissions apply, guild or channel */
1437
+ /** Context where permissions apply */
1642
1438
  in: Guild | TextChannel;
1643
1439
  /** Permission bits to validate */
1644
- scope: BotPermissionScope;
1645
- /** When true, ensure target does not have the given permissions */
1440
+ scope: PermissionScope;
1441
+ /** When true, ensure target does NOT have the given permissions */
1646
1442
  inverse?: boolean;
1647
1443
  }
1648
1444
  /**
@@ -1682,7 +1478,7 @@ interface CheckPermissionOptions extends PermissionErrorCtors {
1682
1478
  * });
1683
1479
  * ```
1684
1480
  */
1685
- declare function checkPermissions(target: Role | GuildMember, ctx: Guild, scope: BotPermissionScope, inverse?: boolean, errors?: PermissionErrorCtors): void;
1481
+ declare function checkPermissions(target: Role | GuildMember, ctx: Guild, scope: PermissionScope, inverse?: boolean, errors?: PermissionErrorNoticeOverrides): void;
1686
1482
  /**
1687
1483
  * Checks permissions for a {@link Role} or a {@link GuildMember} in a {@link TextChannel}. Refuses when a
1688
1484
  * permission in `scope` is missing, or with `inverse` when one is present.
@@ -1720,7 +1516,7 @@ declare function checkPermissions(target: Role | GuildMember, ctx: Guild, scope:
1720
1516
  * });
1721
1517
  * ```
1722
1518
  */
1723
- declare function checkPermissions(target: Role | GuildMember, ctx: TextChannel, scope: BotPermissionScope, inverse?: boolean, errors?: PermissionErrorCtors): void;
1519
+ declare function checkPermissions(target: Role | GuildMember, ctx: TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionErrorNoticeOverrides): void;
1724
1520
  /**
1725
1521
  * Checks permissions using an options object. Refuses when a permission in {@link CheckPermissionOptions.scope} is missing,
1726
1522
  * or with `options.inverse` when one is present.
@@ -1764,123 +1560,6 @@ declare function checkPermissions(target: Role | GuildMember, ctx: TextChannel,
1764
1560
  */
1765
1561
  declare function checkPermissions(options: CheckPermissionOptions): void;
1766
1562
  //#endregion
1767
- //#region src/bot/gates/catalog/permissions.d.ts
1768
- /**
1769
- * Options for {@link RequirePermissions} and {@link RequireBotPermissions}, one override per refusal the gate
1770
- * can show.
1771
- *
1772
- * @example
1773
- * ```ts
1774
- * import { PermissionFlagsBits } from 'discord.js';
1775
- * import type { Guild, GuildMember, Role, TextChannel } from 'discord.js';
1776
- *
1777
- * // `missing` is a Notice ctor: (message, where, missingPerms)
1778
- * class MissingPermsNotice extends Notice {
1779
- * public constructor(message: string, where: Role | TextChannel | Guild | GuildMember, missing: string[]) {
1780
- * super(`${message} (${missing.join(', ')})`);
1781
- * }
1782
- * }
1783
- *
1784
- * RequirePermissions([PermissionFlagsBits.BanMembers], { missing: MissingPermsNotice });
1785
- * ```
1786
- */
1787
- interface RequirePermissionsOptions {
1788
- /** Reword or replace the refusal shown outside a guild. */
1789
- notInGuild?: GateNoticeOptions;
1790
- /** Replace the missing-permissions refusal with a ctor that receives the where and the missing perm names. */
1791
- missing?: PermissionErrorCtors['missing'];
1792
- }
1793
- /**
1794
- * Options for {@link RequireRole}, one override per refusal the gate can show.
1795
- */
1796
- interface RequireRoleOptions {
1797
- /** Reword or replace the refusal shown outside a guild. */
1798
- notInGuild?: GateNoticeOptions;
1799
- /** Reword or replace the refusal shown when the caller lacks the role. */
1800
- missingRole?: GateNoticeOptions;
1801
- }
1802
- /**
1803
- * Requires the caller to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
1804
- *
1805
- * Interaction-only and excludes ModalSubmit, since a modal lacks a reliable cached caller member.
1806
- *
1807
- * @param scope - The permission flag bits the caller must all hold.
1808
- * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
1809
- *
1810
- * @see {@link Gated}
1811
- *
1812
- * @example
1813
- * ```ts
1814
- * import { PermissionFlagsBits } from 'discord.js';
1815
- *
1816
- * \@Gated(RequirePermissions([PermissionFlagsBits.BanMembers]))
1817
- * \@SlashRoute('ban')
1818
- * class BanHandler extends SlashHandler<'ban'> {
1819
- * async execute() {
1820
- * const target = this.options.getUser('target');
1821
- * }
1822
- * }
1823
- * ```
1824
- */
1825
- declare function RequirePermissions(scope: BotPermissionScope, options?: RequirePermissionsOptions): Gate<InteractionGateContext<NonModalInteraction>, 'RequirePermissions'>;
1826
- /**
1827
- * Requires the bot to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
1828
- *
1829
- * It reads the bot's own member, so it attaches to modal handlers as well.
1830
- *
1831
- * @param scope - The permission flag bits the bot must all hold.
1832
- * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
1833
- *
1834
- * @see {@link Gated}
1835
- *
1836
- * @example
1837
- * ```ts
1838
- * import { PermissionFlagsBits } from 'discord.js';
1839
- *
1840
- * \@Gated(RequireBotPermissions([PermissionFlagsBits.BanMembers]))
1841
- * \@ModalRoute(MyModalId)
1842
- * class MyModalHandler extends ModalHandler<[typeof MyModalId]> {
1843
- * async execute() {
1844
- * // ...
1845
- * }
1846
- * }
1847
- * ```
1848
- */
1849
- declare function RequireBotPermissions(scope: BotPermissionScope, options?: RequirePermissionsOptions): Gate<InteractionGateContext, 'RequireBotPermissions'>;
1850
- /**
1851
- * Requires the caller to have `roleId`, read from the member's role cache. Refuses outside a guild first, then
1852
- * when the role is missing.
1853
- *
1854
- * Interaction-only and excludes ModalSubmit.
1855
- *
1856
- * @param roleId - The role snowflake the caller must hold, read from the member's role cache.
1857
- * @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-role one with `missingRole`.
1858
- *
1859
- * @see {@link Gated}
1860
- *
1861
- * @example
1862
- * ```ts
1863
- * \@Gated(RequireRole('123456789012345678'))
1864
- * \@SlashRoute('vip')
1865
- * class VipHandler extends SlashHandler<'vip'> {
1866
- * async execute() {
1867
- * // ...
1868
- * }
1869
- * }
1870
- * ```
1871
- */
1872
- declare function RequireRole(roleId: string, options?: RequireRoleOptions): Gate<InteractionGateContext<NonModalInteraction>, 'RequireRole'>;
1873
- //#endregion
1874
- //#region src/bot/utilities/channels/fetchText.d.ts
1875
- /**
1876
- * Fetches and validates a text channel. Refuses when the channel doesn't exist or isn't a text channel.
1877
- *
1878
- * @param client - The Discord client instance
1879
- * @param channelId - Channel ID or TextChannel instance
1880
- * @returns Promise resolving to the text channel
1881
- */
1882
- declare function fetchText(client: Client, channelId: TextChannelResolvable): Promise<TextChannel>;
1883
- //#endregion
1884
1563
  //#region src/bot/utilities/permissions/checkBotPermissions.d.ts
1885
1564
  /**
1886
1565
  * Checks if the bot has required permissions in a {@link Guild} or {@link TextChannel}. Refuses when a
@@ -1903,28 +1582,23 @@ declare function fetchText(client: Client, channelId: TextChannelResolvable): Pr
1903
1582
  * ]);
1904
1583
  * ```
1905
1584
  */
1906
- declare function checkBotPermissions(target: Guild | TextChannel, scope: BotPermissionScope, inverse?: boolean, errors?: PermissionErrorCtors): void;
1585
+ declare function checkBotPermissions(target: Guild | TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionErrorNoticeOverrides): void;
1907
1586
  //#endregion
1908
1587
  //#region src/bot/utilities/permissions/hasPermsToAssign.d.ts
1909
1588
  /**
1910
1589
  * Optional custom error constructors for {@link HasPermsToAssignOptions}.
1911
1590
  */
1912
- interface AssignRoleErrorCtors {
1913
- /** Custom error for role higher than bot */
1914
- higher?: new (message: string, role: Role, botRole: Role) => Notice;
1915
- /** Custom error for managed role assignment */
1916
- managed?: new (message: string) => Notice;
1917
- /** Custom error for missing Manage Roles permission */
1918
- missing?: new (message: string, where: Role | TextChannel | Guild | GuildMember, missingPerms: string[]) => Notice;
1591
+ interface AssignRoleNoticeOverrides {
1592
+ higherNotice?: new (message: string, role: Role, botRole: Role) => Notice;
1593
+ managedNotice?: new (message: string) => Notice;
1594
+ missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;
1919
1595
  }
1920
1596
  /**
1921
1597
  * Options for {@link hasPermsToAssign}.
1922
1598
  */
1923
1599
  interface HasPermsToAssignOptions {
1924
- /** Target role to validate for assignment */
1925
1600
  targetRole: Role;
1926
- /** Optional custom error constructors */
1927
- errors?: AssignRoleErrorCtors;
1601
+ noticeOverrides?: AssignRoleNoticeOverrides;
1928
1602
  }
1929
1603
  /**
1930
1604
  * Validates if the bot can assign a target role. Refuses when the target role is above the bot's, is managed, or the bot lacks Manage Roles.
@@ -1941,10 +1615,10 @@ interface HasPermsToAssignOptions {
1941
1615
  * // Check with custom error handling
1942
1616
  * hasPermsToAssign({
1943
1617
  * targetRole: role,
1944
- * errors: {
1945
- * higher: CustomHigherError,
1946
- * managed: CustomManagedError,
1947
- * missing: CustomMissingPermissionsError
1618
+ * noticeOverrides: {
1619
+ * higherNotice: CustomHigherNotice,
1620
+ * managedNotice: CustomManagedNotice,
1621
+ * missingNotice: CustomMissingPermissionsNotice
1948
1622
  * }
1949
1623
  * });
1950
1624
  * ```
@@ -2031,41 +1705,7 @@ declare function fetchManyUsers(client: Client, userIds: string[], options?: Fet
2031
1705
  */
2032
1706
  declare function updateMemberRoles(rolesToAdd: Snowflake[], rolesToRemove: Snowflake[], member: GuildMember): Promise<void>;
2033
1707
  //#endregion
2034
- //#region src/bot/ReplySender.d.ts
2035
- /**
2036
- * Sends a {@link ReplyResponse} to an interaction, picking reply, editReply, or followUp from the live
2037
- * acknowledgement state. Logs and drops its own send failures so a dead token never escapes into the
2038
- * controller.
2039
- */
2040
- declare class ReplySender {
2041
- private readonly interaction;
2042
- private readonly logger;
2043
- constructor(interaction: Repliables);
2044
- /**
2045
- * Sends the reply and returns the sent message so a caller can attach a collector to it. Never throws,
2046
- * a failed send is logged and dropped and resolves to `undefined`.
2047
- *
2048
- * @param response - The ComponentsV2 reply to show.
2049
- * @param ephemeral - Whether the reply is ephemeral. {@default `true`}
2050
- * @returns The sent {@link Message}, or `undefined` when the send was swallowed.
2051
- */
2052
- send(response: ReplyResponse, ephemeral?: boolean): Promise<Message | undefined>;
2053
- /**
2054
- * Edits an already-sent message to a new {@link ReplyResponse}. Used to swap a confirmation prompt for
2055
- * its outcome. Never throws, a failed edit is logged and dropped.
2056
- *
2057
- * @param message - The message to edit (the handle a prior {@link send} returned).
2058
- * @param response - The reply to replace it with.
2059
- */
2060
- edit(message: Message, response: ReplyResponse): Promise<void>;
2061
- private dispatch;
2062
- private replyOptions;
2063
- private editBody;
2064
- private logSwallowed;
2065
- }
2066
- //#endregion
2067
1708
  //#region src/bot/confirm/getConfirmation.d.ts
2068
- /** A confirm outcome to show, the reply itself or a factory for it. */
2069
1709
  type Outcome = ReplyResponse | (() => Promisable<ReplyResponse>);
2070
1710
  /** Options shared by both confirmation forms. */
2071
1711
  interface ConfirmOptions {
@@ -2113,7 +1753,7 @@ interface DefaultConfirmOptions extends ConfirmOptions {
2113
1753
  /**
2114
1754
  * Shows a confirm/cancel prompt with built-in Confirm and Cancel buttons and resolves to `true` only if the
2115
1755
  * invoking user clicks confirm. A cancel click or a timeout resolves to `false`. Gate the action behind it
2116
- * with an early return. Never throws, a swallowed send resolves to `false`. Not usable from a
1756
+ * with an early return. A failed prompt send throws into the fault boundary. Not usable from a
2117
1757
  * {@link ModalHandler}, the `interaction` parameter excludes a modal submit at compile time.
2118
1758
  *
2119
1759
  * @param interaction - The repliable interaction to prompt on, `this.event` inside a non-modal handler.
@@ -2139,8 +1779,9 @@ interface DefaultConfirmOptions extends ConfirmOptions {
2139
1779
  declare function getConfirmation(interaction: NonModalInteraction, prompt: string, options?: DefaultConfirmOptions): Promise<boolean>;
2140
1780
  /**
2141
1781
  * Shows a confirm/cancel prompt you build yourself and resolves to `true` only if the invoking user clicks
2142
- * confirm. The factory receives the two minted button ids, set them on your own buttons. A cancel click or a
2143
- * timeout resolves to `false`. Never throws. Not usable from a {@link ModalHandler}.
1782
+ * confirm. The factory receives the two minted button ids. Set them on your own buttons. A cancel click or a
1783
+ * timeout resolves to `false`. A failed prompt send throws into the fault boundary. Not usable from a
1784
+ * {@link ModalHandler}.
2144
1785
  *
2145
1786
  * @param interaction - The repliable interaction to prompt on, `this.event` inside a non-modal handler.
2146
1787
  * @param prompt - A factory given the minted `{ confirm, cancel }` ids that returns the reply to show. Build
@@ -2294,7 +1935,11 @@ interface PaginatorConfig<Item, Prefix extends string> {
2294
1935
  renderItem?: ItemRender<Item>;
2295
1936
  /** Take over the whole page tree. Receives the page data and the controls factory. */
2296
1937
  render?: PageRender<Item>;
2297
- /** Whether the first page is ephemeral. {@default false} */
1938
+ /**
1939
+ * Whether the first page is ephemeral.
1940
+ *
1941
+ * @defaultValue `false`
1942
+ */
2298
1943
  ephemeral?: boolean;
2299
1944
  }
2300
1945
  type PaginatorHandlerCtor<Prefix extends string> = new (event: ButtonInteraction<'cached'>, core: Core) => ButtonHandler<[PageCursor<Prefix>]> & {
@@ -2327,7 +1972,7 @@ declare class Paginator<Item, const Prefix extends string> {
2327
1972
  private readonly config;
2328
1973
  constructor(config: PaginatorConfig<Item, Prefix>);
2329
1974
  /** Render page 0 and send it, picking reply or followUp from the interaction's state. */
2330
- start(interaction: Repliables, core?: Core): Promise<Message | undefined>;
1975
+ start(interaction: Repliables, core?: Core): Promise<Message>;
2331
1976
  /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
2332
1977
  page(ctx: PageContext, n: number): Promise<ReplyResponse>;
2333
1978
  }
@@ -2398,21 +2043,99 @@ interface SubscribeOptions {
2398
2043
  */
2399
2044
  declare function Subscribe<TSubscriber extends SubscriptionKey>(subscriber: TSubscriber, options?: SubscribeOptions): <HandlerCtor extends Constructor<Subscriber<TSubscriber>>>(constructor: HandlerCtor) => void;
2400
2045
  //#endregion
2046
+ //#region src/subscribers/bases/WebhookSender.d.ts
2047
+ /**
2048
+ * A file attached to a webhook report.
2049
+ */
2050
+ interface WebhookFile {
2051
+ name: string;
2052
+ description: string;
2053
+ data: Buffer;
2054
+ }
2055
+ /**
2056
+ * The payload {@link WebhookSender.send} posts.
2057
+ */
2058
+ interface WebhookSendOptions {
2059
+ flags: number;
2060
+ username: string;
2061
+ components: readonly {
2062
+ toJSON(): APIMessageTopLevelComponent;
2063
+ }[];
2064
+ files?: readonly WebhookFile[] | undefined;
2065
+ }
2066
+ /**
2067
+ * Sends component messages to a Discord webhook through `@discordjs/rest`.
2068
+ *
2069
+ * @throws A `SeedcordError` when the url does not carry a webhook id and token.
2070
+ */
2071
+ declare class WebhookSender {
2072
+ private readonly rest;
2073
+ private readonly route;
2074
+ constructor(url: string);
2075
+ /**
2076
+ * Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from
2077
+ * Discord and `unreachable` on any other failure.
2078
+ */
2079
+ verify(): Promise<'ok' | 'missing' | 'unreachable'>;
2080
+ send(options: WebhookSendOptions): Promise<void>;
2081
+ }
2082
+ //#endregion
2401
2083
  //#region src/subscribers/bases/WebhookLog.d.ts
2402
2084
  /**
2403
- * Abstract webhook logging handler for subscriber events.
2085
+ * The content a reporter returns from {@link WebhookLog.report}.
2086
+ */
2087
+ interface WebhookReport {
2088
+ /** Shown as the webhook message author. Defaults to the reporter's class name. */
2089
+ username?: string;
2090
+ components: readonly {
2091
+ toJSON(): APIMessageTopLevelComponent;
2092
+ }[];
2093
+ files?: readonly WebhookFile[] | undefined;
2094
+ }
2095
+ /**
2096
+ * Base class for subscribers that deliver their event to a Discord webhook.
2404
2097
  *
2405
- * Extends Subscriber to provide webhook-based logging capabilities.
2406
- * Implementations must define the webhook client to send messages to by implementing the webhook property.
2098
+ * Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. Url
2099
+ * resolution, validation, sender reuse, and sending run in the base. When the variable is unset
2100
+ * the reporter is skipped at registration with a boot warning.
2407
2101
  *
2408
- * @typeParam KeyOfSubscribers - The specific subscriber type this handler processes
2102
+ * @typeParam KeyOfSubscribers - The subscription key this reporter receives
2409
2103
  */
2410
2104
  declare abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey> extends Subscriber<KeyOfSubscribers> {
2411
- /** The Discord webhook client for sending log messages */
2412
- abstract webhook: WebhookClient;
2413
- constructor(data: AllSubscriptions[KeyOfSubscribers], core: Core);
2105
+ private static readonly senders;
2106
+ /** Builds the message for one published event. */
2107
+ abstract report(): WebhookReport | Promise<WebhookReport>;
2108
+ execute(): Promise<void>;
2109
+ /** @internal */
2110
+ static senderFor(url: string): WebhookSender;
2111
+ /** @internal */
2112
+ static envKeyOf(ctor: NewableFunction): string;
2113
+ /** @internal Returns null when the env var is unset, throws when set but malformed. */
2114
+ static urlOf(envKey: string): string | null;
2414
2115
  }
2415
2116
  //#endregion
2117
+ //#region src/subscribers/decorators/WebhookUrl.d.ts
2118
+ /**
2119
+ * Declares the environment variable a {@link WebhookLog} reporter reads its webhook url from.
2120
+ *
2121
+ * An unset variable disables the reporter with a boot warning. A set but malformed value throws
2122
+ * at boot.
2123
+ *
2124
+ * @param envKey - The environment variable name holding the webhook url
2125
+ * @decorator
2126
+ * @example
2127
+ * ```ts
2128
+ * \@Subscribe('memberBanned')
2129
+ * \@WebhookUrl('BAN_LOG_WEBHOOK_URL')
2130
+ * class BanLog extends WebhookLog<'memberBanned'> {
2131
+ * report() {
2132
+ * return { components: [new BanCard(this.data).component] };
2133
+ * }
2134
+ * }
2135
+ * ```
2136
+ */
2137
+ declare function WebhookUrl(envKey: string): <HandlerCtor extends Constructor<WebhookLog<SubscriptionKey>>>(constructor: HandlerCtor) => void;
2138
+ //#endregion
2416
2139
  //#region src/Seedcord.d.ts
2417
2140
  /**
2418
2141
  * Main Seedcord bot framework class
@@ -2465,5 +2188,5 @@ declare class Seedcord extends Pluggable implements Core {
2465
2188
  /** Package version */
2466
2189
  declare const version: string;
2467
2190
  //#endregion
2468
- export { type AllSubscriptions, ArraySource, AssignRoleErrorCtors, AutocompleteHandler, type AutocompleteOptions, AutocompleteRoute, type BotEvents, type BotPermissionScope, ButtonHandler, ButtonRoute, type CheckPermissionOptions, CommandMentions, type ConfirmOptions, ContextMenuHandler, ContextMenuRoute, type Core, CursorSource, type DefaultConfirmOptions, Emojis, type EventGateContext, EventHandler, EventMiddleware, type EventSpec, type EventsConfig, type FaultSource, FetchUserOptions, type GateContext, Gated, type GatewayBotConfig, type GatewayConfig, HasPermsToAssignOptions, IgnoreBots, type Initializeable, type InjectedEmojiMap, type InjectedMentionMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, Middleware, type MiddlewareOptions, MiddlewareType, ModalHandler, ModalRoute, Nsfw, Paginator, type PaginatorControls, type PermissionErrorCtors, Pluggable, Plugin, type PluginArgs, type PluginCtor, RegisterCommand, RegisterEvent, type RegisterEventOptions, type Repliables, ReplySender, RequireBotPermissions, RequirePermissions, type RequirePermissionsOptions, RequireRole, type RequireRoleOptions, Seedcord, SelectMenuHandler, SelectMenuKind, SelectMenuRoute, SlashHandler, type SlashOptions, SlashRoute, Subscribe, type SubscribeOptions, Subscriber, type SubscriptionData, type SubscriptionKey, type Subscriptions, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, WebhookLog, checkBotPermissions, checkPermissions, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, getConfirmation, hasPermsToAssign, updateMemberRoles, version };
2191
+ export { ArraySource, AssignRoleNoticeOverrides, AutocompleteHandler, type BotEvents, ButtonHandler, type CheckPermissionOptions, CommandMentions, type ConfirmOptions, ContextMenuHandler, type Core, CursorSource, type DefaultConfirmOptions, Emojis, type EventGateContext, EventHandler, EventMiddleware, type EventSpec, type EventsConfig, type FaultSource, FetchUserOptions, type GateContext, Gated, type GatewayBotConfig, type GatewayConfig, HasPermsToAssignOptions, IgnoreBots, type Initializeable, type InjectedEmojiMap, type InjectedMentionMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, Middleware, type MiddlewareOptions, MiddlewareType, ModalHandler, Nsfw, Paginator, type PaginatorControls, type PermissionErrorNoticeOverrides, Pluggable, Plugin, type PluginArgs, type PluginCtor, RegisterEvent, type RegisterEventOptions, RepliableHandler, type Repliables, Seedcord, SelectMenuHandler, ShutdownPhase, SlashHandler, type SlashOptions, StartupPhase, Subscribe, type SubscribeOptions, Subscriber, type SubscriptionData, type SubscriptionKey, type Subscriptions, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, type WebhookFile, WebhookLog, type WebhookReport, WebhookUrl, checkBotPermissions, checkPermissions, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, getConfirmation, hasPermsToAssign, updateMemberRoles, version };
2469
2192
  //# sourceMappingURL=index.d.mts.map