@yaoyuanchao/dingtalk 1.7.3 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/monitor.ts +18 -18
package/package.json
CHANGED
package/src/monitor.ts
CHANGED
|
@@ -308,8 +308,11 @@ export async function startDingTalkMonitor(ctx: DingTalkMonitorContext): Promise
|
|
|
308
308
|
touchActivity(); // Track message activity for heartbeat
|
|
309
309
|
|
|
310
310
|
// Deduplication: skip messages already processed (e.g. re-delivered after reconnect)
|
|
311
|
+
// Check both protocol-level messageId AND business-level msgId, because
|
|
312
|
+
// DingTalk re-delivers with a NEW protocol messageId after reconnect but
|
|
313
|
+
// the same business msgId.
|
|
311
314
|
if (isDuplicateMessage(protocolMsgId)) {
|
|
312
|
-
log?.info?.("[dingtalk] Duplicate message skipped: " + protocolMsgId);
|
|
315
|
+
log?.info?.("[dingtalk] Duplicate message skipped (protocol): " + protocolMsgId);
|
|
313
316
|
return { status: "SUCCESS", message: "OK" };
|
|
314
317
|
}
|
|
315
318
|
markMessageProcessed(protocolMsgId);
|
|
@@ -317,6 +320,15 @@ export async function startDingTalkMonitor(ctx: DingTalkMonitorContext): Promise
|
|
|
317
320
|
try {
|
|
318
321
|
const data: DingTalkRobotMessage = typeof downstream.data === "string"
|
|
319
322
|
? JSON.parse(downstream.data) : downstream.data;
|
|
323
|
+
|
|
324
|
+
// Business-level dedup: same msgId re-delivered with different protocol ID
|
|
325
|
+
const bizMsgId = data.msgId;
|
|
326
|
+
if (bizMsgId && isDuplicateMessage('biz:' + bizMsgId)) {
|
|
327
|
+
log?.info?.("[dingtalk] Duplicate message skipped (bizMsgId): " + bizMsgId);
|
|
328
|
+
return { status: "SUCCESS", message: "OK" };
|
|
329
|
+
}
|
|
330
|
+
if (bizMsgId) markMessageProcessed('biz:' + bizMsgId);
|
|
331
|
+
|
|
320
332
|
setStatus?.({ lastInboundAt: Date.now() });
|
|
321
333
|
await processInboundMessage(data, ctx);
|
|
322
334
|
} catch (err) {
|
|
@@ -1656,7 +1668,6 @@ async function dispatchWithFullPipeline(params: {
|
|
|
1656
1668
|
log, setStatus, onFirstReply } = params;
|
|
1657
1669
|
|
|
1658
1670
|
let firstReplyFired = false;
|
|
1659
|
-
let typingSafetyTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
1660
1671
|
|
|
1661
1672
|
// 1. Resolve agent route via own bindings matching (like official plugin).
|
|
1662
1673
|
// OpenClaw's resolveAgentRoute doesn't handle accountId correctly for multi-account.
|
|
@@ -1752,7 +1763,6 @@ async function dispatchWithFullPipeline(params: {
|
|
|
1752
1763
|
// Recall typing indicator on first delivery
|
|
1753
1764
|
if (!firstReplyFired && onFirstReply) {
|
|
1754
1765
|
firstReplyFired = true;
|
|
1755
|
-
if (typingSafetyTimeout) { clearTimeout(typingSafetyTimeout); typingSafetyTimeout = null; }
|
|
1756
1766
|
await onFirstReply().catch((err) => {
|
|
1757
1767
|
log?.info?.("[dingtalk] onFirstReply error: " + err);
|
|
1758
1768
|
});
|
|
@@ -1780,21 +1790,11 @@ async function dispatchWithFullPipeline(params: {
|
|
|
1780
1790
|
await rt.channel.reply.dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyOptions });
|
|
1781
1791
|
} finally {
|
|
1782
1792
|
markDispatchIdle();
|
|
1783
|
-
// Don't recall typing
|
|
1784
|
-
//
|
|
1785
|
-
//
|
|
1786
|
-
//
|
|
1787
|
-
//
|
|
1788
|
-
if (!firstReplyFired && onFirstReply) {
|
|
1789
|
-
const TYPING_SAFETY_TIMEOUT_MS = 3 * 60 * 1000; // 3 minutes
|
|
1790
|
-
typingSafetyTimeout = setTimeout(async () => {
|
|
1791
|
-
if (!firstReplyFired && onFirstReply) {
|
|
1792
|
-
firstReplyFired = true;
|
|
1793
|
-
log?.info?.('[dingtalk] Typing safety timeout — recalling after no delivery');
|
|
1794
|
-
await onFirstReply().catch(() => {});
|
|
1795
|
-
}
|
|
1796
|
-
}, TYPING_SAFETY_TIMEOUT_MS);
|
|
1797
|
-
}
|
|
1793
|
+
// Don't recall typing here — dispatchReplyFromConfig resolves when dispatch
|
|
1794
|
+
// is *initiated*, not when the agent finishes. The agent may still be doing
|
|
1795
|
+
// tool calls for many minutes before producing text. The deliver callback
|
|
1796
|
+
// above handles recall on first delivery. If the agent crashes without
|
|
1797
|
+
// replying, the emoji reaction simply stays — acceptable tradeoff.
|
|
1798
1798
|
}
|
|
1799
1799
|
|
|
1800
1800
|
// 10. Record activity
|