From fa13c2960e84b18de3d43bef83e3fb1aeb496655 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Mar 2026 20:04:22 +0000 Subject: [PATCH] refactor: share scoped account accessor adapters --- extensions/bluebubbles/src/channel.setup.ts | 7 +++-- extensions/bluebubbles/src/channel.ts | 3 +- extensions/discord/src/shared.ts | 5 ++-- extensions/feishu/src/channel.ts | 7 +++-- extensions/googlechat/src/channel.ts | 15 ++++++---- extensions/imessage/src/shared.ts | 3 +- extensions/irc/src/channel.ts | 13 ++++----- extensions/matrix/src/channel.ts | 20 ++++++------- extensions/mattermost/src/channel.ts | 3 +- extensions/nextcloud-talk/src/channel.ts | 3 +- extensions/signal/src/shared.ts | 3 +- extensions/slack/src/channel.ts | 9 ++++-- extensions/slack/src/setup-surface.ts | 11 ++++++-- extensions/slack/src/shared.ts | 9 ++++-- extensions/telegram/src/shared.ts | 9 ++++-- extensions/whatsapp/src/directory-config.ts | 11 ++++---- extensions/whatsapp/src/shared.ts | 3 +- extensions/zalo/src/channel.ts | 7 +++-- extensions/zalouser/src/shared.ts | 7 +++-- src/plugin-sdk/channel-config-helpers.test.ts | 28 +++++++++++++++++++ src/plugin-sdk/channel-config-helpers.ts | 7 +++++ 21 files changed, 126 insertions(+), 57 deletions(-) diff --git a/extensions/bluebubbles/src/channel.setup.ts b/extensions/bluebubbles/src/channel.setup.ts index 4045b4a9ef1..3caf48436d6 100644 --- a/extensions/bluebubbles/src/channel.setup.ts +++ b/extensions/bluebubbles/src/channel.setup.ts @@ -1,5 +1,8 @@ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from"; -import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema"; import type { ChannelPlugin } from "openclaw/plugin-sdk/core"; import { @@ -30,7 +33,7 @@ const meta = { const bluebubblesConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "bluebubbles", listAccountIds: listBlueBubblesAccountIds, - resolveAccount: (cfg, accountId) => resolveBlueBubblesAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveBlueBubblesAccount), defaultAccountId: resolveDefaultBlueBubblesAccountId, clearBaseFields: ["serverUrl", "password", "name", "webhookPath"], resolveAllowFrom: (account: ResolvedBlueBubblesAccount) => account.config.allowFrom, diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts index 5719b12e22b..41fd3b62cd6 100644 --- a/extensions/bluebubbles/src/channel.ts +++ b/extensions/bluebubbles/src/channel.ts @@ -1,5 +1,6 @@ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from"; import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -57,7 +58,7 @@ const loadBlueBubblesChannelRuntime = createLazyRuntimeNamedExport( const bluebubblesConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "bluebubbles", listAccountIds: listBlueBubblesAccountIds, - resolveAccount: (cfg, accountId) => resolveBlueBubblesAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveBlueBubblesAccount), defaultAccountId: resolveDefaultBlueBubblesAccountId, clearBaseFields: ["serverUrl", "password", "name", "webhookPath"], resolveAllowFrom: (account: ResolvedBlueBubblesAccount) => account.config.allowFrom, diff --git a/extensions/discord/src/shared.ts b/extensions/discord/src/shared.ts index eadb6241899..628f37ef818 100644 --- a/extensions/discord/src/shared.ts +++ b/extensions/discord/src/shared.ts @@ -1,4 +1,5 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; +import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers"; import { createChannelPluginBase } from "openclaw/plugin-sdk/core"; import { inspectDiscordAccount } from "./account-inspect.js"; import { @@ -29,8 +30,8 @@ export const discordSetupWizard = createDiscordSetupWizardProxy( export const discordConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: DISCORD_CHANNEL, listAccountIds: listDiscordAccountIds, - resolveAccount: (cfg, accountId) => resolveDiscordAccount({ cfg, accountId }), - inspectAccount: (cfg, accountId) => inspectDiscordAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveDiscordAccount), + inspectAccount: adaptScopedAccountAccessor(inspectDiscordAccount), defaultAccountId: resolveDefaultDiscordAccountId, clearBaseFields: ["token", "name"], resolveAllowFrom: (account: ResolvedDiscordAccount) => account.config.dm?.allowFrom, diff --git a/extensions/feishu/src/channel.ts b/extensions/feishu/src/channel.ts index e475d756b5c..fc3431586a3 100644 --- a/extensions/feishu/src/channel.ts +++ b/extensions/feishu/src/channel.ts @@ -1,6 +1,9 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; import { createMessageToolCardSchema } from "openclaw/plugin-sdk/channel-actions"; -import { createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createHybridChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import type { ChannelMessageActionAdapter, ChannelMessageToolDiscovery, @@ -164,7 +167,7 @@ const feishuConfigAdapter = createHybridChannelConfigAdapter< >({ sectionKey: "feishu", listAccountIds: listFeishuAccountIds, - resolveAccount: (cfg, accountId) => resolveFeishuAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveFeishuAccount), defaultAccountId: resolveDefaultFeishuAccountId, clearBaseFields: [], resolveAllowFrom: (account) => account.config.allowFrom, diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index c4e76f1561d..5eb4a24d4e8 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -1,5 +1,8 @@ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from"; -import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import { composeWarningCollectors, createAllowlistProviderGroupPolicyWarningCollector, @@ -65,7 +68,7 @@ const formatAllowFromEntry = (entry: string) => const googleChatConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "googlechat", listAccountIds: listGoogleChatAccountIds, - resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount), defaultAccountId: resolveDefaultGoogleChatAccountId, clearBaseFields: [ "serviceAccount", @@ -166,16 +169,16 @@ export const googlechatPlugin = createChatChannelPlugin({ }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => - listResolvedDirectoryUserEntriesFromAllowFrom({ + listResolvedDirectoryUserEntriesFromAllowFrom({ ...params, - resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount), resolveAllowFrom: (account) => account.config.dm?.allowFrom, normalizeId: (entry) => normalizeGoogleChatTarget(entry) ?? entry, }), listGroups: async (params) => - listResolvedDirectoryGroupEntriesFromMapKeys({ + listResolvedDirectoryGroupEntriesFromMapKeys({ ...params, - resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount), resolveGroups: (account) => account.config.groups, }), }), diff --git a/extensions/imessage/src/shared.ts b/extensions/imessage/src/shared.ts index 41275715c36..ffaa2da6a81 100644 --- a/extensions/imessage/src/shared.ts +++ b/extensions/imessage/src/shared.ts @@ -1,4 +1,5 @@ import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, formatTrimmedAllowFromEntries, @@ -32,7 +33,7 @@ export const imessageSetupWizard = createIMessageSetupWizardProxy( export const imessageConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: IMESSAGE_CHANNEL, listAccountIds: listIMessageAccountIds, - resolveAccount: (cfg, accountId) => resolveIMessageAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveIMessageAccount), defaultAccountId: resolveDefaultIMessageAccountId, clearBaseFields: ["cliPath", "dbPath", "service", "region", "name"], resolveAllowFrom: (account: ResolvedIMessageAccount) => account.config.allowFrom, diff --git a/extensions/irc/src/channel.ts b/extensions/irc/src/channel.ts index 69fdc07a79f..f94a1a4da35 100644 --- a/extensions/irc/src/channel.ts +++ b/extensions/irc/src/channel.ts @@ -1,5 +1,6 @@ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from"; import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -64,7 +65,7 @@ const ircConfigAdapter = createScopedChannelConfigAdapter< >({ sectionKey: "irc", listAccountIds: listIrcAccountIds, - resolveAccount: (cfg, accountId) => resolveIrcAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveIrcAccount), defaultAccountId: resolveDefaultIrcAccountId, clearBaseFields: [ "name", @@ -237,11 +238,10 @@ export const ircPlugin: ChannelPlugin = { }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => - listResolvedDirectoryEntriesFromSources({ + listResolvedDirectoryEntriesFromSources({ ...params, kind: "user", - resolveAccount: (cfg, accountId) => - resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveIrcAccount), resolveSources: (account) => [ account.config.allowFrom ?? [], account.config.groupAllowFrom ?? [], @@ -250,11 +250,10 @@ export const ircPlugin: ChannelPlugin = { normalizeId: (entry) => normalizePairingTarget(entry) || null, }), listGroups: async (params) => { - const entries = listResolvedDirectoryEntriesFromSources({ + const entries = listResolvedDirectoryEntriesFromSources({ ...params, kind: "group", - resolveAccount: (cfg, accountId) => - resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveIrcAccount), resolveSources: (account) => [ account.config.channels ?? [], Object.keys(account.config.groups ?? {}), diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index 0efd344c50b..dbbb066831a 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -1,4 +1,5 @@ import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -77,7 +78,7 @@ const matrixConfigAdapter = createScopedChannelConfigAdapter< >({ sectionKey: "matrix", listAccountIds: listMatrixAccountIds, - resolveAccount: (cfg, accountId) => resolveMatrixAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), resolveAccessorAccount: ({ cfg, accountId }) => resolveMatrixAccountConfig({ cfg: cfg as CoreConfig, accountId }), defaultAccountId: resolveDefaultMatrixAccountId, @@ -238,9 +239,10 @@ export const matrixPlugin: ChannelPlugin = { resolveToolPolicy: resolveMatrixGroupToolPolicy, }, threading: { - resolveReplyToMode: createScopedAccountReplyToModeResolver({ - resolveAccount: (cfg, accountId) => - resolveMatrixAccountConfig({ cfg: cfg as CoreConfig, accountId }), + resolveReplyToMode: createScopedAccountReplyToModeResolver< + ReturnType + >({ + resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccountConfig), resolveReplyToMode: (account) => account.replyToMode, }), buildToolContext: ({ context, hasRepliedRef }) => { @@ -277,11 +279,10 @@ export const matrixPlugin: ChannelPlugin = { }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => { - const entries = listResolvedDirectoryEntriesFromSources({ + const entries = listResolvedDirectoryEntriesFromSources({ ...params, kind: "user", - resolveAccount: (cfg, accountId) => - resolveMatrixAccount({ cfg: cfg as CoreConfig, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), resolveSources: (account) => [ account.config.dm?.allowFrom ?? [], account.config.groupAllowFrom ?? [], @@ -306,11 +307,10 @@ export const matrixPlugin: ChannelPlugin = { }); }, listGroups: async (params) => - listResolvedDirectoryEntriesFromSources({ + listResolvedDirectoryEntriesFromSources({ ...params, kind: "group", - resolveAccount: (cfg, accountId) => - resolveMatrixAccount({ cfg: cfg as CoreConfig, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), resolveSources: (account) => [ Object.keys(account.config.groups ?? account.config.rooms ?? {}), ], diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index 476c2c2d19e..fe3d08d213b 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -1,6 +1,7 @@ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from"; import { createMessageToolButtonsSchema } from "openclaw/plugin-sdk/channel-actions"; import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -266,7 +267,7 @@ function formatAllowEntry(entry: string): string { const mattermostConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "mattermost", listAccountIds: listMattermostAccountIds, - resolveAccount: (cfg, accountId) => resolveMattermostAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveMattermostAccount), defaultAccountId: resolveDefaultMattermostAccountId, clearBaseFields: ["botToken", "baseUrl", "name"], resolveAllowFrom: (account: ResolvedMattermostAccount) => account.config.allowFrom, diff --git a/extensions/nextcloud-talk/src/channel.ts b/extensions/nextcloud-talk/src/channel.ts index b04ea9c8c48..b6f7263ea49 100644 --- a/extensions/nextcloud-talk/src/channel.ts +++ b/extensions/nextcloud-talk/src/channel.ts @@ -1,5 +1,6 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -59,7 +60,7 @@ const nextcloudTalkConfigAdapter = createScopedChannelConfigAdapter< >({ sectionKey: "nextcloud-talk", listAccountIds: listNextcloudTalkAccountIds, - resolveAccount: (cfg, accountId) => resolveNextcloudTalkAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveNextcloudTalkAccount), defaultAccountId: resolveDefaultNextcloudTalkAccountId, clearBaseFields: ["botSecret", "botSecretFile", "baseUrl", "name"], resolveAllowFrom: (account) => account.config.allowFrom, diff --git a/extensions/signal/src/shared.ts b/extensions/signal/src/shared.ts index c1c0e8055dc..b9533a4d6db 100644 --- a/extensions/signal/src/shared.ts +++ b/extensions/signal/src/shared.ts @@ -1,4 +1,5 @@ import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -32,7 +33,7 @@ export const signalSetupWizard = createSignalSetupWizardProxy( export const signalConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: SIGNAL_CHANNEL, listAccountIds: listSignalAccountIds, - resolveAccount: (cfg, accountId) => resolveSignalAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveSignalAccount), defaultAccountId: resolveDefaultSignalAccountId, clearBaseFields: ["account", "httpUrl", "httpHost", "httpPort", "cliPath", "name"], resolveAllowFrom: (account: ResolvedSignalAccount) => account.config.allowFrom, diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 53ab6cd21d1..a32cd1e2e4d 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -3,7 +3,10 @@ import { createAccountScopedAllowlistNameResolver, createFlatAllowlistOverrideResolver, } from "openclaw/plugin-sdk/allowlist-config-edit"; -import { createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedDmSecurityResolver, +} from "openclaw/plugin-sdk/channel-config-helpers"; import { createPairingPrefixStripper, createTextPairingAdapter, @@ -389,8 +392,8 @@ export const slackPlugin: ChannelPlugin = { resolveToolPolicy: resolveSlackGroupToolPolicy, }, threading: { - resolveReplyToMode: createScopedAccountReplyToModeResolver({ - resolveAccount: (cfg, accountId) => resolveSlackAccount({ cfg, accountId }), + resolveReplyToMode: createScopedAccountReplyToModeResolver({ + resolveAccount: adaptScopedAccountAccessor(resolveSlackAccount), resolveReplyToMode: (account, chatType) => resolveSlackReplyToMode(account, chatType), }), allowExplicitReplyTagsWhenOff: false, diff --git a/extensions/slack/src/setup-surface.ts b/extensions/slack/src/setup-surface.ts index 3f3e17301f5..ed66533031f 100644 --- a/extensions/slack/src/setup-surface.ts +++ b/extensions/slack/src/setup-surface.ts @@ -1,3 +1,4 @@ +import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers"; import { noteChannelLookupFailure, noteChannelLookupSummary, @@ -12,7 +13,11 @@ import type { ChannelSetupWizardAllowFromEntry, } from "openclaw/plugin-sdk/setup"; import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools"; -import { resolveDefaultSlackAccountId, resolveSlackAccount } from "./accounts.js"; +import { + resolveDefaultSlackAccountId, + resolveSlackAccount, + type ResolvedSlackAccount, +} from "./accounts.js"; import { resolveSlackChannelAllowlist } from "./resolve-channels.js"; import { resolveSlackUserAllowlist } from "./resolve-users.js"; import { createSlackSetupWizardBase } from "./setup-core.js"; @@ -58,13 +63,13 @@ async function promptSlackAllowFrom(params: { normalizeId: (id) => id.toUpperCase(), }); - return await promptLegacyChannelAllowFromForAccount({ + return await promptLegacyChannelAllowFromForAccount({ cfg: params.cfg, channel, prompter: params.prompter, accountId: params.accountId, defaultAccountId: resolveDefaultSlackAccountId(params.cfg), - resolveAccount: (cfg, accountId) => resolveSlackAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveSlackAccount), resolveExisting: (_account, cfg) => cfg.channels?.slack?.allowFrom ?? cfg.channels?.slack?.dm?.allowFrom ?? [], resolveToken: (account) => account.userToken ?? account.botToken ?? "", diff --git a/extensions/slack/src/shared.ts b/extensions/slack/src/shared.ts index 0d7e72a30e1..aa12014aaba 100644 --- a/extensions/slack/src/shared.ts +++ b/extensions/slack/src/shared.ts @@ -1,5 +1,8 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; -import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import { createChannelPluginBase } from "openclaw/plugin-sdk/core"; import { formatDocsLink, @@ -145,8 +148,8 @@ export function isSlackSetupAccountConfigured(account: ResolvedSlackAccount): bo export const slackConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: SLACK_CHANNEL, listAccountIds: listSlackAccountIds, - resolveAccount: (cfg, accountId) => resolveSlackAccount({ cfg, accountId }), - inspectAccount: (cfg, accountId) => inspectSlackAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveSlackAccount), + inspectAccount: adaptScopedAccountAccessor(inspectSlackAccount), defaultAccountId: resolveDefaultSlackAccountId, clearBaseFields: ["botToken", "appToken", "name"], resolveAllowFrom: (account: ResolvedSlackAccount) => account.dm?.allowFrom, diff --git a/extensions/telegram/src/shared.ts b/extensions/telegram/src/shared.ts index 7c3e873f0ff..2e97b481cb5 100644 --- a/extensions/telegram/src/shared.ts +++ b/extensions/telegram/src/shared.ts @@ -1,5 +1,8 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from"; -import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import { createChannelPluginBase } from "openclaw/plugin-sdk/core"; import { buildChannelConfigSchema, @@ -56,8 +59,8 @@ export function formatDuplicateTelegramTokenReason(params: { export const telegramConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: TELEGRAM_CHANNEL, listAccountIds: listTelegramAccountIds, - resolveAccount: (cfg, accountId) => resolveTelegramAccount({ cfg, accountId }), - inspectAccount: (cfg, accountId) => inspectTelegramAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveTelegramAccount), + inspectAccount: adaptScopedAccountAccessor(inspectTelegramAccount), defaultAccountId: resolveDefaultTelegramAccountId, clearBaseFields: ["botToken", "tokenFile", "name"], resolveAllowFrom: (account: ResolvedTelegramAccount) => account.config.allowFrom, diff --git a/extensions/whatsapp/src/directory-config.ts b/extensions/whatsapp/src/directory-config.ts index 1915b6fd4da..86939e7901c 100644 --- a/extensions/whatsapp/src/directory-config.ts +++ b/extensions/whatsapp/src/directory-config.ts @@ -1,15 +1,16 @@ +import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers"; import { listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom, type DirectoryConfigParams, } from "openclaw/plugin-sdk/directory-runtime"; -import { resolveWhatsAppAccount } from "./accounts.js"; +import { resolveWhatsAppAccount, type ResolvedWhatsAppAccount } from "./accounts.js"; import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "./normalize.js"; export async function listWhatsAppDirectoryPeersFromConfig(params: DirectoryConfigParams) { - return listResolvedDirectoryUserEntriesFromAllowFrom({ + return listResolvedDirectoryUserEntriesFromAllowFrom({ ...params, - resolveAccount: (cfg, accountId) => resolveWhatsAppAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveWhatsAppAccount), resolveAllowFrom: (account) => account.allowFrom, normalizeId: (entry) => { const normalized = normalizeWhatsAppTarget(entry); @@ -22,9 +23,9 @@ export async function listWhatsAppDirectoryPeersFromConfig(params: DirectoryConf } export async function listWhatsAppDirectoryGroupsFromConfig(params: DirectoryConfigParams) { - return listResolvedDirectoryGroupEntriesFromMapKeys({ + return listResolvedDirectoryGroupEntriesFromMapKeys({ ...params, - resolveAccount: (cfg, accountId) => resolveWhatsAppAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveWhatsAppAccount), resolveGroups: (account) => account.groups, }); } diff --git a/extensions/whatsapp/src/shared.ts b/extensions/whatsapp/src/shared.ts index fcc5bb92421..c125e597327 100644 --- a/extensions/whatsapp/src/shared.ts +++ b/extensions/whatsapp/src/shared.ts @@ -1,4 +1,5 @@ import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; @@ -36,7 +37,7 @@ export const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy( const whatsappConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: WHATSAPP_CHANNEL, listAccountIds: listWhatsAppAccountIds, - resolveAccount: (cfg, accountId) => resolveWhatsAppAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveWhatsAppAccount), defaultAccountId: resolveDefaultWhatsAppAccountId, clearBaseFields: [], allowTopLevel: false, diff --git a/extensions/zalo/src/channel.ts b/extensions/zalo/src/channel.ts index bcbaf6b3c2a..a5d0ea734a6 100644 --- a/extensions/zalo/src/channel.ts +++ b/extensions/zalo/src/channel.ts @@ -1,4 +1,5 @@ import { + adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, mapAllowFromEntries, @@ -68,7 +69,7 @@ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.r const zaloConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "zalo", listAccountIds: listZaloAccountIds, - resolveAccount: (cfg, accountId) => resolveZaloAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveZaloAccount), defaultAccountId: resolveDefaultZaloAccountId, clearBaseFields: ["botToken", "tokenFile", "name"], resolveAllowFrom: (account: ResolvedZaloAccount) => account.config.allowFrom, @@ -167,9 +168,9 @@ export const zaloPlugin: ChannelPlugin = { }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => - listResolvedDirectoryUserEntriesFromAllowFrom({ + listResolvedDirectoryUserEntriesFromAllowFrom({ ...params, - resolveAccount: (cfg, accountId) => resolveZaloAccount({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveZaloAccount), resolveAllowFrom: (account) => account.config.allowFrom, normalizeId: (entry) => entry.trim().replace(/^(zalo|zl):/i, ""), }), diff --git a/extensions/zalouser/src/shared.ts b/extensions/zalouser/src/shared.ts index 4d4e7f1dff2..153f642dac1 100644 --- a/extensions/zalouser/src/shared.ts +++ b/extensions/zalouser/src/shared.ts @@ -1,4 +1,7 @@ -import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; import type { ChannelPlugin } from "../runtime-api.js"; import { buildChannelConfigSchema, formatAllowFromLowercase } from "../runtime-api.js"; import { @@ -25,7 +28,7 @@ export const zalouserMeta = { const zalouserConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "zalouser", listAccountIds: listZalouserAccountIds, - resolveAccount: (cfg, accountId) => resolveZalouserAccountSync({ cfg, accountId }), + resolveAccount: adaptScopedAccountAccessor(resolveZalouserAccountSync), defaultAccountId: resolveDefaultZalouserAccountId, clearBaseFields: [ "profile", diff --git a/src/plugin-sdk/channel-config-helpers.test.ts b/src/plugin-sdk/channel-config-helpers.test.ts index 296b8bf9a8e..0a63f2ad105 100644 --- a/src/plugin-sdk/channel-config-helpers.test.ts +++ b/src/plugin-sdk/channel-config-helpers.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + adaptScopedAccountAccessor, createScopedAccountConfigAccessors, createScopedChannelConfigAdapter, createScopedChannelConfigBase, @@ -37,6 +38,33 @@ describe("resolveOptionalConfigString", () => { }); }); +describe("adaptScopedAccountAccessor", () => { + it("binds positional callback args into the shared account context object", () => { + const accessor = adaptScopedAccountAccessor(({ cfg, accountId }) => ({ + channel: cfg.channels?.demo, + accountId: accountId ?? "default", + })); + + expect( + accessor( + { + channels: { + demo: { + enabled: true, + }, + }, + }, + "alt", + ), + ).toEqual({ + channel: { + enabled: true, + }, + accountId: "alt", + }); + }); +}); + describe("createScopedAccountConfigAccessors", () => { it("maps allowFrom and defaultTo from the resolved account", () => { const accessors = createScopedAccountConfigAccessors({ diff --git a/src/plugin-sdk/channel-config-helpers.ts b/src/plugin-sdk/channel-config-helpers.ts index 18fb609de31..9571bd15ee6 100644 --- a/src/plugin-sdk/channel-config-helpers.ts +++ b/src/plugin-sdk/channel-config-helpers.ts @@ -57,6 +57,13 @@ export function resolveOptionalConfigString( return normalized || undefined; } +/** Adapt `{ cfg, accountId }` accessors to callback sites that pass positional args. */ +export function adaptScopedAccountAccessor( + accessor: (params: { cfg: Config; accountId?: string | null }) => Result, +): (cfg: Config, accountId?: string | null) => Result { + return (cfg, accountId) => accessor({ cfg, accountId }); +} + /** Build the shared allowlist/default target adapter surface for account-scoped channel configs. */ export function createScopedAccountConfigAccessors< ResolvedAccount,