diskernel 0.1.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.
Files changed (57) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +7 -0
  3. package/dist/core/client.d.ts +9 -0
  4. package/dist/core/client.d.ts.map +1 -0
  5. package/dist/core/client.js +70 -0
  6. package/dist/core/command.d.ts +29 -0
  7. package/dist/core/command.d.ts.map +1 -0
  8. package/dist/core/command.js +570 -0
  9. package/dist/core/config.d.ts +9 -0
  10. package/dist/core/config.d.ts.map +1 -0
  11. package/dist/core/config.js +82 -0
  12. package/dist/core/error.d.ts +8 -0
  13. package/dist/core/error.d.ts.map +1 -0
  14. package/dist/core/error.js +34 -0
  15. package/dist/core/event.d.ts +9 -0
  16. package/dist/core/event.d.ts.map +1 -0
  17. package/dist/core/event.js +66 -0
  18. package/dist/core/logger.d.ts +17 -0
  19. package/dist/core/logger.d.ts.map +1 -0
  20. package/dist/core/logger.js +103 -0
  21. package/dist/index.d.ts +18 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +9 -0
  24. package/dist/types/abstract/client.abc.d.ts +7 -0
  25. package/dist/types/abstract/client.abc.d.ts.map +1 -0
  26. package/dist/types/abstract/client.abc.js +7 -0
  27. package/dist/types/abstract/command.abc.d.ts +8 -0
  28. package/dist/types/abstract/command.abc.d.ts.map +1 -0
  29. package/dist/types/abstract/command.abc.js +15 -0
  30. package/dist/types/abstract/config.abc.d.ts +6 -0
  31. package/dist/types/abstract/config.abc.d.ts.map +1 -0
  32. package/dist/types/abstract/config.abc.js +6 -0
  33. package/dist/types/abstract/error.abc.d.ts +4 -0
  34. package/dist/types/abstract/error.abc.d.ts.map +1 -0
  35. package/dist/types/abstract/error.abc.js +4 -0
  36. package/dist/types/abstract/event.abc.d.ts +5 -0
  37. package/dist/types/abstract/event.abc.d.ts.map +1 -0
  38. package/dist/types/abstract/event.abc.js +3 -0
  39. package/dist/types/abstract/index.d.ts +7 -0
  40. package/dist/types/abstract/index.d.ts.map +1 -0
  41. package/dist/types/abstract/index.js +6 -0
  42. package/dist/types/abstract/logger.abc.d.ts +5 -0
  43. package/dist/types/abstract/logger.abc.d.ts.map +1 -0
  44. package/dist/types/abstract/logger.abc.js +6 -0
  45. package/dist/types/command.d.ts +104 -0
  46. package/dist/types/command.d.ts.map +1 -0
  47. package/dist/types/command.js +79 -0
  48. package/dist/types/config.d.ts +29 -0
  49. package/dist/types/config.d.ts.map +1 -0
  50. package/dist/types/config.js +1 -0
  51. package/dist/types/event.d.ts +7 -0
  52. package/dist/types/event.d.ts.map +1 -0
  53. package/dist/types/event.js +1 -0
  54. package/dist/types/index.d.ts +5 -0
  55. package/dist/types/index.d.ts.map +1 -0
  56. package/dist/types/index.js +4 -0
  57. package/package.json +67 -0
