From 4107a5a4f0cacebd3c59068c74c4eaeb89d9f299 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:42:16 -0500 Subject: [PATCH] fix: honor zalo setup dm policy accounts --- extensions/zalo/src/setup-core.ts | 73 +++++++++++++++++++++-- extensions/zalo/src/setup-surface.test.ts | 52 ++++++++++++++++ extensions/zalo/src/setup-surface.ts | 22 +------ 3 files changed, 122 insertions(+), 25 deletions(-) diff --git a/extensions/zalo/src/setup-core.ts b/extensions/zalo/src/setup-core.ts index 3ecc3b5d99b..1d3d2cd7c68 100644 --- a/extensions/zalo/src/setup-core.ts +++ b/extensions/zalo/src/setup-core.ts @@ -1,11 +1,15 @@ import { + addWildcardAllowFrom, createDelegatedSetupWizardProxy, createPatchedAccountSetupAdapter, createSetupInputPresenceValidator, - createTopLevelChannelDmPolicy, type ChannelSetupWizard, + type ChannelSetupDmPolicy, DEFAULT_ACCOUNT_ID, + normalizeAccountId, } from "openclaw/plugin-sdk/setup"; +import type { OpenClawConfig } from "./runtime-api.js"; +import { resolveDefaultZaloAccountId, resolveZaloAccount } from "./accounts.js"; const channel = "zalo" as const; @@ -30,15 +34,76 @@ export const zaloSetupAdapter = createPatchedAccountSetupAdapter({ : {}, }); -export const zaloDmPolicy = createTopLevelChannelDmPolicy({ +export const zaloDmPolicy: ChannelSetupDmPolicy = { label: "Zalo", channel, policyKey: "channels.zalo.dmPolicy", allowFromKey: "channels.zalo.allowFrom", - getCurrent: (cfg) => cfg.channels?.zalo?.dmPolicy ?? "pairing", + resolveConfigKeys: (_cfg, accountId) => + accountId && accountId !== DEFAULT_ACCOUNT_ID + ? { + policyKey: `channels.zalo.accounts.${accountId}.dmPolicy`, + allowFromKey: `channels.zalo.accounts.${accountId}.allowFrom`, + } + : { + policyKey: "channels.zalo.dmPolicy", + allowFromKey: "channels.zalo.allowFrom", + }, + getCurrent: (cfg, accountId) => + resolveZaloAccount({ + cfg: cfg as OpenClawConfig, + accountId: accountId ?? DEFAULT_ACCOUNT_ID, + }).config.dmPolicy ?? "pairing", + setPolicy: (cfg, policy, accountId) => { + const resolvedAccountId = + accountId && normalizeAccountId(accountId) + ? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID) + : resolveDefaultZaloAccountId(cfg as OpenClawConfig); + const resolved = resolveZaloAccount({ + cfg: cfg as OpenClawConfig, + accountId: resolvedAccountId, + }); + if (resolvedAccountId === DEFAULT_ACCOUNT_ID) { + return { + ...cfg, + channels: { + ...cfg.channels, + zalo: { + ...cfg.channels?.zalo, + enabled: true, + dmPolicy: policy, + ...(policy === "open" + ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } + : {}), + }, + }, + }; + } + return { + ...cfg, + channels: { + ...cfg.channels, + zalo: { + ...cfg.channels?.zalo, + enabled: true, + accounts: { + ...cfg.channels?.zalo?.accounts, + [resolvedAccountId]: { + ...cfg.channels?.zalo?.accounts?.[resolvedAccountId], + enabled: cfg.channels?.zalo?.accounts?.[resolvedAccountId]?.enabled ?? true, + dmPolicy: policy, + ...(policy === "open" + ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } + : {}), + }, + }, + }, + }, + }; + }, promptAllowFrom: async (params) => (await loadZaloSetupWizard()).dmPolicy?.promptAllowFrom?.(params) ?? params.cfg, -}); +}; async function loadZaloSetupWizard(): Promise { return (await import("./setup-surface.js")).zaloSetupWizard; diff --git a/extensions/zalo/src/setup-surface.test.ts b/extensions/zalo/src/setup-surface.test.ts index b71f13ee690..c95282a2f02 100644 --- a/extensions/zalo/src/setup-surface.test.ts +++ b/extensions/zalo/src/setup-surface.test.ts @@ -7,6 +7,7 @@ import { } from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../runtime-api.js"; import { zaloPlugin } from "./channel.js"; +import { zaloDmPolicy } from "./setup-core.js"; const zaloConfigure = createPluginSetupWizardConfigure(zaloPlugin); @@ -40,4 +41,55 @@ describe("zalo setup wizard", () => { expect(result.cfg.channels?.zalo?.botToken).toBe("12345689:abc-xyz"); expect(result.cfg.channels?.zalo?.webhookUrl).toBeUndefined(); }); + + it("reads the named-account DM policy instead of the channel root", () => { + expect( + zaloDmPolicy.getCurrent( + { + channels: { + zalo: { + dmPolicy: "disabled", + accounts: { + work: { + botToken: "12345689:abc-xyz", + dmPolicy: "allowlist", + }, + }, + }, + }, + } as OpenClawConfig, + "work", + ), + ).toBe("allowlist"); + }); + + it("reports account-scoped config keys for named accounts", () => { + expect(zaloDmPolicy.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual({ + policyKey: "channels.zalo.accounts.work.dmPolicy", + allowFromKey: "channels.zalo.accounts.work.allowFrom", + }); + }); + + it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => { + const next = zaloDmPolicy.setPolicy( + { + channels: { + zalo: { + allowFrom: ["123456789"], + accounts: { + work: { + botToken: "12345689:abc-xyz", + }, + }, + }, + }, + } as OpenClawConfig, + "open", + "work", + ); + + expect(next.channels?.zalo?.dmPolicy).toBeUndefined(); + expect(next.channels?.zalo?.accounts?.work?.dmPolicy).toBe("open"); + expect(next.channels?.zalo?.accounts?.work?.allowFrom).toEqual(["123456789", "*"]); + }); }); diff --git a/extensions/zalo/src/setup-surface.ts b/extensions/zalo/src/setup-surface.ts index c292a15f9bd..5ce1a161fb8 100644 --- a/extensions/zalo/src/setup-surface.ts +++ b/extensions/zalo/src/setup-surface.ts @@ -1,6 +1,5 @@ import { buildSingleChannelSecretPromptState, - createTopLevelChannelDmPolicy, createStandardChannelSetupStatus, DEFAULT_ACCOUNT_ID, formatDocsLink, @@ -15,7 +14,7 @@ import { type SecretInput, } from "openclaw/plugin-sdk/setup"; import { listZaloAccountIds, resolveDefaultZaloAccountId, resolveZaloAccount } from "./accounts.js"; -import { zaloSetupAdapter } from "./setup-core.js"; +import { zaloDmPolicy, zaloSetupAdapter } from "./setup-core.js"; const channel = "zalo" as const; @@ -173,25 +172,6 @@ async function promptZaloAllowFrom(params: { } as OpenClawConfig; } -const zaloDmPolicy: ChannelSetupDmPolicy = createTopLevelChannelDmPolicy({ - label: "Zalo", - channel, - policyKey: "channels.zalo.dmPolicy", - allowFromKey: "channels.zalo.allowFrom", - getCurrent: (cfg) => (cfg.channels?.zalo?.dmPolicy ?? "pairing") as "pairing", - promptAllowFrom: async ({ cfg, prompter, accountId }) => { - const id = - accountId && normalizeAccountId(accountId) - ? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID) - : resolveDefaultZaloAccountId(cfg as OpenClawConfig); - return await promptZaloAllowFrom({ - cfg: cfg as OpenClawConfig, - prompter, - accountId: id, - }); - }, -}); - export { zaloSetupAdapter } from "./setup-core.js"; export const zaloSetupWizard: ChannelSetupWizard = {