@jeik/dingtalk-connector 0.8.28 → 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.
Files changed (40) hide show
  1. package/CHANGELOG.md +30 -31
  2. package/README.en.md +36 -71
  3. package/README.md +46 -74
  4. package/dist/{background-work-Cxb4b-91.mjs → background-work-DkaSDqWk.mjs} +12 -1
  5. package/dist/card-content-cache-C7-XHhHw.mjs +2 -0
  6. package/dist/{card-content-cache-bDQFz2u8.mjs → card-content-cache-DT_OYThh.mjs} +15 -1
  7. package/dist/chunk-upload-BKM1y4Ba.mjs +2 -0
  8. package/dist/{chunk-upload-BE71drPg.mjs → chunk-upload-CIU5G_ay.mjs} +1 -1
  9. package/dist/{common-BZiVgd3A.mjs → common-BfIYs1yC.mjs} +3 -3
  10. package/dist/{common-DgL28gCZ.mjs → common-C0Oaf0z-.mjs} +1 -1
  11. package/dist/{connection-CZ7ZoN5_.mjs → connection-obtrPkj4.mjs} +56 -65
  12. package/dist/entry-bundled.mjs +1 -1
  13. package/dist/{gateway-methods-kNX3qvqw.mjs → gateway-methods-BdcpfxEZ.mjs} +1 -1
  14. package/dist/gateway-methods-DQ6TwOAr.mjs +2 -0
  15. package/dist/index.mjs +2 -2
  16. package/dist/{logger-mZ9OSbmD.mjs → logger-CnBTcwyq.mjs} +16 -2
  17. package/dist/logger-D8H2IcYz.mjs +2 -0
  18. package/dist/{media-BqpUGCni.mjs → media-DDtX37T2.mjs} +1 -1
  19. package/dist/{media-DnQB2BrL.mjs → media-g8tJtpfU.mjs} +29 -20
  20. package/dist/{message-handler-B1usz1wb.mjs → message-handler-BrO2i7tg.mjs} +26 -30
  21. package/dist/{messaging-DVS3UKOp.mjs → messaging-CK0Wz3Ze.mjs} +18 -20
  22. package/dist/{runtime-A6G7WAWn.mjs → runtime-DbqGRyhI.mjs} +6 -5
  23. package/dist/{utils-Bjh4r_qS.mjs → utils-Bwz2Fls_.mjs} +1 -1
  24. package/docs/RELEASE_NOTES_V0.8.28.md +4 -4
  25. package/docs/RELEASE_NOTES_V0.8.29.md +57 -0
  26. package/openclaw.plugin.json +9 -5
  27. package/package.json +1 -1
  28. package/src/config/schema.ts +2 -1
  29. package/src/core/connection.ts +90 -113
  30. package/src/core/message-handler.ts +24 -16
  31. package/src/reply-dispatcher.ts +14 -20
  32. package/src/services/media.ts +60 -27
  33. package/src/services/messaging/card-content-cache.ts +11 -6
  34. package/src/services/messaging.ts +52 -38
  35. package/src/utils/background-work.ts +3 -1
  36. package/src/utils/logger.ts +23 -2
  37. package/dist/card-content-cache-c4Ls96eJ.mjs +0 -2
  38. package/dist/chunk-upload-PwhogwfE.mjs +0 -2
  39. package/dist/gateway-methods-BkI1gKaC.mjs +0 -2
  40. package/dist/logger-BmJkQkm1.mjs +0 -2
@@ -1,5 +1,5 @@
1
1
  import { i as checkAndMarkDingtalkMessage } from "./utils-legacy-DQ3HeGHh.mjs";
2
- import { n as onBackgroundWorkCountChange, t as getActiveBackgroundWorkCount } from "./background-work-Cxb4b-91.mjs";
2
+ import { n as onBackgroundWorkCountChange, t as getActiveBackgroundWorkCount } from "./background-work-DkaSDqWk.mjs";
3
3
  import * as fs from "fs";
4
4
  //#region src/core/connection.ts