@@ -0,0 +1,570 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { ApplicationCommandType, AutocompleteInteraction, ButtonInteraction, ChatInputCommandInteraction, Client, ContextMenuCommandBuilder, ContextMenuCommandInteraction, EmbedBuilder, ModalSubmitInteraction, SlashCommandBuilder, SlashCommandSubcommandBuilder, StringSelectMenuInteraction, } from "discord.js";
4
+ import { Core } from "./client.js";
5
+ import { Config } from "./config.js";
6
+ import { getCustomCoreLogger } from "./logger.js";
7
+ import { SlashCommandT, Command as command, BaseAction, BaseCommand } from "#types";
8
+ const Logger = getCustomCoreLogger("commands");
9
+ export class Command extends command {
10
+ static client;
11
+ static commands = [];
12
+ static actions = new Map();
13
+ static subCommands = [];
14
+ static cooldown = new Map();
15
+ static async initialize() {
16
+ this.actions = new Map();
17
+ this.commands = [];
18
+ this.subCommands = [];
19
+ this.cooldown = new Map();
20
+ this.client = Core.Client;
21
+ await this.registerCommands();
22
+ this.client?.on("interactionCreate", (interaction) => {
23
+ if (interaction.isChatInputCommand() ||
24
+ interaction.isContextMenuCommand() ||
25
+ interaction.isAutocomplete())
26
+ this.handleCommands(interaction);
27
+ });
28
+ this.client?.on("guildCreate", async (guild) => {
29
+ Logger.info(`✅ Joined new guild(id: ${guild.id}, name: ${guild.name}). registering commands...`);
30
+ const commands = (() => {
31
+ try {
32
+ return this.buildCommands();
33
+ }
34
+ catch (e) {
35
+ Logger.error(`❌️ Failed to build commands: ${e.message}`);
36
+ Logger.error(`Stack trace: ${e.stack}`);
37
+ return undefined;
38
+ }
39
+ })();
40
+ if (commands)
41
+ await this.registerWithGuildID(guild.id, commands);
42
+ Logger.info('✅ Register commands finished successfully.');
43
+ });
44
+ }
45
+ static async loadCommandFromDir(dir) {
46
+ try {
47
+ const files = await fs.promises.readdir(dir, { recursive: true });
48
+ await Promise.all(files
49
+ .filter((f) => f.endsWith(".js"))
50
+ .map(async (file) => {
51
+ const filePath = path.join(dir, file);
52
+ try {
53
+ const module = await import(filePath);
54
+ const commandClass = Object.values(module)[0];
55
+ let command;
56
+ if (typeof commandClass === "function") {
57
+ try {
58
+ const instance = new commandClass();
59
+ command = instance;
60
+ }
61
+ catch (_e) {
62
+ Logger.warn(`❌️ Failed to instantiate command class: ${file}`);
63
+ return;
64
+ }
65
+ }
66
+ else {
67
+ Logger.warn(`❌️ Command class is not a constructor: ${file}`);
68
+ return;
69
+ }
70
+ if (!command) {
71
+ Logger.warn(`❌️ Invalid command: ${file}`);
72
+ return;
73
+ }
74
+ if (command instanceof BaseCommand) {
75
+ if (typeof command !== "object" ||
76
+ typeof command.name !== "string" ||
77
+ typeof command.type !== "string" ||
78
+ typeof command.execute !== "function") {
79
+ Logger.warn(`❌️ Invalid command: ${file}`);
80
+ return;
81
+ }
82
+ if (!command.isSlashCommand() &&
83
+ !command.isContextMenuCommand()) {
84
+ Logger.warn(`❌️ Invalid command type: ${file}`);
85
+ return;
86
+ }
87
+ command.isCooldownEnabled ??= false;
88
+ command.description ??= "No description provided.";
89
+ if (command.isAdminOnly) {
90
+ if (!Config.get("options")?.feature.enableAdminCommands) {
91
+ Logger.warn(`❌️ Admin command is disabled: ${file}`);
92
+ return;
93
+ }
94
+ if (!Config.get("options")?.adminIds) {
95
+ Logger.warn(`❌️ Admin command is disabled due to no admin IDs in configuration: ${file}`);
96
+ return;
97
+ }
98
+ }
99
+ if (command.isDevOnly) {
100
+ if (!Config.get("options")?.feature.enableDevelopmentCommands) {
101
+ Logger.warn(`❌️ Development command is disabled: ${file}`);
102
+ return;
103
+ }
104
+ }
105
+ this.commands.push(command);
106
+ Logger.debug(`✅️ Loaded command: ${file}`);
107
+ }
108
+ else if (command instanceof BaseAction) {
109
+ if (typeof command !== "object" ||
110
+ typeof command.name !== "string" ||
111
+ typeof command.type !== "string" ||
112
+ typeof command.execute !== "function") {
113
+ Logger.warn(`❌️ Invalid action: ${file}`);
114
+ return;
115
+ }
116
+ this.actions.set(command.name, command);
117
+ Logger.debug(`✅️ Loaded action: ${file}`);
118
+ }
119
+ }
120
+ catch {
121
+ Logger.warn(`❌️ Failed to import command: ${file}`);
122
+ }
123
+ }));
124
+ return true;
125
+ }
126
+ catch {
127
+ Logger.warn(`⚠ Command folder does not exist: ${dir}`);
128
+ return false;
129
+ }
130
+ }
131
+ static buildCommandOptions(option, command) {
132
+ switch (option.type) {
133
+ case "string":
134
+ command.addStringOption((option) => option
135
+ .setName(option.name)
136
+ .setDescription(option.description)
137
+ .setRequired(option.required)
138
+ .addChoices(option.choices ?? []));
139
+ break;
140
+ case "integer":
141
+ command.addIntegerOption((option) => option
142
+ .setName(option.name)
143
+ .setDescription(option.description)
144
+ .setRequired(option.required)
145
+ .addChoices(option.choices ?? []));
146
+ break;
147
+ case "boolean":
148
+ command.addBooleanOption((option) => option
149
+ .setName(option.name)
150
+ .setDescription(option.description)
151
+ .setRequired(option.required));
152
+ break;
153
+ case "channel":
154
+ command.addChannelOption((option) => option
155
+ .setName(option.name)
156
+ .setDescription(option.description)
157
+ .setRequired(option.required));
158
+ break;
159
+ case "role":
160
+ command.addRoleOption((option) => option
161
+ .setName(option.name)
162
+ .setDescription(option.description)
163
+ .setRequired(option.required));
164
+ break;
165
+ case "user":
166
+ command.addUserOption((option) => option
167
+ .setName(option.name)
168
+ .setDescription(option.description)
169
+ .setRequired(option.required));
170
+ break;
171
+ }
172
+ }
173
+ static initializeCooldown(name, globalCooldownTime, userCooldownTime) {
174
+ this.cooldown.set(name, {
175
+ globalCooldownTime,
176
+ userCooldownTime,
177
+ lastUsedGlobal: undefined,
178
+ user: new Map(),
179
+ });
180
+ return undefined;
181
+ }
182
+ static buildCommands() {
183
+ const commands = [];
184
+ for (const command of this.commands) {
185
+ if (command.type === "slash" && !command.parent) {
186
+ const slashCommand = new SlashCommandBuilder()
187
+ .setName(command.name)
188
+ .setDescription(command.description);
189
+ if (command.option) {
190
+ for (const option of command.option) {
191
+ this.buildCommandOptions(option, slashCommand);
192
+ }
193
+ }
194
+ commands.push(slashCommand);
195
+ }
196
+ else if (command.type === "slash" && command.parent) {
197
+ const subCommand = new SlashCommandSubcommandBuilder()
198
+ .setName(command.name)
199
+ .setDescription(command.description);
200
+ if (command.option) {
201
+ for (const option of command.option) {
202
+ this.buildCommandOptions(option, subCommand);
203
+ }
204
+ }
205
+ this.subCommands.push({ parent: command.parent, command: subCommand });
206
+ }
207
+ else if (command.type === "userContextMenu" || command.type === "messageContextMenu") {
208
+ const contextMenuCommand = new ContextMenuCommandBuilder()
209
+ .setName(command.name)
210
+ .setType(command.type === "userContextMenu"
211
+ ? ApplicationCommandType.User
212
+ : ApplicationCommandType.Message);
213
+ commands.push(contextMenuCommand);
214
+ }
215
+ if (command.isCooldownEnabled) {
216
+ this.initializeCooldown(command.name, command.globalCooldownTime, command.userCooldownTime);
217
+ }
218
+ this.subCommands.forEach((v) => {
219
+ const slashCommand = commands.find((c) => c.name === v.parent);
220
+ if (slashCommand instanceof SlashCommandBuilder) {
221
+ slashCommand.addSubcommand(v.command);
222
+ }
223
+ });
224
+ }
225
+ return commands.map((c) => {
226
+ return c.toJSON();
227
+ });
228
+ }
229
+ static buildSubCommandFromNameAndParent(name, parent) {
230
+ const parentCommand = this.commands.find((c) => c.name === parent);
231
+ if (!parentCommand || !parentCommand.hasParent())
232
+ return false;
233
+ const command = this.commands.find((c) => c.name === name && c.parent === parent);
234
+ if (!command)
235
+ return false;
236
+ const subCommand = new SlashCommandSubcommandBuilder()
237
+ .setName(command.name)
238
+ .setDescription(command.hasDescription() ? command.description : "No description provided.");
239
+ if (command.hasOptions()) {
240
+ for (const option of command.option) {
241
+ this.buildCommandOptions(option, subCommand);
242
+ }
243
+ }
244
+ if (command.isCooldownEnabled) {
245
+ this.initializeCooldown(command.name, command.globalCooldownTime, command.userCooldownTime);
246
+ }
247
+ this.subCommands.push({ parent: command.parent, command: subCommand });
248
+ return true;
249
+ }
250
+ static buildCommandFromName(name) {
251
+ const command = this.commands.find((c) => c.name === name);
252
+ if (!command)
253
+ return undefined;
254
+ if (command.isCooldownEnabled) {
255
+ this.initializeCooldown(command.name, command.globalCooldownTime, command.userCooldownTime);
256
+ }
257
+ if (command.parent && command.type === "slash") {
258
+ const parent = this.buildCommandFromName(command.parent);
259
+ if (!(parent instanceof SlashCommandBuilder))
260
+ return undefined;
261
+ if (this.buildSubCommandFromNameAndParent(name, parent.name)) {
262
+ this.subCommands
263
+ .filter((c) => c.parent === parent.name)
264
+ .forEach((c) => {
265
+ parent.addSubcommand(c.command);
266
+ });
267
+ return parent;
268
+ }
269
+ else
270
+ return undefined;
271
+ }
272
+ else if (command.type === "slash") {
273
+ const slashCommand = new SlashCommandBuilder()
274
+ .setName(command.name)
275
+ .setDescription(command.description);
276
+ if (command.option) {
277
+ for (const option of command.option) {
278
+ this.buildCommandOptions(option, slashCommand);
279
+ }
280
+ }
281
+ return slashCommand;
282
+ }
283
+ else if (command.type === "userContextMenu" || command.type === "messageContextMenu") {
284
+ const contextMenuCommand = new ContextMenuCommandBuilder()
285
+ .setName(command.name)
286
+ .setType(command.type === "userContextMenu"
287
+ ? ApplicationCommandType.User
288
+ : ApplicationCommandType.Message);
289
+ return contextMenuCommand;
290
+ }
291
+ }
292
+ static async registerWithGuildID(guildID, commands) {
293
+ const guild = this.client.guilds.cache.get(guildID);
294
+ if (!guild) {
295
+ Logger.warn(`⚠ Guild with ID ${guildID} not found.`);
296
+ return false;
297
+ }
298
+ await guild.commands.set(commands).catch((e) => {
299
+ Logger.error(`❌️ Failed to register commands for guild ${guildID}: ${e.message}`);
300
+ return false;
301
+ });
302
+ Logger.debug(`✅ Registered ${commands.length} commands for guild ${guildID}.`);
303
+ return true;
304
+ }
305
+ static async unregisterFromName(name) {
306
+ const command = (await this.client.application?.commands.fetch())?.find((c) => c.name === name);
307
+ if (!command) {
308
+ Logger.warn(`⚠ Command with name ${name} not found.`);
309
+ return false;
310
+ }
311
+ const guilds = this.client.guilds.cache.values();
312
+ for (const guild of guilds) {
313
+ await guild.commands.delete(command);
314
+ if (guilds.toArray().length < 10) {
315
+ setTimeout(() => { }, 1000);
316
+ }
317
+ }
318
+ return true;
319
+ }
320
+ static async unregisterFromNameAndGuildID(name, guildID) {
321
+ const Command = (await this.client.guilds.cache.get(guildID)?.commands.fetch())?.find((c) => c.name === name);
322
+ if (!Command) {
323
+ Logger.warn(`⚠ Command with name ${name} not found in guild ${guildID}.`);
324
+ return false;
325
+ }
326
+ await this.client.guilds.cache.get(guildID)?.commands.delete(Command);
327
+ return true;
328
+ }
329
+ static async registerFromName(name) {
330
+ const commands = await this.client.application?.commands.fetch();
331
+ if (commands?.find((c) => c.name === name)) {
332
+ Logger.warn(`⚠ Command with name ${name} already exists.`);
333
+ return false;
334
+ }
335
+ const command = this.buildCommandFromName(name)?.toJSON();
336
+ if (!command) {
337
+ Logger.warn(`⚠ Command with name ${name} is invalid.`);
338
+ return false;
339
+ }
340
+ const guilds = this.client.guilds.cache.values();
341
+ for (const guild of guilds) {
342
+ await guild.commands.create(command);
343
+ if (guilds.toArray().length < 10) {
344
+ setTimeout(() => { }, 1000);
345
+ }
346
+ }
347
+ return true;
348
+ }
349
+ static async registerFromNameAndGuildID(name, guildID) {
350
+ if ((await this.client.application?.commands.fetch())?.find((c) => c.name === name)) {
351
+ Logger.warn(`⚠ Command with name ${name} already exists in GuildID ${guildID}.`);
352
+ return false;
353
+ }
354
+ const command = this.buildCommandFromName(name)?.toJSON();
355
+ if (!command) {
356
+ Logger.warn(`⚠ Command with name ${name} is invalid.`);
357
+ return false;
358
+ }
359
+ const guild = this.client.guilds.cache.get(guildID);
360
+ if (!guild) {
361
+ Logger.warn(`⚠ Guild with ID ${guildID} not found.`);
362
+ return false;
363
+ }
364
+ await guild.commands.create(command);
365
+ return true;
366
+ }
367
+ static async registerCommands() {
368
+ if (!(await this.loadCommandFromDir(path.resolve(process.cwd(), "dist", "commands")))) {
369
+ Logger.warn(`❌️ Failed to load commands from ${process.cwd()}/dist/commands`);
370
+ return false;
371
+ }
372
+ Logger.info(`✅️ Loaded ${this.commands.length} commands.`);
373
+ const commands = (() => {
374
+ try {
375
+ return this.buildCommands();
376
+ }
377
+ catch (e) {
378
+ Logger.error(`❌️ Failed to build commands: ${e.message}`);
379
+ Logger.error(`Stack trace: ${e.stack}`);
380
+ return undefined;
381
+ }
382
+ })();
383
+ if (!commands)
384
+ return false;
385
+ const guilds = this.client.guilds.cache.values();
386
+ for (const guild of guilds) {
387
+ await this.registerWithGuildID(guild.id, commands);
388
+ if (guilds.toArray().length < 10) {
389
+ setTimeout(() => { }, 1000);
390
+ }
391
+ }
392
+ Logger.info(`✅️ Registered ${commands.length} commands successfully.`);
393
+ return true;
394
+ }
395
+ static isCooldownPassed(name, userid) {
396
+ if (!this.cooldown.has(name))
397
+ return true;
398
+ const command = this.cooldown.get(name);
399
+ const now = Date.now();
400
+ const isAdmin = Config.get("options")?.adminIds?.includes(userid);
401
+ if (command.globalCooldownTime) {
402
+ if (!isAdmin &&
403
+ command.lastUsedGlobal &&
404
+ now < command.lastUsedGlobal + command.globalCooldownTime) {
405
+ return false;
406
+ }
407
+ command.lastUsedGlobal = now;
408
+ }
409
+ if (command.userCooldownTime && !isAdmin) {
410
+ if (!command.user) {
411
+ command.user = new Map();
412
+ }
413
+ if (command.user.has(userid) &&
414
+ now < command.user.get(userid) + command.userCooldownTime) {
415
+ return false;
416
+ }
417
+ command.user.set(userid, now);
418
+ }
419
+ return true;
420
+ }
421
+ static isPrivilegeCheckPassed(interaction) {
422
+ const interactionName = interaction instanceof ButtonInteraction ||
423
+ interaction instanceof StringSelectMenuInteraction ||
424
+ interaction instanceof ModalSubmitInteraction
425
+ ? interaction.customId
426
+ : interaction.commandName;
427
+ const command = this.commands.find((c) => c.name === interactionName) || this.actions.get(interactionName);
428
+ if (!command)
429
+ return false;
430
+ if (!command.isAdminOnly)
431
+ return true;
432
+ if (!Config.get("options").feature.enableAdminCommands)
433
+ return false;
434
+ if (!Config.get("options").adminIds?.includes(interaction.user.id))
435
+ return false;
436
+ return true;
437
+ }
438
+ static isDevCheckPassed(interaction) {
439
+ const interactionName = interaction instanceof ButtonInteraction ||
440
+ interaction instanceof StringSelectMenuInteraction ||
441
+ interaction instanceof ModalSubmitInteraction
442
+ ? interaction.customId
443
+ : interaction.commandName;
444
+ const command = this.commands.find((c) => c.name === interactionName) || this.actions.get(interactionName);
445
+ if (!command)
446
+ return false;
447
+ if (!command.isDevOnly)
448
+ return true;
449
+ if (!Config.get("options").feature.enableDevelopmentCommands)
450
+ return false;
451
+ if (!Config.get("options").adminIds?.includes(interaction.user.id))
452
+ return false;
453
+ return true;
454
+ }
455
+ static async replyCooldownMessage(interaction) {
456
+ const interactionName = interaction instanceof ButtonInteraction ||
457
+ interaction instanceof StringSelectMenuInteraction ||
458
+ interaction instanceof ModalSubmitInteraction
459
+ ? interaction.customId
460
+ : interaction.commandName;
461
+ const embed = new EmbedBuilder()
462
+ .setTitle("⏳️ レート制限")
463
+ .setDescription("このコマンドにはレート制限があります。少し待ってからもう一度実行してください。\n-# 高頻度でコマンドを実行する行為はおやめください。")
464
+ .setColor("Red");
465
+ await interaction.reply({ embeds: [embed], ephemeral: true }).catch((e) => {
466
+ Logger.warn(`❌️ Failed to reply to ${interactionName} cooldown message. Error: ${e.message}`);
467
+ });
468
+ return;
469
+ }
470
+ static async replyPrivilegeMessage(interaction) {
471
+ const interactionName = interaction instanceof ButtonInteraction ||
472
+ interaction instanceof StringSelectMenuInteraction ||
473
+ interaction instanceof ModalSubmitInteraction
474
+ ? interaction.customId
475
+ : interaction.commandName;
476
+ const embed = new EmbedBuilder()
477
+ .setTitle("❌️ 管理者権限が必要です")
478
+ .setDescription("このコマンドは管理者権限が必要です。")
479
+ .setColor("Red");
480
+ await interaction.reply({ embeds: [embed], ephemeral: true }).catch((e) => {
481
+ Logger.warn(`❌️ Failed to reply to ${interactionName} privilege message. Error: ${e.message}`);
482
+ });
483
+ return;
484
+ }
485
+ static async replyDevMessage(interaction) {
486
+ const interactionName = interaction instanceof ButtonInteraction ||
487
+ interaction instanceof StringSelectMenuInteraction ||
488
+ interaction instanceof ModalSubmitInteraction
489
+ ? interaction.customId
490
+ : interaction.commandName;
491
+ const embed = new EmbedBuilder()
492
+ .setTitle("❌️ 管理者権限が必要です")
493
+ .setDescription("このコマンドは開発環境専用として構成されています。\n実行するには管理者権限が必要です。")
494
+ .setColor("Red");
495
+ await interaction.reply({ embeds: [embed], ephemeral: true }).catch((e) => {
496
+ Logger.warn(`❌️ Failed to reply to ${interactionName} dev message. Error: ${e.message}`);
497
+ });
498
+ return;
499
+ }
500
+ static async handleCommands(interaction) {
501
+ const interactionName = interaction instanceof ButtonInteraction ||
502
+ interaction instanceof StringSelectMenuInteraction ||
503
+ interaction instanceof ModalSubmitInteraction
504
+ ? interaction.customId
505
+ : interaction.commandName;
506
+ Logger.debug(`Triggered command-interaction: ${interactionName} for ${interaction.user.id}(${interaction.user.globalName})`);
507
+ const command = this.commands.find((c) => c.name === interactionName) || this.actions.get(interactionName) || undefined;
508
+ if (!command) {
509
+ Logger.debug(`❌️ Command with name ${interactionName} not found.`);
510
+ return;
511
+ }
512
+ if (interaction instanceof AutocompleteInteraction) {
513
+ if (command instanceof SlashCommandT && command.autocomplete) {
514
+ Logger.debug(`✅️ Triggered autocomplete for ${interaction.commandName}`);
515
+ await command.autocomplete(interaction);
516
+ }
517
+ return;
518
+ }
519
+ const subCommandName = command.type === "slash" && interaction instanceof ChatInputCommandInteraction ? interaction.options.getSubcommand(false) : null;
520
+ const isCommandTypeMatch = command.isSlashCommand() || command.isContextMenuCommand() || command.isButtonAction() || command.isSelectMenuAction() || command.isModalAction();
521
+ if (!isCommandTypeMatch) {
522
+ Logger.debug(`❌️ Command ${interactionName} is not a ${command.type} command`);
523
+ return;
524
+ }
525
+ if (!this.isDevCheckPassed(interaction)) {
526
+ Logger.debug(`❌️ Command ${subCommandName || interactionName} is dev only for ${interaction.user.id}(${interaction.user.globalName})`);
527
+ await this.replyDevMessage(interaction);
528
+ return;
529
+ }
530
+ if (!this.isPrivilegeCheckPassed(interaction)) {
531
+ Logger.debug(`❌️ Command ${subCommandName || interactionName} is admin only for ${interaction.user.id}(${interaction.user.globalName})`);
532
+ await this.replyPrivilegeMessage(interaction);
533
+ return;
534
+ }
535
+ if (!this.isCooldownPassed(subCommandName || command.name, interaction.user.id)) {
536
+ Logger.debug(`❌️ Command ${subCommandName || interactionName} is on cooldown for ${interaction.user.id}(${interaction.user.globalName})`);
537
+ await this.replyCooldownMessage(interaction);
538
+ return;
539
+ }
540
+ Logger.debug(`✅️ Triggered command ${subCommandName || interactionName} for ${interaction.user.id}(${interaction.user.globalName})`);
541
+ if (command.isSlashCommand() && interaction.isChatInputCommand()) {
542
+ if (subCommandName) {
543
+ const subCommand = this.commands.find((c) => c.name === subCommandName && c.parent === command.name);
544
+ if (subCommand && subCommand.type === "slash" && subCommand.execute) {
545
+ await subCommand.execute(interaction);
546
+ return;
547
+ }
548
+ }
549
+ await command.execute(interaction);
550
+ return;
551
+ }
552
+ else if (command.isUserContextCommand() &&
553
+ interaction.isUserContextMenuCommand()) {
554
+ await command.execute(interaction);
555
+ }
556
+ else if (command.isMessageContextCommand() &&
557
+ interaction.isMessageContextMenuCommand()) {
558
+ await command.execute(interaction);
559
+ }
560
+ else if (command.isButtonAction() && interaction.isButton()) {
561
+ await command.execute(interaction);
562
+ }
563
+ else if (command.isSelectMenuAction() && interaction.isStringSelectMenu()) {
564
+ await command.execute(interaction);
565
+ }
566
+ else if (command.isModalAction() && interaction.isModalSubmit()) {
567
+ await command.execute(interaction);
568
+ }
569
+ }
570
+ }
@@ -0,0 +1,9 @@
1
+ import { type ConfigT, Config as config } from "#types";
2
+ export declare class Config extends config {
3
+ protected static instance: ConfigT;
4
+ private static validateConfig;
5
+ constructor(config: ConfigT);
6
+ static get<K extends keyof ConfigT>(key: K): ConfigT[K];
7
+ static isInitialized(): boolean;
8
+ }
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAe,MAAM,IAAI,MAAM,EAAE,MAAM,QAAQ,CAAC;AAUrE,qBAAa,MAAO,SAAQ,MAAM;IAChC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;IAEnC,OAAO,CAAC,MAAM,CAAC,cAAc;gBA2EjB,MAAM,EAAE,OAAO;WAUb,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;WAShD,aAAa,IAAI,OAAO;CAGvC"}
@@ -0,0 +1,82 @@
1
+ import { GatewayIntentBits } from "discord.js";
2
+ import { Config as config } from "#types";
3
+ import { Log } from "./logger.js";
4
+ class EnvConfigError extends Error {
5
+ constructor(message, options) {
6
+ super(message, options);
7
+ this.name = "EnvConfigError";
8
+ }
9
+ }
10
+ export class Config extends config {
11
+ static instance;
12
+ static validateConfig(config) {
13
+ if (!config.TOKEN || typeof config.TOKEN !== "string") {
14
+ throw new EnvConfigError("Invalid or missing TOKEN in configuration.");
15
+ }
16
+ if (!config.intents || config.intents.length === 0) {
17
+ throw new EnvConfigError("At least one intent must be specified in configuration.");
18
+ }
19
+ const validIntents = Object.keys(GatewayIntentBits);
20
+ for (const intent of config.intents) {
21
+ if (!validIntents.includes(intent)) {
22
+ throw new EnvConfigError(`Invalid intent specified: ${intent}`);
23
+ }
24
+ }
25
+ if (config.options) {
26
+ if (config.options.adminIds &&
27
+ (!Array.isArray(config.options.adminIds) ||
28
+ config.options.adminIds.some((id) => typeof id !== "string"))) {
29
+ throw new EnvConfigError("adminIds must be an array of strings.");
30
+ }
31
+ if (config.options.logging) {
32
+ if (![
33
+ "trace",
34
+ "debug",
35
+ "info",
36
+ "warning",
37
+ "error",
38
+ "fatal",
39
+ ].includes(config.options.logging.level)) {
40
+ throw new EnvConfigError("Logging level must be one of 'trace', 'debug', 'info', 'warning', 'error', 'fatal'.");
41
+ }
42
+ if (typeof config.options.logging.enableFileLogging !== "boolean") {
43
+ throw new EnvConfigError("enableFileLogging must be a boolean.");
44
+ }
45
+ if (config.options.logging.enableFileLogging &&
46
+ (typeof config.options.logging.logFilePath !== "string" ||
47
+ config.options.logging.logFilePath.trim() === "")) {
48
+ throw new EnvConfigError("logFilePath must be a non-empty string when file logging is enabled.");
49
+ }
50
+ }
51
+ if (config.options.feature) {
52
+ if (typeof config.options.feature.enableCommandAutoload !== "boolean" ||
53
+ typeof config.options.feature.enableEventAutoload !== "boolean" ||
54
+ typeof config.options.feature.enableAdminCommands !== "boolean" ||
55
+ typeof config.options.feature.enableDevelopmentCommands !== "boolean") {
56
+ throw new EnvConfigError("All feature options must be boolean values.");
57
+ }
58
+ }
59
+ }
60
+ else {
61
+ throw new EnvConfigError("options field is required in configuration.");
62
+ }
63
+ }
64
+ constructor(config) {
65
+ super();
66
+ if (Config.instance) {
67
+ return;
68
+ }
69
+ Config.validateConfig(config);
70
+ Config.instance = config;
71
+ Log.initalizeMainLogger(config.options.logging);
72
+ }
73
+ static get(key) {
74
+ if (!Config.instance) {
75
+ throw new EnvConfigError("Config is not initialized!");
76
+ }
77
+ return Config.instance[key];
78
+ }
79
+ static isInitialized() {
80
+ return Config.instance !== undefined;
81
+ }
82
+ }
@@ -0,0 +1,8 @@
1
+ import { ErrorHandler as errorhandler } from "#types";
2
+ export declare class ErrorHandler extends errorhandler {
3
+ private static uncaughtException;
4
+ private static unhandledRejection;
5
+ private static shutdownHandler;
6
+ static fatal(reason?: string, error?: Error): void;
7
+ }
8
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/core/error.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItD,qBAAa,YAAa,SAAQ,YAAY;IAW5C,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAOhC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAMjC,OAAO,CAAC,MAAM,CAAC,eAAe;WAehB,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;CAM1D"}