seedcord 0.4.3 → 0.5.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.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import 'reflect-metadata';
2
2
  import { Logger, ShutdownPhase, CoordinatedShutdown, CoordinatedStartup, HealthCheck, StartupPhase } from '@seedcord/services';
3
3
  export * from '@seedcord/services';
4
- import chalk2 from 'chalk';
5
- import { SeparatorBuilder, SectionBuilder, MediaGalleryBuilder, FileBuilder, TextDisplayBuilder, ContainerBuilder, SlashCommandSubcommandGroupBuilder, SlashCommandSubcommandBuilder, ContextMenuCommandBuilder, ModalBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, UserSelectMenuBuilder, StringSelectMenuOptionBuilder, StringSelectMenuBuilder, ButtonBuilder, EmbedBuilder, SlashCommandBuilder, InteractionContextType, ActionRowBuilder, TextInputBuilder, Collection, MessageFlags, Events, Client, WebhookClient, DiscordAPIError, SnowflakeUtil, Message } from 'discord.js';
4
+ import chalk4 from 'chalk';
5
+ import { SeparatorBuilder, SectionBuilder, MediaGalleryBuilder, FileBuilder, TextDisplayBuilder, ContainerBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, UserSelectMenuBuilder, StringSelectMenuOptionBuilder, StringSelectMenuBuilder, ButtonBuilder, FileUploadBuilder, TextInputBuilder, LabelBuilder, ModalBuilder, EmbedBuilder, SlashCommandSubcommandGroupBuilder, SlashCommandSubcommandBuilder, ContextMenuCommandBuilder, SlashCommandBuilder, InteractionContextType, ActionRowBuilder, Collection, MessageFlags, Events, Client, WebhookClient, AttachmentBuilder, SeparatorSpacingSize, DiscordAPIError, SnowflakeUtil, Role, PermissionFlagsBits, ChatInputCommandInteraction, AutocompleteInteraction, Message, TextChannel, Guild, RESTJSONErrorCodes, Colors } from 'discord.js';
6
6
  import { Envapt } from 'envapt';
7
- import { traverseDirectory } from '@seedcord/utils';
7
+ import { hexToNumber, traverseDirectory, filterCirculars, prettify } from '@seedcord/utils';
8
8
  export * from '@seedcord/utils';
9
9
  import * as crypto2 from 'crypto';
10
10
  import { EventEmitter } from 'events';
@@ -39,26 +39,26 @@ var Pluggable = class _Pluggable {
39
39
  return this;
40
40
  }
41
41
  /**
42
- * Attaches a plugin to this instance
43
- *
44
- * Plugins provide external functionality and are initialized during the specified startup phase.
45
- * The plugin instance becomes available as a property in `core` wherever it's available.
46
- *
47
- * Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
48
- *
49
- * @typeParam Key - The property name for accessing the plugin
50
- * @typeParam Ctor - The plugin constructor type
51
- * @param key - Property name to access the plugin instance
52
- * @param Plugin - Plugin constructor class
53
- * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
54
- * @param args - Additional arguments to pass to the plugin constructor
55
- * @returns This instance with the plugin attached as a typed property
56
- * @throws An {@link Error} When called after initialization or if key already exists
57
- * @example
58
- * ```typescript
59
- * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
60
- * ```
61
- */
42
+ * Attaches a plugin to this instance
43
+ *
44
+ * Plugins provide external functionality and are initialized during the specified startup phase.
45
+ * The plugin instance becomes available as a property in `core` wherever it's available.
46
+ *
47
+ * Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
48
+ *
49
+ * @typeParam Key - The property name for accessing the plugin
50
+ * @typeParam Ctor - The plugin constructor type
51
+ * @param key - Property name to access the plugin instance
52
+ * @param Plugin - Plugin constructor class
53
+ * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
54
+ * @param args - Additional arguments to pass to the plugin constructor
55
+ * @returns This instance with the plugin attached as a typed property
56
+ * @throws An {@link Error} When called after initialization or if key already exists
57
+ * @example
58
+ * ```typescript
59
+ * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
60
+ * ```
61
+ */
62
62
  attach(key, Plugin2, startupPhase, ...args) {
63
63
  if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
64
64
  if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
@@ -67,13 +67,27 @@ var Pluggable = class _Pluggable {
67
67
  [key]: instance
68
68
  };
69
69
  this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
70
- instance.logger.info(chalk2.bold("Initializing"));
70
+ instance.logger.info(chalk4.bold("Initializing"));
71
71
  await instance.init();
72
- instance.logger.info(chalk2.bold("Initialized"));
72
+ instance.logger.info(chalk4.bold("Initialized"));
73
73
  }, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
74
74
  return Object.assign(this, entry);
75
75
  }
76
76
  };
