aoijs-command-manager-v2
Version:
A command manager for aoi.js that handles slash commands with ease.
443 lines (442 loc) • 22.9 kB
JavaScript
;
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)
};
}
});
};