@jeik/dingtalk-connector 0.8.27 → 0.8.29
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/CHANGELOG.md +48 -0
- package/README.en.md +40 -82
- package/README.md +50 -93
- package/dist/background-work-DkaSDqWk.mjs +53 -0
- package/dist/card-content-cache-C7-XHhHw.mjs +2 -0
- package/dist/{card-content-cache-CYUzCaQJ.mjs → card-content-cache-DT_OYThh.mjs} +15 -1
- package/dist/chunk-upload-BKM1y4Ba.mjs +2 -0
- package/dist/{chunk-upload-BBQgGtcZ.mjs → chunk-upload-CIU5G_ay.mjs} +1 -1
- package/dist/{common-DE0gd0Jg.mjs → common-BfIYs1yC.mjs} +3 -3
- package/dist/{common-B_A78n8u.mjs → common-C0Oaf0z-.mjs} +1 -1
- package/dist/connection-obtrPkj4.mjs +641 -0
- package/dist/entry-bundled.mjs +1 -1
- package/dist/{gateway-methods--aKsyMs-.mjs → gateway-methods-BdcpfxEZ.mjs} +1 -1
- package/dist/gateway-methods-DQ6TwOAr.mjs +2 -0
- package/dist/index.mjs +2 -2
- package/dist/{logger-mZ9OSbmD.mjs → logger-CnBTcwyq.mjs} +16 -2
- package/dist/logger-D8H2IcYz.mjs +2 -0
- package/dist/{media-BqpUGCni.mjs → media-DDtX37T2.mjs} +1 -1
- package/dist/{media-DnQB2BrL.mjs → media-g8tJtpfU.mjs} +29 -20
- package/dist/{message-handler-DQZiKx3i.mjs → message-handler-BrO2i7tg.mjs} +251 -139
- package/dist/{messaging-WleKf54h.mjs → messaging-CK0Wz3Ze.mjs} +23 -21
- package/dist/{runtime-DzZo6Gvk.mjs → runtime-DbqGRyhI.mjs} +6 -5
- package/dist/{utils-Bjh4r_qS.mjs → utils-Bwz2Fls_.mjs} +1 -1
- package/docs/RELEASE_NOTES_V0.8.28.md +56 -0
- package/docs/RELEASE_NOTES_V0.8.29.md +57 -0
- package/openclaw.plugin.json +9 -5
- package/package.json +2 -2
- package/src/config/schema.ts +2 -1
- package/src/core/connection.ts +504 -220
- package/src/core/message-handler.ts +197 -102
- package/src/reply-dispatcher.ts +244 -93
- package/src/services/media.ts +60 -27
- package/src/services/messaging/card-content-cache.ts +11 -6
- package/src/services/messaging/card.ts +2 -0
- package/src/services/messaging.ts +52 -38
- package/src/utils/background-work.ts +65 -0
- package/src/utils/logger.ts +23 -2
- package/dist/card-content-cache-Dwd8HICP.mjs +0 -2
- package/dist/chunk-upload-DaLXXZH3.mjs +0 -2
- package/dist/connection-fc6B4z4G.mjs +0 -423
- package/dist/gateway-methods-BQd4E0Sk.mjs +0 -2
- package/dist/logger-BmJkQkm1.mjs +0 -2
- /package/dist/{game-xiyou-CAT0ilSj.mjs → game-xiyou-uwsv23AC.mjs} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { a as resolveDingtalkAccount } from "./accounts-BAzdqkAV.mjs";
|
|
2
|
-
import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-
|
|
3
|
-
import { n as createLoggerFromConfig } from "./logger-
|
|
2
|
+
import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-DbqGRyhI.mjs";
|
|
3
|
+
import { n as createLoggerFromConfig, r as isDingtalkDebug } from "./logger-CnBTcwyq.mjs";
|
|
4
4
|
import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
|
|
5
5
|
import { i as getOapiAccessToken } from "./utils-CIfI_3Jh.mjs";
|
|
6
|
-
import { S as finalClean, _ as FINAL_TAG, a as sendTextMessage, b as estimateTokens, d as finishAICard, g as unregisterActiveCard, h as streamAICard, i as sendProactive, m as registerActiveCard, p as isQpsLimitError, r as sendMessage, t as sendMarkdownMessage, u as createAICardForTarget, v as PROCESS_TAG, x as extractFinal, y as displayClean } from "./messaging-
|
|
6
|
+
import { S as finalClean, _ as FINAL_TAG, a as sendTextMessage, b as estimateTokens, d as finishAICard, g as unregisterActiveCard, h as streamAICard, i as sendProactive, m as registerActiveCard, p as isQpsLimitError, r as sendMessage, t as sendMarkdownMessage, u as createAICardForTarget, v as PROCESS_TAG, x as extractFinal, y as displayClean } from "./messaging-CK0Wz3Ze.mjs";
|
|
7
7
|
import { a as QUEUE_BUSY_ACK_PHRASES, n as normalizeSlashCommand, t as buildSessionContext } from "./session-DJ4jYqPv.mjs";
|
|
8
8
|
import { c as formatSenderRoles, f as getUserProfile, h as recallEmotionReply, l as getAccessToken, o as formatHiredDateShanghai, r as addEmotionReply, s as formatSenderDisplayLabel, t as DINGTALK_API, u as getOapiAccessToken$1 } from "./utils-legacy-DQ3HeGHh.mjs";
|
|
9
|
-
import "./
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
9
|
+
import { r as trackBackgroundWork } from "./background-work-DkaSDqWk.mjs";
|
|
10
|
+
import "./chunk-upload-CIU5G_ay.mjs";
|
|
11
|
+
import { a as toLocalPath, i as VIDEO_MARKER_PATTERN, o as uploadMediaToDingTalk, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-BfIYs1yC.mjs";
|
|
12
|
+
import { n as lookupCardContent, t as collectCardLookupIds } from "./card-content-cache-DT_OYThh.mjs";
|
|
12
13
|
import * as fs from "fs";
|
|
13
14
|
import * as path from "path";
|
|
14
15
|
import * as os from "node:os";
|
|
@@ -62,7 +63,7 @@ function resolveAgentWorkspaceDir(cfg, agentId) {
|
|
|
62
63
|
* 直接复用 media.ts 权威实现,避免 reply 路径与 message 路径两套逻辑分叉。
|
|
63
64
|
*/
|
|
64
65
|
async function processLocalImages(content, oapiToken, log) {
|
|
65
|
-
const { processLocalImages: impl } = await import("./media-
|
|
66
|
+
const { processLocalImages: impl } = await import("./media-DDtX37T2.mjs");
|
|
66
67
|
return impl(content, oapiToken, log);
|
|
67
68
|
}
|
|
68
69
|
//#endregion
|
|
@@ -261,10 +262,36 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
261
262
|
accountId
|
|
262
263
|
});
|
|
263
264
|
const log = createLoggerFromConfig(account.config, `DingTalk:${accountId}`);
|
|
264
|
-
let currentCardTarget = null;
|
|
265
|
+
let currentCardTarget = preCreatedCard ?? null;
|
|
265
266
|
let accumulatedText = "";
|
|
266
267
|
const deliveredFinalTexts = /* @__PURE__ */ new Set();
|
|
267
268
|
let sessionClosed = false;
|
|
269
|
+
/**
|
|
270
|
+
* 群聊 fail-before-reply 时 OpenClaw 会把用户可见文案收成 NO_REPLY(静默),
|
|
271
|
+
* 但 payload 仍带 rawError/isError。normalize 会在 deliver 前丢掉整包,
|
|
272
|
+
* 导致 closeStreaming 空文本 → answerCard 误定格「思考完成」。
|
|
273
|
+
* 用 onSkip 捕获后写入此处,供 closeStreaming 定稿错误卡。
|
|
274
|
+
*/
|
|
275
|
+
let pendingErrorText = null;
|
|
276
|
+
if (preCreatedCard && !isDirect) registerActiveCard(conversationId, preCreatedCard);
|
|
277
|
+
if (preCreatedCard) log.info(`[DingTalk] 启动即挂接即时建卡 currentCardTarget=${preCreatedCard.cardInstanceId} isDirect=${isDirect}`);
|
|
278
|
+
/** 从 isError/rawError payload 提取中文错误文案(含被静默丢弃的群聊失败) */
|
|
279
|
+
const captureErrorFromPayload = (payload, source) => {
|
|
280
|
+
const p = payload;
|
|
281
|
+
if (!p) return null;
|
|
282
|
+
const raw = readPayloadRawError(p);
|
|
283
|
+
const text = typeof p.text === "string" ? p.text.trim() : "";
|
|
284
|
+
const looksSilent = !text || /^NO_REPLY$/i.test(text) || text.split(/\s+/).every((t) => /^NO_REPLY$/i.test(t));
|
|
285
|
+
let errorText = null;
|
|
286
|
+
if (raw) errorText = matchModelErrorText(raw, { includeCatchAll: true });
|
|
287
|
+
if (!errorText && p.isError && text && !looksSilent) errorText = matchModelErrorText(text, { includeCatchAll: true }) ?? text;
|
|
288
|
+
if (!errorText && (raw || p.isError)) errorText = MODEL_ERROR_GENERIC;
|
|
289
|
+
if (!errorText) return null;
|
|
290
|
+
pendingErrorText = errorText;
|
|
291
|
+
if (!accumulatedText.trim() || looksSilent) accumulatedText = errorText;
|
|
292
|
+
log.info(`[DingTalk][errorCapture] source=${source} len=${errorText.length} preview=${errorText.slice(0, 60)}`);
|
|
293
|
+
return errorText;
|
|
294
|
+
};
|
|
268
295
|
let asyncModeFullResponse = "";
|
|
269
296
|
let currentToolLine = "";
|
|
270
297
|
/**
|
|
@@ -415,10 +442,11 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
415
442
|
}
|
|
416
443
|
}
|
|
417
444
|
};
|
|
418
|
-
|
|
445
|
+
const writePromise = streamWriteChain.then(() => runWrite(force));
|
|
446
|
+
streamWriteChain = writePromise.catch((e) => {
|
|
419
447
|
log.warn(`[DingTalk][stream] 队列任务失败(不中断后续):${e?.message || e}`);
|
|
420
448
|
});
|
|
421
|
-
return
|
|
449
|
+
return writePromise;
|
|
422
450
|
};
|
|
423
451
|
/** 定稿前:取消尾随定时器,强制推送全文并等待队列排空 */
|
|
424
452
|
const flushCardStream = async (content, card) => {
|
|
@@ -559,8 +587,13 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
559
587
|
clearTrailingFlush();
|
|
560
588
|
await streamWriteChain;
|
|
561
589
|
let finalText = finalClean(pickFinalText());
|
|
590
|
+
if (!finalText.trim() && pendingErrorText) {
|
|
591
|
+
finalText = pendingErrorText;
|
|
592
|
+
log.info(`[DingTalk][closeStreaming] 使用 pendingErrorText 定稿(群聊静默失败恢复),len=${finalText.length}`);
|
|
593
|
+
}
|
|
562
594
|
const hadRealAnswer = finalText.trim().length > 0;
|
|
563
|
-
|
|
595
|
+
const isErrorFinal = Boolean(pendingErrorText);
|
|
596
|
+
log.info(`[DingTalk][closeStreaming] 最终答案来源=${finalMarkedText !== null ? "marker[-final-]" : isErrorFinal ? "pendingErrorText" : lastAnswerText ? "非reasoning/累积取长" : "accumulatedText兜底"},长度=${finalText.length},lastAnswerLen=${lastAnswerText.length},accLen=${accumulatedText.length},isErrorFinal=${isErrorFinal}`);
|
|
564
597
|
if (!finalText.trim()) {
|
|
565
598
|
finalText = "✅ 任务执行完成(无文本输出)";
|
|
566
599
|
log.info(`[DingTalk][closeStreaming] 累积文本为空,使用默认提示文案`);
|
|
@@ -573,22 +606,20 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
573
606
|
type: "group",
|
|
574
607
|
openConversationId: conversationId
|
|
575
608
|
};
|
|
576
|
-
console.log(`[DingTalk][LocalImage] closeStreaming 开始媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length}`);
|
|
577
609
|
const mdImgCount = (finalText.match(/!\[[^\]]*\]\([^)]+\)/g) || []).length;
|
|
578
|
-
|
|
610
|
+
if (mdImgCount > 0 || isDingtalkDebug()) log.info(`[DingTalk][LocalImage] closeStreaming 媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length} mdImgs=${mdImgCount}`);
|
|
579
611
|
if (oapiToken) {
|
|
580
612
|
const beforeImg = finalText;
|
|
581
613
|
finalText = await processLocalImages(finalText, oapiToken, log);
|
|
582
|
-
if (beforeImg !== finalText)
|
|
614
|
+
if (beforeImg !== finalText) log.info(`[DingTalk][LocalImage] closeStreaming 图片已处理 | ${beforeImg.length}→${finalText.length}`);
|
|
583
615
|
try {
|
|
584
|
-
const { hasResidualLocalMdImagesOutsideCode } = await import("./media-
|
|
585
|
-
if (hasResidualLocalMdImagesOutsideCode(finalText))
|
|
616
|
+
const { hasResidualLocalMdImagesOutsideCode } = await import("./media-DDtX37T2.mjs");
|
|
617
|
+
if (hasResidualLocalMdImagesOutsideCode(finalText)) log.warn(`[DingTalk][LocalImage] closeStreaming 定稿后仍有代码块外的本地 MD 图未上传,钉钉可能灰图`);
|
|
586
618
|
} catch {}
|
|
587
619
|
finalText = await processVideoMarkers(finalText, "", account.config, oapiToken, log, true, target);
|
|
588
620
|
finalText = await processAudioMarkers(finalText, "", account.config, oapiToken, log, true, target);
|
|
589
621
|
finalText = await uploadAndReplaceFileMarkers(finalText, "", account.config, oapiToken, log, true, target);
|
|
590
|
-
|
|
591
|
-
const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
|
|
622
|
+
const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
|
|
592
623
|
finalText = await processRawMediaPaths(finalText, account.config, oapiToken, log, target);
|
|
593
624
|
log.info(`[DingTalk][closeStreaming] processRawMediaPaths 处理完成`);
|
|
594
625
|
} else log.warn(`[DingTalk][closeStreaming] oapiToken 为空,跳过媒体处理`);
|
|
@@ -602,7 +633,7 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
602
633
|
}
|
|
603
634
|
} else log.info(`[DingTalk][closeStreaming] 养成系统:onCommandOutput 监听到 ${productsToProcess.size} 个 dws 产品: ${[...productsToProcess].join(", ")}`);
|
|
604
635
|
if (productsToProcess.size > 0) {
|
|
605
|
-
const { GamificationEngine } = await import("./game-xiyou-
|
|
636
|
+
const { GamificationEngine } = await import("./game-xiyou-uwsv23AC.mjs");
|
|
606
637
|
const engine = GamificationEngine.getInstanceForUser(senderId);
|
|
607
638
|
if (engine.isEnabled()) {
|
|
608
639
|
const primaryProduct = [...productsToProcess][0];
|
|
@@ -632,7 +663,15 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
632
663
|
const answerTplId = (account.config?.answerCardTemplateId)?.trim() || "d246b7f5-1783-4e9b-bb46-bef52d63050e.schema";
|
|
633
664
|
const answerActToken = Number(account.config?.answerActToken) || 500;
|
|
634
665
|
const answerTokens = estimateTokens(finalText);
|
|
635
|
-
if (
|
|
666
|
+
if (isErrorFinal) {
|
|
667
|
+
log.info(`[DingTalk][closeStreaming] answerCard 模式:错误定稿原卡(len=${finalText.length}),不建答案卡、不定格思考完成`);
|
|
668
|
+
try {
|
|
669
|
+
await flushCardStream(finalText, cardSnapshot);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
log.warn(`[DingTalk][closeStreaming] 错误终稿 flush 失败(继续 FINISH):${e?.message || e}`);
|
|
672
|
+
}
|
|
673
|
+
await finishAICard(cardSnapshot, finalText, account.config, log, void 0, void 0, conversationId);
|
|
674
|
+
} else if (!hadRealAnswer) {
|
|
636
675
|
log.info(`[DingTalk][closeStreaming] answerCard 模式:无真实答案,仅定格原卡思考完成(不建答案卡)`);
|
|
637
676
|
await finalizeOriginalToDone();
|
|
638
677
|
} else if (answerTokens <= answerActToken) {
|
|
@@ -686,14 +725,63 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
686
725
|
accumulatedText = "";
|
|
687
726
|
}
|
|
688
727
|
};
|
|
728
|
+
/**
|
|
729
|
+
* 统一错误定稿:模型/dispatch 异常时把中文提示写到 AI Card 并 FINISHED。
|
|
730
|
+
* 供 dispatcher onError 与 message-handler catch(同步 throw)共用。
|
|
731
|
+
* 不依赖 OpenClaw 返回的 replyOptions.onError(SDK 根本不导出该字段)。
|
|
732
|
+
*/
|
|
733
|
+
const finalizeDispatchError = async (error, kind = "invoke") => {
|
|
734
|
+
const errorMsg = String(error);
|
|
735
|
+
log.error(`[DingTalk][finalizeDispatchError] kind=${kind} failed: ${errorMsg}`);
|
|
736
|
+
await startStreaming();
|
|
737
|
+
const errorText = matchModelErrorText(errorMsg, { includeCatchAll: true }) ?? "⚠️ 模型请求异常,请稍后重试";
|
|
738
|
+
log.warn(`[DingTalk][finalizeDispatchError] 错误提示: ${errorText.slice(0, 80)}`);
|
|
739
|
+
pendingErrorText = errorText;
|
|
740
|
+
accumulatedText = errorText;
|
|
741
|
+
if (!currentCardTarget && !sessionClosed) if (preCreatedCard) try {
|
|
742
|
+
currentCardTarget = preCreatedCard;
|
|
743
|
+
if (!isDirect) registerActiveCard(conversationId, preCreatedCard);
|
|
744
|
+
log.info(`[DingTalk][finalizeDispatchError] 复用即时建卡显示错误(cardInstanceId=${preCreatedCard.cardInstanceId})`);
|
|
745
|
+
} catch (cardErr) {
|
|
746
|
+
log.warn(`[DingTalk][finalizeDispatchError] 复用即时建卡失败:${cardErr.message},降级发送普通消息`);
|
|
747
|
+
try {
|
|
748
|
+
await sendMessage(account.config, sessionWebhook, accumulatedText, {
|
|
749
|
+
useMarkdown: false,
|
|
750
|
+
log: params.runtime.log
|
|
751
|
+
});
|
|
752
|
+
} catch (sendErr) {
|
|
753
|
+
log.error(`[DingTalk][finalizeDispatchError] 降级发送普通消息失败:${sendErr.message}`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
log.warn(`[DingTalk][finalizeDispatchError] 无可用 AI Card,降级发送普通消息`);
|
|
758
|
+
try {
|
|
759
|
+
await sendMessage(account.config, sessionWebhook, accumulatedText, {
|
|
760
|
+
useMarkdown: false,
|
|
761
|
+
log: params.runtime.log
|
|
762
|
+
});
|
|
763
|
+
} catch (sendErr) {
|
|
764
|
+
log.error(`[DingTalk][finalizeDispatchError] 降级发送普通消息失败:${sendErr.message}`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
await closeStreaming();
|
|
768
|
+
};
|
|
689
769
|
const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
690
770
|
...prefixOptions,
|
|
691
771
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId),
|
|
772
|
+
onSkip: (payload, info) => {
|
|
773
|
+
const raw = readPayloadRawError(payload);
|
|
774
|
+
const isErr = Boolean(payload?.isError);
|
|
775
|
+
if (!raw && !isErr) return;
|
|
776
|
+
log.warn(`[DingTalk][onSkip] 群聊静默失败 kind=${info?.kind} reason=${info?.reason} rawLen=${raw.length}`);
|
|
777
|
+
captureErrorFromPayload(payload, `onSkip:${info?.kind ?? "?"}:${info?.reason ?? "?"}`);
|
|
778
|
+
},
|
|
692
779
|
onReplyStart: () => {
|
|
693
780
|
log.info(`[DingTalk][onReplyStart] 开始回复,流式 enabled=${streamingEnabled}`);
|
|
694
781
|
deliveredFinalTexts.clear();
|
|
695
782
|
finalMarkedText = null;
|
|
696
783
|
lastAnswerText = "";
|
|
784
|
+
pendingErrorText = null;
|
|
697
785
|
processMarkerLogged = false;
|
|
698
786
|
finalMarkerLogged = false;
|
|
699
787
|
markerSystemActive = false;
|
|
@@ -710,6 +798,7 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
710
798
|
let text = payload.text ?? "";
|
|
711
799
|
const rawError = readPayloadRawError(payload);
|
|
712
800
|
if (rawError) {
|
|
801
|
+
captureErrorFromPayload(payload, `deliver:${info?.kind ?? "?"}`);
|
|
713
802
|
const matchedErrorText = matchModelErrorText(rawError, { includeCatchAll: true });
|
|
714
803
|
if (matchedErrorText) {
|
|
715
804
|
log.warn(`[DingTalk][deliver] 检测到上游 rawError,替换为中文提示(raw=${rawError.slice(0, 120)})`);
|
|
@@ -720,14 +809,22 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
720
809
|
observeReply(payload.text, payload);
|
|
721
810
|
await startStreaming();
|
|
722
811
|
if (isNonAnswerPayload(payload)) {
|
|
812
|
+
const isModelErrorFinal = Boolean(payload.isError) && info?.kind === "final" && Boolean(text.trim());
|
|
813
|
+
if (isModelErrorFinal) {
|
|
814
|
+
accumulatedText = text;
|
|
815
|
+
log.info(`[DingTalk][deliver] 将上游异常 final 文本存入 accumulatedText(len=${text.length}),防止 onIdle 覆盖`);
|
|
816
|
+
} else if (payload.isError && !accumulatedText.trim() && text.trim()) {
|
|
817
|
+
accumulatedText = text;
|
|
818
|
+
log.info(`[DingTalk][deliver] 将上游异常 payload 文本存入 accumulatedText(len=${text.length}),防止 onIdle 覆盖`);
|
|
819
|
+
}
|
|
723
820
|
if (streamingEnabled && currentCardTarget && !asyncMode && finalMarkedText === null) try {
|
|
724
|
-
await enqueueCardStream(displayClean(text));
|
|
821
|
+
await enqueueCardStream(displayClean(text), { force: isModelErrorFinal });
|
|
725
822
|
} catch (e) {
|
|
726
823
|
if (!isQpsLimitError(e)) log.warn(`[DingTalk][deliver] 状态/错误 payload 写卡失败:${e?.message || e}`);
|
|
727
824
|
}
|
|
728
|
-
if (
|
|
729
|
-
|
|
730
|
-
|
|
825
|
+
if (isModelErrorFinal) {
|
|
826
|
+
log.info(`[DingTalk][deliver] 上游 isError final,主动 closeStreaming 定稿错误卡`);
|
|
827
|
+
await closeStreaming();
|
|
731
828
|
}
|
|
732
829
|
log.info(`[DingTalk][deliver] 非答案 payload(isError=${payload.isError},isStatusNotice=${payload.isStatusNotice}),仅展示不计入最终答案`);
|
|
733
830
|
return;
|
|
@@ -744,7 +841,7 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
744
841
|
const oapiToken = await getOapiAccessToken(account.config);
|
|
745
842
|
if (oapiToken) {
|
|
746
843
|
log.info(`[DingTalk][deliver] 检测到 final 响应,准备处理裸露文件路径`);
|
|
747
|
-
const { processRawMediaPaths } = await import("./media-
|
|
844
|
+
const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
|
|
748
845
|
text = await processRawMediaPaths(text, account.config, oapiToken, log, target);
|
|
749
846
|
log.info(`[DingTalk][deliver] 裸露文件路径处理完成`);
|
|
750
847
|
}
|
|
@@ -838,40 +935,9 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
838
935
|
}
|
|
839
936
|
},
|
|
840
937
|
onError: async (error, info) => {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
await startStreaming();
|
|
845
|
-
const errorText = matchModelErrorText(errorMsg, { includeCatchAll: true }) ?? "⚠️ 模型请求异常,请稍后重试";
|
|
846
|
-
log.warn(`[DingTalk][onError] 错误提示: ${errorText.slice(0, 80)}`);
|
|
847
|
-
if (!accumulatedText.trim()) accumulatedText = errorText;
|
|
848
|
-
if (!currentCardTarget && !sessionClosed) if (preCreatedCard) try {
|
|
849
|
-
currentCardTarget = preCreatedCard;
|
|
850
|
-
if (!isDirect) registerActiveCard(conversationId, preCreatedCard);
|
|
851
|
-
log.info(`[DingTalk][onError] 复用即时建卡显示错误(cardInstanceId=${preCreatedCard.cardInstanceId})`);
|
|
852
|
-
} catch (cardErr) {
|
|
853
|
-
log.warn(`[DingTalk][onError] 复用即时建卡失败:${cardErr.message},降级发送普通消息`);
|
|
854
|
-
try {
|
|
855
|
-
await sendMessage(account.config, sessionWebhook, accumulatedText, {
|
|
856
|
-
useMarkdown: false,
|
|
857
|
-
log: params.runtime.log
|
|
858
|
-
});
|
|
859
|
-
} catch (sendErr) {
|
|
860
|
-
log.error(`[DingTalk][onError] 降级发送普通消息失败:${sendErr.message}`);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
log.warn(`[DingTalk][onError] 卡片已关闭且无即时建卡,降级发送普通消息`);
|
|
865
|
-
try {
|
|
866
|
-
await sendMessage(account.config, sessionWebhook, accumulatedText, {
|
|
867
|
-
useMarkdown: false,
|
|
868
|
-
log: params.runtime.log
|
|
869
|
-
});
|
|
870
|
-
} catch (sendErr) {
|
|
871
|
-
log.error(`[DingTalk][onError] 降级发送普通消息失败:${sendErr.message}`);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
await closeStreaming();
|
|
938
|
+
log.error(`[DingTalk][onError] ${info.kind} reply failed: ${String(error)}`);
|
|
939
|
+
params.runtime.error?.(`dingtalk[${account.accountId}] ${info.kind} reply failed: ${String(error)}`);
|
|
940
|
+
await finalizeDispatchError(error, info.kind);
|
|
875
941
|
typingCallbacks.onIdle?.();
|
|
876
942
|
},
|
|
877
943
|
onIdle: async () => {
|
|
@@ -905,7 +971,7 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
905
971
|
currentToolLine = "";
|
|
906
972
|
await startStreaming();
|
|
907
973
|
if (currentCardTarget) try {
|
|
908
|
-
const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-
|
|
974
|
+
const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
|
|
909
975
|
await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim(), { force: true });
|
|
910
976
|
} catch (err) {
|
|
911
977
|
if (!isQpsLimitError(err)) log.error(`[DingTalk][onPartialReply] final-marker 刷卡失败:${err.message}`);
|
|
@@ -918,7 +984,7 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
918
984
|
if (payload.text?.trim()) hasModelStreamText = true;
|
|
919
985
|
currentToolLine = "";
|
|
920
986
|
try {
|
|
921
|
-
const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-
|
|
987
|
+
const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
|
|
922
988
|
await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim());
|
|
923
989
|
} catch (err) {
|
|
924
990
|
if (isQpsLimitError(err)) log.warn(`[DingTalk][onPartialReply] QPS 限流,已排队尾随重试 latestLen=${latestCardContent.length}`);
|
|
@@ -957,12 +1023,20 @@ function createDingtalkReplyDispatcher(params) {
|
|
|
957
1023
|
}
|
|
958
1024
|
},
|
|
959
1025
|
markDispatchIdle,
|
|
960
|
-
getAsyncModeResponse: () => asyncModeFullResponse
|
|
1026
|
+
getAsyncModeResponse: () => asyncModeFullResponse,
|
|
1027
|
+
/** 同步 dispatch 异常时由 message-handler catch 调用,统一 finish 错误卡 */
|
|
1028
|
+
finalizeDispatchError
|
|
961
1029
|
};
|
|
962
1030
|
}
|
|
963
1031
|
//#endregion
|
|
964
1032
|
//#region src/core/message-handler.ts
|
|
965
1033
|
/**
|
|
1034
|
+
* 单条会话队列任务最长执行时间。
|
|
1035
|
+
* 503 重试 + 工具循环偶发挂死时,若不超时,后续同会话消息会永远堵在 previousTask 上,
|
|
1036
|
+
* 表现:连发多条「网关没反应 / 不贴表情」(表情在排队 ACK 或 dequeued 后才贴)。
|
|
1037
|
+
*/
|
|
1038
|
+
const SESSION_QUEUE_TASK_TIMEOUT_MS = 180 * 1e3;
|
|
1039
|
+
/**
|
|
966
1040
|
* 会话消息队列管理
|
|
967
1041
|
* 用于确保同一会话+agent的消息按顺序处理,避免并发冲突导致AI返回空响应
|
|
968
1042
|
* 队列键格式:{sessionId}:{agentId}
|
|
@@ -1105,8 +1179,9 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
|
|
|
1105
1179
|
conversationId,
|
|
1106
1180
|
allowConversationRecent: false
|
|
1107
1181
|
}) || "";
|
|
1108
|
-
if (cardText)
|
|
1109
|
-
|
|
1182
|
+
if (cardText) {
|
|
1183
|
+
if (isDingtalkDebug()) console.log(`[DingTalk][Quote] interactiveCard 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cardText.length}`);
|
|
1184
|
+
} else if (isDingtalkDebug()) {
|
|
1110
1185
|
const dump = JSON.stringify(repliedMsg ?? {}).slice(0, 800);
|
|
1111
1186
|
console.log(`[DingTalk][Quote] interactiveCard 无正文且缓存未命中(未使用会话最近兜底) | conv=${conversationId || "-"} keys=${Object.keys(contentObj || {}).join(",")} repliedMsg=${dump}`);
|
|
1112
1187
|
}
|
|
@@ -1129,13 +1204,13 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
|
|
|
1129
1204
|
});
|
|
1130
1205
|
if (cached) {
|
|
1131
1206
|
bodyText = cached;
|
|
1132
|
-
console.log(`[DingTalk][Quote] msgType=${msgType} 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cached.length}`);
|
|
1207
|
+
if (isDingtalkDebug()) console.log(`[DingTalk][Quote] msgType=${msgType} 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cached.length}`);
|
|
1133
1208
|
} else if (/card|interactive/i.test(String(msgType || "")) || ids.length > 0) {
|
|
1134
1209
|
bodyText = "钉钉卡片消息。";
|
|
1135
|
-
console.log(`[DingTalk][Quote] 未知卡片类引用 msgType=${msgType} 已简化占位 | keys=${Object.keys(contentObj || {}).join(",")}`);
|
|
1136
|
-
} else console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")} dump=${JSON.stringify(repliedMsg ?? {}).slice(0, 600)}`);
|
|
1210
|
+
if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知卡片类引用 msgType=${msgType} 已简化占位 | keys=${Object.keys(contentObj || {}).join(",")}`);
|
|
1211
|
+
} else if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")} dump=${JSON.stringify(repliedMsg ?? {}).slice(0, 600)}`);
|
|
1137
1212
|
} catch {
|
|
1138
|
-
console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")}`);
|
|
1213
|
+
if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")}`);
|
|
1139
1214
|
}
|
|
1140
1215
|
break;
|
|
1141
1216
|
}
|
|
@@ -1926,7 +2001,7 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
1926
2001
|
const rawText = content.text || "";
|
|
1927
2002
|
let userContent = normalizeSlashCommand(rawText) || (content.imageUrls.length > 0 ? "请描述这张图片" : "");
|
|
1928
2003
|
try {
|
|
1929
|
-
const { GamificationEngine, isGamificationCommand } = await import("./game-xiyou-
|
|
2004
|
+
const { GamificationEngine, isGamificationCommand } = await import("./game-xiyou-uwsv23AC.mjs");
|
|
1930
2005
|
if (isGamificationCommand(rawText)) {
|
|
1931
2006
|
const engine = GamificationEngine.getInstanceForUser(senderId);
|
|
1932
2007
|
if (rawText.trim().startsWith("/西游") || engine.isEnabled()) {
|
|
@@ -2100,6 +2175,8 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2100
2175
|
log?.warn?.(`Failed to send acknowledgment: ${ackErr?.message || ackErr}`);
|
|
2101
2176
|
}
|
|
2102
2177
|
}
|
|
2178
|
+
let earlyCard = params.preCreatedCard;
|
|
2179
|
+
let finalizeDispatchError;
|
|
2103
2180
|
try {
|
|
2104
2181
|
const core = getDingtalkRuntime();
|
|
2105
2182
|
let finalContent = userContent;
|
|
@@ -2165,7 +2242,6 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2165
2242
|
});
|
|
2166
2243
|
const groupReplyModeForCard = config.groupReplyMode || "aicard";
|
|
2167
2244
|
const streamingEnabledForCard = !(!isDirect && (groupReplyModeForCard === "text" || groupReplyModeForCard === "markdown")) && config.streaming !== false;
|
|
2168
|
-
let earlyCard = params.preCreatedCard;
|
|
2169
2245
|
if (!earlyCard && streamingEnabledForCard && !asyncMode) try {
|
|
2170
2246
|
earlyCard = await createAICardForTarget(config, isDirect ? {
|
|
2171
2247
|
type: "user",
|
|
@@ -2183,7 +2259,7 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2183
2259
|
log.warn(`[DingTalk][earlyCard] 即时建卡失败(降级延迟建卡):${e?.message}`);
|
|
2184
2260
|
earlyCard = void 0;
|
|
2185
2261
|
}
|
|
2186
|
-
const { dispatcher, replyOptions, markDispatchIdle, getAsyncModeResponse } = createDingtalkReplyDispatcher({
|
|
2262
|
+
const { dispatcher, replyOptions, markDispatchIdle, getAsyncModeResponse, finalizeDispatchError: finalizeErr } = createDingtalkReplyDispatcher({
|
|
2187
2263
|
cfg,
|
|
2188
2264
|
agentId: matchedAgentId,
|
|
2189
2265
|
runtime,
|
|
@@ -2196,6 +2272,7 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2196
2272
|
asyncMode,
|
|
2197
2273
|
preCreatedCard: earlyCard
|
|
2198
2274
|
});
|
|
2275
|
+
finalizeDispatchError = finalizeErr;
|
|
2199
2276
|
if (config.clientId) {
|
|
2200
2277
|
const botIdentityHint = `[DingTalk Bot Context] Current bot clientId: ${String(config.clientId)}. When executing \`dws chat message send-by-bot\`, always pass \`--client-id ${String(config.clientId)}\` to ensure messages are sent from the correct bot.`;
|
|
2201
2278
|
finalContent = finalContent ? `${finalContent}\n\n${botIdentityHint}` : botIdentityHint;
|
|
@@ -2239,7 +2316,7 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2239
2316
|
finalText = await processVideoMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
|
|
2240
2317
|
finalText = await processAudioMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
|
|
2241
2318
|
finalText = await uploadAndReplaceFileMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
|
|
2242
|
-
const { processRawMediaPaths } = await import("./media-
|
|
2319
|
+
const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
|
|
2243
2320
|
finalText = await processRawMediaPaths(finalText, config, oapiToken, log, mediaTarget);
|
|
2244
2321
|
}
|
|
2245
2322
|
const textToSend = finalText.trim() || "✅ 任务执行完成(无文本输出)";
|
|
@@ -2265,28 +2342,42 @@ async function handleDingTalkMessageInternal(params) {
|
|
|
2265
2342
|
}
|
|
2266
2343
|
} catch (err) {
|
|
2267
2344
|
log?.error?.(`SDK dispatch 失败: ${err.message}`);
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
await
|
|
2271
|
-
} catch (
|
|
2272
|
-
log?.error?.(
|
|
2345
|
+
if (finalizeDispatchError) try {
|
|
2346
|
+
log?.info?.(`[DingTalk] 统一错误定稿:调用 finalizeDispatchError 处理同步异常`);
|
|
2347
|
+
await finalizeDispatchError(err, "invoke");
|
|
2348
|
+
} catch (finalizeErr) {
|
|
2349
|
+
log?.error?.(`[DingTalk] finalizeDispatchError 失败: ${finalizeErr?.message || finalizeErr}`);
|
|
2273
2350
|
}
|
|
2274
|
-
else
|
|
2275
|
-
const
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2351
|
+
else {
|
|
2352
|
+
const errorText = matchModelErrorText(err.message, { includeCatchAll: true }) ?? `⚠️ 抱歉,处理请求时出错: ${err.message}`;
|
|
2353
|
+
let cardSuccess = false;
|
|
2354
|
+
if (earlyCard) try {
|
|
2355
|
+
await finishAICard(earlyCard, errorText, config, log, void 0, void 0, data.conversationId);
|
|
2356
|
+
cardSuccess = true;
|
|
2357
|
+
log?.info?.(`[DingTalk] 极端兜底:finishAICard 错误卡成功`);
|
|
2358
|
+
} catch (cardErr) {
|
|
2359
|
+
log?.error?.(`更新错误卡片失败: ${cardErr.message}`);
|
|
2360
|
+
}
|
|
2361
|
+
if (!cardSuccess) {
|
|
2362
|
+
log?.warn?.(`[DingTalk] 无可用错误定稿路径,走纯文本发送`);
|
|
2363
|
+
try {
|
|
2364
|
+
const token = await getAccessToken(config);
|
|
2365
|
+
const body = {
|
|
2366
|
+
msgtype: "text",
|
|
2367
|
+
text: { content: errorText }
|
|
2368
|
+
};
|
|
2369
|
+
if (!isDirect) body.at = {
|
|
2370
|
+
atUserIds: [senderId],
|
|
2371
|
+
isAtAll: false
|
|
2372
|
+
};
|
|
2373
|
+
await dingtalkHttp.post(sessionWebhook, body, { headers: {
|
|
2374
|
+
"x-acs-dingtalk-access-token": token,
|
|
2375
|
+
"Content-Type": "application/json"
|
|
2376
|
+
} });
|
|
2377
|
+
} catch (fallbackErr) {
|
|
2378
|
+
log?.error?.(`错误消息发送也失败: ${fallbackErr.message}`);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2290
2381
|
}
|
|
2291
2382
|
}
|
|
2292
2383
|
try {
|
|
@@ -2334,54 +2425,33 @@ async function handleDingTalkMessage(params) {
|
|
|
2334
2425
|
const queueKey = `${baseSessionId}:${matchedAgentId}`;
|
|
2335
2426
|
try {
|
|
2336
2427
|
sessionLastActivity.set(queueKey, Date.now());
|
|
2337
|
-
const isQueueBusy = sessionQueues.has(queueKey);
|
|
2338
2428
|
const previousTask = sessionQueues.get(queueKey) || Promise.resolve();
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
});
|
|
2350
|
-
log?.info?.(`[队列] 队列繁忙,已发送普通文本 ACK(groupReplyMode=${groupReplyMode})`);
|
|
2351
|
-
} catch (ackErr) {
|
|
2352
|
-
log?.warn?.(`[队列] 发送普通 ACK 失败: ${ackErr?.message || ackErr}`);
|
|
2429
|
+
const isQueueBusy = sessionQueues.has(queueKey);
|
|
2430
|
+
const cardHolder = {
|
|
2431
|
+
card: void 0,
|
|
2432
|
+
ackDone: !isQueueBusy
|
|
2433
|
+
};
|
|
2434
|
+
const currentTask = previousTask.then(async () => {
|
|
2435
|
+
log?.info?.(`[队列] 开始处理消息,queueKey=${queueKey} wasBusy=${isQueueBusy} timeoutMs=${SESSION_QUEUE_TASK_TIMEOUT_MS}`);
|
|
2436
|
+
if (isQueueBusy && !cardHolder.ackDone) {
|
|
2437
|
+
const waitStart = Date.now();
|
|
2438
|
+
while (!cardHolder.ackDone && Date.now() - waitStart < 2e3) await new Promise((r) => setTimeout(r, 50));
|
|
2353
2439
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
} else log?.warn?.(`[队列] 创建排队 ACK Card 失败(返回 null),跳过 ACK`);
|
|
2369
|
-
addEmotionReply(config, data, log).catch((err) => {
|
|
2370
|
-
log?.warn?.(`[队列] 贴排队表情失败: ${err.message}`);
|
|
2371
|
-
});
|
|
2372
|
-
} catch (ackErr) {
|
|
2373
|
-
log?.warn?.(`[队列] 创建排队 ACK Card 异常: ${ackErr?.message || ackErr}`);
|
|
2374
|
-
}
|
|
2440
|
+
let timeoutId;
|
|
2441
|
+
try {
|
|
2442
|
+
await Promise.race([handleDingTalkMessageInternal({
|
|
2443
|
+
...params,
|
|
2444
|
+
preCreatedCard: cardHolder.card,
|
|
2445
|
+
emotionAlreadyAdded: true
|
|
2446
|
+
}), new Promise((_, reject) => {
|
|
2447
|
+
timeoutId = setTimeout(() => {
|
|
2448
|
+
reject(/* @__PURE__ */ new Error(`会话队列任务超时(${SESSION_QUEUE_TASK_TIMEOUT_MS}ms),释放队列以免堵死后续消息`));
|
|
2449
|
+
}, SESSION_QUEUE_TASK_TIMEOUT_MS);
|
|
2450
|
+
})]);
|
|
2451
|
+
log?.info?.(`[队列] 消息处理完成,queueKey=${queueKey}`);
|
|
2452
|
+
} finally {
|
|
2453
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
2375
2454
|
}
|
|
2376
|
-
}
|
|
2377
|
-
const currentTask = previousTask.then(async () => {
|
|
2378
|
-
log?.info?.(`[队列] 开始处理消息,queueKey=${queueKey}`);
|
|
2379
|
-
await handleDingTalkMessageInternal({
|
|
2380
|
-
...params,
|
|
2381
|
-
preCreatedCard,
|
|
2382
|
-
emotionAlreadyAdded: isQueueBusy
|
|
2383
|
-
});
|
|
2384
|
-
log?.info?.(`[队列] 消息处理完成,queueKey=${queueKey}`);
|
|
2385
2455
|
}).catch((err) => {
|
|
2386
2456
|
log?.error?.(`[队列] 消息处理异常,queueKey=${queueKey}, error=${err.message}`);
|
|
2387
2457
|
}).finally(() => {
|
|
@@ -2391,9 +2461,51 @@ async function handleDingTalkMessage(params) {
|
|
|
2391
2461
|
}
|
|
2392
2462
|
});
|
|
2393
2463
|
sessionQueues.set(queueKey, currentTask);
|
|
2464
|
+
trackBackgroundWork(currentTask, queueKey);
|
|
2465
|
+
log?.info?.(`[队列] 已登记 queueKey=${queueKey} busy=${isQueueBusy} depth≈pending`);
|
|
2466
|
+
addEmotionReply(config, data, log).catch((err) => {
|
|
2467
|
+
log?.warn?.(`[队列] 即时贴表情失败: ${err?.message || err}`);
|
|
2468
|
+
});
|
|
2469
|
+
if (isQueueBusy) {
|
|
2470
|
+
const ackPhrases = QUEUE_BUSY_ACK_PHRASES;
|
|
2471
|
+
const ackText = ackPhrases[Math.floor(Math.random() * ackPhrases.length)];
|
|
2472
|
+
const groupReplyMode = config.groupReplyMode || "aicard";
|
|
2473
|
+
const skipAICard = !isDirect && (groupReplyMode === "text" || groupReplyMode === "markdown");
|
|
2474
|
+
(async () => {
|
|
2475
|
+
try {
|
|
2476
|
+
if (skipAICard) {
|
|
2477
|
+
await sendProactive(config, { openConversationId: data.conversationId }, ackText, {
|
|
2478
|
+
msgType: "text",
|
|
2479
|
+
useAICard: false,
|
|
2480
|
+
fallbackToNormal: true
|
|
2481
|
+
});
|
|
2482
|
+
log?.info?.(`[队列] 队列繁忙,已发送普通文本 ACK(groupReplyMode=${groupReplyMode})`);
|
|
2483
|
+
} else {
|
|
2484
|
+
const card = await createAICardForTarget(config, isDirect ? {
|
|
2485
|
+
type: "user",
|
|
2486
|
+
userId: senderId
|
|
2487
|
+
} : {
|
|
2488
|
+
type: "group",
|
|
2489
|
+
openConversationId: data.conversationId
|
|
2490
|
+
}, log);
|
|
2491
|
+
if (card) {
|
|
2492
|
+
await streamAICard(card, ackText, false, config, log);
|
|
2493
|
+
cardHolder.card = card;
|
|
2494
|
+
log?.info?.(`[队列] 队列繁忙,已创建排队 ACK Card,cardInstanceId=${card.cardInstanceId}`);
|
|
2495
|
+
} else log?.warn?.(`[队列] 创建排队 ACK Card 失败(返回 null)`);
|
|
2496
|
+
}
|
|
2497
|
+
} catch (ackErr) {
|
|
2498
|
+
log?.warn?.(`[队列] 繁忙 ACK 异常: ${ackErr?.message || ackErr}`);
|
|
2499
|
+
} finally {
|
|
2500
|
+
cardHolder.ackDone = true;
|
|
2501
|
+
}
|
|
2502
|
+
})();
|
|
2503
|
+
}
|
|
2394
2504
|
} catch (err) {
|
|
2395
2505
|
log?.error?.(`[队列] 队列管理异常,直接处理: ${err.message}`);
|
|
2396
|
-
handleDingTalkMessageInternal(params)
|
|
2506
|
+
trackBackgroundWork(handleDingTalkMessageInternal(params).catch((e) => {
|
|
2507
|
+
log?.error?.(`[队列] 直接处理也失败: ${e.message}`);
|
|
2508
|
+
}), "queue-fallback");
|
|
2397
2509
|
}
|
|
2398
2510
|
}
|
|
2399
2511
|
//#endregion
|