From 2ee8b807f8782e5dc201728c617ff6c164b30ece Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 7 Mar 2026 19:47:41 +0000 Subject: [PATCH] refactor: dedupe discord account inspect config merge --- src/discord/account-inspect.test.ts | 126 ++++++++++++++++++++++++++++ src/discord/account-inspect.ts | 22 ++--- src/discord/accounts.ts | 11 ++- 3 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 src/discord/account-inspect.test.ts diff --git a/src/discord/account-inspect.test.ts b/src/discord/account-inspect.test.ts new file mode 100644 index 00000000000..0e8303635f9 --- /dev/null +++ b/src/discord/account-inspect.test.ts @@ -0,0 +1,126 @@ +import { describe, expect, it } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; +import { inspectDiscordAccount } from "./account-inspect.js"; + +function asConfig(value: unknown): OpenClawConfig { + return value as OpenClawConfig; +} + +describe("inspectDiscordAccount", () => { + it("prefers account token over channel token and strips Bot prefix", () => { + const inspected = inspectDiscordAccount({ + cfg: asConfig({ + channels: { + discord: { + token: "Bot channel-token", + accounts: { + work: { + token: "Bot account-token", + }, + }, + }, + }, + }), + accountId: "work", + }); + + expect(inspected.token).toBe("account-token"); + expect(inspected.tokenSource).toBe("config"); + expect(inspected.tokenStatus).toBe("available"); + expect(inspected.configured).toBe(true); + }); + + it("reports configured_unavailable for unresolved configured secret input", () => { + const inspected = inspectDiscordAccount({ + cfg: asConfig({ + channels: { + discord: { + accounts: { + work: { + token: { source: "env", id: "DISCORD_TOKEN" }, + }, + }, + }, + }, + }), + accountId: "work", + }); + + expect(inspected.token).toBe(""); + expect(inspected.tokenSource).toBe("config"); + expect(inspected.tokenStatus).toBe("configured_unavailable"); + expect(inspected.configured).toBe(true); + }); + + it("does not fall back when account token key exists but is missing", () => { + const inspected = inspectDiscordAccount({ + cfg: asConfig({ + channels: { + discord: { + token: "Bot channel-token", + accounts: { + work: { + token: "", + }, + }, + }, + }, + }), + accountId: "work", + }); + + expect(inspected.token).toBe(""); + expect(inspected.tokenSource).toBe("none"); + expect(inspected.tokenStatus).toBe("missing"); + expect(inspected.configured).toBe(false); + }); + + it("falls back to channel token when account token is absent", () => { + const inspected = inspectDiscordAccount({ + cfg: asConfig({ + channels: { + discord: { + token: "Bot channel-token", + accounts: { + work: {}, + }, + }, + }, + }), + accountId: "work", + }); + + expect(inspected.token).toBe("channel-token"); + expect(inspected.tokenSource).toBe("config"); + expect(inspected.tokenStatus).toBe("available"); + expect(inspected.configured).toBe(true); + }); + + it("allows env token only for default account", () => { + const defaultInspected = inspectDiscordAccount({ + cfg: asConfig({}), + accountId: "default", + envToken: "Bot env-default", + }); + const namedInspected = inspectDiscordAccount({ + cfg: asConfig({ + channels: { + discord: { + accounts: { + work: {}, + }, + }, + }, + }), + accountId: "work", + envToken: "Bot env-work", + }); + + expect(defaultInspected.token).toBe("env-default"); + expect(defaultInspected.tokenSource).toBe("env"); + expect(defaultInspected.configured).toBe(true); + expect(namedInspected.token).toBe(""); + expect(namedInspected.tokenSource).toBe("none"); + expect(namedInspected.configured).toBe(false); + }); +}); diff --git a/src/discord/account-inspect.ts b/src/discord/account-inspect.ts index 0ece2072744..53357ffd636 100644 --- a/src/discord/account-inspect.ts +++ b/src/discord/account-inspect.ts @@ -1,9 +1,12 @@ import type { OpenClawConfig } from "../config/config.js"; import type { DiscordAccountConfig } from "../config/types.discord.js"; import { hasConfiguredSecretInput, normalizeSecretInputString } from "../config/types.secrets.js"; -import { resolveAccountEntry } from "../routing/account-lookup.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js"; -import { resolveDefaultDiscordAccountId } from "./accounts.js"; +import { + mergeDiscordAccountConfig, + resolveDefaultDiscordAccountId, + resolveDiscordAccountConfig, +} from "./accounts.js"; export type DiscordCredentialStatus = "available" | "configured_unavailable" | "missing"; @@ -18,21 +21,6 @@ export type InspectedDiscordAccount = { config: DiscordAccountConfig; }; -function resolveDiscordAccountConfig( - cfg: OpenClawConfig, - accountId: string, -): DiscordAccountConfig | undefined { - return resolveAccountEntry(cfg.channels?.discord?.accounts, accountId); -} - -function mergeDiscordAccountConfig(cfg: OpenClawConfig, accountId: string): DiscordAccountConfig { - const { accounts: _ignored, ...base } = (cfg.channels?.discord ?? {}) as DiscordAccountConfig & { - accounts?: unknown; - }; - const account = resolveDiscordAccountConfig(cfg, accountId) ?? {}; - return { ...base, ...account }; -} - function inspectDiscordTokenValue(value: unknown): { token: string; tokenSource: "config"; diff --git a/src/discord/accounts.ts b/src/discord/accounts.ts index 33731b4260d..75eeff40b3e 100644 --- a/src/discord/accounts.ts +++ b/src/discord/accounts.ts @@ -19,18 +19,21 @@ const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("di export const listDiscordAccountIds = listAccountIds; export const resolveDefaultDiscordAccountId = resolveDefaultAccountId; -function resolveAccountConfig( +export function resolveDiscordAccountConfig( cfg: OpenClawConfig, accountId: string, ): DiscordAccountConfig | undefined { return resolveAccountEntry(cfg.channels?.discord?.accounts, accountId); } -function mergeDiscordAccountConfig(cfg: OpenClawConfig, accountId: string): DiscordAccountConfig { +export function mergeDiscordAccountConfig( + cfg: OpenClawConfig, + accountId: string, +): DiscordAccountConfig { const { accounts: _ignored, ...base } = (cfg.channels?.discord ?? {}) as DiscordAccountConfig & { accounts?: unknown; }; - const account = resolveAccountConfig(cfg, accountId) ?? {}; + const account = resolveDiscordAccountConfig(cfg, accountId) ?? {}; return { ...base, ...account }; } @@ -41,7 +44,7 @@ export function createDiscordActionGate(params: { const accountId = normalizeAccountId(params.accountId); return createAccountActionGate({ baseActions: params.cfg.channels?.discord?.actions, - accountActions: resolveAccountConfig(params.cfg, accountId)?.actions, + accountActions: resolveDiscordAccountConfig(params.cfg, accountId)?.actions, }); }