fix: honor whatsapp per-group mention overrides
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
### Features
|
### Features
|
||||||
- Gateway: support `gateway.port` + `CLAWDIS_GATEWAY_PORT` across CLI, TUI, and macOS app.
|
- Gateway: support `gateway.port` + `CLAWDIS_GATEWAY_PORT` across CLI, TUI, and macOS app.
|
||||||
- UI: centralize tool display metadata and show action/detail summaries across Web Chat, SwiftUI, Android, and the TUI.
|
- UI: centralize tool display metadata and show action/detail summaries across Web Chat, SwiftUI, Android, and the TUI.
|
||||||
|
- Control UI: support configurable base paths (`gateway.controlUi.basePath`) for hosting under URL prefixes.
|
||||||
- Onboarding: shared wizard engine powering CLI + macOS via gateway wizard RPC.
|
- Onboarding: shared wizard engine powering CLI + macOS via gateway wizard RPC.
|
||||||
- Config: expose schema + UI hints for generic config forms (Web UI + future clients).
|
- Config: expose schema + UI hints for generic config forms (Web UI + future clients).
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
- Build: fix regex literal in tool-meta path detection (watch build error).
|
- Build: fix regex literal in tool-meta path detection (watch build error).
|
||||||
- Build: require AVX2 Bun for x86_64 relay packaging (reject baseline builds).
|
- Build: require AVX2 Bun for x86_64 relay packaging (reject baseline builds).
|
||||||
- Auto-reply: add run-level telemetry + typing TTL guardrails to diagnose stuck replies.
|
- Auto-reply: add run-level telemetry + typing TTL guardrails to diagnose stuck replies.
|
||||||
|
- WhatsApp: honor per-group mention gating overrides when group ids are stored as session keys.
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
- Skills: add Sheets/Docs examples to gog skill (#128) — thanks @mbelinky.
|
- Skills: add Sheets/Docs examples to gog skill (#128) — thanks @mbelinky.
|
||||||
|
|||||||
@@ -1056,6 +1056,58 @@ describe("web auto-reply", () => {
|
|||||||
resetLoadConfigMock();
|
resetLoadConfigMock();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("honors per-group mention overrides when conversationId uses session key", async () => {
|
||||||
|
const sendMedia = vi.fn();
|
||||||
|
const reply = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const sendComposing = vi.fn();
|
||||||
|
const resolver = vi.fn().mockResolvedValue({ text: "ok" });
|
||||||
|
|
||||||
|
setLoadConfigMock(() => ({
|
||||||
|
whatsapp: {
|
||||||
|
allowFrom: ["*"],
|
||||||
|
groups: {
|
||||||
|
"*": { requireMention: true },
|
||||||
|
"123@g.us": { requireMention: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
routing: { groupChat: { mentionPatterns: ["@clawd"] } },
|
||||||
|
}));
|
||||||
|
|
||||||
|
let capturedOnMessage:
|
||||||
|
| ((msg: import("./inbound.js").WebInboundMessage) => Promise<void>)
|
||||||
|
| undefined;
|
||||||
|
const listenerFactory = async (opts: {
|
||||||
|
onMessage: (
|
||||||
|
msg: import("./inbound.js").WebInboundMessage,
|
||||||
|
) => Promise<void>;
|
||||||
|
}) => {
|
||||||
|
capturedOnMessage = opts.onMessage;
|
||||||
|
return { close: vi.fn() };
|
||||||
|
};
|
||||||
|
|
||||||
|
await monitorWebProvider(false, listenerFactory, false, resolver);
|
||||||
|
expect(capturedOnMessage).toBeDefined();
|
||||||
|
|
||||||
|
await capturedOnMessage?.({
|
||||||
|
body: "hello group",
|
||||||
|
from: "whatsapp:group:123@g.us",
|
||||||
|
conversationId: "whatsapp:group:123@g.us",
|
||||||
|
chatId: "123@g.us",
|
||||||
|
chatType: "group",
|
||||||
|
to: "+2",
|
||||||
|
id: "g-per-group-session-key",
|
||||||
|
senderE164: "+111",
|
||||||
|
senderName: "Alice",
|
||||||
|
selfE164: "+999",
|
||||||
|
sendComposing,
|
||||||
|
reply,
|
||||||
|
sendMedia,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolver).toHaveBeenCalledTimes(1);
|
||||||
|
resetLoadConfigMock();
|
||||||
|
});
|
||||||
|
|
||||||
it("supports always-on group activation with silent token and preserves history", async () => {
|
it("supports always-on group activation with silent token and preserves history", async () => {
|
||||||
const sendMedia = vi.fn();
|
const sendMedia = vi.fn();
|
||||||
const reply = vi.fn().mockResolvedValue(undefined);
|
const reply = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { loadConfig } from "../config/config.js";
|
|||||||
import {
|
import {
|
||||||
DEFAULT_IDLE_MINUTES,
|
DEFAULT_IDLE_MINUTES,
|
||||||
loadSessionStore,
|
loadSessionStore,
|
||||||
|
resolveGroupSessionKey,
|
||||||
resolveSessionKey,
|
resolveSessionKey,
|
||||||
resolveStorePath,
|
resolveStorePath,
|
||||||
saveSessionStore,
|
saveSessionStore,
|
||||||
@@ -813,8 +814,16 @@ export async function monitorWebProvider(
|
|||||||
.join(", ");
|
.join(", ");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resolveGroupResolution = (conversationId: string) =>
|
||||||
|
resolveGroupSessionKey({
|
||||||
|
From: conversationId,
|
||||||
|
ChatType: "group",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
});
|
||||||
|
|
||||||
const resolveGroupRequireMentionFor = (conversationId: string) => {
|
const resolveGroupRequireMentionFor = (conversationId: string) => {
|
||||||
const groupConfig = cfg.whatsapp?.groups?.[conversationId];
|
const groupId = resolveGroupResolution(conversationId)?.id ?? conversationId;
|
||||||
|
const groupConfig = cfg.whatsapp?.groups?.[groupId];
|
||||||
if (typeof groupConfig?.requireMention === "boolean") {
|
if (typeof groupConfig?.requireMention === "boolean") {
|
||||||
return groupConfig.requireMention;
|
return groupConfig.requireMention;
|
||||||
}
|
}
|
||||||
@@ -824,9 +833,11 @@ export async function monitorWebProvider(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const resolveGroupActivationFor = (conversationId: string) => {
|
const resolveGroupActivationFor = (conversationId: string) => {
|
||||||
const key = conversationId.startsWith("group:")
|
const key =
|
||||||
? conversationId
|
resolveGroupResolution(conversationId)?.key ??
|
||||||
: `whatsapp:group:${conversationId}`;
|
(conversationId.startsWith("group:")
|
||||||
|
? conversationId
|
||||||
|
: `whatsapp:group:${conversationId}`);
|
||||||
const store = loadSessionStore(sessionStorePath);
|
const store = loadSessionStore(sessionStorePath);
|
||||||
const entry = store[key];
|
const entry = store[key];
|
||||||
const requireMention = resolveGroupRequireMentionFor(conversationId);
|
const requireMention = resolveGroupRequireMentionFor(conversationId);
|
||||||
|
|||||||
Reference in New Issue
Block a user