mirror of https://github.com/openclaw/openclaw.git
fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (#42588)
Merged via squash.
Prepared head SHA: 8ce59bb715
Co-authored-by: MonkeyLeeT <6754057+MonkeyLeeT@users.noreply.github.com>
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Reviewed-by: @scoootscooob
This commit is contained in:
parent
d7ac16788e
commit
843e3c1efb
|
|
@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
|
|||
- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. Thanks @vincentkoc.
|
||||
- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason.
|
||||
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
|
||||
- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) thanks @MonkeyLeeT.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -413,7 +413,13 @@ export async function monitorWebInbox(options: {
|
|||
|
||||
// If this is history/offline catch-up, mark read above but skip auto-reply.
|
||||
if (upsert.type === "append") {
|
||||
continue;
|
||||
const APPEND_RECENT_GRACE_MS = 60_000;
|
||||
const msgTsRaw = msg.messageTimestamp;
|
||||
const msgTsNum = msgTsRaw != null ? Number(msgTsRaw) : NaN;
|
||||
const msgTsMs = Number.isFinite(msgTsNum) ? msgTsNum * 1000 : 0;
|
||||
if (msgTsMs < connectedAtMs - APPEND_RECENT_GRACE_MS) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const enriched = await enrichInboundMessage(msg);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
import "./monitor-inbox.test-harness.js";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { monitorWebInbox } from "./inbound.js";
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
getAuthDir,
|
||||
getSock,
|
||||
installWebMonitorInboxUnitTestHooks,
|
||||
} from "./monitor-inbox.test-harness.js";
|
||||
|
||||
describe("append upsert handling (#20952)", () => {
|
||||
installWebMonitorInboxUnitTestHooks();
|
||||
type InboxOnMessage = NonNullable<Parameters<typeof monitorWebInbox>[0]["onMessage"]>;
|
||||
|
||||
async function tick() {
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
}
|
||||
|
||||
async function startInboxMonitor(onMessage: InboxOnMessage) {
|
||||
const listener = await monitorWebInbox({
|
||||
verbose: false,
|
||||
onMessage,
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
authDir: getAuthDir(),
|
||||
});
|
||||
return { listener, sock: getSock() };
|
||||
}
|
||||
|
||||
it("processes recent append messages (within 60s of connect)", async () => {
|
||||
const onMessage = vi.fn(async () => {});
|
||||
const { listener, sock } = await startInboxMonitor(onMessage);
|
||||
|
||||
// Timestamp ~5 seconds ago — recent, should be processed.
|
||||
const recentTs = Math.floor(Date.now() / 1000) - 5;
|
||||
sock.ev.emit("messages.upsert", {
|
||||
type: "append",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "recent-1", fromMe: false, remoteJid: "120363@g.us" },
|
||||
message: { conversation: "hello from group" },
|
||||
messageTimestamp: recentTs,
|
||||
pushName: "Tester",
|
||||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("skips stale append messages (older than 60s before connect)", async () => {
|
||||
const onMessage = vi.fn(async () => {});
|
||||
const { listener, sock } = await startInboxMonitor(onMessage);
|
||||
|
||||
// Timestamp 5 minutes ago — stale history sync, should be skipped.
|
||||
const staleTs = Math.floor(Date.now() / 1000) - 300;
|
||||
sock.ev.emit("messages.upsert", {
|
||||
type: "append",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "stale-1", fromMe: false, remoteJid: "120363@g.us" },
|
||||
message: { conversation: "old history sync" },
|
||||
messageTimestamp: staleTs,
|
||||
pushName: "OldTester",
|
||||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("skips append messages with NaN/non-finite timestamps", async () => {
|
||||
const onMessage = vi.fn(async () => {});
|
||||
const { listener, sock } = await startInboxMonitor(onMessage);
|
||||
|
||||
// NaN timestamp should be treated as 0 (stale) and skipped.
|
||||
sock.ev.emit("messages.upsert", {
|
||||
type: "append",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "nan-1", fromMe: false, remoteJid: "120363@g.us" },
|
||||
message: { conversation: "bad timestamp" },
|
||||
messageTimestamp: NaN,
|
||||
pushName: "BadTs",
|
||||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("handles Long-like protobuf timestamps correctly", async () => {
|
||||
const onMessage = vi.fn(async () => {});
|
||||
const { listener, sock } = await startInboxMonitor(onMessage);
|
||||
|
||||
// Baileys can deliver messageTimestamp as a Long object (from protobufjs).
|
||||
// Number(longObj) calls valueOf() and returns the numeric value.
|
||||
const recentTs = Math.floor(Date.now() / 1000) - 5;
|
||||
const longLike = { low: recentTs, high: 0, unsigned: true, valueOf: () => recentTs };
|
||||
sock.ev.emit("messages.upsert", {
|
||||
type: "append",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "long-1", fromMe: false, remoteJid: "120363@g.us" },
|
||||
message: { conversation: "long timestamp" },
|
||||
messageTimestamp: longLike,
|
||||
pushName: "LongTs",
|
||||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("always processes notify messages regardless of timestamp", async () => {
|
||||
const onMessage = vi.fn(async () => {});
|
||||
const { listener, sock } = await startInboxMonitor(onMessage);
|
||||
|
||||
// Very old timestamp but type=notify — should always be processed.
|
||||
const oldTs = Math.floor(Date.now() / 1000) - 86400;
|
||||
sock.ev.emit("messages.upsert", {
|
||||
type: "notify",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "notify-1", fromMe: false, remoteJid: "999@s.whatsapp.net" },
|
||||
message: { conversation: "normal message" },
|
||||
messageTimestamp: oldTs,
|
||||
pushName: "User",
|
||||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue