seedcord 0.7.0 → 0.8.0

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.cts CHANGED
@@ -1,13 +1,60 @@
1
- import { CoordinatedShutdown, CoordinatedStartup, Logger, StartupPhase } from '@seedcord/services';
1
+ import { SEEventMapLike, SENoEvents, StrictEventEmitter, CoordinatedShutdown, CoordinatedStartup, Logger, StartupPhase } from '@seedcord/services';
2
2
  export * from '@seedcord/services';
3
- import { ClientOptions, Guild, User, SlashCommandBuilder, ContextMenuCommandBuilder, Collection, Client, ChatInputCommandInteraction, ButtonInteraction, ModalSubmitInteraction, AutocompleteInteraction, AnySelectMenuInteraction, ContextMenuCommandInteraction, ClientEvents, Events, AutocompleteFocusedOption, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, EmbedBuilder, ModalBuilder, LabelBuilder, TextInputBuilder, FileUploadBuilder, ButtonBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, RoleSelectMenuBuilder, ContainerBuilder, TextDisplayBuilder, FileBuilder, MediaGalleryBuilder, SectionBuilder, SeparatorBuilder, ActionRowBuilder, ColorResolvable, Interaction, Role, TextChannel, TextChannelResolvable, MessageActionRowComponentBuilder, Message, GuildMember, Snowflake, WebhookClient } from 'discord.js';
4
- import { Tail, Nullable, TypedConstructor, TypedExclude } from '@seedcord/types';
3
+ import { ClientOptions, Guild, User, SlashCommandBuilder, ContextMenuCommandBuilder, Collection, ClientEvents, Interaction, Client, ChatInputCommandInteraction, ButtonInteraction, ModalSubmitInteraction, AutocompleteInteraction, AnySelectMenuInteraction, ContextMenuCommandInteraction, Events, AutocompleteFocusedOption, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, EmbedBuilder, ModalBuilder, LabelBuilder, TextInputBuilder, FileUploadBuilder, ButtonBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, RoleSelectMenuBuilder, ContainerBuilder, TextDisplayBuilder, FileBuilder, MediaGalleryBuilder, SectionBuilder, SeparatorBuilder, ActionRowBuilder, ColorResolvable, MessageComponentType, ComponentType, MessageComponentInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, Role, TextChannel, TextChannelResolvable, MessageActionRowComponentBuilder, Message, GuildMember, Snowflake, WebhookClient } from 'discord.js';
4
+ import { Tail, Nullable, TypedConstructor, TypedOmit, TypedExclude } from '@seedcord/types';
5
5
  export * from '@seedcord/types';
6
6
  import { UUID } from 'crypto';
7
- import { Constructor, RequireAtLeastOne } from 'type-fest';
7
+ import { Constructor, Promisable, NonEmptyTuple, TupleOf, IntClosedRange, RequireAtLeastOne } from 'type-fest';
8
8
  import { UUID as UUID$1 } from 'node:crypto';
9
9
  export * from '@seedcord/utils';
10
10
 
11
+ /**
12
+ * Emoji mapping interface. Augment this to add your project's emoji keys. Make sure to provide the same keys when configuring emojis in your bot config.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * declare module 'seedcord' {
17
+ * interface EmojiMap {
18
+ * ThumbsUp: string;
19
+ * ThumbsDown: string;
20
+ * Lol: string;
21
+ * Kek: string;
22
+ * }
23
+ * }
24
+ * ```
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * // Or extend it directly
29
+ * declare module 'seedcord' {
30
+ * export interface EmojiMap extends MyEmojiMap {}
31
+ * }
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // Then, import and use it anywhere in your app
37
+ * import { Emojis } from 'seedcord';
38
+ *
39
+ * console.log(Emojis.ThumbsUp); // '<:thumbsup:1872389747982323423>'
40
+ * ```
41
+ */
42
+ interface EmojiMap {
43
+ }
44
+ /**
45
+ * Global emoji mappings object
46
+ *
47
+ * @see {@link EmojiMap}
48
+ */
49
+ declare const Emojis: EmojiMap;
50
+ declare class EmojiInjector {
51
+ private readonly core;
52
+ private readonly logger;
53
+ constructor(core: Core);
54
+ init(): Promise<void>;
55
+ private clearEmojis;
56
+ }
57
+
11
58
  /**
12
59
  * Djs Interactions handlers
13
60
  *
@@ -17,7 +64,10 @@ interface InteractionsConfig {
17
64
  * Path to dir containing interaction handlers.
18
65
  */
19
66
  path: string;
20
- ignoreCustomIds?: string[];
67
+ /**
68
+ * Optional array of custom IDs or regex patterns to ignore in interaction handling
69
+ */
70
+ ignoreCustomIds?: (string | RegExp)[];
21
71
  /**
22
72
  * Optional path to interaction middleware directory
23
73
  */
