seedcord 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require('reflect-metadata');
4
4
  var services = require('@seedcord/services');
5
- var chalk4 = require('chalk');
5
+ var chalk3 = require('chalk');
6
6
  var discord_js = require('discord.js');
7
7
  var envapt = require('envapt');
8
8
  var utils = require('@seedcord/utils');
@@ -29,74 +29,26 @@ function _interopNamespace(e) {
29
29
  return Object.freeze(n);
30
30
  }
31
31
 
32
- var chalk4__default = /*#__PURE__*/_interopDefault(chalk4);
32
+ var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
33
33
  var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
34
34
 
35
35
  var __defProp = Object.defineProperty;
36
36
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
37
- var Plugin = class {
38
- static {
39
- __name(this, "Plugin");
40
- }
41
- pluggable;
42
- constructor(pluggable) {
43
- this.pluggable = pluggable;
44
- }
45
- };
46
- var Pluggable = class _Pluggable {
47
- static {
48
- __name(this, "Pluggable");
49
- }
50
- isInitialized = false;
51
- shutdown;
52
- startup;
53
- static PLUGIN_INIT_TIMEOUT_MS = 15e3;
54
- constructor(shutdown, startup) {
55
- this.shutdown = shutdown;
56
- this.startup = startup;
57
- }
58
- async init() {
59
- if (this.isInitialized) return this;
60
- await this.startup.run();
61
- this.isInitialized = true;
62
- return this;
63
- }
64
- /**
65
- * Attaches a plugin to this instance
66
- *
67
- * Plugins provide external functionality and are initialized during the specified startup phase.
68
- * The plugin instance becomes available as a property in `core` wherever it's available.
69
- *
70
- * Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
71
- *
72
- * @typeParam Key - The property name for accessing the plugin
73
- * @typeParam Ctor - The plugin constructor type
74
- * @param key - Property name to access the plugin instance
75
- * @param Plugin - Plugin constructor class
76
- * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
77
- * @param args - Additional arguments to pass to the plugin constructor
78
- * @returns This instance with the plugin attached as a typed property
79
- * @throws An {@link Error} When called after initialization or if key already exists
80
- * @example
81
- * ```typescript
82
- * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
83
- * ```
84
- */
85
- attach(key, Plugin2, startupPhase, ...args) {
86
- if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
87
- if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
88
- const instance = new Plugin2(this, ...args);
89
- const entry = {
90
- [key]: instance
37
+
38
+ // src/bot/decorators/Command.ts
39
+ var CommandMetadataKey = Symbol("command:metadata");
40
+ function RegisterCommand(scope, guilds = []) {
41
+ return (ctor) => {
42
+ const meta = scope === "global" ? {
43
+ scope
44
+ } : {
45
+ scope,
46
+ guilds
91
47
  };
92
- this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
93
- instance.logger.info(chalk4__default.default.bold("Initializing"));
94
- await instance.init();
95
- instance.logger.info(chalk4__default.default.bold("Initialized"));
96
- }, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
97
- return Object.assign(this, entry);
98
- }
99
- };
48
+ Reflect.defineMetadata(CommandMetadataKey, meta, ctor);
49
+ };
50
+ }
51
+ __name(RegisterCommand, "RegisterCommand");
100
52
  function parseEnvColor(raw, fallback) {
101
53
  if (!raw) return fallback;
102
54
  const toHex = /* @__PURE__ */ __name((n) => `#${n.toString(16).padStart(6, "0")}`, "toHex");
@@ -134,6 +86,7 @@ var BuilderTypes = {
134
86
  modal: discord_js.ModalBuilder,
135
87
  label: discord_js.LabelBuilder,
136
88
  text_input: discord_js.TextInputBuilder,
89
+ file_upload: discord_js.FileUploadBuilder,
137
90
  // Action Row Components
138
91
  button: discord_js.ButtonBuilder,
139
92
  menu_string: discord_js.StringSelectMenuBuilder,
@@ -273,21 +226,6 @@ var CustomError = class extends Error {
273
226
  }
274
227
  };
275
228
 
276
- // src/bot/decorators/Command.ts
277
- var CommandMetadataKey = Symbol("command:metadata");
278
- function RegisterCommand(scope, guilds = []) {
279
- return (ctor) => {
280
- const meta = scope === "global" ? {
281
- scope
282
- } : {
283
- scope,
284
- guilds
285
- };
286
- Reflect.defineMetadata(CommandMetadataKey, meta, ctor);
287
- };
288
- }
289
- __name(RegisterCommand, "RegisterCommand");
290
-
291
229
  // src/bot/controllers/CommandRegistry.ts
292
230
  var CommandRegistry = class {
293
231
  static {
@@ -304,9 +242,9 @@ var CommandRegistry = class {
304
242
  async init() {
305
243
  if (this.isInitialised) return;
306
244
  this.isInitialised = true;
307
- this.logger.info(chalk4__default.default.bold(this.core.config.bot.commands.path));
245
+ this.logger.info(chalk3__default.default.bold(this.core.config.bot.commands.path));
308
246
  await this.loadCommands(this.core.config.bot.commands.path);
309
- this.logger.info(`${chalk4__default.default.bold.green("Loaded")}: ${chalk4__default.default.magenta.bold(this.globalCommands.length)} global, ${chalk4__default.default.magenta.bold(this.guildCommands.size)} guild groups`);
247
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(this.globalCommands.length)} global, ${chalk3__default.default.magenta.bold(this.guildCommands.size)} guild groups`);
310
248
  }
311
249
  async loadCommands(dir) {
312
250
  await utils.traverseDirectory(dir, (_full, rel, mod) => {
@@ -325,24 +263,24 @@ var CommandRegistry = class {
325
263
  const commandType = comp instanceof discord_js.SlashCommandBuilder ? "slash command" : "context menu command";
326
264
  if (meta.scope === "global") {
327
265
  this.globalCommands.push(comp);
328
- this.logger.info(`${chalk4__default.default.italic("Registered")} ${chalk4__default.default.bold.yellow(ctor.name)} from ${chalk4__default.default.gray(rel)}`);
329
- this.logger.info(` \u2192 Global ${commandType}: ${chalk4__default.default.bold.cyan(comp.name)}`);
266
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(ctor.name)} from ${chalk3__default.default.gray(rel)}`);
267
+ this.logger.info(`\u2192 Global ${commandType}: ${chalk3__default.default.bold.cyan(comp.name)}`);
330
268
  } else {
331
269
  for (const g of meta.guilds) {
332
270
  const arr = this.guildCommands.get(g) ?? [];
333
271
  arr.push(comp);
334
272
  this.guildCommands.set(g, arr);
335
273
  }
336
- this.logger.info(`${chalk4__default.default.italic("Registered")} ${chalk4__default.default.bold.yellow(ctor.name)} from ${chalk4__default.default.gray(rel)}`);
337
- this.logger.info(` \u2192 Guild ${commandType}: ${chalk4__default.default.bold.cyan(comp.name)} for ${chalk4__default.default.magenta.bold(meta.guilds.length)} guild(s)`);
274
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(ctor.name)} from ${chalk3__default.default.gray(rel)}`);
275
+ this.logger.info(`\u2192 Guild ${commandType}: ${chalk3__default.default.bold.cyan(comp.name)} for ${chalk3__default.default.magenta.bold(meta.guilds.length)} guild(s)`);
338
276
  }
339
277
  }
340
278
  async setCommands() {
341
279
  if (this.globalCommands.length > 0) {
342
280
  await this.core.bot.client.application?.commands.set(this.globalCommands);
343
281
  const tag = this.globalCommands.length === 1 ? "command" : "commands";
344
- this.logger.info(`${chalk4__default.default.bold.green("Configured")} ${chalk4__default.default.magenta.bold(this.globalCommands.length)} global ${tag}`);
345
- this.logger.info(` \u2192 ${this.globalCommands.map((command) => chalk4__default.default.bold.cyan(command.name)).join(", ")}`);
282
+ this.logger.info(`${chalk3__default.default.bold.green("Configured")} ${chalk3__default.default.magenta.bold(this.globalCommands.length)} global ${tag}`);
283
+ this.logger.info(`\u2192 ${this.globalCommands.map((command) => chalk3__default.default.bold.cyan(command.name)).join(", ")}`);
346
284
  }
347
285
  for (const [guildId, commands] of this.guildCommands.entries()) {
348
286
  const guild = this.core.bot.client.guilds.cache.get(guildId);
@@ -352,12 +290,62 @@ var CommandRegistry = class {
352
290
  }
353
291
  await guild.commands.set(commands);
354
292
  const tag = commands.length === 1 ? "command" : "commands";
355
- this.logger.info(`${chalk4__default.default.bold.green("Configured")} ${chalk4__default.default.magenta.bold(commands.length)} ${tag} for guild ${chalk4__default.default.bold.yellow(guild.name)}`);
356
- this.logger.info(` \u2192 ${commands.map((command) => chalk4__default.default.bold.cyan(command.name)).join(", ")}`);
293
+ this.logger.info(`${chalk3__default.default.bold.green("Configured")} ${chalk3__default.default.magenta.bold(commands.length)} ${tag} for guild ${chalk3__default.default.bold.yellow(guild.name)}`);
294
+ this.logger.info(` \u2192 ${commands.map((command) => chalk3__default.default.bold.cyan(command.name)).join(", ")}`);
357
295
  }
358
296
  }
359
297
  };
360
298
 
299
+ // src/bot/decorators/Events.ts
300
+ var EventMetadataKey = Symbol("event:metadata");
301
+ function RegisterEvent(events, options) {
302
+ return function(constructor) {
303
+ const saved = Reflect.getMetadata(EventMetadataKey, constructor);
304
+ const existing = saved ?? [];
305
+ const toStore = Array.isArray(events) ? events : [
306
+ events
307
+ ];
308
+ const frequency = options?.frequency ?? "on";
309
+ const entries = toStore.map((event) => ({
310
+ event,
311
+ frequency
312
+ }));
313
+ Reflect.defineMetadata(EventMetadataKey, [
314
+ ...existing,
315
+ ...entries
316
+ ], constructor);
317
+ };
318
+ }
319
+ __name(RegisterEvent, "RegisterEvent");
320
+
321
+ // src/bot/decorators/Middlewares.ts
322
+ var MiddlewareType = /* @__PURE__ */ (function(MiddlewareType2) {
323
+ MiddlewareType2["Interaction"] = "middleware:interaction";
324
+ MiddlewareType2["Event"] = "middleware:event";
325
+ return MiddlewareType2;
326
+ })({});
327
+ var MiddlewareMetadataKey = Symbol("middleware:metadata");
328
+ function Middleware(type, priority = 0, options = {}) {
329
+ return (ctor) => {
330
+ const normalizedPriority = Number(priority);
331
+ if (!Number.isFinite(normalizedPriority)) {
332
+ throw new TypeError("Middleware priority must be a finite number");
333
+ }
334
+ if (type === "middleware:interaction" && options.events?.length) {
335
+ throw new Error("Interaction middleware cannot specify event filters");
336
+ }
337
+ const metadata = {
338
+ priority: normalizedPriority,
339
+ type,
340
+ ...options.events ? {
341
+ events: options.events
342
+ } : {}
343
+ };
344
+ Reflect.defineMetadata(MiddlewareMetadataKey, metadata, ctor);
345
+ };
346
+ }
347
+ __name(Middleware, "Middleware");
348
+
361
349
  // src/interfaces/Handler.ts
362
350
  var BaseHandler = class {
363
351
  static {
@@ -457,51 +445,6 @@ function areRoutes(routes) {
457
445
  }
458
446
  __name(areRoutes, "areRoutes");
459
447
 
460
- // src/bot/decorators/Events.ts
461
- var EventMetadataKey = Symbol("event:metadata");
462
- function RegisterEvent(events) {
463
- return function(constructor) {
464
- const saved = Reflect.getMetadata(EventMetadataKey, constructor);
465
- const existing = areRoutes(saved) ? saved : [];
466
- const toStore = Array.isArray(events) ? events : [
467
- events
468
- ];
469
- Reflect.defineMetadata(EventMetadataKey, [
470
- ...existing,
471
- ...toStore
472
- ], constructor);
473
- };
474
- }
475
- __name(RegisterEvent, "RegisterEvent");
476
-
477
- // src/bot/decorators/Middlewares.ts
478
- var MiddlewareType = /* @__PURE__ */ (function(MiddlewareType2) {
479
- MiddlewareType2["Interaction"] = "middleware:interaction";
480
- MiddlewareType2["Event"] = "middleware:event";
481
- return MiddlewareType2;
482
- })({});
483
- var MiddlewareMetadataKey = Symbol("middleware:metadata");
484
- function Middleware(type, priority = 0, options = {}) {
485
- return (ctor) => {
486
- const normalizedPriority = Number(priority);
487
- if (!Number.isFinite(normalizedPriority)) {
488
- throw new TypeError("Middleware priority must be a finite number");
489
- }
490
- if (type === "middleware:interaction" && options.events?.length) {
491
- throw new Error("Interaction middleware cannot specify event filters");
492
- }
493
- const metadata = {
494
- priority: normalizedPriority,
495
- type,
496
- ...options.events ? {
497
- events: options.events
498
- } : {}
499
- };
500
- Reflect.defineMetadata(MiddlewareMetadataKey, metadata, ctor);
501
- };
502
- }
503
- __name(Middleware, "Middleware");
504
-
505
448
  // src/bot/controllers/EventController.ts
506
449
  var EventController = class {
507
450
  static {
@@ -521,27 +464,27 @@ var EventController = class {
521
464
  }
522
465
  this.isInitialized = true;
523
466
  const handlersDir = this.core.config.bot.events.path;
524
- this.logger.info(chalk4__default.default.bold(handlersDir));
467
+ this.logger.info(chalk3__default.default.bold(handlersDir));
525
468
  const middlewareDir = this.core.config.bot.events.middlewares;
526
469
  if (middlewareDir) {
527
- this.logger.info(`${chalk4__default.default.bold(middlewareDir)} ${chalk4__default.default.gray("(middlewares)")}`);
470
+ this.logger.info(`${chalk3__default.default.bold(middlewareDir)} ${chalk3__default.default.gray("(middlewares)")}`);
528
471
  await this.loadMiddlewares(middlewareDir);
529
472
  }
530
473
  await this.loadHandlers(handlersDir);
531
474
  this.attachToClient();
532
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.middlewares.length)} middlewares`);
475
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.middlewares.length)} middlewares`);
533
476
  const loadedEventsArray = [];
534
477
  this.eventMap.forEach((handlers, eventName) => {
535
- loadedEventsArray.push(`${chalk4__default.default.magenta.bold(handlers.length)} ${eventName}`);
478
+ loadedEventsArray.push(`${chalk3__default.default.magenta.bold(handlers.length)} ${eventName}`);
536
479
  });
537
- this.logger.info(`${chalk4__default.default.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
480
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
538
481
  }
