discord-bot-cli
Version:
An easy way to build a command-based discord bot with discord.js.
238 lines (237 loc) • 11.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Command = void 0;
const parseFlags_1 = require("../other/parsing/parseFlags");
const parseArgs_1 = require("../other/parsing/parseArgs");
const CommandResult_1 = require("./CommandResult");
const CommandResultError_1 = require("./errors/CommandResultError");
const CommandCollection_1 = require("./CommandCollection");
const parseValue_1 = require("../other/parsing/parseValue");
const Throttler_1 = require("./Throttler");
const CommandLoadError_1 = require("./errors/CommandLoadError");
const HelpUtils_1 = require("../other/HelpUtils");
class Command {
constructor(
/** Path to the file that contains the command if it's a top-most command, `null` otherwise. */
filepath,
/** Name of this command. */
name,
/** Aliases of this command. */
aliases,
/** The list of permissions the bot's user require to execute this command. */
clientPermissions,
/** The list of permissions the user require to execute this command. */
userPermissions,
/** The list of exemples for this command. */
examples,
/** The description of the command. */
description,
/** This command's parent or `null` if it's a top-most command. */
parent,
/** The [[CommandSet]] that contains this command. */
commandSet,
/** A [[ReadonlyCommandCollection]] of this command's sub-commands. */
subs,
/** A `ReadonlyMap` with this command's arguments' [[ArgDefinition]] */
args,
/** A [[RestDefinition]] if this command use the rest argument, `undefined` otherwise. */
rest,
/** A `ReadonlyMap` with this command's flags' [[FlagDefinition]] */
flags, _flagsShortcuts, _executor, // eslint-disable-line @typescript-eslint/no-explicit-any
_canUse, _help, throttling, _useThrottlerOnSubs,
/** Either or not this command is ignored. */
ignored,
/** Either or not this command can only be used by dev (see [[CommandSetOptions.devIDs]]). */
devOnly,
/** Either or not this command can only be used from a guild. */
guildOnly,
/** Either or not the message that executed this command is deleted after the command execution. */
deleteMessage) {
var _a;
this.filepath = filepath;
this.name = name;
this.aliases = aliases;
this.clientPermissions = clientPermissions;
this.userPermissions = userPermissions;
this.examples = examples;
this.description = description;
this.parent = parent;
this.commandSet = commandSet;
this.subs = subs;
this.args = args;
this.rest = rest;
this.flags = flags;
this._flagsShortcuts = _flagsShortcuts;
this._executor = _executor;
this._canUse = _canUse;
this._help = _help;
this._useThrottlerOnSubs = _useThrottlerOnSubs;
this.ignored = ignored;
this.devOnly = devOnly;
this.guildOnly = guildOnly;
this.deleteMessage = deleteMessage;
this._throttler = throttling ? new Throttler_1.Throttler(throttling.count, throttling.duration) : throttling;
this._throttlingIncludeAdmins = (_a = throttling === null || throttling === void 0 ? void 0 : throttling.includeAdmins) !== null && _a !== void 0 ? _a : false;
}
/** @internal */
static load(filepath, commandSet) {
const module = require(filepath); // eslint-disable-line @typescript-eslint/no-var-requires
if (!module.default)
throw new CommandLoadError_1.CommandLoadError("Command data must be exported as default.");
return Command._build(filepath, commandSet, module.default, null, undefined);
}
static _build(filepath, commandSet, data, parent, parentHelp) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
function resolveInheritance(prop, defaultValue) {
var _a;
return ((_a = data.def.inherit) !== null && _a !== void 0 ? _a : true) && parent ? parent[prop] : defaultValue;
}
const subs = new CommandCollection_1.CommandCollection();
const cmd = new Command(filepath, data.name, (_a = data.def.aliases) !== null && _a !== void 0 ? _a : [], (_b = data.def.clientPermissions) !== null && _b !== void 0 ? _b : [], data.def.userPermissions, (_c = data.def.examples) !== null && _c !== void 0 ? _c : [], (_d = data.def.description) !== null && _d !== void 0 ? _d : "", parent, commandSet, subs, new Map(data.def.args ? Object.entries(data.def.args) : []), data.def.rest, new Map(data.def.flags ? Object.entries(data.def.flags) : []), new Map(data.def.flags
? Object.entries(data.def.flags)
.filter(function (a) {
return a[1].shortcut !== undefined;
})
.map(([k, v]) => [v.shortcut, k])
: []), data.executor, data.def.canUse, (_e = data.def.help) !== null && _e !== void 0 ? _e : parentHelp, data.def.throttling, (_f = data.def.useThrottlerForSubs) !== null && _f !== void 0 ? _f : true, (_g = data.def.ignore) !== null && _g !== void 0 ? _g : resolveInheritance("ignored", false), (_h = data.def.devOnly) !== null && _h !== void 0 ? _h : resolveInheritance("devOnly", false), (_j = data.def.guildOnly) !== null && _j !== void 0 ? _j : resolveInheritance("guildOnly", false), (_k = data.def.deleteMessage) !== null && _k !== void 0 ? _k : resolveInheritance("deleteMessage", false));
for (const subName in data.subs)
subs.add(Command._build(null, commandSet, data.subs[subName], cmd, ((_l = data.def.useHelpOnSubs) !== null && _l !== void 0 ? _l : false) || (!data.def.help && !!parentHelp) ? cmd._help : undefined));
return cmd;
}
// === Getter =====================================================
/**
* The [[Throttler]] used by this command.
*/
get throttler() {
if (this._throttler === null)
return undefined;
if (this._throttler)
return this._throttler;
if (this.parent && this.parent._useThrottlerOnSubs)
return this.parent.throttler;
return undefined;
}
/**
* Either or not this command has an executor.
*/
get hasExecutor() {
return this._executor !== undefined;
}
/**
* Returns an array containing all parents of this command, ordered from top-most command to this command (included).
* @returns An array of this command's parent [[Command]].
*/
getParents() {
const parents = [];
parents.unshift(this);
for (let parent = this.parent; parent; parent = parent.parent)
parents.unshift(parent);
return parents;
}
/**
* Determines if the bot's user have required permissions to execute this command from the guild.
* @param guild The guild from which check permissions.
* @returns Either or not the bot's user have required permissions to execute this command from the guild.
*/
hasClientPermissions(guild) {
return guild.me ? guild.me.hasPermission(this.clientPermissions) : false;
}
/**
* Determines if a member have required permissions to execute this command.
* @param member
* @returns Either or not the member have required permissions to execute this command.
*/
hasPermissions(member) {
if (!this.userPermissions) {
if (this.parent)
return this.parent.hasPermissions(member);
else
return true;
}
return member.hasPermission(this.userPermissions);
}
// =====================================================
/**
* Call the `canUse` handler of this command and its parents (see [[CommandDefinition.canUse]])
* and return the first negative result (`false` or a `string`) or `true`.
* @param user
* @param message
* @returns Result of `canUse` handlers.
*/
canUse(user, message) {
if (this.parent) {
const res = this.parent.canUse(user, message);
if (res !== true)
return res;
}
if (this._canUse)
return this._canUse(user, message);
return true;
}
/**
* Determines of the message author pass the [[canUse]] and the [[hasPermissions]] checks.
* @param message
* @returns Either or not the message author pass the [[canUse]] and the [[hasPermissions]] checks.
*/
checkPermissions(message) {
return (this.canUse(message.author, message) === true && (!message.member || this.hasPermissions(message.member)));
}
/**
* Call the suitable help handler for this command.
* @param message
* @param options
*/
async help(message, options) {
const context = {
message,
options,
commandSet: this.commandSet,
};
if (this._help) {
await this._help(this, context);
}
else if (this.commandSet.helpHandler) {
await this.commandSet.helpHandler(this, context);
}
else {
await HelpUtils_1.defaultHelp(this, context);
}
}
/** @internal */
async execute(message, inputArguments, options, commandSet) {
if (message.guild && !this.hasClientPermissions(message.guild))
throw new CommandResultError_1.CommandResultError(CommandResult_1.CommandResultUtils.clientPermissions(this));
/** This command throttler if is required and defined, undefined otherwise. */
const throttler = !options.devIDs.includes(message.author.id) &&
!(!this._throttlingIncludeAdmins && message.member && message.member.permissions.has("ADMINISTRATOR"))
? this.throttler
: undefined;
if (throttler === null || throttler === void 0 ? void 0 : throttler.throttled)
throw new CommandResultError_1.CommandResultError(CommandResult_1.CommandResultUtils.throttling(this));
if (!this._executor)
throw new CommandResultError_1.CommandResultError(CommandResult_1.CommandResultUtils.noExecutor(this));
const flags = parseFlags_1.parseFlags(message, inputArguments, this.flags, this._flagsShortcuts);
const args = parseArgs_1.parseArgs(message, flags.args, this.args);
const rest = [];
if (this.rest) {
for (const e of args.rest) {
const parsed = parseValue_1.parseValue(this.rest, message, e);
if (parsed.value !== undefined)
rest.push(parsed.value);
}
}
if (throttler)
throttler.add();
return await this._executor(Object.fromEntries(args.argValues), Object.fromEntries(flags.flagValues), {
rest: rest,
message,
guild: message.guild,
member: message.member,
channel: message.channel,
options,
commandSet,
command: this,
});
}
}
exports.Command = Command;