refactor: share config adapter allowFrom and defaultTo helpers

This commit is contained in:
Peter Steinberger
2026-03-07 23:02:59 +00:00
parent feac26c3b7
commit 556aa8a702
17 changed files with 100 additions and 57 deletions

View File

@@ -2,6 +2,7 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRestrictSendersWarnings,
formatNormalizedAllowFromEntries,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import type {
ChannelAccountSnapshot,
@@ -115,9 +116,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
baseUrl: account.baseUrl,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveBlueBubblesAccount({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveBlueBubblesAccount({ cfg: cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatNormalizedAllowFromEntries({
allowFrom,

View File

@@ -2,6 +2,8 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyConfiguredRouteWarnings,
formatAllowFromLowercase,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -114,12 +116,10 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
tokenSource: account.tokenSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveDiscordAccount({ cfg, accountId }).config.dm?.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveDiscordAccount({ cfg, accountId }).config.dm?.allowFrom),
formatAllowFrom: ({ allowFrom }) => formatAllowFromLowercase({ allowFrom }),
resolveDefaultTo: ({ cfg, accountId }) =>
resolveDiscordAccount({ cfg, accountId }).config.defaultTo?.trim() || undefined,
resolveOptionalConfigString(resolveDiscordAccount({ cfg, accountId }).config.defaultTo),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -1,6 +1,7 @@
import {
collectOpenGroupPolicyRestrictSendersWarnings,
formatAllowFromLowercase,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import type { ChannelMeta, ChannelPlugin, ClawdbotConfig } from "openclaw/plugin-sdk/feishu";
import {
@@ -252,7 +253,7 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
}),
resolveAllowFrom: ({ cfg, accountId }) => {
const account = resolveFeishuAccount({ cfg, accountId });
return (account.config?.allowFrom ?? []).map((entry) => String(entry));
return mapAllowFromEntries(account.config?.allowFrom);
},
formatAllowFrom: ({ allowFrom }) => formatAllowFromLowercase({ allowFrom }),
},

View File

@@ -2,6 +2,8 @@ import {
buildAccountScopedDmSecurityPolicy,
buildOpenGroupPolicyConfigureRouteAllowlistWarning,
formatNormalizedAllowFromEntries,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -69,9 +71,7 @@ export const googlechatDock: ChannelDock = {
outbound: { textChunkLimit: 4000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveGoogleChatAccount({ cfg: cfg, accountId }).config.dm?.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveGoogleChatAccount({ cfg: cfg, accountId }).config.dm?.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatNormalizedAllowFromEntries({
allowFrom,
@@ -177,19 +177,19 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
credentialSource: account.credentialSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(
mapAllowFromEntries(
resolveGoogleChatAccount({
cfg: cfg,
accountId,
}).config.dm?.allowFrom ?? []
).map((entry) => String(entry)),
}).config.dm?.allowFrom,
),
formatAllowFrom: ({ allowFrom }) =>
formatNormalizedAllowFromEntries({
allowFrom,
normalizeEntry: formatAllowFromEntry,
}),
resolveDefaultTo: ({ cfg, accountId }) =>
resolveGoogleChatAccount({ cfg, accountId }).config.defaultTo?.trim() || undefined,
resolveOptionalConfigString(resolveGoogleChatAccount({ cfg, accountId }).config.defaultTo),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -2,6 +2,8 @@ import {
buildAccountScopedDmSecurityPolicy,
buildOpenGroupPolicyWarning,
formatNormalizedAllowFromEntries,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
buildBaseAccountStatusSnapshot,
@@ -115,8 +117,8 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = {
passwordSource: account.passwordSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom ?? []).map(
(entry) => String(entry),
mapAllowFromEntries(
resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom,
),
formatAllowFrom: ({ allowFrom }) =>
formatNormalizedAllowFromEntries({
@@ -124,8 +126,9 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = {
normalizeEntry: normalizeIrcAllowEntry,
}),
resolveDefaultTo: ({ cfg, accountId }) =>
resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }).config.defaultTo?.trim() ||
undefined,
resolveOptionalConfigString(
resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }).config.defaultTo,
),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -1,6 +1,7 @@
import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRestrictSendersWarnings,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import {
buildChannelConfigSchema,
@@ -147,10 +148,10 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
tokenSource: account.tokenSource ?? undefined,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(
mapAllowFromEntries(
getLineRuntime().channel.line.resolveLineAccount({ cfg, accountId: accountId ?? undefined })
.config.allowFrom ?? []
).map((entry) => String(entry)),
.config.allowFrom,
),
formatAllowFrom: ({ allowFrom }) =>
allowFrom
.map((entry) => String(entry).trim())

View File

@@ -1,6 +1,7 @@
import {
buildAccountScopedDmSecurityPolicy,
buildOpenGroupPolicyWarning,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -156,7 +157,7 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
}),
resolveAllowFrom: ({ cfg, accountId }) => {
const matrixConfig = resolveMatrixAccountConfig({ cfg: cfg as CoreConfig, accountId });
return (matrixConfig.dm?.allowFrom ?? []).map((entry: string | number) => String(entry));
return mapAllowFromEntries(matrixConfig.dm?.allowFrom);
},
formatAllowFrom: ({ allowFrom }) => normalizeMatrixAllowList(allowFrom),
},

View File

@@ -2,6 +2,7 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRestrictSendersWarnings,
formatNormalizedAllowFromEntries,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -276,9 +277,7 @@ export const mattermostPlugin: ChannelPlugin<ResolvedMattermostAccount> = {
baseUrl: account.baseUrl,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveMattermostAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveMattermostAccount({ cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatNormalizedAllowFromEntries({
allowFrom,

View File

@@ -2,6 +2,7 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRouteAllowlistWarnings,
formatAllowFromLowercase,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -109,9 +110,9 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
baseUrl: account.baseUrl ? "[set]" : "[missing]",
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(
resolveNextcloudTalkAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom ?? []
).map((entry) => String(entry).toLowerCase()),
mapAllowFromEntries(
resolveNextcloudTalkAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom,
).map((entry) => entry.toLowerCase()),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({
allowFrom,

View File

@@ -1,6 +1,8 @@
import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRestrictSendersWarnings,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -143,9 +145,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount> = {
baseUrl: account.baseUrl,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveSignalAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveSignalAccount({ cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
allowFrom
.map((entry) => String(entry).trim())
@@ -153,7 +153,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount> = {
.map((entry) => (entry === "*" ? "*" : normalizeE164(entry.replace(/^signal:/i, ""))))
.filter(Boolean),
resolveDefaultTo: ({ cfg, accountId }) =>
resolveSignalAccount({ cfg, accountId }).config.defaultTo?.trim() || undefined,
resolveOptionalConfigString(resolveSignalAccount({ cfg, accountId }).config.defaultTo),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -2,6 +2,8 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyConfiguredRouteWarnings,
formatAllowFromLowercase,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -166,10 +168,10 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
appTokenSource: account.appTokenSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveSlackAccount({ cfg, accountId }).dm?.allowFrom ?? []).map((entry) => String(entry)),
mapAllowFromEntries(resolveSlackAccount({ cfg, accountId }).dm?.allowFrom),
formatAllowFrom: ({ allowFrom }) => formatAllowFromLowercase({ allowFrom }),
resolveDefaultTo: ({ cfg, accountId }) =>
resolveSlackAccount({ cfg, accountId }).config.defaultTo?.trim() || undefined,
resolveOptionalConfigString(resolveSlackAccount({ cfg, accountId }).config.defaultTo),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -2,6 +2,8 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRouteAllowlistWarnings,
formatAllowFromLowercase,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
@@ -176,15 +178,11 @@ export const telegramPlugin: ChannelPlugin<ResolvedTelegramAccount, TelegramProb
tokenSource: account.tokenSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveTelegramAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveTelegramAccount({ cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(telegram|tg):/i }),
resolveDefaultTo: ({ cfg, accountId }) => {
const val = resolveTelegramAccount({ cfg, accountId }).config.defaultTo;
return val != null ? String(val) : undefined;
},
resolveDefaultTo: ({ cfg, accountId }) =>
resolveOptionalConfigString(resolveTelegramAccount({ cfg, accountId }).config.defaultTo),
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {

View File

@@ -2,6 +2,7 @@ import {
buildAccountScopedDmSecurityPolicy,
buildOpenGroupPolicyRestrictSendersWarning,
buildOpenGroupPolicyWarning,
mapAllowFromEntries,
} from "openclaw/plugin-sdk";
import type {
ChannelAccountSnapshot,
@@ -76,9 +77,7 @@ export const zaloDock: ChannelDock = {
outbound: { textChunkLimit: 2000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveZaloAccount({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveZaloAccount({ cfg: cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(zalo|zl):/i }),
},
@@ -133,9 +132,7 @@ export const zaloPlugin: ChannelPlugin<ResolvedZaloAccount> = {
tokenSource: account.tokenSource,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveZaloAccount({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveZaloAccount({ cfg: cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(zalo|zl):/i }),
},

View File

@@ -1,4 +1,4 @@
import { buildAccountScopedDmSecurityPolicy } from "openclaw/plugin-sdk";
import { buildAccountScopedDmSecurityPolicy, mapAllowFromEntries } from "openclaw/plugin-sdk";
import type {
ChannelAccountSnapshot,
ChannelDirectoryEntry,
@@ -208,9 +208,7 @@ export const zalouserDock: ChannelDock = {
outbound: { textChunkLimit: 2000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(zalouser|zlu):/i }),
},
@@ -273,9 +271,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
configured: undefined,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
String(entry),
),
mapAllowFromEntries(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom),
formatAllowFrom: ({ allowFrom }) =>
formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(zalouser|zlu):/i }),
},

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import { mapAllowFromEntries, resolveOptionalConfigString } from "./channel-config-helpers.js";
describe("mapAllowFromEntries", () => {
it("coerces allowFrom entries to strings", () => {
expect(mapAllowFromEntries(["user", 42, null])).toEqual(["user", "42", "null"]);
});
it("returns empty list for missing input", () => {
expect(mapAllowFromEntries(undefined)).toEqual([]);
});
});
describe("resolveOptionalConfigString", () => {
it("trims and returns string values", () => {
expect(resolveOptionalConfigString(" room:123 ")).toBe("room:123");
});
it("coerces numeric values", () => {
expect(resolveOptionalConfigString(123)).toBe("123");
});
it("returns undefined for empty values", () => {
expect(resolveOptionalConfigString(" ")).toBeUndefined();
expect(resolveOptionalConfigString(undefined)).toBeUndefined();
});
});

View File

@@ -4,10 +4,26 @@ import { resolveIMessageAccount } from "../imessage/accounts.js";
import { normalizeAccountId } from "../routing/session-key.js";
import { resolveWhatsAppAccount } from "../web/accounts.js";
export function mapAllowFromEntries(
allowFrom: Array<string | number> | null | undefined,
): string[] {
return (allowFrom ?? []).map((entry) => String(entry));
}
export function formatTrimmedAllowFromEntries(allowFrom: Array<string | number>): string[] {
return allowFrom.map((entry) => String(entry).trim()).filter(Boolean);
}
export function resolveOptionalConfigString(
value: string | number | null | undefined,
): string | undefined {
if (value == null) {
return undefined;
}
const normalized = String(value).trim();
return normalized || undefined;
}
export function resolveWhatsAppConfigAllowFrom(params: {
cfg: OpenClawConfig;
accountId?: string | null;
@@ -33,12 +49,12 @@ export function resolveIMessageConfigAllowFrom(params: {
cfg: OpenClawConfig;
accountId?: string | null;
}): string[] {
return (resolveIMessageAccount(params).config.allowFrom ?? []).map((entry) => String(entry));
return mapAllowFromEntries(resolveIMessageAccount(params).config.allowFrom);
}
export function resolveIMessageConfigDefaultTo(params: {
cfg: OpenClawConfig;
accountId?: string | null;
}): string | undefined {
return resolveIMessageAccount(params).config.defaultTo?.trim() || undefined;
return resolveOptionalConfigString(resolveIMessageAccount(params).config.defaultTo);
}

View File

@@ -378,6 +378,8 @@ export { SILENT_REPLY_TOKEN, isSilentReplyText } from "../auto-reply/tokens.js";
export { formatInboundFromLabel } from "../auto-reply/envelope.js";
export {
formatTrimmedAllowFromEntries,
mapAllowFromEntries,
resolveOptionalConfigString,
formatWhatsAppConfigAllowFromEntries,
resolveIMessageConfigAllowFrom,
resolveIMessageConfigDefaultTo,