style(extensions): format channel integration updates

This commit is contained in:
Peter Steinberger
2026-04-04 06:02:37 +01:00
parent 8b5672bda4
commit fd75d214f2
33 changed files with 124 additions and 121 deletions

View File

@@ -21,8 +21,8 @@ export function setBlueBubblesDmPolicy(
const existingAllowFrom =
resolvedAccountId === "default"
? cfg.channels?.bluebubbles?.allowFrom
: cfg.channels?.bluebubbles?.accounts?.[resolvedAccountId]?.allowFrom ??
cfg.channels?.bluebubbles?.allowFrom;
: (cfg.channels?.bluebubbles?.accounts?.[resolvedAccountId]?.allowFrom ??
cfg.channels?.bluebubbles?.allowFrom);
return patchScopedAccountConfig({
cfg,
channelKey: channel,

View File

@@ -149,14 +149,9 @@ const dmPolicy: ChannelSetupDmPolicy = {
resolveBlueBubblesAccount({
cfg,
accountId: accountId ?? resolveDefaultBlueBubblesAccountId(cfg),
}).config
.dmPolicy ?? "pairing",
}).config.dmPolicy ?? "pairing",
setPolicy: (cfg, policy, accountId) =>
setBlueBubblesDmPolicy(
cfg,
accountId ?? resolveDefaultBlueBubblesAccountId(cfg),
policy,
),
setBlueBubblesDmPolicy(cfg, accountId ?? resolveDefaultBlueBubblesAccountId(cfg), policy),
promptAllowFrom: promptBlueBubblesAllowFrom,
};

View File

@@ -108,7 +108,8 @@ export async function handleDiscordGuildAction(
const userId = readStringParam(params, "userId", {
required: true,
});
const effectiveAccountId = accountId ?? (cfg ? resolveDefaultDiscordAccountId(cfg) : undefined);
const effectiveAccountId =
accountId ?? (cfg ? resolveDefaultDiscordAccountId(cfg) : undefined);
const member = effectiveAccountId
? await discordGuildActionRuntime.fetchMemberInfoDiscord(guildId, userId, {
accountId: effectiveAccountId,

View File

@@ -499,7 +499,9 @@ describe("handleDiscordGuildAction", () => {
client_status: {},
} as never);
discordGuildActionRuntime.fetchMemberInfoDiscord = vi.fn(async () => ({ user: { id: "U1" } })) as never;
discordGuildActionRuntime.fetchMemberInfoDiscord = vi.fn(async () => ({
user: { id: "U1" },
})) as never;
const result = await handleDiscordGuildAction(
"memberInfo",

View File

@@ -90,9 +90,7 @@ describe("discordMessageActions", () => {
accountId: "work",
});
expect(defaultDiscovery?.actions).toEqual(
expect.arrayContaining(["send", "poll"]),
);
expect(defaultDiscovery?.actions).toEqual(expect.arrayContaining(["send", "poll"]));
expect(defaultDiscovery?.actions).not.toContain("react");
expect(workDiscovery?.actions).toEqual(
expect.arrayContaining(["send", "react", "reactions", "emoji-list"]),

View File

@@ -80,10 +80,7 @@ function resolveDiscordPolicyContext(params: ChannelGroupContext) {
(params.accountId
? params.cfg.channels?.discord?.accounts?.[params.accountId]?.guilds
: undefined) ?? params.cfg.channels?.discord?.guilds;
const guildEntry = resolveDiscordGuildEntry(
guilds,
params.groupSpace,
);
const guildEntry = resolveDiscordGuildEntry(guilds, params.groupSpace);
const channelEntries = guildEntry?.channels;
const channelEntry =
channelEntries && Object.keys(channelEntries).length > 0

View File

@@ -22,6 +22,7 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
import { getChildLogger } from "openclaw/plugin-sdk/runtime-env";
import { logDebug } from "openclaw/plugin-sdk/text-runtime";
import { resolveDefaultDiscordAccountId } from "../accounts.js";
import {
isDiscordGroupAllowedByPolicy,
normalizeDiscordSlug,
@@ -34,7 +35,6 @@ import {
} from "./allow-list.js";
import { resolveDiscordDmCommandAccess } from "./dm-command-auth.js";
import { handleDiscordDmCommandDecision } from "./dm-command-decision.js";
import { resolveDefaultDiscordAccountId } from "../accounts.js";
import {
formatDiscordUserTag,
resolveDiscordSystemLocation,

View File

@@ -36,7 +36,10 @@ function resolveImplicitToolAccountId(params: {
}
const contextualAccountId = normalizeOptionalAccountId(params.defaultAccountId);
if (contextualAccountId && listFeishuAccountIds(params.api.config).includes(contextualAccountId)) {
if (
contextualAccountId &&
listFeishuAccountIds(params.api.config).includes(contextualAccountId)
) {
const contextualAccount = resolveFeishuAccount({
cfg: params.api.config,
accountId: contextualAccountId,

View File

@@ -82,9 +82,7 @@ const googlechatDmPolicy: ChannelSetupDmPolicy = {
dm: {
...currentDm,
policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(currentDm?.allowFrom) }
: {}),
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(currentDm?.allowFrom) } : {}),
},
},
});

View File

@@ -328,10 +328,7 @@ describe("googlechat setup", () => {
expect(next?.channels?.googlechat?.dm?.policy).toBeUndefined();
expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.policy).toBe("open");
expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.allowFrom).toEqual([
"users/123",
"*",
]);
expect(next?.channels?.googlechat?.accounts?.alerts?.dm?.allowFrom).toEqual(["users/123", "*"]);
});
it("keeps startAccount pending until abort, then unregisters", async () => {

View File

@@ -94,9 +94,7 @@ export function resolveLineAccount(params: {
accountId?: string;
}): ResolvedLineAccount {
const cfg = params.cfg;
const accountId = normalizeSharedAccountId(
params.accountId ?? resolveDefaultLineAccountId(cfg),
);
const accountId = normalizeSharedAccountId(params.accountId ?? resolveDefaultLineAccountId(cfg));
const lineConfig = cfg.channels?.line as LineConfig | undefined;
const accounts = lineConfig?.accounts;
const accountConfig =

View File

@@ -19,6 +19,7 @@ import {
beginWebhookRequestPipelineOrReject,
createWebhookInFlightLimiter,
} from "openclaw/plugin-sdk/webhook-request-guards";
import { resolveDefaultLineAccountId } from "./accounts.js";
import { deliverLineAutoReply } from "./auto-reply-delivery.js";
import { createLineBot } from "./bot.js";
import { processLineMessage } from "./markdown-to-line.js";
@@ -37,7 +38,6 @@ import {
showLoadingAnimation,
} from "./send.js";
import { buildTemplateMessageFromPayload } from "./template-messages.js";
import { resolveDefaultLineAccountId } from "./accounts.js";
import type { LineChannelData, ResolvedLineAccount } from "./types.js";
import { createLineNodeWebhookHandler } from "./webhook-node.js";

View File

@@ -1124,10 +1124,7 @@ describe("resolveMatrixAuth", () => {
env: {} as NodeJS.ProcessEnv,
});
expect(matrixDoRequestMock).toHaveBeenCalledWith(
"GET",
"/_matrix/client/v3/account/whoami",
);
expect(matrixDoRequestMock).toHaveBeenCalledWith("GET", "/_matrix/client/v3/account/whoami");
expect(auth).toMatchObject({
accountId: "default",
homeserver: "https://matrix.example.org",

View File

@@ -192,9 +192,7 @@ export const nextcloudTalkDmPolicy: ChannelSetupDmPolicy = {
});
return setNextcloudTalkAccountConfig(cfg as CoreConfig, resolvedAccountId, {
dmPolicy: policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) }
: {}),
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } : {}),
});
},
promptAllowFrom: promptNextcloudTalkAllowFromForAccount,

View File

@@ -5,11 +5,7 @@ import { validateJsonSchemaValue } from "../../../src/plugins/schema-validator.j
import { qqbotPlugin } from "./channel.js";
import { qqbotSetupPlugin } from "./channel.setup.js";
import { QQBotConfigSchema } from "./config-schema.js";
import {
DEFAULT_ACCOUNT_ID,
resolveDefaultQQBotAccountId,
resolveQQBotAccount,
} from "./config.js";
import { DEFAULT_ACCOUNT_ID, resolveDefaultQQBotAccountId, resolveQQBotAccount } from "./config.js";
describe("qqbot config", () => {
it("accepts top-level speech overrides in the manifest schema", () => {

View File

@@ -52,9 +52,9 @@ describe("signalMessageActions", () => {
it("honors account-scoped reaction gates during discovery", () => {
const cfg = createSignalAccountOverrideCfg();
expect(signalMessageActions.describeMessageTool?.({ cfg, accountId: "default" })?.actions).toEqual(
["send"],
);
expect(
signalMessageActions.describeMessageTool?.({ cfg, accountId: "default" })?.actions,
).toEqual(["send"]);
expect(signalMessageActions.describeMessageTool?.({ cfg, accountId: "work" })?.actions).toEqual(
["send", "react"],
);

View File

@@ -140,7 +140,11 @@ export const signalDmPolicy = {
getCurrent: (cfg: OpenClawConfig, accountId?: string) =>
resolveSignalAccount({ cfg, accountId: accountId ?? resolveDefaultSignalAccountId(cfg) }).config
.dmPolicy ?? "pairing",
setPolicy: (cfg: OpenClawConfig, policy: "pairing" | "allowlist" | "open" | "disabled", accountId?: string) =>
setPolicy: (
cfg: OpenClawConfig,
policy: "pairing" | "allowlist" | "open" | "disabled",
accountId?: string,
) =>
patchChannelConfigForAccount({
cfg,
channel,

View File

@@ -1,8 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
__resetSlackChannelTypeCacheForTest,
resolveSlackChannelType,
} from "./channel-type.js";
import { __resetSlackChannelTypeCacheForTest, resolveSlackChannelType } from "./channel-type.js";
const conversationsInfoMock = vi.fn();

View File

@@ -8,9 +8,8 @@ export function listSlackMessageActions(
cfg: OpenClawConfig,
accountId?: string | null,
): ChannelMessageActionName[] {
const accounts = (accountId
? [resolveSlackAccount({ cfg, accountId })]
: listEnabledSlackAccounts(cfg)
const accounts = (
accountId ? [resolveSlackAccount({ cfg, accountId })] : listEnabledSlackAccounts(cfg)
).filter((account) => account.enabled && account.botTokenSource !== "none");
if (accounts.length === 0) {
return [];

View File

@@ -166,8 +166,7 @@ export function createSlackSetupWizardBase(handlers: {
unconfiguredHint: "needs tokens",
configuredScore: 2,
unconfiguredScore: 1,
resolveConfigured: ({ cfg, accountId }) =>
inspectSlackAccount({ cfg, accountId }).configured,
resolveConfigured: ({ cfg, accountId }) => inspectSlackAccount({ cfg, accountId }).configured,
}),
introNote: {
title: "Slack socket mode tokens",

View File

@@ -45,6 +45,7 @@ import {
resolveDefaultAgentId,
resolveDefaultModelForAgent,
} from "./bot-handlers.agent.runtime.js";
import { buildTelegramInboundDebounceKey } from "./bot-handlers.debounce-key.js";
import {
hasInboundMedia,
hasReplyTargetMedia,
@@ -101,7 +102,6 @@ import {
resolveModelSelection,
type ProviderInfo,
} from "./model-buttons.js";
import { buildTelegramInboundDebounceKey } from "./bot-handlers.debounce-key.js";
import { buildInlineKeyboard } from "./send.js";
export const registerTelegramHandlers = ({

View File

@@ -32,7 +32,9 @@ const loadTlonChannelRuntime = createLazyRuntimeModule(() => import("./channel.r
const tlonSetupWizardProxy = createTlonSetupWizardBase({
resolveConfigured: async ({ cfg, accountId }) =>
await (await loadTlonChannelRuntime()).tlonSetupWizard.status.resolveConfigured({
await (
await loadTlonChannelRuntime()
).tlonSetupWizard.status.resolveConfigured({
cfg,
accountId,
}),

View File

@@ -135,7 +135,9 @@ export async function resolveTlonSetupConfigured(
}
const accountIds = listTlonAccountIds(cfg);
return accountIds.length > 0
? accountIds.some((resolvedAccountId) => isConfigured(resolveTlonAccount(cfg, resolvedAccountId)))
? accountIds.some((resolvedAccountId) =>
isConfigured(resolveTlonAccount(cfg, resolvedAccountId)),
)
: isConfigured(resolveTlonAccount(cfg, DEFAULT_ACCOUNT_ID));
}

View File

@@ -1,7 +1,7 @@
import { describe, expect, it, vi, beforeEach } from "vitest";
import { twitchMessageActions } from "./actions.js";
import { twitchOutbound } from "./outbound.js";
import { resolveTwitchAccountContext } from "./config.js";
import { twitchOutbound } from "./outbound.js";
vi.mock("./config.js", () => ({
DEFAULT_ACCOUNT_ID: "default",

View File

@@ -10,11 +10,7 @@ import {
type OpenClawConfig,
type WizardPrompter,
} from "openclaw/plugin-sdk/setup";
import {
DEFAULT_ACCOUNT_ID,
getAccountConfig,
resolveDefaultTwitchAccountId,
} from "./config.js";
import { DEFAULT_ACCOUNT_ID, getAccountConfig, resolveDefaultTwitchAccountId } from "./config.js";
import type { TwitchAccountConfig, TwitchRole } from "./types.js";
import { isAccountConfigured } from "./utils/twitch.js";
@@ -233,11 +229,15 @@ function setTwitchAccessControl(
return cfg;
}
return setTwitchAccount(cfg, {
...account,
allowedRoles,
requireMention,
}, accountId);
return setTwitchAccount(
cfg,
{
...account,
allowedRoles,
requireMention,
},
accountId,
);
}
function resolveTwitchGroupPolicy(cfg: OpenClawConfig): "open" | "allowlist" | "disabled" {
@@ -266,7 +266,10 @@ const twitchDmPolicy: ChannelSetupDmPolicy = {
policyKey: "channels.twitch.allowedRoles",
allowFromKey: "channels.twitch.accounts.<default>.allowFrom",
getCurrent: (cfg) => {
const account = getAccountConfig(cfg as OpenClawConfig, resolveSetupAccountId(cfg as OpenClawConfig));
const account = getAccountConfig(
cfg as OpenClawConfig,
resolveSetupAccountId(cfg as OpenClawConfig),
);
if (account?.allowedRoles?.includes("all")) {
return "open";
}
@@ -296,10 +299,14 @@ const twitchDmPolicy: ChannelSetupDmPolicy = {
.map((s) => s.trim())
.filter(Boolean);
return setTwitchAccount(cfg as OpenClawConfig, {
...(account ?? undefined),
allowFrom,
}, accountId);
return setTwitchAccount(
cfg as OpenClawConfig,
{
...(account ?? undefined),
allowFrom,
},
accountId,
);
},
};
@@ -309,11 +316,17 @@ const twitchGroupAccess: NonNullable<ChannelSetupWizard["groupAccess"]> = {
skipAllowlistEntries: true,
currentPolicy: ({ cfg }) => resolveTwitchGroupPolicy(cfg as OpenClawConfig),
currentEntries: ({ cfg }) => {
const account = getAccountConfig(cfg as OpenClawConfig, resolveSetupAccountId(cfg as OpenClawConfig));
const account = getAccountConfig(
cfg as OpenClawConfig,
resolveSetupAccountId(cfg as OpenClawConfig),
);
return account?.allowFrom ?? [];
},
updatePrompt: ({ cfg }) => {
const account = getAccountConfig(cfg as OpenClawConfig, resolveSetupAccountId(cfg as OpenClawConfig));
const account = getAccountConfig(
cfg as OpenClawConfig,
resolveSetupAccountId(cfg as OpenClawConfig),
);
return Boolean(account?.allowedRoles?.length || account?.allowFrom?.length);
},
setPolicy: ({ cfg, policy }) => setTwitchGroupPolicy(cfg as OpenClawConfig, policy),
@@ -324,9 +337,13 @@ const twitchGroupAccess: NonNullable<ChannelSetupWizard["groupAccess"]> = {
export const twitchSetupAdapter: ChannelSetupAdapter = {
resolveAccountId: ({ cfg }) => resolveSetupAccountId(cfg as OpenClawConfig),
applyAccountConfig: ({ cfg, accountId }) =>
setTwitchAccount(cfg, {
enabled: true,
}, accountId),
setTwitchAccount(
cfg,
{
enabled: true,
},
accountId,
),
};
export const twitchSetupWizard: ChannelSetupWizard = {
@@ -339,7 +356,10 @@ export const twitchSetupWizard: ChannelSetupWizard = {
configuredHint: "configured",
unconfiguredHint: "needs setup",
resolveConfigured: ({ cfg }) => {
const account = getAccountConfig(cfg as OpenClawConfig, resolveSetupAccountId(cfg as OpenClawConfig));
const account = getAccountConfig(
cfg as OpenClawConfig,
resolveSetupAccountId(cfg as OpenClawConfig),
);
return account ? isAccountConfigured(account) : false;
},
resolveStatusLines: ({ cfg }) => {
@@ -382,15 +402,19 @@ export const twitchSetupWizard: ChannelSetupWizard = {
const channelName = await promptChannelName(prompter, account);
const { clientSecret, refreshToken } = await promptRefreshTokenSetup(prompter, account);
const cfgWithAccount = setTwitchAccount(cfg as OpenClawConfig, {
username,
accessToken: token,
clientId,
channel: channelName,
clientSecret,
refreshToken,
enabled: true,
}, accountId);
const cfgWithAccount = setTwitchAccount(
cfg as OpenClawConfig,
{
username,
accessToken: token,
clientId,
channel: channelName,
clientSecret,
refreshToken,
enabled: true,
},
accountId,
);
const cfgWithAllowFrom =
forceAllowFrom && twitchDmPolicy.promptAllowFrom

View File

@@ -2,8 +2,8 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createQueuedWizardPrompter } from "../../../test/helpers/plugins/setup-wizard.js";
import type { OpenClawConfig } from "./runtime-api.js";
import { whatsappPlugin } from "./channel.js";
import type { OpenClawConfig } from "./runtime-api.js";
import { finalizeWhatsAppSetup } from "./setup-finalize.js";
const hoisted = vi.hoisted(() => ({

View File

@@ -167,11 +167,7 @@ describe("resolveWhatsAppHeartbeatRecipients", () => {
{ accountId: "work" },
);
expect(readChannelAllowFromStoreSyncMock).toHaveBeenCalledWith(
"whatsapp",
process.env,
"work",
);
expect(readChannelAllowFromStoreSyncMock).toHaveBeenCalledWith("whatsapp", process.env, "work");
expect(result).toEqual({
recipients: ["+15550000003", "+15550000002"],
source: "allowFrom",
@@ -198,11 +194,7 @@ describe("resolveWhatsAppHeartbeatRecipients", () => {
},
});
expect(readChannelAllowFromStoreSyncMock).toHaveBeenCalledWith(
"whatsapp",
process.env,
"work",
);
expect(readChannelAllowFromStoreSyncMock).toHaveBeenCalledWith("whatsapp", process.env, "work");
expect(result).toEqual({
recipients: ["+15550000003", "+15550000002"],
source: "allowFrom",

View File

@@ -1,3 +1,4 @@
import { resolveDefaultWhatsAppAccountId, resolveWhatsAppAccount } from "./accounts.js";
import {
DEFAULT_ACCOUNT_ID,
loadSessionStore,
@@ -7,7 +8,6 @@ import {
resolveStorePath,
type OpenClawConfig,
} from "./heartbeat-recipients.runtime.js";
import { resolveDefaultWhatsAppAccountId, resolveWhatsAppAccount } from "./accounts.js";
type HeartbeatRecipientsResult = { recipients: string[]; source: string };
type HeartbeatRecipientsOpts = { to?: string; all?: boolean; accountId?: string };
@@ -58,10 +58,11 @@ export function resolveWhatsAppHeartbeatRecipients(
const sessionRecipients = getSessionRecipients(cfg);
const resolvedAccountId =
opts.accountId?.trim() || resolveDefaultWhatsAppAccountId(cfg) || DEFAULT_ACCOUNT_ID;
const configuredAllowFrom =
(resolveWhatsAppAccount({ cfg, accountId: resolvedAccountId }).allowFrom ?? [])
.filter((value) => value !== "*")
.map(normalizeE164);
const configuredAllowFrom = (
resolveWhatsAppAccount({ cfg, accountId: resolvedAccountId }).allowFrom ?? []
)
.filter((value) => value !== "*")
.map(normalizeE164);
const storeAllowFrom = readChannelAllowFromStoreSync(
"whatsapp",
process.env,

View File

@@ -57,9 +57,8 @@ export async function sendMessageWhatsApp(
cfg,
accountId: options.accountId,
});
const { listener: active, accountId: resolvedAccountId } = requireActiveWebListener(
effectiveAccountId,
);
const { listener: active, accountId: resolvedAccountId } =
requireActiveWebListener(effectiveAccountId);
const account = resolveWhatsAppAccount({
cfg,
accountId: resolvedAccountId ?? options.accountId,

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "./runtime-api.js";
import { zaloMessageActions } from "./actions.js";
import type { OpenClawConfig } from "./runtime-api.js";
describe("zaloMessageActions.describeMessageTool", () => {
it("honors the selected Zalo account during discovery", () => {

View File

@@ -82,7 +82,14 @@ function buildReplayEventCacheKey(
): string {
const chatId = update.message?.chat?.id ?? "";
const senderId = update.message?.from?.id ?? "";
return JSON.stringify([target.path, target.account.accountId, update.event_name, chatId, senderId, messageId]);
return JSON.stringify([
target.path,
target.account.accountId,
update.event_name,
chatId,
senderId,
messageId,
]);
}
function isReplayEvent(target: ZaloWebhookTarget, update: ZaloUpdate, nowMs: number): boolean {

View File

@@ -8,8 +8,8 @@ import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "openclaw/plugin-sdk/setup";
import type { OpenClawConfig } from "./runtime-api.js";
import { resolveDefaultZaloAccountId, resolveZaloAccount } from "./accounts.js";
import type { OpenClawConfig } from "./runtime-api.js";
const channel = "zalo" as const;

View File

@@ -69,15 +69,11 @@ function setZalouserDmPolicy(
resolvedAccountId,
{
dmPolicy: policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) }
: {}),
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } : {}),
},
{
dmPolicy: policy,
...(policy === "open"
? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) }
: {}),
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } : {}),
},
);
}
@@ -318,9 +314,10 @@ export const zalouserSetupWizard: ChannelSetupWizard = {
},
resolveStatusLines: async ({ cfg, accountId, configured }) => {
void cfg;
const label = accountId && accountId !== DEFAULT_ACCOUNT_ID
? `Zalo Personal (${accountId})`
: "Zalo Personal";
const label =
accountId && accountId !== DEFAULT_ACCOUNT_ID
? `Zalo Personal (${accountId})`
: "Zalo Personal";
return [`${label}: ${configured ? "logged in" : "needs QR login"}`];
},
},