UNPKG

rashi-discord-bot-lib

Version:

🚀 Powerful Discord bot framework with built-in database, event handling, and utilities

222 lines • 9.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandLoader = void 0; // src/structures/CommandLoader.ts const discord_js_1 = require("discord.js"); const FileLoader_1 = require("../utils/FileLoader"); const Logger_1 = require("../utils/Logger"); // Guards const commandGuards_1 = require("../utils/commandGuards"); const mongo_1 = require("../utils/mongo"); class CommandLoader { logger; fileLoader; slashCommands; prefixCommands; statistics; cooldowns; constructor(logger) { this.logger = logger || new Logger_1.Logger(); this.fileLoader = new FileLoader_1.FileLoader(); this.slashCommands = new discord_js_1.Collection(); this.prefixCommands = new discord_js_1.Collection(); } setCooldowns(cooldowns) { this.cooldowns = cooldowns; } setStatistics(statistics) { this.statistics = statistics; } async loadCommands(client, config) { let slashSize = 0; let prefixSize = 0; try { if (config?.slashPath) { slashSize = await this.loadSlashCommands(client, config.slashPath); } if (config?.prefixPath) { prefixSize = await this.loadPrefixCommands(client, config.prefixPath, config.prefix || '!'); } return { slashSize, prefixSize }; } catch (error) { this.logger.error('Failed to load commands:', error); return { slashSize: 0, prefixSize: 0 }; } } async loadSlashCommands(client, commandsPath) { const result = await this.fileLoader.loadFiles(commandsPath, { extensions: ['.js', '.ts'], recursive: true, requireDefault: true, }); const commandsToRegister = []; let registeredCount = 0; for (const command of result.files) { if (this.validateSlashCommand(command)) { this.slashCommands.set(command.data.name, command); commandsToRegister.push(command.data.toJSON()); registeredCount++; } } if (commandsToRegister.length > 0) { await this.registerSlashCommands(client, commandsToRegister); } this.setupSlashCommandHandler(client); if (result.errors.length > 0) { this.logger.warn(`Failed to load ${result.errors.length} slash command files`); } return registeredCount; } async loadPrefixCommands(client, commandsPath, prefix) { const result = await this.fileLoader.loadFiles(commandsPath, { extensions: ['.js', '.ts'], recursive: true, requireDefault: true, }); let registeredCount = 0; for (const command of result.files) { if (this.validatePrefixCommand(command)) { this.prefixCommands.set(command.name.toLowerCase(), command); registeredCount++; } } this.setupPrefixCommandHandler(client, prefix); if (result.errors.length > 0) { this.logger.warn(`Failed to load ${result.errors.length} prefix command files`); } return registeredCount; } validateSlashCommand(command) { return command && typeof command === 'object' && command.data?.name && typeof command.execute === 'function'; } validatePrefixCommand(command) { return command && typeof command === 'object' && typeof command.name === 'string' && typeof command.execute === 'function'; } async registerSlashCommands(client, commands) { const appId = client.application?.id || client.user?.id || process.env.CLIENT_ID || ''; if (!appId) return; const token = client.token || process.env.BOT_TOKEN || process.env.DISCORD_TOKEN || ''; const rest = new discord_js_1.REST({ version: '10' }).setToken(token); try { await rest.put(discord_js_1.Routes.applicationCommands(appId), { body: commands }); } catch (e) { this.logger.warn('Slash registration failed, retrying on ready'); client.once('ready', async () => { try { await rest.put(discord_js_1.Routes.applicationCommands(appId), { body: commands }); } catch (err) { this.logger.error('Failed again on ready:', err); } }); } } setupSlashCommandHandler(client) { client.on('interactionCreate', async (interaction) => { if (interaction.isAutocomplete()) { const cmd = this.slashCommands.get(interaction.commandName); if (cmd?.autocomplete) await cmd.autocomplete(interaction); return; } if (!interaction.isChatInputCommand()) return; const command = this.slashCommands.get(interaction.commandName); if (!command) return; const startTime = Date.now(); try { const ok = await (0, commandGuards_1.enforceSlashGuards)(interaction, command.data.name, command, this.cooldowns); if (!ok) return; const language = interaction.locale ?? 'en'; await command.execute(interaction, client, language); const responseTime = Date.now() - startTime; this.statistics?.trackCommand(command.data.name, 'slash', responseTime); await this.bumpCommandCounters('slash', command.data.name, interaction.guildId ?? null); } catch (error) { this.statistics?.trackError(); this.logger.error(`Error executing slash command ${interaction.commandName}:`, error); const msg = 'There was an error while executing this command!'; if (interaction.replied || interaction.deferred) { await interaction.followUp({ content: msg, ephemeral: true }); } else { await interaction.reply({ content: msg, ephemeral: true }); } } }); } setupPrefixCommandHandler(client, prefix) { client.on('messageCreate', async (message) => { if (message.author.bot) return; const content = message.content.trim(); const language = message.guild?.preferredLocale || 'en'; if (!content.startsWith(prefix)) { const fast = [...this.prefixCommands.values()].find((cmd) => cmd.fastUse && [cmd.name, ...(cmd.aliases ?? [])].map(n => n.toLowerCase()).includes(content.toLowerCase())); if (fast) { try { const ok = await (0, commandGuards_1.enforcePrefixGuards)(message, fast.name, fast, this.cooldowns); if (!ok) return; await fast.execute(message, [], client, language); this.statistics?.trackCommand(fast.name, 'prefix', 0); await this.bumpCommandCounters('prefix', fast.name, message.guildId ?? null); } catch (e) { this.statistics?.trackError(); this.logger.error(`Error executing fastUse command ${fast.name}:`, e); await message.reply('There was an error while executing this command!'); } } return; } const args = content.slice(prefix.length).trim().split(/ +/); const name = args.shift()?.toLowerCase(); if (!name) return; const command = this.prefixCommands.get(name) || [...this.prefixCommands.values()].find((cmd) => cmd.aliases?.map((a) => a.toLowerCase()).includes(name)); if (!command) return; const startTime = Date.now(); try { const ok = await (0, commandGuards_1.enforcePrefixGuards)(message, command.name, command, this.cooldowns); if (!ok) return; await command.execute(message, args, client, language); const responseTime = Date.now() - startTime; this.statistics?.trackCommand(command.name, 'prefix', responseTime); await this.bumpCommandCounters('prefix', command.name, message.guildId ?? null); } catch (error) { this.statistics?.trackError(); this.logger.error(`Error executing prefix command ${name}:`, error); await message.reply('There was an error while executing this command!'); } }); } async bumpCommandCounters(kind, name, guildId) { try { const db = (0, mongo_1.getDb)(); const col = db.collection('commandMetrics'); const now = new Date(); await col.updateOne({ scope: 'total' }, { $inc: { executed: 1 }, $set: { updatedAt: now }, $setOnInsert: { createdAt: now, executed: 0 } }, { upsert: true }); await col.updateOne({ scope: 'byCommand', kind, name, guildId: guildId ?? null }, { $inc: { executed: 1 }, $set: { updatedAt: now }, $setOnInsert: { createdAt: now, executed: 0 } }, { upsert: true }); } catch { } } getSlashCommands() { return this.slashCommands; } getPrefixCommands() { return this.prefixCommands; } } exports.CommandLoader = CommandLoader; //# sourceMappingURL=CommandLoader.js.map