From 892197c43e0e918c09dc26d5a29f031df6458c3f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 23 Jan 2026 22:20:28 +0000 Subject: [PATCH] refactor: reuse ack reaction helper for whatsapp --- src/channels/ack-reactions.test.ts | 92 +++++++++++++++++++++- src/channels/ack-reactions.ts | 28 +++++++ src/plugin-sdk/index.ts | 8 +- src/web/auto-reply/monitor/ack-reaction.ts | 36 ++++----- 4 files changed, 141 insertions(+), 23 deletions(-) diff --git a/src/channels/ack-reactions.test.ts b/src/channels/ack-reactions.test.ts index 14333e965..8be3fc323 100644 --- a/src/channels/ack-reactions.test.ts +++ b/src/channels/ack-reactions.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { shouldAckReaction } from "./ack-reactions.js"; +import { shouldAckReaction, shouldAckReactionForWhatsApp } from "./ack-reactions.js"; describe("shouldAckReaction", () => { it("honors direct and group-all scopes", () => { @@ -132,3 +132,93 @@ describe("shouldAckReaction", () => { ).toBe(true); }); }); + +describe("shouldAckReactionForWhatsApp", () => { + it("respects direct and group modes", () => { + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: true, + isGroup: false, + directEnabled: true, + groupMode: "mentions", + wasMentioned: false, + groupActivated: false, + }), + ).toBe(true); + + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: true, + isGroup: false, + directEnabled: false, + groupMode: "mentions", + wasMentioned: false, + groupActivated: false, + }), + ).toBe(false); + + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: false, + isGroup: true, + directEnabled: true, + groupMode: "always", + wasMentioned: false, + groupActivated: false, + }), + ).toBe(true); + + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: false, + isGroup: true, + directEnabled: true, + groupMode: "never", + wasMentioned: true, + groupActivated: true, + }), + ).toBe(false); + }); + + it("honors mentions or activation for group-mentions", () => { + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: false, + isGroup: true, + directEnabled: true, + groupMode: "mentions", + wasMentioned: true, + groupActivated: false, + }), + ).toBe(true); + + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: false, + isGroup: true, + directEnabled: true, + groupMode: "mentions", + wasMentioned: false, + groupActivated: true, + }), + ).toBe(true); + + expect( + shouldAckReactionForWhatsApp({ + emoji: "👀", + isDirect: false, + isGroup: true, + directEnabled: true, + groupMode: "mentions", + wasMentioned: false, + groupActivated: false, + }), + ).toBe(false); + }); +}); diff --git a/src/channels/ack-reactions.ts b/src/channels/ack-reactions.ts index dfe2f1879..beeb34a47 100644 --- a/src/channels/ack-reactions.ts +++ b/src/channels/ack-reactions.ts @@ -1,5 +1,7 @@ export type AckReactionScope = "all" | "direct" | "group-all" | "group-mentions" | "off" | "none"; +export type WhatsAppAckReactionMode = "always" | "mentions" | "never"; + export type AckReactionGateParams = { scope: AckReactionScope | undefined; isDirect: boolean; @@ -25,3 +27,29 @@ export function shouldAckReaction(params: AckReactionGateParams): boolean { } return false; } + +export function shouldAckReactionForWhatsApp(params: { + emoji: string; + isDirect: boolean; + isGroup: boolean; + directEnabled: boolean; + groupMode: WhatsAppAckReactionMode; + wasMentioned: boolean; + groupActivated: boolean; +}): boolean { + if (!params.emoji) return false; + if (params.isDirect) return params.directEnabled; + if (!params.isGroup) return false; + if (params.groupMode === "never") return false; + if (params.groupMode === "always") return true; + return shouldAckReaction({ + scope: "group-mentions", + isDirect: false, + isGroup: true, + isMentionableGroup: true, + requireMention: true, + canDetectMention: true, + effectiveWasMentioned: params.wasMentioned, + shouldBypassMention: params.groupActivated, + }); +} diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index 7de6df564..652657b92 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -117,8 +117,12 @@ export { resolveMentionGating, resolveMentionGatingWithBypass, } from "../channels/mention-gating.js"; -export type { AckReactionGateParams, AckReactionScope } from "../channels/ack-reactions.js"; -export { shouldAckReaction } from "../channels/ack-reactions.js"; +export type { + AckReactionGateParams, + AckReactionScope, + WhatsAppAckReactionMode, +} from "../channels/ack-reactions.js"; +export { shouldAckReaction, shouldAckReactionForWhatsApp } from "../channels/ack-reactions.js"; export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js"; export type { NormalizedLocation } from "../channels/location.js"; export { formatLocationText, toLocationContext } from "../channels/location.js"; diff --git a/src/web/auto-reply/monitor/ack-reaction.ts b/src/web/auto-reply/monitor/ack-reaction.ts index 58a2504cd..6a99da312 100644 --- a/src/web/auto-reply/monitor/ack-reaction.ts +++ b/src/web/auto-reply/monitor/ack-reaction.ts @@ -1,5 +1,6 @@ import type { loadConfig } from "../../../config/config.js"; import { logVerbose } from "../../../globals.js"; +import { shouldAckReactionForWhatsApp } from "../../../channels/ack-reactions.js"; import { sendReactionWhatsApp } from "../../outbound.js"; import { formatError } from "../../session.js"; import type { WebInboundMsg } from "../types.js"; @@ -24,30 +25,25 @@ export function maybeSendAckReaction(params: { const groupMode = ackConfig?.group ?? "mentions"; const conversationIdForCheck = params.msg.conversationId ?? params.msg.from; - const shouldSendReaction = () => { - if (!emoji) return false; - - if (params.msg.chatType === "direct") { - return directEnabled; - } - - if (params.msg.chatType === "group") { - if (groupMode === "never") return false; - if (groupMode === "always") return true; - if (groupMode === "mentions") { - const activation = resolveGroupActivationFor({ + const activation = + params.msg.chatType === "group" + ? resolveGroupActivationFor({ cfg: params.cfg, agentId: params.agentId, sessionKey: params.sessionKey, conversationId: conversationIdForCheck, - }); - if (activation === "always") return true; - return params.msg.wasMentioned === true; - } - } - - return false; - }; + }) + : null; + const shouldSendReaction = () => + shouldAckReactionForWhatsApp({ + emoji, + isDirect: params.msg.chatType === "direct", + isGroup: params.msg.chatType === "group", + directEnabled, + groupMode, + wasMentioned: params.msg.wasMentioned === true, + groupActivated: activation === "always", + }); if (!shouldSendReaction()) return;