惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
博客园 - 叶小钗
D
DataBreaches.Net
D
Docker
月光博客
月光博客
博客园 - 司徒正美
Last Week in AI
Last Week in AI
有赞技术团队
有赞技术团队
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(whatsapp): validate inbound timestamps · openclaw/ope...
steipete · 2026-05-29 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -10,6 +10,7 @@ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runt

1010

import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";

1111

import { createInboundDebouncer } from "openclaw/plugin-sdk/channel-inbound-debounce";

1212

import { getChildLogger } from "openclaw/plugin-sdk/logging-core";

13+

import { parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";

1314

import { defaultRuntime } from "openclaw/plugin-sdk/runtime-env";

1415

import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";

1516

import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";

@@ -79,6 +80,17 @@ type LocalGroupMetadataCacheEntry = WhatsAppGroupMetadataCacheEntry & {

7980

mentionParticipants?: WhatsAppOutboundMentionParticipant[];

8081

};

8182
83+

function parseWhatsAppTimestampSeconds(value: unknown): number | undefined {

84+

if (value == null) {

85+

return undefined;

86+

}

87+

if (typeof value === "string") {

88+

return parseStrictFiniteNumber(value);

89+

}

90+

const parsed = Number(value);

91+

return Number.isFinite(parsed) ? parsed : undefined;

92+

}

93+
8294

function rememberGroupMetadataCacheEntry<T extends WhatsAppGroupMetadataCacheEntry>(

8395

cache: Map<string, T>,

8496

jid: string,

@@ -614,9 +626,9 @@ export async function attachWebInboxToSocket(

614626

groupSubject = meta.subject;

615627

groupParticipants = meta.participants;

616628

}

617-

const messageTimestampMs = msg.messageTimestamp

618-

? Number(msg.messageTimestamp) * 1000

619-

: undefined;

629+

const messageTimestampSeconds = parseWhatsAppTimestampSeconds(msg.messageTimestamp);

630+

const messageTimestampMs =

631+

messageTimestampSeconds !== undefined ? messageTimestampSeconds * 1000 : undefined;

620632
621633

const accessCfg = options.loadConfig?.() ?? options.cfg;

622634

const access = await checkInboundAccessControl({

@@ -740,9 +752,8 @@ export async function attachWebInboxToSocket(

740752

return false;

741753

}

742754

const APPEND_RECENT_GRACE_MS = 60_000;

743-

const msgTsRaw = msg.messageTimestamp;

744-

const msgTsNum = msgTsRaw != null ? Number(msgTsRaw) : Number.NaN;

745-

const msgTsMs = Number.isFinite(msgTsNum) ? msgTsNum * 1000 : 0;

755+

const msgTsSeconds = parseWhatsAppTimestampSeconds(msg.messageTimestamp);

756+

const msgTsMs = msgTsSeconds !== undefined ? msgTsSeconds * 1000 : 0;

746757

return msgTsMs < connectedAtMs - APPEND_RECENT_GRACE_MS;

747758

};

748759
Original file line numberDiff line numberDiff line change

@@ -213,6 +213,33 @@ describe("web monitor inbox", () => {

213213

await listener.close();

214214

});

215215
216+

it("still sends pairing replies when live DMs have null timestamps", async () => {

217+

mockLoadConfig.mockReturnValue({});

218+

upsertPairingRequestMock.mockResolvedValueOnce({ code: "PAIRCODE", created: true });

219+
220+

const { onMessage, listener, sock } = await openInboxMonitor();

221+
222+

const upsertBlocked = buildNotifyMessageUpsert({

223+

id: "no-config-null-ts",

224+

remoteJid: "999@s.whatsapp.net",

225+

text: "ping",

226+

timestamp: null as never,

227+

});

228+
229+

sock.ev.emit("messages.upsert", upsertBlocked);

230+

await vi.waitFor(

231+

() => {

232+

expect(sock.sendMessage).toHaveBeenCalledTimes(1);

233+

},

234+

{ timeout: 5_000, interval: 5 },

235+

);

236+
237+

expect(onMessage).not.toHaveBeenCalled();

238+

expectPairingPromptSent(sock, "999@s.whatsapp.net", "+999");

239+
240+

await listener.close();

241+

});

242+
216243

it("skips pairing replies for outbound DMs in same-phone mode", async () => {

217244

await expectOutboundDmSkipsPairing({

218245

selfChatMode: true,

Original file line numberDiff line numberDiff line change

@@ -81,6 +81,29 @@ describe("append upsert handling (#20952)", () => {

8181

await listener.close();

8282

});

8383
84+

it("skips append messages with non-decimal string timestamps", async () => {

85+

const onMessage = vi.fn(async () => {});

86+

const { listener, sock } = await startInboxMonitor(onMessage);

87+
88+

const recentTs = Math.floor(Date.now() / 1000) - 5;

89+

sock.ev.emit("messages.upsert", {

90+

type: "append",

91+

messages: [

92+

{

93+

key: { id: "hex-1", fromMe: false, remoteJid: "120363@g.us" },

94+

message: { conversation: "hex timestamp" },

95+

messageTimestamp: `0x${recentTs.toString(16)}`,

96+

pushName: "HexTs",

97+

},

98+

],

99+

});

100+

await settleInboundWork();

101+
102+

expect(onMessage).not.toHaveBeenCalled();

103+
104+

await listener.close();

105+

});

106+
84107

it("handles Long-like protobuf timestamps correctly", async () => {

85108

const onMessage = vi.fn(async () => {});

86109

const { listener, sock } = await startInboxMonitor(onMessage);