From a8f433d6110eba891d4e053e618f5f09435719dd Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Wed, 18 Mar 2026 03:40:42 +0000 Subject: [PATCH] BlueBubbles: move group policy behind plugin boundary --- extensions/bluebubbles/src/channel.ts | 6 ++- .../bluebubbles/src/group-policy.test.ts | 36 +++++++++++++++++ extensions/bluebubbles/src/group-policy.ts | 40 +++++++++++++++++++ src/plugin-sdk/bluebubbles.ts | 2 +- src/plugin-sdk/compat.ts | 2 +- 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 extensions/bluebubbles/src/group-policy.test.ts create mode 100644 extensions/bluebubbles/src/group-policy.ts diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts index c9ab4c7dc47..25d47aafd93 100644 --- a/extensions/bluebubbles/src/channel.ts +++ b/extensions/bluebubbles/src/channel.ts @@ -16,6 +16,10 @@ import { import { bluebubblesMessageActions } from "./actions.js"; import type { BlueBubblesProbe } from "./channel.runtime.js"; import { BlueBubblesConfigSchema } from "./config-schema.js"; +import { + resolveBlueBubblesGroupRequireMention, + resolveBlueBubblesGroupToolPolicy, +} from "./group-policy.js"; import type { ChannelAccountSnapshot, ChannelPlugin } from "./runtime-api.js"; import { buildChannelConfigSchema, @@ -24,8 +28,6 @@ import { collectBlueBubblesStatusIssues, DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, - resolveBlueBubblesGroupRequireMention, - resolveBlueBubblesGroupToolPolicy, } from "./runtime-api.js"; import { blueBubblesSetupAdapter } from "./setup-core.js"; import { blueBubblesSetupWizard } from "./setup-surface.js"; diff --git a/extensions/bluebubbles/src/group-policy.test.ts b/extensions/bluebubbles/src/group-policy.test.ts new file mode 100644 index 00000000000..883f6c78b71 --- /dev/null +++ b/extensions/bluebubbles/src/group-policy.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "vitest"; +import { + resolveBlueBubblesGroupRequireMention, + resolveBlueBubblesGroupToolPolicy, +} from "./group-policy.js"; + +describe("bluebubbles group policy", () => { + it("uses generic channel group policy helpers", () => { + const cfg = { + channels: { + bluebubbles: { + groups: { + "chat:primary": { + requireMention: false, + tools: { deny: ["exec"] }, + }, + "*": { + requireMention: true, + tools: { allow: ["message.send"] }, + }, + }, + }, + }, + // oxlint-disable-next-line typescript/no-explicit-any + } as any; + + expect(resolveBlueBubblesGroupRequireMention({ cfg, groupId: "chat:primary" })).toBe(false); + expect(resolveBlueBubblesGroupRequireMention({ cfg, groupId: "chat:other" })).toBe(true); + expect(resolveBlueBubblesGroupToolPolicy({ cfg, groupId: "chat:primary" })).toEqual({ + deny: ["exec"], + }); + expect(resolveBlueBubblesGroupToolPolicy({ cfg, groupId: "chat:other" })).toEqual({ + allow: ["message.send"], + }); + }); +}); diff --git a/extensions/bluebubbles/src/group-policy.ts b/extensions/bluebubbles/src/group-policy.ts new file mode 100644 index 00000000000..656bb867a4c --- /dev/null +++ b/extensions/bluebubbles/src/group-policy.ts @@ -0,0 +1,40 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles"; +import { + resolveChannelGroupRequireMention, + resolveChannelGroupToolsPolicy, + type GroupToolPolicyConfig, +} from "openclaw/plugin-sdk/channel-policy"; + +type BlueBubblesGroupContext = { + cfg: OpenClawConfig; + accountId?: string | null; + groupId?: string | null; + senderId?: string | null; + senderName?: string | null; + senderUsername?: string | null; + senderE164?: string | null; +}; + +export function resolveBlueBubblesGroupRequireMention(params: BlueBubblesGroupContext): boolean { + return resolveChannelGroupRequireMention({ + cfg: params.cfg, + channel: "bluebubbles", + groupId: params.groupId, + accountId: params.accountId, + }); +} + +export function resolveBlueBubblesGroupToolPolicy( + params: BlueBubblesGroupContext, +): GroupToolPolicyConfig | undefined { + return resolveChannelGroupToolsPolicy({ + cfg: params.cfg, + channel: "bluebubbles", + groupId: params.groupId, + accountId: params.accountId, + senderId: params.senderId, + senderName: params.senderName, + senderUsername: params.senderUsername, + senderE164: params.senderE164, + }); +} diff --git a/src/plugin-sdk/bluebubbles.ts b/src/plugin-sdk/bluebubbles.ts index 88300031290..346ac01c829 100644 --- a/src/plugin-sdk/bluebubbles.ts +++ b/src/plugin-sdk/bluebubbles.ts @@ -28,7 +28,7 @@ export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js"; export { resolveBlueBubblesGroupRequireMention, resolveBlueBubblesGroupToolPolicy, -} from "../channels/plugins/group-mentions.js"; +} from "../../extensions/bluebubbles/src/group-policy.js"; export { formatPairingApproveHint } from "../channels/plugins/helpers.js"; export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js"; export { diff --git a/src/plugin-sdk/compat.ts b/src/plugin-sdk/compat.ts index 05a85d56e2a..2ce0c6a2c3b 100644 --- a/src/plugin-sdk/compat.ts +++ b/src/plugin-sdk/compat.ts @@ -42,5 +42,5 @@ export { mapAllowlistResolutionInputs } from "./allowlist-resolution.js"; export { resolveBlueBubblesGroupRequireMention, resolveBlueBubblesGroupToolPolicy, -} from "../channels/plugins/group-mentions.js"; +} from "../../extensions/bluebubbles/src/group-policy.js"; export { collectBlueBubblesStatusIssues } from "../channels/plugins/status-issues/bluebubbles.js";