@yanhaidao/wecom 2.4.160 → 2.5.110

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 (313) hide show
  1. package/dist/index.js +68 -0
  2. package/dist/src/accounts.js +20 -0
  3. package/dist/src/agent/handler.js +895 -0
  4. package/dist/src/agent/index.js +5 -0
  5. package/dist/src/app/account-runtime.js +216 -0
  6. package/dist/src/app/bootstrap.js +19 -0
  7. package/dist/src/app/index.js +118 -0
  8. package/dist/src/capability/agent/delivery-service.js +63 -0
  9. package/dist/src/capability/agent/fallback-policy.js +6 -0
  10. package/dist/src/capability/agent/ingress-service.js +33 -0
  11. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  12. package/dist/src/capability/bot/dispatch-config.js +45 -0
  13. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  14. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  15. package/dist/src/capability/bot/sandbox-media.js +138 -0
  16. package/dist/src/capability/bot/service.js +49 -0
  17. package/dist/src/capability/bot/stream-delivery.js +321 -0
  18. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  19. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  20. package/dist/src/capability/bot/types.js +1 -0
  21. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  22. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  23. package/dist/src/capability/calendar/tool.js +365 -0
  24. package/dist/src/capability/calendar/types.js +12 -0
  25. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  26. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  27. package/dist/src/capability/doc/tool.js +1556 -0
  28. package/dist/src/capability/doc/types.js +113 -0
  29. package/dist/src/capability/mcp/index.js +3 -0
  30. package/dist/src/capability/mcp/schema.js +102 -0
  31. package/dist/src/capability/mcp/tool.js +146 -0
  32. package/dist/src/capability/mcp/transport.js +293 -0
  33. package/dist/src/channel.js +224 -0
  34. package/dist/src/config/accounts.js +236 -0
  35. package/dist/src/config/derived-paths.js +31 -0
  36. package/dist/src/config/index.js +7 -0
  37. package/dist/src/config/media.js +110 -0
  38. package/dist/src/config/network.js +32 -0
  39. package/dist/src/config/routing.js +20 -0
  40. package/dist/src/config/runtime-config.js +25 -0
  41. package/dist/src/config/schema.js +4 -0
  42. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  43. package/dist/src/context-store.js +219 -0
  44. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  45. package/dist/src/crypto/index.js +9 -0
  46. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  47. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  48. package/dist/src/crypto.js +145 -0
  49. package/dist/src/domain/models.js +1 -0
  50. package/dist/src/domain/policies.js +32 -0
  51. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  52. package/dist/src/gateway-monitor.js +139 -0
  53. package/dist/src/http.js +114 -0
  54. package/{src/media.ts → dist/src/media.js} +21 -40
  55. package/dist/src/monitor/limits.js +7 -0
  56. package/dist/src/monitor/state.js +28 -0
  57. package/dist/src/monitor.js +84 -0
  58. package/dist/src/observability/audit-log.js +30 -0
  59. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  60. package/dist/src/observability/raw-envelope-log.js +24 -0
  61. package/dist/src/observability/status-registry.js +9 -0
  62. package/dist/src/observability/transport-session-view.js +14 -0
  63. package/dist/src/onboarding.js +546 -0
  64. package/dist/src/outbound.js +557 -0
  65. package/dist/src/runtime/dispatcher.js +57 -0
  66. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  67. package/dist/src/runtime/outbound-intent.js +1 -0
  68. package/dist/src/runtime/reply-orchestrator.js +38 -0
  69. package/dist/src/runtime/routing-bridge.js +26 -0
  70. package/dist/src/runtime/session-manager.js +112 -0
  71. package/dist/src/runtime/source-registry.js +174 -0
  72. package/dist/src/runtime.js +1 -0
  73. package/dist/src/shared/command-auth.js +57 -0
  74. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  75. package/dist/src/shared/media-asset.js +65 -0
  76. package/dist/src/shared/media-service.js +59 -0
  77. package/dist/src/shared/media-types.js +1 -0
  78. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  79. package/dist/src/store/active-reply-store.js +41 -0
  80. package/dist/src/store/interfaces.js +1 -0
  81. package/dist/src/store/memory-store.js +33 -0
  82. package/dist/src/store/stream-batch-store.js +319 -0
  83. package/{src/target.ts → dist/src/target.js} +15 -48
  84. package/dist/src/transport/agent-api/client.js +168 -0
  85. package/dist/src/transport/agent-api/core.js +337 -0
  86. package/dist/src/transport/agent-api/delivery.js +28 -0
  87. package/dist/src/transport/agent-api/media-upload.js +4 -0
  88. package/dist/src/transport/agent-api/reply.js +24 -0
  89. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  90. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  91. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  92. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  93. package/dist/src/transport/agent-callback/inbound.js +4 -0
  94. package/dist/src/transport/agent-callback/reply.js +8 -0
  95. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  96. package/dist/src/transport/agent-callback/session.js +15 -0
  97. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  98. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  99. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  100. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  101. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  102. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  103. package/dist/src/transport/bot-webhook/reply.js +9 -0
  104. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  105. package/dist/src/transport/bot-webhook/session.js +15 -0
  106. package/dist/src/transport/bot-ws/inbound.js +147 -0
  107. package/dist/src/transport/bot-ws/media.js +236 -0
  108. package/dist/src/transport/bot-ws/reply.js +310 -0
  109. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  110. package/dist/src/transport/bot-ws/session.js +15 -0
  111. package/dist/src/transport/http/common.js +78 -0
  112. package/dist/src/transport/http/registry.js +71 -0
  113. package/dist/src/transport/http/request-handler.js +51 -0
  114. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  115. package/dist/src/types/account.js +1 -0
  116. package/dist/src/types/config.js +1 -0
  117. package/dist/src/types/constants.js +28 -0
  118. package/dist/src/types/events.js +1 -0
  119. package/dist/src/types/index.js +1 -0
  120. package/dist/src/types/legacy-stream.js +1 -0
  121. package/dist/src/types/message.js +5 -0
  122. package/dist/src/types/runtime-context.js +1 -0
  123. package/dist/src/types/runtime.js +1 -0
  124. package/dist/src/types.js +1 -0
  125. package/dist/src/upstream/index.js +111 -0
  126. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  127. package/openclaw.plugin.json +15 -0
  128. package/package.json +18 -1
  129. package/.github/workflows/release.yml +0 -143
  130. package/GOVERNANCE.md +0 -26
  131. package/SKILLS_CAL.md +0 -895
  132. package/SKILLS_DOC.md +0 -2288
  133. package/UPSTREAM_CONFIG.md +0 -170
  134. package/UPSTREAM_PLAN.md +0 -175
  135. package/assets/01.bot-add.png +0 -0
  136. package/assets/01.bot-setp2.png +0 -0
  137. package/assets/01.image.jpg +0 -0
  138. package/assets/02.agent.add.png +0 -0
  139. package/assets/02.agent.api-set.png +0 -0
  140. package/assets/02.image.jpg +0 -0
  141. package/assets/03.agent.page.png +0 -0
  142. package/assets/03.bot.page.png +0 -0
  143. package/assets/link-me.jpg +0 -0
  144. package/assets/register.png +0 -0
  145. package/changelog/v2.2.28.md +0 -70
  146. package/changelog/v2.3.10.md +0 -17
  147. package/changelog/v2.3.11.md +0 -19
  148. package/changelog/v2.3.12.md +0 -25
  149. package/changelog/v2.3.13.md +0 -19
  150. package/changelog/v2.3.14.md +0 -48
  151. package/changelog/v2.3.15.md +0 -15
  152. package/changelog/v2.3.16.md +0 -11
  153. package/changelog/v2.3.18.md +0 -22
  154. package/changelog/v2.3.19.md +0 -73
  155. package/changelog/v2.3.2.md +0 -28
  156. package/changelog/v2.3.26.md +0 -21
  157. package/changelog/v2.3.27.md +0 -33
  158. package/changelog/v2.3.4.md +0 -20
  159. package/changelog/v2.3.9.md +0 -22
  160. package/changelog/v2.4.12.md +0 -37
  161. package/changelog/v2.4.16.md +0 -19
  162. package/compat-single-account.md +0 -148
  163. package/index.test.ts +0 -38
  164. package/scripts/test-proxy.ts +0 -70
  165. package/src/accounts.ts +0 -34
  166. package/src/agent/api-client.upload.test.ts +0 -109
  167. package/src/agent/handler.event-filter.test.ts +0 -100
  168. package/src/agent/handler.ts +0 -1105
  169. package/src/agent/index.ts +0 -12
  170. package/src/app/account-runtime.ts +0 -276
  171. package/src/app/bootstrap.ts +0 -29
  172. package/src/app/index.ts +0 -192
  173. package/src/capability/agent/delivery-service.ts +0 -87
  174. package/src/capability/agent/fallback-policy.ts +0 -13
  175. package/src/capability/agent/ingress-service.ts +0 -38
  176. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  177. package/src/capability/bot/dispatch-config.ts +0 -47
  178. package/src/capability/bot/fallback-delivery.ts +0 -178
  179. package/src/capability/bot/local-path-delivery.ts +0 -215
  180. package/src/capability/bot/sandbox-media.test.ts +0 -221
  181. package/src/capability/bot/sandbox-media.ts +0 -176
  182. package/src/capability/bot/service.ts +0 -56
  183. package/src/capability/bot/stream-delivery.ts +0 -379
  184. package/src/capability/bot/stream-finalizer.ts +0 -120
  185. package/src/capability/bot/stream-orchestrator.ts +0 -371
  186. package/src/capability/bot/types.ts +0 -8
  187. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  188. package/src/capability/calendar/tool.ts +0 -417
  189. package/src/capability/calendar/types.ts +0 -309
  190. package/src/capability/doc/tool.ts +0 -1629
  191. package/src/capability/doc/types.ts +0 -792
  192. package/src/capability/mcp/index.ts +0 -10
  193. package/src/capability/mcp/schema.ts +0 -107
  194. package/src/capability/mcp/tool.ts +0 -174
  195. package/src/capability/mcp/transport.ts +0 -394
  196. package/src/channel.config.test.ts +0 -147
  197. package/src/channel.lifecycle.test.ts +0 -255
  198. package/src/channel.meta.test.ts +0 -26
  199. package/src/channel.ts +0 -256
  200. package/src/config/accounts.resolve.test.ts +0 -75
  201. package/src/config/accounts.ts +0 -296
  202. package/src/config/derived-paths.test.ts +0 -111
  203. package/src/config/derived-paths.ts +0 -41
  204. package/src/config/index.ts +0 -26
  205. package/src/config/media.test.ts +0 -113
  206. package/src/config/media.ts +0 -139
  207. package/src/config/network.ts +0 -53
  208. package/src/config/routing.test.ts +0 -88
  209. package/src/config/routing.ts +0 -26
  210. package/src/config/runtime-config.ts +0 -46
  211. package/src/config/schema.ts +0 -90
  212. package/src/context-store.ts +0 -297
  213. package/src/crypto/index.ts +0 -24
  214. package/src/crypto.test.ts +0 -32
  215. package/src/crypto.ts +0 -176
  216. package/src/domain/models.ts +0 -7
  217. package/src/domain/policies.ts +0 -36
  218. package/src/dynamic-agent.account-scope.test.ts +0 -17
  219. package/src/gateway-monitor.ts +0 -181
  220. package/src/http.ts +0 -145
  221. package/src/media.test.ts +0 -82
  222. package/src/monitor/limits.ts +0 -7
  223. package/src/monitor/state.queue.test.ts +0 -185
  224. package/src/monitor/state.ts +0 -34
  225. package/src/monitor.active.test.ts +0 -245
  226. package/src/monitor.inbound-filter.test.ts +0 -63
  227. package/src/monitor.integration.test.ts +0 -208
  228. package/src/monitor.ts +0 -121
  229. package/src/monitor.webhook.test.ts +0 -774
  230. package/src/observability/audit-log.ts +0 -48
  231. package/src/observability/legacy-operational-event-store.ts +0 -36
  232. package/src/observability/raw-envelope-log.ts +0 -28
  233. package/src/observability/status-registry.ts +0 -13
  234. package/src/observability/transport-session-view.ts +0 -14
  235. package/src/onboarding.test.ts +0 -336
  236. package/src/onboarding.ts +0 -704
  237. package/src/outbound.test.ts +0 -1271
  238. package/src/outbound.ts +0 -746
  239. package/src/runtime/dispatcher.ts +0 -71
  240. package/src/runtime/outbound-intent.ts +0 -4
  241. package/src/runtime/reply-orchestrator.test.ts +0 -71
  242. package/src/runtime/reply-orchestrator.ts +0 -67
  243. package/src/runtime/routing-bridge.test.ts +0 -115
  244. package/src/runtime/routing-bridge.ts +0 -44
  245. package/src/runtime/session-manager.test.ts +0 -174
  246. package/src/runtime/session-manager.ts +0 -139
  247. package/src/runtime/source-registry.ts +0 -249
  248. package/src/runtime.ts +0 -14
  249. package/src/shared/command-auth.ts +0 -87
  250. package/src/shared/media-asset.ts +0 -78
  251. package/src/shared/media-service.test.ts +0 -111
  252. package/src/shared/media-service.ts +0 -84
  253. package/src/shared/media-types.ts +0 -5
  254. package/src/shared/xml-parser.test.ts +0 -50
  255. package/src/store/active-reply-store.ts +0 -42
  256. package/src/store/interfaces.ts +0 -11
  257. package/src/store/memory-store.ts +0 -43
  258. package/src/store/stream-batch-store.ts +0 -350
  259. package/src/transport/agent-api/client.ts +0 -277
  260. package/src/transport/agent-api/core.ts +0 -463
  261. package/src/transport/agent-api/delivery.ts +0 -41
  262. package/src/transport/agent-api/media-upload.ts +0 -11
  263. package/src/transport/agent-api/reply.ts +0 -39
  264. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  265. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  266. package/src/transport/agent-api/upstream-reply.ts +0 -43
  267. package/src/transport/agent-callback/http-handler.ts +0 -47
  268. package/src/transport/agent-callback/inbound.ts +0 -5
  269. package/src/transport/agent-callback/reply.ts +0 -13
  270. package/src/transport/agent-callback/request-handler.ts +0 -244
  271. package/src/transport/agent-callback/session.ts +0 -23
  272. package/src/transport/bot-webhook/active-reply.ts +0 -39
  273. package/src/transport/bot-webhook/http-handler.ts +0 -48
  274. package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
  275. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
  276. package/src/transport/bot-webhook/inbound.ts +0 -5
  277. package/src/transport/bot-webhook/message-shape.ts +0 -92
  278. package/src/transport/bot-webhook/protocol.ts +0 -148
  279. package/src/transport/bot-webhook/reply.ts +0 -15
  280. package/src/transport/bot-webhook/request-handler.ts +0 -394
  281. package/src/transport/bot-webhook/session.ts +0 -23
  282. package/src/transport/bot-ws/inbound.test.ts +0 -290
  283. package/src/transport/bot-ws/inbound.ts +0 -163
  284. package/src/transport/bot-ws/media.test.ts +0 -44
  285. package/src/transport/bot-ws/media.ts +0 -321
  286. package/src/transport/bot-ws/reply.test.ts +0 -450
  287. package/src/transport/bot-ws/reply.ts +0 -365
  288. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  289. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  290. package/src/transport/bot-ws/session.ts +0 -28
  291. package/src/transport/http/common.ts +0 -109
  292. package/src/transport/http/registry.ts +0 -92
  293. package/src/transport/http/request-handler.ts +0 -84
  294. package/src/types/account.ts +0 -70
  295. package/src/types/config.ts +0 -114
  296. package/src/types/constants.ts +0 -31
  297. package/src/types/events.ts +0 -21
  298. package/src/types/global.d.ts +0 -9
  299. package/src/types/index.ts +0 -17
  300. package/src/types/legacy-stream.ts +0 -50
  301. package/src/types/message.ts +0 -189
  302. package/src/types/runtime-context.ts +0 -28
  303. package/src/types/runtime.ts +0 -165
  304. package/src/types.ts +0 -41
  305. package/src/upstream/index.ts +0 -150
  306. package/src/upstream.test.ts +0 -84
  307. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  308. package/tsconfig.json +0 -22
  309. package/vitest.config.ts +0 -26
  310. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  311. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  312. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  313. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -1,433 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- import { processBotInboundMessage } from "./inbound-normalizer.js";
