diff --git a/extensions/feishu/src/dedupe-key.test.ts b/extensions/feishu/src/dedupe-key.test.ts index f3603f0d8075..a41d1b98ac1f 100644 --- a/extensions/feishu/src/dedupe-key.test.ts +++ b/extensions/feishu/src/dedupe-key.test.ts @@ -64,6 +64,13 @@ describe("resolveFeishuMessageDedupeKey", () => { expect(key).toBe("om_no_time"); }); + it("falls back to message_id for malformed create_time", () => { + const key = resolveFeishuMessageDedupeKey( + textEvent({ messageId: "om_bad_time", createTime: "1710000000000ms" }), + ); + expect(key).toBe("om_bad_time"); + }); + it("keeps media keyed by message_id plus media key", () => { const event: FeishuMessageEvent = { sender: { sender_id: { open_id: "ou-user" } }, diff --git a/extensions/feishu/src/dedupe-key.ts b/extensions/feishu/src/dedupe-key.ts index 1c4ffe0688e7..4e657a9b653b 100644 --- a/extensions/feishu/src/dedupe-key.ts +++ b/extensions/feishu/src/dedupe-key.ts @@ -1,5 +1,6 @@ // Feishu plugin module implements dedupe key behavior. import { createHash } from "node:crypto"; +import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime"; import { asNullableRecord as readRecord } from "openclaw/plugin-sdk/string-coerce-runtime"; import type { FeishuMessageEvent } from "./event-types.js"; import { normalizeFeishuExternalKey } from "./external-keys.js"; @@ -79,7 +80,12 @@ function resolveTextRetryDedupeKey(event: FeishuMessageDedupeInput): string | un const createTime = event.message.create_time?.trim(); const chatId = event.message.chat_id?.trim(); const senderId = resolveSenderIdentity(event); - if (!createTime || !chatId || !senderId) { + if ( + !createTime || + parseStrictNonNegativeInteger(createTime) === undefined || + !chatId || + !senderId + ) { return undefined; } const contentHash = createHash("sha256")