UNPKG

aoijs-command-manager-v2

Version:

A command manager for aoi.js that handles slash commands with ease.

443 lines (442 loc) 22.9 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _ApplicationCommandManager_instances, _a, _ApplicationCommandManager_bot, _ApplicationCommandManager_commands, _ApplicationCommandManager_directory, _ApplicationCommandManager_options, _ApplicationCommandManager_commandStatus, _ApplicationCommandManager_checkForUpdates, _ApplicationCommandManager_validateAllCommands, _ApplicationCommandManager_validateCommand, _ApplicationCommandManager_showCommandTable, _ApplicationCommandManager_validateAndAddCommand, _ApplicationCommandManager_addPlugins; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApplicationCommandManager = exports.CommandManagerError = void 0; const discord_js_1 = require("discord.js"); const promises_1 = require("fs/promises"); const path_1 = require("path"); const colors_1 = __importDefault(require("colors")); const console_table_printer_1 = require("console-table-printer"); class CommandManagerError extends Error { constructor(message) { super(message); this.name = 'CommandManagerError'; } } exports.CommandManagerError = CommandManagerError; class ApplicationCommandManager { constructor(bot, options = {}) { var _b, _c, _d; _ApplicationCommandManager_instances.add(this); _ApplicationCommandManager_bot.set(this, void 0); _ApplicationCommandManager_commands.set(this, void 0); _ApplicationCommandManager_directory.set(this, null); _ApplicationCommandManager_options.set(this, void 0); _ApplicationCommandManager_commandStatus.set(this, new Map()); if (!bot) { throw new CommandManagerError('Bot instance is required'); } __classPrivateFieldSet(this, _ApplicationCommandManager_bot, bot, "f"); __classPrivateFieldSet(this, _ApplicationCommandManager_commands, new discord_js_1.Collection(), "f"); __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").slashCommandManager = this; __classPrivateFieldSet(this, _ApplicationCommandManager_options, { path: options.path, guildIds: options.guildIds, showTable: (_b = options.showTable) !== null && _b !== void 0 ? _b : true, checkUpdates: (_c = options.checkUpdates) !== null && _c !== void 0 ? _c : true, validateCommands: (_d = options.validateCommands) !== null && _d !== void 0 ? _d : true }, "f"); __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_addPlugins).call(this); if (__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").checkUpdates) { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_checkForUpdates).call(this); } if (__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").path) { this.load(__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").path).then(() => { setTimeout(() => { if (__classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").isReady()) { if (__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").validateCommands) { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_validateAllCommands).call(this); } this.sync(__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").guildIds); if (__classPrivateFieldGet(this, _ApplicationCommandManager_options, "f").showTable) { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_showCommandTable).call(this); } } }, 5000); }).catch(error => { console.error(colors_1.default.red('Error loading commands:'), error.message); }); } } /** * Load all application commands inside a directory. * @param dir - Application commands directory. * @throws {CommandManagerError} If the directory is invalid or commands are invalid */ async load(dir) { try { if (!dir) { throw new CommandManagerError('Directory path is required'); } const root = process.cwd(); const fullPath = (0, path_1.join)(root, dir); try { await (0, promises_1.lstat)(fullPath); } catch (_b) { throw new CommandManagerError(`Directory "${dir}" does not exist`); } const files = await (0, promises_1.readdir)(fullPath); if (files.length === 0) { throw new CommandManagerError(`No files found in directory "${dir}"`); } __classPrivateFieldSet(this, _ApplicationCommandManager_directory, dir, "f"); for (const file of files) { const filePath = (0, path_1.join)(root, dir, file); const stat = await (0, promises_1.lstat)(filePath); if (stat.isDirectory()) { await this.load((0, path_1.join)(dir, file)); continue; } if (!file.endsWith('.js') && !file.endsWith('.ts')) continue; try { const data = require(filePath); if (Array.isArray(data)) { for (const d of data) { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_validateAndAddCommand).call(this, d); } } else { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_validateAndAddCommand).call(this, data); } } catch (error) { console.error(colors_1.default.red(`Error loading command from ${filePath}:`), error instanceof Error ? error.message : String(error)); } } if (__classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").size === 0) { throw new CommandManagerError(`No valid commands found in directory "${dir}"`); } } catch (error) { if (error instanceof CommandManagerError) { throw error; } throw new CommandManagerError(`Failed to load commands: ${error instanceof Error ? error.message : String(error)}`); } } /** * Sync all application commands with the Discord API. * @param guildIDs - Optional array of guild IDs to sync commands to * @throws {CommandManagerError} If guild IDs are invalid or sync fails */ async sync(guildIDs) { var _b; try { if (!__classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").isReady()) { throw new CommandManagerError('Bot is not ready'); } const commands = this.getCommands(); if (Array.isArray(guildIDs)) { for (const guildId of guildIDs) { try { const guild = (_b = __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").guilds.cache.get(guildId)) !== null && _b !== void 0 ? _b : await __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").guilds.fetch(guildId); if (!guild) { throw new CommandManagerError(`Invalid Guild ID: ${guildId}`); } await guild.commands.set(commands); } catch (error) { throw new CommandManagerError(`Failed to sync commands to guild ${guildId}: ${error instanceof Error ? error.message : String(error)}`); } } } else { if (!__classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").application) { throw new CommandManagerError('Bot application not found'); } await __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").application.commands.set(commands); } } catch (error) { if (error instanceof CommandManagerError) { throw error; } throw new CommandManagerError(`Failed to sync commands: ${error instanceof Error ? error.message : String(error)}`); } } /** * Clear all cached commands. * @returns {ApplicationCommandManager} */ clearCommands() { __classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").clear(); return this; } /** * Returns the number of cached commands. * @returns {number} */ commandSize() { return __classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").size; } /** * Get all registered commands * @returns {ApplicationCommandDataResolvable[]} */ getCommands() { return Array.from(__classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").values()); } /** * Command specifications directory. */ get directory() { return __classPrivateFieldGet(this, _ApplicationCommandManager_directory, "f"); } } exports.ApplicationCommandManager = ApplicationCommandManager; _a = ApplicationCommandManager, _ApplicationCommandManager_bot = new WeakMap(), _ApplicationCommandManager_commands = new WeakMap(), _ApplicationCommandManager_directory = new WeakMap(), _ApplicationCommandManager_options = new WeakMap(), _ApplicationCommandManager_commandStatus = new WeakMap(), _ApplicationCommandManager_instances = new WeakSet(), _ApplicationCommandManager_checkForUpdates = async function _ApplicationCommandManager_checkForUpdates() { try { // const { stdout } = await execAsync('npm view aoi-command-manager-v2 version') // const latestVersion = stdout.trim() // const currentVersion = require('../package.json').version // if (latestVersion !== currentVersion) { // console.log(colors.yellow('\n[Update Available]')) // console.log(colors.yellow(`Current version: ${currentVersion}`)) // console.log(colors.yellow(`Latest version: ${latestVersion}`)) // console.log(colors.yellow('Run "npm install aoi-command-manager-v2@latest" to update\n')) // } } catch (error) { console.error(colors_1.default.red('Failed to check for updates:'), error instanceof Error ? error.message : String(error)); } }, _ApplicationCommandManager_validateAllCommands = function _ApplicationCommandManager_validateAllCommands() { __classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").forEach((command, name) => { try { __classPrivateFieldGet(this, _ApplicationCommandManager_instances, "m", _ApplicationCommandManager_validateCommand).call(this, command); __classPrivateFieldGet(this, _ApplicationCommandManager_commandStatus, "f").set(name, { name, status: '✅' }); } catch (error) { __classPrivateFieldGet(this, _ApplicationCommandManager_commandStatus, "f").set(name, { name, status: '❌', error: error instanceof Error ? error.message : String(error) }); } }); }, _ApplicationCommandManager_validateCommand = function _ApplicationCommandManager_validateCommand(command) { if (!command.name) { throw new CommandManagerError('Command must have a name'); } if (typeof command.name !== 'string') { throw new CommandManagerError('Command name must be a string'); } if (command.name.length < 1 || command.name.length > 32) { throw new CommandManagerError('Command name must be between 1 and 32 characters'); } if (!command.description) { throw new CommandManagerError('Command must have a description'); } if (typeof command.description !== 'string') { throw new CommandManagerError('Command description must be a string'); } if (command.description.length < 1 || command.description.length > 100) { throw new CommandManagerError('Command description must be between 1 and 100 characters'); } if (command.options) { for (const option of command.options) { if (!option.name) { throw new CommandManagerError(`Option in command ${command.name} must have a name`); } if (!option.description) { throw new CommandManagerError(`Option ${option.name} in command ${command.name} must have a description`); } } } }, _ApplicationCommandManager_showCommandTable = function _ApplicationCommandManager_showCommandTable() { const commands = Array.from(__classPrivateFieldGet(this, _ApplicationCommandManager_commandStatus, "f").values()); const hasErrors = commands.some(cmd => cmd.status === '❌'); const table = new console_table_printer_1.Table({ title: 'Loaded Slash Commands', columns: [ { name: 'Name', alignment: 'left' }, { name: 'Status', alignment: 'center' }, { name: 'Error', alignment: 'left' } ] }); commands.forEach(cmd => table.addRow({ Name: cmd.name, Status: cmd.status, Error: cmd.error || '' })); table.printTable(); if (hasErrors) { console.log(colors_1.default.yellow('\n⚠️ Some commands have errors. Run $applicationCommandValidate to see detailed validation results.')); } }, _ApplicationCommandManager_validateAndAddCommand = function _ApplicationCommandManager_validateAndAddCommand(command) { if (!command.data) { throw new CommandManagerError('Command must have a data property'); } if (!command.data.name) { throw new CommandManagerError('Command must have a name'); } if (typeof command.data.name !== 'string') { throw new CommandManagerError('Command name must be a string'); } if (command.data instanceof discord_js_1.SlashCommandBuilder) { const jsonData = command.data.toJSON(); __classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").set(command.data.name, jsonData); } else { const jsonData = command.data; __classPrivateFieldGet(this, _ApplicationCommandManager_commands, "f").set(command.data.name, jsonData); } }, _ApplicationCommandManager_addPlugins = function _ApplicationCommandManager_addPlugins() { // Sync commands function __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").functionManager.createFunction({ name: '$applicationCommandSync', type: 'djs', code: async function (d) { const data = d.util.aoiFunc(d); const guildIDs = data.inside.splits; if (!(d.bot.slashCommandManager instanceof _a)) return d.aoiError.fnError(d, 'custom', { inside: data.inside }, 'Cannot find an instance of ApplicationCommandManager!'); if (d.bot.slashCommandManager.commandSize() === 0) return d.aoiError.fnError(d, 'custom', { inside: data.inside }, 'Cannot sync empty commands!'); try { await d.bot.slashCommandManager.sync(guildIDs.length > 0 ? guildIDs : undefined); return { code: d.util.setCode(data) }; } catch (error) { return d.aoiError.fnError(d, 'custom', { inside: data.inside }, `Failed to sync commands: ${error instanceof Error ? error.message : String(error)}`); } } }); // Reload commands function __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").functionManager.createFunction({ name: '$applicationCommandReload', type: 'djs', code: async (d) => { const data = d.util.aoiFunc(d); if (!(d.bot.slashCommandManager instanceof _a)) return d.aoiError.fnError(d, 'custom', { inside: data.inside }, 'Cannot find an instance of ApplicationCommandManager!'); if (!d.bot.slashCommandManager.directory) return d.aoiError.fnError(d, 'custom', {}, 'Cannot find a specification directory!'); try { await d.bot.slashCommandManager.load(d.bot.slashCommandManager.directory); data.result = true; } catch (error) { data.result = false; console.error(colors_1.default.red('Failed to reload commands:'), error instanceof Error ? error.message : String(error)); } return { code: d.util.setCode(data) }; } }); // Validate commands function __classPrivateFieldGet(this, _ApplicationCommandManager_bot, "f").functionManager.createFunction({ name: '$applicationCommandValidate', type: 'djs', code: async (d) => { const data = d.util.aoiFunc(d); if (!(d.bot.slashCommandManager instanceof _a)) return d.aoiError.fnError(d, 'custom', { inside: data.inside }, 'Cannot find an instance of ApplicationCommandManager!'); function isSlashCommand(cmd) { return (typeof cmd === 'object' && typeof cmd.name === 'string' && typeof cmd.description === 'string'); } try { const manager = d.bot.slashCommandManager; const commands = manager.getCommands(); const errors = []; for (const command of commands) { if (!isSlashCommand(command)) { errors.push('Invalid command object (missing name or description)'); continue; } try { if (!command.name) { errors.push(`Command missing name`); continue; } if (typeof command.name !== 'string') { errors.push(`${command.name}: Name must be a string`); continue; } if (command.name.length < 1 || command.name.length > 32) { errors.push(`${command.name}: Name must be between 1 and 32 characters`); continue; } if (!command.description) { errors.push(`${command.name}: Missing description`); continue; } if (typeof command.description !== 'string') { errors.push(`${command.name}: Description must be a string`); continue; } if (command.description.length < 1 || command.description.length > 100) { errors.push(`${command.name}: Description must be between 1 and 100 characters`); continue; } if (command.options && command.options.length > 25) { errors.push(`${command.name}: Cannot have more than 25 options`); continue; } if (command.options) { for (const option of command.options) { if (!option.name) { errors.push(`${command.name}: Option missing name`); continue; } if (!option.description) { errors.push(`${command.name}: Option ${option.name} missing description`); continue; } } } } catch (error) { errors.push(`${command.name}: ${error instanceof Error ? error.message : String(error)}`); } } if (errors.length > 0) { console.log(colors_1.default.red('\nSlash Command Validation Errors:')); errors.forEach(error => console.log(colors_1.default.red(`❌ ${error}`))); data.result = false; } else { console.log(colors_1.default.green('\n✅ All slash commands are valid!')); data.result = true; } } catch (error) { data.result = false; console.error(colors_1.default.red('Failed to validate commands:'), error instanceof Error ? error.message : String(error)); } return { code: d.util.setCode(data) }; } }); };