UNPKG

@lilybird/handlers

Version:
223 lines (222 loc) 10.5 kB
export class ApplicationCommandStore { #globalApplicationCommands = new Map(); #guildApplicationCommands = new Map(); #emit; #assumeTransformed; #customKeys; constructor(handlerListener, options = { transformed: false }) { this.#emit = handlerListener; this.#assumeTransformed = options.transformed; this.#customKeys = options.customKeys; } storeCommand(command) { const { meta, options, handle, autocomplete, ...actualCommand } = command; if (meta?.guild_command === true && !Array.isArray(meta.ids)) throw new Error("Invalid guild command. Lacking 'ids'"); const commands = meta?.guild_command === true ? this.#guildApplicationCommands : this.#globalApplicationCommands; const isContinuingChain = (meta?.guild_command === true ? this.#guildApplicationCommands.size : this.#globalApplicationCommands.size) > 0; if ((!Array.isArray(options) || options.length === 0) || (typeof options !== "undefined" && !this.#isCommandWrapper(options))) { if (typeof handle === "undefined") return this.#emit?.(5, actualCommand.name); actualCommand.options = options; const optsBody = this.#parseOptions(options); const cmd = this.#makeCommandBase(actualCommand, { base_executor: handle, auto_executor: autocomplete }, isContinuingChain, optsBody); commands.set(actualCommand.name, cmd); return; } if (typeof handle !== "undefined") this.#emit?.(6, actualCommand.name); const optsBody = this.#parseOptions(options, true); if (typeof optsBody === "undefined") throw new Error("Something unexpected happened!"); const cmd = this.#compileCommand(actualCommand, options, isContinuingChain, optsBody); commands.set(actualCommand.name, cmd); return; } #parseOptions(options, appendSubCommandLogic = false) { if (this.#assumeTransformed) return undefined; if (typeof options === "undefined") return undefined; const stack = ["const _obj = {"]; for (let i = 0, { length } = options; i < length; i++) { const option = options[i]; stack.push(`${option.name}: undefined`, ","); } stack.pop(); stack.push("};"); if (appendSubCommandLogic) { stack.push("const _opt = interaction.data.options[0];", "if ( _opt.type === 2) {", "sub_command_group = _opt.name;", "for (let i = 0, { length } = _opt.options; i < length; i++) {", "const _g_opt = _opt.options[i];", "if (_g_opt.type === 1) {sub_command = _g_opt.name; parseOpts(_g_opt.options, _obj);}", "}", "}", "else if (_opt.type === 1) {sub_command = _opt.name; parseOpts(_opt.options, _obj)}"); } else stack.push("parseOpts(interaction.data.options, _obj);"); return stack.join(""); } #makeCommandBase(command, handler, useElse, optionsBody = undefined, matchTo = "interaction_name", name = command.name) { const hasOptions = typeof optionsBody !== "undefined"; const names = [`handle_${name.replace("-", "_")}`]; const handlers = [handler.base_executor]; if (typeof handler.auto_executor !== "undefined") { names.push(`auto_${name.replace("-", "_")}`); handlers.push(handler.auto_executor); } let strArgs = this.#assumeTransformed ? "interaction" : "client, interaction"; if (hasOptions && !this.#assumeTransformed) strArgs += ", _obj"; return { body: { command: `${useElse ? "else " : ""}if (${matchTo} === "${command.name}") { ${hasOptions && !this.#assumeTransformed ? optionsBody : ""} return handle_${name.replace("-", "_")}(${strArgs}); }`, autocomplete: typeof handler.auto_executor === "undefined" ? null : `${useElse ? "else " : ""}if (${matchTo} === "${command.name}") { ${hasOptions && !this.#assumeTransformed ? optionsBody : ""} return auto_${name.replace("-", "_")}(${strArgs}); }` }, function: { names, handlers }, json: command }; } #compileCommand(command, options, useElse, optionsBody, matchTo = "interaction_name", name = command.name) { const fns = new Map(); const cmdArr = [`${useElse ? "else " : ""}if (${matchTo} === "${command.name}") {`]; const autoArr = [`${useElse ? "else " : ""}if (${matchTo} === "${command.name}") {`]; const temp = matchTo === "sub_command_group" ? [] : this.#assumeTransformed ? [ `const sub_command = interaction.${this.#customKeys?.sub_command};`, `const sub_command_group = interaction.${this.#customKeys?.sub_command_group}` ] : [ "let sub_command = undefined;", "let sub_command_group = undefined;", optionsBody ]; cmdArr.push(temp.join("")); autoArr.push(temp.join("")); for (let i = 0, { length } = options; i < length; i++) { const subCommand = options[i]; if (subCommand.type === 2) { if (!("options" in subCommand)) throw new Error("SubCommandGroup requires options to exist"); const { options: subOpts, ...realCommand } = subCommand; const realOptions = this.#compileCommand(realCommand, subOpts, i > 0, `${name}_${subCommand.name}`, "sub_command_group"); cmdArr.push(realOptions.body.command); if (realOptions.body.autocomplete !== null) autoArr.push(realOptions.body.autocomplete); for (let j = 0, len = realOptions.function.names.length; j < len; j++) { const n = realOptions.function.names[j]; const h = realOptions.function.handlers[j]; fns.set(n, h); } if (!Array.isArray(command.options)) command.options = []; command.options.push(realCommand); } else if (subCommand.type === 1) { if (!("handle" in subCommand)) throw new Error("SubCommand requires 'handle' to exist"); const { handle, autocomplete, ...realCommand } = subCommand; const cmd = this.#makeCommandBase(realCommand, { base_executor: handle, auto_executor: autocomplete }, i > 0, "", "sub_command", `${name}_${subCommand.name}`); cmdArr.push(cmd.body.command); if (cmd.body.autocomplete !== null) autoArr.push(cmd.body.autocomplete); for (let j = 0, len = cmd.function.names.length; j < len; j++) { const n = cmd.function.names[j]; const h = cmd.function.handlers[j]; fns.set(n, h); } if (!Array.isArray(command.options)) command.options = []; command.options.push(realCommand); } } cmdArr.push("}"); autoArr.push("}"); return { body: { command: cmdArr.join(""), autocomplete: autoArr.length > 4 ? autoArr.join("") : null }, function: { names: [...fns.keys()], handlers: [...fns.values()] }, json: command }; } #isCommandWrapper(options) { for (let i = 0, { length } = options; i < length; i++) { const { type } = options[i]; if (type === 2 || type === 1) return true; } return false; } getCompilationStack() { const functions = new Map(); const cmdArr = [ this.#assumeTransformed ? `const interaction_name = interaction.${this.#customKeys?.name};` : "const interaction_name = interaction.data.name;", `if (interaction.type === ${2}) {` ]; const autoArr = [`else if (interaction.type === ${4}) {`]; const commands = [...this.#globalApplicationCommands.values(), ...this.#guildApplicationCommands.values()]; for (let i = 0, { length } = commands; i < length; i++) { const { body, function: fn } = commands[i]; cmdArr.push(body.command); if (body.autocomplete !== null) autoArr.push(body.autocomplete); for (let j = 0, len = fn.names.length; j < len; j++) { const n = fn.names[j]; const h = fn.handlers[j]; functions.set(n, h); } } const arr = []; if (cmdArr.length > 2) arr.push(cmdArr.join(""), "}"); if (autoArr.length > 1) { if (arr.length === 0) autoArr[0] = autoArr[0].slice(5); if (autoArr[1][0] === "e") autoArr[1] = autoArr[1].slice(5); arr.push(autoArr.join(""), "}"); } if (arr.length === 0) return null; const fNames = functions.keys(); const fHandlers = functions.values(); const compiledListener = arr.join(""); return { functionNames: fNames, handlers: fHandlers, stack: compiledListener }; } compile() { const compiledResult = this.getCompilationStack(); if (compiledResult === null) return null; const { stack, functionNames, handlers } = compiledResult; this.#emit?.(7, stack); return new Function("parseOpts", ...functionNames, this.#assumeTransformed ? `return async (interaction) => { ${stack} }` : `return async (client, interaction) => { ${stack} }`)(innerOptionParser, ...handlers); } getStoredGlobalCommands() { return [...this.#globalApplicationCommands.values()]; } getStoredGuildCommands() { return [...this.#guildApplicationCommands.values()]; } clear() { this.#globalApplicationCommands.clear(); this.#guildApplicationCommands.clear(); } } export function innerOptionParser(options, _obj) { if (!options) return; for (let i = 0, { length } = options; i < length; i++) { const opt = options[i]; _obj[opt.name] = opt.value; } }