/* * Vencord, a modification for Discord's desktop app * Copyright (c) 2022 Vendicated and contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import { makeCodeblock } from "../../utils/misc"; import { generateId, sendBotMessage } from "./commandHelpers"; import { ApplicationCommandInputType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types"; export * from "./commandHelpers"; export * from "./types"; export let BUILT_IN: Command[]; export const commands = {} as Record; // hack for plugins being evaluated before we can grab these from webpack const OptPlaceholder = Symbol("OptionalMessageOption") as any as Option; const ReqPlaceholder = Symbol("RequiredMessageOption") as any as Option; /** * Optional message option named "message" you can use in commands. * Used in "tableflip" or "shrug" * @see {@link RequiredMessageOption} */ export let OptionalMessageOption: Option = OptPlaceholder; /** * Required message option named "message" you can use in commands. * Used in "me" * @see {@link OptionalMessageOption} */ export let RequiredMessageOption: Option = ReqPlaceholder; export const _init = function (cmds: Command[]) { try { BUILT_IN = cmds; OptionalMessageOption = cmds.find(c => c.name === "shrug")!.options![0]; RequiredMessageOption = cmds.find(c => c.name === "me")!.options![0]; } catch (e) { console.error("Failed to load CommandsApi"); } return cmds; } as never; export const _handleCommand = function (cmd: Command, args: Argument[], ctx: CommandContext) { if (!cmd.isVencordCommand) return cmd.execute(args, ctx); const handleError = (err: any) => { // TODO: cancel send if cmd.inputType === BUILT_IN_TEXT const msg = `An Error occurred while executing command "${cmd.name}"`; const reason = err instanceof Error ? err.stack || err.message : String(err); console.error(msg, err); sendBotMessage(ctx.channel.id, { content: `${msg}:\n${makeCodeblock(reason)}`, author: { username: "Vencord" } }); }; try { const res = cmd.execute(args, ctx); return res instanceof Promise ? res.catch(handleError) : res; } catch (err) { return handleError(err); } } as never; function modifyOpt(opt: Option | Command) { opt.displayName ||= opt.name; opt.displayDescription ||= opt.description; opt.options?.forEach((opt, i, opts) => { // See comment above Placeholders if (opt === OptPlaceholder) opts[i] = OptionalMessageOption; else if (opt === ReqPlaceholder) opts[i] = RequiredMessageOption; opt.choices?.forEach(x => x.displayName ||= x.name); modifyOpt(opts[i]); }); } export function registerCommand(command: Command, plugin: string) { if (!BUILT_IN) { console.warn( "[CommandsAPI]", `Not registering ${command.name} as the CommandsAPI hasn't been initialised.`, "Please restart to use commands" ); return; } if (BUILT_IN.some(c => c.name === command.name)) throw new Error(`Command '${command.name}' already exists.`); command.isVencordCommand = true; command.id ??= generateId(); command.applicationId ??= "-1"; // BUILT_IN; command.type ??= ApplicationCommandType.CHAT_INPUT; command.inputType ??= ApplicationCommandInputType.BUILT_IN_TEXT; command.plugin ||= plugin; modifyOpt(command); commands[command.name] = command; BUILT_IN.push(command); } export function unregisterCommand(name: string) { const idx = BUILT_IN.findIndex(c => c.name === name); if (idx === -1) return false; BUILT_IN.splice(idx, 1); delete commands[name]; return true; }