77
+ function parseEnvColor(raw, fallback) {
78
+ if (!raw) return fallback;
79
+ const toHex = /* @__PURE__ */ __name((n) => `#${n.toString(16).padStart(6, "0")}`, "toHex");
80
+ const num = Number(raw);
81
+ if (!Number.isNaN(num) && num >= 0) return toHex(num);
82
+ if (raw.startsWith("#")) return raw;
83
+ if (/^[0-9A-Fa-f]{6}$/.test(raw)) return `#${raw}`;
84
+ const colorKey = Object.keys(Colors).find((key) => key.toLowerCase() === raw.toLowerCase());
85
+ if (colorKey) return toHex(Colors[colorKey]);
86
+ return fallback;
87
+ }
88
+ __name(parseEnvColor, "parseEnvColor");
89
+
90
+ // src/interfaces/Components.ts
77
91
  function _ts_decorate(decorators, target, key, desc) {
78
92
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
79
93
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -86,8 +100,19 @@ function _ts_metadata(k, v) {
86
100
  }
87
101
  __name(_ts_metadata, "_ts_metadata");
88
102
  var BuilderTypes = {
103
+ // Command Components
89
104
  command: SlashCommandBuilder,
105
+ context_menu: ContextMenuCommandBuilder,
106
+ subcommand: SlashCommandSubcommandBuilder,
107
+ group: SlashCommandSubcommandGroupBuilder,
108
+ // Embed Components
90
109
  embed: EmbedBuilder,
110
+ // Modal Components
111
+ modal: ModalBuilder,
112
+ label: LabelBuilder,
113
+ text_input: TextInputBuilder,
114
+ file_upload: FileUploadBuilder,
115
+ // Action Row Components
91
116
  button: ButtonBuilder,
92
117
  menu_string: StringSelectMenuBuilder,
93
118
  menu_option_string: StringSelectMenuOptionBuilder,
@@ -95,10 +120,7 @@ var BuilderTypes = {
95
120
  menu_channel: ChannelSelectMenuBuilder,
96
121
  menu_mentionable: MentionableSelectMenuBuilder,
97
122
  menu_role: RoleSelectMenuBuilder,
98
- modal: ModalBuilder,
99
- context_menu: ContextMenuCommandBuilder,
100
- subcommand: SlashCommandSubcommandBuilder,
101
- group: SlashCommandSubcommandGroupBuilder,
123
+ // ComponentsV2
102
124
  container: ContainerBuilder,
103
125
  text_display: TextDisplayBuilder,
104
126
  file: FileBuilder,
@@ -112,13 +134,9 @@ var RowTypes = {
112
134
  menu_user: ActionRowBuilder,
113
135
  menu_channel: ActionRowBuilder,
114
136
  menu_mentionable: ActionRowBuilder,
115
- menu_role: ActionRowBuilder,
116
- modal: ActionRowBuilder
117
- };
118
- var ModalTypes = {
119
- text: TextInputBuilder
137
+ menu_role: ActionRowBuilder
120
138
  };
121
- var BaseComponent = class BaseComponent2 {
139
+ var BaseComponent = class {
122
140
  static {
123
141
  __name(this, "BaseComponent");
124
142
  }
@@ -128,26 +146,26 @@ var BaseComponent = class BaseComponent2 {
128
146
  this._component = new ComponentClass();
129
147
  }
130
148
  /**
131
- * Gets the component instance for configuration
132
- *
133
- * Use this to access Discord.js builder methods like setTitle(), setDescription(), etc.
134
- *
135
- * Use this in your component classes to configure the builder
136
- * @example this.instance.setTitle('My Modal')
137
- */
149
+ * Gets the component instance for configuration
150
+ *
151
+ * Use this to access Discord.js builder methods like setTitle(), setDescription(), etc.
152
+ *
153
+ * Use this in your component classes to configure the builder
154
+ * @example this.instance.setTitle('My Modal')
155
+ */
138
156
  get instance() {
139
157
  return this._component;
140
158
  }
141
159
  /**
142
- * Builds a customId string for interactive components
143
- *
144
- * Creates customIds in the format "prefix:arg1-arg2-arg3" for buttons, modals, etc.
145
- * Arguments are joined with hyphens and separated from prefix with a colon.
146
- *
147
- * @param prefix - The route prefix that handlers will match against
148
- * @param args - Additional arguments to encode in the customId
149
- * @returns Formatted customId string
150
- */
160
+ * Builds a customId string for interactive components
161
+ *
162
+ * Creates customIds in the format `prefix:arg1-arg2-arg3` for buttons, modals, etc.
163
+ * Arguments are joined with hyphens and separated from prefix with a colon.
164
+ *
165
+ * @param prefix - The route prefix that handlers will match against
166
+ * @param args - Additional arguments to encode in the customId
167
+ * @returns Formatted customId string
168
+ */
151
169
  buildCustomId(prefix, ...args) {
152
170
  if (args.length === 0) return prefix;
153
171
  return `${prefix}:${args.join("-")}`;
@@ -161,6 +179,9 @@ var BuilderComponent = class extends BaseComponent {
161
179
  const ComponentClass = BuilderTypes[type];
162
180
  super(ComponentClass);
163
181
  if (this.instance instanceof EmbedBuilder) this.instance.setColor(this.botColor);
182
+ if (this.instance instanceof ContainerBuilder) {
183
+ this.instance.setAccentColor(this.botColor === "Default" ? void 0 : hexToNumber(this.botColor.toString()));
184
+ }
164
185
  if (this.instance instanceof SlashCommandBuilder || this.instance instanceof ContextMenuCommandBuilder) {
165
186
  this.instance.setContexts(InteractionContextType.Guild);
166
187
  }
@@ -171,7 +192,8 @@ var BuilderComponent = class extends BaseComponent {
171
192
  };
172
193
  _ts_decorate([
173
194
  Envapt("DEFAULT_BOT_COLOR", {
174
- fallback: "Default"
195
+ fallback: "Default",
196
+ converter: /* @__PURE__ */ __name((raw, fallback) => parseEnvColor(raw, fallback), "converter")
175
197
  }),
176
198
  _ts_metadata("design:type", typeof ColorResolvable === "undefined" ? Object : ColorResolvable)
177
199
  ], BuilderComponent.prototype, "botColor", void 0);
@@ -187,39 +209,13 @@ var RowComponent = class extends BaseComponent {
187
209
  return this.instance;
188
210
  }
189
211
  };
190
- var ModalRow = class ModalRow2 extends RowComponent {
191
- static {
192
- __name(this, "ModalRow");
193
- }
194
- /**
195
- * Creates a new modal action row with the specified component.
196
- *
197
- * @param component - The modal field component to wrap in an action row
198
- */
199
- constructor(component) {
200
- super("modal");
201
- this.instance.addComponents(component);
202
- }
203
- };
204
- var ModalComponent = class extends BaseComponent {
205
- static {
206
- __name(this, "ModalComponent");
207
- }
208
- constructor(type) {
209
- const ComponentClass = ModalTypes[type];
210
- super(ComponentClass);
211
- }
212
- get component() {
213
- return new ModalRow(this.instance).component;
214
- }
215
- };
216
212
  var BaseErrorEmbed = class extends BuilderComponent {
217
213
  static {
218
214
  __name(this, "BaseErrorEmbed");
219
215
  }
220
216
  /**
221
- * Creates a new error embed with default configuration.
222
- */
217
+ * Creates a new error embed with default configuration.
218
+ */
223
219
  constructor() {
224
220
  super("embed");
225
221
  this.instance.setTitle("Cannot Proceed");
@@ -237,27 +233,25 @@ var CustomError = class extends Error {
237
233
  Error.captureStackTrace(this, this.constructor);
238
234
  }
239
235
  /**
240
- * Whether this error should be emitted to logs
241
- *
242
- * Controls logging behavior. Errors with emit=true will always be logged,
243
- * while emit=false errors may be suppressed in production.
244
- *
245
- * @returns True if the error should be logged
246
- */
236
+ * Whether this error should be emitted to logs
237
+ *
238
+ * Controls logging behavior. Errors with emit=true will always be logged,
239
+ * while emit=false errors may be suppressed in production.
240
+ *
241
+ * @returns True if the error should be logged
242
+ */
247
243
  get emit() {
248
244
  return this._emit;
249
245
  }
250
246
  /**
251
- * Sets whether this error should be emitted to logs
252
- *
253
- * @see {@link emit}
254
- */
247
+ * Sets whether this error should be emitted to logs
248
+ */
255
249
  set emit(value) {
256
250
  this._emit = value;
257
251
  }
258
252
  };
259
253
 
260
- // src/bot/decorators/CommandRegisterable.ts
254
+ // src/bot/decorators/Command.ts
261
255
  var CommandMetadataKey = Symbol("command:metadata");
262
256
  function RegisterCommand(scope, guilds = []) {
263
257
  return (ctor) => {
@@ -288,9 +282,9 @@ var CommandRegistry = class {
288
282
  async init() {
289
283
  if (this.isInitialised) return;
290
284
  this.isInitialised = true;
291
- this.logger.info(chalk2.bold(this.core.config.bot.commands.path));
285
+ this.logger.info(chalk4.bold(this.core.config.bot.commands.path));
292
286
  await this.loadCommands(this.core.config.bot.commands.path);
293
- this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold(this.globalCommands.length)} global, ${chalk2.magenta.bold(this.guildCommands.size)} guild groups`);
287
+ this.logger.info(`${chalk4.bold.green("Loaded")}: ${chalk4.magenta.bold(this.globalCommands.length)} global, ${chalk4.magenta.bold(this.guildCommands.size)} guild groups`);
294
288
  }
295
289
  async loadCommands(dir) {
296
290
  await traverseDirectory(dir, (_full, rel, mod) => {
@@ -309,24 +303,24 @@ var CommandRegistry = class {
309
303
  const commandType = comp instanceof SlashCommandBuilder ? "slash command" : "context menu command";
310
304
  if (meta.scope === "global") {
311
305
  this.globalCommands.push(comp);
312
- this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(ctor.name)} from ${chalk2.gray(rel)}`);
313
- this.logger.info(` \u2192 Global ${commandType}: ${chalk2.bold.cyan(comp.name)}`);
306
+ this.logger.info(`${chalk4.italic("Registered")} ${chalk4.bold.yellow(ctor.name)} from ${chalk4.gray(rel)}`);
307
+ this.logger.info(` \u2192 Global ${commandType}: ${chalk4.bold.cyan(comp.name)}`);
314
308
  } else {
315
309
  for (const g of meta.guilds) {
316
310
  const arr = this.guildCommands.get(g) ?? [];
317
311
  arr.push(comp);
318
312
  this.guildCommands.set(g, arr);
319
313
  }
320
- this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(ctor.name)} from ${chalk2.gray(rel)}`);
321
- this.logger.info(` \u2192 Guild ${commandType}: ${chalk2.bold.cyan(comp.name)} for ${chalk2.magenta.bold(meta.guilds.length)} guild(s)`);
314
+ this.logger.info(`${chalk4.italic("Registered")} ${chalk4.bold.yellow(ctor.name)} from ${chalk4.gray(rel)}`);
315
+ this.logger.info(` \u2192 Guild ${commandType}: ${chalk4.bold.cyan(comp.name)} for ${chalk4.magenta.bold(meta.guilds.length)} guild(s)`);
322
316
  }
323
317
  }
324
318
  async setCommands() {
325
319
  if (this.globalCommands.length > 0) {
326
320
  await this.core.bot.client.application?.commands.set(this.globalCommands);
327
321
  const tag = this.globalCommands.length === 1 ? "command" : "commands";
328
- this.logger.info(`${chalk2.bold.green("Configured")} ${chalk2.magenta.bold(this.globalCommands.length)} global ${tag}`);
329
- this.logger.info(` \u2192 ${this.globalCommands.map((command) => chalk2.bold.cyan(command.name)).join(", ")}`);
322
+ this.logger.info(`${chalk4.bold.green("Configured")} ${chalk4.magenta.bold(this.globalCommands.length)} global ${tag}`);
323
+ this.logger.info(` \u2192 ${this.globalCommands.map((command) => chalk4.bold.cyan(command.name)).join(", ")}`);
330
324
  }
331
325
  for (const [guildId, commands] of this.guildCommands.entries()) {
332
326
  const guild = this.core.bot.client.guilds.cache.get(guildId);
@@ -336,14 +330,14 @@ var CommandRegistry = class {
336
330
  }
337
331
  await guild.commands.set(commands);
338
332
  const tag = commands.length === 1 ? "command" : "commands";
339
- this.logger.info(`${chalk2.bold.green("Configured")} ${chalk2.magenta.bold(commands.length)} ${tag} for guild ${chalk2.bold.yellow(guild.name)}`);
340
- this.logger.info(` \u2192 ${commands.map((command) => chalk2.bold.cyan(command.name)).join(", ")}`);
333
+ this.logger.info(`${chalk4.bold.green("Configured")} ${chalk4.magenta.bold(commands.length)} ${tag} for guild ${chalk4.bold.yellow(guild.name)}`);
334
+ this.logger.info(` \u2192 ${commands.map((command) => chalk4.bold.cyan(command.name)).join(", ")}`);
341
335
  }
342
336
  }
343
337
  };
344
338
 
345
339
  // src/interfaces/Handler.ts
346
- var BaseHandler = class BaseHandler2 {
340
+ var BaseHandler = class {
347
341
  static {
348
342
  __name(this, "BaseHandler");
349
343
  }
@@ -374,19 +368,19 @@ var BaseHandler = class BaseHandler2 {
374
368
  return this.event;
375
369
  }
376
370
  /**
377
- * Gets arguments parsed from interaction customId
378
- *
379
- * Arguments are extracted from customId using ":" and "-" separators.
380
- * For customId "accept:user123-guild456", returns ["user123", "guild456"]
381
- */
371
+ * Gets arguments parsed from interaction customId
372
+ *
373
+ * Arguments are extracted from customId using `:` and `-` separators.
374
+ * For customId `accept:user123-guild456`, returns `["user123", "guild456"]`
375
+ */
382
376
  getArgs() {
383
377
  return this.args;
384
378
  }
385
379
  /**
386
- * Gets a specific argument by index from parsed customId
387
- * @param index - Zero-based index of the argument to retrieve
388
- * @returns The argument at the specified index, or undefined if not found
389
- */
380
+ * Gets a specific argument by index from parsed customId
381
+ * @param index - Zero-based index of the argument to retrieve
382
+ * @returns The argument at the specified index, or undefined if not found
383
+ */
390
384
  getArg(index) {
391
385
  return this.args[index];
392
386
  }
@@ -407,11 +401,19 @@ var InteractionMiddleware = class extends BaseHandler {
407
401
  super(event, core, args);
408
402
  }
409
403
  };
404
+ var EventMiddleware = class extends BaseHandler {
405
+ static {
406
+ __name(this, "EventMiddleware");
407
+ }
408
+ constructor(event, core, args) {
409
+ super(event, core, args);
410
+ }
411
+ };
410
412
  var AutocompleteHandler = class extends BaseHandler {
411
413
  static {
412
414
  __name(this, "AutocompleteHandler");
413
415
  }
414
- /** The currently focused autocomplete option (Based on what you set in \@AutocompleteRoute) */
416
+ /** The currently focused autocomplete option (Based on what you set in `@AutocompleteRoute`) */
415
417
  focused;
416
418
  constructor(event, core, args) {
417
419
  super(event, core, args);
@@ -427,15 +429,57 @@ var EventHandler = class extends BaseHandler {
427
429
  }
428
430
  };
429
431
 
430
- // src/bot/decorators/EventRegisterable.ts
432
+ // src/miscellaneous/areRoutes.ts
433
+ function areRoutes(routes) {
434
+ return Array.isArray(routes) && routes.every((r) => typeof r === "string");
435
+ }
436
+ __name(areRoutes, "areRoutes");
437
+
438
+ // src/bot/decorators/Events.ts
431
439
  var EventMetadataKey = Symbol("event:metadata");
432
- function RegisterEvent(eventName) {
440
+ function RegisterEvent(events) {
433
441
  return function(constructor) {
434
- Reflect.defineMetadata(EventMetadataKey, eventName, constructor);
442
+ const saved = Reflect.getMetadata(EventMetadataKey, constructor);
443
+ const existing = areRoutes(saved) ? saved : [];
444
+ const toStore = Array.isArray(events) ? events : [
445
+ events
446
+ ];
447
+ Reflect.defineMetadata(EventMetadataKey, [
448
+ ...existing,
449
+ ...toStore
450
+ ], constructor);
435
451
  };
436
452
  }
437
453
  __name(RegisterEvent, "RegisterEvent");
438
454
 
455
+ // src/bot/decorators/Middlewares.ts
456
+ var MiddlewareType = /* @__PURE__ */ (function(MiddlewareType2) {
457
+ MiddlewareType2["Interaction"] = "middleware:interaction";
458
+ MiddlewareType2["Event"] = "middleware:event";
459
+ return MiddlewareType2;
460
+ })({});
461
+ var MiddlewareMetadataKey = Symbol("middleware:metadata");
462
+ function Middleware(type, priority = 0, options = {}) {
463
+ return (ctor) => {
464
+ const normalizedPriority = Number(priority);
465
+ if (!Number.isFinite(normalizedPriority)) {
466
+ throw new TypeError("Middleware priority must be a finite number");
467
+ }
468
+ if (type === "middleware:interaction" && options.events?.length) {
469
+ throw new Error("Interaction middleware cannot specify event filters");
470
+ }
471
+ const metadata = {
472
+ priority: normalizedPriority,
473
+ type,
474
+ ...options.events ? {
475
+ events: options.events
476
+ } : {}
477
+ };
478
+ Reflect.defineMetadata(MiddlewareMetadataKey, metadata, ctor);
479
+ };
480
+ }
481
+ __name(Middleware, "Middleware");
482
+
439
483
  // src/bot/controllers/EventController.ts
440
484
  var EventController = class {
441
485
  static {
@@ -445,6 +489,7 @@ var EventController = class {
445
489
  logger = new Logger("Events");
446
490
  isInitialized = false;
447
491
  eventMap = new Collection();
492
+ middlewares = [];
448
493
  constructor(core) {
449
494
  this.core = core;
450
495
  }
@@ -454,42 +499,96 @@ var EventController = class {
454
499
  }
455
500
  this.isInitialized = true;
456
501
  const handlersDir = this.core.config.bot.events.path;
457
- this.logger.info(chalk2.bold(handlersDir));
502
+ this.logger.info(chalk4.bold(handlersDir));
503
+ const middlewareDir = this.core.config.bot.events.middlewares;
504
+ if (middlewareDir) {
505
+ this.logger.info(`${chalk4.bold(middlewareDir)} ${chalk4.gray("(middlewares)")}`);
506
+ await this.loadMiddlewares(middlewareDir);
507
+ }
458
508
  await this.loadHandlers(handlersDir);
459
509
  this.attachToClient();
510
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.middlewares.length)} middlewares`);
460
511
  const loadedEventsArray = [];
461
512
  this.eventMap.forEach((handlers, eventName) => {
462
- loadedEventsArray.push(`${chalk2.magenta.bold(handlers.length)} ${eventName}`);
513
+ loadedEventsArray.push(`${chalk4.magenta.bold(handlers.length)} ${eventName}`);
463
514
  });
464
- this.logger.info(`${chalk2.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
515
+ this.logger.info(`${chalk4.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
465
516
  }
466
517
  async loadHandlers(dir) {
467
518
  await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
468
519
  for (const val of Object.values(imported)) {
469
- if (this.isEventHandlerClass(val)) {
470
- this.registerHandler(val);
471
- this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
472
- }
520
+ if (!this.isEventHandlerClass(val)) continue;
521
+ this.registerHandler(val);
522
+ this.logger.info(`${chalk4.italic("Registered")} ${chalk4.bold.yellow(val.name)} from ${chalk4.gray(relativePath)}`);
523
+ }
524
+ }, this.logger);
525
+ }
526
+ async loadMiddlewares(dir) {
527
+ await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
528
+ for (const val of Object.values(imported)) {
529
+ if (!this.isMiddlewareClass(val)) continue;
530
+ const metadata = Reflect.getMetadata(MiddlewareMetadataKey, val);
531
+ if (metadata?.type !== MiddlewareType.Event) continue;
532
+ this.registerMiddleware(val, metadata, relativePath);
473
533
  }
474
534
  }, this.logger);
475
535
  }
536
+ registerMiddleware(middlewareCtor, metadata, relativePath) {
537
+ const alreadyRegistered = this.middlewares.some((entry) => entry.ctor === middlewareCtor);
538
+ if (alreadyRegistered) return;
539
+ this.middlewares.push({
540
+ ctor: middlewareCtor,
541
+ priority: metadata.priority,
542
+ ...metadata.events ? {
543
+ events: metadata.events
544
+ } : {}
545
+ });
546
+ this.middlewares.sort((a, b) => a.priority - b.priority);
547
+ this.logger.info(`${chalk4.italic("Registered event middleware")} ${chalk4.bold.yellow(middlewareCtor.name)} ${chalk4.gray(`(priority ${metadata.priority})`)} from ${chalk4.gray(relativePath)}`);
548
+ }
549
+ async runMiddlewares(eventName, args) {
550
+ for (const { ctor, events } of this.middlewares) {
551
+ if (events && !events.includes(eventName)) continue;
552
+ try {
553
+ const middleware = new ctor(args, this.core);
554
+ if (middleware.hasChecks()) await middleware.runChecks();
555
+ if (middleware.shouldBreak() || middleware.hasErrors()) return false;
556
+ await middleware.execute();
557
+ if (middleware.shouldBreak() || middleware.hasErrors()) return false;
558
+ } catch (err) {
559
+ this.logger.error(`Error in event middleware ${ctor.name} for event ${String(eventName)}:`, err);
560
+ return false;
561
+ }
562
+ }
563
+ return true;
564
+ }
476
565
  isEventHandlerClass(obj) {
477
566
  if (typeof obj !== "function") return false;
478
567
  return obj.prototype instanceof EventHandler && Reflect.hasMetadata(EventMetadataKey, obj);
479
568
  }
569
+ isMiddlewareClass(obj) {
570
+ if (typeof obj !== "function") return false;
571
+ return obj.prototype instanceof EventMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
572
+ }
480
573
  registerHandler(handlerClass) {
481
- const eventName = Reflect.getMetadata(EventMetadataKey, handlerClass);
482
- if (!eventName) return;
483
- let handlers = this.eventMap.get(eventName);
484
- if (!handlers) {
485
- handlers = [];
486
- this.eventMap.set(eventName, handlers);
574
+ const raw = Reflect.getMetadata(EventMetadataKey, handlerClass);
575
+ const names = areRoutes(raw) ? raw : typeof raw === "string" ? [
576
+ raw
577
+ ] : [];
578
+ if (names.length === 0) return;
579
+ for (const name of names) {
580
+ const key = name;
581
+ let handlers = this.eventMap.get(key);
582
+ if (!handlers) {
583
+ handlers = [];
584
+ this.eventMap.set(key, handlers);
585
+ }
586
+ handlers.push(handlerClass);
487
587
  }
488
- handlers.push(handlerClass);
489
588
  }
490
589
  attachToClient() {
491
590
  for (const [eventName] of this.eventMap) {
492
- this.logger.debug(`Attaching ${chalk2.bold.green(eventName)} to ${chalk2.bold.yellow(this.core.bot.client.user?.username)}`);
591
+ this.logger.debug(`Attaching ${chalk4.bold.green(eventName)} to ${chalk4.bold.yellow(this.core.bot.client.user?.username)}`);
493
592
  this.core.bot.client.on(eventName, (...args) => {
494
593
  void (async () => {
495
594
  await this.processEvent(eventName, args);
@@ -498,11 +597,13 @@ var EventController = class {
498
597
  }
499
598
  }
500
599
  async processEvent(eventName, args) {
600
+ const shouldContinue = await this.runMiddlewares(eventName, args);
601
+ if (!shouldContinue) return;
501
602
  const handlerCtors = this.eventMap.get(eventName);
502
603
  if (!handlerCtors || handlerCtors.length === 0) return;
503
604
  for (const HandlerCtor of handlerCtors) {
504
605
  try {
505
- this.logger.debug(`Processing ${chalk2.bold.green(eventName)} with ${chalk2.gray(HandlerCtor.name)}`);
606
+ this.logger.debug(`Processing ${chalk4.bold.green(eventName)} with ${chalk4.gray(HandlerCtor.name)}`);
506
607
  const handler = new HandlerCtor(args, this.core);
507
608
  if (handler.hasChecks()) {
508
609
  await handler.runChecks();
@@ -518,7 +619,7 @@ var EventController = class {
518
619
  }
519
620
  };
520
621
 
521
- // src/bot/decorators/InteractionConfigurable.ts
622
+ // src/bot/decorators/Interactions.ts
522
623
  var InteractionRoutes = /* @__PURE__ */ (function(InteractionRoutes2) {
523
624
  InteractionRoutes2["Slash"] = "interaction:slash";
524
625
  InteractionRoutes2["Button"] = "interaction:button";
@@ -598,9 +699,6 @@ function SelectMenuRoute(type, routeOrRoutes) {
598
699
  }
599
700
  __name(SelectMenuRoute, "SelectMenuRoute");
600
701
  function storeMetadata(symbol, routes, constructor) {
601
- const areRoutes = /* @__PURE__ */ __name((routes2) => {
602
- return Array.isArray(routes2) && routes2.every((r) => typeof r === "string");
603
- }, "areRoutes");
604
702
  const savedRoutes = Reflect.getMetadata(symbol, constructor);
605
703
  const existing = areRoutes(savedRoutes) ? savedRoutes : [];
606
704
  const toStore = Array.isArray(routes) ? routes : [
@@ -621,11 +719,11 @@ var DatabaseError = class extends CustomError {
621
719
  }
622
720
  uuid;
623
721
  /**
624
- * Creates a new DatabaseError.
625
- *
626
- * @param message - The error message describing what went wrong
627
- * @param uuid - A unique identifier for this specific error instance
628
- */
722
+ * Creates a new DatabaseError.
723
+ *
724
+ * @param message - The error message describing what went wrong
725
+ * @param uuid - A unique identifier for this specific error instance
726
+ */
629
727
  constructor(message, uuid) {
630
728
  super(message), this.uuid = uuid;
631
729
  this.emit = true;
@@ -635,69 +733,71 @@ var DatabaseError = class extends CustomError {
635
733
  }
636
734
  };
637
735
 
638
- // src/bot/utilities/ErrorHandlingUtils.ts
639
- var ErrorHandlingUtils = class {
640
- static {
641
- __name(this, "ErrorHandlingUtils");
642
- }
643
- static logger = new Logger("Errors");
644
- /**
645
- * Processes an error and extracts the standardized response, if available.
646
- *
647
- * Handles different error types (CustomError, DatabaseError) with appropriate
648
- * logging, side effects, and user-facing error messages.
649
- *
650
- * @param error - The error to process
651
- * @param core - The core framework instance
652
- * @param guild - The guild where the error occurred (if any)
653
- * @param user - The user who triggered the error (if any)
654
- * @returns Object containing UUID and formatted error response embed
655
- */
656
- static extractErrorResponse(error, core, guild, user) {
657
- const uuid = crypto2.randomUUID();
658
- if (error instanceof CustomError) {
659
- if (error instanceof DatabaseError) {
660
- core.effects.emit("unknownException", {
661
- uuid,
662
- error,
663
- guild,
664
- user
665
- });
666
- this.logger.error(`DatabaseError: ${error.uuid}`);
667
- } else if (error.emit) {
668
- this.logger.error(`${error.name}: ${error.message}`, error);
669
- }
670
- return {
736
+ // src/bot/utilities/errors/extractErrorResponse.ts
737
+ function _ts_decorate2(decorators, target, key, desc) {
738
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
739
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
740
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
741
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
742
+ }
743
+ __name(_ts_decorate2, "_ts_decorate");
744
+ function _ts_metadata2(k, v) {
745
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
746
+ }
747
+ __name(_ts_metadata2, "_ts_metadata");
748
+ var logger = new Logger("ErrorsHandling");
749
+ function extractErrorResponse(error, core, guild, user, metadata) {
750
+ const uuid = crypto2.randomUUID();
751
+ if (error instanceof CustomError) {
752
+ if (error instanceof DatabaseError) {
753
+ core.effects.emit("unknownException", {
671
754
  uuid,
672
- response: error.response
673
- };
755
+ error,
756
+ guild,
757
+ user,
758
+ metadata
759
+ });
760
+ logger.error(`DatabaseError: ${error.uuid}`);
761
+ } else if (error.emit) {
762
+ logger.error(`${error.name}: ${error.message}`, error);
674
763
  }
675
- const showStack = core.config.bot.errorStack;
676
- if (showStack) this.logger.error(uuid, error);
677
- else this.logger.error(`${uuid} | ${error.message}`);
678
- core.effects.emit("unknownException", {
679
- uuid,
680
- error,
681
- guild,
682
- user
683
- });
684
764
  return {
685
765
  uuid,
686
- response: new GenericError(uuid).response
766
+ response: error.response
687
767
  };
688
768
  }
689
- };
690
- var GenericError = class GenericError2 extends CustomError {
769
+ const showStack = core.config.bot.errorStack;
770
+ if (showStack) logger.error(uuid, error);
771
+ else logger.error(`${uuid} | ${error.message}`);
772
+ core.effects.emit("unknownException", {
773
+ uuid,
774
+ error,
775
+ guild,
776
+ user,
777
+ metadata
778
+ });
779
+ return {
780
+ uuid,
781
+ response: new GenericError(uuid).response
782
+ };
783
+ }
784
+ __name(extractErrorResponse, "extractErrorResponse");
785
+ var GenericError = class extends CustomError {
691
786
  static {
692
787
  __name(this, "GenericError");
693
788
  }
694
789
  uuid;
790
+ developerUsername = "the developer";
695
791
  constructor(uuid) {
696
792
  super("An unknown error occurred"), this.uuid = uuid;
697
- this.response.setTitle("Error").setDescription(`An unknown error occurred. Please reach out to the developer with a way to reproduce the error and the following:
793
+ this.response.setTitle("Error").setDescription(`An unknown error occurred. Please reach out to ${this.developerUsername} with a way to reproduce the error and the following:
698
794
  ### UUID: \`${this.uuid}\``);
699
795
  }
700
796
  };
797
+ _ts_decorate2([
798
+ Envapt("DEVELOPER_DISCORD_USERNAME"),
799
+ _ts_metadata2("design:type", String)
800
+ ], GenericError.prototype, "developerUsername", void 0);
701
801
 
702
802
  // src/bot/decorators/Catchable.ts
703
803
  function Catchable(options) {
@@ -714,7 +814,7 @@ function Catchable(options) {
714
814
  if (!(error instanceof Error)) throw error;
715
815
  this.setErrored();
716
816
  if (log) console.error(error);
717
- const { response } = ErrorHandlingUtils.extractErrorResponse(error, this.core, interaction.guild, interaction.user);
817
+ const { response } = extractErrorResponse(error, this.core, interaction.guild, interaction.user, interaction);
718
818
  const res = {
719
819
  embeds: [
720
820
  response
@@ -748,17 +848,17 @@ function Catchable(options) {
748
848
  __name(Catchable, "Catchable");
749
849
 
750
850
  // src/bot/defaults/UnhandledEvent.ts
751
- function _ts_decorate2(decorators, target, key, desc) {
851
+ function _ts_decorate3(decorators, target, key, desc) {
752
852
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
753
853
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
754
854
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
755
855
  return c > 3 && r && Object.defineProperty(target, key, r), r;
756
856
  }
757
- __name(_ts_decorate2, "_ts_decorate");
758
- function _ts_metadata2(k, v) {
857
+ __name(_ts_decorate3, "_ts_decorate");
858
+ function _ts_metadata3(k, v) {
759
859
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
760
860
  }
761
- __name(_ts_metadata2, "_ts_metadata");
861
+ __name(_ts_metadata3, "_ts_metadata");
762
862
  var UnhandledEvent = class extends InteractionHandler {
763
863
  static {
764
864
  __name(this, "UnhandledEvent");
@@ -770,12 +870,32 @@ var UnhandledEvent = class extends InteractionHandler {
770
870
  });
771
871
  }
772
872
  };
773
- _ts_decorate2([
873
+ _ts_decorate3([
774
874
  Catchable(),
775
- _ts_metadata2("design:type", Function),
776
- _ts_metadata2("design:paramtypes", []),
777
- _ts_metadata2("design:returntype", Promise)
875
+ _ts_metadata3("design:type", Function),
876
+ _ts_metadata3("design:paramtypes", []),
877
+ _ts_metadata3("design:returntype", Promise)
778
878
  ], UnhandledEvent.prototype, "execute", null);
879
+ function buildSlashRoute(arg1, arg2, arg3) {
880
+ let command;
881
+ let sub;
882
+ let group;
883
+ if (typeof arg1 === "string") {
884
+ command = arg1;
885
+ sub = arg2;
886
+ group = arg3;
887
+ } else if (arg1 instanceof ChatInputCommandInteraction || arg1 instanceof AutocompleteInteraction) {
888
+ command = arg1.commandName;
889
+ group = arg1.options.getSubcommandGroup(false) ?? void 0;
890
+ sub = arg1.options.getSubcommand(false) ?? void 0;
891
+ } else {
892
+ throw new TypeError("Invalid argument passed to buildSlashRoute");
893
+ }
894
+ if (sub && group) return `${command}/${group}/${sub}`;
895
+ if (sub) return `${command}/${sub}`;
896
+ return command;
897
+ }
898
+ __name(buildSlashRoute, "buildSlashRoute");
779
899
 
780
900
  // src/bot/controllers/InteractionController.ts
781
901
  var InteractionController = class {
@@ -809,40 +929,66 @@ var InteractionController = class {
809
929
  if (this.isInitialized) return;
810
930
  this.isInitialized = true;
811
931
  const handlersDir = this.core.config.bot.interactions.path;
812
- this.logger.info(chalk2.bold(handlersDir));
932
+ this.logger.info(chalk4.bold(handlersDir));
933
+ const middlewareDir = this.core.config.bot.interactions.middlewares;
934
+ if (middlewareDir) {
935
+ this.logger.info(`${chalk4.bold(middlewareDir)} ${chalk4.gray("(middlewares)")}`);
936
+ await this.loadMiddlewares(middlewareDir);
937
+ }
813
938
  await this.loadHandlers(handlersDir);
814
939
  this.attachToClient();
815
- this.logger.info(`${chalk2.bold.green("Loaded interaction handlers:")}`);
816
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.slashMap.size)} slash commands`);
817
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.buttonMap.size)} buttons`);
818
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.modalMap.size)} modals`);
819
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.stringSelectMap.size)} string selects`);
820
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.userSelectMap.size)} user selects`);
821
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.roleSelectMap.size)} role selects`);
822
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.channelSelectMap.size)} channel selects`);
823
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
824
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
825
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.userContextMenuMap.size)} user context menus`);
826
- this.logger.info(`\u2192 ${chalk2.magenta.bold(this.autocompleteMap.size)} autocomplete`);
940
+ this.logger.info(`${chalk4.bold.green("Loaded interaction handlers:")}`);
941
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.middlewares.length)} middlewares`);
942
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.slashMap.size)} slash commands`);
943
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.buttonMap.size)} buttons`);
944
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.modalMap.size)} modals`);
945
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.stringSelectMap.size)} string selects`);
946
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.userSelectMap.size)} user selects`);
947
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.roleSelectMap.size)} role selects`);
948
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.channelSelectMap.size)} channel selects`);
949
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
950
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
951
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.userContextMenuMap.size)} user context menus`);
952
+ this.logger.info(`\u2192 ${chalk4.magenta.bold(this.autocompleteMap.size)} autocomplete`);
827
953
  }
828
954
  async loadHandlers(dir) {
829
955
  await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
830
956
  for (const val of Object.values(imported)) {
831
- if (this.isHandlerClass(val)) {
832
- this.registerHandler(val);
833
- this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
834
- }
957
+ if (!this.isHandlerClass(val)) continue;
958
+ this.registerHandler(val);
959
+ this.logger.info(`${chalk4.italic("Registered")} ${chalk4.bold.yellow(val.name)} from ${chalk4.gray(relativePath)}`);
835
960
  }
836
961
  }, this.logger);
837
962
  }
963
+ async loadMiddlewares(dir) {
964
+ await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
965
+ for (const val of Object.values(imported)) {
966
+ if (!this.isMiddlewareClass(val)) continue;
967
+ const metadata = Reflect.getMetadata(MiddlewareMetadataKey, val);
968
+ if (metadata?.type !== MiddlewareType.Interaction) continue;
969
+ this.registerMiddleware(val, metadata, relativePath);
970
+ }
971
+ }, this.logger);
972
+ }
973
+ registerMiddleware(middlewareCtor, metadata, relativePath) {
974
+ const alreadyRegistered = this.middlewares.some((entry) => entry.ctor === middlewareCtor);
975
+ if (alreadyRegistered) return;
976
+ this.middlewares.push({
977
+ ctor: middlewareCtor,
978
+ priority: metadata.priority
979
+ });
980
+ this.middlewares.sort((a, b) => a.priority - b.priority);
981
+ this.logger.info(`${chalk4.italic("Registered middleware")} ${chalk4.bold.yellow(middlewareCtor.name)} ${chalk4.gray(`(priority ${metadata.priority})`)} from ${chalk4.gray(relativePath)}`);
982
+ }
838
983
  isHandlerClass(obj) {
839
984
  if (typeof obj !== "function") return false;
840
985
  return obj.prototype instanceof InteractionHandler && Reflect.hasMetadata(InteractionMetadataKey, obj) || obj.prototype instanceof AutocompleteHandler && Reflect.hasMetadata(InteractionMetadataKey, obj);
841
986
  }
987
+ isMiddlewareClass(obj) {
988
+ if (typeof obj !== "function") return false;
989
+ return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
990
+ }
842
991
  registerHandler(handlerClass) {
843
- const areRoutes = /* @__PURE__ */ __name((routes) => {
844
- return Array.isArray(routes) && routes.every((r) => typeof r === "string");
845
- }, "areRoutes");
846
992
  const routeTypes = [
847
993
  [
848
994
  InteractionRoutes.Slash,
@@ -899,7 +1045,7 @@ var InteractionController = class {
899
1045
  attachToClient() {
900
1046
  this.core.bot.client.on(Events.InteractionCreate, (interaction) => {
901
1047
  this.handleInteraction(interaction).catch((err) => {
902
- this.logger.error(`[${chalk2.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
1048
+ this.logger.error(`[${chalk4.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
903
1049
  });
904
1050
  });
905
1051
  }
@@ -921,17 +1067,19 @@ var InteractionController = class {
921
1067
  async processInteraction(interaction, extractKey, getHandler, args) {
922
1068
  const key = extractKey(interaction);
923
1069
  if (this.keysToIgnore.has(key)) return;
924
- for (const MiddlewareCtor of this.middlewares) {
925
- const middleware = new MiddlewareCtor(interaction, this.core, args);
1070
+ for (const { ctor } of this.middlewares) {
1071
+ const middleware = new ctor(interaction, this.core, args);
1072
+ if (middleware.hasChecks()) await middleware.runChecks();
1073
+ if (middleware.shouldBreak() || middleware.hasErrors()) return;
926
1074
  await middleware.execute();
927
- if (middleware.hasErrors()) return;
1075
+ if (middleware.shouldBreak() || middleware.hasErrors()) return;
928
1076
  }
929
1077
  let HandlerCtor = getHandler(key);
930
1078
  if (!HandlerCtor) {
931
- this.logger.warn(`No handler found for key ${chalk2.bold.cyan(key)}. Falling back to UnhandledEvent.`);
1079
+ this.logger.warn(`No handler found for key ${chalk4.bold.cyan(key)}. Falling back to UnhandledEvent.`);
932
1080
  HandlerCtor = UnhandledEvent;
933
1081
  }
934
- this.logger.debug(`Processing ${chalk2.bold.green(key)} with ${chalk2.gray(HandlerCtor.name)}`);
1082
+ this.logger.debug(`Processing ${chalk4.bold.green(key)} with ${chalk4.gray(HandlerCtor.name)}`);
935
1083
  const handler = new HandlerCtor(interaction, this.core, args);
936
1084
  if (handler.hasChecks()) await handler.runChecks();
937
1085
  if (handler.shouldBreak()) return;
@@ -978,7 +1126,7 @@ var InteractionController = class {
978
1126
  }
979
1127
  }
980
1128
  async handleSlashCommand(interaction) {
981
- const route = this.buildSlashRoute(interaction);
1129
+ const route = buildSlashRoute(interaction);
982
1130
  await this.processInteraction(interaction, () => route, (key) => this.slashMap.get(key));
983
1131
  }
984
1132
  async handleButton(interaction) {
@@ -1009,26 +1157,11 @@ var InteractionController = class {
1009
1157
  await this.processInteraction(interaction, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
1010
1158
  }
1011
1159
  async handleAutocomplete(interaction) {
1012
- const route = this.buildSlashRoute(interaction);
1160
+ const route = buildSlashRoute(interaction);
1013
1161
  const focused = interaction.options.getFocused(true);
1014
1162
  const autocompleteKey = `${route}:${focused.name}`;
1015
1163
  await this.processInteraction(interaction, () => autocompleteKey, (key) => this.autocompleteMap.get(key));
1016
1164
  }
1017
- // Build the route from commandName, subcommandGroup, subcommand
1018
- buildSlashRoute(interaction) {
1019
- const command = interaction.commandName;
1020
- const group = interaction.options.getSubcommandGroup(false);
1021
- const sub = interaction.options.getSubcommand(false);
1022
- let route = command;
1023
- if (group && sub) {
1024
- route = `${route}/${group}/${sub}`;
1025
- } else if (group) {
1026
- route = `${route}/${group}`;
1027
- } else if (sub) {
1028
- route = `${route}/${sub}`;
1029
- }
1030
- return route;
1031
- }
1032
1165
  };
1033
1166
  var EmojiInjector = class {
1034
1167
  static {
@@ -1041,7 +1174,7 @@ var EmojiInjector = class {
1041
1174
  }
1042
1175
  async init() {
1043
1176
  if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
1044
- this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold("0")} emojis`);
1177
+ this.logger.info(`${chalk4.bold.green("Loaded")}: ${chalk4.magenta.bold("0")} emojis`);
1045
1178
  return;
1046
1179
  }
1047
1180
  const configEmojis = this.core.config.bot.emojis;
@@ -1052,25 +1185,25 @@ var EmojiInjector = class {
1052
1185
  if (emoji) {
1053
1186
  configEmojis[key] = `<${emoji.identifier}>`;
1054
1187
  foundCount++;
1055
- this.logger.debug(`${chalk2.bold.green("Found")}: ${chalk2.magenta.bold(emojiName)} (${emoji.id})`);
1188
+ this.logger.debug(`${chalk4.bold.green("Found")}: ${chalk4.magenta.bold(emojiName)} (${emoji.id})`);
1056
1189
  }
1057
1190
  });
1058
- this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold(foundCount)} emojis`);
1191
+ this.logger.info(`${chalk4.bold.green("Loaded")}: ${chalk4.magenta.bold(foundCount)} emojis`);
1059
1192
  }
1060
1193
  };
1061
1194
 
1062
1195
  // src/bot/Bot.ts
1063
- function _ts_decorate3(decorators, target, key, desc) {
1196
+ function _ts_decorate4(decorators, target, key, desc) {
1064
1197
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1065
1198
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1066
1199
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1067
1200
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1068
1201
  }
1069
- __name(_ts_decorate3, "_ts_decorate");
1070
- function _ts_metadata3(k, v) {
1202
+ __name(_ts_decorate4, "_ts_decorate");
1203
+ function _ts_metadata4(k, v) {
1071
1204
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1072
1205
  }
1073
- __name(_ts_metadata3, "_ts_metadata");
1206
+ __name(_ts_metadata4, "_ts_metadata");
1074
1207
  var Bot = class extends Plugin {
1075
1208
  static {
1076
1209
  __name(this, "Bot");
@@ -1083,10 +1216,6 @@ var Bot = class extends Plugin {
1083
1216
  events;
1084
1217
  commands;
1085
1218
  emojiInjector;
1086
- /**
1087
- * @param core - Seedcord core instance
1088
- * @internal
1089
- */
1090
1219
  constructor(core) {
1091
1220
  super(core), this.core = core;
1092
1221
  this._client = new Client(core.config.bot.clientOptions);
@@ -1097,9 +1226,9 @@ var Bot = class extends Plugin {
1097
1226
  this.core.shutdown.addTask(ShutdownPhase.DiscordCleanup, "stop-bot", async () => await this.stop());
1098
1227
  }
1099
1228
  /**
1100
- * Initializes Discord client and all controllers
1101
- * @internal
1102
- */
1229
+ * Initializes Discord client and all controllers
1230
+ * @internal
1231
+ */
1103
1232
  async init() {
1104
1233
  if (this.isInitialized) {
1105
1234
  return;
@@ -1113,40 +1242,40 @@ var Bot = class extends Plugin {
1113
1242
  await this.emojiInjector.init();
1114
1243
  }
1115
1244
  /**
1116
- * Stops the bot and cleans up connections
1117
- * @internal
1118
- */
1245
+ * Stops the bot and cleans up connections
1246
+ * @internal
1247
+ */
1119
1248
  async stop() {
1120
1249
  this._client.removeAllListeners();
1121
1250
  await this.logout();
1122
1251
  }
1123
1252
  /**
1124
- * Logs the bot into Discord using the configured token
1125
- */
1253
+ * Logs the bot into Discord using the configured token
1254
+ */
1126
1255
  async login() {
1127
1256
  await this._client.login(this.botToken);
1128
- this.logger.info(`Logged in as ${chalk2.bold.magenta(this._client.user?.username)}!`);
1257
+ this.logger.info(`Logged in as ${chalk4.bold.magenta(this._client.user?.username)}!`);
1129
1258
  return this;
1130
1259
  }
1131
1260
  /**
1132
- * Logs out and destroys the Discord client connection
1133
- */
1261
+ * Logs out and destroys the Discord client connection
1262
+ */
1134
1263
  async logout() {
1135
1264
  await this._client.destroy();
1136
- this.logger.info(chalk2.bold.red("Logged out of Discord!"));
1265
+ this.logger.info(chalk4.bold.red("Logged out of Discord!"));
1137
1266
  }
1138
1267
  get client() {
1139
1268
  return this._client;
1140
1269
  }
1141
1270
  };
1142
- _ts_decorate3([
1271
+ _ts_decorate4([
1143
1272
  Envapt("DISCORD_BOT_TOKEN", {
1144
1273
  converter(raw, _fallback) {
1145
1274
  if (typeof raw !== "string") throw new Error("Missing DISCORD_BOT_TOKEN");
1146
1275
  return raw;
1147
1276
  }
1148
1277
  }),
1149
- _ts_metadata3("design:type", String)
1278
+ _ts_metadata4("design:type", String)
1150
1279
  ], Bot.prototype, "botToken", void 0);
1151
1280
 
1152
1281
  // src/effects/decorators/RegisterEffect.ts
@@ -1166,11 +1295,11 @@ var EffectsHandler = class {
1166
1295
  data;
1167
1296
  core;
1168
1297
  /**
1169
- * Creates a new effects handler instance.
1170
- *
1171
- * @param data - The effect event data
1172
- * @param core - The core framework instance
1173
- */
1298
+ * Creates a new effects handler instance.
1299
+ *
1300
+ * @param data - The effect event data
1301
+ * @param core - The core framework instance
1302
+ */
1174
1303
  constructor(data, core) {
1175
1304
  this.data = data;
1176
1305
  this.core = core;
@@ -1190,35 +1319,55 @@ var WebhookLog = class extends EffectsHandler {
1190
1319
  };
1191
1320
 
1192
1321
  // src/effects/default/UnknownException.ts
1193
- function _ts_decorate4(decorators, target, key, desc) {
1322
+ function _ts_decorate5(decorators, target, key, desc) {
1194
1323
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1195
1324
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1196
1325
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1197
1326
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1198
1327
  }
1199
- __name(_ts_decorate4, "_ts_decorate");
1200
- function _ts_metadata4(k, v) {
1328
+ __name(_ts_decorate5, "_ts_decorate");
1329
+ function _ts_metadata5(k, v) {
1201
1330
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1202
1331
  }
1203
- __name(_ts_metadata4, "_ts_metadata");
1332
+ __name(_ts_metadata5, "_ts_metadata");
1204
1333
  var UnknownException = class _UnknownException extends WebhookLog {
1205
1334
  static {
1206
1335
  __name(this, "UnknownException");
1207
1336
  }
1337
+ static logger = new Logger("Effect: UnknownException");
1208
1338
  webhook = new WebhookClient({
1209
1339
  url: _UnknownException.unknownExceptionWebhookUrl
1210
1340
  });
1211
1341
  async execute() {
1212
- await this.webhook.send({
1213
- username: "Unknown Exception",
1214
- avatarURL: "https://cdn.discordapp.com/attachments/1351446034827579466/1351446912947191830/warning-2.png",
1215
- embeds: [
1216
- new UnhandledErrorEmbed(this.data).component
1217
- ]
1342
+ const metadataFile = this.prepareMetadataFile();
1343
+ try {
1344
+ await this.webhook.send({
1345
+ flags: "IsComponentsV2",
1346
+ withComponents: true,
1347
+ username: "Unknown Exception",
1348
+ avatarURL: "https://cdn.discordapp.com/attachments/1351446034827579466/1351446912947191830/warning-2.png",
1349
+ components: [
1350
+ new UnhandledErrorContainer(this.data).component
1351
+ ],
1352
+ files: metadataFile ? [
1353
+ metadataFile
1354
+ ] : []
1355
+ });
1356
+ } catch (error) {
1357
+ _UnknownException.logger.error("Failed to send unknown exception webhook", error);
1358
+ }
1359
+ }
1360
+ prepareMetadataFile() {
1361
+ const { metadata } = this.data;
1362
+ if (!metadata) return null;
1363
+ const content = filterCirculars(metadata);
1364
+ return new AttachmentBuilder(Buffer.from(JSON.stringify(content, void 0, 2), "utf-8"), {
1365
+ name: "metadata.json",
1366
+ description: "Metadata associated with the error"
1218
1367
  });
1219
1368
  }
1220
1369
  };
1221
- _ts_decorate4([
1370
+ _ts_decorate5([
1222
1371
  Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
1223
1372
  converter(raw, _fallback) {
1224
1373
  if (!raw) throw new Error("Missing UNKNOWN_EXCEPTION_WEBHOOK_URL");
@@ -1226,27 +1375,38 @@ _ts_decorate4([
1226
1375
  return raw;
1227
1376
  }
1228
1377
  }),
1229
- _ts_metadata4("design:type", String)
1378
+ _ts_metadata5("design:type", String)
1230
1379
  ], UnknownException, "unknownExceptionWebhookUrl", void 0);
1231
- UnknownException = _ts_decorate4([
1380
+ UnknownException = _ts_decorate5([
1232
1381
  RegisterEffect("unknownException")
1233
1382
  ], UnknownException);
1234
- var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
1383
+ var DefaultSeparator = class DefaultSeparator2 extends BuilderComponent {
1235
1384
  static {
1236
- __name(this, "UnhandledErrorEmbed");
1385
+ __name(this, "DefaultSeparator");
1386
+ }
1387
+ constructor() {
1388
+ super("separator");
1389
+ this.instance.setSpacing(SeparatorSpacingSize.Small).setDivider(true);
1390
+ }
1391
+ };
1392
+ var UnhandledErrorContainer = class UnhandledErrorContainer2 extends BuilderComponent {
1393
+ static {
1394
+ __name(this, "UnhandledErrorContainer");
1237
1395
  }
1238
1396
  constructor(data) {
1239
- super("embed");
1240
- const { uuid, error, guild, user } = data;
1241
- this.instance.setTitle(`An unknown exception was thrown`).setColor("#ef4860").setDescription(`**Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`
1397
+ super("container");
1398
+ const { uuid, error, guild, user, metadata } = data;
1399
+ this.instance.addTextDisplayComponents((text) => text.setContent(`### An unknown exception was thrown
1400
+ **Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`
1242
1401
  **Guild Name:** ${guild?.name ?? "Not used in a guild"}
1243
1402
  **User ID:** \`${user?.id ?? "Missing user info"}\`
1244
1403
  **Username:** ${user?.username ?? "Missing user info"}
1245
- ### UUID: \`${uuid}\`
1246
- \`\`\`${error.stack}\`\`\``);
1247
- this.setTimestampsIfAvailable(error);
1404
+ `)).addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### UUID \`${uuid}\`
1405
+ \`\`\`${error.stack}\`\`\``));
1406
+ this.addTimestampsIfAvailable(error);
1407
+ this.addMetadataIfAvailable(metadata);
1248
1408
  }