539
482
  async loadHandlers(dir) {
540
483
  await utils.traverseDirectory(dir, (_fullPath, relativePath, imported) => {
541
484
  for (const val of Object.values(imported)) {
542
485
  if (!this.isEventHandlerClass(val)) continue;
543
486
  this.registerHandler(val);
544
- this.logger.info(`${chalk4__default.default.italic("Registered")} ${chalk4__default.default.bold.yellow(val.name)} from ${chalk4__default.default.gray(relativePath)}`);
487
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(val.name)} from ${chalk3__default.default.gray(relativePath)}`);
545
488
  }
546
489
  }, this.logger);
547
490
  }
@@ -566,7 +509,7 @@ var EventController = class {
566
509
  } : {}
567
510
  });
568
511
  this.middlewares.sort((a, b) => a.priority - b.priority);
569
- this.logger.info(`${chalk4__default.default.italic("Registered event middleware")} ${chalk4__default.default.bold.yellow(middlewareCtor.name)} ${chalk4__default.default.gray(`(priority ${metadata.priority})`)} from ${chalk4__default.default.gray(relativePath)}`);
512
+ this.logger.info(`${chalk3__default.default.italic("Registered event middleware")} ${chalk3__default.default.bold.yellow(middlewareCtor.name)} ${chalk3__default.default.gray(`(priority ${metadata.priority})`)} from ${chalk3__default.default.gray(relativePath)}`);
570
513
  }
571
514
  async runMiddlewares(eventName, args) {
572
515
  for (const { ctor, events } of this.middlewares) {
@@ -594,6 +537,21 @@ var EventController = class {
594
537
  }
595
538
  registerHandler(handlerClass) {
596
539
  const raw = Reflect.getMetadata(EventMetadataKey, handlerClass);
540
+ if (Array.isArray(raw)) {
541
+ for (const entry of raw) {
542
+ const key = entry.event;
543
+ let handlers = this.eventMap.get(key);
544
+ if (!handlers) {
545
+ handlers = [];
546
+ this.eventMap.set(key, handlers);
547
+ }
548
+ handlers.push({
549
+ ctor: handlerClass,
550
+ frequency: entry.frequency
551
+ });
552
+ }
553
+ return;
554
+ }
597
555
  const names = areRoutes(raw) ? raw : typeof raw === "string" ? [
598
556
  raw
599
557
  ] : [];
@@ -605,38 +563,47 @@ var EventController = class {
605
563
  handlers = [];
606
564
  this.eventMap.set(key, handlers);
607
565
  }
608
- handlers.push(handlerClass);
566
+ handlers.push({
567
+ ctor: handlerClass,
568
+ frequency: "on"
569
+ });
609
570
  }
610
571
  }
611
572
  attachToClient() {
612
- for (const [eventName] of this.eventMap) {
613
- this.logger.debug(`Attaching ${chalk4__default.default.bold.green(eventName)} to ${chalk4__default.default.bold.yellow(this.core.bot.client.user?.username)}`);
614
- this.core.bot.client.on(eventName, (...args) => {
615
- void (async () => {
616
- await this.processEvent(eventName, args);
617
- })();
618
- });
573
+ for (const [eventName, handlerEntries] of this.eventMap) {
574
+ this.logger.debug(`Attaching ${chalk3__default.default.bold.green(eventName)} to ${chalk3__default.default.bold.yellow(this.core.bot.client.user?.username)}`);
575
+ for (const entry of handlerEntries) {
576
+ const register = entry.frequency === "once" ? this.core.bot.client.once.bind(this.core.bot.client) : this.core.bot.client.on.bind(this.core.bot.client);
577
+ register(eventName, (...args) => {
578
+ void (async () => {
579
+ await this.processEvent(eventName, args, entry.ctor);
580
+ })();
581
+ });
582
+ }
619
583
  }
620
584
  }
621
- async processEvent(eventName, args) {
585
+ async processEvent(eventName, args, specificHandler) {
622
586
  const shouldContinue = await this.runMiddlewares(eventName, args);
623
587
  if (!shouldContinue) return;
624
- const handlerCtors = this.eventMap.get(eventName);
625
- if (!handlerCtors || handlerCtors.length === 0) return;
626
- for (const HandlerCtor of handlerCtors) {
627
- try {
628
- this.logger.debug(`Processing ${chalk4__default.default.bold.green(eventName)} with ${chalk4__default.default.gray(HandlerCtor.name)}`);
629
- const handler = new HandlerCtor(args, this.core);
630
- if (handler.hasChecks()) {
631
- await handler.runChecks();
632
- }
633
- if (handler.shouldBreak()) return;
634
- if (!handler.hasErrors()) {
635
- await handler.execute();
636
- }
637
- } catch (err) {
638
- this.logger.error(`Error in event ${String(eventName)} handler ${HandlerCtor.name}:`, err);
639
- }
588
+ if (specificHandler) {
589
+ await this.processHandler(eventName, specificHandler, args);
590
+ return;
591
+ }
592
+ const handlerEntries = this.eventMap.get(eventName);
593
+ if (!handlerEntries || handlerEntries.length === 0) return;
594
+ for (const entry of handlerEntries) {
595
+ await this.processHandler(eventName, entry.ctor, args);
596
+ }
597
+ }
598
+ async processHandler(eventName, ctor, args) {
599
+ try {
600
+ this.logger.debug(`Processing ${chalk3__default.default.bold.green(eventName)} with ${chalk3__default.default.gray(ctor.name)}`);
601
+ const handler = new ctor(args, this.core);
602
+ if (handler.hasChecks()) await handler.runChecks();
603
+ if (handler.shouldBreak()) return;
604
+ if (!handler.hasErrors()) await handler.execute();
605
+ } catch (err) {
606
+ this.logger.error(`Error in event ${String(eventName)} handler ${ctor.name}:`, err);
640
607
  }
641
608
  }
642
609
  };
@@ -733,6 +700,26 @@ function storeMetadata(symbol, routes, constructor) {
733
700
  Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
734
701
  }
735
702
  __name(storeMetadata, "storeMetadata");
703
+ function buildSlashRoute(arg1, arg2, arg3) {
704
+ let command;
705
+ let sub;
706
+ let group;
707
+ if (typeof arg1 === "string") {
708
+ command = arg1;
709
+ sub = arg2;
710
+ group = arg3;
711
+ } else if (arg1 instanceof discord_js.ChatInputCommandInteraction || arg1 instanceof discord_js.AutocompleteInteraction) {
712
+ command = arg1.commandName;
713
+ group = arg1.options.getSubcommandGroup(false) ?? void 0;
714
+ sub = arg1.options.getSubcommand(false) ?? void 0;
715
+ } else {
716
+ throw new TypeError("Invalid argument passed to buildSlashRoute");
717
+ }
718
+ if (sub && group) return `${command}/${group}/${sub}`;
719
+ if (sub) return `${command}/${sub}`;
720
+ return command;
721
+ }
722
+ __name(buildSlashRoute, "buildSlashRoute");
736
723
 
737
724
  // src/bot/errors/Database.ts
738
725
  var DatabaseError = class extends CustomError {
@@ -898,26 +885,6 @@ _ts_decorate3([
898
885
  _ts_metadata3("design:paramtypes", []),
899
886
  _ts_metadata3("design:returntype", Promise)
900
887
  ], UnhandledEvent.prototype, "execute", null);
901
- function buildSlashRoute(arg1, arg2, arg3) {
902
- let command;
903
- let sub;
904
- let group;
905
- if (typeof arg1 === "string") {
906
- command = arg1;
907
- sub = arg2;
908
- group = arg3;
909
- } else if (arg1 instanceof discord_js.ChatInputCommandInteraction || arg1 instanceof discord_js.AutocompleteInteraction) {
910
- command = arg1.commandName;
911
- group = arg1.options.getSubcommandGroup(false) ?? void 0;
912
- sub = arg1.options.getSubcommand(false) ?? void 0;
913
- } else {
914
- throw new TypeError("Invalid argument passed to buildSlashRoute");
915
- }
916
- if (sub && group) return `${command}/${group}/${sub}`;
917
- if (sub) return `${command}/${sub}`;
918
- return command;
919
- }
920
- __name(buildSlashRoute, "buildSlashRoute");
921
888
 
922
889
  // src/bot/controllers/InteractionController.ts
923
890
  var InteractionController = class {
@@ -951,34 +918,34 @@ var InteractionController = class {
951
918
  if (this.isInitialized) return;
952
919
  this.isInitialized = true;
953
920
  const handlersDir = this.core.config.bot.interactions.path;
954
- this.logger.info(chalk4__default.default.bold(handlersDir));
921
+ this.logger.info(chalk3__default.default.bold(handlersDir));
955
922
  const middlewareDir = this.core.config.bot.interactions.middlewares;
956
923
  if (middlewareDir) {
957
- this.logger.info(`${chalk4__default.default.bold(middlewareDir)} ${chalk4__default.default.gray("(middlewares)")}`);
924
+ this.logger.info(`${chalk3__default.default.bold(middlewareDir)} ${chalk3__default.default.gray("(middlewares)")}`);
958
925
  await this.loadMiddlewares(middlewareDir);
959
926
  }
960
927
  await this.loadHandlers(handlersDir);
961
928
  this.attachToClient();
962
- this.logger.info(`${chalk4__default.default.bold.green("Loaded interaction handlers:")}`);
963
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.middlewares.length)} middlewares`);
964
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.slashMap.size)} slash commands`);
965
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.buttonMap.size)} buttons`);
966
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.modalMap.size)} modals`);
967
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.stringSelectMap.size)} string selects`);
968
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.userSelectMap.size)} user selects`);
969
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.roleSelectMap.size)} role selects`);
970
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.channelSelectMap.size)} channel selects`);
971
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
972
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
973
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.userContextMenuMap.size)} user context menus`);
974
- this.logger.info(`\u2192 ${chalk4__default.default.magenta.bold(this.autocompleteMap.size)} autocomplete`);
929
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded interaction handlers:")}`);
930
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.middlewares.length)} middlewares`);
931
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.slashMap.size)} slash commands`);
932
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.buttonMap.size)} buttons`);
933
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.modalMap.size)} modals`);
934
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.stringSelectMap.size)} string selects`);
935
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.userSelectMap.size)} user selects`);
936
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.roleSelectMap.size)} role selects`);
937
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.channelSelectMap.size)} channel selects`);
938
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
939
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
940
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.userContextMenuMap.size)} user context menus`);
941
+ this.logger.info(`\u2192 ${chalk3__default.default.magenta.bold(this.autocompleteMap.size)} autocomplete`);
975
942
  }
976
943
  async loadHandlers(dir) {
977
944
  await utils.traverseDirectory(dir, (_fullPath, relativePath, imported) => {
978
945
  for (const val of Object.values(imported)) {
979
946
  if (!this.isHandlerClass(val)) continue;
980
947
  this.registerHandler(val);
981
- this.logger.info(`${chalk4__default.default.italic("Registered")} ${chalk4__default.default.bold.yellow(val.name)} from ${chalk4__default.default.gray(relativePath)}`);
948
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(val.name)} from ${chalk3__default.default.gray(relativePath)}`);
982
949
  }
