seedcord 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2279 @@
1
+ 'use strict';
2
+
3
+ require('reflect-metadata');
4
+ var chalk6 = require('chalk');
5
+ var discord_js = require('discord.js');
6
+ var envapt = require('envapt');
7
+ var promises = require('fs/promises');
8
+ var path = require('path');
9
+ var winston = require('winston');
10
+ var crypto2 = require('crypto');
11
+ var events = require('events');
12
+ var http = require('http');
13
+ var mongoose2 = require('mongoose');
14
+
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
+
17
+ function _interopNamespace(e) {
18
+ if (e && e.__esModule) return e;
19
+ var n = Object.create(null);
20
+ if (e) {
21
+ Object.keys(e).forEach(function (k) {
22
+ if (k !== 'default') {
23
+ var d = Object.getOwnPropertyDescriptor(e, k);
24
+ Object.defineProperty(n, k, d.get ? d : {
25
+ enumerable: true,
26
+ get: function () { return e[k]; }
27
+ });
28
+ }
29
+ });
30
+ }
31
+ n.default = e;
32
+ return Object.freeze(n);
33
+ }
34
+
35
+ var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
36
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
37
+ var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
38
+ var mongoose2__default = /*#__PURE__*/_interopDefault(mongoose2);
39
+
40
+ var __defProp = Object.defineProperty;
41
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
42
+ function _ts_decorate(decorators, target, key, desc) {
43
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
44
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
45
+ 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;
46
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
47
+ }
48
+ __name(_ts_decorate, "_ts_decorate");
49
+ function _ts_metadata(k, v) {
50
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
51
+ }
52
+ __name(_ts_metadata, "_ts_metadata");
53
+ var Globals = class extends envapt.Envapter {
54
+ static {
55
+ __name(this, "Globals");
56
+ }
57
+ static botToken;
58
+ // Healthcheck
59
+ static healthCheckPort;
60
+ static healthCheckPath;
61
+ // Coordinated Shutdown
62
+ static shutdownIsEnabled;
63
+ // Unknown Exception Webhook URL
64
+ static unknownExceptionWebhookUrl;
65
+ // Variables
66
+ /**
67
+ * The default color of the bot's embeds. Can simply override by `Globals.botColor =`
68
+ */
69
+ static botColor = this.isProduction ? "#fe565a" : "#3fa045";
70
+ };
71
+ _ts_decorate([
72
+ envapt.Envapt("DISCORD_BOT_TOKEN", {
73
+ fallback: void 0
74
+ }),
75
+ _ts_metadata("design:type", String)
76
+ ], Globals, "botToken", void 0);
77
+ _ts_decorate([
78
+ envapt.Envapt("HEALTH_CHECK_PORT", {
79
+ fallback: 6956
80
+ }),
81
+ _ts_metadata("design:type", Number)
82
+ ], Globals, "healthCheckPort", void 0);
83
+ _ts_decorate([
84
+ envapt.Envapt("HEALTH_CHECK_PATH", {
85
+ fallback: "/healthcheck"
86
+ }),
87
+ _ts_metadata("design:type", String)
88
+ ], Globals, "healthCheckPath", void 0);
89
+ _ts_decorate([
90
+ envapt.Envapt("SHUTDOWN_IS_ENABLED", {
91
+ fallback: false
92
+ }),
93
+ _ts_metadata("design:type", Boolean)
94
+ ], Globals, "shutdownIsEnabled", void 0);
95
+ _ts_decorate([
96
+ envapt.Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
97
+ fallback: void 0
98
+ }),
99
+ _ts_metadata("design:type", String)
100
+ ], Globals, "unknownExceptionWebhookUrl", void 0);
101
+
102
+ // src/interfaces/Components.ts
103
+ var BuilderTypes = {
104
+ command: discord_js.SlashCommandBuilder,
105
+ embed: discord_js.EmbedBuilder,
106
+ button: discord_js.ButtonBuilder,
107
+ menu_string: discord_js.StringSelectMenuBuilder,
108
+ menu_option_string: discord_js.StringSelectMenuOptionBuilder,
109
+ menu_user: discord_js.UserSelectMenuBuilder,
110
+ menu_channel: discord_js.ChannelSelectMenuBuilder,
111
+ menu_mentionable: discord_js.MentionableSelectMenuBuilder,
112
+ menu_role: discord_js.RoleSelectMenuBuilder,
113
+ modal: discord_js.ModalBuilder,
114
+ subcommand: discord_js.SlashCommandSubcommandBuilder,
115
+ group: discord_js.SlashCommandSubcommandGroupBuilder,
116
+ context_menu: discord_js.ContextMenuCommandBuilder
117
+ };
118
+ var RowTypes = {
119
+ button: discord_js.ActionRowBuilder,
120
+ menu_string: discord_js.ActionRowBuilder,
121
+ menu_user: discord_js.ActionRowBuilder,
122
+ menu_channel: discord_js.ActionRowBuilder,
123
+ menu_mentionable: discord_js.ActionRowBuilder,
124
+ menu_role: discord_js.ActionRowBuilder,
125
+ modal: discord_js.ActionRowBuilder
126
+ };
127
+ var ModalTypes = {
128
+ text: discord_js.TextInputBuilder
129
+ };
130
+ var BaseComponent = class BaseComponent2 {
131
+ static {
132
+ __name(this, "BaseComponent");
133
+ }
134
+ _component;
135
+ // eslint-disable-next-line @typescript-eslint/naming-convention
136
+ constructor(ComponentClass) {
137
+ this._component = new ComponentClass();
138
+ }
139
+ /**
140
+ * @description Returns the instantiated component
141
+ * @note Use this for configuring the component using its instance setters.
142
+ * @usage `this.instance.someMethod()`
143
+ */
144
+ get instance() {
145
+ return this._component;
146
+ }
147
+ };
148
+ var BuilderComponent = class extends BaseComponent {
149
+ static {
150
+ __name(this, "BuilderComponent");
151
+ }
152
+ constructor(type) {
153
+ const ComponentClass = BuilderTypes[type];
154
+ super(ComponentClass);
155
+ if (this.instance instanceof discord_js.EmbedBuilder) this.instance.setColor(Globals.botColor);
156
+ if (this.instance instanceof discord_js.SlashCommandBuilder || this.instance instanceof discord_js.ContextMenuCommandBuilder) {
157
+ this.instance.setContexts(discord_js.InteractionContextType.Guild);
158
+ }
159
+ }
160
+ get component() {
161
+ return this.instance;
162
+ }
163
+ buildCustomId(prefix, ...args) {
164
+ if (args.length === 0) return prefix;
165
+ return `${prefix}:${args.join("-")}`;
166
+ }
167
+ };
168
+ var RowComponent = class extends BaseComponent {
169
+ static {
170
+ __name(this, "RowComponent");
171
+ }
172
+ constructor(type) {
173
+ const ComponentClass = RowTypes[type];
174
+ super(ComponentClass);
175
+ }
176
+ get component() {
177
+ return this.instance;
178
+ }
179
+ };
180
+ var ModalRow = class ModalRow2 extends RowComponent {
181
+ static {
182
+ __name(this, "ModalRow");
183
+ }
184
+ constructor(component) {
185
+ super("modal");
186
+ this.instance.addComponents(component);
187
+ }
188
+ };
189
+ var ModalComponent = class extends BaseComponent {
190
+ static {
191
+ __name(this, "ModalComponent");
192
+ }
193
+ constructor(type) {
194
+ const ComponentClass = ModalTypes[type];
195
+ super(ComponentClass);
196
+ }
197
+ get component() {
198
+ return new ModalRow(this.instance).component;
199
+ }
200
+ };
201
+ var BaseErrorEmbed = class extends BuilderComponent {
202
+ static {
203
+ __name(this, "BaseErrorEmbed");
204
+ }
205
+ constructor() {
206
+ super("embed");
207
+ this.instance.setTitle("Cannot Proceed");
208
+ }
209
+ };
210
+ var CustomError = class extends Error {
211
+ static {
212
+ __name(this, "CustomError");
213
+ }
214
+ message;
215
+ _emit = false;
216
+ response = new BaseErrorEmbed().component;
217
+ constructor(message) {
218
+ super(message), this.message = message;
219
+ Error.captureStackTrace(this, this.constructor);
220
+ }
221
+ get emit() {
222
+ return this._emit;
223
+ }
224
+ };
225
+
226
+ // src/bot/errors/Database.ts
227
+ var DatabaseConnectionFailure = class extends CustomError {
228
+ static {
229
+ __name(this, "DatabaseConnectionFailure");
230
+ }
231
+ constructor(message = "Failed to connect to the database.") {
232
+ super(message);
233
+ this.response.setDescription("Failed to connect to the database.");
234
+ }
235
+ };
236
+ var DatabaseError = class extends CustomError {
237
+ static {
238
+ __name(this, "DatabaseError");
239
+ }
240
+ uuid;
241
+ _emit = true;
242
+ constructor(message, uuid) {
243
+ super(message), this.uuid = uuid;
244
+ this.name = "DatabaseError";
245
+ this.response.setTitle("Database Error").setDescription(`An error occurred while interacting with the database.
246
+ ### UUID: \`${this.uuid}\``);
247
+ }
248
+ };
249
+ var Logger = class _Logger {
250
+ static {
251
+ __name(this, "Logger");
252
+ }
253
+ static instances = /* @__PURE__ */ new Map();
254
+ static instance(prefix) {
255
+ let instance = this.instances.get(prefix);
256
+ if (!instance) {
257
+ instance = new _Logger(prefix);
258
+ this.instances.set(prefix, instance);
259
+ }
260
+ return instance;
261
+ }
262
+ constructor(transportName) {
263
+ const consoleTransport = this.createConsoleTransport(transportName);
264
+ this.initializeLogger(consoleTransport);
265
+ }
266
+ getFormatCustomizations() {
267
+ const padding = 7;
268
+ return [
269
+ winston.format.errors({
270
+ stack: true
271
+ }),
272
+ winston.format.splat(),
273
+ winston.format.colorize({
274
+ level: true
275
+ }),
276
+ winston.format.timestamp({
277
+ format: "D MMM, hh:mm:ss a"
278
+ }),
279
+ winston.format.printf((info) => {
280
+ const ts = String(info.timestamp ?? "");
281
+ const lvl = String(info.level).padEnd(padding);
282
+ const lbl = String(info.label ?? "");
283
+ const msg = String(info.message ?? "");
284
+ const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;
285
+ const splatSym = Symbol.for("splat");
286
+ const raw = info[splatSym];
287
+ const extras = Array.isArray(raw) ? raw : [];
288
+ const cleaned = extras.filter((x) => !(x instanceof Error)).filter((x) => {
289
+ if (!x) return false;
290
+ if (typeof x !== "object") return true;
291
+ return Object.keys(x).length > 0;
292
+ });
293
+ let rendered = base;
294
+ if (typeof info.stack === "string") {
295
+ rendered += `
296
+ ${String(info.stack)}`;
297
+ }
298
+ if (cleaned.length) {
299
+ const parts = [];
300
+ for (const x of cleaned) {
301
+ if (typeof x === "string") parts.push(x);
302
+ else {
303
+ try {
304
+ parts.push(JSON.stringify(x, null, 2));
305
+ } catch {
306
+ parts.push(String(x));
307
+ }
308
+ }
309
+ }
310
+ rendered += `
311
+ ${parts.join(" ")}`;
312
+ }
313
+ return rendered;
314
+ })
315
+ ];
316
+ }
317
+ createConsoleTransport(transportName) {
318
+ return new winston.transports.Console({
319
+ format: winston.format.combine(winston.format.label({
320
+ label: transportName
321
+ }), ...this.getFormatCustomizations()),
322
+ level: Globals.isDevelopment ? "silly" : Globals.isStaging ? "debug" : "info"
323
+ });
324
+ }
325
+ initializeLogger(consoleTransport) {
326
+ const transportsArray = [
327
+ consoleTransport
328
+ ];
329
+ if (Globals.isDevelopment) {
330
+ const maxSizeInMB = 10;
331
+ transportsArray.push(new winston.transports.File({
332
+ filename: "logs/application.log",
333
+ level: "debug",
334
+ format: winston.format.combine(winston.format.uncolorize(), winston.format.errors({
335
+ stack: true
336
+ }), winston.format.timestamp(), winston.format.json({
337
+ bigint: true,
338
+ space: 2
339
+ })),
340
+ maxsize: maxSizeInMB * 1024 * 1024,
341
+ maxFiles: 5,
342
+ tailable: true
343
+ }));
344
+ }
345
+ this.logger = winston.createLogger({
346
+ transports: transportsArray
347
+ });
348
+ }
349
+ error(msg, ...args) {
350
+ this.logger.error(msg, ...args);
351
+ }
352
+ warn(msg, ...args) {
353
+ this.logger.warn(msg, ...args);
354
+ }
355
+ info(msg, ...args) {
356
+ this.logger.info(msg, ...args);
357
+ }
358
+ http(msg, ...args) {
359
+ this.logger.http(msg, ...args);
360
+ }
361
+ verbose(msg, ...args) {
362
+ this.logger.verbose(msg, ...args);
363
+ }
364
+ debug(msg, ...args) {
365
+ this.logger.debug(msg, ...args);
366
+ }
367
+ silly(msg, ...args) {
368
+ this.logger.silly(msg, ...args);
369
+ }
370
+ // eslint-disable-next-line @typescript-eslint/naming-convention
371
+ static Error(prefix, msg, ...args) {
372
+ const logger = this.instance(prefix);
373
+ logger.error(msg, ...args);
374
+ }
375
+ // eslint-disable-next-line @typescript-eslint/naming-convention
376
+ static Info(prefix, msg, ...args) {
377
+ const logger = this.instance(prefix);
378
+ logger.info(msg, ...args);
379
+ }
380
+ // eslint-disable-next-line @typescript-eslint/naming-convention
381
+ static Warn(prefix, msg, ...args) {
382
+ const logger = this.instance(prefix);
383
+ logger.warn(msg, ...args);
384
+ }
385
+ // eslint-disable-next-line @typescript-eslint/naming-convention
386
+ static Debug(prefix, msg, ...args) {
387
+ const logger = this.instance(prefix);
388
+ logger.debug(msg, ...args);
389
+ }
390
+ // eslint-disable-next-line @typescript-eslint/naming-convention
391
+ static Silly(prefix, msg, ...args) {
392
+ const logger = this.instance(prefix);
393
+ logger.silly(msg, ...args);
394
+ }
395
+ };
396
+
397
+ // src/library/Helpers.ts
398
+ function numberFixer(num, decimalPlaces) {
399
+ const factor = Math.pow(10, decimalPlaces);
400
+ return Math.round((num + Number.EPSILON) * factor) / factor;
401
+ }
402
+ __name(numberFixer, "numberFixer");
403
+ function percentage(num1, num2) {
404
+ return Number((num1 / num2 * 100).toFixed(2));
405
+ }
406
+ __name(percentage, "percentage");
407
+ function generateAsciiTable(data) {
408
+ if (data.length === 0) return "";
409
+ const firstRow = data[0];
410
+ if (!firstRow || firstRow.length === 0) return "";
411
+ let table = "";
412
+ const columnWidths = [];
413
+ for (let i = 0; i < firstRow.length; i++) {
414
+ let maxWidth = 0;
415
+ for (const row of data) {
416
+ const cell = row[i];
417
+ if (cell !== void 0) maxWidth = Math.max(maxWidth, cell.length);
418
+ }
419
+ columnWidths.push(maxWidth);
420
+ }
421
+ const createLine = /* @__PURE__ */ __name((char, left, intersect, right) => {
422
+ let line = left;
423
+ columnWidths.forEach((width, index) => {
424
+ line += char.repeat(width + 2);
425
+ if (index < columnWidths.length - 1) line += intersect;
426
+ else line += right;
427
+ });
428
+ line += "\n";
429
+ return line;
430
+ }, "createLine");
431
+ table += createLine("\u2550", "\u2554", "\u2566", "\u2557");
432
+ data.forEach((row, rowIndex) => {
433
+ table += "\u2551";
434
+ row.forEach((cell, columnIndex) => {
435
+ const columnWidth = columnWidths[columnIndex];
436
+ if (columnWidth !== void 0) table += ` ${cell.padEnd(columnWidth)} \u2551`;
437
+ });
438
+ table += "\n";
439
+ if (rowIndex < data.length - 1) table += createLine("\u2500", "\u2560", "\u256C", "\u2563");
440
+ else table += createLine("\u2550", "\u255A", "\u2569", "\u255D");
441
+ });
442
+ return table;
443
+ }
444
+ __name(generateAsciiTable, "generateAsciiTable");
445
+ function formatWord(word) {
446
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
447
+ }
448
+ __name(formatWord, "formatWord");
449
+ function longestStringLength(arr) {
450
+ return Math.max(...arr.map((el) => el.toString().length));
451
+ }
452
+ __name(longestStringLength, "longestStringLength");
453
+ function currentTime() {
454
+ return Math.floor(Date.now() / 1e3);
455
+ }
456
+ __name(currentTime, "currentTime");
457
+ function ordinal(n) {
458
+ const s = [
459
+ "th",
460
+ "st",
461
+ "nd",
462
+ "rd"
463
+ ];
464
+ const v = n % 100;
465
+ const index = (v - 20) % 10;
466
+ const suffix = s[index] ?? s[v] ?? s[0];
467
+ if (!suffix) return `${n}th`;
468
+ return `${n}${suffix}`;
469
+ }
470
+ __name(ordinal, "ordinal");
471
+ function prettyDifference(numBefore, numAfter) {
472
+ return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();
473
+ }
474
+ __name(prettyDifference, "prettyDifference");
475
+ function isTsOrJsFile(entry) {
476
+ return entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".map");
477
+ }
478
+ __name(isTsOrJsFile, "isTsOrJsFile");
479
+ async function traverseDirectory(dir, callback) {
480
+ let entries;
481
+ try {
482
+ entries = await promises.readdir(dir, {
483
+ withFileTypes: true
484
+ });
485
+ } catch {
486
+ Logger.Error("Failed to read directory", dir);
487
+ entries = [];
488
+ }
489
+ for (const entry of entries) {
490
+ const fullPath = path__namespace.join(dir, entry.name);
491
+ const relativePath = path__namespace.relative(process.cwd(), fullPath);
492
+ if (entry.isDirectory()) {
493
+ await traverseDirectory(fullPath, callback);
494
+ } else if (isTsOrJsFile(entry)) {
495
+ const imported = await import(fullPath);
496
+ await callback(fullPath, relativePath, imported);
497
+ }
498
+ }
499
+ }
500
+ __name(traverseDirectory, "traverseDirectory");
501
+ function throwCustomError(error, message, customError) {
502
+ const uuid = crypto.randomUUID();
503
+ Logger.Error("Throwing Custom Error", error.name);
504
+ if (typeof customError === typeof DatabaseError) {
505
+ const errorMessage = error instanceof Error ? error.message : message;
506
+ throw new customError(errorMessage, uuid);
507
+ } else {
508
+ if (error instanceof Error) {
509
+ throw new customError(`${message}: ${error.message ? error.message : error.toString()}`);
510
+ } else {
511
+ throw new customError(message);
512
+ }
513
+ }
514
+ }
515
+ __name(throwCustomError, "throwCustomError");
516
+ function prettify(key) {
517
+ return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").trim();
518
+ }
519
+ __name(prettify, "prettify");
520
+ function fyShuffle(items) {
521
+ const array = items.slice();
522
+ for (let i = array.length - 1; i > 0; i--) {
523
+ const j = Math.floor(Math.random() * (i + 1));
524
+ [array[i], array[j]] = [
525
+ array[j],
526
+ array[i]
527
+ ];
528
+ }
529
+ return array;
530
+ }
531
+ __name(fyShuffle, "fyShuffle");
532
+
533
+ // src/bot/decorators/CommandRegisterable.ts
534
+ var CommandMetadataKey = Symbol("command:metadata");
535
+ function RegisterCommand(scope, guilds = []) {
536
+ return (ctor) => {
537
+ const meta = scope === "global" ? {
538
+ scope
539
+ } : {
540
+ scope,
541
+ guilds
542
+ };
543
+ Reflect.defineMetadata(CommandMetadataKey, meta, ctor);
544
+ };
545
+ }
546
+ __name(RegisterCommand, "RegisterCommand");
547
+
548
+ // src/bot/controllers/CommandRegistry.ts
549
+ var CommandRegistry = class {
550
+ static {
551
+ __name(this, "CommandRegistry");
552
+ }
553
+ core;
554
+ logger = new Logger("Commands");
555
+ isInitialised = false;
556
+ globalCommands = [];
557
+ guildCommands = /* @__PURE__ */ new Map();
558
+ constructor(core) {
559
+ this.core = core;
560
+ }
561
+ async init() {
562
+ if (this.isInitialised) return;
563
+ this.isInitialised = true;
564
+ this.logger.info(chalk6__default.default.bold(this.core.config.bot.commands.path));
565
+ await this.loadCommands(this.core.config.bot.commands.path);
566
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.magenta.bold(this.globalCommands.length)} global, ${chalk6__default.default.magenta.bold(this.guildCommands.size)} guild groups`);
567
+ }
568
+ async loadCommands(dir) {
569
+ await traverseDirectory(dir, (_full, rel, mod) => {
570
+ for (const exported of Object.values(mod)) if (this.isCommandClass(exported)) this.registerCommand(exported, rel);
571
+ });
572
+ }
573
+ isCommandClass(obj) {
574
+ if (typeof obj !== "function") return false;
575
+ return obj.prototype instanceof BuilderComponent && Reflect.hasMetadata(CommandMetadataKey, obj);
576
+ }
577
+ registerCommand(ctor, rel) {
578
+ const meta = Reflect.getMetadata(CommandMetadataKey, ctor);
579
+ if (!meta) return;
580
+ const instance = new ctor();
581
+ const comp = instance.component;
582
+ const commandType = comp instanceof discord_js.SlashCommandBuilder ? "slash command" : "context menu command";
583
+ if (meta.scope === "global") {
584
+ this.globalCommands.push(comp);
585
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(ctor.name)} from ${chalk6__default.default.gray(rel)}`);
586
+ this.logger.info(` \u2192 Global ${commandType}: ${chalk6__default.default.bold.cyan(comp.name)}`);
587
+ } else {
588
+ for (const g of meta.guilds) {
589
+ const arr = this.guildCommands.get(g) ?? [];
590
+ arr.push(comp);
591
+ this.guildCommands.set(g, arr);
592
+ }
593
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(ctor.name)} from ${chalk6__default.default.gray(rel)}`);
594
+ this.logger.info(` \u2192 Guild ${commandType}: ${chalk6__default.default.bold.cyan(comp.name)} for ${chalk6__default.default.magenta.bold(meta.guilds.length)} guild(s)`);
595
+ }
596
+ }
597
+ async setCommands() {
598
+ if (this.globalCommands.length > 0) {
599
+ await this.core.bot.client.application?.commands.set(this.globalCommands);
600
+ const tag = this.globalCommands.length === 1 ? "command" : "commands";
601
+ this.logger.info(`${chalk6__default.default.bold.green("Configured")} ${chalk6__default.default.magenta.bold(this.globalCommands.length)} global ${tag}`);
602
+ this.logger.info(` \u2192 ${this.globalCommands.map((command) => chalk6__default.default.bold.cyan(command.name)).join(", ")}`);
603
+ }
604
+ for (const [guildId, commands] of this.guildCommands.entries()) {
605
+ const guild = this.core.bot.client.guilds.cache.get(guildId);
606
+ if (!guild) {
607
+ this.logger.warn(`Guild with ID ${guildId} not found, skipping command registration.`);
608
+ continue;
609
+ }
610
+ await guild.commands.set(commands);
611
+ const tag = commands.length === 1 ? "command" : "commands";
612
+ this.logger.info(`${chalk6__default.default.bold.green("Configured")} ${chalk6__default.default.magenta.bold(commands.length)} ${tag} for guild ${chalk6__default.default.bold.yellow(guild.name)}`);
613
+ this.logger.info(` \u2192 ${commands.map((command) => chalk6__default.default.bold.cyan(command.name)).join(", ")}`);
614
+ }
615
+ }
616
+ };
617
+
618
+ // src/interfaces/Handler.ts
619
+ var BaseHandler = class BaseHandler2 {
620
+ static {
621
+ __name(this, "BaseHandler");
622
+ }
623
+ core;
624
+ checkable = false;
625
+ break = false;
626
+ errored = false;
627
+ event;
628
+ args = [];
629
+ constructor(event, core, args) {
630
+ this.core = core;
631
+ this.event = event;
632
+ this.args = args ?? [];
633
+ }
634
+ hasChecks() {
635
+ return this.checkable;
636
+ }
637
+ hasErrors() {
638
+ return this.errored;
639
+ }
640
+ setErrored() {
641
+ this.errored = true;
642
+ }
643
+ shouldBreak() {
644
+ return this.break;
645
+ }
646
+ getEvent() {
647
+ return this.event;
648
+ }
649
+ /**
650
+ * Get the arguments passed from the customId
651
+ * For example, if customId is "accept:user123-guild456", args will be ["user123", "guild456"]
652
+ */
653
+ getArgs() {
654
+ return this.args;
655
+ }
656
+ /**
657
+ * Get a specific argument by index
658
+ */
659
+ getArg(index) {
660
+ return this.args[index];
661
+ }
662
+ };
663
+ var InteractionHandler = class extends BaseHandler {
664
+ static {
665
+ __name(this, "InteractionHandler");
666
+ }
667
+ constructor(event, core, args) {
668
+ super(event, core, args);
669
+ }
670
+ };
671
+ var InteractionMiddleware = class extends BaseHandler {
672
+ static {
673
+ __name(this, "InteractionMiddleware");
674
+ }
675
+ constructor(event, core, args) {
676
+ super(event, core, args);
677
+ }
678
+ };
679
+ var AutocompleteHandler = class extends BaseHandler {
680
+ static {
681
+ __name(this, "AutocompleteHandler");
682
+ }
683
+ focused;
684
+ constructor(event, core, args) {
685
+ super(event, core, args);
686
+ this.focused = this.event.options.getFocused(true);
687
+ }
688
+ };
689
+ var EventHandler = class extends BaseHandler {
690
+ static {
691
+ __name(this, "EventHandler");
692
+ }
693
+ constructor(event, core, args) {
694
+ super(event, core, args);
695
+ }
696
+ };
697
+
698
+ // src/bot/decorators/EventRegisterable.ts
699
+ var EventMetadataKey = Symbol("event:metadata");
700
+ function RegisterEvent(eventName) {
701
+ return function(constructor) {
702
+ Reflect.defineMetadata(EventMetadataKey, eventName, constructor);
703
+ };
704
+ }
705
+ __name(RegisterEvent, "RegisterEvent");
706
+
707
+ // src/bot/controllers/EventController.ts
708
+ var EventController = class {
709
+ static {
710
+ __name(this, "EventController");
711
+ }
712
+ core;
713
+ logger = new Logger("Events");
714
+ isInitialized = false;
715
+ eventMap = /* @__PURE__ */ new Map();
716
+ constructor(core) {
717
+ this.core = core;
718
+ }
719
+ async init() {
720
+ if (this.isInitialized) {
721
+ return;
722
+ }
723
+ this.isInitialized = true;
724
+ const handlersDir = this.core.config.bot.events.path;
725
+ this.logger.info(chalk6__default.default.bold(handlersDir));
726
+ await this.loadHandlers(handlersDir);
727
+ this.attachToClient();
728
+ const loadedEventsArray = [];
729
+ this.eventMap.forEach((handlers, eventName) => {
730
+ loadedEventsArray.push(`${chalk6__default.default.magenta.bold(handlers.length)} ${eventName}`);
731
+ });
732
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
733
+ }
734
+ async loadHandlers(dir) {
735
+ await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
736
+ for (const val of Object.values(imported)) {
737
+ if (this.isEventHandlerClass(val)) {
738
+ this.registerHandler(val);
739
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(val.name)} from ${chalk6__default.default.gray(relativePath)}`);
740
+ }
741
+ }
742
+ });
743
+ }
744
+ isEventHandlerClass(obj) {
745
+ if (typeof obj !== "function") return false;
746
+ return obj.prototype instanceof EventHandler && Reflect.hasMetadata(EventMetadataKey, obj);
747
+ }
748
+ registerHandler(handlerClass) {
749
+ const eventName = Reflect.getMetadata(EventMetadataKey, handlerClass);
750
+ if (!eventName) return;
751
+ let handlers = this.eventMap.get(eventName);
752
+ if (!handlers) {
753
+ handlers = [];
754
+ this.eventMap.set(eventName, handlers);
755
+ }
756
+ handlers.push(handlerClass);
757
+ }
758
+ attachToClient() {
759
+ for (const [eventName] of this.eventMap) {
760
+ this.logger.debug(`Attaching ${chalk6__default.default.bold.green(eventName)} to ${chalk6__default.default.bold.yellow(this.core.bot.client.user?.username)}`);
761
+ this.core.bot.client.on(eventName, (...args) => {
762
+ void (async () => {
763
+ await this.processEvent(eventName, args);
764
+ })();
765
+ });
766
+ }
767
+ }
768
+ async processEvent(eventName, args) {
769
+ const handlerCtors = this.eventMap.get(eventName);
770
+ if (!handlerCtors || handlerCtors.length === 0) return;
771
+ for (const HandlerCtor of handlerCtors) {
772
+ try {
773
+ this.logger.debug(`Processing ${chalk6__default.default.bold.green(eventName)} with ${chalk6__default.default.gray(HandlerCtor.name)}`);
774
+ const handler = new HandlerCtor(args, this.core);
775
+ if (handler.hasChecks()) {
776
+ await handler.runChecks();
777
+ }
778
+ if (handler.shouldBreak()) return;
779
+ if (!handler.hasErrors()) {
780
+ await handler.execute();
781
+ }
782
+ } catch (err) {
783
+ this.logger.error(`Error in event ${String(eventName)} handler ${HandlerCtor.name}:`, err);
784
+ }
785
+ }
786
+ }
787
+ };
788
+
789
+ // src/bot/decorators/InteractionConfigurable.ts
790
+ var InteractionRoutes = /* @__PURE__ */ (function(InteractionRoutes2) {
791
+ InteractionRoutes2["Slash"] = "interaction:slash";
792
+ InteractionRoutes2["Button"] = "interaction:button";
793
+ InteractionRoutes2["Modal"] = "interaction:modal";
794
+ InteractionRoutes2["StringMenu"] = "interaction:stringMenu";
795
+ InteractionRoutes2["UserMenu"] = "interaction:userMenu";
796
+ InteractionRoutes2["RoleMenu"] = "interaction:roleMenu";
797
+ InteractionRoutes2["ChannelMenu"] = "interaction:channelMenu";
798
+ InteractionRoutes2["MentionableMenu"] = "interaction:mentionableMenu";
799
+ InteractionRoutes2["MessageContextMenu"] = "interaction:messageContextMenu";
800
+ InteractionRoutes2["UserContextMenu"] = "interaction:userContextMenu";
801
+ InteractionRoutes2["Autocomplete"] = "interaction:autocomplete";
802
+ return InteractionRoutes2;
803
+ })({});
804
+ var SelectMenuType = /* @__PURE__ */ (function(SelectMenuType2) {
805
+ SelectMenuType2["String"] = "string";
806
+ SelectMenuType2["User"] = "user";
807
+ SelectMenuType2["Role"] = "role";
808
+ SelectMenuType2["Channel"] = "channel";
809
+ SelectMenuType2["Mentionable"] = "mentionable";
810
+ return SelectMenuType2;
811
+ })({});
812
+ var InteractionMetadataKey = Symbol("interaction:metadata");
813
+ function SlashRoute(routeOrRoutes) {
814
+ return function(constructor) {
815
+ storeMetadata("interaction:slash", routeOrRoutes, constructor);
816
+ };
817
+ }
818
+ __name(SlashRoute, "SlashRoute");
819
+ function ButtonRoute(routeOrRoutes) {
820
+ return function(constructor) {
821
+ storeMetadata("interaction:button", routeOrRoutes, constructor);
822
+ };
823
+ }
824
+ __name(ButtonRoute, "ButtonRoute");
825
+ function ModalRoute(routeOrRoutes) {
826
+ return function(constructor) {
827
+ storeMetadata("interaction:modal", routeOrRoutes, constructor);
828
+ };
829
+ }
830
+ __name(ModalRoute, "ModalRoute");
831
+ function ContextMenuRoute(type, routeOrRoutes) {
832
+ return function(constructor) {
833
+ const routeType = type === "message" ? "interaction:messageContextMenu" : "interaction:userContextMenu";
834
+ storeMetadata(routeType, routeOrRoutes, constructor);
835
+ };
836
+ }
837
+ __name(ContextMenuRoute, "ContextMenuRoute");
838
+ function AutocompleteRoute(commandRoutes, focusedFields) {
839
+ return function(constructor) {
840
+ const routes = Array.isArray(commandRoutes) ? commandRoutes : [
841
+ commandRoutes
842
+ ];
843
+ const fields = Array.isArray(focusedFields) ? focusedFields : [
844
+ focusedFields
845
+ ];
846
+ routes.forEach((route) => {
847
+ fields.forEach((field) => {
848
+ const autocompleteKey = `${route}:${field}`;
849
+ storeMetadata("interaction:autocomplete", autocompleteKey, constructor);
850
+ });
851
+ });
852
+ };
853
+ }
854
+ __name(AutocompleteRoute, "AutocompleteRoute");
855
+ function SelectMenuRoute(type, routeOrRoutes) {
856
+ return function(constructor) {
857
+ const routeMap = {
858
+ ["string"]: "interaction:stringMenu",
859
+ ["user"]: "interaction:userMenu",
860
+ ["role"]: "interaction:roleMenu",
861
+ ["channel"]: "interaction:channelMenu",
862
+ ["mentionable"]: "interaction:mentionableMenu"
863
+ };
864
+ storeMetadata(routeMap[type], routeOrRoutes, constructor);
865
+ };
866
+ }
867
+ __name(SelectMenuRoute, "SelectMenuRoute");
868
+ function storeMetadata(symbol, routes, constructor) {
869
+ const areRoutes = /* @__PURE__ */ __name((routes2) => {
870
+ return Array.isArray(routes2) && routes2.every((r) => typeof r === "string");
871
+ }, "areRoutes");
872
+ const savedRoutes = Reflect.getMetadata(symbol, constructor);
873
+ const existing = areRoutes(savedRoutes) ? savedRoutes : [];
874
+ const toStore = Array.isArray(routes) ? routes : [
875
+ routes
876
+ ];
877
+ Reflect.defineMetadata(symbol, [
878
+ ...existing,
879
+ ...toStore
880
+ ], constructor);
881
+ Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
882
+ }
883
+ __name(storeMetadata, "storeMetadata");
884
+ var ErrorHandlingUtils = class {
885
+ static {
886
+ __name(this, "ErrorHandlingUtils");
887
+ }
888
+ static logger = new Logger("Errors");
889
+ static handleError(error, core, guild, user) {
890
+ const uuid = crypto2__namespace.randomUUID();
891
+ if (error instanceof CustomError) {
892
+ if (error instanceof DatabaseError) {
893
+ core.hooks.emit("unknownException", {
894
+ uuid,
895
+ error,
896
+ guild,
897
+ user
898
+ });
899
+ this.logger.error(`DatabaseError: ${error.uuid}`);
900
+ } else if (error.emit) {
901
+ this.logger.error(`${error.name}: ${error.message}`, error);
902
+ }
903
+ return {
904
+ uuid,
905
+ response: error.response
906
+ };
907
+ }
908
+ this.logger.error(uuid, error);
909
+ core.hooks.emit("unknownException", {
910
+ uuid,
911
+ error,
912
+ guild,
913
+ user
914
+ });
915
+ return {
916
+ uuid,
917
+ response: new GenericError(uuid).response
918
+ };
919
+ }
920
+ };
921
+ var GenericError = class GenericError2 extends CustomError {
922
+ static {
923
+ __name(this, "GenericError");
924
+ }
925
+ uuid;
926
+ constructor(uuid) {
927
+ super("An unknown error occurred"), this.uuid = uuid;
928
+ 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:
929
+ ### UUID: \`${this.uuid}\``);
930
+ }
931
+ };
932
+
933
+ // src/bot/decorators/Catchable.ts
934
+ function Catchable(options) {
935
+ return function(_target, _propertyKey, descriptor) {
936
+ const log = options?.log ?? false;
937
+ const forceFollowup = options?.forceFollowup ?? false;
938
+ const originalMethod = descriptor.value;
939
+ descriptor.value = async function(...args) {
940
+ const interaction = this.getEvent();
941
+ if (!originalMethod) throw new Error("Method not found");
942
+ try {
943
+ await originalMethod.apply(this, args);
944
+ } catch (error) {
945
+ if (!(error instanceof Error)) throw error;
946
+ this.setErrored();
947
+ if (log) console.error(error);
948
+ const { response } = ErrorHandlingUtils.handleError(error, this.core, interaction.guild, interaction.user);
949
+ const res = {
950
+ embeds: [
951
+ response
952
+ ],
953
+ components: []
954
+ };
955
+ if (forceFollowup) {
956
+ await interaction.followUp({
957
+ flags: discord_js.MessageFlags.Ephemeral,
958
+ ...res
959
+ });
960
+ return;
961
+ }
962
+ if (interaction.replied) {
963
+ await interaction.followUp({
964
+ flags: discord_js.MessageFlags.Ephemeral,
965
+ ...res
966
+ });
967
+ } else if (interaction.deferred) {
968
+ await interaction.editReply(res);
969
+ } else {
970
+ await interaction.reply({
971
+ flags: discord_js.MessageFlags.Ephemeral,
972
+ ...res
973
+ });
974
+ }
975
+ }
976
+ };
977
+ };
978
+ }
979
+ __name(Catchable, "Catchable");
980
+
981
+ // src/bot/defaults/UnhandledEvent.ts
982
+ function _ts_decorate2(decorators, target, key, desc) {
983
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
984
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
985
+ 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;
986
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
987
+ }
988
+ __name(_ts_decorate2, "_ts_decorate");
989
+ function _ts_metadata2(k, v) {
990
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
991
+ }
992
+ __name(_ts_metadata2, "_ts_metadata");
993
+ var UnhandledEvent = class extends InteractionHandler {
994
+ static {
995
+ __name(this, "UnhandledEvent");
996
+ }
997
+ async execute() {
998
+ await this.event.reply({
999
+ content: `Feature not implemented yet.`,
1000
+ flags: discord_js.MessageFlags.Ephemeral
1001
+ });
1002
+ }
1003
+ };
1004
+ _ts_decorate2([
1005
+ Catchable(),
1006
+ _ts_metadata2("design:type", Function),
1007
+ _ts_metadata2("design:paramtypes", []),
1008
+ _ts_metadata2("design:returntype", Promise)
1009
+ ], UnhandledEvent.prototype, "execute", null);
1010
+
1011
+ // src/bot/controllers/InteractionController.ts
1012
+ var InteractionController = class {
1013
+ static {
1014
+ __name(this, "InteractionController");
1015
+ }
1016
+ core;
1017
+ logger = new Logger("Interactions");
1018
+ isInitialized = false;
1019
+ slashMap = /* @__PURE__ */ new Map();
1020
+ buttonMap = /* @__PURE__ */ new Map();
1021
+ modalMap = /* @__PURE__ */ new Map();
1022
+ stringSelectMap = /* @__PURE__ */ new Map();
1023
+ userSelectMap = /* @__PURE__ */ new Map();
1024
+ roleSelectMap = /* @__PURE__ */ new Map();
1025
+ channelSelectMap = /* @__PURE__ */ new Map();
1026
+ mentionableSelectMap = /* @__PURE__ */ new Map();
1027
+ messageContextMenuMap = /* @__PURE__ */ new Map();
1028
+ userContextMenuMap = /* @__PURE__ */ new Map();
1029
+ autocompleteMap = /* @__PURE__ */ new Map();
1030
+ keysToIgnore = /* @__PURE__ */ new Set([
1031
+ "confirm!confirmable",
1032
+ "cancel!confirmable"
1033
+ ]);
1034
+ middlewares = [];
1035
+ constructor(core) {
1036
+ this.core = core;
1037
+ }
1038
+ async init() {
1039
+ if (this.isInitialized) return;
1040
+ this.isInitialized = true;
1041
+ const handlersDir = this.core.config.bot.interactions.path;
1042
+ this.logger.info(chalk6__default.default.bold(handlersDir));
1043
+ await this.loadHandlers(handlersDir);
1044
+ this.attachToClient();
1045
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded interaction handlers:")}`);
1046
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.slashMap.size)} slash commands`);
1047
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.buttonMap.size)} buttons`);
1048
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.modalMap.size)} modals`);
1049
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.stringSelectMap.size)} string selects`);
1050
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.userSelectMap.size)} user selects`);
1051
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.roleSelectMap.size)} role selects`);
1052
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.channelSelectMap.size)} channel selects`);
1053
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
1054
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
1055
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.userContextMenuMap.size)} user context menus`);
1056
+ this.logger.info(`\u2192 ${chalk6__default.default.magenta.bold(this.autocompleteMap.size)} autocomplete`);
1057
+ }
1058
+ async loadHandlers(dir) {
1059
+ await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
1060
+ for (const val of Object.values(imported)) {
1061
+ if (this.isHandlerClass(val)) {
1062
+ this.registerHandler(val);
1063
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(val.name)} from ${chalk6__default.default.gray(relativePath)}`);
1064
+ }
1065
+ }
1066
+ });
1067
+ }
1068
+ isHandlerClass(obj) {
1069
+ if (typeof obj !== "function") return false;
1070
+ return obj.prototype instanceof InteractionHandler && Reflect.hasMetadata(InteractionMetadataKey, obj) || obj.prototype instanceof AutocompleteHandler && Reflect.hasMetadata(InteractionMetadataKey, obj);
1071
+ }
1072
+ registerHandler(handlerClass) {
1073
+ const areRoutes = /* @__PURE__ */ __name((routes) => {
1074
+ return Array.isArray(routes) && routes.every((r) => typeof r === "string");
1075
+ }, "areRoutes");
1076
+ const routeTypes = [
1077
+ [
1078
+ InteractionRoutes.Slash,
1079
+ this.slashMap
1080
+ ],
1081
+ [
1082
+ InteractionRoutes.Button,
1083
+ this.buttonMap
1084
+ ],
1085
+ [
1086
+ InteractionRoutes.Modal,
1087
+ this.modalMap
1088
+ ],
1089
+ [
1090
+ InteractionRoutes.StringMenu,
1091
+ this.stringSelectMap
1092
+ ],
1093
+ [
1094
+ InteractionRoutes.UserMenu,
1095
+ this.userSelectMap
1096
+ ],
1097
+ [
1098
+ InteractionRoutes.RoleMenu,
1099
+ this.roleSelectMap
1100
+ ],
1101
+ [
1102
+ InteractionRoutes.ChannelMenu,
1103
+ this.channelSelectMap
1104
+ ],
1105
+ [
1106
+ InteractionRoutes.MentionableMenu,
1107
+ this.mentionableSelectMap
1108
+ ],
1109
+ [
1110
+ InteractionRoutes.MessageContextMenu,
1111
+ this.messageContextMenuMap
1112
+ ],
1113
+ [
1114
+ InteractionRoutes.UserContextMenu,
1115
+ this.userContextMenuMap
1116
+ ],
1117
+ [
1118
+ InteractionRoutes.Autocomplete,
1119
+ this.autocompleteMap
1120
+ ]
1121
+ ];
1122
+ for (const [routeType, map] of routeTypes) {
1123
+ const meta = Reflect.getMetadata(routeType, handlerClass);
1124
+ if (!areRoutes(meta)) continue;
1125
+ const routes = meta;
1126
+ routes.forEach((route) => map.set(route, handlerClass));
1127
+ }
1128
+ }
1129
+ attachToClient() {
1130
+ this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
1131
+ this.handleInteraction(interaction).catch((err) => {
1132
+ this.logger.error(`[${chalk6__default.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
1133
+ });
1134
+ });
1135
+ }
1136
+ parseCustomId(customId) {
1137
+ const parts = customId.split(":");
1138
+ const prefix = parts[0] ?? "";
1139
+ const argString = parts[1] ?? "";
1140
+ const args = argString ? argString.split("-") : [];
1141
+ return {
1142
+ prefix,
1143
+ args
1144
+ };
1145
+ }
1146
+ async handleCustomIdInteraction(interaction, getMap, interactionType) {
1147
+ const { prefix, args } = this.parseCustomId(interaction.customId);
1148
+ if (!prefix) return this.logger.warn(`${interactionType} has invalid customId: ${interaction.customId}`);
1149
+ await this.processInteraction(interaction, () => prefix, (key) => getMap().get(key), args);
1150
+ }
1151
+ async processInteraction(interaction, extractKey, getHandler, args) {
1152
+ const key = extractKey(interaction);
1153
+ if (this.keysToIgnore.has(key)) return;
1154
+ for (const MiddlewareCtor of this.middlewares) {
1155
+ const middleware = new MiddlewareCtor(interaction, this.core, args);
1156
+ await middleware.execute();
1157
+ if (middleware.hasErrors()) return;
1158
+ }
1159
+ let HandlerCtor = getHandler(key);
1160
+ if (!HandlerCtor) {
1161
+ this.logger.warn(`No handler found for key ${chalk6__default.default.bold.cyan(key)}. Falling back to UnhandledEvent.`);
1162
+ HandlerCtor = UnhandledEvent;
1163
+ }
1164
+ this.logger.debug(`Processing ${chalk6__default.default.bold.green(key)} with ${chalk6__default.default.gray(HandlerCtor.name)}`);
1165
+ const handler = new HandlerCtor(interaction, this.core, args);
1166
+ if (handler.hasChecks()) await handler.runChecks();
1167
+ if (handler.shouldBreak()) return;
1168
+ if (!handler.hasErrors()) await handler.execute();
1169
+ }
1170
+ async handleInteraction(interaction) {
1171
+ switch (true) {
1172
+ case interaction.isChatInputCommand():
1173
+ await this.handleSlashCommand(interaction);
1174
+ break;
1175
+ case interaction.isButton():
1176
+ await this.handleButton(interaction);
1177
+ break;
1178
+ case interaction.isModalSubmit():
1179
+ await this.handleModal(interaction);
1180
+ break;
1181
+ case interaction.isStringSelectMenu():
1182
+ await this.handleStringSelectMenu(interaction);
1183
+ break;
1184
+ case interaction.isUserSelectMenu():
1185
+ await this.handleUserSelectMenu(interaction);
1186
+ break;
1187
+ case interaction.isRoleSelectMenu():
1188
+ await this.handleRoleSelectMenu(interaction);
1189
+ break;
1190
+ case interaction.isChannelSelectMenu():
1191
+ await this.handleChannelSelectMenu(interaction);
1192
+ break;
1193
+ case interaction.isMentionableSelectMenu():
1194
+ await this.handleMentionableSelectMenu(interaction);
1195
+ break;
1196
+ case interaction.isMessageContextMenuCommand():
1197
+ await this.handleMessageContextMenu(interaction);
1198
+ break;
1199
+ case interaction.isUserContextMenuCommand():
1200
+ await this.handleUserContextMenu(interaction);
1201
+ break;
1202
+ case interaction.isAutocomplete():
1203
+ await this.handleAutocomplete(interaction);
1204
+ break;
1205
+ default:
1206
+ this.logger.warn(`Unhandled interaction type: ${interaction.type}`);
1207
+ break;
1208
+ }
1209
+ }
1210
+ async handleSlashCommand(interaction) {
1211
+ const route = this.buildSlashRoute(interaction);
1212
+ await this.processInteraction(interaction, () => route, (key) => this.slashMap.get(key));
1213
+ }
1214
+ async handleButton(interaction) {
1215
+ await this.handleCustomIdInteraction(interaction, () => this.buttonMap, "Button");
1216
+ }
1217
+ async handleModal(interaction) {
1218
+ await this.handleCustomIdInteraction(interaction, () => this.modalMap, "Modal");
1219
+ }
1220
+ async handleStringSelectMenu(interaction) {
1221
+ await this.handleCustomIdInteraction(interaction, () => this.stringSelectMap, "String select menu");
1222
+ }
1223
+ async handleUserSelectMenu(interaction) {
1224
+ await this.handleCustomIdInteraction(interaction, () => this.userSelectMap, "User select menu");
1225
+ }
1226
+ async handleRoleSelectMenu(interaction) {
1227
+ await this.handleCustomIdInteraction(interaction, () => this.roleSelectMap, "Role select menu");
1228
+ }
1229
+ async handleChannelSelectMenu(interaction) {
1230
+ await this.handleCustomIdInteraction(interaction, () => this.channelSelectMap, "Channel select menu");
1231
+ }
1232
+ async handleMentionableSelectMenu(interaction) {
1233
+ await this.handleCustomIdInteraction(interaction, () => this.mentionableSelectMap, "Mentionable select menu");
1234
+ }
1235
+ async handleMessageContextMenu(interaction) {
1236
+ await this.processInteraction(interaction, () => interaction.commandName, (key) => this.messageContextMenuMap.get(key));
1237
+ }
1238
+ async handleUserContextMenu(interaction) {
1239
+ await this.processInteraction(interaction, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
1240
+ }
1241
+ async handleAutocomplete(interaction) {
1242
+ const route = this.buildSlashRoute(interaction);
1243
+ const focused = interaction.options.getFocused(true);
1244
+ const autocompleteKey = `${route}:${focused.name}`;
1245
+ await this.processInteraction(interaction, () => autocompleteKey, (key) => this.autocompleteMap.get(key));
1246
+ }
1247
+ // Build the route from commandName, subcommandGroup, subcommand
1248
+ buildSlashRoute(interaction) {
1249
+ const command = interaction.commandName;
1250
+ const group = interaction.options.getSubcommandGroup(false);
1251
+ const sub = interaction.options.getSubcommand(false);
1252
+ let route = command;
1253
+ if (group && sub) {
1254
+ route = `${route}/${group}/${sub}`;
1255
+ } else if (group) {
1256
+ route = `${route}/${group}`;
1257
+ } else if (sub) {
1258
+ route = `${route}/${sub}`;
1259
+ }
1260
+ return route;
1261
+ }
1262
+ };
1263
+ var EmojiInjector = class {
1264
+ static {
1265
+ __name(this, "EmojiInjector");
1266
+ }
1267
+ core;
1268
+ logger = new Logger("Emojis");
1269
+ constructor(core) {
1270
+ this.core = core;
1271
+ }
1272
+ async init() {
1273
+ if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
1274
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.magenta.bold("0")} emojis`);
1275
+ return;
1276
+ }
1277
+ const configEmojis = this.core.config.bot.emojis;
1278
+ await this.core.bot.client.application?.emojis.fetch();
1279
+ let foundCount = 0;
1280
+ Object.entries(configEmojis).forEach(([key, emojiName]) => {
1281
+ const emoji = this.core.bot.client.application?.emojis.cache.find((e) => e.name === emojiName);
1282
+ if (emoji) {
1283
+ configEmojis[key] = `<${emoji.identifier}>`;
1284
+ foundCount++;
1285
+ }
1286
+ });
1287
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.magenta.bold(foundCount)} emojis`);
1288
+ }
1289
+ };
1290
+ var Plugin = class {
1291
+ static {
1292
+ __name(this, "Plugin");
1293
+ }
1294
+ pluggable;
1295
+ constructor(pluggable) {
1296
+ this.pluggable = pluggable;
1297
+ }
1298
+ };
1299
+ var Pluggable = class _Pluggable {
1300
+ static {
1301
+ __name(this, "Pluggable");
1302
+ }
1303
+ isInitialized = false;
1304
+ shutdown;
1305
+ startup;
1306
+ static PLUGIN_INIT_TIMEOUT_MS = 15e3;
1307
+ constructor(shutdown, startup) {
1308
+ this.shutdown = shutdown;
1309
+ this.startup = startup;
1310
+ }
1311
+ async init() {
1312
+ if (this.isInitialized) return this;
1313
+ await this.startup.run();
1314
+ this.isInitialized = true;
1315
+ return this;
1316
+ }
1317
+ attach(key, Plugin2, startupPhase, ...args) {
1318
+ if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
1319
+ if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
1320
+ const instance = new Plugin2(this, ...args);
1321
+ const entry = {
1322
+ [key]: instance
1323
+ };
1324
+ this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
1325
+ instance.logger.info(chalk6__default.default.bold("Initializing"));
1326
+ await instance.init();
1327
+ instance.logger.info(chalk6__default.default.bold("Initialized"));
1328
+ }, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
1329
+ return Object.assign(this, entry);
1330
+ }
1331
+ };
1332
+ var CoordinatedLifecycle = class {
1333
+ static {
1334
+ __name(this, "CoordinatedLifecycle");
1335
+ }
1336
+ phaseOrder;
1337
+ phaseEnum;
1338
+ logger;
1339
+ events = new events.EventEmitter();
1340
+ tasksMap = /* @__PURE__ */ new Map();
1341
+ constructor(loggerName, phaseOrder, phaseEnum) {
1342
+ this.phaseOrder = phaseOrder;
1343
+ this.phaseEnum = phaseEnum;
1344
+ this.logger = new Logger(loggerName);
1345
+ this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
1346
+ }
1347
+ /**
1348
+ * Add a task to a specific phase
1349
+ */
1350
+ addTask(phase, taskName, task, timeoutMs) {
1351
+ if (!this.canAddTask()) return;
1352
+ const tasks = this.tasksMap.get(phase);
1353
+ if (!tasks) throw new Error(`Unknown phase: ${phase}`);
1354
+ tasks.push({
1355
+ name: taskName,
1356
+ task,
1357
+ timeout: timeoutMs
1358
+ });
1359
+ this.logger.debug(`${chalk6__default.default.italic("Added")} ${this.getTaskType()} task ${chalk6__default.default.bold.cyan(taskName)} to phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1360
+ }
1361
+ /**
1362
+ * Remove a task by name from a specific phase
1363
+ */
1364
+ removeTask(phase, taskName) {
1365
+ if (!this.canRemoveTask()) return false;
1366
+ const tasks = this.tasksMap.get(phase);
1367
+ if (!tasks) return false;
1368
+ const initialLength = tasks.length;
1369
+ const filteredTasks = tasks.filter((task) => task.name !== taskName);
1370
+ this.tasksMap.set(phase, filteredTasks);
1371
+ const removed = initialLength !== filteredTasks.length;
1372
+ if (removed) {
1373
+ this.logger.debug(`${chalk6__default.default.italic("Removed")} ${this.getTaskType()} task ${chalk6__default.default.bold.cyan(taskName)} from phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1374
+ }
1375
+ return removed;
1376
+ }
1377
+ /**
1378
+ * Run all tasks in a specific phase
1379
+ */
1380
+ async runPhase(phase) {
1381
+ const tasks = this.tasksMap.get(phase) ?? [];
1382
+ if (tasks.length === 0) {
1383
+ this.logger.warn(`No tasks to run in phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1384
+ return;
1385
+ }
1386
+ this.logger.info(`${chalk6__default.default.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])} with ${chalk6__default.default.bold.cyan(tasks.length)} tasks`);
1387
+ this.emit(`phase:${phase}:start`);
1388
+ const results = await this.executeTasksInPhase(phase, tasks);
1389
+ const failures = results.filter((r) => r.status === "rejected").length;
1390
+ if (failures > 0) {
1391
+ const errorMessage = `Phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])} completed with ${chalk6__default.default.bold.red(failures)} failed tasks`;
1392
+ throw new Error(errorMessage);
1393
+ } else {
1394
+ this.logger.info(`Phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])} ${chalk6__default.default.bold.green("completed successfully")}`);
1395
+ }
1396
+ this.emit(`phase:${phase}:complete`);
1397
+ }
1398
+ /**
1399
+ * Run a single task with timeout
1400
+ */
1401
+ async runTaskWithTimeout(phase, task) {
1402
+ this.logger.info(`${chalk6__default.default.italic("Starting")} task ${chalk6__default.default.bold.cyan(task.name)} in phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1403
+ try {
1404
+ await Promise.race([
1405
+ task.task(),
1406
+ new Promise((_, reject) => {
1407
+ setTimeout(() => {
1408
+ reject(new Error(`Task '${task.name}' timed out after ${task.timeout}ms`));
1409
+ }, task.timeout);
1410
+ })
1411
+ ]);
1412
+ this.logger.info(`${chalk6__default.default.italic("Completed")} task ${chalk6__default.default.bold.cyan(task.name)} in phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}`);
1413
+ } catch (error) {
1414
+ this.logger.error(`${chalk6__default.default.italic("Failed")} task ${chalk6__default.default.bold.cyan(task.name)} in phase ${chalk6__default.default.bold.magenta(this.phaseEnum[phase])}:`, error);
1415
+ throw error;
1416
+ }
1417
+ }
1418
+ /**
1419
+ * Subscribe to lifecycle events
1420
+ */
1421
+ on(event, listener) {
1422
+ this.events.on(event, listener);
1423
+ }
1424
+ /**
1425
+ * Unsubscribe from lifecycle events
1426
+ */
1427
+ off(event, listener) {
1428
+ this.events.off(event, listener);
1429
+ }
1430
+ emit(event, ...args) {
1431
+ return this.events.emit(event, ...args);
1432
+ }
1433
+ };
1434
+
1435
+ // src/services/Lifecycle/CoordinatedShutdown.ts
1436
+ var ShutdownPhase = /* @__PURE__ */ (function(ShutdownPhase2) {
1437
+ ShutdownPhase2[ShutdownPhase2["StopAcceptingRequests"] = 1] = "StopAcceptingRequests";
1438
+ ShutdownPhase2[ShutdownPhase2["StopServices"] = 2] = "StopServices";
1439
+ ShutdownPhase2[ShutdownPhase2["ExternalResources"] = 3] = "ExternalResources";
1440
+ ShutdownPhase2[ShutdownPhase2["DiscordCleanup"] = 4] = "DiscordCleanup";
1441
+ ShutdownPhase2[ShutdownPhase2["FinalCleanup"] = 5] = "FinalCleanup";
1442
+ return ShutdownPhase2;
1443
+ })({});
1444
+ var PHASE_ORDER = [
1445
+ 1,
1446
+ 2,
1447
+ 3,
1448
+ 4,
1449
+ 5
1450
+ ];
1451
+ var LOG_FLUSH_DELAY_MS = 500;
1452
+ var CoordinatedShutdown = class extends CoordinatedLifecycle {
1453
+ static {
1454
+ __name(this, "CoordinatedShutdown");
1455
+ }
1456
+ isShuttingDown = false;
1457
+ exitCode = 0;
1458
+ constructor() {
1459
+ super("CoordinatedShutdown", PHASE_ORDER, ShutdownPhase);
1460
+ this.registerSignalHandlers();
1461
+ }
1462
+ canAddTask() {
1463
+ return Globals.shutdownIsEnabled;
1464
+ }
1465
+ canRemoveTask() {
1466
+ return true;
1467
+ }
1468
+ getTaskType() {
1469
+ return "shutdown";
1470
+ }
1471
+ async executeTasksInPhase(phase, tasks) {
1472
+ const results = [];
1473
+ for (const task of tasks) {
1474
+ results.push(await Promise.resolve().then(() => this.runTaskWithTimeout(phase, task)).then(() => ({
1475
+ status: "fulfilled",
1476
+ value: void 0
1477
+ }), (reason) => ({
1478
+ status: "rejected",
1479
+ reason
1480
+ })));
1481
+ }
1482
+ return results;
1483
+ }
1484
+ registerSignalHandlers() {
1485
+ if (!Globals.shutdownIsEnabled) return;
1486
+ process.on("SIGTERM", () => {
1487
+ this.logger.info(`Received ${chalk6__default.default.yellow.bold("SIGTERM")} signal`);
1488
+ void this.run(0);
1489
+ });
1490
+ process.on("SIGINT", () => {
1491
+ this.logger.info(`Received ${chalk6__default.default.yellow.bold("SIGINT")} signal`);
1492
+ void this.run(0);
1493
+ });
1494
+ }
1495
+ /**
1496
+ * Add a task to a specific shutdown phase
1497
+ */
1498
+ addTask(phase, taskName, task, timeoutMs = 5e3) {
1499
+ super.addTask(phase, taskName, task, timeoutMs);
1500
+ }
1501
+ /**
1502
+ * Remove a task by name from a specific phase
1503
+ */
1504
+ removeTask(phase, taskName) {
1505
+ return super.removeTask(phase, taskName);
1506
+ }
1507
+ /**
1508
+ * Start the coordinated shutdown sequence
1509
+ */
1510
+ async run(exitCode = 0) {
1511
+ if (this.isShuttingDown) {
1512
+ this.logger.warn("Shutdown sequence already in progress");
1513
+ return;
1514
+ }
1515
+ this.isShuttingDown = true;
1516
+ this.exitCode = exitCode;
1517
+ this.logger.info(`${chalk6__default.default.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk6__default.default.bold.cyan(exitCode)}`);
1518
+ this.emit("shutdown:start");
1519
+ try {
1520
+ for (const phase of PHASE_ORDER) {
1521
+ await this.runPhase(phase);
1522
+ }
1523
+ this.logger.info(`${chalk6__default.default.bold.green("Coordinated shutdown completed")} successfully`);
1524
+ this.emit("shutdown:complete");
1525
+ } catch (error) {
1526
+ this.logger.error(`${chalk6__default.default.bold.red("Coordinated shutdown failed")}`);
1527
+ this.emit("shutdown:error", error);
1528
+ } finally {
1529
+ this.logger.info(`${chalk6__default.default.bold.red("Exiting")} process with code ${chalk6__default.default.bold.cyan(this.exitCode)}`);
1530
+ setTimeout(() => {
1531
+ process.exit(this.exitCode);
1532
+ }, LOG_FLUSH_DELAY_MS);
1533
+ }
1534
+ }
1535
+ /**
1536
+ * Subscribe to shutdown events
1537
+ */
1538
+ on(event, listener) {
1539
+ super.on(event, listener);
1540
+ }
1541
+ /**
1542
+ * Unsubscribe from shutdown events
1543
+ */
1544
+ off(event, listener) {
1545
+ super.off(event, listener);
1546
+ }
1547
+ };
1548
+
1549
+ // src/bot/Bot.ts
1550
+ var Bot = class extends Plugin {
1551
+ static {
1552
+ __name(this, "Bot");
1553
+ }
1554
+ core;
1555
+ logger = new Logger("Bot");
1556
+ isInitialized = false;
1557
+ _client;
1558
+ interactions;
1559
+ events;
1560
+ commands;
1561
+ emojiInjector;
1562
+ constructor(core) {
1563
+ super(core), this.core = core;
1564
+ this._client = new discord_js.Client(core.config.bot.clientOptions);
1565
+ this.interactions = new InteractionController(core);
1566
+ this.events = new EventController(core);
1567
+ this.commands = new CommandRegistry(this.core);
1568
+ this.emojiInjector = new EmojiInjector(this.core);
1569
+ this.core.shutdown.addTask(ShutdownPhase.DiscordCleanup, "stop-bot", async () => await this.stop());
1570
+ }
1571
+ async init() {
1572
+ if (this.isInitialized) {
1573
+ return;
1574
+ }
1575
+ this.isInitialized = true;
1576
+ await this.login();
1577
+ await this.interactions.init();
1578
+ await this.events.init();
1579
+ await this.commands.init();
1580
+ await this.commands.setCommands();
1581
+ await this.emojiInjector.init();
1582
+ }
1583
+ async stop() {
1584
+ this._client.removeAllListeners();
1585
+ await this.logout();
1586
+ }
1587
+ async login() {
1588
+ await this._client.login(Globals.botToken);
1589
+ this.logger.info(`Logged in as ${chalk6__default.default.bold.magenta(this._client.user?.username)}!`);
1590
+ return this;
1591
+ }
1592
+ async logout() {
1593
+ await this._client.destroy();
1594
+ this.logger.info(chalk6__default.default.bold.red("Logged out of Discord!"));
1595
+ }
1596
+ get client() {
1597
+ return this._client;
1598
+ }
1599
+ };
1600
+
1601
+ // src/hooks/decorators/RegisterHook.ts
1602
+ var HookMetadataKey = Symbol("hook:metadata");
1603
+ function RegisterHook(hook) {
1604
+ return function(constructor) {
1605
+ Reflect.defineMetadata(HookMetadataKey, hook, constructor);
1606
+ };
1607
+ }
1608
+ __name(RegisterHook, "RegisterHook");
1609
+
1610
+ // src/hooks/interfaces/HookHandler.ts
1611
+ var HookHandler = class {
1612
+ static {
1613
+ __name(this, "HookHandler");
1614
+ }
1615
+ data;
1616
+ core;
1617
+ constructor(data, core) {
1618
+ this.data = data;
1619
+ this.core = core;
1620
+ this.data = data;
1621
+ this.core = core;
1622
+ }
1623
+ };
1624
+
1625
+ // src/hooks/interfaces/abstracts/WebhookLog.ts
1626
+ var WebhookLog = class extends HookHandler {
1627
+ static {
1628
+ __name(this, "WebhookLog");
1629
+ }
1630
+ constructor(data, core) {
1631
+ super(data, core);
1632
+ }
1633
+ };
1634
+
1635
+ // src/hooks/default/UnknownException.ts
1636
+ function _ts_decorate3(decorators, target, key, desc) {
1637
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1638
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1639
+ 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;
1640
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1641
+ }
1642
+ __name(_ts_decorate3, "_ts_decorate");
1643
+ var UnknownException = class extends WebhookLog {
1644
+ static {
1645
+ __name(this, "UnknownException");
1646
+ }
1647
+ webhook = new discord_js.WebhookClient({
1648
+ url: Globals.unknownExceptionWebhookUrl
1649
+ });
1650
+ async execute() {
1651
+ await this.webhook.send({
1652
+ username: "Unknown Exception",
1653
+ avatarURL: "https://cdn.discordapp.com/attachments/1351446034827579466/1351446912947191830/warning-2.png",
1654
+ embeds: [
1655
+ new UnhandledErrorEmbed(this.data).component
1656
+ ]
1657
+ });
1658
+ }
1659
+ };
1660
+ UnknownException = _ts_decorate3([
1661
+ RegisterHook("unknownException")
1662
+ ], UnknownException);
1663
+ var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
1664
+ static {
1665
+ __name(this, "UnhandledErrorEmbed");
1666
+ }
1667
+ constructor(data) {
1668
+ super("embed");
1669
+ const { uuid, error, guild, user } = data;
1670
+ this.instance.setTitle(`An unknown exception was thrown`).setColor("#ef4860").setDescription(`**Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`
1671
+ **Guild Name:** ${guild?.name ?? "Not used in a guild"}
1672
+ **User ID:** \`${user?.id ?? "Missing user info"}\`
1673
+ **Username:** ${user?.username ?? "Missing user info"}
1674
+ ### UUID: \`${uuid}\`
1675
+ \`\`\`${error.stack}\`\`\``);
1676
+ this.setTimestampsIfAvailable(error);
1677
+ }
1678
+ setTimestampsIfAvailable(error) {
1679
+ if (!(error instanceof discord_js.DiscordAPIError)) return;
1680
+ const now = Date.now();
1681
+ const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
1682
+ if (!snowflake) return void 0;
1683
+ const interactionTs = Number(discord_js.SnowflakeUtil.deconstruct(snowflake).timestamp);
1684
+ const diff = now - interactionTs;
1685
+ const seconds = Math.floor(diff / 1e3);
1686
+ const millis = diff % 1e3;
1687
+ this.instance.addFields([
1688
+ {
1689
+ name: "Timestamps",
1690
+ value: `- **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})
1691
+ - **\`Error logged \` :** ${new Date(now).toISOString()} (${now})
1692
+ - **\`Offset \` :** ${seconds}s ${millis}ms`,
1693
+ inline: true
1694
+ }
1695
+ ]);
1696
+ }
1697
+ };
1698
+ var HookEmitter = class {
1699
+ static {
1700
+ __name(this, "HookEmitter");
1701
+ }
1702
+ emitter = new events.EventEmitter();
1703
+ on(event, listener) {
1704
+ this.emitter.on(event, listener);
1705
+ return this;
1706
+ }
1707
+ once(event, listener) {
1708
+ this.emitter.once(event, listener);
1709
+ return this;
1710
+ }
1711
+ emit(event, data) {
1712
+ return this.emitter.emit(event, data);
1713
+ }
1714
+ };
1715
+
1716
+ // src/hooks/HookController.ts
1717
+ var HookController = class extends Plugin {
1718
+ static {
1719
+ __name(this, "HookController");
1720
+ }
1721
+ core;
1722
+ logger = new Logger("Hooks");
1723
+ isInitialized = false;
1724
+ hookMap = /* @__PURE__ */ new Map();
1725
+ emitter = new HookEmitter();
1726
+ constructor(core) {
1727
+ super(core), this.core = core;
1728
+ }
1729
+ async init() {
1730
+ if (this.isInitialized) return;
1731
+ this.isInitialized = true;
1732
+ const hooksDir = this.core.config.hooks.path;
1733
+ this.logger.info(chalk6__default.default.bold(hooksDir));
1734
+ this.registerHook("unknownException", UnknownException);
1735
+ await this.loadHooks(hooksDir);
1736
+ this.attachHooks();
1737
+ const totalHooks = Array.from(this.hookMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1738
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.bold.magenta(totalHooks)} hooks`);
1739
+ }
1740
+ async loadHooks(dir) {
1741
+ await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
1742
+ for (const exportName of Object.keys(imported)) {
1743
+ const val = imported[exportName];
1744
+ if (this.isHookHandler(val)) {
1745
+ const hookName = Reflect.getMetadata(HookMetadataKey, val);
1746
+ if (hookName) {
1747
+ this.registerHook(hookName, val);
1748
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(val.name)} from ${chalk6__default.default.gray(relativePath)}`);
1749
+ }
1750
+ }
1751
+ }
1752
+ });
1753
+ }
1754
+ registerHook(hookName, handler) {
1755
+ let handlers = this.hookMap.get(hookName);
1756
+ if (!handlers) {
1757
+ handlers = [];
1758
+ this.hookMap.set(hookName, handlers);
1759
+ }
1760
+ handlers.push(handler);
1761
+ }
1762
+ isHookHandler(obj) {
1763
+ if (typeof obj !== "function") return false;
1764
+ return obj.prototype instanceof HookHandler;
1765
+ }
1766
+ attachHooks() {
1767
+ for (const [hookName, handlerCtors] of this.hookMap) {
1768
+ this.emitter.on(hookName, (data) => {
1769
+ for (const HandlerCtor of handlerCtors) {
1770
+ try {
1771
+ const instance = new HandlerCtor(data, this.core);
1772
+ void instance.execute();
1773
+ } catch (err) {
1774
+ this.logger.error(`Error in hook ${String(hookName)} handler ${HandlerCtor.name}:`, err);
1775
+ }
1776
+ }
1777
+ });
1778
+ }
1779
+ }
1780
+ emit(event, data) {
1781
+ return this.emitter.emit(event, data);
1782
+ }
1783
+ };
1784
+ var HTTP_OK = 200;
1785
+ var HTTP_NOT_FOUND = 404;
1786
+ var HealthCheck = class extends Plugin {
1787
+ static {
1788
+ __name(this, "HealthCheck");
1789
+ }
1790
+ core;
1791
+ logger = new Logger("HealthCheck");
1792
+ port = Globals.healthCheckPort;
1793
+ path = Globals.healthCheckPath;
1794
+ server;
1795
+ constructor(core) {
1796
+ super(core), this.core = core;
1797
+ this.core.shutdown.addTask(ShutdownPhase.StopServices, "stop-healthcheck-server", async () => await this.stop());
1798
+ }
1799
+ /**
1800
+ * Starts the health check server.
1801
+ * @returns Promise that resolves when the server is listening
1802
+ */
1803
+ async init() {
1804
+ return new Promise((resolve, reject) => {
1805
+ this.server = http.createServer((req, res) => {
1806
+ if (req.method === "GET" && req.url === this.path) {
1807
+ res.writeHead(HTTP_OK, {
1808
+ "Content-Type": "application/json"
1809
+ });
1810
+ res.end(JSON.stringify({
1811
+ status: "ok",
1812
+ timestamp: Date.now()
1813
+ }));
1814
+ } else {
1815
+ res.writeHead(HTTP_NOT_FOUND, {
1816
+ "Content-Type": "application/json"
1817
+ });
1818
+ res.end(JSON.stringify({
1819
+ status: "not found"
1820
+ }));
1821
+ }
1822
+ });
1823
+ this.server.on("error", reject);
1824
+ this.server.listen(this.port, () => {
1825
+ this.logger.info(`${chalk6__default.default.green.bold("\u2713")} Health check server listening on ${chalk6__default.default.cyan(`http://localhost:${this.port}${this.path}`)}`);
1826
+ resolve();
1827
+ });
1828
+ });
1829
+ }
1830
+ stop() {
1831
+ return new Promise((shutdownResolve) => {
1832
+ this.server?.close(() => {
1833
+ this.logger.info(chalk6__default.default.bold.red("Health check server stopped"));
1834
+ shutdownResolve();
1835
+ });
1836
+ });
1837
+ }
1838
+ };
1839
+ var StartupPhase = /* @__PURE__ */ (function(StartupPhase2) {
1840
+ StartupPhase2[StartupPhase2["Validation"] = 1] = "Validation";
1841
+ StartupPhase2[StartupPhase2["Discovery"] = 2] = "Discovery";
1842
+ StartupPhase2[StartupPhase2["Registration"] = 3] = "Registration";
1843
+ StartupPhase2[StartupPhase2["Configuration"] = 4] = "Configuration";
1844
+ StartupPhase2[StartupPhase2["Instantiation"] = 5] = "Instantiation";
1845
+ StartupPhase2[StartupPhase2["Activation"] = 6] = "Activation";
1846
+ StartupPhase2[StartupPhase2["Ready"] = 7] = "Ready";
1847
+ return StartupPhase2;
1848
+ })({});
1849
+ var PHASE_ORDER2 = [
1850
+ 1,
1851
+ 2,
1852
+ 3,
1853
+ 4,
1854
+ 5,
1855
+ 6,
1856
+ 7
1857
+ ];
1858
+ var CoordinatedStartup = class extends CoordinatedLifecycle {
1859
+ static {
1860
+ __name(this, "CoordinatedStartup");
1861
+ }
1862
+ isStartingUp = false;
1863
+ hasStarted = false;
1864
+ constructor() {
1865
+ super("CoordinatedStartup", PHASE_ORDER2, StartupPhase);
1866
+ }
1867
+ /**
1868
+ * Add a task to a specific startup phase
1869
+ */
1870
+ addTask(phase, taskName, task, timeoutMs = 1e4) {
1871
+ super.addTask(phase, taskName, task, timeoutMs);
1872
+ }
1873
+ canAddTask() {
1874
+ if (this.hasStarted) {
1875
+ throw new Error("Cannot add tasks after startup sequence has already completed");
1876
+ }
1877
+ if (this.isStartingUp) {
1878
+ throw new Error("Cannot add tasks while startup sequence is in progress");
1879
+ }
1880
+ return true;
1881
+ }
1882
+ canRemoveTask() {
1883
+ if (this.isStartingUp) {
1884
+ throw new Error("Cannot remove tasks while startup sequence is in progress");
1885
+ }
1886
+ return true;
1887
+ }
1888
+ getTaskType() {
1889
+ return "startup";
1890
+ }
1891
+ async executeTasksInPhase(phase, tasks) {
1892
+ const results = [];
1893
+ for (const task of tasks) {
1894
+ results.push(await Promise.resolve().then(() => this.runTaskWithTimeout(phase, task)).then(() => ({
1895
+ status: "fulfilled",
1896
+ value: void 0
1897
+ }), (reason) => ({
1898
+ status: "rejected",
1899
+ reason
1900
+ })));
1901
+ }
1902
+ return results;
1903
+ }
1904
+ /**
1905
+ * Start the coordinated startup sequence
1906
+ */
1907
+ async run() {
1908
+ if (this.hasStarted) {
1909
+ this.logger.warn("Startup sequence has already completed");
1910
+ return;
1911
+ }
1912
+ if (this.isStartingUp) {
1913
+ this.logger.warn("Startup sequence already in progress");
1914
+ return;
1915
+ }
1916
+ this.isStartingUp = true;
1917
+ this.logger.info(`${chalk6__default.default.bold.green("Starting")} coordinated startup sequence`);
1918
+ this.emit("startup:start");
1919
+ try {
1920
+ for (const phase of PHASE_ORDER2) await this.runPhase(phase);
1921
+ this.hasStarted = true;
1922
+ this.logger.info(`${chalk6__default.default.bold.green("Coordinated startup completed")} successfully`);
1923
+ this.emit("startup:complete");
1924
+ } catch (error) {
1925
+ this.logger.error(`${chalk6__default.default.bold.red("Coordinated startup failed")}`);
1926
+ this.emit("startup:error", error);
1927
+ throw error;
1928
+ } finally {
1929
+ this.isStartingUp = false;
1930
+ }
1931
+ }
1932
+ /**
1933
+ * Subscribe to startup events
1934
+ */
1935
+ on(event, listener) {
1936
+ super.on(event, listener);
1937
+ }
1938
+ /**
1939
+ * Unsubscribe from startup events
1940
+ */
1941
+ off(event, listener) {
1942
+ super.off(event, listener);
1943
+ }
1944
+ /**
1945
+ * Check if startup has completed
1946
+ */
1947
+ get isReady() {
1948
+ return this.hasStarted;
1949
+ }
1950
+ /**
1951
+ * Check if startup is currently running
1952
+ */
1953
+ get isRunning() {
1954
+ return this.isStartingUp;
1955
+ }
1956
+ };
1957
+
1958
+ // src/Seedcord.ts
1959
+ var Seedcord = class _Seedcord extends Pluggable {
1960
+ static {
1961
+ __name(this, "Seedcord");
1962
+ }
1963
+ config;
1964
+ static isInstantiated = false;
1965
+ shutdown;
1966
+ startup;
1967
+ hooks;
1968
+ bot;
1969
+ healthCheck;
1970
+ constructor(config) {
1971
+ const shutdown = new CoordinatedShutdown();
1972
+ const startup = new CoordinatedStartup();
1973
+ super(shutdown, startup), this.config = config;
1974
+ this.shutdown = shutdown;
1975
+ this.startup = startup;
1976
+ if (_Seedcord.isInstantiated) {
1977
+ throw new Error("Seedcord can only be instantiated once. Use the existing instance instead.");
1978
+ }
1979
+ _Seedcord.isInstantiated = true;
1980
+ this.hooks = new HookController(this);
1981
+ this.bot = new Bot(this);
1982
+ this.healthCheck = new HealthCheck(this);
1983
+ this.registerStartupTasks();
1984
+ }
1985
+ registerStartupTasks() {
1986
+ this.startup.addTask(StartupPhase.Configuration, "Hook Initialization", async () => {
1987
+ this.hooks.logger.info(chalk6__default.default.bold("Initializing"));
1988
+ await this.hooks.init();
1989
+ this.hooks.logger.info(chalk6__default.default.bold("Initialized"));
1990
+ });
1991
+ this.startup.addTask(StartupPhase.Instantiation, "Bot Initialization", async () => {
1992
+ this.bot.logger.info(chalk6__default.default.bold("Initializing"));
1993
+ await this.bot.init();
1994
+ this.bot.logger.info(chalk6__default.default.bold("Initialized"));
1995
+ });
1996
+ this.startup.addTask(StartupPhase.Ready, "Health Check", async () => {
1997
+ this.healthCheck.logger.info(chalk6__default.default.bold("Initializing"));
1998
+ await this.healthCheck.init();
1999
+ this.healthCheck.logger.info(chalk6__default.default.bold("Initialized"));
2000
+ });
2001
+ }
2002
+ async start() {
2003
+ await super.init();
2004
+ return this;
2005
+ }
2006
+ };
2007
+
2008
+ // src/bot/decorators/Checkable.ts
2009
+ function Checkable(ctor) {
2010
+ return class extends ctor {
2011
+ static name = ctor.name;
2012
+ checkable = true;
2013
+ };
2014
+ }
2015
+ __name(Checkable, "Checkable");
2016
+ function EventCatchable(log) {
2017
+ return function(_target, _prop, descriptor) {
2018
+ const original = descriptor.value;
2019
+ descriptor.value = async function(...args) {
2020
+ if (!original) throw new Error("Method not found");
2021
+ try {
2022
+ await original.apply(this, args);
2023
+ } catch (err) {
2024
+ if (!(err instanceof Error)) throw err;
2025
+ this.setErrored();
2026
+ if (log) console.error(err);
2027
+ const eventArgs = Array.isArray(this.getEvent()) ? this.getEvent() : [
2028
+ this.getEvent()
2029
+ ];
2030
+ const msg = eventArgs.find((x) => x instanceof discord_js.Message);
2031
+ const result = ErrorHandlingUtils.handleError(err, this.core, msg?.guild ?? null, msg?.author ?? null);
2032
+ if (!msg) return;
2033
+ await msg.reply({
2034
+ embeds: [
2035
+ result.response
2036
+ ],
2037
+ components: []
2038
+ });
2039
+ }
2040
+ };
2041
+ };
2042
+ }
2043
+ __name(EventCatchable, "EventCatchable");
2044
+
2045
+ // src/services/CooldownManager.ts
2046
+ var CooldownManager = class {
2047
+ static {
2048
+ __name(this, "CooldownManager");
2049
+ }
2050
+ window;
2051
+ Err;
2052
+ msg;
2053
+ map = /* @__PURE__ */ new Map();
2054
+ constructor(opts = {}) {
2055
+ this.window = opts.cooldown ?? 1e3;
2056
+ this.Err = opts.err ?? Error;
2057
+ this.msg = opts.message ?? "Cooldown active";
2058
+ }
2059
+ /** Record usage without any checks. */
2060
+ set(key) {
2061
+ this.map.set(key, Date.now());
2062
+ }
2063
+ /**
2064
+ * Verify cooldown for `key`.\
2065
+ * If active → throws the custom error.\
2066
+ * If not active → updates timestamp and returns void.
2067
+ */
2068
+ check(key) {
2069
+ const now = Date.now();
2070
+ const last = this.map.get(key);
2071
+ const remaining = this.window - (now - (last ?? 0));
2072
+ if (Globals.isDevelopment && remaining > 0) {
2073
+ Logger.Debug("CooldownManager", `${key} - ${remaining}ms remaining`);
2074
+ }
2075
+ if (last !== void 0 && remaining > 0) {
2076
+ throw new this.Err(this.msg, remaining);
2077
+ }
2078
+ this.map.set(key, now);
2079
+ }
2080
+ /** Returns true if the key is still cooling down (does not update timestamp). */
2081
+ isActive(key) {
2082
+ const last = this.map.get(key);
2083
+ return last !== void 0 && Date.now() - last < this.window;
2084
+ }
2085
+ /** Remove a key from the map (useful for manual resets). */
2086
+ clear(key) {
2087
+ this.map.delete(key);
2088
+ }
2089
+ };
2090
+ var ModelMetadataKey = Symbol("db:model");
2091
+ function DatabaseModel(collection) {
2092
+ return (target, propertyKey) => {
2093
+ const schema = target[propertyKey];
2094
+ const name = String(collection);
2095
+ const model = mongoose2__default.default.model(name, schema);
2096
+ Reflect.defineMetadata(ModelMetadataKey, model, target);
2097
+ };
2098
+ }
2099
+ __name(DatabaseModel, "DatabaseModel");
2100
+
2101
+ // src/mongo/decorators/DatabaseService.ts
2102
+ var ServiceMetadataKey = Symbol("db:serviceKey");
2103
+ function DatabaseService(key) {
2104
+ return (ctor) => {
2105
+ Reflect.defineMetadata(ServiceMetadataKey, key, ctor);
2106
+ };
2107
+ }
2108
+ __name(DatabaseService, "DatabaseService");
2109
+
2110
+ // src/mongo/BaseService.ts
2111
+ var BaseService = class {
2112
+ static {
2113
+ __name(this, "BaseService");
2114
+ }
2115
+ db;
2116
+ core;
2117
+ model;
2118
+ constructor(db, core) {
2119
+ this.db = db;
2120
+ this.core = core;
2121
+ const ctor = this.constructor;
2122
+ const key = Reflect.getMetadata(ServiceMetadataKey, ctor);
2123
+ if (!key) throw new Error(`Missing @DatabaseService on ${ctor.name}`);
2124
+ const model = Reflect.getMetadata(ModelMetadataKey, ctor);
2125
+ if (!model) throw new Error(`Missing @DatabaseModel on ${ctor.name}`);
2126
+ this.model = model;
2127
+ db._register(key, this);
2128
+ }
2129
+ };
2130
+
2131
+ // src/mongo/Mongo.ts
2132
+ var Mongo = class extends Plugin {
2133
+ static {
2134
+ __name(this, "Mongo");
2135
+ }
2136
+ core;
2137
+ options;
2138
+ logger = new Logger("MongoDB");
2139
+ isInitialised = false;
2140
+ uri;
2141
+ /**
2142
+ * Map of all loaded services.
2143
+ * Keys come from `@DatabaseService('key')`
2144
+ */
2145
+ services = {};
2146
+ constructor(core, options) {
2147
+ super(core), this.core = core, this.options = options;
2148
+ this.uri = options.uri;
2149
+ this.core.shutdown.addTask(ShutdownPhase.ExternalResources, "stop-database", async () => await this.stop());
2150
+ }
2151
+ async init() {
2152
+ if (this.isInitialised) return;
2153
+ this.isInitialised = true;
2154
+ await this.connect();
2155
+ await this.loadServices();
2156
+ }
2157
+ async stop() {
2158
+ await this.disconnect();
2159
+ }
2160
+ async connect() {
2161
+ await mongoose2__default.default.connect(this.uri, {
2162
+ dbName: this.options.dbName,
2163
+ ...Globals.isProduction && {
2164
+ tls: true,
2165
+ ssl: true
2166
+ }
2167
+ }).then((i) => this.logger.info(`Connected to MongoDB: ${chalk6__default.default.bold.magenta(i.connection.name)}`)).catch((err) => throwCustomError(err, "Could not connect to MongoDB", DatabaseConnectionFailure));
2168
+ }
2169
+ async disconnect() {
2170
+ await mongoose2__default.default.disconnect().then(() => this.logger.info(chalk6__default.default.red.bold("Disconnected from MongoDB"))).catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${err.message}`));
2171
+ }
2172
+ async loadServices() {
2173
+ const servicesDir = this.options.servicesDir;
2174
+ this.logger.info(chalk6__default.default.bold(servicesDir));
2175
+ await traverseDirectory(servicesDir, (_full, rel, mod) => {
2176
+ for (const Service of Object.values(mod)) {
2177
+ if (this.isServiceClass(Service)) {
2178
+ const instance = new Service(this, this.core);
2179
+ this.logger.info(`${chalk6__default.default.italic("Registered")} ${chalk6__default.default.bold.yellow(instance.constructor.name)} from ${chalk6__default.default.gray(rel)}`);
2180
+ }
2181
+ }
2182
+ });
2183
+ this.logger.info(`${chalk6__default.default.bold.green("Loaded")}: ${chalk6__default.default.magenta(Object.keys(this.services).length)} services`);
2184
+ }
2185
+ isServiceClass(obj) {
2186
+ return typeof obj === "function" && obj.prototype instanceof BaseService && Reflect.hasMetadata(ServiceMetadataKey, obj);
2187
+ }
2188
+ _register(key, instance) {
2189
+ this.services[key] = instance;
2190
+ }
2191
+ };
2192
+
2193
+ // src/mongo/decorators/DBCatchable.ts
2194
+ function DBCatchable(errorMessage) {
2195
+ return function(_target, _propertyKey, descriptor) {
2196
+ const originalMethod = descriptor.value;
2197
+ descriptor.value = async function(...args) {
2198
+ if (!originalMethod) {
2199
+ throw new Error("Method not found");
2200
+ }
2201
+ try {
2202
+ return await originalMethod.apply(this, args);
2203
+ } catch (error) {
2204
+ if (!(error instanceof CustomError)) {
2205
+ throwCustomError(error, errorMessage, DatabaseError);
2206
+ } else {
2207
+ throw error;
2208
+ }
2209
+ }
2210
+ };
2211
+ };
2212
+ }
2213
+ __name(DBCatchable, "DBCatchable");
2214
+
2215
+ exports.AutocompleteHandler = AutocompleteHandler;
2216
+ exports.AutocompleteRoute = AutocompleteRoute;
2217
+ exports.BaseErrorEmbed = BaseErrorEmbed;
2218
+ exports.BaseService = BaseService;
2219
+ exports.Bot = Bot;
2220
+ exports.BuilderComponent = BuilderComponent;
2221
+ exports.ButtonRoute = ButtonRoute;
2222
+ exports.Catchable = Catchable;
2223
+ exports.Checkable = Checkable;
2224
+ exports.CommandMetadataKey = CommandMetadataKey;
2225
+ exports.ContextMenuRoute = ContextMenuRoute;
2226
+ exports.CooldownManager = CooldownManager;
2227
+ exports.CoordinatedShutdown = CoordinatedShutdown;
2228
+ exports.CoordinatedStartup = CoordinatedStartup;
2229
+ exports.CustomError = CustomError;
2230
+ exports.DBCatchable = DBCatchable;
2231
+ exports.DatabaseModel = DatabaseModel;
2232
+ exports.DatabaseService = DatabaseService;
2233
+ exports.EventCatchable = EventCatchable;
2234
+ exports.EventHandler = EventHandler;
2235
+ exports.EventMetadataKey = EventMetadataKey;
2236
+ exports.Globals = Globals;
2237
+ exports.HealthCheck = HealthCheck;
2238
+ exports.HookController = HookController;
2239
+ exports.HookEmitter = HookEmitter;
2240
+ exports.HookHandler = HookHandler;
2241
+ exports.HookMetadataKey = HookMetadataKey;
2242
+ exports.InteractionHandler = InteractionHandler;
2243
+ exports.InteractionMetadataKey = InteractionMetadataKey;
2244
+ exports.InteractionMiddleware = InteractionMiddleware;
2245
+ exports.InteractionRoutes = InteractionRoutes;
2246
+ exports.Logger = Logger;
2247
+ exports.ModalComponent = ModalComponent;
2248
+ exports.ModalRoute = ModalRoute;
2249
+ exports.ModelMetadataKey = ModelMetadataKey;
2250
+ exports.Mongo = Mongo;
2251
+ exports.Pluggable = Pluggable;
2252
+ exports.Plugin = Plugin;
2253
+ exports.RegisterCommand = RegisterCommand;
2254
+ exports.RegisterEvent = RegisterEvent;
2255
+ exports.RegisterHook = RegisterHook;
2256
+ exports.RowComponent = RowComponent;
2257
+ exports.Seedcord = Seedcord;
2258
+ exports.SelectMenuRoute = SelectMenuRoute;
2259
+ exports.SelectMenuType = SelectMenuType;
2260
+ exports.ServiceMetadataKey = ServiceMetadataKey;
2261
+ exports.ShutdownPhase = ShutdownPhase;
2262
+ exports.SlashRoute = SlashRoute;
2263
+ exports.StartupPhase = StartupPhase;
2264
+ exports.WebhookLog = WebhookLog;
2265
+ exports.currentTime = currentTime;
2266
+ exports.formatWord = formatWord;
2267
+ exports.fyShuffle = fyShuffle;
2268
+ exports.generateAsciiTable = generateAsciiTable;
2269
+ exports.isTsOrJsFile = isTsOrJsFile;
2270
+ exports.longestStringLength = longestStringLength;
2271
+ exports.numberFixer = numberFixer;
2272
+ exports.ordinal = ordinal;
2273
+ exports.percentage = percentage;
2274
+ exports.prettify = prettify;
2275
+ exports.prettyDifference = prettyDifference;
2276
+ exports.throwCustomError = throwCustomError;
2277
+ exports.traverseDirectory = traverseDirectory;
2278
+ //# sourceMappingURL=index.cjs.map
2279
+ //# sourceMappingURL=index.cjs.map