1249
- setTimestampsIfAvailable(error) {
1409
+ addTimestampsIfAvailable(error) {
1250
1410
  if (!(error instanceof DiscordAPIError)) return;
1251
1411
  const now = Date.now();
1252
1412
  const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
@@ -1255,15 +1415,14 @@ var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
1255
1415
  const diff = now - interactionTs;
1256
1416
  const seconds = Math.floor(diff / 1e3);
1257
1417
  const millis = diff % 1e3;
1258
- this.instance.addFields([
1259
- {
1260
- name: "Timestamps",
1261
- value: `- **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})
1418
+ this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### Timestamps
1419
+ - **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})
1262
1420
  - **\`Error logged \` :** ${new Date(now).toISOString()} (${now})
1263
- - **\`Offset \` :** ${seconds}s ${millis}ms`,
1264
- inline: true
1265
- }
1266
- ]);
1421
+ - **\`Offset \` :** ${seconds}s ${millis}ms`));
1422
+ }
1423
+ addMetadataIfAvailable(metadata) {
1424
+ if (!metadata) return;
1425
+ this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
1267
1426
  }
1268
1427
  };
1269
1428
  var EffectsEmitter = class {
@@ -1272,37 +1431,37 @@ var EffectsEmitter = class {
1272
1431
  }
1273
1432
  emitter = new EventEmitter();
1274
1433
  /**
1275
- * Registers a listener for the specified side effect.
1276
- *
1277
- * @typeParam KeyOfEffects - The side effect name type
1278
- * @param event - The side effect name to listen for
1279
- * @param listener - Function to call when the event is emitted
1280
- * @returns This EffectsEmitter instance for chaining
1281
- */
1434
+ * Registers a listener for the specified side effect.
1435
+ *
1436
+ * @typeParam KeyOfEffects - The side effect name type
1437
+ * @param event - The side effect name to listen for
1438
+ * @param listener - Function to call when the event is emitted
1439
+ * @returns This EffectsEmitter instance for chaining
1440
+ */
1282
1441
  on(event, listener) {
1283
1442
  this.emitter.on(event, listener);
1284
1443
  return this;
1285
1444
  }
1286
1445
  /**
1287
- * Registers a one-time listener for the specified side effect.
1288
- *
1289
- * @typeParam KeyOfEffects - The side effect name type
1290
- * @param event - The side effect name to listen for once
1291
- * @param listener - Function to call when the event is emitted
1292
- * @returns This EffectsEmitter instance for chaining
1293
- */
1446
+ * Registers a one-time listener for the specified side effect.
1447
+ *
1448
+ * @typeParam KeyOfEffects - The side effect name type
1449
+ * @param event - The side effect name to listen for once
1450
+ * @param listener - Function to call when the event is emitted
1451
+ * @returns This EffectsEmitter instance for chaining
1452
+ */
1294
1453
  once(event, listener) {
1295
1454
  this.emitter.once(event, listener);
1296
1455
  return this;
1297
1456
  }
1298
1457
  /**
1299
- * Emits a side effect with the provided data.
1300
- *
1301
- * @typeParam KeyOfEffects - The side effect name type
1302
- * @param event - The side effect name to emit
1303
- * @param data - The data to pass to registered listeners
1304
- * @returns True if the event had listeners, false otherwise
1305
- */
1458
+ * Emits a side effect with the provided data.
1459
+ *
1460
+ * @typeParam KeyOfEffects - The side effect name type
1461
+ * @param event - The side effect name to emit
1462
+ * @param data - The data to pass to registered listeners
1463
+ * @returns True if the event had listeners, false otherwise
1464
+ */
1306
1465
  emit(event, data) {
1307
1466
  return this.emitter.emit(event, data);
1308
1467
  }
@@ -1325,12 +1484,12 @@ var EffectsRegistry = class extends Plugin {
1325
1484
  if (this.isInitialized) return;
1326
1485
  this.isInitialized = true;
1327
1486
  const effectsDir = this.core.config.effects.path;
1328
- this.logger.info(chalk2.bold(effectsDir));
1487
+ this.logger.info(chalk4.bold(effectsDir));
1329
1488
  this.registerEffect("unknownException", UnknownException);
1330
1489
  await this.loadEffects(effectsDir);
1331
1490
  this.attachEffects();
1332
1491
  const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1333
- this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.bold.magenta(totalEffects)} side effects`);
1492
+ this.logger.info(`${chalk4.bold.green("Loaded")}: ${chalk4.bold.magenta(totalEffects)} side effects`);
1334
1493
  }
1335
1494
  async loadEffects(dir) {
1336
1495
  await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
@@ -1340,7 +1499,7 @@ var EffectsRegistry = class extends Plugin {
1340
1499
  const effectName = Reflect.getMetadata(EffectMetadataKey, val);
1341
1500
  if (effectName) {
1342
1501
  this.registerEffect(effectName, val);
1343
- this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
1502
+ this.logger.info(`${chalk4.italic("Registered")} ${chalk4.bold.yellow(val.name)} from ${chalk4.gray(relativePath)}`);
1344
1503
  }
1345
1504
  }
1346
1505
  }
@@ -1395,11 +1554,11 @@ var Seedcord = class _Seedcord extends Pluggable {
1395
1554
  /** @see {@link HealthCheck} */
1396
1555
  healthCheck;
1397
1556
  /**
1398
- * Creates a new Seedcord instance
1399
- *
1400
- * @param config - Bot configuration including paths and Discord client options
1401
- * @throws An {@link Error} When attempting to create multiple instances (singleton)
1402
- */
1557
+ * Creates a new Seedcord instance
1558
+ *
1559
+ * @param config - Bot configuration including paths and Discord client options
1560
+ * @throws An {@link Error} When attempting to create multiple instances (singleton)
1561
+ */
1403
1562
  constructor(config) {
1404
1563
  const shutdown = new CoordinatedShutdown();
1405
1564
  const startup = new CoordinatedStartup();
@@ -1416,31 +1575,31 @@ var Seedcord = class _Seedcord extends Pluggable {
1416
1575
  this.registerStartupTasks();
1417
1576
  }
1418
1577
  /**
1419
- * Registers default startup tasks
1420
- * @internal
1421
- */
1578
+ * Registers default startup tasks
1579
+ * @internal
1580
+ */
1422
1581
  registerStartupTasks() {
1423
1582
  this.startup.addTask(StartupPhase.Configuration, "Effect Initialization", async () => {
1424
- this.effects.logger.info(chalk2.bold("Initializing"));
1583
+ this.effects.logger.info(chalk4.bold("Initializing"));
1425
1584
  await this.effects.init();
1426
- this.effects.logger.info(chalk2.bold("Initialized"));
1585
+ this.effects.logger.info(chalk4.bold("Initialized"));
1427
1586
  });
1428
1587
  this.startup.addTask(StartupPhase.Instantiation, "Bot Initialization", async () => {
1429
- this.bot.logger.info(chalk2.bold("Initializing"));
1588
+ this.bot.logger.info(chalk4.bold("Initializing"));
1430
1589
  await this.bot.init();
1431
- this.bot.logger.info(chalk2.bold("Initialized"));
1590
+ this.bot.logger.info(chalk4.bold("Initialized"));
1432
1591
  });
1433
1592
  this.startup.addTask(StartupPhase.Ready, "Health Check", async () => {
1434
- this.healthCheck.logger.info(chalk2.bold("Initializing"));
1593
+ this.healthCheck.logger.info(chalk4.bold("Initializing"));
1435
1594
  await this.healthCheck.init();
1436
- this.healthCheck.logger.info(chalk2.bold("Initialized"));
1595
+ this.healthCheck.logger.info(chalk4.bold("Initialized"));
1437
1596
  });
1438
1597
  }
1439
1598
  /**
1440
- * Starts the bot and runs all initialization tasks
1441
- *
1442
- * @returns This Seedcord instance when fully initialized
1443
- */
1599
+ * Starts the bot and runs all initialization tasks
1600
+ *
1601
+ * @returns This Seedcord instance when fully initialized
1602
+ */
1444
1603
  async start() {
1445
1604
  await super.init();
1446
1605
  return this;
@@ -1470,7 +1629,7 @@ function EventCatchable(log) {
1470
1629
  this.getEvent()
1471
1630
  ];
1472
1631
  const msg = eventArgs.find((x) => x instanceof Message);
1473
- const { response } = ErrorHandlingUtils.extractErrorResponse(err, this.core, msg?.guild ?? null, msg?.author ?? null);
1632
+ const { response } = extractErrorResponse(err, this.core, msg?.guild ?? null, msg?.author ?? null, eventArgs);
1474
1633
  if (!msg) return;
1475
1634
  await msg.reply({
1476
1635
  embeds: [
@@ -1483,6 +1642,232 @@ function EventCatchable(log) {
1483
1642
  };
1484
1643
  }
1485
1644
  __name(EventCatchable, "EventCatchable");
1645
+
1646
+ // src/bot/errors/Channels.ts
1647
+ var ChannelNotFoundError = class extends CustomError {
1648
+ static {
1649
+ __name(this, "ChannelNotFoundError");
1650
+ }
1651
+ channelId;
1652
+ /**
1653
+ * Creates a new ChannelNotFoundError.
1654
+ *
1655
+ * @param message - The error message
1656
+ * @param channelId - The ID of the channel that could not be found
1657
+ */
1658
+ constructor(message, channelId) {
1659
+ super(message), this.channelId = channelId;
1660
+ this.response.setDescription(`Channel with ID \`${this.channelId}\` not found.`);
1661
+ }
1662
+ };
1663
+ var CannotSendEmbedsError = class extends CustomError {
1664
+ static {
1665
+ __name(this, "CannotSendEmbedsError");
1666
+ }
1667
+ channelId;
1668
+ /**
1669
+ * Creates a new CannotSendEmbedsError.
1670
+ *
1671
+ * @param message - The error message
1672
+ * @param channelId - The ID of the channel where embeds cannot be sent
1673
+ */
1674
+ constructor(message, channelId) {
1675
+ super(message), this.channelId = channelId;
1676
+ this.response.setDescription(`Cannot send embeds in <#${this.channelId}>.
1677
+
1678
+ Please ensure I have the following permissions:
1679
+ \u2022 View Channel
1680
+ \u2022 Send Messages
1681
+ \u2022 Embed Links
1682
+ \u2022 Attach Files
1683
+ \u2022 Read Message History
1684
+ \u2022 Use External Emojis
1685
+ `);
1686
+ }
1687
+ };
1688
+ var CouldNotFindChannel = class extends CustomError {
1689
+ static {
1690
+ __name(this, "CouldNotFindChannel");
1691
+ }
1692
+ channelId;
1693
+ /**
1694
+ * Creates a new CouldNotFindChannel error.
1695
+ *
1696
+ * @param message - The error message
1697
+ * @param channelId - The ID of the channel that could not be found
1698
+ */
1699
+ constructor(message, channelId) {
1700
+ super(message), this.channelId = channelId;
1701
+ this.response.setDescription(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`);
1702
+ }
1703
+ };
1704
+ var ChannelNotTextChannel = class extends CustomError {
1705
+ static {
1706
+ __name(this, "ChannelNotTextChannel");
1707
+ }
1708
+ channelId;
1709
+ /**
1710
+ * Creates a new ChannelNotTextChannel error.
1711
+ *
1712
+ * @param message - The error message
1713
+ * @param channelId - The ID of the channel that is not a text channel
1714
+ */
1715
+ constructor(message, channelId) {
1716
+ super(message), this.channelId = channelId;
1717
+ this.response.setDescription(`Channel with ID \`${this.channelId}\` is not a text channel.`);
1718
+ }
1719
+ };
1720
+ var MissingPermissions = class extends CustomError {
1721
+ static {
1722
+ __name(this, "MissingPermissions");
1723
+ }
1724
+ missingPerms;
1725
+ roleOrChannel;
1726
+ /**
1727
+ * Creates a new BotMissingPermissionsError.
1728
+ *
1729
+ * @param message - The error message
1730
+ * @param missingPerms - Array of missing permission names
1731
+ * @param roleOrChannel - The role or channel where permissions are missing
1732
+ */
1733
+ constructor(message, missingPerms, roleOrChannel) {
1734
+ super(message), this.missingPerms = missingPerms, this.roleOrChannel = roleOrChannel;
1735
+ const missing = this.missingPerms.map((perm) => `\u2022 ${perm}`).join("\n");
1736
+ const errorSubtext = this.roleOrChannel instanceof Role ? `My role, <@&${this.roleOrChannel.id}>, is missing the following permissions:` : `I am missing the following permissions in <#${this.roleOrChannel.id}>:`;
1737
+ this.response.setDescription(`${errorSubtext}
1738
+
1739
+ Please ensure I have the following missing permission(s):
1740
+ ${missing}`);
1741
+ }
1742
+ };
1743
+ var RoleHigherThanMe = class extends CustomError {
1744
+ static {
1745
+ __name(this, "RoleHigherThanMe");
1746
+ }
1747
+ role;
1748
+ botRole;
1749
+ /**
1750
+ * Creates a new RoleHigherThanMe error.
1751
+ *
1752
+ * @param message - The error message
1753
+ */
1754
+ constructor(message, role, botRole) {
1755
+ super(message), this.role = role, this.botRole = botRole;
1756
+ this.response.setDescription(`I cannot assign a role that is higher than me.
1757
+
1758
+ The role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`);
1759
+ }
1760
+ };
1761
+ var CannotAssignBotRole = class extends CustomError {
1762
+ static {
1763
+ __name(this, "CannotAssignBotRole");
1764
+ }
1765
+ /**
1766
+ * Creates a new CannotAssignBotRole error.
1767
+ *
1768
+ * @param message - The error message
1769
+ */
1770
+ constructor(message = "I cannot assign a managed role.") {
1771
+ super(message);
1772
+ this.response.setDescription("I cannot assign a managed role.");
1773
+ }
1774
+ };
1775
+ var RoleDoesNotExist = class extends CustomError {
1776
+ static {
1777
+ __name(this, "RoleDoesNotExist");
1778
+ }
1779
+ roleId;
1780
+ /**
1781
+ * Creates a new RoleDoesNotExist error.
1782
+ *
1783
+ * @param message - The error message
1784
+ * @param roleId - The ID of the role that doesn't exist
1785
+ */
1786
+ constructor(message, roleId) {
1787
+ super(message), this.roleId = roleId;
1788
+ this.response.setDescription(`The role with ID \`${this.roleId}\` does not exist.`);
1789
+ }
1790
+ };
1791
+ var HasDangerousPermissions = class extends CustomError {
1792
+ static {
1793
+ __name(this, "HasDangerousPermissions");
1794
+ }
1795
+ role;
1796
+ dangerousPerms;
1797
+ /**
1798
+ * Creates a new HasDangerousPermissions error.
1799
+ *
1800
+ * @param message - The error message
1801
+ * @param role - The role with dangerous permissions
1802
+ * @param dangerousPerms - Array of dangerous permission names
1803
+ */
1804
+ constructor(message, role, dangerousPerms) {
1805
+ super(message), this.role = role, this.dangerousPerms = dangerousPerms;
1806
+ const dangerous = this.dangerousPerms.map((perm) => `\u2022 ${perm}`).join("\n");
1807
+ this.response.setDescription(`The role <@&${this.role.id}> has the following dangerous permissions:
1808
+
1809
+ Please ensure the following dangerous permission(s) are not enabled:
1810
+ ${dangerous}`);
1811
+ }
1812
+ };
1813
+
1814
+ // src/bot/errors/User.ts
1815
+ var UserNotInGuild = class extends CustomError {
1816
+ static {
1817
+ __name(this, "UserNotInGuild");
1818
+ }
1819
+ /**
1820
+ * Creates a new UserNotInGuild error.
1821
+ *
1822
+ * @param message - The error message
1823
+ */
1824
+ constructor(message = "User is not in the guild.") {
1825
+ super(message);
1826
+ this.response.setDescription("User is not in the guild.");
1827
+ }
1828
+ };
1829
+ var UserNotFound = class extends CustomError {
1830
+ static {
1831
+ __name(this, "UserNotFound");
1832
+ }
1833
+ userArg;
1834
+ /**
1835
+ * Creates a new UserNotFound error.
1836
+ *
1837
+ * @param userArg - The user argument that could not be resolved
1838
+ */
1839
+ constructor(userArg) {
1840
+ super(`User not found: ${userArg}`), this.userArg = userArg;
1841
+ this.response.setTitle("User Not Found").setDescription(`User probably doesn't exist or was deleted.
1842
+ **User Argument:** \`${this.userArg}\`
1843
+ Please check the user ID and try again. Only pass valid user IDs as the argument.`);
1844
+ }
1845
+ };
1846
+ async function fetchText(client, channelId) {
1847
+ if (channelId instanceof TextChannel) {
1848
+ return channelId;
1849
+ }
1850
+ let channel = client.channels.cache.get(channelId);
1851
+ if (!channel) {
1852
+ try {
1853
+ channel = await client.channels.fetch(channelId);
1854
+ } catch {
1855
+ throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
1856
+ }
1857
+ }
1858
+ if (channel?.isTextBased()) {
1859
+ return channel;
1860
+ }
1861
+ throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
1862
+ }
1863
+ __name(fetchText, "fetchText");
1864
+
1865
+ // src/bot/utilities/channels/sendInText.ts
1866
+ async function sendInText(client, channelId, message) {
1867
+ const channel = await fetchText(client, channelId);
1868
+ return await channel.send(message);
1869
+ }
1870
+ __name(sendInText, "sendInText");
1486
1871
  function throwCustomError(error, message, CustomError2) {
1487
1872
  const uuid = crypto.randomUUID();
1488
1873
  Logger.Error("Throwing Custom Error", error.name);
@@ -1499,6 +1884,265 @@ function throwCustomError(error, message, CustomError2) {
1499
1884
  }
1500
1885
  __name(throwCustomError, "throwCustomError");
1501
1886
 
1502
- export { AutocompleteHandler, AutocompleteRoute, BaseErrorEmbed, Bot, BuilderComponent, ButtonRoute, Catchable, Checkable, CommandMetadataKey, ContextMenuRoute, CustomError, DatabaseError, EffectMetadataKey, EffectsEmitter, EffectsHandler, EffectsRegistry, EventCatchable, EventHandler, EventMetadataKey, InteractionHandler, InteractionMetadataKey, InteractionMiddleware, InteractionRoutes, ModalComponent, ModalRoute, Pluggable, Plugin, RegisterCommand, RegisterEffect, RegisterEvent, RowComponent, Seedcord, SelectMenuRoute, SelectMenuType, SlashRoute, WebhookLog, throwCustomError };
1887
+ // src/bot/utilities/messages/attemptSendDM.ts
1888
+ async function attemptSendDM(user, content) {
1889
+ const payload = {
1890
+ ...content.content !== void 0 && {
1891
+ content: content.content
1892
+ },
1893
+ ...content.embeds !== void 0 && {
1894
+ embeds: [
1895
+ ...content.embeds
1896
+ ]
1897
+ },
1898
+ ...content.components !== void 0 && {
1899
+ components: [
1900
+ ...content.components
1901
+ ]
1902
+ }
1903
+ };
1904
+ try {
1905
+ return await user.send(payload);
1906
+ } catch {
1907
+ return null;
1908
+ }
1909
+ }
1910
+ __name(attemptSendDM, "attemptSendDM");
1911
+ var PermissionNames = new Map(Object.entries(PermissionFlagsBits).map(([key, bit]) => [
1912
+ bit,
1913
+ prettify(key)
1914
+ ]));
1915
+ var PERM_GROUPS = {
1916
+ manage: /* @__PURE__ */ new Map([
1917
+ [
1918
+ PermissionFlagsBits.ManageChannels,
1919
+ "Manage Channels"
1920
+ ],
1921
+ [
1922
+ PermissionFlagsBits.ManageRoles,
1923
+ "Manage Roles"
1924
+ ],
1925
+ [
1926
+ PermissionFlagsBits.ManageWebhooks,
1927
+ "Manage Webhooks"
1928
+ ],
1929
+ [
1930
+ PermissionFlagsBits.ManageMessages,
1931
+ "Manage Messages"
1932
+ ],
1933
+ [
1934
+ PermissionFlagsBits.ManageNicknames,
1935
+ "Manage Nicknames"
1936
+ ]
1937
+ ]),
1938
+ embed: /* @__PURE__ */ new Map([
1939
+ [
1940
+ PermissionFlagsBits.ViewChannel,
1941
+ "View Channel"
1942
+ ],
1943
+ [
1944
+ PermissionFlagsBits.SendMessages,
1945
+ "Send Messages"
1946
+ ],
1947
+ [
1948
+ PermissionFlagsBits.EmbedLinks,
1949
+ "Embed Links"
1950
+ ],
1951
+ [
1952
+ PermissionFlagsBits.AttachFiles,
1953
+ "Attach Files"
1954
+ ],
1955
+ [
1956
+ PermissionFlagsBits.UseExternalEmojis,
1957
+ "Use External Emojis"
1958
+ ],
1959
+ [
1960
+ PermissionFlagsBits.ReadMessageHistory,
1961
+ "Read Message History"
1962
+ ]
1963
+ ]),
1964
+ others: /* @__PURE__ */ new Map([
1965
+ [
1966
+ PermissionFlagsBits.AddReactions,
1967
+ "Add Reactions"
1968
+ ],
1969
+ [
1970
+ PermissionFlagsBits.UseApplicationCommands,
1971
+ "Use Application Commands"
1972
+ ]
1973
+ ])
1974
+ };
1975
+ function checkPermissions(client, roleOrChannel, scope = "all", inverse = false) {
1976
+ let required;
1977
+ if (Array.isArray(scope)) {
1978
+ required = /* @__PURE__ */ new Map();
1979
+ for (const bit of scope) {
1980
+ const name = PermissionNames.get(bit);
1981
+ if (name) required.set(bit, name);
1982
+ }
1983
+ } else {
1984
+ switch (scope) {
1985
+ case "manage":
1986
+ required = PERM_GROUPS.manage;
1987
+ break;
1988
+ case "embed":
1989
+ required = PERM_GROUPS.embed;
1990
+ break;
1991
+ case "others":
1992
+ required = new Map([
1993
+ ...PERM_GROUPS.others,
1994
+ ...PERM_GROUPS.embed
1995
+ ]);
1996
+ break;
1997
+ default:
1998
+ required = new Map([
1999
+ ...PERM_GROUPS.manage,
2000
+ ...PERM_GROUPS.others,
2001
+ ...PERM_GROUPS.embed
2002
+ ]);
2003
+ break;
2004
+ }
2005
+ }
2006
+ let permissions;
2007
+ if (roleOrChannel instanceof Role) {
2008
+ permissions = roleOrChannel.permissions;
2009
+ } else {
2010
+ if (!client.user) throw new Error("Client user is not available");
2011
+ permissions = roleOrChannel.permissionsFor(client.user, true);
2012
+ }
2013
+ if (!permissions) {
2014
+ throw new MissingPermissions("Missing Permissions", Array.from(required.values()), roleOrChannel);
2015
+ }
2016
+ if (inverse) {
2017
+ const dangerous = Array.from(required.entries()).filter(([bit]) => permissions.has(bit, true)).map(([, name]) => name);
2018
+ if (dangerous.length > 0) {
2019
+ throw new HasDangerousPermissions("Role has dangerous permissions", roleOrChannel, dangerous);
2020
+ }
2021
+ } else {
2022
+ const missing = Array.from(required.entries()).filter(([bit]) => !permissions.has(bit, true)).map(([, name]) => name);
2023
+ if (missing.length > 0) {
2024
+ throw new MissingPermissions("Missing Permissions", missing, roleOrChannel);
2025
+ }
2026
+ }
2027
+ }
2028
+ __name(checkPermissions, "checkPermissions");
2029
+
2030
+ // src/bot/utilities/roles/getBotRole.ts
2031
+ function getBotRole(client, guild) {
2032
+ if (!client.user) throw new Error("Client user is not available");
2033
+ const botRole = guild.roles.botRoleFor(client.user);
2034
+ if (!botRole) throw new Error("Bot role not found in guild");
2035
+ return botRole;
2036
+ }
2037
+ __name(getBotRole, "getBotRole");
2038
+
2039
+ // src/bot/utilities/roles/checkBotPermissions.ts
2040
+ function checkBotPermissions(client, guildOrChannel, scope = "all", inverse = false) {
2041
+ if (guildOrChannel instanceof Guild) {
2042
+ const botRole = getBotRole(client, guildOrChannel);
2043
+ checkPermissions(client, botRole, scope, inverse);
2044
+ } else {
2045
+ checkPermissions(client, guildOrChannel, scope);
2046
+ }
2047
+ }
2048
+ __name(checkBotPermissions, "checkBotPermissions");
2049
+ async function fetchRole(clientOrGuild, roleId) {
2050
+ let role;
2051
+ if (!roleId) {
2052
+ throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
2053
+ }
2054
+ if (clientOrGuild instanceof Guild) {
2055
+ const guild = clientOrGuild;
2056
+ role = guild.roles.cache.get(roleId);
2057
+ if (!role) {
2058
+ try {
2059
+ role = await guild.roles.fetch(roleId);
2060
+ } catch {
2061
+ throw new RoleDoesNotExist("Role not found in specified guild", roleId);
2062
+ }
2063
+ }
2064
+ } else {
2065
+ const client = clientOrGuild;
2066
+ role = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find((role2) => role2);
2067
+ if (!role) {
2068
+ const guilds = client.guilds.cache;
2069
+ for (const guild of guilds.values()) {
2070
+ try {
2071
+ role = await guild.roles.fetch(roleId);
2072
+ if (role) break;
2073
+ } catch {
2074
+ continue;
2075
+ }
2076
+ }
2077
+ }
2078
+ }
2079
+ if (!role) {
2080
+ throw new RoleDoesNotExist("Role not found", roleId);
2081
+ }
2082
+ return role;
2083
+ }
2084
+ __name(fetchRole, "fetchRole");
2085
+ function hasPermsToAssign(targetRole) {
2086
+ const botRole = getBotRole(targetRole.client, targetRole.guild);
2087
+ if (targetRole.comparePositionTo(botRole) >= 0) {
2088
+ throw new RoleHigherThanMe("Role is higher than me", targetRole, botRole);
2089
+ }
2090
+ if (targetRole.managed) {
2091
+ throw new CannotAssignBotRole(`Cannot assign bot role ${targetRole.name}`);
2092
+ }
2093
+ checkBotPermissions(targetRole.client, targetRole.guild, [
2094
+ PermissionFlagsBits.ManageRoles
2095
+ ]);
2096
+ }
2097
+ __name(hasPermsToAssign, "hasPermsToAssign");
2098
+
2099
+ // src/bot/utilities/users/fetchGuildMember.ts
2100
+ async function fetchGuildMember(guild, userId) {
2101
+ let user = guild.members.cache.get(userId);
2102
+ user ??= await guild.members.fetch(userId).catch(() => {
2103
+ throw new UserNotInGuild(`User with ID ${userId} not found in guild`);
2104
+ });
2105
+ return user;
2106
+ }
2107
+ __name(fetchGuildMember, "fetchGuildMember");
2108
+
2109
+ // src/bot/utilities/users/fetchManyGuildMembers.ts
2110
+ async function fetchManyGuildMembers(guild, userIds) {
2111
+ const results = await Promise.allSettled(userIds.map((userId) => fetchGuildMember(guild, userId)));
2112
+ return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2113
+ }
2114
+ __name(fetchManyGuildMembers, "fetchManyGuildMembers");
2115
+ async function fetchUser(client, userId) {
2116
+ let user = client.users.cache.get(userId);
2117
+ user ??= await client.users.fetch(userId).catch((err) => {
2118
+ if (err instanceof DiscordAPIError && err.code === RESTJSONErrorCodes.UnknownUser) {
2119
+ throw new UserNotFound(userId);
2120
+ }
2121
+ throw err;
2122
+ });
2123
+ return user;
2124
+ }
2125
+ __name(fetchUser, "fetchUser");
2126
+
2127
+ // src/bot/utilities/users/fetchManyUsers.ts
2128
+ async function fetchManyUsers(client, userIds) {
2129
+ const results = await Promise.allSettled(userIds.map((userId) => fetchUser(client, userId)));
2130
+ return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2131
+ }
2132
+ __name(fetchManyUsers, "fetchManyUsers");
2133
+
2134
+ // src/bot/utilities/users/updateMemberRoles.ts
2135
+ async function updateMemberRoles(rolesToAdd, rolesToRemove, member) {
2136
+ const current = new Set(member.roles.cache.map((r) => r.id));
2137
+ const toAdd = new Set(rolesToAdd);
2138
+ const toRemove = new Set(rolesToRemove);
2139
+ const updated = current.union(toAdd).difference(toRemove);
2140
+ await member.roles.set([
2141
+ ...updated
2142
+ ]);
2143
+ }
2144
+ __name(updateMemberRoles, "updateMemberRoles");
2145
+
2146
+ export { AutocompleteHandler, AutocompleteRoute, BaseComponent, BaseErrorEmbed, BaseHandler, Bot, BuilderComponent, BuilderTypes, ButtonRoute, CannotAssignBotRole, CannotSendEmbedsError, Catchable, ChannelNotFoundError, ChannelNotTextChannel, Checkable, CommandMetadataKey, CommandRegistry, ContextMenuRoute, CouldNotFindChannel, CustomError, DatabaseError, EffectMetadataKey, EffectsEmitter, EffectsHandler, EffectsRegistry, EmojiInjector, EventCatchable, EventController, EventHandler, EventMetadataKey, EventMiddleware, GenericError, HasDangerousPermissions, InteractionController, InteractionHandler, InteractionMetadataKey, InteractionMiddleware, InteractionRoutes, Middleware, MiddlewareMetadataKey, MiddlewareType, MissingPermissions, ModalRoute, PERM_GROUPS, PermissionNames, Pluggable, Plugin, RegisterCommand, RegisterEffect, RegisterEvent, RoleDoesNotExist, RoleHigherThanMe, RowComponent, RowTypes, Seedcord, SelectMenuRoute, SelectMenuType, SlashRoute, UnhandledEvent, UnknownException, UserNotFound, UserNotInGuild, WebhookLog, attemptSendDM, buildSlashRoute, checkBotPermissions, checkPermissions, extractErrorResponse, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, hasPermsToAssign, sendInText, throwCustomError, updateMemberRoles };
1503
2147
  //# sourceMappingURL=index.mjs.map
1504
2148
  //# sourceMappingURL=index.mjs.map