From 402332340b727d7f4087b1f24dcd4eecf16b0891 Mon Sep 17 00:00:00 2001 From: Reto Brunner Date: Sat, 4 Mar 2023 17:00:53 +0100 Subject: [PATCH 1/2] pluginCommand: type it and guard against bad input --- server/plugins/inputs/index.ts | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/server/plugins/inputs/index.ts b/server/plugins/inputs/index.ts index e3390361..95141b5f 100644 --- a/server/plugins/inputs/index.ts +++ b/server/plugins/inputs/index.ts @@ -3,6 +3,7 @@ import log from "../../log"; import Chan, {Channel} from "../../models/chan"; import Network, {NetworkWithIrcFramework} from "../../models/network"; import {PackageInfo} from "../packages"; +import PublicClient from "../packages/publicClient"; export type PluginInputHandler = ( this: Client, @@ -15,7 +16,18 @@ export type PluginInputHandler = ( type Plugin = { commands: string[]; input: (network: Network, chan: Chan, cmd: string, args: string[]) => void; - allowDisconnected?: boolean | undefined; + allowDisconnected?: boolean; +}; + +type ExternalPluginCommand = { + packageInfo: PackageInfo; + input: ( + pub: PublicClient, + netChan: {network: Network; chan: Chan}, + cmd: string, + args: string[] + ) => void; + allowDisconnected?: boolean; }; const clientSideCommands = ["/collapse", "/expand", "/search"]; @@ -79,7 +91,7 @@ for (const input of builtInInputs) { }); } -const pluginCommands = new Map(); +const pluginCommands = new Map(); const getCommands = () => Array.from(userInputs.keys()) @@ -89,9 +101,22 @@ const getCommands = () => .concat(passThroughCommands) .sort(); -const addPluginCommand = (packageInfo: PackageInfo, command, func) => { - func.packageInfo = packageInfo; - pluginCommands.set(command, func); +const addPluginCommand = (packageInfo: PackageInfo, command: any, obj: any) => { + if (typeof command !== "string") { + log.error(`plugin {packageInfo.packageName} tried to register a bad command`); + return; + } else if (!obj || typeof obj.input !== "function") { + log.error( + `plugin ${packageInfo.packageName} tried to register command "${command} without a callback"` + ); + return; + } + + pluginCommands.set(command, { + packageInfo: packageInfo, + input: obj.input, + allowDisconnected: obj.allowDisconnected, + }); }; export default { From 4e954b919c86ad17f6c7f934de4aa8d6fe5b9b1d Mon Sep 17 00:00:00 2001 From: Reto Brunner Date: Sat, 4 Mar 2023 17:01:58 +0100 Subject: [PATCH 2/2] server/client: refactor command input Keep happy path on the left and try to return as early as we can to help the reader understand the logic better The function is too large to be able to quickly scan an if / else chain and see the function return at the end --- server/client.ts | 70 +++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/server/client.ts b/server/client.ts index 826bef13..4ed691c2 100644 --- a/server/client.ts +++ b/server/client.ts @@ -465,38 +465,9 @@ class Client { const cmd = args?.shift()?.toLowerCase() || ""; const irc = target.network.irc; - let connected = irc && irc.connection && irc.connection.connected; + const connected = irc?.connected; - if (inputs.userInputs.has(cmd)) { - const plugin = inputs.userInputs.get(cmd); - - if (!plugin) { - // should be a no-op - throw new Error(`Plugin ${cmd} not found`); - } - - if (typeof plugin.input === "function" && (connected || plugin.allowDisconnected)) { - connected = true; - plugin.input.apply(client, [target.network, target.chan, cmd, args]); - } - } else if (inputs.pluginCommands.has(cmd)) { - const plugin = inputs.pluginCommands.get(cmd); - - if (typeof plugin.input === "function" && (connected || plugin.allowDisconnected)) { - connected = true; - plugin.input( - new PublicClient(client, plugin.packageInfo), - {network: target.network, chan: target.chan}, - cmd, - args - ); - } - } else if (connected) { - // TODO: fix - irc!.raw(text); - } - - if (!connected) { + const emitFailureDisconnected = () => { target.chan.pushMessage( this, new Msg({ @@ -504,7 +475,44 @@ class Client { text: "You are not connected to the IRC network, unable to send your command.", }) ); + }; + + const plugin = inputs.userInputs.get(cmd); + + if (plugin) { + if (!connected && !plugin.allowDisconnected) { + emitFailureDisconnected(); + return; + } + + plugin.input.apply(client, [target.network, target.chan, cmd, args]); + return; } + + const extPlugin = inputs.pluginCommands.get(cmd); + + if (extPlugin) { + if (!connected && !extPlugin.allowDisconnected) { + emitFailureDisconnected(); + return; + } + + extPlugin.input( + new PublicClient(client, extPlugin.packageInfo), + {network: target.network, chan: target.chan}, + cmd, + args + ); + return; + } + + if (!connected) { + emitFailureDisconnected(); + return; + } + + // TODO: fix + irc!.raw(text); } compileCustomHighlights() {