UNPKG

discord-bot-cli

Version:

An easy way to build a command-based discord bot with discord.js.

191 lines (190 loc) 8.19 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandSet = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const Command_1 = require("./Command"); const logger_1 = require("../logger"); const CommandResult_1 = require("./CommandResult"); const localization_json_1 = __importDefault(require("../data/localization.json")); const deepMerge_1 = require("../utils/deepMerge"); const HelpUtils_1 = require("../other/HelpUtils"); const template_1 = require("../utils/template"); const CommandResultError_1 = require("./errors/CommandResultError"); const CommandCollection_1 = require("./CommandCollection"); const PathUtils_1 = require("../utils/PathUtils"); const chalk_1 = __importDefault(require("chalk")); const CommandLoadError_1 = require("./errors/CommandLoadError"); class CommandSet { constructor(_defaultOptions) { this._defaultOptions = _defaultOptions; this._commands = new CommandCollection_1.CommandCollection(); /** If defined, called when [[Command.help]] is called and if the command didn't define its own help handler. */ this.helpHandler = undefined; } /** A readonly collection of commands. */ get commands() { return this._commands; } _loadFile(path) { const debugPath = chalk_1.default.underline(PathUtils_1.relativeFromEntryPoint(path)); logger_1.Logger.debug(`Load command from ${debugPath}`); try { const command = Command_1.Command.load(path, this); if (command.ignored) logger_1.Logger.log(`Command ignored ${debugPath}`); else { if (!this._commands.add(command)) logger_1.Logger.warn(`Command not loaded, the name is already taken. (${debugPath})`); } } catch (e) { let error; if (e instanceof CommandLoadError_1.CommandLoadError) error = e.message; else error = e; logger_1.Logger.error(`Fail to load command from ${debugPath}\n${chalk_1.default.red("Error:")}`, error); } } /** * Loads commands from the given folder path. * Command files must have the extension `.cmd.js`. * @param commandDirPath - The path to the folder where the commands are (relative to node entry point). * @param includeTS - If set to `true`, files with `.cmd.ts` extension are also loaded. Usefull if you use `ts-node` (default is `false`) */ loadCommands(commandDirPath, includeTS = false) { try { commandDirPath = PathUtils_1.resolveFromEntryPoint(commandDirPath); const cmdFiles = fs_1.default .readdirSync(commandDirPath) .filter(file => file.endsWith(".cmd.js") || (includeTS && file.endsWith(".cmd.ts"))); for (const file of cmdFiles) { const filePath = path_1.default.resolve(path_1.default.format({ dir: commandDirPath, base: file })); this._loadFile(filePath); } } catch (e) { logger_1.Logger.error(`Fail to load commands in ${commandDirPath} :`, e); } } buildin(...buildinCommandNames) { if (buildinCommandNames.includes("all")) { this.loadCommands(__dirname + "/../commands"); } else { for (const name of buildinCommandNames) { const filePath = path_1.default.resolve(path_1.default.format({ dir: __dirname + "../commands", name: name, ext: ".cmd.js", })); if (fs_1.default.existsSync(filePath)) this._loadFile(filePath); } } } /** * Reloads a command. * @param command - The command to reload. */ reload(command) { if (!command.filepath) throw Error("Cannot reload sub command."); this._commands.delete(command); delete require.cache[command.filepath]; this._loadFile(command.filepath); } /** @internal */ resolve(args) { const _args = [...args]; // make a copy of args let cmd = this._commands.get(_args[0]); if (cmd) { let sub = cmd; do { cmd = sub; _args.shift(); sub = cmd.subs.get(_args[0]); } while (sub); return { command: cmd, args: _args }; } else { return { args: _args }; } } /** * Parses the message's content and executes the command. * @param message - The message to parse. * @param options - Options to define the parsing behaviour. * @returns The result of the parsing. */ async parse(message, options) { // function OptionsError(paramName: string) { // return new Error( // `Invalid options value: "${paramName}" is invalid.` // ); // } var _a; async function Reply(content) { await message.reply(content).catch(logger_1.Logger.error); } const opts = deepMerge_1.deepMerge({}, defaultOptions, this._defaultOptions, options); let content; const botMentionStr = `<@!${(_a = message.client.user) === null || _a === void 0 ? void 0 : _a.id}>`; if (opts.allowMentionAsPrefix && message.content.startsWith(botMentionStr)) content = message.content.substring(botMentionStr.length); else if (message.content.startsWith(opts.prefix)) content = message.content.substring(opts.prefix.length); else return CommandResult_1.CommandResultUtils.notPrefixed(); // extract the command & arguments from message const rawArgs = (content.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g) || []).map(a => /^(".*"|'.*')$/.test(a) ? a.substring(1, a.length - 1) : a); const { command, args } = this.resolve(rawArgs); if (!command) return CommandResult_1.CommandResultUtils.commandNotFound(); if (command.guildOnly && !message.guild) { await message.reply(template_1.template(opts.localization.misc.guildOnlyWarning, { command: HelpUtils_1.commandFullName(command), })); return CommandResult_1.CommandResultUtils.guildOnly(command); } if (command.devOnly && !opts.devIDs.includes(message.author.id)) return CommandResult_1.CommandResultUtils.devOnly(command); if (!opts.devIDs.includes(message.author.id) || !opts.skipDevsPermissionsChecking) { const result = command.canUse(message.author, message); if (typeof result === "string") await Reply(result); if (result !== true) return CommandResult_1.CommandResultUtils.unauthorizedUser(command); } if (message.member && !command.hasPermissions(message.member)) return CommandResult_1.CommandResultUtils.unauthorizedUser(command); try { const result = await command.execute(message, args, opts, this); if (command.deleteMessage && message.channel.type === "text") await message.delete().catch(logger_1.Logger.error); return CommandResult_1.CommandResultUtils.ok(command, result); } catch (e) { if (e instanceof CommandResultError_1.CommandResultError) { if (e.replyMessage && e.replyMessage !== "") await Reply(e.replyMessage); return e.commandResult; } else return CommandResult_1.CommandResultUtils.error(e); } } } exports.CommandSet = CommandSet; /** @internal */ const defaultOptions = { prefix: "", devIDs: [], localization: localization_json_1.default, allowMentionAsPrefix: false, skipDevsPermissionsChecking: false, };