diff options
author | Ven <vendicated@riseup.net> | 2022-10-29 20:45:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-29 20:45:31 +0200 |
commit | d72542405af8873a542b55128d8b0c6311183235 (patch) | |
tree | caa295bcec18eb56ca2360558130fc0c9c7aa047 /src/api | |
parent | 95aa2d9d8d850c27e88fce76f065d5e71c8022c2 (diff) | |
download | Vencord-d72542405af8873a542b55128d8b0c6311183235.tar.gz Vencord-d72542405af8873a542b55128d8b0c6311183235.tar.bz2 Vencord-d72542405af8873a542b55128d8b0c6311183235.zip |
Implement Subcommands; fix errors due to Settings <-> Plugins circular imports (#174)
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/Commands/index.ts | 46 | ||||
-rw-r--r-- | src/api/Commands/types.ts | 1 | ||||
-rw-r--r-- | src/api/settings.ts | 14 |
3 files changed, 49 insertions, 12 deletions
diff --git a/src/api/Commands/index.ts b/src/api/Commands/index.ts index 3b42379..a20ac50 100644 --- a/src/api/Commands/index.ts +++ b/src/api/Commands/index.ts @@ -18,7 +18,7 @@ import { makeCodeblock } from "../../utils/misc"; import { generateId, sendBotMessage } from "./commandHelpers"; -import { ApplicationCommandInputType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types"; +import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types"; export * from "./commandHelpers"; export * from "./types"; @@ -79,7 +79,12 @@ export const _handleCommand = function (cmd: Command, args: Argument[], ctx: Com } } as never; -function modifyOpt(opt: Option | Command) { + +/** + * Prepare a Command Option for Discord by filling missing fields + * @param opt + */ +export function prepareOption<O extends Option | Command>(opt: O): O { opt.displayName ||= opt.name; opt.displayDescription ||= opt.description; opt.options?.forEach((opt, i, opts) => { @@ -88,11 +93,36 @@ function modifyOpt(opt: Option | Command) { else if (opt === ReqPlaceholder) opts[i] = RequiredMessageOption; opt.choices?.forEach(x => x.displayName ||= x.name); - modifyOpt(opts[i]); + prepareOption(opts[i]); + }); + return opt; +} + +// Yes, Discord registers individual commands for each subcommand +// TODO: This probably doesn't support nested subcommands. If that is ever needed, +// investigate +function registerSubCommands(cmd: Command, plugin: string) { + cmd.options?.forEach(o => { + if (o.type !== ApplicationCommandOptionType.SUB_COMMAND) + throw new Error("When specifying sub-command options, all options must be sub-commands."); + const subCmd = { + ...cmd, + ...o, + type: ApplicationCommandType.CHAT_INPUT, + name: `${cmd.name} ${o.name}`, + displayName: `${cmd.name} ${o.name}`, + subCommandPath: [{ + name: o.name, + type: o.type, + displayName: o.name + }], + rootCommand: cmd + }; + registerCommand(subCmd as any, plugin); }); } -export function registerCommand(command: Command, plugin: string) { +export function registerCommand<C extends Command>(command: C, plugin: string) { if (!BUILT_IN) { console.warn( "[CommandsAPI]", @@ -112,7 +142,13 @@ export function registerCommand(command: Command, plugin: string) { command.inputType ??= ApplicationCommandInputType.BUILT_IN_TEXT; command.plugin ||= plugin; - modifyOpt(command); + prepareOption(command); + + if (command.options?.[0]?.type === ApplicationCommandOptionType.SUB_COMMAND) { + registerSubCommands(command, plugin); + return; + } + commands[command.name] = command; BUILT_IN.push(command); } diff --git a/src/api/Commands/types.ts b/src/api/Commands/types.ts index a40353f..9acab66 100644 --- a/src/api/Commands/types.ts +++ b/src/api/Commands/types.ts @@ -81,6 +81,7 @@ export interface Argument { name: string; value: string; focused: undefined; + options: Argument[]; } export interface Command { diff --git a/src/api/settings.ts b/src/api/settings.ts index e25572f..9e518c6 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -42,12 +42,6 @@ const DefaultSettings: Settings = { plugins: {} }; -for (const plugin in plugins) { - DefaultSettings.plugins[plugin] = { - enabled: plugins[plugin].required ?? false - }; -} - try { var settings = JSON.parse(VencordNative.ipc.sendSync(IpcEvents.GET_SETTINGS)) as Settings; mergeDefaults(settings, DefaultSettings); @@ -60,13 +54,19 @@ type SubscriptionCallback = ((newValue: any, path: string) => void) & { _path?: const subscriptions = new Set<SubscriptionCallback>(); // Wraps the passed settings object in a Proxy to nicely handle change listeners and default values -function makeProxy(settings: Settings, root = settings, path = ""): Settings { +function makeProxy(settings: any, root = settings, path = ""): Settings { return new Proxy(settings, { get(target, p: string) { const v = target[p]; // using "in" is important in the following cases to properly handle falsy or nullish values if (!(p in target)) { + // Return empty for plugins with no settings + if (path === "plugins" && p in plugins) + return target[p] = makeProxy({ + enabled: plugins[p].required ?? false + }, root, `plugins/${p}`); + // Since the property is not set, check if this is a plugin's setting and if so, try to resolve // the default value. if (path.startsWith("plugins.")) { |