5
5
  /**
@@ -64,7 +64,7 @@ let _connectionNoticePrinted = false;
64
64
  function printConnectionNoticeOnce() {
65
65
  if (_connectionNoticePrinted) return;
66
66
  _connectionNoticePrinted = true;
67
- console.log("[dingtalk-connector] ℹ️ 上游 dingtalk-stream 噪音已过滤;须 OPEN+REGISTERED 才报 ready,REGISTERED 超时会强制重连(禁止假 online)。正常运行不应出现 ≤30s 周期性硬重连。");
67
+ console.log("[dingtalk-connector] ℹ️ 上游 dingtalk-stream 噪音已过滤;OPEN 成功即可上线,REGISTERED 尽量等待;若环境不推 REGISTERED,首条消息会确认订阅。正常运行不应出现 ≤30s 周期性硬重连。");
68
68
  }
69
69
  async function monitorSingleAccount(opts) {
70
70
  const { cfg, account, runtime, abortSignal, messageHandler, onStatusChange } = opts;
@@ -72,7 +72,8 @@ async function monitorSingleAccount(opts) {
72
72
  silenceDingtalkStreamConsoleNoise();
73
73
  const clawdbotConfig = cfg;
74
74
  const log = runtime?.log;
75
- const { createLoggerFromConfig } = await import("./logger-BmJkQkm1.mjs");
75
+ const { createLoggerFromConfig, setDingtalkDebug } = await import("./logger-D8H2IcYz.mjs");
76
+ if (account.config?.debug) setDingtalkDebug(true);
76
77
  const logger = createLoggerFromConfig(account.config, `DingTalk:${accountId}`);
77
78
  if (!account.clientId || !account.clientSecret) throw new Error(`[DingTalk][${accountId}] Missing credentials: clientId=${account.clientId ? "present" : "MISSING"}, clientSecret=${account.clientSecret ? "present" : "MISSING"}. Please check your configuration in channels.dingtalk-connector.`);
78
79
  const clientIdStr = String(account.clientId);
@@ -169,13 +170,14 @@ async function monitorSingleAccount(opts) {
169
170
  return Math.min(exponentialDelay + jitter, MAX_BACKOFF_DELAY);
170
171
  }
171
172
  /**
172
- * 等待钉钉 Stream 真正可收消息。
173
+ * 等待钉钉 Stream 可服务。
173
174
  *
174
- * dingtalk-stream 的 client.connect() 在创建 WebSocket 后立刻 resolve,
175
- * **不等** socket open,更不等服务端 SYSTEM/REGISTERED。
175
+ * - OPEN:必须成功,否则 throw
176
+ * - REGISTERED:尽量等待;超时则 **降级 OPEN 上线**(不 throw)
176
177
  *
177
- * 顺序:socket OPEN → SYSTEM topic=REGISTERED(client.registered=true)
178
- * 任一步失败均 throw,禁止 OPEN-only 假 ready。
178
+ * 原因(生产日志):部分环境 socket 已 OPEN 且 CALLBACK 能收消息,
179
+ * 但长期收不到 SYSTEM/REGISTERED 或 client.registered 不被置 true。
180
+ * 若硬性失败会触发网关 auto-restart 风暴,而旧连接/半连接其实仍在处理消息。
179
181
  */
