UNPKG

seyfert

Version:

The most advanced framework for discord bots

748 lines (747 loc) 35.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HandleCommand = void 0; const _1 = require("."); const transformers_1 = require("../client/transformers"); const constants_1 = require("../common/it/constants"); const components_1 = require("../components"); const structures_1 = require("../structures"); const types_1 = require("../types"); class HandleCommand { client; constructor(client) { this.client = client; } async autocomplete(interaction, optionsResolver, command) { // idc, is a YOU problem if (!command?.autocomplete) { return this.client.logger.warn(`${optionsResolver.fullCommandName} ${command?.name} command does not have 'autocomplete' callback`); } try { try { try { await command.autocomplete(interaction); } catch (error) { if (!command.onAutocompleteError) return this.client.logger.error(`${optionsResolver.fullCommandName} ${command.name} just threw an error, ${error ? (typeof error === 'object' && 'message' in error ? error.message : error) : 'Unknown'}`); await command.onAutocompleteError(interaction, error); } } catch (error) { await optionsResolver.getCommand()?.onInternalError?.(this.client, optionsResolver.getCommand(), error); } } catch (error) { // pass } } async contextMenu(command, interaction, context) { if (context.guildId && command.botPermissions) { const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions); if (permissions) return command.onBotPermissionsFail?.(context, permissions); } const resultGlobal = await this.runGlobalMiddlewares(command, context); if (typeof resultGlobal === 'boolean') return; const resultMiddle = await this.runMiddlewares(command, context); if (typeof resultMiddle === 'boolean') return; try { try { await command.run(context); await command.onAfterRun?.(context, undefined); } catch (error) { await command.onRunError?.(context, error); await command.onAfterRun?.(context, error); } } catch (error) { try { await command.onInternalError?.(this.client, command, error); } catch { // pass } } } contextMenuMessage(command, interaction, context) { return this.contextMenu(command, interaction, context); } contextMenuUser(command, interaction, context) { return this.contextMenu(command, interaction, context); } async entryPoint(command, interaction, context) { if (context.guildId && command.botPermissions) { const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions); if (permissions) return command.onBotPermissionsFail(context, permissions); } const resultGlobal = await this.runGlobalMiddlewares(command, context); if (typeof resultGlobal === 'boolean') return; const resultMiddle = await this.runMiddlewares(command, context); if (typeof resultMiddle === 'boolean') return; try { try { await command.run(context); await command.onAfterRun?.(context, undefined); } catch (error) { await command.onRunError(context, error); await command.onAfterRun?.(context, error); } } catch (error) { try { await command.onInternalError(this.client, command, error); } catch { // pass } } } async chatInput(command, interaction, resolver, context) { if (context.guildId) { if (command.botPermissions) { const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions); if (permissions) return command.onBotPermissionsFail?.(context, permissions); } if (command.defaultMemberPermissions) { const permissions = this.checkPermissions(interaction.member.permissions, command.defaultMemberPermissions); if (permissions) return command.onPermissionsFail?.(context, permissions); } } if (!(await this.runOptions(command, context, resolver))) return; const resultGlobal = await this.runGlobalMiddlewares(command, context); if (typeof resultGlobal === 'boolean') return; const resultMiddle = await this.runMiddlewares(command, context); if (typeof resultMiddle === 'boolean') return; try { try { await command.run(context); await command.onAfterRun?.(context, undefined); } catch (error) { await command.onRunError?.(context, error); await command.onAfterRun?.(context, error); } } catch (error) { try { await command.onInternalError?.(this.client, command, error); } catch { // pass } } } async modal(interaction) { const context = new components_1.ModalContext(this.client, interaction); const extended = this.client.options?.context?.(interaction) ?? {}; Object.assign(context, extended); await this.client.components.executeModal(context); } async messageComponent(interaction) { //@ts-expect-error const context = new components_1.ComponentContext(this.client, interaction); const extended = this.client.options?.context?.(interaction) ?? {}; Object.assign(context, extended); await this.client.components.executeComponent(context); } async interaction(body, shardId, __reply) { this.client.debugger?.debug(`[${types_1.InteractionType[body.type] ?? body.type}] Interaction received.`); switch (body.type) { case types_1.InteractionType.ApplicationCommandAutocomplete: { const optionsResolver = this.makeResolver(this.client, body.data.options ?? [], this.getCommand(body.data), body.guild_id, body.data.resolved); const interaction = new structures_1.AutocompleteInteraction(this.client, body, optionsResolver, __reply); const command = optionsResolver.getAutocomplete(); await this.autocomplete(interaction, optionsResolver, command); } break; case types_1.InteractionType.ApplicationCommand: { switch (body.data.type) { case types_1.ApplicationCommandType.Message: { const data = this.makeMenuCommand(body, shardId, __reply); if (!data) return; await this.contextMenuMessage(data.command, data.interaction, data.context); break; } case types_1.ApplicationCommandType.User: { const data = this.makeMenuCommand(body, shardId, __reply); if (!data) return; await this.contextMenuUser(data.command, data.interaction, data.context); break; } case types_1.ApplicationCommandType.PrimaryEntryPoint: { const command = this.client.commands.entryPoint; if (!command?.run) return; const interaction = structures_1.BaseInteraction.from(this.client, body, __reply); const context = new _1.EntryPointContext(this.client, interaction, shardId, command); const extendContext = this.client.options?.context?.(interaction) ?? {}; Object.assign(context, extendContext); await this.entryPoint(command, interaction, context); break; } case types_1.ApplicationCommandType.ChatInput: { const parentCommand = this.getCommand(body.data); const optionsResolver = this.makeResolver(this.client, body.data.options ?? [], parentCommand, body.guild_id, body.data.resolved); const command = optionsResolver.getCommand(); if (!command?.run) return this.client.logger.warn(`${optionsResolver.fullCommandName} command does not have 'run' callback`); const interaction = structures_1.BaseInteraction.from(this.client, body, __reply); const context = new _1.CommandContext(this.client, interaction, optionsResolver, shardId, command); const extendContext = this.client.options?.context?.(interaction) ?? {}; Object.assign(context, extendContext); await this.chatInput(command, interaction, optionsResolver, context); break; } } break; } case types_1.InteractionType.ModalSubmit: { const interaction = structures_1.BaseInteraction.from(this.client, body, __reply); if (this.client.components.hasModal(interaction)) { await this.client.components.onModalSubmit(interaction); } else await this.modal(interaction); } break; case types_1.InteractionType.MessageComponent: { const interaction = structures_1.BaseInteraction.from(this.client, body, __reply); if (this.client.components.hasComponent(body.message.id, interaction.customId)) { await this.client.components.onComponent(body.message.id, interaction); } else await this.messageComponent(interaction); } break; } } async message(rawMessage, shardId) { const self = this.client; if (!self.options.commands?.prefix) return; const message = transformers_1.Transformers.Message(this.client, rawMessage); const prefixes = (await self.options.commands.prefix(message)).sort((a, b) => b.length - a.length); const prefix = prefixes.find(x => rawMessage.content.startsWith(x)); if (!(prefix !== undefined && rawMessage.content.startsWith(prefix))) return; const content = rawMessage.content.slice(prefix.length).trimStart(); const { fullCommandName, command, parent, argsContent } = this.resolveCommandFromContent(content, prefix, rawMessage); if (!command || argsContent === undefined) return; if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`); if (!(command.contexts.includes(types_1.InteractionContextType.BotDM) || rawMessage.guild_id)) return; if (!command.contexts.includes(types_1.InteractionContextType.Guild) && rawMessage.guild_id) return; if (command.guildId && !command.guildId?.includes(rawMessage.guild_id)) return; const resolved = { channels: {}, roles: {}, users: {}, members: {}, attachments: {}, }; const args = this.argsParser(argsContent, command, message); const { options, errors } = await this.argsOptionsParser(command, rawMessage, args, resolved); const optionsResolver = this.makeResolver(self, options, parent, rawMessage.guild_id, resolved); const context = new _1.CommandContext(self, message, optionsResolver, shardId, command); //@ts-expect-error const extendContext = self.options?.context?.(message) ?? {}; Object.assign(context, extendContext); try { if (errors.length) { return command.onOptionsError?.(context, Object.fromEntries(errors.map(x => { return [ x.name, { failed: true, value: x.error, parseError: x.fullError, }, ]; }))); } if (rawMessage.guild_id) { if (command.defaultMemberPermissions) { const memberPermissions = await self.members.permissions(rawMessage.guild_id, rawMessage.author.id); const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions); const guild = await this.client.guilds.raw(rawMessage.guild_id); if (permissions && guild.owner_id !== rawMessage.author.id) { return command.onPermissionsFail?.(context, memberPermissions.keys(permissions)); } } if (command.botPermissions) { const appPermissions = await self.members.permissions(rawMessage.guild_id, self.botId); const permissions = this.checkPermissions(appPermissions, command.botPermissions); if (permissions) { return command.onBotPermissionsFail?.(context, permissions); } } } if (!(await this.runOptions(command, context, optionsResolver))) return; const resultGlobal = await this.runGlobalMiddlewares(command, context); if (typeof resultGlobal === 'boolean') return; const resultMiddle = await this.runMiddlewares(command, context); if (typeof resultMiddle === 'boolean') return; try { await command.run(context); await command.onAfterRun?.(context, undefined); } catch (error) { await command.onRunError?.(context, error); await command.onAfterRun?.(context, error); } } catch (error) { try { await command.onInternalError?.(this.client, command, error); } catch { // http 418 } } } argsParser(content, _command, _message) { const args = {}; for (const i of content.match(/-(.*?)(?=\s-|$)/gs) ?? []) { args[i.slice(1).split(' ')[0]] = i.split(' ').slice(1).join(' '); } return args; } resolveCommandFromContent(content, _prefix, _message) { const result = this.getCommandFromContent(content .split(' ') .filter(x => x) .slice(0, 3)); if (!result.command) return result; let newContent = content; for (const i of result.fullCommandName.split(' ')) { newContent = newContent.slice(newContent.indexOf(i) + i.length); } return { ...result, argsContent: newContent.slice(1), }; } getCommandFromContent(commandRaw) { const rawParentName = commandRaw[0]; const rawGroupName = commandRaw.length === 3 ? commandRaw[1] : undefined; const rawSubcommandName = rawGroupName ? commandRaw[2] : commandRaw[1]; const parent = this.getParentMessageCommand(rawParentName); const fullCommandName = `${rawParentName}${rawGroupName ? ` ${rawGroupName} ${rawSubcommandName}` : `${rawSubcommandName ? ` ${rawSubcommandName}` : ''}`}`; if (!(parent instanceof _1.Command)) return { fullCommandName }; if (rawGroupName && !parent.groups?.[rawGroupName] && !parent.groupsAliases?.[rawGroupName]) return this.getCommandFromContent([rawParentName, rawGroupName]); if (rawSubcommandName && !parent.options?.some(x => x instanceof _1.SubCommand && (x.name === rawSubcommandName || x.aliases?.includes(rawSubcommandName)))) return this.getCommandFromContent([rawParentName]); const groupName = rawGroupName ? parent.groupsAliases?.[rawGroupName] || rawGroupName : undefined; const command = groupName || rawSubcommandName ? parent.options?.find(opt => { if (opt instanceof _1.SubCommand) { if (groupName) { if (opt.group !== groupName) return false; } if (opt.group && !groupName) return false; return rawSubcommandName === opt.name || opt.aliases?.includes(rawSubcommandName); } return false; }) : parent; return { command, fullCommandName, parent, }; } makeResolver(...args) { return transformers_1.Transformers.OptionResolver(...args); } getParentMessageCommand(rawParentName) { return this.client.commands.values.find(x => (!('ignore' in x) || x.ignore !== _1.IgnoreCommand.Message) && (x.name === rawParentName || ('aliases' in x ? x.aliases?.includes(rawParentName) : false))); } getCommand(data) { return this.client.commands.values.find(command => { if (data.guild_id) { return command.guildId?.includes(data.guild_id) && command.name === data.name; } return command.name === data.name; }); } checkPermissions(app, bot) { if (app.has('Administrator')) return; const permissions = app.missings(...app.values([bot])); if (permissions.length) { return app.keys(permissions); } return; } fetchChannel(_option, query) { const id = query.match(/[0-9]{17,19}/g)?.[0]; if (id) return this.client.channels.raw(id); return null; } fetchUser(_option, query) { const id = query.match(/[0-9]{17,19}/g)?.[0]; if (id) return this.client.users.raw(id); return null; } fetchMember(_option, query, guildId) { const id = query.match(/[0-9]{17,19}/g)?.[0]; if (id) return this.client.members.raw(guildId, id); return null; } fetchRole(_option, query, guildId) { const id = query.match(/[0-9]{17,19}/g)?.[0]; if (id && guildId) return this.client.roles.raw(guildId, id); return null; } async runGlobalMiddlewares(command, context) { try { const resultRunGlobalMiddlewares = await _1.BaseCommand.__runMiddlewares(context, (this.client.options?.globalMiddlewares ?? []), true); if (resultRunGlobalMiddlewares.pass) { return false; } if ('error' in resultRunGlobalMiddlewares) { await command.onMiddlewaresError?.(context, resultRunGlobalMiddlewares.error ?? 'Unknown error'); return false; } return resultRunGlobalMiddlewares; } catch (e) { try { await command.onInternalError?.(this.client, command, e); } catch { // http 418 } } return false; } async runMiddlewares(command, context) { try { const resultRunMiddlewares = await _1.BaseCommand.__runMiddlewares(context, command.middlewares, false); if (resultRunMiddlewares.pass) { return false; } if ('error' in resultRunMiddlewares) { await command.onMiddlewaresError?.(context, resultRunMiddlewares.error ?? 'Unknown error'); return false; } return resultRunMiddlewares; } catch (e) { try { await command.onInternalError?.(this.client, command, e); } catch { // http 418 } } return false; } makeMenuCommand(body, shardId, __reply) { const command = this.getCommand(body.data); const interaction = structures_1.BaseInteraction.from(this.client, body, __reply); // idc, is a YOU problem if (!command?.run) return this.client.logger.warn(`${command?.name ?? 'Unknown'} command does not have 'run' callback`); const context = new _1.MenuCommandContext(this.client, interaction, shardId, command); const extendContext = this.client.options?.context?.(interaction) ?? {}; Object.assign(context, extendContext); return { command, interaction, context }; } async runOptions(command, context, resolver) { const [erroredOptions, result] = await command.__runOptions(context, resolver); if (erroredOptions) { try { await command.onOptionsError?.(context, result); } catch (e) { try { await command.onInternalError?.(this.client, command, e); } catch { // http 418 } } return false; } return true; } async argsOptionsParser(command, message, args, resolved) { const options = []; const errors = []; let indexAttachment = -1; for (const i of (command.options ?? [])) { try { if (!args[i.name] && i.type !== types_1.ApplicationCommandOptionType.Attachment) continue; let value; switch (i.type) { case types_1.ApplicationCommandOptionType.Attachment: if (message.attachments[++indexAttachment]) { value = message.attachments[indexAttachment].id; resolved.attachments[value] = message.attachments[indexAttachment]; } break; case types_1.ApplicationCommandOptionType.Boolean: value = ['yes', 'y', 'true', 'treu'].includes(args[i.name].toLowerCase()); break; case types_1.ApplicationCommandOptionType.Channel: { const rawQuery = message.content.match(/(?<=<#)[0-9]{17,19}(?=>)/g)?.find(x => args[i.name]?.includes(x)) || args[i.name]; if (!rawQuery) continue; const channel = (await this.client.cache.channels?.raw(rawQuery)) ?? (await this.fetchChannel(i, rawQuery)); if (!channel) break; if ('channel_types' in i) { if (!i.channel_types.includes(channel.type)) { errors.push({ name: i.name, error: `The entered channel type is not one of ${i .channel_types.map(t => types_1.ChannelType[t]) .join(', ')}`, fullError: ['CHANNEL_TYPES', i.channel_types], }); break; } } value = channel.id; //discord funny memoentnt!!!!!!!! resolved.channels[channel.id] = channel; } break; case types_1.ApplicationCommandOptionType.Mentionable: { const matches = args[i.name]?.match(/<@[0-9]{17,19}(?=>)|<@&[0-9]{17,19}(?=>)/g) ?? []; for (const match of matches) { if (match.includes('&')) { const rawId = match.slice(3); if (rawId) { const role = (await this.client.cache.roles?.raw(rawId)) ?? (await this.fetchRole(i, rawId, message.guild_id)); if (role) { value = rawId; resolved.roles[rawId] = role; break; } } } else { const rawId = match.slice(2); const raw = message.mentions.find(x => rawId === x.id); if (raw) { const { member, ...user } = raw; value = raw.id; resolved.users[raw.id] = user; if (member) resolved.members[raw.id] = member; break; } } } } break; case types_1.ApplicationCommandOptionType.Role: { const rawQuery = message.mention_roles.find(x => args[i.name]?.includes(x)) || args[i.name]; if (!rawQuery) continue; const role = (await this.client.cache.roles?.raw(rawQuery)) ?? (await this.fetchRole(i, rawQuery, message.guild_id)); if (role) { value = role.id; resolved.roles[role.id] = role; } } break; case types_1.ApplicationCommandOptionType.User: { const rawQuery = message.mentions.find(x => args[i.name]?.includes(x.id))?.id || args[i.name]; if (!rawQuery) continue; const raw = message.mentions.find(x => args[i.name]?.includes(x.id)) ?? (await this.client.cache.users?.raw(rawQuery)) ?? (await this.fetchUser(i, rawQuery)); if (raw) { value = raw.id; resolved.users[raw.id] = raw; if (message.guild_id) { const member = message.mentions.find(x => args[i.name]?.includes(x.id))?.member ?? (await this.client.cache.members?.raw(value, message.guild_id)) ?? (await this.fetchMember(i, value, message.guild_id)); if (member) resolved.members[value] = member; } } } break; case types_1.ApplicationCommandOptionType.String: { const option = i; if (option.choices?.length) { const choice = option.choices.find(x => x.name === args[i.name]); if (!choice) { errors.push({ name: i.name, error: `The entered choice is invalid. Please choose one of the following options: ${option.choices .map(x => x.name) .join(', ')}`, fullError: ['STRING_INVALID_CHOICE', option.choices], }); break; } value = choice.value; break; } if (option.min_length !== undefined) { if (args[i.name].length < option.min_length) { errors.push({ name: i.name, error: `The entered string has less than ${option.min_length} characters. The minimum required is ${option.min_length} characters`, fullError: ['STRING_MIN_LENGTH', option.min_length], }); break; } } if (option.max_length !== undefined) { if (args[i.name].length > option.max_length) { errors.push({ name: i.name, error: `The entered string has more than ${option.max_length} characters. The maximum required is ${option.max_length} characters`, fullError: ['STRING_MAX_LENGTH', option.max_length], }); break; } } value = args[i.name]; } break; case types_1.ApplicationCommandOptionType.Number: case types_1.ApplicationCommandOptionType.Integer: { const option = i; if (option.choices?.length) { const choice = option.choices.find(x => x.name === args[i.name]); if (!choice) { errors.push({ name: i.name, error: `The entered choice is invalid. Please choose one of the following options: ${option.choices .map(x => x.name) .join(', ')}`, fullError: ['NUMBER_INVALID_CHOICE', option.choices], }); break; } value = choice.value; break; } value = i.type === types_1.ApplicationCommandOptionType.Integer ? Math.trunc(Number(args[i.name])) : Number(args[i.name]); if (Number.isNaN(value)) { value = undefined; errors.push({ name: i.name, error: 'The entered choice is an invalid number', fullError: ['NUMBER_NAN', args[i.name]], }); break; } if (value <= -constants_1.INTEGER_OPTION_VALUE_LIMIT || value >= constants_1.INTEGER_OPTION_VALUE_LIMIT) { value = undefined; errors.push({ name: i.name, error: 'The entered number must be between -2^53 and 2^53', fullError: ['NUMBER_OUT_OF_BOUNDS', constants_1.INTEGER_OPTION_VALUE_LIMIT], }); break; } if (option.min_value !== undefined) { if (value < option.min_value) { value = undefined; errors.push({ name: i.name, error: `The entered number is less than ${option.min_value}. The minimum allowed is ${option.min_value}`, fullError: ['NUMBER_MIN_VALUE', option.min_value], }); break; } } if (option.max_value !== undefined) { if (value > option.max_value) { value = undefined; errors.push({ name: i.name, error: `The entered number is greater than ${option.max_value}. The maximum allowed is ${option.max_value}`, fullError: ['NUMBER_MAX_VALUE', option.max_value], }); break; } break; } } break; } if (value !== undefined) { options.push({ name: i.name, type: i.type, value, }); } else if (i.required) if (!errors.some(x => x.name === i.name)) errors.push({ error: 'Option is required but returned undefined', name: i.name, fullError: ['OPTION_REQUIRED'], }); } catch (e) { errors.push({ error: e && typeof e === 'object' && 'message' in e ? e.message : `${e}`, name: i.name, fullError: ['UNKNOWN', e], }); } } return { errors, options }; } } exports.HandleCommand = HandleCommand;