983
950
  }, this.logger);
984
951
  }
@@ -1000,7 +967,7 @@ var InteractionController = class {
1000
967
  priority: metadata.priority
1001
968
  });
1002
969
  this.middlewares.sort((a, b) => a.priority - b.priority);
1003
- this.logger.info(`${chalk4__default.default.italic("Registered middleware")} ${chalk4__default.default.bold.yellow(middlewareCtor.name)} ${chalk4__default.default.gray(`(priority ${metadata.priority})`)} from ${chalk4__default.default.gray(relativePath)}`);
970
+ this.logger.info(`${chalk3__default.default.italic("Registered middleware")} ${chalk3__default.default.bold.yellow(middlewareCtor.name)} ${chalk3__default.default.gray(`(priority ${metadata.priority})`)} from ${chalk3__default.default.gray(relativePath)}`);
1004
971
  }
1005
972
  isHandlerClass(obj) {
1006
973
  if (typeof obj !== "function") return false;
@@ -1067,7 +1034,7 @@ var InteractionController = class {
1067
1034
  attachToClient() {
1068
1035
  this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
1069
1036
  this.handleInteraction(interaction).catch((err) => {
1070
- this.logger.error(`[${chalk4__default.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
1037
+ this.logger.error(`[${chalk3__default.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
1071
1038
  });
1072
1039
  });
1073
1040
  }
@@ -1098,10 +1065,10 @@ var InteractionController = class {
1098
1065
  }
1099
1066
  let HandlerCtor = getHandler(key);
1100
1067
  if (!HandlerCtor) {
1101
- this.logger.warn(`No handler found for key ${chalk4__default.default.bold.cyan(key)}. Falling back to UnhandledEvent.`);
1068
+ this.logger.warn(`No handler found for key ${chalk3__default.default.bold.cyan(key)}. Falling back to UnhandledEvent.`);
1102
1069
  HandlerCtor = UnhandledEvent;
1103
1070
  }
1104
- this.logger.debug(`Processing ${chalk4__default.default.bold.green(key)} with ${chalk4__default.default.gray(HandlerCtor.name)}`);
1071
+ this.logger.debug(`Processing ${chalk3__default.default.bold.green(key)} with ${chalk3__default.default.gray(HandlerCtor.name)}`);
1105
1072
  const handler = new HandlerCtor(interaction, this.core, args);
1106
1073
  if (handler.hasChecks()) await handler.runChecks();
1107
1074
  if (handler.shouldBreak()) return;
@@ -1185,6 +1152,69 @@ var InteractionController = class {
1185
1152
  await this.processInteraction(interaction, () => autocompleteKey, (key) => this.autocompleteMap.get(key));
1186
1153
  }
1187
1154
  };
1155
+ var Plugin = class {
1156
+ static {
1157
+ __name(this, "Plugin");
1158
+ }
1159
+ pluggable;
1160
+ constructor(pluggable) {
1161
+ this.pluggable = pluggable;
1162
+ }
1163
+ };
1164
+ var Pluggable = class _Pluggable {
1165
+ static {
1166
+ __name(this, "Pluggable");
1167
+ }
1168
+ isInitialized = false;
1169
+ shutdown;
1170
+ startup;
1171
+ static PLUGIN_INIT_TIMEOUT_MS = 15e3;
1172
+ constructor(shutdown, startup) {
1173
+ this.shutdown = shutdown;
1174
+ this.startup = startup;
1175
+ }
1176
+ async init() {
1177
+ if (this.isInitialized) return this;
1178
+ await this.startup.run();
1179
+ this.isInitialized = true;
1180
+ return this;
1181
+ }
1182
+ /**
1183
+ * Attaches a plugin to this instance
1184
+ *
1185
+ * Plugins provide external functionality and are initialized during the specified startup phase.
1186
+ * The plugin instance becomes available as a property in `core` wherever it's available.
1187
+ *
1188
+ * Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
1189
+ *
1190
+ * @typeParam Key - The property name for accessing the plugin
1191
+ * @typeParam Ctor - The plugin constructor type
1192
+ * @param key - Property name to access the plugin instance
1193
+ * @param Plugin - Plugin constructor class
1194
+ * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
1195
+ * @param args - Additional arguments to pass to the plugin constructor
1196
+ * @returns This instance with the plugin attached as a typed property
1197
+ * @throws An {@link Error} When called after initialization or if key already exists
1198
+ * @example
1199
+ * ```typescript
1200
+ * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
1201
+ * ```
1202
+ */
1203
+ attach(key, Plugin2, startupPhase, ...args) {
1204
+ if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
1205
+ if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
1206
+ const instance = new Plugin2(this, ...args);
1207
+ const entry = {
1208
+ [key]: instance
1209
+ };
1210
+ this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
1211
+ instance.logger.info(chalk3__default.default.bold("Initializing"));
1212
+ await instance.init();
1213
+ instance.logger.info(chalk3__default.default.bold("Initialized"));
1214
+ }, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
1215
+ return Object.assign(this, entry);
1216
+ }
1217
+ };
1188
1218
  var EmojiInjector = class {
1189
1219
  static {
1190
1220
  __name(this, "EmojiInjector");
@@ -1196,7 +1226,7 @@ var EmojiInjector = class {
1196
1226
  }
1197
1227
  async init() {
1198
1228
  if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
1199
- this.logger.info(`${chalk4__default.default.bold.green("Loaded")}: ${chalk4__default.default.magenta.bold("0")} emojis`);
1229
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold("0")} emojis`);
1200
1230
  return;
1201
1231
  }
1202
1232
  const configEmojis = this.core.config.bot.emojis;
@@ -1207,10 +1237,10 @@ var EmojiInjector = class {
1207
1237
  if (emoji) {
1208
1238
  configEmojis[key] = `<${emoji.identifier}>`;
1209
1239
  foundCount++;
1210
- this.logger.debug(`${chalk4__default.default.bold.green("Found")}: ${chalk4__default.default.magenta.bold(emojiName)} (${emoji.id})`);
1240
+ this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${emoji.id})`);
1211
1241
  }
1212
1242
  });
