UNPKG

@ayanaware/bentocord

Version:

Bentocord is a Bento plugin designed to rapidly build fully functional Discord Bots.

836 lines 37.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandManager = void 0; const util = require("util"); const bento_1 = require("@ayanaware/bento"); const logger_api_1 = require("@ayanaware/logger-api"); const eris_1 = require("eris"); const BentocordInterface_1 = require("../BentocordInterface"); const BentocordVariable_1 = require("../BentocordVariable"); const Discord_1 = require("../discord/Discord"); const DiscordEvent_1 = require("../discord/constants/DiscordEvent"); const CommandContext_1 = require("./CommandContext"); const CommandManager_1 = require("./constants/CommandManager"); const OptionType_1 = require("./constants/OptionType"); const SuppressorType_1 = require("./constants/SuppressorType"); const Parser_1 = require("./internal/Parser"); const Tokenizer_1 = require("./internal/Tokenizer"); const options_1 = require("./options"); const supressors_1 = require("./supressors"); const { MessageFlags } = eris_1.Constants; const log = logger_api_1.Logger.get(null); class CommandManager { constructor() { this.name = '@ayanaware/bentocord:CommandManager'; this.commands = new Map(); this.aliases = new Map(); this.resolvers = new Map(); this.suppressors = new Map(); } async onLoad() { // Load built-in resolvers options_1.Resolvers.forEach(resolver => this.addResolver(resolver)); // Load built-in suppressors supressors_1.Suppressors.forEach(suppressor => this.addSuppressor(suppressor)); } async onChildLoad(entity) { try { if (typeof entity.definition === 'object') { return this.addCommand(entity); } else if (typeof entity.option !== 'undefined') { this.addResolver(entity); } else if (typeof entity.suppressor !== 'undefined') { this.addSuppressor(entity); } } catch (e) { log.warn(e.toString()); } } async onChildUnload(entity) { try { if (typeof entity.definition === 'object') { this.removeCommand(entity); } else if (typeof entity.option !== 'undefined') { this.removeResolver(entity.option); } else if (typeof entity.suppressor !== 'undefined') { this.removeSuppressor(entity.suppressor); } } catch (e) { log.warn(e.toString()); } } /** * Add Resolver * @param resolver OptionResolver */ addResolver(resolver) { this.resolvers.set(resolver.option, resolver); } /** * Remove Resolver * @param type OptionType or string */ removeResolver(type) { this.resolvers.delete(type); } getResolvers() { return this.resolvers; } findResolver(type) { return this.resolvers.get(type); } async executeResolver(ctx, option, input) { const resolver = this.findResolver(option.type); if (!resolver) return null; return resolver.resolve(ctx, option, input); } /** * Add Suppressor * @param suppressor Suppressor */ addSuppressor(suppressor) { this.suppressors.set(suppressor.suppressor, suppressor); } /** * Remove Suppressors * @param type SuppressorType or string */ removeSuppressor(type) { this.suppressors.delete(type); } async executeSuppressors(ctx, option) { if (!Array.isArray(option.suppressors) || option.suppressors.length < 1) return false; for (let definition of option.suppressors) { if (typeof definition !== 'object') definition = { type: definition, args: [] }; // resolve name let name = definition.type; if (typeof name === 'number') name = SuppressorType_1.SuppressorType[name]; let args = definition.args; if (typeof definition.args === 'function') args = await definition.args(); if (!Array.isArray(args)) args = []; const suppressor = this.suppressors.get(definition.type); if (!suppressor) continue; const result = await suppressor.suppress(ctx, option, ...args); if (result === false) continue; return { name, message: result }; } return false; } /** * Get prefix for a guild * @param snowflake guildId * @returns prefix */ async getPrefix(snowflake) { let prefix = this.defaultPrefix; if (snowflake) { const customPrefix = await this.interface.getPrefix(snowflake); if (customPrefix) prefix = customPrefix; } return prefix; } /** * Set prefix for a guild * @param snowflake guildId * @param prefix new prefix */ async setPrefix(snowflake, prefix) { return this.interface.setPrefix(snowflake, prefix); } /** * Get primary name of command alias or option name * @param name string or array of string and translatables. First element is always a string * @returns string */ getPrimaryName(name) { let primary = name; if (Array.isArray(primary)) primary = primary[0]; return primary.toLocaleLowerCase().replace(/\s/g, ''); } /** * Get all translations for a possibly translatable * @param item Array<PossiblyTranslatable> * @returns Array of Tuple [string, Array<{ lang: string }>] */ async getItemTranslations(items, normalize = false) { if (!Array.isArray(items)) items = [items]; const collector = []; for (const item of items) { const iteration = ['', {}]; if (typeof item === 'object') { iteration[0] = await this.interface.formatTranslation(item.key, item.repl, {}, item.backup ?? item.key); iteration[1] = await this.interface.formatTranslationMap(item.key, item.repl) ?? {}; } else if (typeof item === 'string') { iteration[0] = item; } if (normalize) { // Thx to infinitestory for Discord's validation regex iteration[0] = iteration[0].toLocaleLowerCase().replace(/[^-_\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]|\s/gu, ''); iteration[1] = Object.fromEntries(Object.entries(iteration[1]).map(([k, v]) => [k, v.toLocaleLowerCase().replace(/[^-_\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]|\s/gu, '')])); } collector.push(iteration); } return collector; } /** * Get all commands and their details */ getCommands() { return this.commands; } getCategorizedCommands() { const out = new Map(); for (const [command, details] of this.commands) { const category = details.category ?? 'general'; if (!out.has(category)) out.set(category, new Map()); out.get(category).set(command, details); } return out; } isSubCommand(option) { return option.type === OptionType_1.OptionType.SUB_COMMAND; } isSubCommandGroup(option) { return option.type === OptionType_1.OptionType.SUB_COMMAND_GROUP; } isAnySubCommand(option) { return this.isSubCommand(option) || this.isSubCommandGroup(option); } /** * Add command * @param command Command */ async addCommand(command) { if (typeof command.execute !== 'function') throw new Error('Execute must be a function'); if (typeof command.definition !== 'object') throw new Error('Definition must be an object'); const definition = command.definition; if (definition.name.length < 1) throw new Error('At least one name must be defined'); const primary = this.getPrimaryName(definition.name); // check dupes & save if (this.commands.has(primary)) throw new Error(`Command name "${primary}" already exists`); // add permissions const permissions = this.getPermissionDetails(command); const commandDetails = { command, definition, category: definition.category ?? null, permissions }; this.commands.set(primary, commandDetails); const aliases = await this.getItemTranslations(definition.name, true); // register alias => primary alias for (const [aliasName, translations] of aliases) { // check if alias exists and references a different command const existing = this.aliases.get(aliasName); if (existing && existing !== primary) throw new Error(`${primary}: Attempted to register existing alias "${aliasName}", which is registered to "${existing}"`); this.aliases.set(aliasName, primary); // register translations for (const [lang, translation] of Object.entries(translations)) { const existingTranslation = this.aliases.get(translation); if (existingTranslation && existingTranslation !== primary) { log.warn(`${primary}: Skipping "${lang}" translation alias "${translation}", because it is already registered to "${existingTranslation}"`); continue; } this.aliases.set(translation, primary); } } // emit event this.api.emit(CommandManager_1.CommandManagerEvent.COMMAND_ADD, command); } /** * Remove Command * @param command Command */ removeCommand(command) { if (typeof command === 'string') command = this.findCommand(command); if (!command) throw new Error('Failed to find Command'); const definition = command.definition; const primary = this.getPrimaryName(definition.name); // remove any aliases for (const [alias, name] of this.aliases.entries()) { if (name === primary) this.aliases.delete(alias); } // remove reference this.commands.delete(primary); // emit event this.api.emit(CommandManager_1.CommandManagerEvent.COMMAND_REMOVE, command); } /** * Find Command by alias * @param alias Alias * @returns Command */ findCommand(alias) { if (alias) alias = alias.toLocaleLowerCase(); // convert alias to primary alias const primary = this.aliases.get(alias); if (!primary) return null; // get command const command = this.commands.get(primary); if (!command) return null; return command.command; } /** * Get all valid command related permissions, include all and categories * @returns Map of permission => CommandPermissionDetails */ getPermissions() { const collector = new Map(); collector.set('all', { permission: 'all', defaults: { user: false, admin: true }, hidden: false, type: 'GROUP' }); for (const commandDetails of this.commands.values()) { const { command, permissions } = commandDetails; if (!command) continue; // add category permissions if (command.definition.category) { const category = ['all', command.definition.category].join('.'); if (!collector.has(category)) collector.set(category, { permission: category, defaults: { user: false, admin: true }, hidden: false, type: 'GROUP' }); } for (const permission of permissions) collector.set(permission.permission, { ...permission, command, type: 'COMMAND' }); } return collector; } /** * Runs pre-flight checks such as perms & suppressors before executing command * @param command Command * @param ctx CommandContext * @returns boolean, if false you should not execute the command */ async prepareCommand(command, ctx) { const definition = command.definition; const primary = this.getPrimaryName(definition.name); // handle ignoreMode if (this.ignoreMode === 'true' && !(await ctx.isBotOwner())) { log.warn(`Skipped Command "${primary}" execution by "${ctx.userId}", because the bot is in ignoreMode.`); if (ctx instanceof CommandContext_1.InteractionCommandContext) await ctx.createResponse({ content: 'Execution disabled, bot is in ignoreMode', flags: MessageFlags.EPHEMERAL }); return false; } // handle checkCommand const check = await this.interface.checkCommand(command, ctx); if (!check) { if (ctx instanceof CommandContext_1.InteractionCommandContext) await ctx.deleteExecutionMessage(); return false; } // handle allowDM if (ctx.channel.type === eris_1.Constants.ChannelTypes.DM && !(definition.allowDM ?? true)) { await ctx.createTranslatedResponse('BENTOCORD_COMMAND_DM_DISABLED', {}, 'This command cannot be used in direct messages'); return false; } // process suppressors const suppressed = await this.executeSuppressors(ctx, definition); if (suppressed) { await ctx.createTranslatedResponse('BENTOCORD_SUPPRESSOR_HALT', { suppressor: suppressed.name, message: suppressed.message }, 'Execution was halted by `{suppressor}`: {message}'); return false; } // check permission const permissionName = definition.permissionName ?? primary; return this.interface.checkPermission(ctx, permissionName, definition.permissionDefaults); } async executeCommand(command, ctx, options) { const definition = command.definition; const primary = this.getPrimaryName(definition.name); // selfPermissions if (ctx.guild) { const selfPermissions = definition.selfPermissions ?? []; // add default selfPermissions const defaultSelfPermissions = await this.interface.selfPermissions(command, ctx); defaultSelfPermissions.forEach(p => selfPermissions.includes(p) ? null : selfPermissions.push(p)); if (selfPermissions.length > 0) { const channelPermissions = ctx.channel.permissionsOf(ctx.selfId); const guildPermissions = ctx.guild.permissionsOf(ctx.selfId); const unfufilled = []; for (const permission of selfPermissions) { // check overrides, then server wide if (!channelPermissions.has(permission)) unfufilled.push(permission); else if (!guildPermissions.has(permission)) unfufilled.push(permission); } if (unfufilled.length > 0) { return ctx.createTranslatedResponse('BENTOCORD_COMMANDMANAGER_MISSING_PERMS', { permissions: unfufilled.join(', ') }, 'Command cannot be executed. The following Discord permissions must be granted:\n```{permissions}```'); } } } const start = process.hrtime(); // Command Execution try { // TODO: Use Typescript metadata to ensure .execute() and options match await command.execute(ctx, options); const end = process.hrtime(start); const nano = end[0] * 1e9 + end[1]; const mili = nano / 1e6; this.api.emit(CommandManager_1.CommandManagerEvent.COMMAND_SUCCESS, command, ctx, options, mili); log.debug(`Command "${primary}" executed by "${ctx.userId}", took ${mili}ms`); } catch (e) { const end = process.hrtime(start); const nano = end[0] * 1e9 + end[1]; const mili = nano / 1e6; // halt requested (this is lazy, I'll fix it later, probably) if (e === CommandManager_1.NON_ERROR_HALT) { this.api.emit(CommandManager_1.CommandManagerEvent.COMMAND_SUCCESS, command, ctx, options, mili); log.debug(`Command "${primary}" executed by "${ctx.userId}", took ${mili}ms`); return; } this.api.emit(CommandManager_1.CommandManagerEvent.COMMAND_FAILURE, e, command, ctx, options, mili); log.error(`Command ${primary}.execute() error:\n${util.inspect(e)}`); if (e instanceof Error) { return ctx.createTranslatedResponse('BENTOCORD_COMMANDMANAGER_ERROR', { error: e.message }, 'An error occurred while executing this command: `{error}`'); } } } getPermissionDetails(command) { const definition = command.definition; const collector = []; const primary = this.getPrimaryName(definition.name); const permissionName = definition.permissionName ?? primary; let defaults = definition.permissionDefaults ?? { user: true, admin: true }; if (typeof defaults === 'boolean') defaults = { user: defaults, admin: false }; const hidden = definition.hidden ?? false; collector.push({ permission: permissionName, defaults, hidden, path: [] }); // walk options const walkOptions = (options = [], path = [], permPath = []) => { for (const option of (options ?? [])) { if (option.type !== OptionType_1.OptionType.SUB_COMMAND && option.type !== OptionType_1.OptionType.SUB_COMMAND_GROUP) continue; const subPrimary = this.getPrimaryName(option.name); const subPath = [...path, subPrimary]; const subName = option.permissionName ?? subPrimary; const subPermPath = [...permPath, subName]; let subDefaults = option.permissionDefaults ?? { user: true, admin: true }; if (typeof subDefaults === 'boolean') subDefaults = { user: subDefaults, admin: true }; let subHidden = option.hidden ?? false; // if top-levl hidden then we are too if (hidden) subHidden = true; // add subcommand permissions const finalName = [permissionName, ...subPermPath].join('.'); collector.push({ permission: finalName, defaults: subDefaults, hidden: subHidden, path: subPath }); if (Array.isArray(option.options)) walkOptions(option.options, subPath, subPermPath); } }; walkOptions(definition.options); return collector; } getTypePreview(option) { // Prepend type information to description let typeBuild = '['; if (typeof option.type === 'number') typeBuild += OptionType_1.OptionType[option.type]; else typeBuild += option.type; // handle array if (option.array) typeBuild += ', ...'; typeBuild += ']'; return typeBuild; } async fufillInteractionOptions(ctx, definition, data) { const primary = this.getPrimaryName(definition.name); return this.processInteractionOptions(ctx, definition.options, data.options, [primary]); } async processInteractionOptions(ctx, options, optionData, path = []) { let collector = {}; if (!options) options = []; if (!optionData) optionData = []; for (const option of options) { const primary = this.getPrimaryName(option.name); // Handle SubCommand & SubCommandGroup if (this.isAnySubCommand(option)) { const subOptionData = optionData.find(d => primary === d.name.toLocaleLowerCase()); if (!subOptionData) continue; // check permission const permissionName = option.permissionName ?? primary; const subPath = [...path, permissionName]; if (!(await this.interface.checkPermission(ctx, subPath, option.permissionDefaults))) throw CommandManager_1.NON_ERROR_HALT; // process suppressors const suppressed = await this.executeSuppressors(ctx, option); if (suppressed) throw new Error(`Suppressor \`${suppressed.name}\` halted execution: ${suppressed.message}`); collector = { ...collector, [primary]: await this.processInteractionOptions(ctx, option.options, subOptionData.options, subPath) }; break; } const data = optionData.find(d => d.name.toLocaleLowerCase() === primary); const value = data?.value.toString(); collector = { ...collector, [primary]: await this.resolveOption(ctx, option, value) }; } return collector; } /** * Matches up input text with options * @param options Array of CommandOption * @param input User input */ async fufillTextOptions(ctx, definition, input) { const tokens = new Tokenizer_1.Tokenizer(input).tokenize(); // TODO: Build allowedOptions const output = new Parser_1.Parser(tokens).parse(); const primary = this.getPrimaryName(definition.name); return this.processTextOptions(ctx, definition.options, output, 0, [primary]); } async processTextOptions(ctx, options, output, index = 0, path = []) { let collector = {}; if (!options) options = []; let promptSubs = []; for (const option of options) { const names = await this.getItemTranslations(option.name, true); const primary = names[0][0]; if (this.isAnySubCommand(option)) { // track this suboption for prompt promptSubs.push(option); // phrase is a single element const phrase = output.phrases[index]; // validate arg matches subcommand or group name if (!phrase || names.every(n => n[0] !== phrase.value.toLocaleLowerCase())) continue; index++; // check permission const permissionName = option.permissionName ?? primary; const subPath = [...path, permissionName]; if (!(await this.interface.checkPermission(ctx, subPath, option.permissionDefaults))) throw CommandManager_1.NON_ERROR_HALT; // process suppressors const suppressed = await this.executeSuppressors(ctx, option); if (suppressed) throw new Error(`Suppressor \`${suppressed.name}\` halted execution: ${suppressed.message}`); // process nested option collector = { ...collector, [primary]: await this.processTextOptions(ctx, option.options, output, index, subPath) }; promptSubs = []; // collected successfully break; } let value; const textOption = output.options.find(o => o.key.toLocaleLowerCase() === primary.toLocaleLowerCase()); if (textOption) { value = textOption.value; } else { // phrase let phrases = []; if (option.rest) phrases = output.phrases.slice(index, option.limit || Infinity); else if (output.phrases.length > index) phrases = [output.phrases[index]]; // consume index += phrases.length; value = phrases.map(p => p.value).join(' '); } collector = { ...collector, [primary]: await this.resolveOption(ctx, option, value) }; } // Prompt for subcommand option let useSub = null; if (promptSubs.length > 1) { const choices = []; for (const sub of promptSubs) { if (sub.hidden ?? false) continue; // don't show hidden subcommands/groups const primary = this.getPrimaryName(sub.name); choices.push({ value: primary, label: primary, description: sub.description, match: [primary] }); } const content = await ctx.formatTranslation('BENTOCORD_PROMPT_SUBCOMMAND', {}, 'Please select a subcommand:'); const choice = await ctx.choice(choices, content); useSub = choice; } else if (promptSubs.length === 1) { const sub = promptSubs[0]; const primary = this.getPrimaryName(sub.name); useSub = primary; } if (useSub) { for (const option of options) { const primary = this.getPrimaryName(option.name); if (primary !== useSub.toLocaleLowerCase()) continue; if (!this.isAnySubCommand(option)) continue; // check permission const permissionName = option.permissionName ?? primary; const subPath = [...path, permissionName]; if (!(await this.interface.checkPermission(ctx, subPath, option.permissionDefaults))) throw CommandManager_1.NON_ERROR_HALT; // process suppressors const suppressed = await this.executeSuppressors(ctx, option); if (suppressed) throw new Error(`Suppressor \`${suppressed.name}\` halted execution: ${suppressed.message}`); if (option) collector = { ...collector, [useSub]: await this.processTextOptions(ctx, option.options, output, index, subPath) }; } } return collector; } async resolveOption(ctx, option, raw) { if (raw === undefined) raw = ''; // array support let inputs = option.array ? raw.split(/,\s?/gi) : [raw]; inputs = inputs.filter(i => !!i); const primary = this.getPrimaryName(option.name); // Auto prompt missing data on required option if (inputs.length < 1 && (typeof option.required !== 'boolean' || option.required) && !('choices' in option)) { const type = this.getTypePreview(option); const content = await ctx.formatTranslation('BENTOCORD_PROMPT_OPTION', { option: primary, type }, 'Please provide an input for option `{option}` of type `{type}`'); const input = await ctx.prompt(content, async (s) => [true, s]); inputs = option.array ? input.split(/,\s?/gi) : [input]; inputs = inputs.filter(i => !!i); } const value = []; for (const input of inputs) { const result = await this.executeResolver(ctx, option, input); // Reduce Choose Prompt if (Array.isArray(result)) { if (result.length === 0) { continue; } else if (result.length === 1) { // single element array value.push(result[0]); continue; } const resolver = this.resolvers.get(option.type); const choices = []; for (const item of result) { const match = []; let label = item.toString(); if (resolver && typeof resolver.reduce === 'function') { const reduce = await resolver.reduce(ctx, option, item); label = reduce.display; if (reduce.extra) match.push(reduce.extra); } choices.push({ value: item, label, match }); } const choice = await ctx.choice(choices); value.push(choice); continue; } if (result != null) value.push(result); } let out = value; // unwrap array if need be if (!option.array && Array.isArray(out)) out = out[0]; // default value if ((out == null || (Array.isArray(out) && out.length === 0)) && option.default !== undefined) out = option.default; // handle choices if ('choices' in option) { let choices = option.choices; if (typeof choices === 'function') choices = await choices(); if (choices.length === 0) throw new Error('No choices available for this option.'); const findChoice = choices.find(c => out && (c.value === out.toString() || c.value === parseInt(out.toString(), 10))); if (!findChoice) { const content = await ctx.formatTranslation('BENTOCORD_PROMPT_CHOICE_OPTION', { option: primary }, 'Please select one of the following choices for option `{option}`'); const finalChoices = choices.map(c => ({ ...c, match: [c.value.toString()], })); out = await ctx.choice(finalChoices, content); } } // required value if ((out == null || (Array.isArray(out) && out.length === 0)) && (typeof option.required !== 'boolean' || option.required)) throw new Error(`Failed to resolve required option "${primary}"`); // TODO: Transform function return out; } async handleInteractionCreate(interaction) { // Only handle APPLICATION_COMMAND interactions if (interaction.type !== eris_1.Constants.InteractionTypes.APPLICATION_COMMAND) return; interaction = interaction; const data = interaction.data; const command = this.findCommand(data.name); if (!command) return; // command not found const definition = command.definition; if (typeof definition.registerSlash === 'boolean' && !definition.registerSlash) return; // slash disabled const ctx = new CommandContext_1.InteractionCommandContext(this.api, interaction, command); ctx.alias = data.name; try { await ctx.prepare(); // Deny interactions from bots; Safety precaution if (ctx.user.bot) return; // pre-flight checks, perms, suppressors, etc if (!(await this.prepareCommand(command, ctx))) return; // fufill options const options = await this.fufillInteractionOptions(ctx, definition, data); return this.executeCommand(command, ctx, options); } catch (e) { // halt requested (this is lazy, I'll fix it later, probably) if (e === CommandManager_1.NON_ERROR_HALT) return; log.error(`Command "${definition.name[0]}" option error:\n${util.inspect(e)}`); if (e instanceof Error) { return ctx.createTranslatedResponse('BENTOCORD_COMMANDMANAGER_ERROR', { error: e.message }, 'An error occurred while executing this command: `{error}`'); } } } async handleMessageCreate(message) { // Deny messages without content, channel, or author if (!message.content || !message.channel || !message.author) return; // Deny messages from bots if (message.author.bot) return; // raw message const raw = message.content; let prefixes = [this.defaultPrefix]; // guild prefix override if (message.guildID) { const guildPrefix = await this.interface.getPrefix(message.guildID); if (guildPrefix) prefixes = [guildPrefix]; } // extra prefixes support const extraPrefixes = await this.interface.getExtraPrefixes(); if (Array.isArray(extraPrefixes) && extraPrefixes.length > 0) prefixes = [...prefixes, ...extraPrefixes]; // escape prefixes prefixes = prefixes.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); // first capture group prefix let build = `^(?<prefix>${prefixes.join('|')}`; // if we have selfId allow mentions if (this.discord.client?.user?.id) build = `${build}|<(?:@|@!)${this.discord.client.user.id}>`; // find command and arguments build = `${build})\\s?(?<name>[\\w]+)\\s?(?<args>.+)?$`; // example of finished regex: `/^(?<prefix>=|<@!?185476724627210241>)\s?(?<name>[\w]+)\s?(?<args>.+)?$/si` const matches = new RegExp(build, 'siu').exec(raw); // message is not a command if (!matches) return; const prefix = matches.groups.prefix; let name = matches.groups.name; let args = matches.groups.args; let command = this.findCommand(name); if (!command) { if (message.guildID && !message.author.bot) { const [newName, newArguments] = await this.interface.resolveAlias(name, args, message); if (newName) { command = this.findCommand(newName); if (command) { name = newName; args = newArguments; // continue with normal command execution } else { return; // command not found } } else { return; // command not found } } else { return; // not in guild, or author is a bot } } const definition = command.definition; // Execution via prefix was disabled if (definition.disablePrefix) return; // CommandContext const ctx = new CommandContext_1.MessageCommandContext(this.api, message, command); ctx.prefix = prefix; ctx.alias = name; try { await ctx.prepare(); // pre-flight checks, perms, suppressors, etc if (!(await this.prepareCommand(command, ctx))) return; // fufill options const options = await this.fufillTextOptions(ctx, definition, args); return this.executeCommand(command, ctx, options); } catch (e) { // halt requested (this is lazy, I'll fix it later, probably) if (e === CommandManager_1.NON_ERROR_HALT) return; log.error(`Command "${definition.name[0]}" error:\n${util.inspect(e)}`); if (e instanceof Error) { return ctx.createTranslatedResponse('BENTOCORD_COMMANDMANAGER_ERROR', { error: e.message }, 'An error occurred while executing this command: `{error}`'); } } } } __decorate([ (0, bento_1.Variable)({ name: BentocordVariable_1.BentocordVariable.BENTOCORD_COMMAND_PREFIX, default: 'bentocord' }), __metadata("design:type", String) ], CommandManager.prototype, "defaultPrefix", void 0); __decorate([ (0, bento_1.Variable)({ name: BentocordVariable_1.BentocordVariable.BENTOCORD_IGNORE_MODE, default: 'false' }), __metadata("design:type", String) ], CommandManager.prototype, "ignoreMode", void 0); __decorate([ (0, bento_1.Inject)(), __metadata("design:type", BentocordInterface_1.BentocordInterface) ], CommandManager.prototype, "interface", void 0); __decorate([ (0, bento_1.Inject)(), __metadata("design:type", Discord_1.Discord) ], CommandManager.prototype, "discord", void 0); __decorate([ (0, bento_1.Subscribe)(Discord_1.Discord, DiscordEvent_1.DiscordEvent.INTERACTION_CREATE), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], CommandManager.prototype, "handleInteractionCreate", null); __decorate([ (0, bento_1.Subscribe)(Discord_1.Discord, DiscordEvent_1.DiscordEvent.MESSAGE_CREATE), __metadata("design:type", Function), __metadata("design:paramtypes", [eris_1.Message]), __metadata("design:returntype", Promise) ], CommandManager.prototype, "handleMessageCreate", null); exports.CommandManager = CommandManager; //# sourceMappingURL=CommandManager.js.map