seedcord 0.1.0-alpha.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.
@@ -0,0 +1,824 @@
1
+ import { ClientOptions, Guild, User, Client, ChatInputCommandInteraction, ButtonInteraction, ModalSubmitInteraction, AutocompleteInteraction, AnySelectMenuInteraction, ContextMenuCommandInteraction, ClientEvents, Events, AutocompleteFocusedOption, SlashCommandBuilder, EmbedBuilder, ButtonBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, RoleSelectMenuBuilder, ModalBuilder, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, ContextMenuCommandBuilder, ActionRowBuilder, TextInputBuilder, ColorResolvable, WebhookClient } from 'discord.js';
2
+ import { Nullish, LifecycleTask, PhaseEvents, UnionToTuple, Tail, TypedConstructor, ConstructorFunction, IDocument } from '@seedcord/types';
3
+ import { UUID } from 'crypto';
4
+ import { EventEmitter } from 'node:events';
5
+ import mongoose from 'mongoose';
6
+ import { Envapter } from 'envapt';
7
+ import * as fs from 'node:fs';
8
+
9
+ /**
10
+ * Discord interaction hooks configuration
11
+ */
12
+ interface InteractionsConfig {
13
+ /**
14
+ * Path for interaction hooks, such as slash commands and buttons.
15
+ */
16
+ path: string;
17
+ }
18
+ /**
19
+ * Discord event hooks configuration
20
+ */
21
+ interface EventsConfig {
22
+ /**
23
+ * Path for event hooks.
24
+ */
25
+ path: string;
26
+ }
27
+ /**
28
+ * Discord command registry configuration
29
+ */
30
+ interface CommandsConfig {
31
+ /**
32
+ * Path for command hooks.
33
+ */
34
+ path: string;
35
+ }
36
+ /**
37
+ * Discord hook hooks configuration
38
+ */
39
+ interface HooksConfig {
40
+ /**
41
+ * Path for hook hooks.
42
+ */
43
+ path: string;
44
+ }
45
+ /**
46
+ * Discord bot configuration
47
+ */
48
+ interface BotConfig {
49
+ interactions: InteractionsConfig;
50
+ events: EventsConfig;
51
+ commands: CommandsConfig;
52
+ /**
53
+ * Discord.js ClientOptions passed directly to the Client constructor
54
+ */
55
+ clientOptions: ClientOptions;
56
+ /**
57
+ * Optional emoji mappings for the bot
58
+ */
59
+ emojis?: Record<string, string>;
60
+ }
61
+ interface Config {
62
+ bot: BotConfig;
63
+ hooks: HooksConfig;
64
+ }
65
+
66
+ declare class Logger {
67
+ private logger;
68
+ private static readonly instances;
69
+ private static instance;
70
+ constructor(transportName: string);
71
+ private getFormatCustomizations;
72
+ private createConsoleTransport;
73
+ private initializeLogger;
74
+ error(msg: string, ...args: unknown[]): void;
75
+ warn(msg: string, ...args: unknown[]): void;
76
+ info(msg: string, ...args: unknown[]): void;
77
+ http(msg: string, ...args: unknown[]): void;
78
+ verbose(msg: string, ...args: unknown[]): void;
79
+ debug(msg: string, ...args: unknown[]): void;
80
+ silly(msg: string, ...args: unknown[]): void;
81
+ static Error(prefix: string, msg: string, ...args: unknown[]): void;
82
+ static Info(prefix: string, msg: string, ...args: unknown[]): void;
83
+ static Warn(prefix: string, msg: string, ...args: unknown[]): void;
84
+ static Debug(prefix: string, msg: string, ...args: unknown[]): void;
85
+ static Silly(prefix: string, msg: string, ...args: unknown[]): void;
86
+ }
87
+
88
+ interface DefaultHooks {
89
+ unknownException: {
90
+ uuid: UUID;
91
+ error: Error;
92
+ guild: Nullish<Guild>;
93
+ user: Nullish<User>;
94
+ };
95
+ }
96
+ interface Hooks {
97
+ }
98
+ interface AllHooks extends DefaultHooks, Hooks {
99
+ }
100
+ type HookKeys = keyof AllHooks;
101
+ type HookParams<KeyOfHooks extends HookKeys> = AllHooks[KeyOfHooks];
102
+
103
+ declare class HookController extends Plugin {
104
+ protected core: Core;
105
+ readonly logger: Logger;
106
+ private isInitialized;
107
+ private readonly hookMap;
108
+ private readonly emitter;
109
+ constructor(core: Core);
110
+ init(): Promise<void>;
111
+ private loadHooks;
112
+ private registerHook;
113
+ private isHookHandler;
114
+ private attachHooks;
115
+ emit<KeyOfHooks extends HookKeys>(event: KeyOfHooks, data: AllHooks[KeyOfHooks]): boolean;
116
+ }
117
+
118
+ /**
119
+ * Abstract base class for coordinated lifecycle management (startup/shutdown)
120
+ */
121
+ declare abstract class CoordinatedLifecycle<TPhase extends number> {
122
+ protected readonly phaseOrder: TPhase[];
123
+ protected readonly phaseEnum: Record<number, string>;
124
+ protected readonly logger: Logger;
125
+ protected readonly events: EventEmitter<[never]>;
126
+ protected readonly tasksMap: Map<TPhase, LifecycleTask[]>;
127
+ protected constructor(loggerName: string, phaseOrder: TPhase[], phaseEnum: Record<number, string>);
128
+ /**
129
+ * Add a task to a specific phase
130
+ */
131
+ addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void;
132
+ /**
133
+ * Remove a task by name from a specific phase
134
+ */
135
+ removeTask(phase: TPhase, taskName: string): boolean;
136
+ /**
137
+ * Run all tasks in a specific phase
138
+ */
139
+ protected runPhase(phase: TPhase): Promise<void>;
140
+ /**
141
+ * Run a single task with timeout
142
+ */
143
+ protected runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void>;
144
+ /**
145
+ * Subscribe to lifecycle events
146
+ */
147
+ on(event: string, listener: (...args: unknown[]) => void): void;
148
+ /**
149
+ * Unsubscribe from lifecycle events
150
+ */
151
+ off(event: string, listener: (...args: unknown[]) => void): void;
152
+ protected emit(event: string, ...args: unknown[]): boolean;
153
+ protected abstract canAddTask(): boolean;
154
+ protected abstract canRemoveTask(): boolean;
155
+ protected abstract getTaskType(): string;
156
+ protected abstract executeTasksInPhase(phase: TPhase, tasks: LifecycleTask[]): Promise<PromiseSettledResult<void>[]>;
157
+ }
158
+
159
+ /**
160
+ * Shutdown phases for coordinated application shutdown.
161
+ */
162
+ declare enum ShutdownPhase {
163
+ /** Stop accepting new requests/interactions */
164
+ StopAcceptingRequests = 1,
165
+ /** Stop background services (health checks, etc.) */
166
+ StopServices = 2,
167
+ /** Disconnect from external resources (database, APIs) */
168
+ ExternalResources = 3,
169
+ /** Disconnect from Discord */
170
+ DiscordCleanup = 4,
171
+ /** Final cleanup tasks */
172
+ FinalCleanup = 5
173
+ }
174
+ type CoordinatedShutdownEventKey = PhaseEvents<'shutdown', UnionToTuple<ShutdownPhase>>;
175
+ interface ShutdownTask extends LifecycleTask {
176
+ }
177
+ declare class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase> {
178
+ private isShuttingDown;
179
+ private exitCode;
180
+ constructor();
181
+ protected canAddTask(): boolean;
182
+ protected canRemoveTask(): boolean;
183
+ protected getTaskType(): string;
184
+ protected executeTasksInPhase(phase: ShutdownPhase, tasks: LifecycleTask[]): Promise<PromiseSettledResult<void>[]>;
185
+ private registerSignalHandlers;
186
+ /**
187
+ * Add a task to a specific shutdown phase
188
+ */
189
+ addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs?: number): void;
190
+ /**
191
+ * Remove a task by name from a specific phase
192
+ */
193
+ removeTask(phase: ShutdownPhase, taskName: string): boolean;
194
+ /**
195
+ * Start the coordinated shutdown sequence
196
+ */
197
+ run(exitCode?: number): Promise<void>;
198
+ /**
199
+ * Subscribe to shutdown events
200
+ */
201
+ on(event: CoordinatedShutdownEventKey, listener: (...args: unknown[]) => void): void;
202
+ /**
203
+ * Unsubscribe from shutdown events
204
+ */
205
+ off(event: CoordinatedShutdownEventKey, listener: (...args: unknown[]) => void): void;
206
+ }
207
+
208
+ /**
209
+ * Startup phases for coordinated plugin initialization.
210
+ */
211
+ declare enum StartupPhase {
212
+ /** Validate environment variables and config files */
213
+ Validation = 1,
214
+ /** Discover plugin constructors via decorators or registry */
215
+ Discovery = 2,
216
+ /** Register plugin metadata and declared dependencies */
217
+ Registration = 3,
218
+ /** Inject and validate plugin-specific configuration */
219
+ Configuration = 4,
220
+ /** Instantiate plugin classes with Core and arguments */
221
+ Instantiation = 5,
222
+ /** Activate plugins by calling their init/setup hooks */
223
+ Activation = 6,
224
+ /** Mark seedcord as ready and start handling interactions */
225
+ Ready = 7
226
+ }
227
+ type CoordinatedStartupEventKey = PhaseEvents<'startup', UnionToTuple<StartupPhase>>;
228
+ interface StartupTask extends LifecycleTask {
229
+ }
230
+ declare class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase> {
231
+ private isStartingUp;
232
+ private hasStarted;
233
+ constructor();
234
+ /**
235
+ * Add a task to a specific startup phase
236
+ */
237
+ addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs?: number): void;
238
+ protected canAddTask(): boolean;
239
+ protected canRemoveTask(): boolean;
240
+ protected getTaskType(): string;
241
+ protected executeTasksInPhase(phase: StartupPhase, tasks: LifecycleTask[]): Promise<PromiseSettledResult<void>[]>;
242
+ /**
243
+ * Start the coordinated startup sequence
244
+ */
245
+ run(): Promise<void>;
246
+ /**
247
+ * Subscribe to startup events
248
+ */
249
+ on(event: CoordinatedStartupEventKey, listener: (...args: unknown[]) => void): void;
250
+ /**
251
+ * Unsubscribe from startup events
252
+ */
253
+ off(event: CoordinatedStartupEventKey, listener: (...args: unknown[]) => void): void;
254
+ /**
255
+ * Check if startup has completed
256
+ */
257
+ get isReady(): boolean;
258
+ /**
259
+ * Check if startup is currently running
260
+ */
261
+ get isRunning(): boolean;
262
+ }
263
+
264
+ interface BaseCore {
265
+ readonly bot: Bot;
266
+ readonly hooks: HookController;
267
+ readonly shutdown: CoordinatedShutdown;
268
+ readonly startup: CoordinatedStartup;
269
+ readonly config: Config;
270
+ start(): Promise<this>;
271
+ }
272
+ interface Core extends BaseCore {
273
+ }
274
+
275
+ interface Initializeable {
276
+ init(): Promise<void>;
277
+ }
278
+ declare abstract class Plugin implements Initializeable {
279
+ protected pluggable: Core;
280
+ abstract logger: Logger;
281
+ constructor(pluggable: Core);
282
+ abstract init(): Promise<void>;
283
+ }
284
+ /** A plugin constructor can accept any tail of args after Core */
285
+ type PluginCtor<TPlugin extends Plugin = Plugin> = new (core: Core, ...args: any[]) => TPlugin;
286
+ /** Convenience: the argument list a given constructor expects */
287
+ type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;
288
+ declare class Pluggable {
289
+ protected isInitialized: boolean;
290
+ protected readonly shutdown: CoordinatedShutdown;
291
+ protected readonly startup: CoordinatedStartup;
292
+ private static readonly PLUGIN_INIT_TIMEOUT_MS;
293
+ constructor(shutdown: CoordinatedShutdown, startup: CoordinatedStartup);
294
+ protected init(): Promise<this>;
295
+ attach<Key extends string, Ctor extends PluginCtor>(this: this, key: Key, Plugin: Ctor, startupPhase: StartupPhase, ...args: PluginArgs<Ctor>): this & Record<Key, InstanceType<Ctor>>;
296
+ }
297
+
298
+ declare class Bot extends Plugin {
299
+ protected core: Core;
300
+ readonly logger: Logger;
301
+ private isInitialized;
302
+ private readonly _client;
303
+ private readonly interactions;
304
+ private readonly events;
305
+ private readonly commands;
306
+ private readonly emojiInjector;
307
+ constructor(core: Core);
308
+ init(): Promise<void>;
309
+ stop(): Promise<void>;
310
+ private login;
311
+ private logout;
312
+ get client(): Client;
313
+ }
314
+
315
+ declare class Seedcord extends Pluggable implements Core {
316
+ readonly config: Config;
317
+ private static isInstantiated;
318
+ readonly shutdown: CoordinatedShutdown;
319
+ readonly startup: CoordinatedStartup;
320
+ readonly hooks: HookController;
321
+ readonly bot: Bot;
322
+ private readonly healthCheck;
323
+ constructor(config: Config);
324
+ private registerStartupTasks;
325
+ start(): Promise<this>;
326
+ }
327
+ interface Seedcord extends Core {
328
+ }
329
+
330
+ type ValidInteractionTypes = ChatInputCommandInteraction | ButtonInteraction | ModalSubmitInteraction | AutocompleteInteraction | AnySelectMenuInteraction | ContextMenuCommandInteraction;
331
+ type ValidNonInteractionTypes = ClientEvents[Exclude<keyof ClientEvents, Events.InteractionCreate>];
332
+ type ValidEventTypes = ValidInteractionTypes | ValidNonInteractionTypes;
333
+ type Repliables = Exclude<ValidInteractionTypes, AutocompleteInteraction>;
334
+ type RepliableInteractionHandler = InteractionHandler<Repliables> | InteractionMiddleware<Repliables>;
335
+ interface Handler {
336
+ execute(): Promise<void>;
337
+ }
338
+ interface WithChecks {
339
+ runChecks(): Promise<void>;
340
+ }
341
+ interface HandlerWithChecks extends WithChecks, Handler {
342
+ }
343
+ declare abstract class BaseHandler<ValidEvent extends ValidEventTypes> implements Handler {
344
+ core: Core;
345
+ protected checkable: boolean;
346
+ protected break: boolean;
347
+ protected errored: boolean;
348
+ protected event: ValidEvent;
349
+ protected args: string[];
350
+ protected constructor(event: ValidEvent, core: Core, args?: string[]);
351
+ abstract execute(): Promise<void>;
352
+ hasChecks(): this is HandlerWithChecks;
353
+ hasErrors(): boolean;
354
+ setErrored(): void;
355
+ shouldBreak(): boolean;
356
+ getEvent(): ValidEvent;
357
+ /**
358
+ * Get the arguments passed from the customId
359
+ * For example, if customId is "accept:user123-guild456", args will be ["user123", "guild456"]
360
+ */
361
+ protected getArgs(): string[];
362
+ /**
363
+ * Get a specific argument by index
364
+ */
365
+ protected getArg(index: number): string | undefined;
366
+ }
367
+ /**
368
+ * All interactions with the bot including Handlers and what those hooks do or pass to other services should extend this class.
369
+ * This class implements ICheckable when the decorator Checkable is used on the class.
370
+ * @implements Handler
371
+ * @template Repliable - A type that extends one of the ValidEventTypes. Can add more types to the ValidEventTypes union type if needed.
372
+ */
373
+ declare abstract class InteractionHandler<Repliable extends Repliables> extends BaseHandler<Repliable> implements Handler {
374
+ constructor(event: Repliable, core: Core, args?: string[]);
375
+ }
376
+ declare abstract class InteractionMiddleware<Repliable extends Repliables> extends BaseHandler<Repliable> implements Handler {
377
+ constructor(event: Repliable, core: Core, args?: string[]);
378
+ }
379
+ /**
380
+ * Autocomplete interactions handler - separate from repliable interactions
381
+ */
382
+ declare abstract class AutocompleteHandler extends BaseHandler<AutocompleteInteraction> implements Handler {
383
+ protected readonly focused: AutocompleteFocusedOption;
384
+ constructor(event: AutocompleteInteraction, core: Core, args?: string[]);
385
+ }
386
+ /**
387
+ * All non-interaction events with the bot including Handlers and what those hooks do or pass to other services should extend this class.
388
+ * This class implements ICheckable when the decorator Checkable is used on the class.
389
+ * @implements Handler
390
+ * @template Repliable - A type that extends one of the ValidEventTypes. Can add more types to the ValidEventTypes union type if needed.
391
+ */
392
+ declare abstract class EventHandler<Repliable extends keyof ClientEvents> extends BaseHandler<ClientEvents[Repliable]> implements Handler {
393
+ constructor(event: ClientEvents[Repliable], core: Core, args?: string[]);
394
+ }
395
+ type HandlerConstructor = TypedConstructor<typeof InteractionHandler | typeof AutocompleteHandler>;
396
+ type MiddlewareConstructor = TypedConstructor<typeof InteractionMiddleware> & (new (event: Repliables, core: Core, args?: string[]) => InteractionMiddleware<Repliables>);
397
+ type AutocompleteHandlerConstructor = TypedConstructor<typeof AutocompleteHandler> & (new (event: AutocompleteInteraction, core: Core, args?: string[]) => AutocompleteHandler);
398
+ type EventHandlerConstructor = TypedConstructor<typeof EventHandler>;
399
+
400
+ type HandlerCtor = new (...args: any[]) => Handler;
401
+ declare function Checkable<TypeHandler extends HandlerCtor>(ctor: TypeHandler): TypeHandler;
402
+
403
+ interface CatchableOptions {
404
+ log?: boolean;
405
+ forceFollowup?: boolean;
406
+ }
407
+ declare function Catchable(options?: CatchableOptions): (_target: RepliableInteractionHandler, _propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<void>>) => void;
408
+
409
+ declare const BuilderTypes: {
410
+ command: typeof SlashCommandBuilder;
411
+ embed: typeof EmbedBuilder;
412
+ button: typeof ButtonBuilder;
413
+ menu_string: typeof StringSelectMenuBuilder;
414
+ menu_option_string: typeof StringSelectMenuOptionBuilder;
415
+ menu_user: typeof UserSelectMenuBuilder;
416
+ menu_channel: typeof ChannelSelectMenuBuilder;
417
+ menu_mentionable: typeof MentionableSelectMenuBuilder;
418
+ menu_role: typeof RoleSelectMenuBuilder;
419
+ modal: typeof ModalBuilder;
420
+ subcommand: typeof SlashCommandSubcommandBuilder;
421
+ group: typeof SlashCommandSubcommandGroupBuilder;
422
+ context_menu: typeof ContextMenuCommandBuilder;
423
+ };
424
+ declare const RowTypes: {
425
+ button: typeof ActionRowBuilder<ButtonBuilder>;
426
+ menu_string: typeof ActionRowBuilder<StringSelectMenuBuilder>;
427
+ menu_user: typeof ActionRowBuilder<UserSelectMenuBuilder>;
428
+ menu_channel: typeof ActionRowBuilder<ChannelSelectMenuBuilder>;
429
+ menu_mentionable: typeof ActionRowBuilder<MentionableSelectMenuBuilder>;
430
+ menu_role: typeof ActionRowBuilder<RoleSelectMenuBuilder>;
431
+ modal: typeof ActionRowBuilder<ModalActionRowComponentBuilder>;
432
+ };
433
+ declare const ModalTypes: {
434
+ text: typeof TextInputBuilder;
435
+ };
436
+ type BuilderType = keyof typeof BuilderTypes;
437
+ type InstantiatedBuilder<BuilderKey extends BuilderType> = InstanceType<(typeof BuilderTypes)[BuilderKey]>;
438
+ type ActionRowComponentType = keyof typeof RowTypes;
439
+ type InstantiatedActionRow<RowKey extends ActionRowComponentType> = InstanceType<(typeof RowTypes)[RowKey]>;
440
+ type ModalFieldTypes = keyof typeof ModalTypes;
441
+ type InstantiatedModalField<ModalKey extends ModalFieldTypes> = InstanceType<(typeof ModalTypes)[ModalKey]>;
442
+ declare abstract class BaseComponent<TComponent> {
443
+ private readonly _component;
444
+ protected constructor(ComponentClass: new () => TComponent);
445
+ /**
446
+ * @description Returns the configured component
447
+ * @note Do not use for mutating using setters.
448
+ * @see instance for mutating the component
449
+ * @usage `new SomeComponent().component`
450
+ */
451
+ abstract get component(): InstantiatedBuilder<BuilderType> | InstantiatedActionRow<ActionRowComponentType>;
452
+ /**
453
+ * @description Returns the instantiated component
454
+ * @note Use this for configuring the component using its instance setters.
455
+ * @usage `this.instance.someMethod()`
456
+ */
457
+ protected get instance(): TComponent;
458
+ }
459
+ declare abstract class BuilderComponent<BuilderKey extends BuilderType> extends BaseComponent<InstantiatedBuilder<BuilderKey>> {
460
+ protected constructor(type: BuilderKey);
461
+ get component(): InstantiatedBuilder<BuilderKey>;
462
+ buildCustomId(prefix: string, ...args: string[]): string;
463
+ }
464
+ declare abstract class RowComponent<RowKey extends ActionRowComponentType> extends BaseComponent<InstantiatedActionRow<RowKey>> {
465
+ protected constructor(type: RowKey);
466
+ get component(): InstantiatedActionRow<RowKey>;
467
+ }
468
+ declare abstract class ModalComponent<ModalKey extends ModalFieldTypes> extends BaseComponent<InstantiatedModalField<ModalKey>> {
469
+ protected constructor(type: ModalKey);
470
+ get component(): InstantiatedActionRow<'modal'>;
471
+ }
472
+ declare class BaseErrorEmbed extends BuilderComponent<'embed'> {
473
+ constructor();
474
+ }
475
+ declare abstract class CustomError extends Error {
476
+ message: string;
477
+ protected _emit: boolean;
478
+ readonly response: EmbedBuilder;
479
+ protected constructor(message: string);
480
+ get emit(): boolean;
481
+ }
482
+ type CustomErrorConstructor = new (message: string, ...args: any[]) => CustomError;
483
+
484
+ declare const CommandMetadataKey: unique symbol;
485
+ type CommandCtor = new (...args: any[]) => BuilderComponent<'command' | 'context_menu'>;
486
+ interface GlobalMeta {
487
+ scope: 'global';
488
+ }
489
+ interface GuildMeta {
490
+ scope: 'guild';
491
+ guilds: string[];
492
+ }
493
+ type CommandMeta = GlobalMeta | GuildMeta;
494
+ declare function RegisterCommand(scope: 'global'): (ctor: CommandCtor) => void;
495
+ declare function RegisterCommand(scope: 'guild', guilds: string[]): (ctor: CommandCtor) => void;
496
+
497
+ declare function EventCatchable(log?: boolean): (_target: EventHandler<keyof ClientEvents>, _prop: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<void>>) => void;
498
+
499
+ declare const EventMetadataKey: unique symbol;
500
+ declare function RegisterEvent<KeyofEvents extends keyof ClientEvents>(eventName: KeyofEvents): (constructor: ConstructorFunction) => void;
501
+
502
+ declare enum InteractionRoutes {
503
+ Slash = "interaction:slash",
504
+ Button = "interaction:button",
505
+ Modal = "interaction:modal",
506
+ StringMenu = "interaction:stringMenu",
507
+ UserMenu = "interaction:userMenu",
508
+ RoleMenu = "interaction:roleMenu",
509
+ ChannelMenu = "interaction:channelMenu",
510
+ MentionableMenu = "interaction:mentionableMenu",
511
+ MessageContextMenu = "interaction:messageContextMenu",
512
+ UserContextMenu = "interaction:userContextMenu",
513
+ Autocomplete = "interaction:autocomplete"
514
+ }
515
+ declare enum SelectMenuType {
516
+ String = "string",
517
+ User = "user",
518
+ Role = "role",
519
+ Channel = "channel",
520
+ Mentionable = "mentionable"
521
+ }
522
+ declare const InteractionMetadataKey: unique symbol;
523
+ /**
524
+ * Decorator for slash command routes. The route can be:
525
+ * - "profile"
526
+ * - "settings/edit"
527
+ * - "settings/advanced/edit"
528
+ *
529
+ * Pass an array of routes if the same handler should respond to multiple.
530
+ */
531
+ declare function SlashRoute(routeOrRoutes: string | string[]): (constructor: ConstructorFunction) => void;
532
+ /**
533
+ * Decorator for button routes. This should match a `customId` prefix.
534
+ * e.g., 'accept' or 'login'.
535
+ */
536
+ declare function ButtonRoute(routeOrRoutes: string | string[]): (constructor: ConstructorFunction) => void;
537
+ /**
538
+ * Decorator for modal routes. This should match a `customId` prefix.
539
+ */
540
+ declare function ModalRoute(routeOrRoutes: string | string[]): (constructor: ConstructorFunction) => void;
541
+ /**
542
+ * Decorator for context menu commands. This should match the command name.
543
+ * @param type - The type of context menu ('message' or 'user')
544
+ * @param routeOrRoutes - The command name(s) to route to this handler
545
+ */
546
+ declare function ContextMenuRoute(type: 'message' | 'user', routeOrRoutes: string | string[]): (constructor: ConstructorFunction) => void;
547
+ /**
548
+ * Decorator for autocomplete interactions. Supports routing by command and focused field.
549
+ * @param commandRoutes - Command name or subcommand paths
550
+ * @param focusedFields - Focused option names to handle
551
+ * @example @AutocompleteRoute('user', 'name') // Single command, single field
552
+ * @example @AutocompleteRoute(['user', 'profile'], ['name', 'bio']) // Multiple commands, multiple fields
553
+ */
554
+ declare function AutocompleteRoute(commandRoutes: string | string[], focusedFields: string | string[]): (constructor: ConstructorFunction) => void;
555
+ /**
556
+ * Decorator for select menu routes. This should match a `customId` prefix.
557
+ */
558
+ declare function SelectMenuRoute(type: SelectMenuType, routeOrRoutes: string | string[]): (constructor: ConstructorFunction) => void;
559
+
560
+ declare class HealthCheck extends Plugin {
561
+ private readonly core;
562
+ readonly logger: Logger;
563
+ private readonly port;
564
+ private readonly path;
565
+ private server?;
566
+ constructor(core: Core);
567
+ /**
568
+ * Starts the health check server.
569
+ * @returns Promise that resolves when the server is listening
570
+ */
571
+ init(): Promise<void>;
572
+ stop(): Promise<void>;
573
+ }
574
+
575
+ interface CooldownOptions {
576
+ cooldown?: number;
577
+ err?: new (msg: string, ...args: any[]) => Error;
578
+ message?: string;
579
+ }
580
+ /**
581
+ * Lightweight utility for per-key cooldowns
582
+ */
583
+ declare class CooldownManager {
584
+ private readonly window;
585
+ private readonly Err;
586
+ private readonly msg;
587
+ private readonly map;
588
+ constructor(opts?: CooldownOptions);
589
+ /** Record usage without any checks. */
590
+ set(key: string): void;
591
+ /**
592
+ * Verify cooldown for `key`.\
593
+ * If active → throws the custom error.\
594
+ * If not active → updates timestamp and returns void.
595
+ */
596
+ check(key: string): void;
597
+ /** Returns true if the key is still cooling down (does not update timestamp). */
598
+ isActive(key: string): boolean;
599
+ /** Remove a key from the map (useful for manual resets). */
600
+ clear(key: string): void;
601
+ }
602
+
603
+ interface Services {
604
+ }
605
+ type ServiceKeys = keyof Services;
606
+
607
+ interface MongoOptions {
608
+ servicesDir: string;
609
+ uri: string;
610
+ dbName: string;
611
+ }
612
+ declare class Mongo extends Plugin {
613
+ readonly core: Core;
614
+ private readonly options;
615
+ readonly logger: Logger;
616
+ private isInitialised;
617
+ private readonly uri;
618
+ /**
619
+ * Map of all loaded services.
620
+ * Keys come from `@DatabaseService('key')`
621
+ */
622
+ readonly services: Services;
623
+ constructor(core: Core, options: MongoOptions);
624
+ init(): Promise<void>;
625
+ stop(): Promise<void>;
626
+ private connect;
627
+ private disconnect;
628
+ private loadServices;
629
+ private isServiceClass;
630
+ _register<SKey extends keyof Services>(key: SKey, instance: Services[SKey]): void;
631
+ }
632
+
633
+ declare abstract class BaseService<Doc extends IDocument = IDocument> {
634
+ protected readonly db: Mongo;
635
+ protected readonly core: Core;
636
+ readonly model: mongoose.Model<Doc>;
637
+ constructor(db: Mongo, core: Core);
638
+ }
639
+ type BaseServiceConstructor = TypedConstructor<typeof BaseService>;
640
+
641
+ declare function DBCatchable<TypeReturn>(errorMessage: string): (_target: unknown, _propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<TypeReturn>>) => void;
642
+
643
+ declare const ModelMetadataKey: unique symbol;
644
+ declare function DatabaseModel<TService extends ServiceKeys>(collection: TService): <SchemaObj extends Record<KeyOfSchema, mongoose.Schema>, KeyOfSchema extends keyof SchemaObj & (string | symbol)>(target: SchemaObj, propertyKey: KeyOfSchema) => void;
645
+
646
+ declare const ServiceMetadataKey: unique symbol;
647
+ declare function DatabaseService<TService extends ServiceKeys>(key: TService): <DatabaseCtor extends ConstructorFunction & {
648
+ prototype: BaseService;
649
+ }>(ctor: DatabaseCtor) => void;
650
+
651
+ /**
652
+ * Global configuration variables for the application. These ENV variables are reserved for the framework.
653
+ *
654
+ * Set them in your `.env` file to override the default.
655
+ */
656
+ declare class Globals extends Envapter {
657
+ static readonly botToken: string;
658
+ static readonly healthCheckPort: number;
659
+ static readonly healthCheckPath: string;
660
+ static readonly shutdownIsEnabled: boolean;
661
+ static readonly unknownExceptionWebhookUrl: string;
662
+ /**
663
+ * The default color of the bot's embeds. Can simply override by `Globals.botColor =`
664
+ */
665
+ static botColor: ColorResolvable;
666
+ }
667
+
668
+ /**
669
+ * Rounds a number to a specified number of decimal places.
670
+ *
671
+ * @param num - The number to be rounded.
672
+ * @param decimalPlaces - The number of decimal places to round to.
673
+ * @returns The rounded number.
674
+ */
675
+ declare function numberFixer(num: number, decimalPlaces: number): number;
676
+ /**
677
+ * Takes two numbers and returns the percentage of the first number in the second number with two decimal places.
678
+ *
679
+ * @param num1 The first number.
680
+ * @param num2 The second number.
681
+ *
682
+ * @returns The percentage of the first number in the second number with two decimal places.
683
+ */
684
+ declare function percentage(num1: number, num2: number): number;
685
+ /**
686
+ * Generates an ASCII table from the provided data.
687
+ *
688
+ * @param data - The data to be displayed in the table.
689
+ * @returns The generated ASCII table as a string.
690
+ */
691
+ declare function generateAsciiTable(data: string[][]): string;
692
+ /**
693
+ * Returns the word with it's first letter capitalized and the rest in lowercase.
694
+ * @param word - The word to be formatted.
695
+ * @returns The formatted word.
696
+ */
697
+ declare function formatWord(word: string): string;
698
+ /**
699
+ * Function takes an array of strings or numbers and returns the number of characters in the longest string/number
700
+ * @param arr - The array of strings or numbers
701
+ */
702
+ declare function longestStringLength(arr: (string | number)[]): number;
703
+ /**
704
+ * Return current time in seconds
705
+ */
706
+ declare function currentTime(): number;
707
+ /**
708
+ * Returns the ordinal suffix for a given number.
709
+ *
710
+ * @param n - The number to get the ordinal for
711
+ * @returns The number with its ordinal suffix
712
+ *
713
+ * @example
714
+ * ordinal(1); // "1st"
715
+ * ordinal(22); // "22nd"
716
+ * ordinal(13); // "13th"
717
+ */
718
+ declare function ordinal(n: number): string;
719
+ /**
720
+ * Calculates the difference between two numbers and formats it as a string with a '+' prefix for positive differences.
721
+ *
722
+ * @param numBefore - The initial number value
723
+ * @param numAfter - The final number value
724
+ * @returns A string representing the difference, with a '+' sign for positive differences
725
+ *
726
+ * @example
727
+ * // Returns "+5"
728
+ * prettyDifference(10, 15);
729
+ *
730
+ * @example
731
+ * // Returns "-3"
732
+ * prettyDifference(10, 7);
733
+ */
734
+ declare function prettyDifference(numBefore: number, numAfter: number): string;
735
+ /**
736
+ * Determines if a directory entry is a TypeScript or JavaScript file.
737
+ *
738
+ * @param entry - The directory entry to check.
739
+ * @returns True if the entry is a file ending with .ts or .js.
740
+ */
741
+ declare function isTsOrJsFile(entry: fs.Dirent): boolean;
742
+ /**
743
+ * Recursively traverses through a directory, importing all .ts and .js files and applying a callback to each import.
744
+ *
745
+ * @param dir - The directory path to traverse.
746
+ * @param callback - A function that will be called for each imported module. It receives the full file path, the file's relative path, and the imported module as arguments.
747
+ * @returns A Promise that resolves when the traversal is complete.
748
+ */
749
+ declare function traverseDirectory(dir: string, callback: (fullPath: string, relativePath: string, imported: Record<string, unknown>) => Promise<void> | void): Promise<void>;
750
+ /**
751
+ * Throws a custom error with a formatted message and optional UUID.
752
+ *
753
+ * Wraps an unknown error in a {@link CustomError} subclass. If the error class
754
+ * is {@link DatabaseError}, a UUID is generated and passed to the constructor.
755
+ *
756
+ * @typeParam T - A constructor for a {@link CustomError} subclass
757
+ * @param error - The original error or value
758
+ * @param message - Custom message to include
759
+ * @param CustomError - Error class to instantiate and throw
760
+ * @throws Instance of the provided {@link CustomError} subclass
761
+ *
762
+ * @example
763
+ * try {
764
+ * // risky code
765
+ * } catch (e) {
766
+ * throwCustomError(e, "Something went wrong", MyCustomError);
767
+ * }
768
+ */
769
+ declare function throwCustomError<Ctor extends CustomErrorConstructor>(error: unknown, message: string, customError: Ctor): never;
770
+ /**
771
+ * Converts a camelCase or snake_case string into a human-readable format.
772
+ *
773
+ * This function inserts spaces before capital letters that follow lowercase
774
+ * letters, replaces underscores with spaces, and trims any extra whitespace.
775
+ *
776
+ * @param key - The string to be converted to a human-readable format
777
+ * @returns A prettified string with proper spacing
778
+ *
779
+ * @example
780
+ * prettify("camelCaseString") // returns "camel Case String"
781
+ * @example
782
+ * prettify("snake_case_string") // returns "snake case string"
783
+ */
784
+ declare function prettify(key: string): string;
785
+ /**
786
+ * Shuffles an array using the Fisher-Yates algorithm.
787
+ * This function creates a new array with the same elements in a random order,
788
+ * without modifying the original array.
789
+ *
790
+ * @template TArray - The type of elements in the array
791
+ * @param {TArray[]} items - The array to shuffle
792
+ * @returns {TArray[]} A new array with the same elements in a random order
793
+ *
794
+ * @example
795
+ * const numbers = [1, 2, 3, 4, 5];
796
+ * const shuffled = fyShuffle(numbers);
797
+ * // shuffled might be [3, 1, 5, 2, 4]
798
+ * // numbers is still [1, 2, 3, 4, 5]
799
+ */
800
+ declare function fyShuffle<TArray>(items: TArray[]): TArray[];
801
+
802
+ declare class HookEmitter {
803
+ private readonly emitter;
804
+ on<KeyOfHooks extends HookKeys>(event: KeyOfHooks, listener: (data: AllHooks[KeyOfHooks]) => void): this;
805
+ once<KeyOfHooks extends HookKeys>(event: KeyOfHooks, listener: (data: AllHooks[KeyOfHooks]) => void): this;
806
+ emit<KeyOfHooks extends HookKeys>(event: KeyOfHooks, data: AllHooks[KeyOfHooks]): boolean;
807
+ }
808
+
809
+ declare const HookMetadataKey: unique symbol;
810
+ declare function RegisterHook<THook extends HookKeys>(hook: THook): (constructor: ConstructorFunction) => void;
811
+
812
+ declare abstract class HookHandler<KeyOfHooks extends HookKeys> {
813
+ protected readonly data: AllHooks[KeyOfHooks];
814
+ protected readonly core: Core;
815
+ constructor(data: AllHooks[KeyOfHooks], core: Core);
816
+ abstract execute(): Promise<void>;
817
+ }
818
+
819
+ declare abstract class WebhookLog<KeyofHooks extends HookKeys> extends HookHandler<KeyofHooks> {
820
+ abstract webhook: WebhookClient;
821
+ constructor(data: AllHooks[KeyofHooks], core: Core);
822
+ }
823
+
824
+ export { type AllHooks, AutocompleteHandler, type AutocompleteHandlerConstructor, AutocompleteRoute, BaseErrorEmbed, BaseService, type BaseServiceConstructor, Bot, BuilderComponent, ButtonRoute, Catchable, Checkable, type CommandMeta, CommandMetadataKey, type Config, ContextMenuRoute, CooldownManager, type CooldownOptions, CoordinatedShutdown, CoordinatedStartup, type Core, CustomError, type CustomErrorConstructor, DBCatchable, DatabaseModel, DatabaseService, type DefaultHooks, EventCatchable, EventHandler, type EventHandlerConstructor, EventMetadataKey, Globals, type Handler, type HandlerConstructor, HealthCheck, HookController, HookEmitter, HookHandler, type HookKeys, HookMetadataKey, type HookParams, type Hooks, type Initializeable, InteractionHandler, InteractionMetadataKey, InteractionMiddleware, InteractionRoutes, Logger, type MiddlewareConstructor, ModalComponent, ModalRoute, ModelMetadataKey, Mongo, Pluggable, Plugin, type PluginArgs, type PluginCtor, RegisterCommand, RegisterEvent, RegisterHook, type RepliableInteractionHandler, type Repliables, RowComponent, Seedcord, SelectMenuRoute, SelectMenuType, type ServiceKeys, ServiceMetadataKey, type Services, ShutdownPhase, type ShutdownTask, SlashRoute, StartupPhase, type StartupTask, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionTypes, WebhookLog, type WithChecks, currentTime, formatWord, fyShuffle, generateAsciiTable, isTsOrJsFile, longestStringLength, numberFixer, ordinal, percentage, prettify, prettyDifference, throwCustomError, traverseDirectory };