discord-akairo
Version:
A highly customizable bot framework for Discord.js.
316 lines (276 loc) • 11.1 kB
JavaScript
const AkairoError = require('../../util/AkairoError');
const AkairoModule = require('../AkairoModule');
const Argument = require('./arguments/Argument');
const ArgumentRunner = require('./arguments/ArgumentRunner');
const ContentParser = require('./ContentParser');
/**
* Represents a command.
* @param {string} id - Command ID.
* @param {CommandOptions} [options={}] - Options for the command.
* @extends {AkairoModule}
*/
class Command extends AkairoModule {
constructor(id, options = {}) {
super(id, { category: options.category });
const {
aliases = [],
args = this.args || [],
quoted = true,
separator,
channel = null,
ownerOnly = false,
editable = true,
typing = false,
cooldown = null,
ratelimit = 1,
argumentDefaults = {},
description = '',
prefix = this.prefix,
clientPermissions = this.clientPermissions,
userPermissions = this.userPermissions,
regex = this.regex,
condition = this.condition || (() => false),
before = this.before || (() => undefined),
lock,
ignoreCooldown,
ignorePermissions,
flags = [],
optionFlags = []
} = options;
/**
* Command names.
* @type {string[]}
*/
this.aliases = aliases;
const { flagWords, optionFlagWords } = Array.isArray(args)
? ContentParser.getFlags(args)
: { flagWords: flags, optionFlagWords: optionFlags };
this.contentParser = new ContentParser({
flagWords,
optionFlagWords,
quoted,
separator
});
this.argumentRunner = new ArgumentRunner(this);
this.argumentGenerator = Array.isArray(args)
? ArgumentRunner.fromArguments(args.map(arg => [arg.id, new Argument(this, arg)]))
: args.bind(this);
/**
* Usable only in this channel type.
* @type {?string}
*/
this.channel = channel;
/**
* Usable only by the client owner.
* @type {boolean}
*/
this.ownerOnly = Boolean(ownerOnly);
/**
* Whether or not this command can be ran by an edit.
* @type {boolean}
*/
this.editable = Boolean(editable);
/**
* Whether or not to type during command execution.
* @type {boolean}
*/
this.typing = Boolean(typing);
/**
* Cooldown in milliseconds.
* @type {?number}
*/
this.cooldown = cooldown;
/**
* Uses allowed before cooldown.
* @type {number}
*/
this.ratelimit = ratelimit;
/**
* Default prompt options.
* @type {DefaultArgumentOptions}
*/
this.argumentDefaults = argumentDefaults;
/**
* Description of the command.
* @type {string|any}
*/
this.description = Array.isArray(description) ? description.join('\n') : description;
/**
* Command prefix overwrite.
* @type {?string|string[]|PrefixSupplier}
*/
this.prefix = typeof prefix === 'function' ? prefix.bind(this) : prefix;
/**
* Permissions required to run command by the client.
* @type {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier}
*/
this.clientPermissions = typeof clientPermissions === 'function' ? clientPermissions.bind(this) : clientPermissions;
/**
* Permissions required to run command by the user.
* @type {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier}
*/
this.userPermissions = typeof userPermissions === 'function' ? userPermissions.bind(this) : userPermissions;
/**
* The regex trigger for this command.
* @type {RegExp|RegexSupplier}
*/
this.regex = typeof regex === 'function' ? regex.bind(this) : regex;
/**
* Checks if the command should be ran by using an arbitrary condition.
* @method
* @param {Message} message - Message being handled.
* @returns {boolean}
*/
this.condition = condition.bind(this);
/**
* Runs before argument parsing and execution.
* @method
* @param {Message} message - Message being handled.
* @returns {any}
*/
this.before = before.bind(this);
/**
* The key supplier for the locker.
* @type {?KeySupplier}
*/
this.lock = lock;
if (typeof lock === 'string') {
this.lock = {
guild: message => message.guild && message.guild.id,
channel: message => message.channel.id,
user: message => message.author.id
}[lock];
}
if (this.lock) {
/**
* Stores the current locks.
* @type {?Set<string>}
*/
this.locker = new Set();
}
/**
* ID of user(s) to ignore cooldown or a function to ignore.
* @type {?Snowflake|Snowflake[]|IgnoreCheckPredicate}
*/
this.ignoreCooldown = typeof ignoreCooldown === 'function' ? ignoreCooldown.bind(this) : ignoreCooldown;
/**
* ID of user(s) to ignore `userPermissions` checks or a function to ignore.
* @type {?Snowflake|Snowflake[]|IgnoreCheckPredicate}
*/
this.ignorePermissions = typeof ignorePermissions === 'function' ? ignorePermissions.bind(this) : ignorePermissions;
/**
* The ID of this command.
* @name Command#id
* @type {string}
*/
/**
* The command handler.
* @name Command#handler
* @type {CommandHandler}
*/
}
/**
* Executes the command.
* @abstract
* @param {Message} message - Message that triggered the command.
* @param {any} args - Evaluated arguments.
* @returns {any}
*/
exec() {
throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'exec');
}
/**
* Parses content using the command's arguments.
* @param {Message} message - Message to use.
* @param {string} content - String to parse.
* @returns {Promise<Flag|any>}
*/
parse(message, content) {
const parsed = this.contentParser.parse(content);
return this.argumentRunner.run(message, parsed, this.argumentGenerator);
}
/**
* Reloads the command.
* @method
* @name Command#reload
* @returns {Command}
*/
/**
* Removes the command.
* @method
* @name Command#remove
* @returns {Command}
*/
}
module.exports = Command;
/**
* Options to use for command execution behavior.
* Also includes properties from AkairoModuleOptions.
* @typedef {AkairoModuleOptions} CommandOptions
* @prop {string[]} [aliases=[]] - Command names.
* @prop {ArgumentOptions[]|ArgumentGenerator} [args=[]] - Argument options or generator.
* @prop {boolean} [quoted=true] - Whether or not to consider quotes.
* @prop {string} [separator] - Custom separator for argument input.
* @prop {string[]} [flags=[]] - Flags to use when using an ArgumentGenerator.
* @prop {string[]} [optionFlags=[]] - Option flags to use when using an ArgumentGenerator.
* @prop {string} [channel] - Restricts channel to either 'guild' or 'dm'.
* @prop {boolean} [ownerOnly=false] - Whether or not to allow client owner(s) only.
* @prop {boolean} [typing=false] - Whether or not to type in channel during execution.
* @prop {boolean} [editable=true] - Whether or not message edits will run this command.
* @prop {number} [cooldown] - The command cooldown in milliseconds.
* @prop {number} [ratelimit=1] - Amount of command uses allowed until cooldown.
* @prop {string|string[]|PrefixSupplier} [prefix] - The prefix(es) to overwrite the global one for this command.
* @prop {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier} [userPermissions] - Permissions required by the user to run this command.
* @prop {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier} [clientPermissions] - Permissions required by the client to run this command.
* @prop {RegExp|RegexSupplier} [regex] - A regex to match in messages that are not directly commands.
* The args object will have `match` and `matches` properties.
* @prop {ExecutionPredicate} [condition] - Whether or not to run on messages that are not directly commands.
* @prop {BeforeAction} [before] - Function to run before argument parsing and execution.
* @prop {KeySupplier|string} [lock] - The key type or key generator for the locker. If lock is a string, it's expected one of 'guild', 'channel', or 'user'.
* @prop {Snowflake|Snowflake[]|IgnoreCheckPredicate} [ignoreCooldown] - ID of user(s) to ignore cooldown or a function to ignore.
* @prop {Snowflake|Snowflake[]|IgnoreCheckPredicate} [ignorePermissions] - ID of user(s) to ignore `userPermissions` checks or a function to ignore.
* @prop {DefaultArgumentOptions} [argumentDefaults] - The default argument options.
* @prop {StringResolvable} [description=''] - Description of the command.
*/
/**
* A function to run before argument parsing and execution.
* @typedef {Function} BeforeAction
* @param {Message} message - Message that triggered the command.
* @returns {any}
*/
/**
* A function used to supply the key for the locker.
* @typedef {Function} KeySupplier
* @param {Message} message - Message that triggered the command.
* @param {any} args - Evaluated arguments.
* @returns {string}
*/
/**
* A function used to check if the command should run arbitrarily.
* @typedef {Function} ExecutionPredicate
* @param {Message} message - Message to check.
* @returns {boolean}
*/
/**
* A function used to check if a message has permissions for the command.
* A non-null return value signifies the reason for missing permissions.
* @typedef {Function} MissingPermissionSupplier
* @param {Message} message - Message that triggered the command.
* @returns {any}
*/
/**
* A function used to return a regular expression.
* @typedef {Function} RegexSupplier
* @param {Message} message - Message to get regex for.
* @returns {RegExp}
*/
/**
* Generator for arguments.
* When yielding argument options, that argument is ran and the result of the processing is given.
* The last value when the generator is done is the resulting `args` for the command's `exec`.
* @typedef {GeneratorFunction} ArgumentGenerator
* @param {Message} message - Message that triggered the command.
* @param {ContentParserResult} parsed - Parsed content.
* @param {ArgumentRunnerState} state - Argument processing state.
* @returns {IterableIterator<ArgumentOptions|Flag>}
*/