@@ -72,6 +122,9 @@ interface BotConfig {
72
122
  *
73
123
  * Value: The emoji identifier used in Discord
74
124
  *
125
+ * @see {@link EmojiMap}
126
+ * @see {@link Emojis}
127
+ *
75
128
  * @example
76
129
  * ```ts
77
130
  * // This map's values...
@@ -94,7 +147,7 @@ interface BotConfig {
94
147
  * };
95
148
  * ```
96
149
  */
97
- emojis?: Record<string, string>;
150
+ emojis?: Partial<EmojiMap>;
98
151
  /**
99
152
  * Whether to show the error stack trace in the terminal in errors caught by the `@Catchable` decorator
100
153
  *
@@ -118,7 +171,7 @@ interface Initializeable {
118
171
  * Extend this class to create plugins that integrate with the Seedcord lifecycle.
119
172
  * Plugins have access to the core instance and must implement initialization logic.
120
173
  */
121
- declare abstract class Plugin implements Initializeable {
174
+ declare abstract class Plugin<TPluginEvents extends SEEventMapLike<TPluginEvents> = SENoEvents> extends StrictEventEmitter<TPluginEvents> implements Initializeable {
122
175
  protected pluggable: Core;
123
176
  /** Logger instance for this plugin - must be implemented by subclasses */
124
177
  abstract logger: Logger;
@@ -145,7 +198,7 @@ type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;
145
198
  * Provides plugin attachment capabilities and lifecycle management.
146
199
  * Plugins are attached during configuration and initialized during startup.
147
200
  */
148
- declare class Pluggable {
201
+ declare class Pluggable<TPluggableEvents extends SEEventMapLike<TPluggableEvents> = SENoEvents> extends StrictEventEmitter<TPluggableEvents> {
149
202
  protected isInitialized: boolean;
150
203
  protected readonly shutdown: CoordinatedShutdown;
151
204
  protected readonly startup: CoordinatedStartup;
@@ -167,7 +220,7 @@ declare class Pluggable {
167
220
  * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
168
221
  * @param args - Additional arguments to pass to the plugin constructor
169
222
  * @returns This instance with the plugin attached as a typed property
170
- * @throws An {@link Error} When called after initialization or if key already exists
223
+ * @throws A {@link SeedcordError} When called after initialization or if key already exists
171
224
  * @example
172
225
  * ```typescript
173
226
  * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
@@ -301,11 +354,22 @@ declare class CommandRegistry implements Initializeable {
301
354
  setCommands(): Promise<void>;
302
355
  }
303
356
 
357
+ /**
358
+ * Bot event types
359
+ */
360
+ interface BotEvents {
361
+ 'error:unhandled:interaction': [error: Error];
362
+ 'error:unhandled:event': [error: Error];
363
+ 'any:event': {
364
+ [K in keyof ClientEvents]: [K, ...ClientEvents[K]];
365
+ }[keyof ClientEvents];
366
+ 'any:interaction': [interaction: Interaction];
367
+ }
304
368
  /**
305
369
  * Discord bot implementation that manages client and controllers
306
370
  * @internal - Accessed via core.bot, not directly instantiated by users
307
371
  */
308
- declare class Bot extends Plugin {
372
+ declare class Bot extends Plugin<BotEvents> {
309
373
  protected core: Core;
310
374
  readonly botToken: string;
311
375
  readonly logger: Logger;
@@ -315,6 +379,7 @@ declare class Bot extends Plugin {
315
379
  private readonly events;
316
380
  readonly commands: CommandRegistry;
317
381
  private readonly emojiInjector;
382
+ readonly emojis: EmojiMap;
318
383
  constructor(core: Core);
319
384
  /**
320
385
  * Initializes Discord client and all controllers
@@ -335,6 +400,23 @@ declare class Bot extends Plugin {
335
400
  */
336
401
  private logout;
337
402
  get client(): Client;
403
+ /**
404
+ * Emits with correlation between a Discord event key and its exact arg tuple.
405
+ *
406
+ * @typeParam TKey - Discord event name
407
+ * @param event - Must be the literal `any:event`
408
+ * @param name - Concrete Discord event name
409
+ * @param args - Exact tuple for that Discord event
410
+ */
411
+ emit<TKey extends keyof ClientEvents>(event: 'any:event', name: TKey, ...args: ClientEvents[TKey]): boolean;
412
+ /**
413
+ * Fallback for other BotEvents keys.
414
+ *
415
+ * @typeParam TEventKey - Bot event key
416
+ * @param event - Bot event key
417
+ * @param args - Tuple payload for the key
418
+ */
419
+ emit<TEventKey extends keyof BotEvents>(event: TEventKey, ...args: BotEvents[TEventKey]): boolean;
338
420
  }
339
421
 
340
422
  /**
@@ -360,7 +442,7 @@ declare class Seedcord extends Pluggable implements Core {
360
442
  * Creates a new Seedcord instance
361
443
  *
362
444
  * @param config - Bot configuration including paths and Discord client options
363
- * @throws An {@link Error} When attempting to create multiple instances (singleton)
445
+ * @throws An {@link SeedcordError} When attempting to create multiple instances (singleton)
364
446
  */
365
447
  constructor(config: Config);
366
448
  /**
@@ -378,14 +460,17 @@ declare class Seedcord extends Pluggable implements Core {
378
460
 
379
461
  /** All valid Discord.js interaction types that can be handled */
380
462
  type ValidInteractionTypes = ChatInputCommandInteraction | ButtonInteraction | ModalSubmitInteraction | AutocompleteInteraction | AnySelectMenuInteraction | ContextMenuCommandInteraction;
463
+ type ValidNonInteractionKeys = Exclude<keyof ClientEvents, Events.InteractionCreate>;
381
464
  /** All valid Discord.js client events except interaction events */
382
- type ValidNonInteractionTypes = ClientEvents[Exclude<keyof ClientEvents, Events.InteractionCreate>];
465
+ type ValidNonInteractionTypes = ClientEvents[ValidNonInteractionKeys];
383
466
  /** All event types that can be handled (interactions and client events) */
384
467
  type ValidEventTypes = ValidInteractionTypes | ValidNonInteractionTypes;
385
468
  /** Interaction types that can receive replies (excludes autocomplete) */
386
469
  type Repliables = Exclude<ValidInteractionTypes, AutocompleteInteraction>;
387
470
  /** Handler types that can reply to interactions */
388
471
  type RepliableInteractionHandler = InteractionHandler<Repliables> | InteractionMiddleware<Repliables>;
472
+ /** Event handler types that can reply to events */
473
+ type RepliableEventHandler = EventHandler<ValidNonInteractionKeys> | EventMiddleware<ValidNonInteractionKeys>;
389
474
  /** Base interface for event handlers */
390
475
  interface Handler {
391
476
  execute(): Promise<void>;
@@ -427,12 +512,17 @@ declare abstract class BaseHandler<ValidEvent extends ValidEventTypes> implement
427
512
  protected errored: boolean;
428
513
  protected event: ValidEvent;
429
514
  protected args: string[];
515
+ protected logger: Logger;
430
516
  protected constructor(event: ValidEvent, core: Core, args?: string[]);
431
517
  /**
432
518
  * Main handler logic - implement this method to define behavior
433
519
  * @virtual Override this method in your handler classes
434
520
  */
435
521
  abstract execute(): Promise<void>;
522
+ /**
523
+ * Populates the handler with necessary data before execution. Override this method in your handler classes to customize population logic. This method is called at the end of the constructor before all async operations.
524
+ */
525
+ protected populate(): void;
436
526
  hasChecks(): this is HandlerWithChecks;
437
527
  hasErrors(): boolean;
438
528
  setErrored(): void;
@@ -479,7 +569,7 @@ declare abstract class InteractionMiddleware<Repliable extends Repliables> exten
479
569
  *
480
570
  * Middleware runs before event handlers and can modify behavior or block execution.
481
571
  */
482
- declare abstract class EventMiddleware<EventName extends keyof ClientEvents> extends BaseHandler<ClientEvents[EventName]> implements Handler {
572
+ declare abstract class EventMiddleware<EventName extends ValidNonInteractionKeys> extends BaseHandler<ClientEvents[EventName]> implements Handler {
483
573
  constructor(event: ClientEvents[EventName], core: Core, args?: string[]);
484
574
  }
485
575
  /**
@@ -501,7 +591,7 @@ declare abstract class AutocompleteHandler extends BaseHandler<AutocompleteInter
501
591
  *
502
592
  * @typeParam Repliable - The Discord event type this handler processes
503
593
  */
504
- declare abstract class EventHandler<Repliable extends keyof ClientEvents> extends BaseHandler<ClientEvents[Repliable]> implements Handler {
594
+ declare abstract class EventHandler<Repliable extends ValidNonInteractionKeys> extends BaseHandler<ClientEvents[Repliable]> implements Handler {
505
595
  constructor(event: ClientEvents[Repliable], core: Core, args?: string[]);
506
596
  }
507
597
  /** Constructor type for interaction and autocomplete handlers */
@@ -511,7 +601,7 @@ type InteractionMiddlewareConstructor = TypedConstructor<typeof InteractionMiddl
511
601
  /** Constructor type for legacy interaction middleware */
512
602
  type MiddlewareConstructor = InteractionMiddlewareConstructor;
513
603
  /** Constructor type for event middleware */
514
- type EventMiddlewareConstructor = TypedConstructor<typeof EventMiddleware> & (new <EventName extends keyof ClientEvents>(event: ClientEvents[EventName], core: Core, args?: string[]) => EventMiddleware<EventName>);
604
+ type EventMiddlewareConstructor = TypedConstructor<typeof EventMiddleware> & (new <EventName extends ValidNonInteractionKeys>(event: ClientEvents[EventName], core: Core, args?: string[]) => EventMiddleware<EventName>);
515
605
  /** Constructor type for autocomplete handlers */
516
606
  type AutocompleteHandlerConstructor = TypedConstructor<typeof AutocompleteHandler> & (new (event: AutocompleteInteraction, core: Core, args?: string[]) => AutocompleteHandler);
517
607
  /** Constructor type for Discord client event handlers */
@@ -830,7 +920,7 @@ declare function RegisterCommand(scope: 'guild', guilds: string[]): (ctor: Comma
830
920
  * }
831
921
  * ```
832
922
  */
833
- declare function EventCatchable(log?: boolean): (_target: EventHandler<keyof ClientEvents>, _prop: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<void>>) => void;
923
+ declare function EventCatchable(log?: boolean): (_target: RepliableEventHandler, _prop: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<void>>) => void;
834
924
 
835
925
  /**
836
926
  * The frequency at which the event handler should be executed.
@@ -843,9 +933,9 @@ type EventFrequency = 'once' | 'on';
843
933
  * @internal
844
934
  */
845
935
  declare const EventMetadataKey: unique symbol;
846
- /** Options accepted by the `@RegisterEvent` decorator. */
936
+ /** Options accepted by the event registration decorator. */
847
937
  interface RegisterEventOptions {
848
- /** Frequency: `'once'` or `'on'`. Defaults to `'on'`. */
938
+ /** Frequency: 'once' or 'on'. Defaults to 'on'. */
849
939
  readonly frequency?: EventFrequency | undefined;
850
940
  }
851
941
  /**
@@ -853,24 +943,28 @@ interface RegisterEventOptions {
853
943
  *
854
944
  * @internal
855
945
  */
856
- interface RegisterEventMetadataEntry<KeyofEvents extends keyof ClientEvents> {
857
- readonly event: KeyofEvents;
946
+ interface RegisterEventMetadataEntry<EventKey extends keyof ClientEvents> {
947
+ readonly event: EventKey;
858
948
  readonly frequency: EventFrequency;
859
949
  }
950
+ /** Tuple describing a single event and optional options. */
951
+ type EventSpec<EventKey extends ValidNonInteractionKeys> = readonly [
952
+ event: EventKey,
953
+ options?: RegisterEventOptions
954
+ ];
860
955
  /**
861
- * Registers an event handler class with a one or multiple Discord.js event(s).
956
+ * Registers an event handler class with one or more Discord.js events.
862
957
  *
863
- * Associates the decorated class with the mentioned Discord client event(s) for automatic registration and execution when the event is emitted.
958
+ * Associates the decorated class with the provided Discord client events for automatic registration and execution when those events are emitted.
864
959
  *
865
- * You can use this decorator multiple times on the same class to register for different events with varying option settings.
960
+ * Supply any number of event tuples. Each tuple can include per event options.
866
961
  *
867
- * @typeParam KeyofEvents - The key of the Discord.js ClientEvents to register for
868
- * @param events - The Discord.js event name(s) to listen for
869
- * @param options - Options to configure the event handler registration.
962
+ * @typeParam EventKey - Union of Discord.js ClientEvents keys to register for
963
+ * @param defs - One or more tuples of [event, options]
870
964
  * @decorator
871
965
  * @example
872
- * ```typescript
873
- * \@RegisterEvent(Events.MessageCreate)
966
+ * ```ts
967
+ * \@RegisterEvent([Events.MessageCreate])
874
968
  * class MessageHandler extends EventHandler<Events.MessageCreate> {
875
969
  * async execute() {
876
970
  * // Handle message creation
@@ -879,8 +973,8 @@ interface RegisterEventMetadataEntry<KeyofEvents extends keyof ClientEvents> {
879
973
  * ```
880
974
  *
881
975
  * @example
882
- * ```typescript
883
- * \@RegisterEvent([Events.MessageCreate, Events.MessageUpdate])
976
+ * ```ts
977
+ * \@RegisterEvent([Events.MessageCreate], [Events.MessageUpdate])
884
978
  * class MessageHandler extends EventHandler<Events.MessageCreate | Events.MessageUpdate> {
885
979
  * async execute() {
886
980
  * // Handle message creation or update
@@ -890,15 +984,15 @@ interface RegisterEventMetadataEntry<KeyofEvents extends keyof ClientEvents> {
890
984
  *
891
985
  * @example
892
986
  * ```ts
893
- * \@RegisterEvent(Events.MessageCreate, { frequency: 'once' })
894
- * class OneTimeMessageHandler extends EventHandler<Events.MessageCreate> {
987
+ * \@RegisterEvent([Events.MessageCreate, { frequency: 'once' }], [Events.MessageUpdate])
988
+ * class OneTimeMessageHandler extends EventHandler<Events.MessageCreate | Events.MessageUpdate> {
895
989
  * async execute() {
896
- * // Handle only the first message creation
990
+ * // Handle only the first message creation, and message update normally
897
991
  * }
898
992
  * }
899
993
  * ```
900
994
  */
901
- declare function RegisterEvent<KeyofEvents extends keyof ClientEvents>(events: KeyofEvents | KeyofEvents[], options?: RegisterEventOptions): (constructor: Constructor<unknown>) => void;
995
+ declare function RegisterEvent<const Defs extends readonly EventSpec<ValidNonInteractionKeys>[]>(...defs: Defs): <HandlerCtor extends Constructor<EventHandler<Defs[number][0]>>>(constructor: HandlerCtor) => void;
902
996
 
903
997
  /**
904
998
  * Enum defining interaction route types for decorators
@@ -966,7 +1060,7 @@ declare const InteractionMetadataKey: unique symbol;
966
1060
  * }
967
1061
  * ```
968
1062
  */
969
- declare function SlashRoute(routeOrRoutes: string | string[]): (constructor: Constructor<unknown>) => void;
1063
+ declare function SlashRoute(routeOrRoutes: string | string[]): (constructor: Constructor<InteractionHandler<ChatInputCommandInteraction>>) => void;
970
1064
  /**
971
1065
  * Routes button interactions to handler classes
972
1066
  *
@@ -977,7 +1071,7 @@ declare function SlashRoute(routeOrRoutes: string | string[]): (constructor: Con
977
1071
  * @param routeOrRoutes - CustomId prefix(es) to handle
978
1072
  * @decorator
979
1073
  */
980
- declare function ButtonRoute(routeOrRoutes: string | string[]): (constructor: Constructor<unknown>) => void;
1074
+ declare function ButtonRoute(routeOrRoutes: string | string[]): (constructor: Constructor<InteractionHandler<ButtonInteraction>>) => void;
981
1075
  /**
982
1076
  * Routes modal submissions to handler classes
983
1077
  *
@@ -986,7 +1080,7 @@ declare function ButtonRoute(routeOrRoutes: string | string[]): (constructor: Co
986
1080
  * @param routeOrRoutes - CustomId prefix(es) to handle
987
1081
  * @decorator
988
1082
  */
989
- declare function ModalRoute(routeOrRoutes: string | string[]): (constructor: Constructor<unknown>) => void;
1083
+ declare function ModalRoute(routeOrRoutes: string | string[]): (constructor: Constructor<InteractionHandler<ModalSubmitInteraction>>) => void;
990
1084
  /**
991
1085
  * Routes context menu commands to handler classes
992
1086
  *
@@ -994,7 +1088,7 @@ declare function ModalRoute(routeOrRoutes: string | string[]): (constructor: Con
994
1088
  * @param routeOrRoutes - Command name(s) to handle
995
1089
  * @decorator
996
1090
  */
997
- declare function ContextMenuRoute(type: 'message' | 'user', routeOrRoutes: string | string[]): (constructor: Constructor<unknown>) => void;
1091
+ declare function ContextMenuRoute(type: 'message' | 'user', routeOrRoutes: string | string[]): (constructor: Constructor<InteractionHandler<ContextMenuCommandInteraction>>) => void;
998
1092
  /**
999
1093
  * Routes autocomplete interactions to handler classes
1000
1094
  *
@@ -1007,7 +1101,7 @@ declare function ContextMenuRoute(type: 'message' | 'user', routeOrRoutes: strin
1007
1101
  * @example \@AutocompleteRoute(['user', 'profile'], ['name', 'bio']) // Multiple commands, multiple fields
1008
1102
  * @decorator
1009
1103
  */
1010
- declare function AutocompleteRoute(commandRoutes: string | string[], focusedFields: string | string[]): (constructor: Constructor<unknown>) => void;
1104
+ declare function AutocompleteRoute(commandRoutes: string | string[], focusedFields: string | string[]): (constructor: Constructor<AutocompleteHandler>) => void;
1011
1105
  /**
1012
1106
  * Routes select menu interactions to handler classes
1013
1107
  *
@@ -1026,7 +1120,7 @@ declare function AutocompleteRoute(commandRoutes: string | string[], focusedFiel
1026
1120
  * }
1027
1121
  * ```
1028
1122
  */
1029
- declare function SelectMenuRoute(type: SelectMenuType, routeOrRoutes: string | string[]): (constructor: Constructor<unknown>) => void;
1123
+ declare function SelectMenuRoute(type: SelectMenuType, routeOrRoutes: string | string[]): (constructor: Constructor<InteractionHandler<AnySelectMenuInteraction>>) => void;
1030
1124
 
1031
1125
  /**
1032
1126
  * Middleware types supported by Seedcord
@@ -1043,12 +1137,14 @@ declare enum MiddlewareType {
1043
1137
  declare const MiddlewareMetadataKey: unique symbol;
1044
1138
  /**
1045
1139
  * Additional middleware registration options
1140
+ *
1141
+ * @typeParam MType - The type of middleware being registered
1046
1142
  */
1047
- interface MiddlewareOptions {
1143
+ interface MiddlewareOptions<MType extends MiddlewareType> {
1048
1144
  /**
1049
1145
  * Restrict event middleware execution to specific Discord client events
1050
1146
  */
1051
- readonly events?: readonly (keyof ClientEvents)[];
1147
+ readonly events?: MType extends MiddlewareType.Event ? readonly ValidNonInteractionKeys[] : never;
1052
1148
  }
1053
1149
  /**
1054
1150
  * Metadata stored for middleware registration
@@ -1065,7 +1161,7 @@ interface MiddlewareMetadata {
1065
1161
  /**
1066
1162
  * Optional list of Discord client events to target
1067
1163
  */
1068
- events?: readonly (keyof ClientEvents)[];
1164
+ events?: readonly ValidNonInteractionKeys[];
1069
1165
  }
1070
1166
  /**
1071
1167
  * Decorator used to register middleware with priority ordering. The lower the priority number, the earlier it runs.
@@ -1083,10 +1179,197 @@ interface MiddlewareMetadata {
1083
1179
  * \@Middleware(MiddlewareType.Event, 10, { events: [Events.MessageCreate, Events.MessageUpdate] })
1084
1180
  * class MyEventMiddleware extends EventMiddleware {}
1085
1181
  * ```
1086
- * @throws A {@link TypeError} If priority is not a finite number
1087
- * @throws An {@link Error} If interaction middleware specifies event filters
1182
+ * @throws A {@link SeedcordTypeError} If priority is not a finite number
1183
+ * @throws A {@link SeedcordError} If interaction middleware specifies event filters
1184
+ */
1185
+ declare function Middleware<MType extends MiddlewareType>(type: MType, priority?: number, options?: MiddlewareOptions<MType>): (ctor: Constructor<MType extends MiddlewareType.Interaction ? InteractionMiddleware<Repliables> : EventMiddleware<ValidNonInteractionKeys>>) => void;
1186
+
1187
+ /**
1188
+ * Context object supplied to Confirmable factory callbacks.
1189
+ */
1190
+ interface ConfirmableContext {
1191
+ /** Handler instance that owns the decorated method. */
1192
+ handler: RepliableInteractionHandler;
1193
+ /** Interaction that triggered the decorated handler. */
1194
+ interaction: Repliables;
1195
+ /** Resolved confirmation question shown to the user. */
1196
+ question: string;
1197
+ }
1198
+ /**
1199
+ * Factory helper that supports synchronous and asynchronous Confirmable values.
1200
+ */
1201
+ type ConfirmableFactory<ConfirmableResult> = ConfirmableResult | ((ctx: ConfirmableContext) => Promisable<ConfirmableResult>);
1202
+ /**
1203
+ * Utility type to extract the underlying component type from builder components.
1204
+ *
1205
+ * @internal
1206
+ */
1207
+ type ExtractComponent<TComp> = TComp extends {
1208
+ component: infer C;
1209
+ } ? C : never;
1210
+ /**
1211
+ * Normalized container component structure used by Confirmable internals.
1212
+ *
1213
+ * @internal
1214
+ */
1215
+ type ContainerLike = ExtractComponent<BuilderComponent<'container'>>;
1216
+ /**
1217
+ * Normalized embed component structure used by Confirmable internals.
1218
+ *
1219
+ * Ensures the embed count remains within the Discord limit of 1..10.
1220
+ *
1221
+ * @internal
1222
+ */
1223
+ type EmbedLike = TupleOf<IntClosedRange<1, 10>, ExtractComponent<BuilderComponent<'embed'>>>;
1224
+ /**
1225
+ * Normalized action row structure for button components.
1226
+ *
1227
+ * @internal
1228
+ */
1229
+ type RowLike = ExtractComponent<RowComponent<'button'>>;
1230
+ /**
1231
+ * Response payload produced when using classic message component rows.
1232
+ *
1233
+ * @internal
1234
+ */
1235
+ interface ClassicPayload {
1236
+ components: readonly RowLike[];
1237
+ embeds?: EmbedLike;
1238
+ content?: string;
1239
+ flags?: unknown;
1240
+ }
1241
+ /**
1242
+ * Response payload produced when using the Component V2 container API.
1243
+ *
1244
+ * @internal
1245
+ */
1246
+ interface ComponentsV2Payload {
1247
+ flags: 'IsComponentsV2';
1248
+ components: readonly ContainerLike[];
1249
+ }
1250
+ /**
1251
+ * Payload variants that Confirmable may send back to the platform.
1252
+ *
1253
+ * @internal
1254
+ */
1255
+ type ConfirmablePayload = ClassicPayload | ComponentsV2Payload;
1256
+ /**
1257
+ * Information supplied to the optional onResolved callback.
1258
+ */
1259
+ interface ConfirmableResolution extends ConfirmableContext {
1260
+ /** Whether the user confirmed the action. */
1261
+ confirmed: boolean;
1262
+ /** Whether the confirmation UI timed out without a response. */
1263
+ timedOut: boolean;
1264
+ /** Interaction that resolved the decision, if any. */
1265
+ button?: MessageComponentInteraction;
1266
+ }
1267
+ /**
1268
+ * Maps a message component type to the corresponding interaction subtype.
1269
+ *
1270
+ * @internal
1271
+ */
1272
+ type ComponentInteractionFor<TComponentType extends MessageComponentType> = TComponentType extends ComponentType.Button ? ButtonInteraction : TComponentType extends ComponentType.StringSelect ? StringSelectMenuInteraction : TComponentType extends ComponentType.UserSelect ? UserSelectMenuInteraction : TComponentType extends ComponentType.RoleSelect ? RoleSelectMenuInteraction : TComponentType extends ComponentType.MentionableSelect ? MentionableSelectMenuInteraction : TComponentType extends ComponentType.ChannelSelect ? ChannelSelectMenuInteraction : MessageComponentInteraction;
1273
+ /**
1274
+ * Confirmable configuration shared across the classic and component V2 modes.
1275
+ */
1276
+ interface ConfirmableSharedOptions<TComponentType extends MessageComponentType = ComponentType.Button> {
1277
+ ephemeral?: boolean;
1278
+ timeoutMs?: number;
1279
+ componentType?: TComponentType;
1280
+ onResolved?: (r: ConfirmableResolution) => Promisable<void>;
1281
+ defer?: boolean;
1282
+ }
1283
+ /**
1284
+ * Classic mode payload that allows omitting components when clearing the UI.
1285
+ */
1286
+ type MaybeClearedClassic = TypedOmit<ClassicPayload, 'components'> & {
1287
+ components?: readonly RowLike[];
1288
+ };
1289
+ /**
1290
+ * Outcome UI factories used by classic message component rows.
1291
+ */
1292
+ interface OutcomeUiClassic {
1293
+ /** Factory invoked when the user cancels the prompt. */
1294
+ onCancel: ConfirmableFactory<MaybeClearedClassic>;
1295
+ /** Factory invoked when the prompt times out. */
1296
+ onTimeout: ConfirmableFactory<MaybeClearedClassic>;
1297
+ /** Factory invoked when the user confirms the prompt. */
1298
+ onConfirm?: ConfirmableFactory<MaybeClearedClassic>;
1299
+ }
1300
+ /**
1301
+ * Outcome UI factories used by the Component V2 container API.
1302
+ */
1303
+ interface OutcomeUiV2 {
1304
+ /** Factory invoked when the user cancels the prompt. */
1305
+ onCancel: ConfirmableFactory<ComponentsV2Payload>;
1306
+ /** Factory invoked when the prompt times out. */
1307
+ onTimeout: ConfirmableFactory<ComponentsV2Payload>;
1308
+ /** Factory invoked when the user confirms the prompt. */
1309
+ onConfirm?: ConfirmableFactory<ComponentsV2Payload>;
1310
+ }
1311
+ /**
1312
+ * Source definition that resolves a confirmation decision from a component interaction.
1313
+ */
1314
+ interface ResolverSource<TComponentType extends MessageComponentType> {
1315
+ /** Message component type expected by the resolver. */
1316
+ componentType: TComponentType;
1317
+ /** Resolves whether the user confirmed the prompt. */
1318
+ resolveDecision: (i: ComponentInteractionFor<TComponentType>) => Promisable<boolean>;
1319
+ }
1320
+ /**
1321
+ * Source definition that lists custom IDs for confirm and cancel actions.
1322
+ */
1323
+ interface CustomIdSource {
1324
+ /** Custom IDs that should resolve the confirmation as accepted. */
1325
+ confirmCustomIds: readonly string[];
1326
+ /** Optional custom IDs that should resolve the confirmation as rejected. */
1327
+ cancelCustomIds?: readonly string[];
1328
+ }
1329
+ /**
1330
+ * Configuration for Confirmable when using classic message component rows.
1331
+ */
1332
+ type ConfirmableClassicOptions<TComponentType extends MessageComponentType = ComponentType.Button> = ConfirmableSharedOptions<TComponentType> & (ResolverSource<TComponentType> | CustomIdSource) & {
1333
+ /** Prompt content or embed factory shown to the user. */
1334
+ prompt: ConfirmableFactory<BuilderComponent<'embed'> | string>;
1335
+ /** Action row factory producing confirm and cancel components. */
1336
+ rows: ConfirmableFactory<NonEmptyTuple<RowLike>>;
1337
+ container?: never;
1338
+ /** Outcome UI factories applied once the decision is resolved. */
1339
+ outcomeUi: OutcomeUiClassic;
1340
+ };
1341
+ /**
1342
+ * Configuration for Confirmable when using the Component V2 container API.
1343
+ */
1344
+ type ConfirmableComponentsV2Options<TComponentType extends MessageComponentType = ComponentType.Button> = ConfirmableSharedOptions<TComponentType> & (ResolverSource<TComponentType> | CustomIdSource) & {
1345
+ /** Container factory that renders the confirmation UI. */
1346
+ container: ConfirmableFactory<BuilderComponent<'container'>>;
1347
+ prompt?: never;
1348
+ rows?: never;
1349
+ /** Outcome UI factories applied once the decision is resolved. */
1350
+ outcomeUi: OutcomeUiV2;
1351
+ };
1352
+ /**
1353
+ * Union of supported Confirmable configuration variants.
1088
1354
  */
1089
- declare function Middleware(type: MiddlewareType, priority?: number, options?: MiddlewareOptions): ClassDecorator;
1355
+ type ConfirmableOptions<TComponentType extends MessageComponentType = ComponentType.Button> = ConfirmableClassicOptions<TComponentType> | ConfirmableComponentsV2Options<TComponentType>;
1356
+ /**
1357
+ * Supported signature for the question argument passed to {@link Confirmable}.
1358
+ */
1359
+ type QuestionInput = string | (() => Promise<string>);
1360
+
1361
+ /**
1362
+ * Wraps a repliable handler method with an interactive confirmation flow.
1363
+ *
1364
+ * Displays a prompt, waits for a follow-up component interaction, and conditionally executes the original method
1365
+ * based on the user's decision. The decorator supports both classic action rows + embed/content and ComponentV2 containers.
1366
+ *
1367
+ * @typeParam TComp - Message component type that should resolve the confirmation.
1368
+ * @param question - Static string or lazy factory that resolves the prompt question.
1369
+ * @param options - Confirmation flow configuration.
1370
+ * @decorator
1371
+ */
1372
+ declare function Confirmable<TComponent extends MessageComponentType = ComponentType.Button>(question: QuestionInput, options: ConfirmableOptions<TComponent>): (_target: RepliableInteractionHandler, _propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: unknown[]) => Promise<void>>) => TypedPropertyDescriptor<(...args: unknown[]) => Promise<void>>;
1090
1373
 
1091
1374
  /**
1092
1375
  * Manages Discord event handler registration and execution.
@@ -1333,13 +1616,6 @@ declare class UserNotFound extends CustomError {
1333
1616
  constructor(userArg: string);
1334
1617
  }
1335
1618
 
1336
- declare class EmojiInjector {
1337
- private readonly core;
1338
- private readonly logger;
1339
- constructor(core: Core);
1340
- init(): Promise<void>;
1341
- }
1342
-
1343
1619
  /**
1344
1620
  * Fetches and validates a text channel.
1345
1621
  *
@@ -1523,7 +1799,7 @@ declare function fetchRole(clientOrGuild: Client | Guild, roleId: string): Promi
1523
1799
  * @param client - The Discord client instance
1524
1800
  * @param guild - The guild to get the bot's role from
1525
1801
  * @returns The bot's highest role in the guild
1526
- * @throws An {@link Error} When the bot is not in the guild
1802
+ * @throws A {@link SeedcordError} When the bot is not in the guild
1527
1803
  */
1528
1804
  declare function getBotRole(client: Client, guild: Guild): Role;
1529
1805
 
@@ -1735,7 +2011,7 @@ declare abstract class WebhookLog<KeyOfEffects extends EffectKeys> extends Effec
1735
2011
  *
1736
2012
  * Developers need to set the UNKNOWN_EXCEPTION_WEBHOOK_URL environment variable in their .env file otherwise this effect will throw an error during initialization.
1737
2013
  *
1738
- * @throws Error if UNKNOWN_EXCEPTION_WEBHOOK_URL is not set or is invalid
2014
+ * @throws A {@link SeedcordError} if UNKNOWN_EXCEPTION_WEBHOOK_URL is not set or is invalid
1739
2015
  */
1740
2016
  declare class UnknownException extends WebhookLog<'unknownException'> {
1741
2017
  private static readonly logger;
@@ -1745,4 +2021,4 @@ declare class UnknownException extends WebhookLog<'unknownException'> {
1745
2021
  private prepareMetadataFile;
1746
2022
  }
1747
2023
 
1748
- export { type ActionRowComponentType, type AllEffects, type AtleastOneMessageComponent, AutocompleteHandler, type AutocompleteHandlerConstructor, AutocompleteRoute, BaseComponent, type BaseCore, BaseErrorEmbed, BaseHandler, Bot, type BotConfig, type BotPermissionScope, type BotPermissionScopeGroups, BuilderComponent, type BuilderType, BuilderTypes, ButtonRoute, CannotAssignBotRole, CannotSendEmbedsError, Catchable, type CatchableOptions, ChannelNotFoundError, ChannelNotTextChannel, Checkable, type CommandCtor, type CommandMeta, CommandMetadataKey, CommandRegistry, type CommandRouteString, type CommandScope, type CommandsConfig, type Config, ContextMenuRoute, type Core, CouldNotFindChannel, CustomError, type CustomErrorConstructor, DatabaseError, type DefaultEffects, type EffectKeys, EffectMetadataKey, type EffectParams, type Effects, type EffectsConfig, EffectsEmitter, EffectsHandler, EffectsRegistry, EmojiInjector, EventCatchable, EventController, EventHandler, type EventHandlerConstructor, EventMetadataKey, EventMiddleware, type EventMiddlewareConstructor, type EventsConfig, type ExtractedErrorResponse, GenericError, type GlobalMeta, type GuildMeta, type Handler, type HandlerConstructor, type HandlerCtor, type HandlerWithChecks, HasDangerousPermissions, type Initializeable, type InstantiatedActionRow, type InstantiatedBuilder, InteractionController, InteractionHandler, InteractionMetadataKey, InteractionMiddleware, type InteractionMiddlewareConstructor, InteractionRoutes, type InteractionsConfig, type MessageContent, Middleware, type MiddlewareConstructor, type MiddlewareMetadata, MiddlewareMetadataKey, type MiddlewareOptions, MiddlewareType, MissingPermissions, ModalRoute, PERM_GROUPS, PermissionNames, Pluggable, Plugin, type PluginArgs, type PluginCtor, RegisterCommand, RegisterEffect, type RegisterEffectMetadataEntry, type RegisterEffectOptions, RegisterEvent, type RegisterEventMetadataEntry, type RegisterEventOptions, type RepliableInteractionHandler, type Repliables, RoleDoesNotExist, RoleHigherThanMe, RowComponent, RowTypes, Seedcord, SelectMenuRoute, SelectMenuType, SlashRoute, UnhandledEvent, UnknownException, UserNotFound, UserNotInGuild, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionTypes, WebhookLog, type WithChecks, attemptSendDM, buildSlashRoute, checkBotPermissions, checkPermissions, extractErrorResponse, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, hasPermsToAssign, sendInText, throwCustomError, updateMemberRoles };
2024
+ export { type ActionRowComponentType, type AllEffects, type AtleastOneMessageComponent, AutocompleteHandler, type AutocompleteHandlerConstructor, AutocompleteRoute, BaseComponent, type BaseCore, BaseErrorEmbed, BaseHandler, Bot, type BotConfig, type BotEvents, type BotPermissionScope, type BotPermissionScopeGroups, BuilderComponent, type BuilderType, BuilderTypes, ButtonRoute, CannotAssignBotRole, CannotSendEmbedsError, Catchable, type CatchableOptions, ChannelNotFoundError, ChannelNotTextChannel, Checkable, type ClassicPayload, type CommandCtor, type CommandMeta, CommandMetadataKey, CommandRegistry, type CommandRouteString, type CommandScope, type CommandsConfig, type ComponentInteractionFor, type ComponentsV2Payload, type Config, Confirmable, type ConfirmableClassicOptions, type ConfirmableComponentsV2Options, type ConfirmableContext, type ConfirmableFactory, type ConfirmableOptions, type ConfirmablePayload, type ConfirmableResolution, type ConfirmableSharedOptions, type ContainerLike, ContextMenuRoute, type Core, CouldNotFindChannel, CustomError, type CustomErrorConstructor, type CustomIdSource, DatabaseError, type DefaultEffects, type EffectKeys, EffectMetadataKey, type EffectParams, type Effects, type EffectsConfig, EffectsEmitter, EffectsHandler, EffectsRegistry, type EmbedLike, EmojiInjector, type EmojiMap, Emojis, EventCatchable, EventController, EventHandler, type EventHandlerConstructor, EventMetadataKey, EventMiddleware, type EventMiddlewareConstructor, type EventSpec, type EventsConfig, type ExtractComponent, type ExtractedErrorResponse, GenericError, type GlobalMeta, type GuildMeta, type Handler, type HandlerConstructor, type HandlerCtor, type HandlerWithChecks, HasDangerousPermissions, type Initializeable, type InstantiatedActionRow, type InstantiatedBuilder, InteractionController, InteractionHandler, InteractionMetadataKey, InteractionMiddleware, type InteractionMiddlewareConstructor, InteractionRoutes, type InteractionsConfig, type MaybeClearedClassic, type MessageContent, Middleware, type MiddlewareConstructor, type MiddlewareMetadata, MiddlewareMetadataKey, type MiddlewareOptions, MiddlewareType, MissingPermissions, ModalRoute, type OutcomeUiClassic, type OutcomeUiV2, PERM_GROUPS, PermissionNames, Pluggable, Plugin, type PluginArgs, type PluginCtor, type QuestionInput, RegisterCommand, RegisterEffect, type RegisterEffectMetadataEntry, type RegisterEffectOptions, RegisterEvent, type RegisterEventMetadataEntry, type RegisterEventOptions, type RepliableEventHandler, type RepliableInteractionHandler, type Repliables, type ResolverSource, RoleDoesNotExist, RoleHigherThanMe, RowComponent, type RowLike, RowTypes, Seedcord, SelectMenuRoute, SelectMenuType, SlashRoute, UnhandledEvent, UnknownException, UserNotFound, UserNotInGuild, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, type ValidNonInteractionTypes, WebhookLog, type WithChecks, attemptSendDM, buildSlashRoute, checkBotPermissions, checkPermissions, extractErrorResponse, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, hasPermsToAssign, sendInText, throwCustomError, updateMemberRoles };