seedcord 0.1.0 → 0.2.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/README.md +5 -0
- package/dist/index.cjs +251 -1126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -558
- package/dist/index.d.mts +27 -558
- package/dist/index.d.ts +27 -558
- package/dist/index.mjs +226 -1090
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -21
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,79 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
2
|
+
import { Logger, ShutdownPhase, CoordinatedShutdown, CoordinatedStartup, HealthCheck, StartupPhase } from '@seedcord/services';
|
|
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, MessageFlags, Events, Client, WebhookClient, DiscordAPIError, SnowflakeUtil, Message } from 'discord.js';
|
|
6
|
+
import { Envapt } from 'envapt';
|
|
7
|
+
import { traverseDirectory } from '@seedcord/utils';
|
|
8
|
+
export * from '@seedcord/utils';
|
|
8
9
|
import * as crypto2 from 'crypto';
|
|
9
10
|
import { EventEmitter } from 'events';
|
|
10
|
-
import { createServer } from 'http';
|
|
11
11
|
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
13
13
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
|
+
var Plugin = class {
|
|
15
|
+
static {
|
|
16
|
+
__name(this, "Plugin");
|
|
17
|
+
}
|
|
18
|
+
pluggable;
|
|
19
|
+
constructor(pluggable) {
|
|
20
|
+
this.pluggable = pluggable;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var Pluggable = class _Pluggable {
|
|
24
|
+
static {
|
|
25
|
+
__name(this, "Pluggable");
|
|
26
|
+
}
|
|
27
|
+
isInitialized = false;
|
|
28
|
+
shutdown;
|
|
29
|
+
startup;
|
|
30
|
+
static PLUGIN_INIT_TIMEOUT_MS = 15e3;
|
|
31
|
+
constructor(shutdown, startup) {
|
|
32
|
+
this.shutdown = shutdown;
|
|
33
|
+
this.startup = startup;
|
|
34
|
+
}
|
|
35
|
+
async init() {
|
|
36
|
+
if (this.isInitialized) return this;
|
|
37
|
+
await this.startup.run();
|
|
38
|
+
this.isInitialized = true;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
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
|
+
*/
|
|
62
|
+
attach(key, Plugin2, startupPhase, ...args) {
|
|
63
|
+
if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
|
|
64
|
+
if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
|
|
65
|
+
const instance = new Plugin2(this, ...args);
|
|
66
|
+
const entry = {
|
|
67
|
+
[key]: instance
|
|
68
|
+
};
|
|
69
|
+
this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
|
|
70
|
+
instance.logger.info(chalk2.bold("Initializing"));
|
|
71
|
+
await instance.init();
|
|
72
|
+
instance.logger.info(chalk2.bold("Initialized"));
|
|
73
|
+
}, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
|
|
74
|
+
return Object.assign(this, entry);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
14
77
|
function _ts_decorate(decorators, target, key, desc) {
|
|
15
78
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
79
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -22,54 +85,6 @@ function _ts_metadata(k, v) {
|
|
|
22
85
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
23
86
|
}
|
|
24
87
|
__name(_ts_metadata, "_ts_metadata");
|
|
25
|
-
var Globals = class extends Envapter {
|
|
26
|
-
static {
|
|
27
|
-
__name(this, "Globals");
|
|
28
|
-
}
|
|
29
|
-
static botToken;
|
|
30
|
-
// Healthcheck
|
|
31
|
-
static healthCheckPort;
|
|
32
|
-
static healthCheckPath;
|
|
33
|
-
// Coordinated Shutdown
|
|
34
|
-
static shutdownIsEnabled;
|
|
35
|
-
// Unknown Exception Webhook URL
|
|
36
|
-
static unknownExceptionWebhookUrl;
|
|
37
|
-
// Variables
|
|
38
|
-
/** Default color for bot embeds - can be overridden by setting Globals.botColor */
|
|
39
|
-
static botColor = this.isProduction ? "#fe565a" : "#3fa045";
|
|
40
|
-
};
|
|
41
|
-
_ts_decorate([
|
|
42
|
-
Envapt("DISCORD_BOT_TOKEN", {
|
|
43
|
-
fallback: void 0
|
|
44
|
-
}),
|
|
45
|
-
_ts_metadata("design:type", String)
|
|
46
|
-
], Globals, "botToken", void 0);
|
|
47
|
-
_ts_decorate([
|
|
48
|
-
Envapt("HEALTH_CHECK_PORT", {
|
|
49
|
-
fallback: 6956
|
|
50
|
-
}),
|
|
51
|
-
_ts_metadata("design:type", Number)
|
|
52
|
-
], Globals, "healthCheckPort", void 0);
|
|
53
|
-
_ts_decorate([
|
|
54
|
-
Envapt("HEALTH_CHECK_PATH", {
|
|
55
|
-
fallback: "/healthcheck"
|
|
56
|
-
}),
|
|
57
|
-
_ts_metadata("design:type", String)
|
|
58
|
-
], Globals, "healthCheckPath", void 0);
|
|
59
|
-
_ts_decorate([
|
|
60
|
-
Envapt("SHUTDOWN_IS_ENABLED", {
|
|
61
|
-
fallback: false
|
|
62
|
-
}),
|
|
63
|
-
_ts_metadata("design:type", Boolean)
|
|
64
|
-
], Globals, "shutdownIsEnabled", void 0);
|
|
65
|
-
_ts_decorate([
|
|
66
|
-
Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
|
|
67
|
-
fallback: void 0
|
|
68
|
-
}),
|
|
69
|
-
_ts_metadata("design:type", String)
|
|
70
|
-
], Globals, "unknownExceptionWebhookUrl", void 0);
|
|
71
|
-
|
|
72
|
-
// src/interfaces/Components.ts
|
|
73
88
|
var BuilderTypes = {
|
|
74
89
|
command: SlashCommandBuilder,
|
|
75
90
|
embed: EmbedBuilder,
|
|
@@ -83,7 +98,13 @@ var BuilderTypes = {
|
|
|
83
98
|
modal: ModalBuilder,
|
|
84
99
|
context_menu: ContextMenuCommandBuilder,
|
|
85
100
|
subcommand: SlashCommandSubcommandBuilder,
|
|
86
|
-
group: SlashCommandSubcommandGroupBuilder
|
|
101
|
+
group: SlashCommandSubcommandGroupBuilder,
|
|
102
|
+
container: ContainerBuilder,
|
|
103
|
+
text_display: TextDisplayBuilder,
|
|
104
|
+
file: FileBuilder,
|
|
105
|
+
media: MediaGalleryBuilder,
|
|
106
|
+
section: SectionBuilder,
|
|
107
|
+
separator: SeparatorBuilder
|
|
87
108
|
};
|
|
88
109
|
var RowTypes = {
|
|
89
110
|
button: ActionRowBuilder,
|
|
@@ -117,6 +138,20 @@ var BaseComponent = class BaseComponent2 {
|
|
|
117
138
|
get instance() {
|
|
118
139
|
return this._component;
|
|
119
140
|
}
|
|
141
|
+
/**
|
|
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
|
+
*/
|
|
151
|
+
buildCustomId(prefix, ...args) {
|
|
152
|
+
if (args.length === 0) return prefix;
|
|
153
|
+
return `${prefix}:${args.join("-")}`;
|
|
154
|
+
}
|
|
120
155
|
};
|
|
121
156
|
var BuilderComponent = class extends BaseComponent {
|
|
122
157
|
static {
|
|
@@ -125,7 +160,7 @@ var BuilderComponent = class extends BaseComponent {
|
|
|
125
160
|
constructor(type) {
|
|
126
161
|
const ComponentClass = BuilderTypes[type];
|
|
127
162
|
super(ComponentClass);
|
|
128
|
-
if (this.instance instanceof EmbedBuilder) this.instance.setColor(
|
|
163
|
+
if (this.instance instanceof EmbedBuilder) this.instance.setColor(this.botColor);
|
|
129
164
|
if (this.instance instanceof SlashCommandBuilder || this.instance instanceof ContextMenuCommandBuilder) {
|
|
130
165
|
this.instance.setContexts(InteractionContextType.Guild);
|
|
131
166
|
}
|
|
@@ -133,21 +168,13 @@ var BuilderComponent = class extends BaseComponent {
|
|
|
133
168
|
get component() {
|
|
134
169
|
return this.instance;
|
|
135
170
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Builds a customId string for interactive components
|
|
138
|
-
*
|
|
139
|
-
* Creates customIds in the format "prefix:arg1-arg2-arg3" for buttons, modals, etc.
|
|
140
|
-
* Arguments are joined with hyphens and separated from prefix with a colon.
|
|
141
|
-
*
|
|
142
|
-
* @param prefix - The route prefix that handlers will match against
|
|
143
|
-
* @param args - Additional arguments to encode in the customId
|
|
144
|
-
* @returns Formatted customId string
|
|
145
|
-
*/
|
|
146
|
-
buildCustomId(prefix, ...args) {
|
|
147
|
-
if (args.length === 0) return prefix;
|
|
148
|
-
return `${prefix}:${args.join("-")}`;
|
|
149
|
-
}
|
|
150
171
|
};
|
|
172
|
+
_ts_decorate([
|
|
173
|
+
Envapt("DEFAULT_BOT_COLOR", {
|
|
174
|
+
fallback: "Default"
|
|
175
|
+
}),
|
|
176
|
+
_ts_metadata("design:type", typeof ColorResolvable === "undefined" ? Object : ColorResolvable)
|
|
177
|
+
], BuilderComponent.prototype, "botColor", void 0);
|
|
151
178
|
var RowComponent = class extends BaseComponent {
|
|
152
179
|
static {
|
|
153
180
|
__name(this, "RowComponent");
|
|
@@ -222,387 +249,6 @@ var CustomError = class extends Error {
|
|
|
222
249
|
}
|
|
223
250
|
};
|
|
224
251
|
|
|
225
|
-
// src/bot/errors/Database.ts
|
|
226
|
-
var DatabaseError = class extends CustomError {
|
|
227
|
-
static {
|
|
228
|
-
__name(this, "DatabaseError");
|
|
229
|
-
}
|
|
230
|
-
uuid;
|
|
231
|
-
_emit = true;
|
|
232
|
-
/**
|
|
233
|
-
* Creates a new DatabaseError.
|
|
234
|
-
*
|
|
235
|
-
* @param message - The error message describing what went wrong
|
|
236
|
-
* @param uuid - A unique identifier for this specific error instance
|
|
237
|
-
*/
|
|
238
|
-
constructor(message, uuid) {
|
|
239
|
-
super(message), this.uuid = uuid;
|
|
240
|
-
this.name = "DatabaseError";
|
|
241
|
-
this.response.setTitle("Database Error").setDescription(`An error occurred while interacting with the database.
|
|
242
|
-
### UUID: \`${this.uuid}\``);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
var Logger = class _Logger {
|
|
246
|
-
static {
|
|
247
|
-
__name(this, "Logger");
|
|
248
|
-
}
|
|
249
|
-
static instances = /* @__PURE__ */ new Map();
|
|
250
|
-
static instance(prefix) {
|
|
251
|
-
let instance = this.instances.get(prefix);
|
|
252
|
-
if (!instance) {
|
|
253
|
-
instance = new _Logger(prefix);
|
|
254
|
-
this.instances.set(prefix, instance);
|
|
255
|
-
}
|
|
256
|
-
return instance;
|
|
257
|
-
}
|
|
258
|
-
constructor(transportName) {
|
|
259
|
-
const consoleTransport = this.createConsoleTransport(transportName);
|
|
260
|
-
this.initializeLogger(consoleTransport);
|
|
261
|
-
}
|
|
262
|
-
getFormatCustomizations() {
|
|
263
|
-
const padding = 7;
|
|
264
|
-
return [
|
|
265
|
-
format.errors({
|
|
266
|
-
stack: true
|
|
267
|
-
}),
|
|
268
|
-
format.splat(),
|
|
269
|
-
format.colorize({
|
|
270
|
-
level: true
|
|
271
|
-
}),
|
|
272
|
-
format.timestamp({
|
|
273
|
-
format: "D MMM, hh:mm:ss a"
|
|
274
|
-
}),
|
|
275
|
-
format.printf((info) => {
|
|
276
|
-
const ts = String(info.timestamp ?? "");
|
|
277
|
-
const lvl = String(info.level).padEnd(padding);
|
|
278
|
-
const lbl = String(info.label ?? "");
|
|
279
|
-
const msg = String(info.message ?? "");
|
|
280
|
-
const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;
|
|
281
|
-
const splatSym = Symbol.for("splat");
|
|
282
|
-
const raw = info[splatSym];
|
|
283
|
-
const extras = Array.isArray(raw) ? raw : [];
|
|
284
|
-
const cleaned = extras.filter((x) => !(x instanceof Error)).filter((x) => {
|
|
285
|
-
if (!x) return false;
|
|
286
|
-
if (typeof x !== "object") return true;
|
|
287
|
-
return Object.keys(x).length > 0;
|
|
288
|
-
});
|
|
289
|
-
let rendered = base;
|
|
290
|
-
if (typeof info.stack === "string") {
|
|
291
|
-
rendered += `
|
|
292
|
-
${String(info.stack)}`;
|
|
293
|
-
}
|
|
294
|
-
if (cleaned.length) {
|
|
295
|
-
const parts = [];
|
|
296
|
-
for (const x of cleaned) {
|
|
297
|
-
if (typeof x === "string") parts.push(x);
|
|
298
|
-
else {
|
|
299
|
-
try {
|
|
300
|
-
parts.push(JSON.stringify(x, null, 2));
|
|
301
|
-
} catch {
|
|
302
|
-
parts.push(String(x));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
rendered += `
|
|
307
|
-
${parts.join(" ")}`;
|
|
308
|
-
}
|
|
309
|
-
return rendered;
|
|
310
|
-
})
|
|
311
|
-
];
|
|
312
|
-
}
|
|
313
|
-
createConsoleTransport(transportName) {
|
|
314
|
-
return new transports.Console({
|
|
315
|
-
format: format.combine(format.label({
|
|
316
|
-
label: transportName
|
|
317
|
-
}), ...this.getFormatCustomizations()),
|
|
318
|
-
level: Globals.isDevelopment ? "silly" : Globals.isStaging ? "debug" : "info"
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
initializeLogger(consoleTransport) {
|
|
322
|
-
const transportsArray = [
|
|
323
|
-
consoleTransport
|
|
324
|
-
];
|
|
325
|
-
if (Globals.isDevelopment) {
|
|
326
|
-
const maxSizeInMB = 10;
|
|
327
|
-
transportsArray.push(new transports.File({
|
|
328
|
-
filename: "logs/application.log",
|
|
329
|
-
level: "debug",
|
|
330
|
-
format: format.combine(format.uncolorize(), format.errors({
|
|
331
|
-
stack: true
|
|
332
|
-
}), format.timestamp(), format.json({
|
|
333
|
-
bigint: true,
|
|
334
|
-
space: 2
|
|
335
|
-
})),
|
|
336
|
-
maxsize: maxSizeInMB * 1024 * 1024,
|
|
337
|
-
maxFiles: 5,
|
|
338
|
-
tailable: true
|
|
339
|
-
}));
|
|
340
|
-
}
|
|
341
|
-
this.logger = createLogger({
|
|
342
|
-
transports: transportsArray
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Logs an error message with optional additional data.
|
|
347
|
-
*
|
|
348
|
-
* @param msg - The error message to log
|
|
349
|
-
* @param args - Additional data to include in the log entry
|
|
350
|
-
*/
|
|
351
|
-
error(msg, ...args) {
|
|
352
|
-
this.logger.error(msg, ...args);
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Logs a warning message with optional additional data.
|
|
356
|
-
*
|
|
357
|
-
* @param msg - The warning message to log
|
|
358
|
-
* @param args - Additional data to include in the log entry
|
|
359
|
-
*/
|
|
360
|
-
warn(msg, ...args) {
|
|
361
|
-
this.logger.warn(msg, ...args);
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Logs an informational message with optional additional data.
|
|
365
|
-
*
|
|
366
|
-
* @param msg - The informational message to log
|
|
367
|
-
* @param args - Additional data to include in the log entry
|
|
368
|
-
*/
|
|
369
|
-
info(msg, ...args) {
|
|
370
|
-
this.logger.info(msg, ...args);
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Logs an HTTP-related message with optional additional data.
|
|
374
|
-
*
|
|
375
|
-
* @param msg - The HTTP message to log
|
|
376
|
-
* @param args - Additional data to include in the log entry
|
|
377
|
-
*/
|
|
378
|
-
http(msg, ...args) {
|
|
379
|
-
this.logger.http(msg, ...args);
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Logs a verbose message with optional additional data.
|
|
383
|
-
*
|
|
384
|
-
* @param msg - The verbose message to log
|
|
385
|
-
* @param args - Additional data to include in the log entry
|
|
386
|
-
*/
|
|
387
|
-
verbose(msg, ...args) {
|
|
388
|
-
this.logger.verbose(msg, ...args);
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Logs a debug message with optional additional data.
|
|
392
|
-
*
|
|
393
|
-
* @param msg - The debug message to log
|
|
394
|
-
* @param args - Additional data to include in the log entry
|
|
395
|
-
*/
|
|
396
|
-
debug(msg, ...args) {
|
|
397
|
-
this.logger.debug(msg, ...args);
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Logs a silly/trace level message with optional additional data.
|
|
401
|
-
*
|
|
402
|
-
* @param msg - The silly message to log
|
|
403
|
-
* @param args - Additional data to include in the log entry
|
|
404
|
-
*/
|
|
405
|
-
silly(msg, ...args) {
|
|
406
|
-
this.logger.silly(msg, ...args);
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* Static method to log an error message with a specific prefix.
|
|
410
|
-
* Creates or retrieves a logger instance for the given prefix.
|
|
411
|
-
*
|
|
412
|
-
* @param prefix - The logger prefix/label to use
|
|
413
|
-
* @param msg - The error message to log
|
|
414
|
-
* @param args - Additional data to include in the log entry
|
|
415
|
-
*/
|
|
416
|
-
static Error(prefix, msg, ...args) {
|
|
417
|
-
const logger = this.instance(prefix);
|
|
418
|
-
logger.error(msg, ...args);
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Static method to log an informational message with a specific prefix.
|
|
422
|
-
* Creates or retrieves a logger instance for the given prefix.
|
|
423
|
-
*
|
|
424
|
-
* @param prefix - The logger prefix/label to use
|
|
425
|
-
* @param msg - The informational message to log
|
|
426
|
-
* @param args - Additional data to include in the log entry
|
|
427
|
-
*/
|
|
428
|
-
static Info(prefix, msg, ...args) {
|
|
429
|
-
const logger = this.instance(prefix);
|
|
430
|
-
logger.info(msg, ...args);
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Static method to log a warning message with a specific prefix.
|
|
434
|
-
* Creates or retrieves a logger instance for the given prefix.
|
|
435
|
-
*
|
|
436
|
-
* @param prefix - The logger prefix/label to use
|
|
437
|
-
* @param msg - The warning message to log
|
|
438
|
-
* @param args - Additional data to include in the log entry
|
|
439
|
-
*/
|
|
440
|
-
static Warn(prefix, msg, ...args) {
|
|
441
|
-
const logger = this.instance(prefix);
|
|
442
|
-
logger.warn(msg, ...args);
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Static method to log a debug message with a specific prefix.
|
|
446
|
-
* Creates or retrieves a logger instance for the given prefix.
|
|
447
|
-
*
|
|
448
|
-
* @param prefix - The logger prefix/label to use
|
|
449
|
-
* @param msg - The debug message to log
|
|
450
|
-
* @param args - Additional data to include in the log entry
|
|
451
|
-
*/
|
|
452
|
-
static Debug(prefix, msg, ...args) {
|
|
453
|
-
const logger = this.instance(prefix);
|
|
454
|
-
logger.debug(msg, ...args);
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Static method to log a silly/trace level message with a specific prefix.
|
|
458
|
-
* Creates or retrieves a logger instance for the given prefix.
|
|
459
|
-
*
|
|
460
|
-
* @param prefix - The logger prefix/label to use
|
|
461
|
-
* @param msg - The silly message to log
|
|
462
|
-
* @param args - Additional data to include in the log entry
|
|
463
|
-
*/
|
|
464
|
-
static Silly(prefix, msg, ...args) {
|
|
465
|
-
const logger = this.instance(prefix);
|
|
466
|
-
logger.silly(msg, ...args);
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
// src/library/Helpers.ts
|
|
471
|
-
function numberFixer(num, decimalPlaces) {
|
|
472
|
-
const factor = Math.pow(10, decimalPlaces);
|
|
473
|
-
return Math.round((num + Number.EPSILON) * factor) / factor;
|
|
474
|
-
}
|
|
475
|
-
__name(numberFixer, "numberFixer");
|
|
476
|
-
function percentage(num1, num2) {
|
|
477
|
-
return Number((num1 / num2 * 100).toFixed(2));
|
|
478
|
-
}
|
|
479
|
-
__name(percentage, "percentage");
|
|
480
|
-
function generateAsciiTable(data) {
|
|
481
|
-
if (data.length === 0) return "";
|
|
482
|
-
const firstRow = data[0];
|
|
483
|
-
if (!firstRow || firstRow.length === 0) return "";
|
|
484
|
-
let table = "";
|
|
485
|
-
const columnWidths = [];
|
|
486
|
-
for (let i = 0; i < firstRow.length; i++) {
|
|
487
|
-
let maxWidth = 0;
|
|
488
|
-
for (const row of data) {
|
|
489
|
-
const cell = row[i];
|
|
490
|
-
if (cell !== void 0) maxWidth = Math.max(maxWidth, cell.length);
|
|
491
|
-
}
|
|
492
|
-
columnWidths.push(maxWidth);
|
|
493
|
-
}
|
|
494
|
-
const createLine = /* @__PURE__ */ __name((char, left, intersect, right) => {
|
|
495
|
-
let line = left;
|
|
496
|
-
columnWidths.forEach((width, index) => {
|
|
497
|
-
line += char.repeat(width + 2);
|
|
498
|
-
if (index < columnWidths.length - 1) line += intersect;
|
|
499
|
-
else line += right;
|
|
500
|
-
});
|
|
501
|
-
line += "\n";
|
|
502
|
-
return line;
|
|
503
|
-
}, "createLine");
|
|
504
|
-
table += createLine("\u2550", "\u2554", "\u2566", "\u2557");
|
|
505
|
-
data.forEach((row, rowIndex) => {
|
|
506
|
-
table += "\u2551";
|
|
507
|
-
row.forEach((cell, columnIndex) => {
|
|
508
|
-
const columnWidth = columnWidths[columnIndex];
|
|
509
|
-
if (columnWidth !== void 0) table += ` ${cell.padEnd(columnWidth)} \u2551`;
|
|
510
|
-
});
|
|
511
|
-
table += "\n";
|
|
512
|
-
if (rowIndex < data.length - 1) table += createLine("\u2500", "\u2560", "\u256C", "\u2563");
|
|
513
|
-
else table += createLine("\u2550", "\u255A", "\u2569", "\u255D");
|
|
514
|
-
});
|
|
515
|
-
return table;
|
|
516
|
-
}
|
|
517
|
-
__name(generateAsciiTable, "generateAsciiTable");
|
|
518
|
-
function formatWord(word) {
|
|
519
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
520
|
-
}
|
|
521
|
-
__name(formatWord, "formatWord");
|
|
522
|
-
function longestStringLength(arr) {
|
|
523
|
-
return Math.max(...arr.map((el) => el.toString().length));
|
|
524
|
-
}
|
|
525
|
-
__name(longestStringLength, "longestStringLength");
|
|
526
|
-
function currentTime() {
|
|
527
|
-
return Math.floor(Date.now() / 1e3);
|
|
528
|
-
}
|
|
529
|
-
__name(currentTime, "currentTime");
|
|
530
|
-
function ordinal(n) {
|
|
531
|
-
const s = [
|
|
532
|
-
"th",
|
|
533
|
-
"st",
|
|
534
|
-
"nd",
|
|
535
|
-
"rd"
|
|
536
|
-
];
|
|
537
|
-
const v = n % 100;
|
|
538
|
-
const index = (v - 20) % 10;
|
|
539
|
-
const suffix = s[index] ?? s[v] ?? s[0];
|
|
540
|
-
if (!suffix) return `${n}th`;
|
|
541
|
-
return `${n}${suffix}`;
|
|
542
|
-
}
|
|
543
|
-
__name(ordinal, "ordinal");
|
|
544
|
-
function prettyDifference(numBefore, numAfter) {
|
|
545
|
-
return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();
|
|
546
|
-
}
|
|
547
|
-
__name(prettyDifference, "prettyDifference");
|
|
548
|
-
function isTsOrJsFile(entry) {
|
|
549
|
-
return entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".map");
|
|
550
|
-
}
|
|
551
|
-
__name(isTsOrJsFile, "isTsOrJsFile");
|
|
552
|
-
async function traverseDirectory(dir, callback) {
|
|
553
|
-
let entries;
|
|
554
|
-
try {
|
|
555
|
-
entries = await readdir(dir, {
|
|
556
|
-
withFileTypes: true
|
|
557
|
-
});
|
|
558
|
-
} catch {
|
|
559
|
-
Logger.Error("Failed to read directory", dir);
|
|
560
|
-
entries = [];
|
|
561
|
-
}
|
|
562
|
-
for (const entry of entries) {
|
|
563
|
-
const fullPath = path.join(dir, entry.name);
|
|
564
|
-
const relativePath = path.relative(process.cwd(), fullPath);
|
|
565
|
-
if (entry.isDirectory()) {
|
|
566
|
-
await traverseDirectory(fullPath, callback);
|
|
567
|
-
} else if (isTsOrJsFile(entry)) {
|
|
568
|
-
const imported = await import(fullPath);
|
|
569
|
-
await callback(fullPath, relativePath, imported);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
__name(traverseDirectory, "traverseDirectory");
|
|
574
|
-
function throwCustomError(error, message, CustomError2) {
|
|
575
|
-
const uuid = crypto.randomUUID();
|
|
576
|
-
Logger.Error("Throwing Custom Error", error.name);
|
|
577
|
-
if (typeof CustomError2 === typeof DatabaseError) {
|
|
578
|
-
const errorMessage = error instanceof Error ? error.message : message;
|
|
579
|
-
throw new CustomError2(errorMessage, uuid);
|
|
580
|
-
} else {
|
|
581
|
-
if (error instanceof Error) {
|
|
582
|
-
throw new CustomError2(`${message}: ${error.message ? error.message : error.toString()}`);
|
|
583
|
-
} else {
|
|
584
|
-
throw new CustomError2(message);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
__name(throwCustomError, "throwCustomError");
|
|
589
|
-
function prettify(key) {
|
|
590
|
-
return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").trim();
|
|
591
|
-
}
|
|
592
|
-
__name(prettify, "prettify");
|
|
593
|
-
function fyShuffle(items) {
|
|
594
|
-
const array = items.slice();
|
|
595
|
-
for (let i = array.length - 1; i > 0; i--) {
|
|
596
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
597
|
-
[array[i], array[j]] = [
|
|
598
|
-
array[j],
|
|
599
|
-
array[i]
|
|
600
|
-
];
|
|
601
|
-
}
|
|
602
|
-
return array;
|
|
603
|
-
}
|
|
604
|
-
__name(fyShuffle, "fyShuffle");
|
|
605
|
-
|
|
606
252
|
// src/bot/decorators/CommandRegisterable.ts
|
|
607
253
|
var CommandMetadataKey = Symbol("command:metadata");
|
|
608
254
|
function RegisterCommand(scope, guilds = []) {
|
|
@@ -634,14 +280,14 @@ var CommandRegistry = class {
|
|
|
634
280
|
async init() {
|
|
635
281
|
if (this.isInitialised) return;
|
|
636
282
|
this.isInitialised = true;
|
|
637
|
-
this.logger.info(
|
|
283
|
+
this.logger.info(chalk2.bold(this.core.config.bot.commands.path));
|
|
638
284
|
await this.loadCommands(this.core.config.bot.commands.path);
|
|
639
|
-
this.logger.info(`${
|
|
285
|
+
this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold(this.globalCommands.length)} global, ${chalk2.magenta.bold(this.guildCommands.size)} guild groups`);
|
|
640
286
|
}
|
|
641
287
|
async loadCommands(dir) {
|
|
642
288
|
await traverseDirectory(dir, (_full, rel, mod) => {
|
|
643
289
|
for (const exported of Object.values(mod)) if (this.isCommandClass(exported)) this.registerCommand(exported, rel);
|
|
644
|
-
});
|
|
290
|
+
}, this.logger);
|
|
645
291
|
}
|
|
646
292
|
isCommandClass(obj) {
|
|
647
293
|
if (typeof obj !== "function") return false;
|
|
@@ -655,24 +301,24 @@ var CommandRegistry = class {
|
|
|
655
301
|
const commandType = comp instanceof SlashCommandBuilder ? "slash command" : "context menu command";
|
|
656
302
|
if (meta.scope === "global") {
|
|
657
303
|
this.globalCommands.push(comp);
|
|
658
|
-
this.logger.info(`${
|
|
659
|
-
this.logger.info(` \u2192 Global ${commandType}: ${
|
|
304
|
+
this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(ctor.name)} from ${chalk2.gray(rel)}`);
|
|
305
|
+
this.logger.info(` \u2192 Global ${commandType}: ${chalk2.bold.cyan(comp.name)}`);
|
|
660
306
|
} else {
|
|
661
307
|
for (const g of meta.guilds) {
|
|
662
308
|
const arr = this.guildCommands.get(g) ?? [];
|
|
663
309
|
arr.push(comp);
|
|
664
310
|
this.guildCommands.set(g, arr);
|
|
665
311
|
}
|
|
666
|
-
this.logger.info(`${
|
|
667
|
-
this.logger.info(` \u2192 Guild ${commandType}: ${
|
|
312
|
+
this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(ctor.name)} from ${chalk2.gray(rel)}`);
|
|
313
|
+
this.logger.info(` \u2192 Guild ${commandType}: ${chalk2.bold.cyan(comp.name)} for ${chalk2.magenta.bold(meta.guilds.length)} guild(s)`);
|
|
668
314
|
}
|
|
669
315
|
}
|
|
670
316
|
async setCommands() {
|
|
671
317
|
if (this.globalCommands.length > 0) {
|
|
672
318
|
await this.core.bot.client.application?.commands.set(this.globalCommands);
|
|
673
319
|
const tag = this.globalCommands.length === 1 ? "command" : "commands";
|
|
674
|
-
this.logger.info(`${
|
|
675
|
-
this.logger.info(` \u2192 ${this.globalCommands.map((command) =>
|
|
320
|
+
this.logger.info(`${chalk2.bold.green("Configured")} ${chalk2.magenta.bold(this.globalCommands.length)} global ${tag}`);
|
|
321
|
+
this.logger.info(` \u2192 ${this.globalCommands.map((command) => chalk2.bold.cyan(command.name)).join(", ")}`);
|
|
676
322
|
}
|
|
677
323
|
for (const [guildId, commands] of this.guildCommands.entries()) {
|
|
678
324
|
const guild = this.core.bot.client.guilds.cache.get(guildId);
|
|
@@ -682,8 +328,8 @@ var CommandRegistry = class {
|
|
|
682
328
|
}
|
|
683
329
|
await guild.commands.set(commands);
|
|
684
330
|
const tag = commands.length === 1 ? "command" : "commands";
|
|
685
|
-
this.logger.info(`${
|
|
686
|
-
this.logger.info(` \u2192 ${commands.map((command) =>
|
|
331
|
+
this.logger.info(`${chalk2.bold.green("Configured")} ${chalk2.magenta.bold(commands.length)} ${tag} for guild ${chalk2.bold.yellow(guild.name)}`);
|
|
332
|
+
this.logger.info(` \u2192 ${commands.map((command) => chalk2.bold.cyan(command.name)).join(", ")}`);
|
|
687
333
|
}
|
|
688
334
|
}
|
|
689
335
|
};
|
|
@@ -800,24 +446,24 @@ var EventController = class {
|
|
|
800
446
|
}
|
|
801
447
|
this.isInitialized = true;
|
|
802
448
|
const handlersDir = this.core.config.bot.events.path;
|
|
803
|
-
this.logger.info(
|
|
449
|
+
this.logger.info(chalk2.bold(handlersDir));
|
|
804
450
|
await this.loadHandlers(handlersDir);
|
|
805
451
|
this.attachToClient();
|
|
806
452
|
const loadedEventsArray = [];
|
|
807
453
|
this.eventMap.forEach((handlers, eventName) => {
|
|
808
|
-
loadedEventsArray.push(`${
|
|
454
|
+
loadedEventsArray.push(`${chalk2.magenta.bold(handlers.length)} ${eventName}`);
|
|
809
455
|
});
|
|
810
|
-
this.logger.info(`${
|
|
456
|
+
this.logger.info(`${chalk2.bold.green("Loaded")}: ${this.eventMap.size > 0 ? loadedEventsArray.join(", ") : "none"}`);
|
|
811
457
|
}
|
|
812
458
|
async loadHandlers(dir) {
|
|
813
459
|
await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
|
|
814
460
|
for (const val of Object.values(imported)) {
|
|
815
461
|
if (this.isEventHandlerClass(val)) {
|
|
816
462
|
this.registerHandler(val);
|
|
817
|
-
this.logger.info(`${
|
|
463
|
+
this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
|
|
818
464
|
}
|
|
819
465
|
}
|
|
820
|
-
});
|
|
466
|
+
}, this.logger);
|
|
821
467
|
}
|
|
822
468
|
isEventHandlerClass(obj) {
|
|
823
469
|
if (typeof obj !== "function") return false;
|
|
@@ -835,7 +481,7 @@ var EventController = class {
|
|
|
835
481
|
}
|
|
836
482
|
attachToClient() {
|
|
837
483
|
for (const [eventName] of this.eventMap) {
|
|
838
|
-
this.logger.debug(`Attaching ${
|
|
484
|
+
this.logger.debug(`Attaching ${chalk2.bold.green(eventName)} to ${chalk2.bold.yellow(this.core.bot.client.user?.username)}`);
|
|
839
485
|
this.core.bot.client.on(eventName, (...args) => {
|
|
840
486
|
void (async () => {
|
|
841
487
|
await this.processEvent(eventName, args);
|
|
@@ -848,7 +494,7 @@ var EventController = class {
|
|
|
848
494
|
if (!handlerCtors || handlerCtors.length === 0) return;
|
|
849
495
|
for (const HandlerCtor of handlerCtors) {
|
|
850
496
|
try {
|
|
851
|
-
this.logger.debug(`Processing ${
|
|
497
|
+
this.logger.debug(`Processing ${chalk2.bold.green(eventName)} with ${chalk2.gray(HandlerCtor.name)}`);
|
|
852
498
|
const handler = new HandlerCtor(args, this.core);
|
|
853
499
|
if (handler.hasChecks()) {
|
|
854
500
|
await handler.runChecks();
|
|
@@ -959,6 +605,29 @@ function storeMetadata(symbol, routes, constructor) {
|
|
|
959
605
|
Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
|
|
960
606
|
}
|
|
961
607
|
__name(storeMetadata, "storeMetadata");
|
|
608
|
+
|
|
609
|
+
// src/bot/errors/Database.ts
|
|
610
|
+
var DatabaseError = class extends CustomError {
|
|
611
|
+
static {
|
|
612
|
+
__name(this, "DatabaseError");
|
|
613
|
+
}
|
|
614
|
+
uuid;
|
|
615
|
+
_emit = true;
|
|
616
|
+
/**
|
|
617
|
+
* Creates a new DatabaseError.
|
|
618
|
+
*
|
|
619
|
+
* @param message - The error message describing what went wrong
|
|
620
|
+
* @param uuid - A unique identifier for this specific error instance
|
|
621
|
+
*/
|
|
622
|
+
constructor(message, uuid) {
|
|
623
|
+
super(message), this.uuid = uuid;
|
|
624
|
+
this.name = "DatabaseError";
|
|
625
|
+
this.response.setTitle("Database Error").setDescription(`An error occurred while interacting with the database.
|
|
626
|
+
### UUID: \`${this.uuid}\``);
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// src/bot/utilities/ErrorHandlingUtils.ts
|
|
962
631
|
var ErrorHandlingUtils = class {
|
|
963
632
|
static {
|
|
964
633
|
__name(this, "ErrorHandlingUtils");
|
|
@@ -1129,31 +798,31 @@ var InteractionController = class {
|
|
|
1129
798
|
if (this.isInitialized) return;
|
|
1130
799
|
this.isInitialized = true;
|
|
1131
800
|
const handlersDir = this.core.config.bot.interactions.path;
|
|
1132
|
-
this.logger.info(
|
|
801
|
+
this.logger.info(chalk2.bold(handlersDir));
|
|
1133
802
|
await this.loadHandlers(handlersDir);
|
|
1134
803
|
this.attachToClient();
|
|
1135
|
-
this.logger.info(`${
|
|
1136
|
-
this.logger.info(`\u2192 ${
|
|
1137
|
-
this.logger.info(`\u2192 ${
|
|
1138
|
-
this.logger.info(`\u2192 ${
|
|
1139
|
-
this.logger.info(`\u2192 ${
|
|
1140
|
-
this.logger.info(`\u2192 ${
|
|
1141
|
-
this.logger.info(`\u2192 ${
|
|
1142
|
-
this.logger.info(`\u2192 ${
|
|
1143
|
-
this.logger.info(`\u2192 ${
|
|
1144
|
-
this.logger.info(`\u2192 ${
|
|
1145
|
-
this.logger.info(`\u2192 ${
|
|
1146
|
-
this.logger.info(`\u2192 ${
|
|
804
|
+
this.logger.info(`${chalk2.bold.green("Loaded interaction handlers:")}`);
|
|
805
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.slashMap.size)} slash commands`);
|
|
806
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.buttonMap.size)} buttons`);
|
|
807
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.modalMap.size)} modals`);
|
|
808
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.stringSelectMap.size)} string selects`);
|
|
809
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.userSelectMap.size)} user selects`);
|
|
810
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.roleSelectMap.size)} role selects`);
|
|
811
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.channelSelectMap.size)} channel selects`);
|
|
812
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.mentionableSelectMap.size)} mentionable selects`);
|
|
813
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.messageContextMenuMap.size)} message context menus`);
|
|
814
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.userContextMenuMap.size)} user context menus`);
|
|
815
|
+
this.logger.info(`\u2192 ${chalk2.magenta.bold(this.autocompleteMap.size)} autocomplete`);
|
|
1147
816
|
}
|
|
1148
817
|
async loadHandlers(dir) {
|
|
1149
818
|
await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
|
|
1150
819
|
for (const val of Object.values(imported)) {
|
|
1151
820
|
if (this.isHandlerClass(val)) {
|
|
1152
821
|
this.registerHandler(val);
|
|
1153
|
-
this.logger.info(`${
|
|
822
|
+
this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
|
|
1154
823
|
}
|
|
1155
824
|
}
|
|
1156
|
-
});
|
|
825
|
+
}, this.logger);
|
|
1157
826
|
}
|
|
1158
827
|
isHandlerClass(obj) {
|
|
1159
828
|
if (typeof obj !== "function") return false;
|
|
@@ -1219,7 +888,7 @@ var InteractionController = class {
|
|
|
1219
888
|
attachToClient() {
|
|
1220
889
|
this.core.bot.client.on(Events.InteractionCreate, (interaction) => {
|
|
1221
890
|
this.handleInteraction(interaction).catch((err) => {
|
|
1222
|
-
this.logger.error(`[${
|
|
891
|
+
this.logger.error(`[${chalk2.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
|
|
1223
892
|
});
|
|
1224
893
|
});
|
|
1225
894
|
}
|
|
@@ -1248,10 +917,10 @@ var InteractionController = class {
|
|
|
1248
917
|
}
|
|
1249
918
|
let HandlerCtor = getHandler(key);
|
|
1250
919
|
if (!HandlerCtor) {
|
|
1251
|
-
this.logger.warn(`No handler found for key ${
|
|
920
|
+
this.logger.warn(`No handler found for key ${chalk2.bold.cyan(key)}. Falling back to UnhandledEvent.`);
|
|
1252
921
|
HandlerCtor = UnhandledEvent;
|
|
1253
922
|
}
|
|
1254
|
-
this.logger.debug(`Processing ${
|
|
923
|
+
this.logger.debug(`Processing ${chalk2.bold.green(key)} with ${chalk2.gray(HandlerCtor.name)}`);
|
|
1255
924
|
const handler = new HandlerCtor(interaction, this.core, args);
|
|
1256
925
|
if (handler.hasChecks()) await handler.runChecks();
|
|
1257
926
|
if (handler.shouldBreak()) return;
|
|
@@ -1361,7 +1030,7 @@ var EmojiInjector = class {
|
|
|
1361
1030
|
}
|
|
1362
1031
|
async init() {
|
|
1363
1032
|
if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
|
|
1364
|
-
this.logger.info(`${
|
|
1033
|
+
this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold("0")} emojis`);
|
|
1365
1034
|
return;
|
|
1366
1035
|
}
|
|
1367
1036
|
const configEmojis = this.core.config.bot.emojis;
|
|
@@ -1372,331 +1041,25 @@ var EmojiInjector = class {
|
|
|
1372
1041
|
if (emoji) {
|
|
1373
1042
|
configEmojis[key] = `<${emoji.identifier}>`;
|
|
1374
1043
|
foundCount++;
|
|
1044
|
+
this.logger.debug(`${chalk2.bold.green("Found")}: ${chalk2.magenta.bold(emojiName)} (${emoji.id})`);
|
|
1375
1045
|
}
|
|
1376
1046
|
});
|
|
1377
|
-
this.logger.info(`${
|
|
1378
|
-
}
|
|
1379
|
-
};
|
|
1380
|
-
var Plugin = class {
|
|
1381
|
-
static {
|
|
1382
|
-
__name(this, "Plugin");
|
|
1383
|
-
}
|
|
1384
|
-
pluggable;
|
|
1385
|
-
constructor(pluggable) {
|
|
1386
|
-
this.pluggable = pluggable;
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
var Pluggable = class _Pluggable {
|
|
1390
|
-
static {
|
|
1391
|
-
__name(this, "Pluggable");
|
|
1392
|
-
}
|
|
1393
|
-
isInitialized = false;
|
|
1394
|
-
shutdown;
|
|
1395
|
-
startup;
|
|
1396
|
-
static PLUGIN_INIT_TIMEOUT_MS = 15e3;
|
|
1397
|
-
constructor(shutdown, startup) {
|
|
1398
|
-
this.shutdown = shutdown;
|
|
1399
|
-
this.startup = startup;
|
|
1400
|
-
}
|
|
1401
|
-
async init() {
|
|
1402
|
-
if (this.isInitialized) return this;
|
|
1403
|
-
await this.startup.run();
|
|
1404
|
-
this.isInitialized = true;
|
|
1405
|
-
return this;
|
|
1406
|
-
}
|
|
1407
|
-
/**
|
|
1408
|
-
* Attaches a plugin to this instance
|
|
1409
|
-
*
|
|
1410
|
-
* Plugins provide external functionality and are initialized during the specified startup phase.
|
|
1411
|
-
* The plugin instance becomes available as a property in `core` wherever it's available.
|
|
1412
|
-
*
|
|
1413
|
-
* Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
|
|
1414
|
-
*
|
|
1415
|
-
* @typeParam Key - The property name for accessing the plugin
|
|
1416
|
-
* @typeParam Ctor - The plugin constructor type
|
|
1417
|
-
* @param key - Property name to access the plugin instance
|
|
1418
|
-
* @param Plugin - Plugin constructor class
|
|
1419
|
-
* @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
|
|
1420
|
-
* @param args - Additional arguments to pass to the plugin constructor
|
|
1421
|
-
* @returns This instance with the plugin attached as a typed property
|
|
1422
|
-
* @throws An {@link Error} When called after initialization or if key already exists
|
|
1423
|
-
* @example
|
|
1424
|
-
* ```typescript
|
|
1425
|
-
* seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', dbName: 'seedcord' })
|
|
1426
|
-
* ```
|
|
1427
|
-
*/
|
|
1428
|
-
attach(key, Plugin2, startupPhase, ...args) {
|
|
1429
|
-
if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
|
|
1430
|
-
if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
|
|
1431
|
-
const instance = new Plugin2(this, ...args);
|
|
1432
|
-
const entry = {
|
|
1433
|
-
[key]: instance
|
|
1434
|
-
};
|
|
1435
|
-
this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
|
|
1436
|
-
instance.logger.info(chalk6.bold("Initializing"));
|
|
1437
|
-
await instance.init();
|
|
1438
|
-
instance.logger.info(chalk6.bold("Initialized"));
|
|
1439
|
-
}, _Pluggable.PLUGIN_INIT_TIMEOUT_MS);
|
|
1440
|
-
return Object.assign(this, entry);
|
|
1441
|
-
}
|
|
1442
|
-
};
|
|
1443
|
-
var CoordinatedLifecycle = class {
|
|
1444
|
-
static {
|
|
1445
|
-
__name(this, "CoordinatedLifecycle");
|
|
1446
|
-
}
|
|
1447
|
-
phaseOrder;
|
|
1448
|
-
phaseEnum;
|
|
1449
|
-
logger;
|
|
1450
|
-
events = new EventEmitter();
|
|
1451
|
-
tasksMap = /* @__PURE__ */ new Map();
|
|
1452
|
-
constructor(loggerName, phaseOrder, phaseEnum) {
|
|
1453
|
-
this.phaseOrder = phaseOrder;
|
|
1454
|
-
this.phaseEnum = phaseEnum;
|
|
1455
|
-
this.logger = new Logger(loggerName);
|
|
1456
|
-
this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
|
|
1457
|
-
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Adds a lifecycle task to a specific phase.
|
|
1460
|
-
*
|
|
1461
|
-
* Tasks are executed in phase order during lifecycle operations.
|
|
1462
|
-
* Each task has a timeout to prevent hanging operations.
|
|
1463
|
-
*
|
|
1464
|
-
* @param phase - The lifecycle phase to add the task to
|
|
1465
|
-
* @param taskName - Unique name for the task (used for logging and removal)
|
|
1466
|
-
* @param task - Async function to execute during the phase
|
|
1467
|
-
* @param timeoutMs - Maximum time allowed for task execution in milliseconds
|
|
1468
|
-
* @example
|
|
1469
|
-
* ```typescript
|
|
1470
|
-
* lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {
|
|
1471
|
-
* await database.connect();
|
|
1472
|
-
* }, 10000);
|
|
1473
|
-
* ```
|
|
1474
|
-
*/
|
|
1475
|
-
addTask(phase, taskName, task, timeoutMs) {
|
|
1476
|
-
if (!this.canAddTask()) return;
|
|
1477
|
-
const tasks = this.tasksMap.get(phase);
|
|
1478
|
-
if (!tasks) throw new Error(`Unknown phase: ${phase}`);
|
|
1479
|
-
tasks.push({
|
|
1480
|
-
name: taskName,
|
|
1481
|
-
task,
|
|
1482
|
-
timeout: timeoutMs
|
|
1483
|
-
});
|
|
1484
|
-
this.logger.debug(`${chalk6.italic("Added")} ${this.getTaskType()} task ${chalk6.bold.cyan(taskName)} to phase ${chalk6.bold.magenta(this.phaseEnum[phase])}`);
|
|
1485
|
-
}
|
|
1486
|
-
/**
|
|
1487
|
-
* Removes a lifecycle task from a specific phase.
|
|
1488
|
-
*
|
|
1489
|
-
* @param phase - The lifecycle phase to remove the task from
|
|
1490
|
-
* @param taskName - Name of the task to remove
|
|
1491
|
-
* @returns True if the task was found and removed, false otherwise
|
|
1492
|
-
*/
|
|
1493
|
-
removeTask(phase, taskName) {
|
|
1494
|
-
if (!this.canRemoveTask()) return false;
|
|
1495
|
-
const tasks = this.tasksMap.get(phase);
|
|
1496
|
-
if (!tasks) return false;
|
|
1497
|
-
const initialLength = tasks.length;
|
|
1498
|
-
const filteredTasks = tasks.filter((task) => task.name !== taskName);
|
|
1499
|
-
this.tasksMap.set(phase, filteredTasks);
|
|
1500
|
-
const removed = initialLength !== filteredTasks.length;
|
|
1501
|
-
if (removed) {
|
|
1502
|
-
this.logger.debug(`${chalk6.italic("Removed")} ${this.getTaskType()} task ${chalk6.bold.cyan(taskName)} from phase ${chalk6.bold.magenta(this.phaseEnum[phase])}`);
|
|
1503
|
-
}
|
|
1504
|
-
return removed;
|
|
1505
|
-
}
|
|
1506
|
-
/**
|
|
1507
|
-
* Run all tasks in a specific phase
|
|
1508
|
-
*/
|
|
1509
|
-
async runPhase(phase) {
|
|
1510
|
-
const tasks = this.tasksMap.get(phase) ?? [];
|
|
1511
|
-
if (tasks.length === 0) {
|
|
1512
|
-
this.logger.warn(`No tasks to run in phase ${chalk6.bold.magenta(this.phaseEnum[phase])}`);
|
|
1513
|
-
return;
|
|
1514
|
-
}
|
|
1515
|
-
this.logger.info(`${chalk6.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk6.bold.magenta(this.phaseEnum[phase])} with ${chalk6.bold.cyan(tasks.length)} tasks`);
|
|
1516
|
-
this.emit(`phase:${phase}:start`);
|
|
1517
|
-
const results = await this.executeTasksInPhase(phase, tasks);
|
|
1518
|
-
const failures = results.filter((r) => r.status === "rejected").length;
|
|
1519
|
-
if (failures > 0) {
|
|
1520
|
-
const errorMessage = `Phase ${chalk6.bold.magenta(this.phaseEnum[phase])} completed with ${chalk6.bold.red(failures)} failed tasks`;
|
|
1521
|
-
throw new Error(errorMessage);
|
|
1522
|
-
} else {
|
|
1523
|
-
this.logger.info(`Phase ${chalk6.bold.magenta(this.phaseEnum[phase])} ${chalk6.bold.green("completed successfully")}`);
|
|
1524
|
-
}
|
|
1525
|
-
this.emit(`phase:${phase}:complete`);
|
|
1526
|
-
}
|
|
1527
|
-
/**
|
|
1528
|
-
* Run a single task with timeout
|
|
1529
|
-
*/
|
|
1530
|
-
async runTaskWithTimeout(phase, task) {
|
|
1531
|
-
this.logger.info(`${chalk6.italic("Starting")} task ${chalk6.bold.cyan(task.name)} in phase ${chalk6.bold.magenta(this.phaseEnum[phase])}`);
|
|
1532
|
-
try {
|
|
1533
|
-
await Promise.race([
|
|
1534
|
-
task.task(),
|
|
1535
|
-
new Promise((_, reject) => {
|
|
1536
|
-
setTimeout(() => {
|
|
1537
|
-
reject(new Error(`Task '${task.name}' timed out after ${task.timeout}ms`));
|
|
1538
|
-
}, task.timeout);
|
|
1539
|
-
})
|
|
1540
|
-
]);
|
|
1541
|
-
this.logger.info(`${chalk6.italic("Completed")} task ${chalk6.bold.cyan(task.name)} in phase ${chalk6.bold.magenta(this.phaseEnum[phase])}`);
|
|
1542
|
-
} catch (error) {
|
|
1543
|
-
this.logger.error(`${chalk6.italic("Failed")} task ${chalk6.bold.cyan(task.name)} in phase ${chalk6.bold.magenta(this.phaseEnum[phase])}:`, error);
|
|
1544
|
-
throw error;
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
/**
|
|
1548
|
-
* Subscribe to lifecycle events
|
|
1549
|
-
*/
|
|
1550
|
-
on(event, listener) {
|
|
1551
|
-
this.events.on(event, listener);
|
|
1552
|
-
}
|
|
1553
|
-
/**
|
|
1554
|
-
* Unsubscribe from lifecycle events
|
|
1555
|
-
*/
|
|
1556
|
-
off(event, listener) {
|
|
1557
|
-
this.events.off(event, listener);
|
|
1558
|
-
}
|
|
1559
|
-
emit(event, ...args) {
|
|
1560
|
-
return this.events.emit(event, ...args);
|
|
1561
|
-
}
|
|
1562
|
-
};
|
|
1563
|
-
|
|
1564
|
-
// src/services/Lifecycle/CoordinatedShutdown.ts
|
|
1565
|
-
var ShutdownPhase = /* @__PURE__ */ (function(ShutdownPhase2) {
|
|
1566
|
-
ShutdownPhase2[ShutdownPhase2["StopAcceptingRequests"] = 1] = "StopAcceptingRequests";
|
|
1567
|
-
ShutdownPhase2[ShutdownPhase2["StopServices"] = 2] = "StopServices";
|
|
1568
|
-
ShutdownPhase2[ShutdownPhase2["ExternalResources"] = 3] = "ExternalResources";
|
|
1569
|
-
ShutdownPhase2[ShutdownPhase2["DiscordCleanup"] = 4] = "DiscordCleanup";
|
|
1570
|
-
ShutdownPhase2[ShutdownPhase2["FinalCleanup"] = 5] = "FinalCleanup";
|
|
1571
|
-
return ShutdownPhase2;
|
|
1572
|
-
})({});
|
|
1573
|
-
var PHASE_ORDER = [
|
|
1574
|
-
1,
|
|
1575
|
-
2,
|
|
1576
|
-
3,
|
|
1577
|
-
4,
|
|
1578
|
-
5
|
|
1579
|
-
];
|
|
1580
|
-
var LOG_FLUSH_DELAY_MS = 500;
|
|
1581
|
-
var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
1582
|
-
static {
|
|
1583
|
-
__name(this, "CoordinatedShutdown");
|
|
1584
|
-
}
|
|
1585
|
-
isShuttingDown = false;
|
|
1586
|
-
exitCode = 0;
|
|
1587
|
-
constructor() {
|
|
1588
|
-
super("CoordinatedShutdown", PHASE_ORDER, ShutdownPhase);
|
|
1589
|
-
this.registerSignalHandlers();
|
|
1590
|
-
}
|
|
1591
|
-
canAddTask() {
|
|
1592
|
-
return Globals.shutdownIsEnabled;
|
|
1593
|
-
}
|
|
1594
|
-
canRemoveTask() {
|
|
1595
|
-
return true;
|
|
1596
|
-
}
|
|
1597
|
-
getTaskType() {
|
|
1598
|
-
return "shutdown";
|
|
1599
|
-
}
|
|
1600
|
-
async executeTasksInPhase(phase, tasks) {
|
|
1601
|
-
const results = [];
|
|
1602
|
-
for (const task of tasks) {
|
|
1603
|
-
results.push(await Promise.resolve().then(() => this.runTaskWithTimeout(phase, task)).then(() => ({
|
|
1604
|
-
status: "fulfilled",
|
|
1605
|
-
value: void 0
|
|
1606
|
-
}), (reason) => ({
|
|
1607
|
-
status: "rejected",
|
|
1608
|
-
reason
|
|
1609
|
-
})));
|
|
1610
|
-
}
|
|
1611
|
-
return results;
|
|
1612
|
-
}
|
|
1613
|
-
registerSignalHandlers() {
|
|
1614
|
-
if (!Globals.shutdownIsEnabled) return;
|
|
1615
|
-
process.on("SIGTERM", () => {
|
|
1616
|
-
this.logger.info(`Received ${chalk6.yellow.bold("SIGTERM")} signal`);
|
|
1617
|
-
void this.run(0);
|
|
1618
|
-
});
|
|
1619
|
-
process.on("SIGINT", () => {
|
|
1620
|
-
this.logger.info(`Received ${chalk6.yellow.bold("SIGINT")} signal`);
|
|
1621
|
-
void this.run(0);
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
/**
|
|
1625
|
-
* Adds a task to a specific shutdown phase with timeout.
|
|
1626
|
-
*
|
|
1627
|
-
* @param phase - The shutdown phase from {@link ShutdownPhase}
|
|
1628
|
-
* @param taskName - Unique identifier for the task
|
|
1629
|
-
* @param task - Async function to execute
|
|
1630
|
-
* @param timeoutMs - Task timeout in milliseconds (default: 5000)
|
|
1631
|
-
*/
|
|
1632
|
-
addTask(phase, taskName, task, timeoutMs = 5e3) {
|
|
1633
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
1634
|
-
}
|
|
1635
|
-
/**
|
|
1636
|
-
* Removes a task from a specific shutdown phase.
|
|
1637
|
-
*
|
|
1638
|
-
* @param phase - The shutdown phase to remove from
|
|
1639
|
-
* @param taskName - Name of the task to remove
|
|
1640
|
-
* @returns True if task was found and removed
|
|
1641
|
-
*/
|
|
1642
|
-
removeTask(phase, taskName) {
|
|
1643
|
-
return super.removeTask(phase, taskName);
|
|
1644
|
-
}
|
|
1645
|
-
/**
|
|
1646
|
-
* Executes the coordinated shutdown sequence.
|
|
1647
|
-
*
|
|
1648
|
-
* Runs all registered tasks across shutdown phases in reverse order.
|
|
1649
|
-
* Tasks within each phase are executed in parallel for faster shutdown.
|
|
1650
|
-
* Process exits with the specified code when complete.
|
|
1651
|
-
*
|
|
1652
|
-
* @param exitCode - Process exit code (default: 0)
|
|
1653
|
-
* @returns Promise that resolves when shutdown is complete
|
|
1654
|
-
* @example
|
|
1655
|
-
* ```typescript
|
|
1656
|
-
* shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);
|
|
1657
|
-
* await shutdown.run(0); // Graceful shutdown
|
|
1658
|
-
* ```
|
|
1659
|
-
*/
|
|
1660
|
-
async run(exitCode = 0) {
|
|
1661
|
-
if (this.isShuttingDown) {
|
|
1662
|
-
this.logger.warn("Shutdown sequence already in progress");
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
this.isShuttingDown = true;
|
|
1666
|
-
this.exitCode = exitCode;
|
|
1667
|
-
this.logger.info(`${chalk6.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk6.bold.cyan(exitCode)}`);
|
|
1668
|
-
this.emit("shutdown:start");
|
|
1669
|
-
try {
|
|
1670
|
-
for (const phase of PHASE_ORDER) {
|
|
1671
|
-
await this.runPhase(phase);
|
|
1672
|
-
}
|
|
1673
|
-
this.logger.info(`${chalk6.bold.green("Coordinated shutdown completed")} successfully`);
|
|
1674
|
-
this.emit("shutdown:complete");
|
|
1675
|
-
} catch (error) {
|
|
1676
|
-
this.logger.error(`${chalk6.bold.red("Coordinated shutdown failed")}`);
|
|
1677
|
-
this.emit("shutdown:error", error);
|
|
1678
|
-
} finally {
|
|
1679
|
-
this.logger.info(`${chalk6.bold.red("Exiting")} process with code ${chalk6.bold.cyan(this.exitCode)}`);
|
|
1680
|
-
setTimeout(() => {
|
|
1681
|
-
process.exit(this.exitCode);
|
|
1682
|
-
}, LOG_FLUSH_DELAY_MS);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Subscribe to shutdown events
|
|
1687
|
-
*/
|
|
1688
|
-
on(event, listener) {
|
|
1689
|
-
super.on(event, listener);
|
|
1690
|
-
}
|
|
1691
|
-
/**
|
|
1692
|
-
* Unsubscribe from shutdown events
|
|
1693
|
-
*/
|
|
1694
|
-
off(event, listener) {
|
|
1695
|
-
super.off(event, listener);
|
|
1047
|
+
this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.magenta.bold(foundCount)} emojis`);
|
|
1696
1048
|
}
|
|
1697
1049
|
};
|
|
1698
1050
|
|
|
1699
1051
|
// src/bot/Bot.ts
|
|
1052
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
1053
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1054
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1055
|
+
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;
|
|
1056
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1057
|
+
}
|
|
1058
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
1059
|
+
function _ts_metadata3(k, v) {
|
|
1060
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1061
|
+
}
|
|
1062
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
1700
1063
|
var Bot = class extends Plugin {
|
|
1701
1064
|
static {
|
|
1702
1065
|
__name(this, "Bot");
|
|
@@ -1750,8 +1113,8 @@ var Bot = class extends Plugin {
|
|
|
1750
1113
|
* Logs the bot into Discord using the configured token
|
|
1751
1114
|
*/
|
|
1752
1115
|
async login() {
|
|
1753
|
-
await this._client.login(
|
|
1754
|
-
this.logger.info(`Logged in as ${
|
|
1116
|
+
await this._client.login(this.botToken);
|
|
1117
|
+
this.logger.info(`Logged in as ${chalk2.bold.magenta(this._client.user?.username)}!`);
|
|
1755
1118
|
return this;
|
|
1756
1119
|
}
|
|
1757
1120
|
/**
|
|
@@ -1759,12 +1122,21 @@ var Bot = class extends Plugin {
|
|
|
1759
1122
|
*/
|
|
1760
1123
|
async logout() {
|
|
1761
1124
|
await this._client.destroy();
|
|
1762
|
-
this.logger.info(
|
|
1125
|
+
this.logger.info(chalk2.bold.red("Logged out of Discord!"));
|
|
1763
1126
|
}
|
|
1764
1127
|
get client() {
|
|
1765
1128
|
return this._client;
|
|
1766
1129
|
}
|
|
1767
1130
|
};
|
|
1131
|
+
_ts_decorate3([
|
|
1132
|
+
Envapt("DISCORD_BOT_TOKEN", {
|
|
1133
|
+
converter(raw, _fallback) {
|
|
1134
|
+
if (typeof raw !== "string") throw new Error("Missing DISCORD_BOT_TOKEN");
|
|
1135
|
+
return raw;
|
|
1136
|
+
}
|
|
1137
|
+
}),
|
|
1138
|
+
_ts_metadata3("design:type", String)
|
|
1139
|
+
], Bot.prototype, "botToken", void 0);
|
|
1768
1140
|
|
|
1769
1141
|
// src/effects/decorators/RegisterEffect.ts
|
|
1770
1142
|
var EffectMetadataKey = Symbol("effect:metadata");
|
|
@@ -1807,19 +1179,23 @@ var WebhookLog = class extends EffectsHandler {
|
|
|
1807
1179
|
};
|
|
1808
1180
|
|
|
1809
1181
|
// src/effects/default/UnknownException.ts
|
|
1810
|
-
function
|
|
1182
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
1811
1183
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1812
1184
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1813
1185
|
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;
|
|
1814
1186
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1815
1187
|
}
|
|
1816
|
-
__name(
|
|
1817
|
-
|
|
1188
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
1189
|
+
function _ts_metadata4(k, v) {
|
|
1190
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1191
|
+
}
|
|
1192
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1193
|
+
var UnknownException = class _UnknownException extends WebhookLog {
|
|
1818
1194
|
static {
|
|
1819
1195
|
__name(this, "UnknownException");
|
|
1820
1196
|
}
|
|
1821
1197
|
webhook = new WebhookClient({
|
|
1822
|
-
url:
|
|
1198
|
+
url: _UnknownException.unknownExceptionWebhookUrl
|
|
1823
1199
|
});
|
|
1824
1200
|
async execute() {
|
|
1825
1201
|
await this.webhook.send({
|
|
@@ -1831,7 +1207,16 @@ var UnknownException = class extends WebhookLog {
|
|
|
1831
1207
|
});
|
|
1832
1208
|
}
|
|
1833
1209
|
};
|
|
1834
|
-
|
|
1210
|
+
_ts_decorate4([
|
|
1211
|
+
Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
|
|
1212
|
+
converter(raw, _fallback) {
|
|
1213
|
+
if (!raw) throw new Error("Missing UNKNOWN_EXCEPTION_WEBHOOK_URL");
|
|
1214
|
+
return raw;
|
|
1215
|
+
}
|
|
1216
|
+
}),
|
|
1217
|
+
_ts_metadata4("design:type", String)
|
|
1218
|
+
], UnknownException, "unknownExceptionWebhookUrl", void 0);
|
|
1219
|
+
UnknownException = _ts_decorate4([
|
|
1835
1220
|
RegisterEffect("unknownException")
|
|
1836
1221
|
], UnknownException);
|
|
1837
1222
|
var UnhandledErrorEmbed = class UnhandledErrorEmbed2 extends BuilderComponent {
|
|
@@ -1928,12 +1313,12 @@ var EffectsRegistry = class extends Plugin {
|
|
|
1928
1313
|
if (this.isInitialized) return;
|
|
1929
1314
|
this.isInitialized = true;
|
|
1930
1315
|
const effectsDir = this.core.config.effects.path;
|
|
1931
|
-
this.logger.info(
|
|
1316
|
+
this.logger.info(chalk2.bold(effectsDir));
|
|
1932
1317
|
this.registerEffect("unknownException", UnknownException);
|
|
1933
1318
|
await this.loadEffects(effectsDir);
|
|
1934
1319
|
this.attachEffects();
|
|
1935
1320
|
const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
|
|
1936
|
-
this.logger.info(`${
|
|
1321
|
+
this.logger.info(`${chalk2.bold.green("Loaded")}: ${chalk2.bold.magenta(totalEffects)} side effects`);
|
|
1937
1322
|
}
|
|
1938
1323
|
async loadEffects(dir) {
|
|
1939
1324
|
await traverseDirectory(dir, (_fullPath, relativePath, imported) => {
|
|
@@ -1943,11 +1328,11 @@ var EffectsRegistry = class extends Plugin {
|
|
|
1943
1328
|
const effectName = Reflect.getMetadata(EffectMetadataKey, val);
|
|
1944
1329
|
if (effectName) {
|
|
1945
1330
|
this.registerEffect(effectName, val);
|
|
1946
|
-
this.logger.info(`${
|
|
1331
|
+
this.logger.info(`${chalk2.italic("Registered")} ${chalk2.bold.yellow(val.name)} from ${chalk2.gray(relativePath)}`);
|
|
1947
1332
|
}
|
|
1948
1333
|
}
|
|
1949
1334
|
}
|
|
1950
|
-
});
|
|
1335
|
+
}, this.logger);
|
|
1951
1336
|
}
|
|
1952
1337
|
registerEffect(effectName, handler) {
|
|
1953
1338
|
let handlers = this.effectsMap.get(effectName);
|
|
@@ -1979,202 +1364,6 @@ var EffectsRegistry = class extends Plugin {
|
|
|
1979
1364
|
return this.emitter.emit(event, data);
|
|
1980
1365
|
}
|
|
1981
1366
|
};
|
|
1982
|
-
var HTTP_OK = 200;
|
|
1983
|
-
var HTTP_NOT_FOUND = 404;
|
|
1984
|
-
var HealthCheck = class extends Plugin {
|
|
1985
|
-
static {
|
|
1986
|
-
__name(this, "HealthCheck");
|
|
1987
|
-
}
|
|
1988
|
-
core;
|
|
1989
|
-
logger = new Logger("HealthCheck");
|
|
1990
|
-
port = Globals.healthCheckPort;
|
|
1991
|
-
path = Globals.healthCheckPath;
|
|
1992
|
-
server;
|
|
1993
|
-
constructor(core) {
|
|
1994
|
-
super(core), this.core = core;
|
|
1995
|
-
this.core.shutdown.addTask(ShutdownPhase.StopServices, "stop-healthcheck-server", async () => await this.stop());
|
|
1996
|
-
}
|
|
1997
|
-
/**
|
|
1998
|
-
* Starts the health check server.
|
|
1999
|
-
* @returns Promise that resolves when the server is listening
|
|
2000
|
-
*/
|
|
2001
|
-
async init() {
|
|
2002
|
-
return new Promise((resolve, reject) => {
|
|
2003
|
-
this.server = createServer((req, res) => {
|
|
2004
|
-
if (req.method === "GET" && req.url === this.path) {
|
|
2005
|
-
res.writeHead(HTTP_OK, {
|
|
2006
|
-
"Content-Type": "application/json"
|
|
2007
|
-
});
|
|
2008
|
-
res.end(JSON.stringify({
|
|
2009
|
-
status: "ok",
|
|
2010
|
-
timestamp: Date.now()
|
|
2011
|
-
}));
|
|
2012
|
-
} else {
|
|
2013
|
-
res.writeHead(HTTP_NOT_FOUND, {
|
|
2014
|
-
"Content-Type": "application/json"
|
|
2015
|
-
});
|
|
2016
|
-
res.end(JSON.stringify({
|
|
2017
|
-
status: "not found"
|
|
2018
|
-
}));
|
|
2019
|
-
}
|
|
2020
|
-
});
|
|
2021
|
-
this.server.on("error", reject);
|
|
2022
|
-
this.server.listen(this.port, () => {
|
|
2023
|
-
this.logger.info(`${chalk6.green.bold("\u2713")} Health check server listening on ${chalk6.cyan(`http://localhost:${this.port}${this.path}`)}`);
|
|
2024
|
-
resolve();
|
|
2025
|
-
});
|
|
2026
|
-
});
|
|
2027
|
-
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Stops the health check server.
|
|
2030
|
-
*
|
|
2031
|
-
* @returns Promise that resolves when the server is closed
|
|
2032
|
-
*/
|
|
2033
|
-
stop() {
|
|
2034
|
-
return new Promise((shutdownResolve) => {
|
|
2035
|
-
this.server?.close(() => {
|
|
2036
|
-
this.logger.info(chalk6.bold.red("Health check server stopped"));
|
|
2037
|
-
shutdownResolve();
|
|
2038
|
-
});
|
|
2039
|
-
});
|
|
2040
|
-
}
|
|
2041
|
-
};
|
|
2042
|
-
var StartupPhase = /* @__PURE__ */ (function(StartupPhase2) {
|
|
2043
|
-
StartupPhase2[StartupPhase2["Validation"] = 1] = "Validation";
|
|
2044
|
-
StartupPhase2[StartupPhase2["Discovery"] = 2] = "Discovery";
|
|
2045
|
-
StartupPhase2[StartupPhase2["Registration"] = 3] = "Registration";
|
|
2046
|
-
StartupPhase2[StartupPhase2["Configuration"] = 4] = "Configuration";
|
|
2047
|
-
StartupPhase2[StartupPhase2["Instantiation"] = 5] = "Instantiation";
|
|
2048
|
-
StartupPhase2[StartupPhase2["Activation"] = 6] = "Activation";
|
|
2049
|
-
StartupPhase2[StartupPhase2["Ready"] = 7] = "Ready";
|
|
2050
|
-
return StartupPhase2;
|
|
2051
|
-
})({});
|
|
2052
|
-
var PHASE_ORDER2 = [
|
|
2053
|
-
1,
|
|
2054
|
-
2,
|
|
2055
|
-
3,
|
|
2056
|
-
4,
|
|
2057
|
-
5,
|
|
2058
|
-
6,
|
|
2059
|
-
7
|
|
2060
|
-
];
|
|
2061
|
-
var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
2062
|
-
static {
|
|
2063
|
-
__name(this, "CoordinatedStartup");
|
|
2064
|
-
}
|
|
2065
|
-
isStartingUp = false;
|
|
2066
|
-
hasStarted = false;
|
|
2067
|
-
constructor() {
|
|
2068
|
-
super("CoordinatedStartup", PHASE_ORDER2, StartupPhase);
|
|
2069
|
-
}
|
|
2070
|
-
/**
|
|
2071
|
-
* Adds a task to a specific startup phase with timeout.
|
|
2072
|
-
*
|
|
2073
|
-
* @param phase - The startup phase from {@link StartupPhase}
|
|
2074
|
-
* @param taskName - Unique identifier for the task
|
|
2075
|
-
* @param task - Async function to execute
|
|
2076
|
-
* @param timeoutMs - Task timeout in milliseconds (default: 10000)
|
|
2077
|
-
*/
|
|
2078
|
-
addTask(phase, taskName, task, timeoutMs = 1e4) {
|
|
2079
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
2080
|
-
}
|
|
2081
|
-
canAddTask() {
|
|
2082
|
-
if (this.hasStarted) {
|
|
2083
|
-
throw new Error("Cannot add tasks after startup sequence has already completed");
|
|
2084
|
-
}
|
|
2085
|
-
if (this.isStartingUp) {
|
|
2086
|
-
throw new Error("Cannot add tasks while startup sequence is in progress");
|
|
2087
|
-
}
|
|
2088
|
-
return true;
|
|
2089
|
-
}
|
|
2090
|
-
canRemoveTask() {
|
|
2091
|
-
if (this.isStartingUp) {
|
|
2092
|
-
throw new Error("Cannot remove tasks while startup sequence is in progress");
|
|
2093
|
-
}
|
|
2094
|
-
return true;
|
|
2095
|
-
}
|
|
2096
|
-
getTaskType() {
|
|
2097
|
-
return "startup";
|
|
2098
|
-
}
|
|
2099
|
-
async executeTasksInPhase(phase, tasks) {
|
|
2100
|
-
const results = [];
|
|
2101
|
-
for (const task of tasks) {
|
|
2102
|
-
results.push(await Promise.resolve().then(() => this.runTaskWithTimeout(phase, task)).then(() => ({
|
|
2103
|
-
status: "fulfilled",
|
|
2104
|
-
value: void 0
|
|
2105
|
-
}), (reason) => ({
|
|
2106
|
-
status: "rejected",
|
|
2107
|
-
reason
|
|
2108
|
-
})));
|
|
2109
|
-
}
|
|
2110
|
-
return results;
|
|
2111
|
-
}
|
|
2112
|
-
/**
|
|
2113
|
-
* Executes the coordinated startup sequence.
|
|
2114
|
-
*
|
|
2115
|
-
* Runs all registered tasks across startup phases in the correct order.
|
|
2116
|
-
* Each phase completes before the next phase begins. Tasks within a phase
|
|
2117
|
-
* are executed sequentially to maintain predictable initialization.
|
|
2118
|
-
*
|
|
2119
|
-
* @returns Promise that resolves when startup is complete
|
|
2120
|
-
* @throws An {@link Error} If startup fails or is called multiple times
|
|
2121
|
-
* @example
|
|
2122
|
-
* ```typescript
|
|
2123
|
-
* const startup = new CoordinatedStartup();
|
|
2124
|
-
* startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);
|
|
2125
|
-
* await startup.run();
|
|
2126
|
-
* ```
|
|
2127
|
-
*/
|
|
2128
|
-
async run() {
|
|
2129
|
-
if (this.hasStarted) {
|
|
2130
|
-
this.logger.warn("Startup sequence has already completed");
|
|
2131
|
-
return;
|
|
2132
|
-
}
|
|
2133
|
-
if (this.isStartingUp) {
|
|
2134
|
-
this.logger.warn("Startup sequence already in progress");
|
|
2135
|
-
return;
|
|
2136
|
-
}
|
|
2137
|
-
this.isStartingUp = true;
|
|
2138
|
-
this.logger.info(`${chalk6.bold.green("Starting")} coordinated startup sequence`);
|
|
2139
|
-
this.emit("startup:start");
|
|
2140
|
-
try {
|
|
2141
|
-
for (const phase of PHASE_ORDER2) await this.runPhase(phase);
|
|
2142
|
-
this.hasStarted = true;
|
|
2143
|
-
this.logger.info(`${chalk6.bold.green("Coordinated startup completed")} successfully`);
|
|
2144
|
-
this.emit("startup:complete");
|
|
2145
|
-
} catch (error) {
|
|
2146
|
-
this.logger.error(`${chalk6.bold.red("Coordinated startup failed")}`);
|
|
2147
|
-
this.emit("startup:error", error);
|
|
2148
|
-
throw error;
|
|
2149
|
-
} finally {
|
|
2150
|
-
this.isStartingUp = false;
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
/**
|
|
2154
|
-
* Subscribe to startup events
|
|
2155
|
-
*/
|
|
2156
|
-
on(event, listener) {
|
|
2157
|
-
super.on(event, listener);
|
|
2158
|
-
}
|
|
2159
|
-
/**
|
|
2160
|
-
* Unsubscribe from startup events
|
|
2161
|
-
*/
|
|
2162
|
-
off(event, listener) {
|
|
2163
|
-
super.off(event, listener);
|
|
2164
|
-
}
|
|
2165
|
-
/**
|
|
2166
|
-
* Check if startup has completed
|
|
2167
|
-
*/
|
|
2168
|
-
get isReady() {
|
|
2169
|
-
return this.hasStarted;
|
|
2170
|
-
}
|
|
2171
|
-
/**
|
|
2172
|
-
* Check if startup is currently running
|
|
2173
|
-
*/
|
|
2174
|
-
get isRunning() {
|
|
2175
|
-
return this.isStartingUp;
|
|
2176
|
-
}
|
|
2177
|
-
};
|
|
2178
1367
|
|
|
2179
1368
|
// src/Seedcord.ts
|
|
2180
1369
|
var Seedcord = class _Seedcord extends Pluggable {
|
|
@@ -2211,7 +1400,7 @@ var Seedcord = class _Seedcord extends Pluggable {
|
|
|
2211
1400
|
_Seedcord.isInstantiated = true;
|
|
2212
1401
|
this.effects = new EffectsRegistry(this);
|
|
2213
1402
|
this.bot = new Bot(this);
|
|
2214
|
-
this.healthCheck = new HealthCheck(this);
|
|
1403
|
+
this.healthCheck = new HealthCheck(this.shutdown);
|
|
2215
1404
|
this.registerStartupTasks();
|
|
2216
1405
|
}
|
|
2217
1406
|
/**
|
|
@@ -2220,19 +1409,19 @@ var Seedcord = class _Seedcord extends Pluggable {
|
|
|
2220
1409
|
*/
|
|
2221
1410
|
registerStartupTasks() {
|
|
2222
1411
|
this.startup.addTask(StartupPhase.Configuration, "Effect Initialization", async () => {
|
|
2223
|
-
this.effects.logger.info(
|
|
1412
|
+
this.effects.logger.info(chalk2.bold("Initializing"));
|
|
2224
1413
|
await this.effects.init();
|
|
2225
|
-
this.effects.logger.info(
|
|
1414
|
+
this.effects.logger.info(chalk2.bold("Initialized"));
|
|
2226
1415
|
});
|
|
2227
1416
|
this.startup.addTask(StartupPhase.Instantiation, "Bot Initialization", async () => {
|
|
2228
|
-
this.bot.logger.info(
|
|
1417
|
+
this.bot.logger.info(chalk2.bold("Initializing"));
|
|
2229
1418
|
await this.bot.init();
|
|
2230
|
-
this.bot.logger.info(
|
|
1419
|
+
this.bot.logger.info(chalk2.bold("Initialized"));
|
|
2231
1420
|
});
|
|
2232
1421
|
this.startup.addTask(StartupPhase.Ready, "Health Check", async () => {
|
|
2233
|
-
this.healthCheck.logger.info(
|
|
1422
|
+
this.healthCheck.logger.info(chalk2.bold("Initializing"));
|
|
2234
1423
|
await this.healthCheck.init();
|
|
2235
|
-
this.healthCheck.logger.info(
|
|
1424
|
+
this.healthCheck.logger.info(chalk2.bold("Initialized"));
|
|
2236
1425
|
});
|
|
2237
1426
|
}
|
|
2238
1427
|
/**
|
|
@@ -2282,75 +1471,22 @@ function EventCatchable(log) {
|
|
|
2282
1471
|
};
|
|
2283
1472
|
}
|
|
2284
1473
|
__name(EventCatchable, "EventCatchable");
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
* Creates a new CooldownManager instance.
|
|
2297
|
-
*
|
|
2298
|
-
* @param opts - Configuration options for the cooldown behavior
|
|
2299
|
-
*/
|
|
2300
|
-
constructor(opts = {}) {
|
|
2301
|
-
this.window = opts.cooldown ?? 1e3;
|
|
2302
|
-
this.Err = opts.err ?? Error;
|
|
2303
|
-
this.msg = opts.message ?? "Cooldown active";
|
|
2304
|
-
}
|
|
2305
|
-
/**
|
|
2306
|
-
* Records usage timestamp for a key without any cooldown checks.
|
|
2307
|
-
*
|
|
2308
|
-
* @param key - The unique identifier for the cooldown entry
|
|
2309
|
-
*/
|
|
2310
|
-
set(key) {
|
|
2311
|
-
this.map.set(key, Date.now());
|
|
2312
|
-
}
|
|
2313
|
-
/**
|
|
2314
|
-
* Verifies cooldown status for a key and updates timestamp if not active.
|
|
2315
|
-
*
|
|
2316
|
-
* If the cooldown is still active, throws the configured error.
|
|
2317
|
-
* If not active, updates the timestamp and returns successfully.
|
|
2318
|
-
*
|
|
2319
|
-
* @param key - The unique identifier to check cooldown for
|
|
2320
|
-
* @throws An {@link Err} When the cooldown is still active for the given key
|
|
2321
|
-
*/
|
|
2322
|
-
check(key) {
|
|
2323
|
-
const now = Date.now();
|
|
2324
|
-
const last = this.map.get(key);
|
|
2325
|
-
const remaining = this.window - (now - (last ?? 0));
|
|
2326
|
-
if (Globals.isDevelopment && remaining > 0) {
|
|
2327
|
-
Logger.Debug("CooldownManager", `${key} - ${remaining}ms remaining`);
|
|
2328
|
-
}
|
|
2329
|
-
if (last !== void 0 && remaining > 0) {
|
|
2330
|
-
throw new this.Err(this.msg, remaining);
|
|
1474
|
+
function throwCustomError(error, message, CustomError2) {
|
|
1475
|
+
const uuid = crypto.randomUUID();
|
|
1476
|
+
Logger.Error("Throwing Custom Error", error.name);
|
|
1477
|
+
if (typeof CustomError2 === typeof DatabaseError) {
|
|
1478
|
+
const errorMessage = error instanceof Error ? error.message : message;
|
|
1479
|
+
throw new CustomError2(errorMessage, uuid);
|
|
1480
|
+
} else {
|
|
1481
|
+
if (error instanceof Error) {
|
|
1482
|
+
throw new CustomError2(`${message}: ${error.message ? error.message : error.toString()}`);
|
|
1483
|
+
} else {
|
|
1484
|
+
throw new CustomError2(message);
|
|
2331
1485
|
}
|
|
2332
|
-
this.map.set(key, now);
|
|
2333
|
-
}
|
|
2334
|
-
/**
|
|
2335
|
-
* Checks if a key is currently cooling down without updating timestamp.
|
|
2336
|
-
*
|
|
2337
|
-
* @param key - The unique identifier to check
|
|
2338
|
-
* @returns True if the key is still cooling down, false otherwise
|
|
2339
|
-
*/
|
|
2340
|
-
isActive(key) {
|
|
2341
|
-
const last = this.map.get(key);
|
|
2342
|
-
return last !== void 0 && Date.now() - last < this.window;
|
|
2343
1486
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
*
|
|
2347
|
-
* @param key - The unique identifier to remove (useful for manual resets)
|
|
2348
|
-
*/
|
|
2349
|
-
clear(key) {
|
|
2350
|
-
this.map.delete(key);
|
|
2351
|
-
}
|
|
2352
|
-
};
|
|
1487
|
+
}
|
|
1488
|
+
__name(throwCustomError, "throwCustomError");
|
|
2353
1489
|
|
|
2354
|
-
export { AutocompleteHandler, AutocompleteRoute, BaseErrorEmbed, Bot, BuilderComponent, ButtonRoute, Catchable, Checkable, CommandMetadataKey, ContextMenuRoute,
|
|
1490
|
+
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 };
|
|
2355
1491
|
//# sourceMappingURL=index.mjs.map
|
|
2356
1492
|
//# sourceMappingURL=index.mjs.map
|