import { readConfigFileSnapshot, validateConfigObject, writeConfigFile, } from "../../config/config.js"; import { getConfigValueAtPath, parseConfigPath, setConfigValueAtPath, unsetConfigValueAtPath, } from "../../config/config-paths.js"; import { getConfigOverrides, resetConfigOverrides, setConfigOverride, unsetConfigOverride, } from "../../config/runtime-overrides.js"; import { resolveChannelConfigWrites } from "../../channels/plugins/config-writes.js"; import { normalizeChannelId } from "../../channels/registry.js"; import { logVerbose } from "../../globals.js"; import type { CommandHandler } from "./commands-types.js"; import { parseConfigCommand } from "./config-commands.js"; import { parseDebugCommand } from "./debug-commands.js"; export const handleConfigCommand: CommandHandler = async (params, allowTextCommands) => { if (!allowTextCommands) return null; const configCommand = parseConfigCommand(params.command.commandBodyNormalized); if (!configCommand) return null; if (!params.command.isAuthorizedSender) { logVerbose( `Ignoring /config from unauthorized sender: ${params.command.senderId || ""}`, ); return { shouldContinue: false }; } if (params.cfg.commands?.config !== true) { return { shouldContinue: false, reply: { text: "⚠️ /config is disabled. Set commands.config=true to enable.", }, }; } if (configCommand.action === "error") { return { shouldContinue: false, reply: { text: `⚠️ ${configCommand.message}` }, }; } if (configCommand.action === "set" || configCommand.action === "unset") { const channelId = params.command.channelId ?? normalizeChannelId(params.command.channel); const allowWrites = resolveChannelConfigWrites({ cfg: params.cfg, channelId, accountId: params.ctx.AccountId, }); if (!allowWrites) { const channelLabel = channelId ?? "this channel"; const hint = channelId ? `channels.${channelId}.configWrites=true` : "channels..configWrites=true"; return { shouldContinue: false, reply: { text: `⚠️ Config writes are disabled for ${channelLabel}. Set ${hint} to enable.`, }, }; } } const snapshot = await readConfigFileSnapshot(); if (!snapshot.valid || !snapshot.parsed || typeof snapshot.parsed !== "object") { return { shouldContinue: false, reply: { text: "⚠️ Config file is invalid; fix it before using /config.", }, }; } const parsedBase = structuredClone(snapshot.parsed as Record); if (configCommand.action === "show") { const pathRaw = configCommand.path?.trim(); if (pathRaw) { const parsedPath = parseConfigPath(pathRaw); if (!parsedPath.ok || !parsedPath.path) { return { shouldContinue: false, reply: { text: `⚠️ ${parsedPath.error ?? "Invalid path."}` }, }; } const value = getConfigValueAtPath(parsedBase, parsedPath.path); const rendered = JSON.stringify(value ?? null, null, 2); return { shouldContinue: false, reply: { text: `⚙️ Config ${pathRaw}:\n\`\`\`json\n${rendered}\n\`\`\``, }, }; } const json = JSON.stringify(parsedBase, null, 2); return { shouldContinue: false, reply: { text: `⚙️ Config (raw):\n\`\`\`json\n${json}\n\`\`\`` }, }; } if (configCommand.action === "unset") { const parsedPath = parseConfigPath(configCommand.path); if (!parsedPath.ok || !parsedPath.path) { return { shouldContinue: false, reply: { text: `⚠️ ${parsedPath.error ?? "Invalid path."}` }, }; } const removed = unsetConfigValueAtPath(parsedBase, parsedPath.path); if (!removed) { return { shouldContinue: false, reply: { text: `⚙️ No config value found for ${configCommand.path}.` }, }; } const validated = validateConfigObject(parsedBase); if (!validated.ok) { const issue = validated.issues[0]; return { shouldContinue: false, reply: { text: `⚠️ Config invalid after unset (${issue.path}: ${issue.message}).`, }, }; } await writeConfigFile(validated.config); return { shouldContinue: false, reply: { text: `⚙️ Config updated: ${configCommand.path} removed.` }, }; } if (configCommand.action === "set") { const parsedPath = parseConfigPath(configCommand.path); if (!parsedPath.ok || !parsedPath.path) { return { shouldContinue: false, reply: { text: `⚠️ ${parsedPath.error ?? "Invalid path."}` }, }; } setConfigValueAtPath(parsedBase, parsedPath.path, configCommand.value); const validated = validateConfigObject(parsedBase); if (!validated.ok) { const issue = validated.issues[0]; return { shouldContinue: false, reply: { text: `⚠️ Config invalid after set (${issue.path}: ${issue.message}).`, }, }; } await writeConfigFile(validated.config); const valueLabel = typeof configCommand.value === "string" ? `"${configCommand.value}"` : JSON.stringify(configCommand.value); return { shouldContinue: false, reply: { text: `⚙️ Config updated: ${configCommand.path}=${valueLabel ?? "null"}`, }, }; } return null; }; export const handleDebugCommand: CommandHandler = async (params, allowTextCommands) => { if (!allowTextCommands) return null; const debugCommand = parseDebugCommand(params.command.commandBodyNormalized); if (!debugCommand) return null; if (!params.command.isAuthorizedSender) { logVerbose( `Ignoring /debug from unauthorized sender: ${params.command.senderId || ""}`, ); return { shouldContinue: false }; } if (params.cfg.commands?.debug !== true) { return { shouldContinue: false, reply: { text: "⚠️ /debug is disabled. Set commands.debug=true to enable.", }, }; } if (debugCommand.action === "error") { return { shouldContinue: false, reply: { text: `⚠️ ${debugCommand.message}` }, }; } if (debugCommand.action === "show") { const overrides = getConfigOverrides(); const hasOverrides = Object.keys(overrides).length > 0; if (!hasOverrides) { return { shouldContinue: false, reply: { text: "⚙️ Debug overrides: (none)" }, }; } const json = JSON.stringify(overrides, null, 2); return { shouldContinue: false, reply: { text: `⚙️ Debug overrides (memory-only):\n\`\`\`json\n${json}\n\`\`\``, }, }; } if (debugCommand.action === "reset") { resetConfigOverrides(); return { shouldContinue: false, reply: { text: "⚙️ Debug overrides cleared; using config on disk." }, }; } if (debugCommand.action === "unset") { const result = unsetConfigOverride(debugCommand.path); if (!result.ok) { return { shouldContinue: false, reply: { text: `⚠️ ${result.error ?? "Invalid path."}` }, }; } if (!result.removed) { return { shouldContinue: false, reply: { text: `⚙️ No debug override found for ${debugCommand.path}.`, }, }; } return { shouldContinue: false, reply: { text: `⚙️ Debug override removed for ${debugCommand.path}.` }, }; } if (debugCommand.action === "set") { const result = setConfigOverride(debugCommand.path, debugCommand.value); if (!result.ok) { return { shouldContinue: false, reply: { text: `⚠️ ${result.error ?? "Invalid override."}` }, }; } const valueLabel = typeof debugCommand.value === "string" ? `"${debugCommand.value}"` : JSON.stringify(debugCommand.value); return { shouldContinue: false, reply: { text: `⚙️ Debug override set: ${debugCommand.path}=${valueLabel ?? "null"}`, }, }; } return null; };