4
- import { decryptWecomMediaWithMeta } from "../../media.js";
5
-
6
- vi.mock("../../media.js", () => ({
7
- decryptWecomMediaWithMeta: vi.fn(),
8
- }));
9
-
10
- describe("processBotInboundMessage quote media", () => {
11
- const recordOperationalIssue = vi.fn();
12
- const logError = vi.fn();
13
-
14
- beforeEach(() => {
15
- vi.clearAllMocks();
16
- });
17
-
18
- it("downloads quote.file for text messages", async () => {
19
- vi.mocked(decryptWecomMediaWithMeta).mockResolvedValue({
20
- buffer: Buffer.from("%PDF-1.7 test"),
21
- sourceContentType: "application/pdf",
22
- sourceFilename: "quoted.pdf",
23
- sourceUrl: "https://example.com/quoted.pdf",
24
- });
25
-
26
- const result = await processBotInboundMessage({
27
- target: {
28
- account: {
29
- accountId: "purple",
30
- encodingAESKey: "account-aes-key",
31
- },
32
- config: {
33
- channels: {
34
- wecom: {
35
- mediaMaxMb: 24,
36
- },
37
- },
38
- },
39
- runtime: {
40
- error: logError,
41
- },
42
- } as never,
43
- msg: {
44
- msgid: "msg-quote-file",
45
- msgtype: "text",
46
- text: { content: "看这个引用" },
47
- quote: {
48
- msgtype: "file",
49
- file: {
50
- url: "https://example.com/quoted.pdf",
51
- },
52
- },
53
- } as never,
54
- recordOperationalIssue,
55
- });
56
-
57
- expect(decryptWecomMediaWithMeta).toHaveBeenCalledWith(
58
- "https://example.com/quoted.pdf",
59
- "account-aes-key",
60
- expect.objectContaining({
61
- maxBytes: 24 * 1024 * 1024,
62
- }),
63
- );
64
- expect(result.media).toBeDefined();
65
- expect(result.media?.contentType).toBe("application/pdf");
66
- expect(result.body).toContain("[引用: 文件]");
67
- });
68
-
69
- it("keeps quote.voice as text only without media decryption", async () => {
70
- const result = await processBotInboundMessage({
71
- target: {
72
- account: {
73
- accountId: "purple",
74
- encodingAESKey: "account-aes-key",
75
- },
76
- config: {
77
- channels: {
78
- wecom: {
79
- mediaMaxMb: 24,
80
- },
81
- },
82
- },
83
- runtime: {
84
- error: logError,
85
- },
86
- } as never,
87
- msg: {
88
- msgid: "msg-quote-voice",
89
- msgtype: "text",
90
- text: { content: "这个语音引用是什么意思" },
91
- quote: {
92
- msgtype: "voice",
93
- voice: { content: "这里是语音转写文本" },
94
- },
95
- } as never,
96
- recordOperationalIssue,
97
- });
98
-
99
- expect(result.media).toBeUndefined();
100
- expect(result.body).toContain("[引用: 语音] 这里是语音转写文本");
101
- expect(decryptWecomMediaWithMeta).not.toHaveBeenCalled();
102
- });
103
-
104
- it("extracts first image from quote.mixed", async () => {
105
- vi.mocked(decryptWecomMediaWithMeta).mockResolvedValue({
106
- buffer: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
107
- sourceContentType: "image/png",
108
- sourceFilename: "quoted.png",
109
- sourceUrl: "https://example.com/quoted.png",
110
- });
111
-
112
- const result = await processBotInboundMessage({
113
- target: {
114
- account: {
115
- accountId: "purple",
116
- encodingAESKey: "account-aes-key",
117
- },
118
- config: {
119
- channels: {
120
- wecom: {
121
- mediaMaxMb: 24,
122
- },
123
- },
124
- },
125
- runtime: {
126
- error: logError,
127
- },
128
- } as never,
129
- msg: {
130
- msgid: "msg-quote-mixed",
131
- msgtype: "text",
132
- text: { content: "看这个图文引用" },
133
- quote: {
134
- msgtype: "mixed",
135
- mixed: {
136
- msg_item: [
137
- { msgtype: "text", text: { content: "正文" } },
138
- { msgtype: "image", image: { url: "https://example.com/quoted.png", aeskey: "item-aes-key" } },
139
- ],
140
- },
141
- },
142
- } as never,
143
- recordOperationalIssue,
144
- });
145
-
146
- expect(result.media).toBeDefined();
147
- expect(result.media?.contentType).toBe("image/png");
148
- expect(decryptWecomMediaWithMeta).toHaveBeenCalledWith(
149
- "https://example.com/quoted.png",
150
- "item-aes-key",
151
- expect.any(Object),
152
- );
153
- });
154
-
155
- it("records expired_or_forbidden reason for quote media decrypt failure", async () => {
156
- vi.mocked(decryptWecomMediaWithMeta).mockRejectedValue(new Error("HTTP 403 forbidden"));
157
-
158
- const result = await processBotInboundMessage({
159
- target: {
160
- account: {
161
- accountId: "purple",
162
- encodingAESKey: "account-aes-key",
163
- },
164
- config: {
165
- channels: {
166
- wecom: {
167
- mediaMaxMb: 24,
168
- },
169
- },
170
- },
171
- runtime: {
172
- error: logError,
173
- },
174
- } as never,
175
- msg: {
176
- msgid: "msg-quote-fail",
177
- msgtype: "text",
178
- text: { content: "读取这个引用" },
179
- quote: {
180
- msgtype: "file",
181
- file: { url: "https://example.com/expired.pdf" },
182
- },
183
- } as never,
184
- recordOperationalIssue,
185
- });
186
-
187
- expect(result.media).toBeUndefined();
188
- expect(result.body).toContain("[quote:file]");
189
- expect(recordOperationalIssue).toHaveBeenCalledWith(
190
- expect.objectContaining({
191
- summary: expect.stringContaining("reason=expired_or_forbidden"),
192
- }),
193
- );
194
- });
195
-
196
- it("downloads quote.image for text messages", async () => {
197
- vi.mocked(decryptWecomMediaWithMeta).mockResolvedValue({
198
- buffer: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
199
- sourceContentType: "image/png",
200
- sourceFilename: "quoted.png",
201
- sourceUrl: "https://example.com/quoted.png",
202
- });
203
-
204
- const result = await processBotInboundMessage({
205
- target: {
206
- account: {
207
- accountId: "purple",
208
- encodingAESKey: "account-aes-key",
209
- },
210
- config: {
211
- channels: {
212
- wecom: {
213
- mediaMaxMb: 24,
214
- },
215
- },
216
- },
217
- runtime: {
218
- error: logError,
219
- },
220
- } as never,
221
- msg: {
222
- msgid: "msg-quote-image",
223
- msgtype: "text",
224
- text: { content: "看这个引用图片" },
225
- quote: {
226
- msgtype: "image",
227
- image: {
228
- url: "https://example.com/quoted.png",
229
- },
230
- },
231
- } as never,
232
- recordOperationalIssue,
233
- });
234
-
235
- expect(decryptWecomMediaWithMeta).toHaveBeenCalledWith(
236
- "https://example.com/quoted.png",
237
- "account-aes-key",
238
- expect.objectContaining({
239
- maxBytes: 24 * 1024 * 1024,
240
- }),
241
- );
242
- expect(result.media).toBeDefined();
243
- expect(result.media?.contentType).toBe("image/png");
244
- expect(result.body).toContain("[引用: 图片]");
245
- });
246
-
247
- it("downloads quote.video for text messages", async () => {
248
- vi.mocked(decryptWecomMediaWithMeta).mockResolvedValue({
249
- buffer: Buffer.from([0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70]),
250
- sourceContentType: "video/mp4",
251
- sourceFilename: "quoted.mp4",
252
- sourceUrl: "https://example.com/quoted.mp4",
253
- });
254
-
255
- const result = await processBotInboundMessage({
256
- target: {
257
- account: {
258
- accountId: "purple",
259
- encodingAESKey: "account-aes-key",
260
- },
261
- config: {
262
- channels: {
263
- wecom: {
264
- mediaMaxMb: 24,
265
- },
266
- },
267
- },
268
- runtime: {
269
- error: logError,
270
- },
271
- } as never,
272
- msg: {
273
- msgid: "msg-quote-video",
274
- msgtype: "text",
275
- text: { content: "看这个引用视频" },
276
- quote: {
277
- msgtype: "video",
278
- video: {
279
- url: "https://example.com/quoted.mp4",
280
- },
281
- },
282
- } as never,
283
- recordOperationalIssue,
284
- });
285
-
286
- expect(decryptWecomMediaWithMeta).toHaveBeenCalledWith(
287
- "https://example.com/quoted.mp4",
288
- "account-aes-key",
289
- expect.objectContaining({
290
- maxBytes: 24 * 1024 * 1024,
291
- }),
292
- );
293
- expect(result.media).toBeDefined();
294
- expect(result.media?.contentType).toBe("video/mp4");
295
- expect(result.body).toContain("[引用: 视频]");
296
- });
297
-
298
- it("prioritizes top-level media over quote media", async () => {
299
- vi.mocked(decryptWecomMediaWithMeta).mockResolvedValue({
300
- buffer: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
301
- sourceContentType: "image/png",
302
- sourceFilename: "top-level.png",
303
- sourceUrl: "https://example.com/top-level.png",
304
- });
305
-
306
- const result = await processBotInboundMessage({
307
- target: {
308
- account: {
309
- accountId: "purple",
310
- encodingAESKey: "account-aes-key",
311
- },
312
- config: {
313
- channels: {
314
- wecom: {
315
- mediaMaxMb: 24,
316
- },
317
- },
318
- },
319
- runtime: {
320
- error: logError,
321
- },
322
- } as never,
323
- msg: {
324
- msgid: "msg-both-media",
325
- msgtype: "image",
326
- image: {
327
- url: "https://example.com/top-level.png",
328
- },
329
- quote: {
330
- msgtype: "file",
331
- file: {
332
- url: "https://example.com/quoted.pdf",
333
- },
334
- },
335
- } as never,
336
- recordOperationalIssue,
337
- });
338
-
339
- // Should have downloaded top-level image, not quote file
340
- expect(decryptWecomMediaWithMeta).toHaveBeenCalledWith(
341
- "https://example.com/top-level.png",
342
- "account-aes-key",
343
- expect.any(Object),
344
- );
345
- expect(decryptWecomMediaWithMeta).not.toHaveBeenCalledWith(
346
- expect.stringContaining("quoted.pdf"),
347
- expect.any(String),
348
- expect.any(Object),
349
- );
350
- expect(result.media?.contentType).toBe("image/png");
351
- expect(result.body).toBe("[image]");
352
- });
353
-
354
- it("classifies timeout error for quote media", async () => {
355
- vi.mocked(decryptWecomMediaWithMeta).mockRejectedValue(new Error("Request timeout after 15000ms"));
356
-
357
- const result = await processBotInboundMessage({
358
- target: {
359
- account: {
360
- accountId: "purple",
361
- encodingAESKey: "account-aes-key",
362
- },
363
- config: {
364
- channels: {
365
- wecom: {
366
- mediaMaxMb: 24,
367
- },
368
- },
369
- },
370
- runtime: {
371
- error: logError,
372
- },
373
- } as never,
374
- msg: {
375
- msgid: "msg-quote-timeout",
376
- msgtype: "text",
377
- text: { content: "下载这个文件" },
378
- quote: {
379
- msgtype: "file",
380
- file: { url: "https://example.com/slow.pdf" },
381
- },
382
- } as never,
383
- recordOperationalIssue,
384
- });
385
-
386
- expect(result.media).toBeUndefined();
387
- expect(recordOperationalIssue).toHaveBeenCalledWith(
388
- expect.objectContaining({
389
- summary: expect.stringContaining("reason=timeout"),
390
- }),
391
- );
392
- });
393
-
394
- it("classifies decrypt error for quote media", async () => {
395
- vi.mocked(decryptWecomMediaWithMeta).mockRejectedValue(new Error("Bad decrypt"));
396
-
397
- const result = await processBotInboundMessage({
398
- target: {
399
- account: {
400
- accountId: "purple",
401
- encodingAESKey: "account-aes-key",
402
- },
403
- config: {
404
- channels: {
405
- wecom: {
406
- mediaMaxMb: 24,
407
- },
408
- },
409
- },
410
- runtime: {
411
- error: logError,
412
- },
413
- } as never,
414
- msg: {
415
- msgid: "msg-quote-decrypt-fail",
416
- msgtype: "voice",
417
- voice: { content: "这是语音转写" },
418
- quote: {
419
- msgtype: "image",
420
- image: { url: "https://example.com/bad.png" },
421
- },
422
- } as never,
423
- recordOperationalIssue,
424
- });
425
-
426
- expect(result.media).toBeUndefined();
427
- expect(recordOperationalIssue).toHaveBeenCalledWith(
428
- expect.objectContaining({
429
- summary: expect.stringContaining("reason=decrypt"),
430
- }),
431
- );
432
- });
433
- });