feat(whatsapp): add per-session ackReaction override
Allows controlling auto-reactions (ack emoji) per-session via sessions.json. Session-level ackReaction can be set to 'always', 'mentions', or 'never' to override global direct/group settings. - Add sessionMode parameter to shouldAckReactionForWhatsApp - Load session config in maybeSendAckReaction to check override - Add ackReaction field to SessionEntry type - Add tests for session override behavior - Document per-session override in whatsapp.md
This commit is contained in:
parent
fdcac0ccf4
commit
387ab475e2
@ -239,6 +239,16 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
|
||||
- `"mentions"`: React only when bot is @mentioned
|
||||
- `"never"`: Never react in groups
|
||||
|
||||
**Per-session override:**
|
||||
The global and per-account configurations can be overridden for specific sessions (DMs or Groups) using session configuration (in `sessions.json`).
|
||||
- `ackReaction`: `"always" | "mentions" | "never"`
|
||||
- `"always"`: Enables reactions (overrides global off/never).
|
||||
- `"never"`: Disables reactions (overrides global on/always/mentions).
|
||||
- `"mentions"`: Enforces mention-only reactions.
|
||||
- Useful for disabling reactions in specific DMs (`"never"`) or forcing them in specific groups.
|
||||
|
||||
> **Note:** Setting `"mentions"` on a DM session effectively disables reactions for that DM, since direct messages don't have @mentions. Use `"always"` or `"never"` for DMs.
|
||||
|
||||
**Per-account override:**
|
||||
```json
|
||||
{
|
||||
|
||||
@ -225,6 +225,63 @@ describe("shouldAckReactionForWhatsApp", () => {
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
it("honors session overrides", () => {
|
||||
// Session: NEVER overrides Global: ALWAYS
|
||||
expect(
|
||||
shouldAckReactionForWhatsApp({
|
||||
emoji: "👀",
|
||||
isDirect: false,
|
||||
isGroup: true,
|
||||
directEnabled: true,
|
||||
groupMode: "always",
|
||||
wasMentioned: false,
|
||||
groupActivated: false,
|
||||
sessionMode: "never",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
// Session: ALWAYS overrides Global: NEVER
|
||||
expect(
|
||||
shouldAckReactionForWhatsApp({
|
||||
emoji: "👀",
|
||||
isDirect: false,
|
||||
isGroup: true,
|
||||
directEnabled: true,
|
||||
groupMode: "never",
|
||||
wasMentioned: false,
|
||||
groupActivated: false,
|
||||
sessionMode: "always",
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
// Session: MENTIONS in DM (effectively OFF unless mentioned, which is false)
|
||||
expect(
|
||||
shouldAckReactionForWhatsApp({
|
||||
emoji: "👀",
|
||||
isDirect: true,
|
||||
isGroup: false,
|
||||
directEnabled: true, // Globally enabled
|
||||
groupMode: "mentions",
|
||||
wasMentioned: false,
|
||||
groupActivated: false,
|
||||
sessionMode: "mentions", // Override to strict mentions
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
// Session: MENTIONS in Group (works if mentioned)
|
||||
expect(
|
||||
shouldAckReactionForWhatsApp({
|
||||
emoji: "👀",
|
||||
isDirect: false,
|
||||
isGroup: true,
|
||||
directEnabled: true,
|
||||
groupMode: "never", // Global OFF
|
||||
wasMentioned: true, // But mentioned
|
||||
groupActivated: false,
|
||||
sessionMode: "mentions", // Session override ON (if mentioned)
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeAckReactionAfterReply", () => {
|
||||
|
||||
@ -36,8 +36,27 @@ export function shouldAckReactionForWhatsApp(params: {
|
||||
groupMode: WhatsAppAckReactionMode;
|
||||
wasMentioned: boolean;
|
||||
groupActivated: boolean;
|
||||
sessionMode?: "always" | "mentions" | "never";
|
||||
}): boolean {
|
||||
if (!params.emoji) return false;
|
||||
|
||||
// Session override
|
||||
if (params.sessionMode === "never") return false;
|
||||
if (params.sessionMode === "always") return true;
|
||||
if (params.sessionMode === "mentions") {
|
||||
return shouldAckReaction({
|
||||
scope: "group-mentions",
|
||||
isDirect: params.isDirect,
|
||||
isGroup: params.isGroup,
|
||||
isMentionableGroup: true,
|
||||
requireMention: true,
|
||||
canDetectMention: true,
|
||||
effectiveWasMentioned: params.wasMentioned,
|
||||
shouldBypassMention: params.groupActivated,
|
||||
});
|
||||
}
|
||||
|
||||
// Config fallback
|
||||
if (params.isDirect) return params.directEnabled;
|
||||
if (!params.isGroup) return false;
|
||||
if (params.groupMode === "never") return false;
|
||||
|
||||
@ -54,6 +54,7 @@ export type SessionEntry = {
|
||||
authProfileOverride?: string;
|
||||
authProfileOverrideSource?: "auto" | "user";
|
||||
authProfileOverrideCompactionCount?: number;
|
||||
ackReaction?: "always" | "mentions" | "never";
|
||||
groupActivation?: "mention" | "always";
|
||||
groupActivationNeedsSystemIntro?: boolean;
|
||||
sendPolicy?: "allow" | "deny";
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { loadConfig } from "../../../config/config.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
|
||||
import { logVerbose } from "../../../globals.js";
|
||||
import { shouldAckReactionForWhatsApp } from "../../../channels/ack-reactions.js";
|
||||
import { sendReactionWhatsApp } from "../../outbound.js";
|
||||
@ -25,6 +26,16 @@ export function maybeSendAckReaction(params: {
|
||||
const groupMode = ackConfig?.group ?? "mentions";
|
||||
const conversationIdForCheck = params.msg.conversationId ?? params.msg.from;
|
||||
|
||||
// Load session to check for per-session ackReaction override.
|
||||
// Note: loadSessionStore uses an in-memory cache (45s TTL) so this is not
|
||||
// doing disk I/O on every message in typical usage patterns.
|
||||
const storePath = resolveStorePath(params.cfg.session?.store, {
|
||||
agentId: params.agentId,
|
||||
});
|
||||
const store = loadSessionStore(storePath);
|
||||
const sessionEntry = store[params.sessionKey];
|
||||
const sessionMode = sessionEntry?.ackReaction;
|
||||
|
||||
const activation =
|
||||
params.msg.chatType === "group"
|
||||
? resolveGroupActivationFor({
|
||||
@ -41,6 +52,7 @@ export function maybeSendAckReaction(params: {
|
||||
isGroup: params.msg.chatType === "group",
|
||||
directEnabled,
|
||||
groupMode,
|
||||
sessionMode,
|
||||
wasMentioned: params.msg.wasMentioned === true,
|
||||
groupActivated: activation === "always",
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user