From 7936f6a2bccd477f09e63d138334a502da9d8979 Mon Sep 17 00:00:00 2001 From: seeyebe Date: Fri, 26 Sep 2025 18:23:46 +0300 Subject: [PATCH] feat: custom command aliases --- backend/src/applyCommandAliases.ts | 99 ++++++++++++++++++++++++++++++ backend/src/index.ts | 1 + backend/src/types.ts | 6 ++ 3 files changed, 106 insertions(+) create mode 100644 backend/src/applyCommandAliases.ts diff --git a/backend/src/applyCommandAliases.ts b/backend/src/applyCommandAliases.ts new file mode 100644 index 000000000..e831f6def --- /dev/null +++ b/backend/src/applyCommandAliases.ts @@ -0,0 +1,99 @@ +import { PluginMessageCommandManager } from "knub"; + +type RawAliasValue = string | string[]; +type GuildAliasConfig = Record>; + +type PluginDataWithAliases = { + context?: string; + pluginName?: string; + fullConfig?: { + aliases?: GuildAliasConfig; + }; +}; + +const originalAdd = PluginMessageCommandManager.prototype.add; + +function normalizeAliasValue(value: RawAliasValue): string[] { + const values = Array.isArray(value) ? value : [value]; + return values + .map((alias) => alias.trim()) + .filter((alias) => alias.length > 0); +} + +PluginMessageCommandManager.prototype.add = function addWithCommandAliases( + this: PluginMessageCommandManager, + blueprint: any, +) { + const pluginData = (this as unknown as { pluginData?: PluginDataWithAliases }).pluginData; + if (pluginData?.context === "guild") { + const aliasesByPlugin = pluginData.fullConfig?.aliases; + if (aliasesByPlugin) { + const pluginName = pluginData.pluginName ?? ""; + const pluginAliases = + aliasesByPlugin[pluginName] ?? aliasesByPlugin[pluginName.toLowerCase()]; + + if (pluginAliases) { + const normalizedAliasMap = new Map(); + for (const [rawKey, rawValue] of Object.entries(pluginAliases)) { + const normalizedKey = rawKey.trim().toLowerCase(); + if (!normalizedKey) { + continue; + } + + const normalizedValues = normalizeAliasValue(rawValue); + if (normalizedValues.length === 0) { + continue; + } + + normalizedAliasMap.set(normalizedKey, normalizedValues); + } + + if (normalizedAliasMap.size > 0 && blueprint?.trigger) { + const baseTriggers = Array.isArray(blueprint.trigger) + ? [...blueprint.trigger] + : [blueprint.trigger]; + const updatedTriggers = [...baseTriggers]; + + const existingStringTriggers = new Set( + baseTriggers + .filter((trigger): trigger is string => typeof trigger === "string") + .map((trigger) => trigger.trim().toLowerCase()), + ); + + let changed = false; + for (const trigger of baseTriggers) { + if (typeof trigger !== "string") { + continue; + } + + const triggerKey = trigger.trim().toLowerCase(); + const aliasList = normalizedAliasMap.get(triggerKey); + if (!aliasList) { + continue; + } + + for (const alias of aliasList) { + const aliasKey = alias.toLowerCase(); + if (existingStringTriggers.has(aliasKey)) { + continue; + } + + existingStringTriggers.add(aliasKey); + updatedTriggers.push(alias); + changed = true; + } + } + + if (changed) { + blueprint = { + ...blueprint, + trigger: updatedTriggers, + }; + } + } + } + } + } + + return originalAdd.call(this, blueprint); +}; diff --git a/backend/src/index.ts b/backend/src/index.ts index 5a39027cd..36367022f 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,6 +1,7 @@ // KEEP THIS AS FIRST IMPORT // See comment in module for details import "./threadsSignalFix.js"; +import "./applyCommandAliases.js"; import { Client, diff --git a/backend/src/types.ts b/backend/src/types.ts index 35142f4cb..e8fbff6ef 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -6,6 +6,12 @@ export const zZeppelinGuildConfig = z.strictObject({ // From BaseConfig prefix: z.string().optional(), levels: z.record(zSnowflake, z.number()).optional(), + aliases: z + .record( + z.string(), + z.record(z.string(), z.union([z.string().min(1), z.array(z.string().min(1))])), + ) + .optional(), plugins: z.record(z.string(), z.unknown()).optional(), });