1213
- this.logger.info(`${chalk4__default.default.bold.green("Loaded")}: ${chalk4__default.default.magenta.bold(foundCount)} emojis`);
1243
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(foundCount)} emojis`);
1214
1244
  }
1215
1245
  };
1216
1246
 
@@ -1276,7 +1306,7 @@ var Bot = class extends Plugin {
1276
1306
  */
1277
1307
  async login() {
1278
1308
  await this._client.login(this.botToken);
1279
- this.logger.info(`Logged in as ${chalk4__default.default.bold.magenta(this._client.user?.username)}!`);
1309
+ this.logger.info(`Logged in as ${chalk3__default.default.bold.magenta(this._client.user?.username)}!`);
1280
1310
  return this;
1281
1311
  }
1282
1312
  /**
@@ -1284,7 +1314,7 @@ var Bot = class extends Plugin {
1284
1314
  */
1285
1315
  async logout() {
1286
1316
  await this._client.destroy();
1287
- this.logger.info(chalk4__default.default.bold.red("Logged out of Discord!"));
1317
+ this.logger.info(chalk3__default.default.bold.red("Logged out of Discord!"));
1288
1318
  }
1289
1319
  get client() {
1290
1320
  return this._client;
@@ -1302,14 +1332,18 @@ _ts_decorate4([
1302
1332
 
1303
1333
  // src/effects/decorators/RegisterEffect.ts
1304
1334
  var EffectMetadataKey = Symbol("effect:metadata");
1305
- function RegisterEffect(effect) {
1335
+ function RegisterEffect(effect, options) {
1306
1336
  return function(constructor) {
1307
- Reflect.defineMetadata(EffectMetadataKey, effect, constructor);
1337
+ const meta = {
1338
+ effect,
1339
+ frequency: options?.frequency
1340
+ };
1341
+ Reflect.defineMetadata(EffectMetadataKey, meta, constructor);
1308
1342
  };
1309
1343
  }
1310
1344
  __name(RegisterEffect, "RegisterEffect");
1311
1345
 
1312
- // src/effects/interfaces/EffectsHandler.ts
1346
+ // src/effects/EffectsHandler.ts
1313
1347
  var EffectsHandler = class {
1314
1348
  static {
1315
1349
  __name(this, "EffectsHandler");
@@ -1330,7 +1364,7 @@ var EffectsHandler = class {
1330
1364
  }
1331
1365
  };
1332
1366
 
1333
- // src/effects/interfaces/abstracts/WebhookLog.ts
1367
+ // src/effects/bases/WebhookLog.ts
1334
1368
  var WebhookLog = class extends EffectsHandler {
1335
1369
  static {
1336
1370
  __name(this, "WebhookLog");
@@ -1506,51 +1540,56 @@ var EffectsRegistry = class extends Plugin {
1506
1540
  if (this.isInitialized) return;
1507
1541
  this.isInitialized = true;
1508
1542
  const effectsDir = this.core.config.effects.path;
1509
- this.logger.info(chalk4__default.default.bold(effectsDir));
1510
- this.registerEffect("unknownException", exports.UnknownException);
1543
+ this.logger.info(chalk3__default.default.bold(effectsDir));
1544
+ this.registerEffect(exports.UnknownException, {
1545
+ effect: "unknownException",
1546
+ frequency: "on"
1547
+ });
1511
1548
  await this.loadEffects(effectsDir);
1512
1549
  this.attachEffects();
1513
1550
  const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1514
- this.logger.info(`${chalk4__default.default.bold.green("Loaded")}: ${chalk4__default.default.bold.magenta(totalEffects)} side effects`);
1551
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.bold.magenta(totalEffects)} side effects`);
1515
1552
  }
