UNPKG

dexare

Version:

Modular and extendable Discord bot framework

168 lines (167 loc) 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const common_tags_1 = require("common-tags"); const constants_1 = require("../../constants"); class DexareCommand { /** * @param creator The instantiating creator. * @param opts The options for the command. */ constructor(client, opts) { /** Whether the command is enabled globally */ this._globalEnabled = true; if (this.constructor.name === 'DexareCommand') throw new Error('The base DexareCommand cannot be instantiated.'); this.cmdsModule = client.commands; this.client = client; this.name = opts.name; this.aliases = opts.aliases || []; this.category = opts.category || 'Uncategorized'; this.description = opts.description; this.userPermissions = opts.userPermissions; this.clientPermissions = opts.clientPermissions; this.throttling = opts.throttling; this.metadata = opts.metadata; } /** * Checks whether the context member has permission to use the command. * @param ctx The triggering context * @return {boolean|string} Whether the member has permission, or an error message to respond with if they don't */ hasPermission(ctx, event) { if (this.userPermissions) { const permObject = this.client.permissions.toObject(ctx.message); let permissionMap = event && event.has('dexare/permissionMap') ? event.get('dexare/permissionMap') : {}; permissionMap = this.client.permissions.map(permObject, this.userPermissions, permissionMap, event); if (event) event.set('dexare/permissionMap', permissionMap); const missing = this.userPermissions.filter((perm) => !permissionMap[perm]); if (missing.length > 0) { if (missing.includes('dexare.elevated')) return `The \`${this.name}\` command can only be used by the bot developers or elevated users.`; else if (missing.includes('dexare.nsfwchannel')) return `The \`${this.name}\` command can only be ran in NSFW channels.`; else if (missing.includes('dexare.inguild')) return `The \`${this.name}\` command can only be ran in guilds.`; else if (missing.length === 1) { return `The \`${this.name}\` command requires you to have the "${constants_1.PermissionNames[missing[0]] || missing[0]}" permission.`; } return (0, common_tags_1.oneLine) ` The \`${this.name}\` command requires you to have the following permissions: ${missing.map((perm) => constants_1.PermissionNames[perm] || perm).join(', ')} `; } } return true; } /** * Called when the command is prevented from running. * @param ctx Command context the command is running from * @param reason Reason that the command was blocked * (built-in reasons are `permission`, `throttling`) * @param data Additional data associated with the block. * - permission: `response` ({@link string}) to send * - throttling: `throttle` ({@link Object}), `remaining` ({@link number}) time in seconds */ onBlock(ctx, reason, data) { switch (reason) { case 'permission': { if (data.response) return ctx.reply(data.response); return ctx.reply(`You do not have permission to use the \`${this.name}\` command.`); } case 'clientPermissions': { if (data.missing.length === 1) { return ctx.reply(`I need the "${constants_1.PermissionNames['discord.' + data.missing[0].toLowerCase()]}" permission for the \`${this.name}\` command to work.`); } return ctx.reply((0, common_tags_1.oneLine) ` I need the following permissions for the \`${this.name}\` command to work: ${data.missing.map((perm) => constants_1.PermissionNames['discord.' + perm.toLowerCase()]).join(', ')} `); } case 'throttling': { return ctx.reply(data.remaining ? `You may not use the \`${this.name}\` command again for another ${data.remaining.toFixed(1)} seconds.` : `You are currently ratelimited from using the \`${this.name}\` command. Try again later.`); } default: return null; } } /** * Called when the command produces an error while running. * @param err Error that was thrown * @param ctx Command context the command is running from */ onError(err, ctx) { return ctx.reply(`An error occurred while running the \`${this.name}\` command.`); } /** * Checks if the command is usable for a message * @param message The message */ isUsable(ctx) { if (!ctx) return this._globalEnabled; const hasPermission = this.hasPermission(ctx); return typeof hasPermission !== 'string' && hasPermission; } /** * Used to throttle a user from the command. * @param object The permission object to throttle * @param event The event to use */ async throttle(object, event) { if (!this.throttling) return; const permObject = this.client.permissions.toObject(object); if (this.throttling.bypass && this.throttling.bypass.length) { let permissionMap = event && event.has('dexare/permissionMap') ? event.get('dexare/permissionMap') : {}; permissionMap = this.client.permissions.map(permObject, this.throttling.bypass, permissionMap, event); if (event) event.set('dexare/permissionMap', permissionMap); const missing = this.throttling.bypass.filter((perm) => !permissionMap[perm]); if (!missing.length) return; } return await this.client.data.throttle('command_' + this.name, this.throttling, permObject.user.id, event); } /** * Runs the command. * @param ctx The context of the message */ async run(ctx) { throw new Error(`${this.constructor.name} doesn't have a run() method.`); } /** * Preloads the command. * This function is called upon loading the command, NOT after logging in. */ async preload() { return true; } /** Reloads the command. */ reload() { if (!this.filePath) throw new Error('Cannot reload a command without a file path defined!'); const newCommand = require(this.filePath); this.cmdsModule.reregister(newCommand, this); } /** Unloads the command. */ unload() { if (this.filePath && require.cache[this.filePath]) delete require.cache[this.filePath]; this.cmdsModule.unregister(this); } /** * Finalizes the return output. * @param response The response from the command run * @param ctx The context of the message */ finalize(response, ctx) { if (typeof response === 'string' || (response && response.constructor && response.constructor.name === 'Object')) return ctx.send(response); } } exports.default = DexareCommand;