180
182
  async function waitForStreamReady(timeoutMs = STREAM_READY_TIMEOUT_MS) {
181
183
  const started = Date.now();
@@ -209,7 +211,7 @@ async function monitorSingleAccount(opts) {
209
211
  }
210
212
  if (client.registered === true) {
211
213
  logger.info(`✅ Stream 已就绪(OPEN + REGISTERED),耗时 ${Date.now() - started}ms`);
212
- return;
214
+ return { registered: true };
213
215
  }
214
216
  if (await new Promise((resolve) => {
215
217
  const socket = client.socket;
@@ -245,9 +247,10 @@ async function monitorSingleAccount(opts) {
245
247
  socket.on("message", onMessage);
246
248
  }) || client.registered === true) {
247
249
  logger.info(`✅ Stream 已就绪(OPEN + REGISTERED),耗时 ${Date.now() - started}ms`);
248
- return;
250
+ return { registered: true };
249
251
  }
250
- throw new Error(`SYSTEM/REGISTERED 超时(${timeoutMs}ms):socket 已 OPEN 但订阅未生效,禁止报 connected`);
252
+ logger.warn(`⚠️ 未观察到 SYSTEM/REGISTERED(${timeoutMs}ms),socket 已 OPEN → 降级上线。 收到首条机器人消息后会记为「订阅 empirically 生效」。`);
253
+ return { registered: false };
251
254
  }
252
255
  /**
253
256
  * connect 之后、wait ready 之前挂上 pong/message/close。
@@ -261,26 +264,27 @@ async function monitorSingleAccount(opts) {
261
264
  setupCloseListener();
262
265
  }
263
266
  /**
264
- * 单次:connect + 挂 listener + 等 OPEN/REGISTERED。
265
- * 失败 throw,不报 connected。
267
+ * 单次:connect + 挂 listener + 等 OPEN(REGISTERED 可选)。
268
+ * 仅 OPEN 失败 throw;REGISTERED 超时则降级成功。
266
269
  */
267
- async function connectAndWaitRegistered() {
270
+ async function connectAndWaitReady() {
268
271
  await client.connect();
269
272
  attachSocketLifecycleListeners();
270
- await waitForStreamReady(STREAM_READY_TIMEOUT_MS);
271
- if (client.registered !== true) throw new Error("connect 后 client.registered 仍为 false");
273
+ return await waitForStreamReady(STREAM_READY_TIMEOUT_MS);
272
274
  }
273
275
  /**
274
- * 直到 REGISTERED 成功或次数用尽。
275
- * 用于初次启动与硬重连:绝不在未订阅时对外 connected=true。
276
+ * 建立可服务连接。
277
+ * - 优先拿到 REGISTERED
278
+ * - OPEN 成功即可上线(避免环境不推 REGISTERED 时 restart 风暴)
279
+ * - 仅在 OPEN 都失败时多轮重试
276
280
  */
277
281
  async function ensureRegisteredConnection(maxAttempts = REGISTERED_CONNECT_MAX_ATTEMPTS) {
278
282
  let lastError;
279
283
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
280
- if (isStopped) throw new Error("连接已停止,中止 REGISTERED 等待");
284
+ if (isStopped) throw new Error("连接已停止,中止 Stream 建立");
281
285
  try {
282
286
  if (attempt > 1) {
283
- logger.warn(`🔄 REGISTERED 未就绪,强制重连 ${attempt}/${maxAttempts}…`);
287
+ logger.warn(`🔄 Stream 建立重试 ${attempt}/${maxAttempts}…`);
284
288
  try {
285
289
  if (client.socket) await client.disconnect();
286
290
  } catch (discErr) {
@@ -288,18 +292,18 @@ async function monitorSingleAccount(opts) {
288
292
  }
289
293
  const delay = Math.min(1e3 * attempt, 5e3);
290
294
  await new Promise((r) => setTimeout(r, delay));
291
- } else logger.info(`⏳ 建立 Stream 并等待 REGISTERED(单次超时 ${STREAM_READY_TIMEOUT_MS}ms,最多 ${maxAttempts} 次)…`);
292
- await connectAndWaitRegistered();
293
- noteSocketAlive("registered-ok");
295
+ } else logger.info(`⏳ 建立 Stream(OPEN 必须;REGISTERED 尽量等待 ${STREAM_READY_TIMEOUT_MS}ms)…`);
296
+ const ready = await connectAndWaitReady();
297
+ noteSocketAlive(ready.registered ? "registered-ok" : "open-degraded");
294
298
  connectionEstablishedTime = Date.now();
295
- logger.info(`✅ 订阅已生效 registered=true(attempt ${attempt}/${maxAttempts}, socket=${client.socket?.readyState})`);
299
+ logger.info(`✅ Stream 可服务(attempt ${attempt}/${maxAttempts}, registered=${ready.registered || Boolean(client.registered)}, socket=${client.socket?.readyState})`);
296
300
  return;
297
301
  } catch (err) {
298
302
  lastError = err instanceof Error ? err : new Error(String(err));
299
- logger.warn(`⚠️ Stream 就绪失败 attempt=${attempt}/${maxAttempts}: ${lastError.message}`);
303
+ logger.warn(`⚠️ Stream 建立失败 attempt=${attempt}/${maxAttempts}: ${lastError.message}`);
300
304
  }
301
305
  }
302
- throw new Error(`钉钉 Stream 在 ${maxAttempts} 次尝试后仍未 REGISTERED,拒绝假 connected。 最后错误: ${lastError?.message || "unknown"}`);
306
+ throw new Error(`钉钉 Stream 在 ${maxAttempts} 次尝试后仍无法 OPEN。 最后错误: ${lastError?.message || "unknown"}`);
303
307
  }
304
308
  /** 统一重连函数,带指数退避(无限重连) */
305
309
  async function doReconnect(immediate = false) {
@@ -506,21 +510,19 @@ async function monitorSingleAccount(opts) {
506
510
  receivedCount++;
507
511
  lastMessageTime = Date.now();
508
512
  noteSocketAlive("inbound-callback");
513
+ if (client.registered !== true) {
514
+ client.registered = true;
515
+ logger.info(`✅ 收到机器人 CALLBACK,视为订阅已生效(此前可能未观察到 SYSTEM/REGISTERED)`);
516
+ }
509
517
  onStatusChange?.({ lastInboundAt: Date.now() });
510
518
  const messageId = res.headers?.messageId;
511
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
512
- logger.info(`\n========== 收到新消息 ==========`);
513
- logger.info(`时间:${timestamp}`);
514
- logger.info(`MessageId: ${messageId || "N/A"}`);
515
- logger.info(`Headers: ${JSON.stringify(res.headers || {})}`);
516
- logger.info(`Data 长度:${res.data?.length || 0} 字符`);
517
519
  let acked = false;
518
520
  const ackMessage = (why) => {
519
521
  if (acked || !messageId) return;
520
522
  try {
521
523
  client.socketCallBackResponse(messageId, { success: true });
522
524
  acked = true;
523
- logger.info(`✅ 已确认回调 messageId=${messageId} (${why})`);
525
+ logger.debug(`ACK messageId=${messageId} (${why})`);
524
526
  } catch (ackErr) {
525
527
  logger.warn(`确认回调失败: ${ackErr?.message || ackErr}`);
526
528
  }
@@ -531,37 +533,30 @@ async function monitorSingleAccount(opts) {
531
533
  try {
532
534
  data = JSON.parse(res.data);
533
535
  } catch (parseError) {
534
- logger.error("Failed to parse response data as JSON:", {
535
- error: parseError instanceof Error ? parseError.message : String(parseError),
536
- rawData: typeof res.data === "string" ? res.data.substring(0, 500) : res.data,
537
- dataType: typeof res.data
538
- });
539
- throw new Error(`Invalid JSON response from DingTalk API. Error: ${parseError instanceof Error ? parseError.message : String(parseError)}. Raw data (first 100 chars): ${String(res.data).substring(0, 100)}`);
536
+ logger.error(`JSON 解析失败: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
537
+ throw new Error(`Invalid JSON from DingTalk: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
540
538
  }
541
- logger.info(`\n----- 消息详情 -----`);
542
- logger.info(`消息类型:${data.msgtype || "unknown"}`);
543
- logger.info(`会话类型:${data.conversationType === "1" ? "DM (单聊)" : data.conversationType === "2" ? "Group (群聊)" : data.conversationType}`);
544
- logger.info(`发送者:${data.senderNick || "unknown"} (${data.senderStaffId || data.senderId || "unknown"})`);
545
- logger.info(`会话 ID: ${data.conversationId || "N/A"}`);
546
- logger.info(`消息 ID: ${data.msgId || "N/A"}`);
547
- logger.info(`SessionWebhook: ${data.sessionWebhook ? "已提供" : "未提供"}`);
548
- logger.info(`RobotCode: ${data.robotCode || account.config?.clientId || "N/A"}`);
549
- if (data.chatbotUserId || data.chatbotCorpId) console.log(`[DingTalk:${accountId}] [BotIdentity] accountId=${accountId} chatbotUserId=${data.chatbotUserId || "N/A"} chatbotCorpId=${data.chatbotCorpId || "N/A"}`);
550
539
  const businessMsgId = data.msgId;
540
+ const convType = data.conversationType === "1" ? "DM" : data.conversationType === "2" ? "Group" : String(data.conversationType ?? "?");
541
+ const preview = (data.text?.content || "").trim().slice(0, 80) || data.msgtype || "(no text)";
542
+ logger.info(`📩 入站 ${convType} from=${data.senderNick || data.senderStaffId || data.senderId || "?"} msg=${businessMsgId || messageId || "-"} preview=${JSON.stringify(preview)}`);
543
+ logger.debug(`Headers: ${JSON.stringify(res.headers || {})}`);
544
+ logger.debug(`字段: ${Object.keys(data).join(",")}`);
545
+ if (data.chatbotUserId || data.chatbotCorpId) {
546
+ const key = `${accountId}:${data.chatbotUserId || ""}:${data.chatbotCorpId || ""}`;
547
+ if (!globalThis.__dingtalkBotIdentityLogged) globalThis.__dingtalkBotIdentityLogged = /* @__PURE__ */ new Set();
548
+ const seen = globalThis.__dingtalkBotIdentityLogged;
549
+ if (!seen.has(key)) {
550
+ seen.add(key);
551
+ console.log(`[DingTalk:${accountId}] [BotIdentity] chatbotUserId=${data.chatbotUserId || "N/A"} chatbotCorpId=${data.chatbotCorpId || "N/A"}`);
552
+ }
553
+ }
551
554
  if (checkAndMarkDingtalkMessage(accountId, messageId, businessMsgId)) {
552
555
  processedCount++;
553
556
  ackMessage("duplicate");
554
- logger.warn(`⚠️ 检测到重复消息,跳过:protocol=${messageId || "-"} business=${businessMsgId || "-"} (${processedCount}/${receivedCount})`);
555
- logger.info(`========== 消息处理结束(重复) ==========\n`);
557
+ logger.warn(`⚠️ 重复消息已跳过 protocol=${messageId || "-"} business=${businessMsgId || "-"}`);
556
558
  return;
557
559
  }
558
- let contentPreview = "N/A";
559
- if (data.text?.content) contentPreview = data.text.content.length > 100 ? data.text.content.substring(0, 100) + "..." : data.text.content;
560
- else if (data.content) contentPreview = JSON.stringify(data.content).substring(0, 100) + "...";
561
- logger.info(`消息内容预览:${contentPreview}`);
562
- logger.info(`完整数据字段:${Object.keys(data).join(", ")}`);
563
- logger.info(`----- 消息详情结束 -----\n`);
564
- logger.info(`🚀 开始处理消息...`);
565
560
  await messageHandler({
566
561
  accountId,
567
562
  config: account.config,
@@ -574,16 +569,12 @@ async function monitorSingleAccount(opts) {
574
569
  ackMessage("enqueued");
575
570
  noteSocketAlive("after-enqueue");
576
571
  processedCount++;
577
- logger.info(`✅ 消息处理完成 (${processedCount}/${receivedCount})`);
578
- logger.info(`========== 消息处理结束(成功) ==========\n`);
572
+ logger.info(`✅ 入队完成 (${processedCount}/${receivedCount})`);
579
573
  } catch (error) {
580
574
  processedCount++;
581
- const errorMsg = `❌ 处理消息异常 (${processedCount}/${receivedCount}): ${error?.message || "未知错误"}`;
582
- const errorStack = error?.stack || "无堆栈信息";
583
- logger.error(errorMsg);
584
- logger.error(`错误堆栈:\n${errorStack}`);
585
- if (!acked) logger.warn(`⚠️ 未 ACK messageId=${messageId || "N/A"},等待钉钉重投(避免网关永久丢消息)`);
586
- logger.info(`========== 消息处理结束(失败) ==========\n`);
575
+ logger.error(`❌ 入站处理失败 (${processedCount}/${receivedCount}): ${error?.message || "未知错误"}`);
576
+ if (error?.stack) logger.debug(error.stack);
577
+ if (!acked) logger.warn(`⚠️ 未 ACK messageId=${messageId || "N/A"},等待钉钉重投`);
587
578
  } finally {
588
579
  markMessageProcessingEnd();
589
580
  }
@@ -23,7 +23,7 @@ var entry_bundled_default = defineBundledChannelEntry({
23
23
  exportName: "setDingtalkRuntime"
24
24
  },
25
25
  async registerFull(api) {
26
- const { registerGatewayMethods } = await import("./gateway-methods-BkI1gKaC.mjs");
26
+ const { registerGatewayMethods } = await import("./gateway-methods-DQ6TwOAr.mjs");
27
27
  registerGatewayMethods(api);
28
28
  }
29
29
  });
@@ -1,7 +1,7 @@
1
1
  import { a as resolveDingtalkAccount, t as listDingtalkAccountIds } from "./accounts-BAzdqkAV.mjs";
2
2
  import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
3
3
  import { r as getAccessToken, t as DINGTALK_API } from "./utils-CIfI_3Jh.mjs";
4
- import { c as prepareMultiBotMentions, d as finishAICard, i as sendProactive, s as buildBotMentionTable } from "./messaging-DVS3UKOp.mjs";
4
+ import { c as prepareMultiBotMentions, d as finishAICard, i as sendProactive, s as buildBotMentionTable } from "./messaging-CK0Wz3Ze.mjs";
5
5
  import { d as getUnionId, h as recallEmotionReply } from "./utils-legacy-DQ3HeGHh.mjs";
6
6
  //#region src/docs.ts
7
7
  var DingtalkDocsClient = class {
@@ -0,0 +1,2 @@
1
+ import { t as registerGatewayMethods } from "./gateway-methods-BdcpfxEZ.mjs";
2
+ export { registerGatewayMethods };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { a as initDingtalkPluginConfigSchema, i as dingtalkPlugin, n as setDingtalkRuntime } from "./runtime-A6G7WAWn.mjs";
2
- import { t as registerGatewayMethods } from "./gateway-methods-kNX3qvqw.mjs";
1
+ import { a as initDingtalkPluginConfigSchema, i as dingtalkPlugin, n as setDingtalkRuntime } from "./runtime-DbqGRyhI.mjs";
2
+ import { t as registerGatewayMethods } from "./gateway-methods-BdcpfxEZ.mjs";
3
3
  //#region index.ts
4
4
  /**
5
5
  * 检测同一 plugin id 在多个路径被加载的情况。
@@ -1,8 +1,21 @@
1
1
  //#region src/utils/logger.ts
2
2
  /**
3
3
  * 日志工具模块
4
- * 根据 debug 配置控制日志输出
4
+ * 根据配置里的 debug 控制日志输出(无需环境变量)
5
5
  */
6
+ /** 任一账号/连接开启过 debug 后为 true;默认静默 */
7
+ let pluginDebugEnabled = false;
8
+ /**
9
+ * 设置插件全局 debug(由配置 `debug: true` 驱动)
10
+ * 仅在 true 时打开;避免多账号中未开 debug 的账号把开关关掉。
11
+ */
12
+ function setDingtalkDebug(enabled) {
13
+ if (enabled) pluginDebugEnabled = true;
14
+ }
15
+ /** 是否输出图片/引用/队列等详细诊断日志 */
16
+ function isDingtalkDebug() {
17
+ return pluginDebugEnabled;
18
+ }
6
19
  /**
7
20
  * 创建日志记录器
8
21
  * @param debug - 是否启用 debug 模式
@@ -10,6 +23,7 @@
10
23
  * @returns 日志记录器对象
11
24
  */
12
25
  function createLogger(debug = false, prefix = "") {
26
+ if (debug) setDingtalkDebug(true);
13
27
  return {
14
28
  /**
15
29
  * 打印 info 级别日志
@@ -55,4 +69,4 @@ function createLoggerFromConfig(config, prefix = "") {
55
69
  return createLogger(!!config?.debug, prefix);
56
70
  }
57
71
  //#endregion
58
- export { createLoggerFromConfig as n, createLogger as t };
72
+ export { setDingtalkDebug as i, createLoggerFromConfig as n, isDingtalkDebug as r, createLogger as t };
@@ -0,0 +1,2 @@
1
+ import { i as setDingtalkDebug, n as createLoggerFromConfig } from "./logger-CnBTcwyq.mjs";
2
+ export { createLoggerFromConfig, setDingtalkDebug };
@@ -1,2 +1,2 @@
1
- import { _ as toLocalPath, d as processLocalImages, f as processRawMediaPaths, h as sendFileProactive, o as hasResidualLocalMdImagesOutsideCode, p as processVideoMarkers } from "./media-DnQB2BrL.mjs";
1
+ import { _ as toLocalPath, d as processLocalImages, f as processRawMediaPaths, h as sendFileProactive, o as hasResidualLocalMdImagesOutsideCode, p as processVideoMarkers } from "./media-g8tJtpfU.mjs";
2
2
  export { hasResidualLocalMdImagesOutsideCode, processLocalImages, processRawMediaPaths, processVideoMarkers, sendFileProactive, toLocalPath };
@@ -1,3 +1,4 @@
1
+ import { r as isDingtalkDebug } from "./logger-CnBTcwyq.mjs";
1
2
  import { n as dingtalkOapiHttp, t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
2
3
  import { n as DINGTALK_OAPI } from "./utils-CIfI_3Jh.mjs";
3
4
  import { createRequire } from "node:module";
@@ -76,9 +77,8 @@ function toLocalPath(raw) {
76
77
  return filePath;
77
78
  }
78
79
  /**
79
- * 本地图诊断:默认 console.log 一行(不走 log.warn,避免 [sendTextToDingTalk] 双前缀)。
80
- * verbose=true 的细节(realpath 等)仅在 debug 时输出。
81
- * 决策路径(匹配/exists/上传成败/残留)务必 always-on,方便查灰图。
80
+ * 本地图诊断日志。
81
+ * 默认静默:无图/空扫描不再刷屏。仅在上传/失败/灰图风险,或 debug: true 时输出。
82
82
  */
83
83
  function logLocalImage(log, step, detail, verbose = false) {
84
84
  const line = detail ? `[DingTalk][LocalImage] ${step} | ${detail}` : `[DingTalk][LocalImage] ${step}`;
@@ -88,6 +88,12 @@ function logLocalImage(log, step, detail, verbose = false) {
88
88
  } catch {}
89
89
  return;
90
90
  }
91
+ if (!(isDingtalkDebug() || /失败|灰图|残留|上传成功|上传失败|下载成功|下载失败|重试|remote|Remote/i.test(`${step} ${detail || ""}`))) {
92
+ try {
93
+ log?.debug?.(line);
94
+ } catch {}
95
+ return;
96
+ }
91
97
  console.log(line);
92
98
  }
93
99
  /** 钉钉 mediaId 形态(oapi 返回常带 @) */
@@ -117,7 +123,7 @@ function logMediaIdTrace(stage, content, extra, force = false) {
117
123
  const residualLocal = mdTargets.filter((t) => isLocalImageRef(t));
118
124
  const httpCount = (text.match(/https?:\/\//gi) || []).length;
119
125
  const suspiciousBare = /type=image|sampleImage|photoURL/i.test(`${stage} ${extra || ""}`) ? [] : bareIds;
120
- if (!(force || suspiciousBare.length > 0 || residualLocal.length > 0 || /API前|完成|:out|:in|after-process|processImages/i.test(stage))) return;
126
+ if (!(force || isDingtalkDebug() || suspiciousBare.length > 0 || residualLocal.length > 0)) return;
121
127
  const preview = text.replace(/\n/g, "\\n").slice(0, 200);
122
128
  console.log(`[DingTalk][MediaIdTrace] ${stage} | len=${text.length} mediaIdCount=${ids.length}` + (ids.length ? ` ids=[${ids.slice(0, 4).join(",")}]` : "") + ` BARE_ids=${suspiciousBare.length ? suspiciousBare.join(",") : "无"} mdImgs=[${mdImgs.length ? mdImgs.join(" || ") : "-"}] httpCount=${httpCount}` + (residualLocal.length ? ` residualLocal=${residualLocal.length}:${residualLocal.slice(0, 3).map((p) => p.slice(0, 80)).join("|")}` : " residualLocal=0") + (extra ? ` ${extra}` : "") + ` preview="${preview}"`);
123
129
  }
@@ -277,7 +283,7 @@ async function downloadRemoteUrlToTemp(url) {
277
283
  }
278
284
  const tmp = path.join(os.tmpdir(), `dingtalk-md-remote-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`);
279
285
  fs.writeFileSync(tmp, buf);
280
- console.log(`[DingTalk][MediaIdTrace] 远程图下载成功 | bytes=${buf.length} tmp=${tmp}`);
286
+ if (isDingtalkDebug()) console.log(`[DingTalk][MediaIdTrace] 远程图下载成功 | bytes=${buf.length} tmp=${tmp}`);
281
287
  return tmp;
282
288
  } catch (e) {
283
289
  console.warn(`[DingTalk][MediaIdTrace] 远程图下载异常 | ${e?.message || e}`);
@@ -312,27 +318,29 @@ function replaceOutsideImageTargets(content, original, replacement) {
312
318
  * - 本地:正文重复的本地路径换成 down.dingtalk.com 可点链接(仍同一条消息)
313
319
  * - **不再拆第二条 text 消息**
314
320
  *
315
- * 日志约定(always-on,查灰图必看):
316
- * - 开始 / 扫描每张 ![] / 本地 exists / 上传成败 / 远程下载 / 残留 / out
321
+ * 日志约定(默认静默;配置 debug: true 全量):
322
+ * - 有图上传/失败/灰图残留才 console;无图路径不刷屏
317
323
  */
318
324
  async function processImagesForOutbound(content, oapiToken, log) {
319
325
  logLocalImage(log, "processImagesForOutbound 开始", `contentLen=${content?.length ?? 0} hasToken=${!!oapiToken}`);
320
326
  if (!oapiToken) {
321
- logLocalImage(log, "processImagesForOutbound 跳过", "无 oapiToken(本地路径无法上传→必灰图)");
322
- logMediaIdTrace("processImagesForOutbound:skip-no-token", content, void 0, true);
327
+ if (/!\[[^\]]*\]\([^)]+\)/.test(content || "")) {
328
+ logLocalImage(log, "processImagesForOutbound 跳过", "无 oapiToken(本地路径无法上传→必灰图)");
329
+ logMediaIdTrace("processImagesForOutbound:skip-no-token", content);
330
+ }
323
331
  return {
324
332
  text: content || "",
325
333
  followUpUrls: []
326
334
  };
327
335
  }
328
336
  if (!content || typeof content !== "string") {
329
- logLocalImage(log, "processImagesForOutbound 跳过", "content 为空");
337
+ logLocalImage(log, "processImagesForOutbound 跳过", "content 为空", true);
330
338
  return {
331
339
  text: content || "",
332
340
  followUpUrls: []
333
341
  };
334
342
  }
335
- logMediaIdTrace("processImagesForOutbound:in", content, void 0, true);
343
+ logMediaIdTrace("processImagesForOutbound:in", content);
336
344
  const codeRanges = getMarkdownCodeRanges(content);
337
345
  if (codeRanges.length > 0) logLocalImage(log, "processImagesForOutbound 代码区域", `count=${codeRanges.length}(围栏/行内 code 内的图路径将跳过)`);
338
346
  const mdMatches = [...content.matchAll(LOCAL_IMAGE_RE)];
@@ -435,9 +443,10 @@ async function processImagesForOutbound(content, oapiToken, log) {
435
443
  if (m.index !== void 0 && isIndexInCodeRange(m.index, getMarkdownCodeRanges(result))) return false;
436
444
  return isLocalImageRef((m[2] || "").trim());
437
445
  }).map((m) => (m[2] || "").trim());
438
- logLocalImage(log, "processImagesForOutbound 完成", `uploads=${uploadCount} localTried=${localTried} remoteTried=${remoteTried} skippedCode=${skippedCode} skippedOther=${skippedOther} outLen=${result.length}` + (residual.length ? ` ⚠️residualLocal=${residual.length}:${residual.slice(0, 3).join("|")}` : " residualLocal=0"));
446
+ if (uploadCount > 0 || localTried > 0 || remoteTried > 0 || residual.length > 0) logLocalImage(log, residual.length ? "processImagesForOutbound 完成(有残留)" : "processImagesForOutbound 上传成功", `uploads=${uploadCount} localTried=${localTried} remoteTried=${remoteTried} skippedCode=${skippedCode} skippedOther=${skippedOther} outLen=${result.length}` + (residual.length ? ` ⚠️residualLocal=${residual.length}:${residual.slice(0, 3).join("|")}` : " residualLocal=0"));
447
+ else logLocalImage(log, "processImagesForOutbound 完成", `uploads=0 outLen=${result.length}`, true);
439
448
  if (residual.length) logLocalImage(log, "processImagesForOutbound ⚠️仍有代码块外本地路径未替换(钉钉必灰图)", residual.join(" | "));
440
- logMediaIdTrace("processImagesForOutbound:out", result, void 0, true);
449
+ logMediaIdTrace("processImagesForOutbound:out", result);
441
450
  return {
442
451
  text: result,
443
452
  followUpUrls: []
@@ -658,7 +667,7 @@ async function extractAudioDuration(filePath, log) {
658
667
  */
659
668
  async function sendVideoMessage(config, sessionWebhook, fileName, mediaId, log, metadata) {
660
669
  try {
661
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
670
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
662
671
  const videoMessage = {
663
672
  msgtype: "video",
664
673
  video: {
@@ -686,8 +695,8 @@ async function sendVideoMessage(config, sessionWebhook, fileName, mediaId, log,
686
695
  */
687
696
  async function sendVideoProactive(config, target, videoMediaId, picMediaId, metadata, log) {
688
697
  try {
689
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
690
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
698
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
699
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
691
700
  const msgParam = {
692
701
  duration: metadata?.duration.toString() || "60000",
693
702
  videoMediaId,
@@ -732,8 +741,8 @@ async function sendVideoProactive(config, target, videoMediaId, picMediaId, meta
732
741
  */
733
742
  async function sendAudioProactive(config, target, fileName, mediaId, log, durationMs) {
734
743
  try {
735
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
736
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
744
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
745
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
737
746
  const msgParam = {
738
747
  mediaId,
739
748
  duration: durationMs && durationMs > 0 ? durationMs.toString() : "60000"
@@ -770,8 +779,8 @@ async function sendAudioProactive(config, target, fileName, mediaId, log, durati
770
779
  */
771
780
  async function sendFileProactive(config, target, fileInfo, mediaId, log) {
772
781
  try {
773
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
774
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
782
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
783
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
775
784
  const resolvedFileName = fileInfo.fileName || path.basename(fileInfo.path);
776
785
  const msgParam = {
777
786
  mediaId,
@@ -1,15 +1,15 @@
1
1
  import { a as resolveDingtalkAccount } from "./accounts-BAzdqkAV.mjs";
2
- import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-A6G7WAWn.mjs";
3
- import { n as createLoggerFromConfig } from "./logger-mZ9OSbmD.mjs";
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-DVS3UKOp.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-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 { r as trackBackgroundWork } from "./background-work-Cxb4b-91.mjs";
10
- import "./chunk-upload-BE71drPg.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-BZiVgd3A.mjs";
12
- import { n as lookupCardContent, t as collectCardLookupIds } from "./card-content-cache-bDQFz2u8.mjs";
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";
13
13
  import * as fs from "fs";
14
14
  import * as path from "path";
15
15
  import * as os from "node:os";
@@ -63,7 +63,7 @@ function resolveAgentWorkspaceDir(cfg, agentId) {
63
63
  * 直接复用 media.ts 权威实现,避免 reply 路径与 message 路径两套逻辑分叉。
64
64
  */
65
65
  async function processLocalImages(content, oapiToken, log) {
66
- const { processLocalImages: impl } = await import("./media-BqpUGCni.mjs");
66
+ const { processLocalImages: impl } = await import("./media-DDtX37T2.mjs");
67
67
  return impl(content, oapiToken, log);
68
68
  }
69
69
  //#endregion
@@ -606,22 +606,20 @@ function createDingtalkReplyDispatcher(params) {
606
606
  type: "group",
607
607
  openConversationId: conversationId
608
608
  };
609
- console.log(`[DingTalk][LocalImage] closeStreaming 开始媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length}`);
610
609
  const mdImgCount = (finalText.match(/!\[[^\]]*\]\([^)]+\)/g) || []).length;
611
- console.log(`[DingTalk][LocalImage] closeStreaming 正文 markdown 图数量=${mdImgCount} preview=${JSON.stringify(finalText.slice(0, 200))}`);
610
+ if (mdImgCount > 0 || isDingtalkDebug()) log.info(`[DingTalk][LocalImage] closeStreaming 媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length} mdImgs=${mdImgCount}`);
612
611
  if (oapiToken) {
613
612
  const beforeImg = finalText;
614
613
  finalText = await processLocalImages(finalText, oapiToken, log);
615
- if (beforeImg !== finalText) console.log(`[DingTalk][LocalImage] closeStreaming 图片处理 | ${beforeImg.length}→${finalText.length}`);
614
+ if (beforeImg !== finalText) log.info(`[DingTalk][LocalImage] closeStreaming 图片已处理 | ${beforeImg.length}→${finalText.length}`);
616
615
  try {
617
- const { hasResidualLocalMdImagesOutsideCode } = await import("./media-BqpUGCni.mjs");
618
- if (hasResidualLocalMdImagesOutsideCode(finalText)) console.warn(`[DingTalk][LocalImage] closeStreaming 定稿后仍有代码块外的本地 MD 图未上传,钉钉可能灰图`);
616
+ const { hasResidualLocalMdImagesOutsideCode } = await import("./media-DDtX37T2.mjs");
617
+ if (hasResidualLocalMdImagesOutsideCode(finalText)) log.warn(`[DingTalk][LocalImage] closeStreaming 定稿后仍有代码块外的本地 MD 图未上传,钉钉可能灰图`);
619
618
  } catch {}
620
619
  finalText = await processVideoMarkers(finalText, "", account.config, oapiToken, log, true, target);
621
620
  finalText = await processAudioMarkers(finalText, "", account.config, oapiToken, log, true, target);
622
621
  finalText = await uploadAndReplaceFileMarkers(finalText, "", account.config, oapiToken, log, true, target);
623
- log.info(`[DingTalk][closeStreaming] 准备调用 processRawMediaPaths`);
624
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
622
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
625
623
  finalText = await processRawMediaPaths(finalText, account.config, oapiToken, log, target);
626
624
  log.info(`[DingTalk][closeStreaming] processRawMediaPaths 处理完成`);
627
625
  } else log.warn(`[DingTalk][closeStreaming] oapiToken 为空,跳过媒体处理`);
@@ -774,11 +772,8 @@ function createDingtalkReplyDispatcher(params) {
774
772
  onSkip: (payload, info) => {
775
773
  const raw = readPayloadRawError(payload);
776
774
  const isErr = Boolean(payload?.isError);
777
- if (!raw && !isErr) {
778
- log.info(`[DingTalk][onSkip] kind=${info?.kind} reason=${info?.reason} 非错误静默(NO_REPLY/empty),跳过错误捕获`);
779
- return;
780
- }
781
- log.warn(`[DingTalk][onSkip] kind=${info?.kind} reason=${info?.reason} isError=${isErr} rawLen=${raw.length}(群聊静默失败常见)`);
775
+ if (!raw && !isErr) return;
776
+ log.warn(`[DingTalk][onSkip] 群聊静默失败 kind=${info?.kind} reason=${info?.reason} rawLen=${raw.length}`);
782
777
  captureErrorFromPayload(payload, `onSkip:${info?.kind ?? "?"}:${info?.reason ?? "?"}`);
783
778
  },
784
779
  onReplyStart: () => {
@@ -846,7 +841,7 @@ function createDingtalkReplyDispatcher(params) {
846
841
  const oapiToken = await getOapiAccessToken(account.config);
847
842
  if (oapiToken) {
848
843
  log.info(`[DingTalk][deliver] 检测到 final 响应,准备处理裸露文件路径`);
849
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
844
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
850
845
  text = await processRawMediaPaths(text, account.config, oapiToken, log, target);
851
846
  log.info(`[DingTalk][deliver] 裸露文件路径处理完成`);
852
847
  }
@@ -976,7 +971,7 @@ function createDingtalkReplyDispatcher(params) {
976
971
  currentToolLine = "";
977
972
  await startStreaming();
978
973
  if (currentCardTarget) try {
979
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DgL28gCZ.mjs");
974
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
980
975
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim(), { force: true });
981
976
  } catch (err) {
982
977
  if (!isQpsLimitError(err)) log.error(`[DingTalk][onPartialReply] final-marker 刷卡失败:${err.message}`);
@@ -989,7 +984,7 @@ function createDingtalkReplyDispatcher(params) {
989
984
  if (payload.text?.trim()) hasModelStreamText = true;
990
985
  currentToolLine = "";
991
986
  try {
992
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DgL28gCZ.mjs");
987
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
993
988
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim());
994
989
  } catch (err) {
995
990
  if (isQpsLimitError(err)) log.warn(`[DingTalk][onPartialReply] QPS 限流,已排队尾随重试 latestLen=${latestCardContent.length}`);
@@ -1184,8 +1179,9 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
1184
1179
  conversationId,
1185
1180
  allowConversationRecent: false
1186
1181
  }) || "";
1187
- if (cardText) console.log(`[DingTalk][Quote] interactiveCard 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cardText.length}`);
1188
- else {
1182
+ if (cardText) {
1183
+ if (isDingtalkDebug()) console.log(`[DingTalk][Quote] interactiveCard 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cardText.length}`);
1184
+ } else if (isDingtalkDebug()) {
1189
1185
  const dump = JSON.stringify(repliedMsg ?? {}).slice(0, 800);
1190
1186
  console.log(`[DingTalk][Quote] interactiveCard 无正文且缓存未命中(未使用会话最近兜底) | conv=${conversationId || "-"} keys=${Object.keys(contentObj || {}).join(",")} repliedMsg=${dump}`);
1191
1187
  }
@@ -1208,13 +1204,13 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
1208
1204
  });
1209
1205
  if (cached) {
1210
1206
  bodyText = cached;
1211
- 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}`);
1212
1208
  } else if (/card|interactive/i.test(String(msgType || "")) || ids.length > 0) {
1213
1209
  bodyText = "钉钉卡片消息。";
1214
- console.log(`[DingTalk][Quote] 未知卡片类引用 msgType=${msgType} 已简化占位 | keys=${Object.keys(contentObj || {}).join(",")}`);
1215
- } 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)}`);
1216
1212
  } catch {
1217
- 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(",")}`);
1218
1214
  }
1219
1215
  break;
1220
1216
  }
@@ -2320,7 +2316,7 @@ async function handleDingTalkMessageInternal(params) {
2320
2316
  finalText = await processVideoMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2321
2317
  finalText = await processAudioMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2322
2318
  finalText = await uploadAndReplaceFileMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2323
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
2319
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
2324
2320
  finalText = await processRawMediaPaths(finalText, config, oapiToken, log, mediaTarget);
2325
2321
  }
2326
2322
  const textToSend = finalText.trim() || "✅ 任务执行完成(无文本输出)";