@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.en.md +40 -82
  3. package/README.md +50 -93
  4. package/dist/background-work-DkaSDqWk.mjs +53 -0
  5. package/dist/card-content-cache-C7-XHhHw.mjs +2 -0
  6. package/dist/{card-content-cache-CYUzCaQJ.mjs → card-content-cache-DT_OYThh.mjs} +15 -1
  7. package/dist/chunk-upload-BKM1y4Ba.mjs +2 -0
  8. package/dist/{chunk-upload-BBQgGtcZ.mjs → chunk-upload-CIU5G_ay.mjs} +1 -1
  9. package/dist/{common-DE0gd0Jg.mjs → common-BfIYs1yC.mjs} +3 -3
  10. package/dist/{common-B_A78n8u.mjs → common-C0Oaf0z-.mjs} +1 -1
  11. package/dist/connection-obtrPkj4.mjs +641 -0
  12. package/dist/entry-bundled.mjs +1 -1
  13. package/dist/{gateway-methods--aKsyMs-.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-DQZiKx3i.mjs → message-handler-BrO2i7tg.mjs} +251 -139
  21. package/dist/{messaging-WleKf54h.mjs → messaging-CK0Wz3Ze.mjs} +23 -21
  22. package/dist/{runtime-DzZo6Gvk.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 +56 -0
  25. package/docs/RELEASE_NOTES_V0.8.29.md +57 -0
  26. package/openclaw.plugin.json +9 -5
  27. package/package.json +2 -2
  28. package/src/config/schema.ts +2 -1
  29. package/src/core/connection.ts +504 -220
  30. package/src/core/message-handler.ts +197 -102
  31. package/src/reply-dispatcher.ts +244 -93
  32. package/src/services/media.ts +60 -27
  33. package/src/services/messaging/card-content-cache.ts +11 -6
  34. package/src/services/messaging/card.ts +2 -0
  35. package/src/services/messaging.ts +52 -38
  36. package/src/utils/background-work.ts +65 -0
  37. package/src/utils/logger.ts +23 -2
  38. package/dist/card-content-cache-Dwd8HICP.mjs +0 -2
  39. package/dist/chunk-upload-DaLXXZH3.mjs +0 -2
  40. package/dist/connection-fc6B4z4G.mjs +0 -423
  41. package/dist/gateway-methods-BQd4E0Sk.mjs +0 -2
  42. package/dist/logger-BmJkQkm1.mjs +0 -2
  43. /package/dist/{game-xiyou-CAT0ilSj.mjs → game-xiyou-uwsv23AC.mjs} +0 -0
@@ -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,