const path = require("path");
/** A command that can be run */
class Command {
/**
* @typedef {Object} CommandInfo
* @property {string} name - The name of the command (must be lowercase)
* @property {string[]} [aliases] - Alternative names for the command (all must be lowercase)
* @property {string} group - The ID of the group the command belongs to (must be lowercase)
* @property {string} description - A short description of the command
*/
/**
* @param {ModzClient} client - The client the command is for
* @param {CommandInfo} info - The command information
*/
constructor(client, info) {
this.constructor.validateInfo(client, info);
/**
* The client this command is for
* @name Command#client
*/
Object.defineProperty(this, "client", { value: client });
// Command name
/**
* Name of this command
* @type {string}
*/
this.name = info.name;
// Command aliases
/**
* Aliases for the command
* @type {string[]}
*/
this.aliases = info.aliases;
// Command group
/**
* Group this command belongs to
* @type {string}
*/
this.group = info.group;
// Command description
/**
* Description for the command
* @type {string}
*/
this.description = info.description;
}
// eslint-disable-next-line valid-jsdoc
/**
* Runs the command
* @param {Message} message - The message the command is being run for
* @param {string[]} args - The arguments for the command
* @return {Promise<?Message|?Array<Message>>}
* @abstract
*/
async run(message, args) { // eslint-disable-line no-unused-vars, require-await
throw new Error(`${this.constructor.name} doesn't have a run() method.`);
}
/**
* Validates the constructor parameters
* @param {ModzClient} client - Client to validate
* @param {CommandInfo} info - Info to validate
* @private
*/
static validateInfo(client, info) {
if (!client) throw new Error("A client must be specified.");
if (typeof info !== "object") throw new TypeError("Command info must be an Object.");
if (typeof info.name !== "string") throw new TypeError("Command name must be a string.");
if (info.name !== info.name.toLowerCase()) throw new Error("Command name must be lowercase.");
if (typeof info.description !== "string") throw new TypeError("Command description must be a string.");
if (info.aliases && (!Array.isArray(info.aliases) || info.aliases.some(ali => typeof ali !== "string"))) {
throw new TypeError("Command aliases must be an Array of strings.");
}
if (info.aliases && info.aliases.some(ali => ali !== ali.toLowerCase())) {
throw new Error("Command aliases must be lowercase.");
}
if (typeof info.group !== "string") throw new TypeError("group name must be a string.");
}
/**
* Reloads the command
*/
reload() {
let cmdPath, cached, newCmd;
try {
cmdPath = this.client.loader.resolveCommandPath(this.group, this.name);
cached = require.cache[cmdPath];
delete require.cache[cmdPath];
newCmd = require(cmdPath);
} catch (err) {
if (cached) require.cache[cmdPath] = cached;
try {
cmdPath = path.join(__dirname, this.group, `${this.name}.js`);
cached = require.cache[cmdPath];
delete require.cache[cmdPath];
newCmd = require(cmdPath);
} catch (err2) {
if (cached) require.cache[cmdPath] = cached;
if (err2.message.includes("Cannot find module")) throw err; else throw err2;
}
}
this.client.loader.reloadCommand(newCmd, this);
}
}
module.exports = Command;