diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts index f2d4b84f8bc..7d01f233371 100644 --- a/extensions/line/src/channel.ts +++ b/extensions/line/src/channel.ts @@ -19,6 +19,7 @@ import { type LineChannelData, type ResolvedLineAccount, } from "../api.js"; +import { resolveLineGroupRequireMention } from "./group-policy.js"; import { getLineRuntime } from "./runtime.js"; import { lineSetupAdapter } from "./setup-core.js"; import { lineSetupWizard } from "./setup-surface.js"; @@ -127,18 +128,7 @@ export const linePlugin: ChannelPlugin = { }, }, groups: { - resolveRequireMention: ({ cfg, accountId, groupId }) => { - const account = getLineRuntime().channel.line.resolveLineAccount({ - cfg, - accountId: accountId ?? undefined, - }); - const groups = account.config.groups; - if (!groups || !groupId) { - return false; - } - const groupConfig = groups[groupId] ?? groups["*"]; - return groupConfig?.requireMention ?? false; - }, + resolveRequireMention: resolveLineGroupRequireMention, }, messaging: { normalizeTarget: (target) => { diff --git a/extensions/line/src/group-policy.test.ts b/extensions/line/src/group-policy.test.ts new file mode 100644 index 00000000000..b9fa64321aa --- /dev/null +++ b/extensions/line/src/group-policy.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from "vitest"; +import { resolveLineGroupRequireMention } from "./group-policy.js"; + +describe("line group policy", () => { + it("matches raw and prefixed LINE group keys for requireMention", () => { + const cfg = { + channels: { + line: { + groups: { + "room:r123": { + requireMention: false, + }, + "group:g123": { + requireMention: false, + }, + "*": { + requireMention: true, + }, + }, + }, + }, + // oxlint-disable-next-line typescript/no-explicit-any + } as any; + + expect(resolveLineGroupRequireMention({ cfg, groupId: "r123" })).toBe(false); + expect(resolveLineGroupRequireMention({ cfg, groupId: "room:r123" })).toBe(false); + expect(resolveLineGroupRequireMention({ cfg, groupId: "g123" })).toBe(false); + expect(resolveLineGroupRequireMention({ cfg, groupId: "group:g123" })).toBe(false); + expect(resolveLineGroupRequireMention({ cfg, groupId: "other" })).toBe(true); + }); + + it("uses account-scoped prefixed LINE group config for requireMention", () => { + const cfg = { + channels: { + line: { + groups: { + "*": { + requireMention: true, + }, + }, + accounts: { + work: { + groups: { + "group:g123": { + requireMention: false, + }, + }, + }, + }, + }, + }, + // oxlint-disable-next-line typescript/no-explicit-any + } as any; + + expect(resolveLineGroupRequireMention({ cfg, groupId: "g123", accountId: "work" })).toBe(false); + }); +}); diff --git a/extensions/line/src/group-policy.ts b/extensions/line/src/group-policy.ts new file mode 100644 index 00000000000..e6b4fa0ba95 --- /dev/null +++ b/extensions/line/src/group-policy.ts @@ -0,0 +1,22 @@ +import { resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy"; +import { resolveExactLineGroupConfigKey, type OpenClawConfig } from "openclaw/plugin-sdk/line-core"; + +type LineGroupContext = { + cfg: OpenClawConfig; + accountId?: string | null; + groupId?: string | null; +}; + +export function resolveLineGroupRequireMention(params: LineGroupContext): boolean { + const exactGroupId = resolveExactLineGroupConfigKey({ + cfg: params.cfg, + accountId: params.accountId, + groupId: params.groupId, + }); + return resolveChannelGroupRequireMention({ + cfg: params.cfg, + channel: "line", + groupId: exactGroupId ?? params.groupId, + accountId: params.accountId, + }); +} diff --git a/src/channels/plugins/group-mentions.test.ts b/src/channels/plugins/group-mentions.test.ts deleted file mode 100644 index 7375112ac34..00000000000 --- a/src/channels/plugins/group-mentions.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - resolveBlueBubblesGroupRequireMention, - resolveBlueBubblesGroupToolPolicy, - resolveLineGroupRequireMention, - resolveLineGroupToolPolicy, -} from "./group-mentions.js"; - -describe("group mentions (bluebubbles)", () => { - it("uses generic channel group policy helpers", () => { - const blueBubblesCfg = { - 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: blueBubblesCfg, groupId: "chat:primary" }), - ).toBe(false); - expect( - resolveBlueBubblesGroupRequireMention({ cfg: blueBubblesCfg, groupId: "chat:other" }), - ).toBe(true); - expect( - resolveBlueBubblesGroupToolPolicy({ cfg: blueBubblesCfg, groupId: "chat:primary" }), - ).toEqual({ deny: ["exec"] }); - expect( - resolveBlueBubblesGroupToolPolicy({ cfg: blueBubblesCfg, groupId: "chat:other" }), - ).toEqual({ - allow: ["message.send"], - }); - }); -}); - -describe("group mentions (line)", () => { - it("matches raw and prefixed LINE group keys for requireMention and tools", () => { - const lineCfg = { - channels: { - line: { - groups: { - "room:r123": { - requireMention: false, - tools: { allow: ["message.send"] }, - }, - "group:g123": { - requireMention: false, - tools: { deny: ["exec"] }, - }, - "*": { - requireMention: true, - }, - }, - }, - }, - // oxlint-disable-next-line typescript/no-explicit-any - } as any; - - expect(resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "r123" })).toBe(false); - expect(resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "room:r123" })).toBe(false); - expect(resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "g123" })).toBe(false); - expect(resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "group:g123" })).toBe(false); - expect(resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "other" })).toBe(true); - expect(resolveLineGroupToolPolicy({ cfg: lineCfg, groupId: "r123" })).toEqual({ - allow: ["message.send"], - }); - expect(resolveLineGroupToolPolicy({ cfg: lineCfg, groupId: "g123" })).toEqual({ - deny: ["exec"], - }); - }); - - it("uses account-scoped prefixed LINE group config for requireMention", () => { - const lineCfg = { - channels: { - line: { - groups: { - "*": { - requireMention: true, - }, - }, - accounts: { - work: { - groups: { - "group:g123": { - requireMention: false, - }, - }, - }, - }, - }, - }, - // oxlint-disable-next-line typescript/no-explicit-any - } as any; - - expect( - resolveLineGroupRequireMention({ cfg: lineCfg, groupId: "g123", accountId: "work" }), - ).toBe(false); - }); -}); diff --git a/src/channels/plugins/group-mentions.ts b/src/channels/plugins/group-mentions.ts deleted file mode 100644 index c3268496b03..00000000000 --- a/src/channels/plugins/group-mentions.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - resolveChannelGroupRequireMention, - resolveChannelGroupToolsPolicy, -} from "../../config/group-policy.js"; -import type { GroupToolPolicyConfig } from "../../config/types.tools.js"; -import { resolveExactLineGroupConfigKey } from "../../line/group-keys.js"; -import type { ChannelGroupContext } from "./types.js"; - -type GroupMentionParams = ChannelGroupContext; - -type ChannelGroupPolicyChannel = - | "telegram" - | "whatsapp" - | "imessage" - | "googlechat" - | "bluebubbles" - | "line"; - -function resolveChannelRequireMention( - params: GroupMentionParams, - channel: ChannelGroupPolicyChannel, - groupId: string | null | undefined = params.groupId, -): boolean { - return resolveChannelGroupRequireMention({ - cfg: params.cfg, - channel, - groupId, - accountId: params.accountId, - }); -} - -function resolveChannelToolPolicyForSender( - params: GroupMentionParams, - channel: ChannelGroupPolicyChannel, - groupId: string | null | undefined = params.groupId, -): GroupToolPolicyConfig | undefined { - return resolveChannelGroupToolsPolicy({ - cfg: params.cfg, - channel, - groupId, - accountId: params.accountId, - senderId: params.senderId, - senderName: params.senderName, - senderUsername: params.senderUsername, - senderE164: params.senderE164, - }); -} - -export function resolveWhatsAppGroupRequireMention(params: GroupMentionParams): boolean { - return resolveChannelRequireMention(params, "whatsapp"); -} - -export function resolveIMessageGroupRequireMention(params: GroupMentionParams): boolean { - return resolveChannelRequireMention(params, "imessage"); -} - -export function resolveGoogleChatGroupRequireMention(params: GroupMentionParams): boolean { - return resolveChannelRequireMention(params, "googlechat"); -} - -export function resolveGoogleChatGroupToolPolicy( - params: GroupMentionParams, -): GroupToolPolicyConfig | undefined { - return resolveChannelToolPolicyForSender(params, "googlechat"); -} - -export function resolveBlueBubblesGroupRequireMention(params: GroupMentionParams): boolean { - return resolveChannelRequireMention(params, "bluebubbles"); -} - -export function resolveWhatsAppGroupToolPolicy( - params: GroupMentionParams, -): GroupToolPolicyConfig | undefined { - return resolveChannelToolPolicyForSender(params, "whatsapp"); -} - -export function resolveIMessageGroupToolPolicy( - params: GroupMentionParams, -): GroupToolPolicyConfig | undefined { - return resolveChannelToolPolicyForSender(params, "imessage"); -} - -export function resolveBlueBubblesGroupToolPolicy( - params: GroupMentionParams, -): GroupToolPolicyConfig | undefined { - return resolveChannelToolPolicyForSender(params, "bluebubbles"); -} - -export function resolveLineGroupRequireMention(params: GroupMentionParams): boolean { - const exactGroupId = resolveExactLineGroupConfigKey({ - cfg: params.cfg, - accountId: params.accountId, - groupId: params.groupId, - }); - if (exactGroupId) { - return resolveChannelGroupRequireMention({ - cfg: params.cfg, - channel: "line", - groupId: exactGroupId, - accountId: params.accountId, - }); - } - return resolveChannelRequireMention(params, "line"); -} - -export function resolveLineGroupToolPolicy( - params: GroupMentionParams, -): GroupToolPolicyConfig | undefined { - const exactGroupId = resolveExactLineGroupConfigKey({ - cfg: params.cfg, - accountId: params.accountId, - groupId: params.groupId, - }); - if (exactGroupId) { - return resolveChannelToolPolicyForSender(params, "line", exactGroupId); - } - return resolveChannelToolPolicyForSender(params, "line"); -} diff --git a/src/plugin-sdk/line-core.ts b/src/plugin-sdk/line-core.ts index eef1b3c2b76..04b2950a50d 100644 --- a/src/plugin-sdk/line-core.ts +++ b/src/plugin-sdk/line-core.ts @@ -15,5 +15,6 @@ export { resolveDefaultLineAccountId, resolveLineAccount, } from "../line/accounts.js"; +export { resolveExactLineGroupConfigKey } from "../line/group-keys.js"; export type { ResolvedLineAccount } from "../line/types.js"; export { LineConfigSchema } from "../line/config-schema.js";