mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 13:00:48 +00:00
fix(feishu): add early event-level dedup to prevent duplicate replies
Add synchronous in-memory dedup at EventDispatcher handler level using message_id as key with 5-minute TTL and 2000-entry cap. This catches duplicate events immediately when they arrive from the Lark SDK — before the inbound debouncer or processing queue — preventing the race condition where two concurrent dispatches enter the pipeline before either records the messageId in the downstream dedup layer. Fixes the root cause reported in #42687.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import * as crypto from "crypto";
|
||||
import * as Lark from "@larksuiteoapi/node-sdk";
|
||||
import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "openclaw/plugin-sdk/feishu";
|
||||
import { createDedupeCache } from "openclaw/plugin-sdk/feishu";
|
||||
import { resolveFeishuAccount } from "./accounts.js";
|
||||
import { raceWithTimeoutAndAbort } from "./async.js";
|
||||
import {
|
||||
@@ -389,10 +390,24 @@ function registerEventHandlers(
|
||||
},
|
||||
});
|
||||
|
||||
// Early event-level dedup to drop duplicate webhook retries and WebSocket replays
|
||||
// before they enter the debouncer or processing pipeline. The downstream dedup in
|
||||
// handleFeishuMessage guards against restarts (persistent), but cannot prevent two
|
||||
// concurrent dispatches of the same event from both being enqueued.
|
||||
const eventDedup = createDedupeCache({ ttlMs: 5 * 60 * 1000, maxSize: 2_000 });
|
||||
|
||||
eventDispatcher.register({
|
||||
"im.message.receive_v1": async (data) => {
|
||||
const event = data as unknown as FeishuMessageEvent;
|
||||
const messageId = event.message?.message_id?.trim();
|
||||
if (messageId) {
|
||||
const eventKey = `${accountId}:evt:${messageId}`;
|
||||
if (!eventDedup.check(eventKey)) {
|
||||
log(`feishu[${accountId}]: dropping duplicate event for message ${messageId}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const processMessage = async () => {
|
||||
const event = data as unknown as FeishuMessageEvent;
|
||||
await inboundDebouncer.enqueue(event);
|
||||
};
|
||||
if (fireAndForget) {
|
||||
|
||||
Reference in New Issue
Block a user