discord-bot-cli
Version:
An easy way to build a command-based discord bot with discord.js.
191 lines (190 loc) • 8.19 kB
JavaScript
;
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,
};