fix(whatsapp): filter fromMe messages in groups to prevent infinite loop (#53386)

This commit is contained in:
w-sss
2026-03-24 18:44:34 +08:00
committed by Peter Steinberger
parent f52752889b
commit 0d4b47a14e
2 changed files with 78 additions and 0 deletions

View File

@@ -162,3 +162,68 @@ describe("WhatsApp dmPolicy precedence", () => {
expect(sendMessageMock).not.toHaveBeenCalled();
});
});
describe("WhatsApp group fromMe filtering", () => {
beforeEach(async () => {
vi.resetModules();
({ checkInboundAccessControl } = await import("./access-control.js"));
});
it("blocks fromMe messages in groups to prevent infinite loops (#53386)", async () => {
setAccessControlTestConfig({
channels: {
whatsapp: {
accounts: {
default: {
groupPolicy: "open",
},
},
},
},
});
const result = await checkInboundAccessControl({
accountId: "default",
from: "1234567890@g.us",
selfE164: "+15550009999",
senderE164: "+15550009999",
group: true,
pushName: "Owner",
isFromMe: true,
sock: { sendMessage: sendMessageMock },
remoteJid: "1234567890@g.us",
});
expect(result.allowed).toBe(false);
expect(result.shouldMarkRead).toBe(false);
});
it("allows fromMe=false messages in groups", async () => {
setAccessControlTestConfig({
channels: {
whatsapp: {
accounts: {
default: {
groupPolicy: "open",
},
},
},
},
});
const result = await checkInboundAccessControl({
accountId: "default",
from: "1234567890@g.us",
selfE164: "+15550009999",
senderE164: "+15550001111",
group: true,
pushName: "Other User",
isFromMe: false,
sock: { sendMessage: sendMessageMock },
remoteJid: "1234567890@g.us",
});
expect(result.allowed).toBe(true);
expect(result.shouldMarkRead).toBe(true);
});
});

View File

@@ -83,6 +83,19 @@ export async function checkInboundAccessControl(params: {
typeof params.messageTimestampMs === "number" &&
params.messageTimestampMs < params.connectedAtMs - pairingGraceMs;
// Filter fromMe messages in groups to prevent infinite loops
// When an agent sends a message to a group, WhatsApp echoes it back.
// Without this filter, the agent would respond to its own message.
if (params.group && params.isFromMe) {
logVerbose("Skipping fromMe group message (prevents infinite loop)");
return {
allowed: false,
shouldMarkRead: false,
isSelfChat,
resolvedAccountId: account.accountId,
};
}
// Group policy filtering:
// - "open": groups bypass allowFrom, only mention-gating applies
// - "disabled": block all group messages entirely