1516
1553
  async loadEffects(dir) {
1517
1554
  await utils.traverseDirectory(dir, (_fullPath, relativePath, imported) => {
1518
1555
  for (const exportName of Object.keys(imported)) {
1519
1556
  const val = imported[exportName];
1520
1557
  if (this.isEffectHandler(val)) {
1521
- const effectName = Reflect.getMetadata(EffectMetadataKey, val);
1522
- if (effectName) {
1523
- this.registerEffect(effectName, val);
1524
- this.logger.info(`${chalk4__default.default.italic("Registered")} ${chalk4__default.default.bold.yellow(val.name)} from ${chalk4__default.default.gray(relativePath)}`);
1525
- }
1558
+ const meta = Reflect.getMetadata(EffectMetadataKey, val);
1559
+ this.registerEffect(val, meta);
1560
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(val.name)} from ${chalk3__default.default.gray(relativePath)}`);
1526
1561
  }
1527
1562
  }
1528
1563
  }, this.logger);
1529
1564
  }
1530
- registerEffect(effectName, handler) {
1531
- let handlers = this.effectsMap.get(effectName);
1565
+ registerEffect(handler, options) {
1566
+ let handlers = this.effectsMap.get(options.effect);
1532
1567
  if (!handlers) {
1533
1568
  handlers = [];
1534
- this.effectsMap.set(effectName, handlers);
1569
+ this.effectsMap.set(options.effect, handlers);
1535
1570
  }
1536
- handlers.push(handler);
1571
+ handlers.push({
1572
+ ctor: handler,
1573
+ frequency: options.frequency ?? "on"
1574
+ });
1537
1575
  }
1538
1576
  isEffectHandler(obj) {
1539
1577
  if (typeof obj !== "function") return false;
1540
- return obj.prototype instanceof EffectsHandler;
1578
+ return obj.prototype instanceof EffectsHandler && Reflect.hasMetadata(EffectMetadataKey, obj);
1541
1579
  }
1542
1580
  attachEffects() {
1543
- for (const [effectName, handlerCtors] of this.effectsMap) {
1544
- this.emitter.on(effectName, (data) => {
1545
- for (const HandlerCtor of handlerCtors) {
1581
+ for (const [effectName, handlerEntries] of this.effectsMap) {
1582
+ for (const entry of handlerEntries) {
1583
+ const register = entry.frequency === "once" ? this.emitter.once.bind(this.emitter) : this.emitter.on.bind(this.emitter);
1584
+ register(effectName, (data) => {
1546
1585
  try {
1547
- const instance = new HandlerCtor(data, this.core);
1586
+ const instance = new entry.ctor(data, this.core);
1548
1587
  void instance.execute();
1549
1588
  } catch (err) {
1550
- this.logger.error(`Error in side effect ${String(effectName)} handler ${HandlerCtor.name}:`, err);
1589
+ this.logger.error(`Error in side effect ${String(effectName)} handler ${entry.ctor.name}:`, err);
1551
1590
  }
1552
- }
1553
- });
1591
+ });
1592
+ }
1554
1593
  }
1555
1594
  }
1556
1595
  emit(event, data) {
@@ -1602,19 +1641,19 @@ var Seedcord = class _Seedcord extends Pluggable {
1602
1641
  */
1603
1642
  registerStartupTasks() {
1604
1643
  this.startup.addTask(services.StartupPhase.Configuration, "Effect Initialization", async () => {
1605
- this.effects.logger.info(chalk4__default.default.bold("Initializing"));
1644
+ this.effects.logger.info(chalk3__default.default.bold("Initializing"));
1606
1645
  await this.effects.init();
1607
- this.effects.logger.info(chalk4__default.default.bold("Initialized"));
1646
+ this.effects.logger.info(chalk3__default.default.bold("Initialized"));
1608
1647
  });
1609
1648
  this.startup.addTask(services.StartupPhase.Instantiation, "Bot Initialization", async () => {
1610
- this.bot.logger.info(chalk4__default.default.bold("Initializing"));
1649
+ this.bot.logger.info(chalk3__default.default.bold("Initializing"));
1611
1650
  await this.bot.init();
1612
- this.bot.logger.info(chalk4__default.default.bold("Initialized"));
1651
+ this.bot.logger.info(chalk3__default.default.bold("Initialized"));
1613
1652
  });
1614
1653
  this.startup.addTask(services.StartupPhase.Ready, "Health Check", async () => {
1615
- this.healthCheck.logger.info(chalk4__default.default.bold("Initializing"));
1654
+ this.healthCheck.logger.info(chalk3__default.default.bold("Initializing"));
1616
1655
  await this.healthCheck.init();
1617
- this.healthCheck.logger.info(chalk4__default.default.bold("Initialized"));
1656
+ this.healthCheck.logger.info(chalk3__default.default.bold("Initialized"));
1618
1657
  });
1619
1658
  }
1620
1659
  /**