@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,290 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { mapBotWsFrameToInboundEvent } from "./inbound.js";
4
- import type { ResolvedBotAccount } from "../../types/index.js";
5
-
6
- function createBotAccount(): ResolvedBotAccount {
7
- return {
8
- accountId: "haidao",
9
- configured: true,
10
- primaryTransport: "ws",
11
- wsConfigured: true,
12
- webhookConfigured: false,
13
- config: {},
14
- ws: {
15
- botId: "bot-id",
16
- secret: "secret",
17
- },
18
- token: "",
19
- encodingAESKey: "",
20
- receiveId: "",
21
- botId: "bot-id",
22
- secret: "secret",
23
- };
24
- }
25
-
26
- describe("mapBotWsFrameToInboundEvent", () => {
27
- it("includes quote content in text events", () => {
28
- const event = mapBotWsFrameToInboundEvent({
29
- account: createBotAccount(),
30
- frame: {
31
- cmd: "aibot_msg_callback",
32
- headers: { req_id: "req-1" },
33
- body: {
34
- msgid: "msg-1",
35
- msgtype: "text",
36
- chattype: "group",
37
- chatid: "group-1",
38
- from: { userid: "user-1" },
39
- text: { content: "@daodao 这个线索价值" },
40
- quote: {
41
- msgtype: "text",
42
- text: { content: "原始引用内容" },
43
- },
44
- },
45
- },
46
- });
47
-
48
- expect(event.text).toBe("@daodao 这个线索价值\n\n> 原始引用内容");
49
- });
50
-
51
- it("extracts attachments from mixed events", () => {
52
- const event = mapBotWsFrameToInboundEvent({
53
- account: createBotAccount(),
54
- frame: {
55
- cmd: "aibot_msg_callback",
56
- headers: { req_id: "req-2" },
57
- body: {
58
- msgid: "msg-2",
59
- msgtype: "mixed",
60
- chattype: "group",
61
- chatid: "group-2",
62
- from: { userid: "user-2" },
63
- mixed: {
64
- msg_item: [
65
- {
66
- msgtype: "text",
67
- text: { content: "来看看这张图" },
68
- },
69
- {
70
- msgtype: "image",
71
- image: { url: "https://example.com/image.jpg", aeskey: "mock-aes-key" },
72
- },
73
- {
74
- msgtype: "file",
75
- file: { url: "https://example.com/doc.pdf", aeskey: "mock-file-key" },
76
- },
77
- {
78
- msgtype: "video",
79
- video: { url: "https://example.com/demo.mp4", aeskey: "mock-video-key" },
80
- },
81
- ],
82
- },
83
- },
84
- },
85
- });
86
-
87
- expect(event.attachments).toBeDefined();
88
- expect(event.attachments).toHaveLength(3);
89
- expect(event.attachments![0]).toEqual({
90
- name: "image",
91
- remoteUrl: "https://example.com/image.jpg",
92
- aesKey: "mock-aes-key",
93
- });
94
- expect(event.attachments![1]).toEqual({
95
- name: "file",
96
- remoteUrl: "https://example.com/doc.pdf",
97
- aesKey: "mock-file-key",
98
- });
99
- expect(event.attachments![2]).toEqual({
100
- name: "video",
101
- remoteUrl: "https://example.com/demo.mp4",
102
- aesKey: "mock-video-key",
103
- });
104
- });
105
-
106
- it("extracts attachment from quote file in text events", () => {
107
- const event = mapBotWsFrameToInboundEvent({
108
- account: createBotAccount(),
109
- frame: {
110
- cmd: "aibot_msg_callback",
111
- headers: { req_id: "req-3" },
112
- body: {
113
- msgid: "msg-3",
114
- msgtype: "text",
115
- chattype: "single",
116
- from: { userid: "user-3" },
117
- text: { content: "请读取这个引用文件" },
118
- quote: {
119
- msgtype: "file",
120
- file: { url: "https://example.com/quoted.pdf", aeskey: "quoted-file-key" },
121
- },
122
- },
123
- },
124
- });
125
-
126
- expect(event.inboundKind).toBe("text");
127
- expect(event.attachments).toEqual([
128
- {
129
- name: "file",
130
- remoteUrl: "https://example.com/quoted.pdf",
131
- aesKey: "quoted-file-key",
132
- },
133
- ]);
134
- });
135
-
136
- it("maps top-level video to video inbound kind and attachment", () => {
137
- const event = mapBotWsFrameToInboundEvent({
138
- account: createBotAccount(),
139
- frame: {
140
- cmd: "aibot_msg_callback",
141
- headers: { req_id: "req-4" },
142
- body: {
143
- msgid: "msg-4",
144
- msgtype: "video",
145
- chattype: "single",
146
- from: { userid: "user-4" },
147
- video: { url: "https://example.com/top-video.mp4", aeskey: "top-video-key" },
148
- },
149
- },
150
- });
151
-
152
- expect(event.inboundKind).toBe("video");
153
- expect(event.attachments).toEqual([
154
- {
155
- name: "video",
156
- remoteUrl: "https://example.com/top-video.mp4",
157
- aesKey: "top-video-key",
158
- },
159
- ]);
160
- });
161
-
162
- it("extracts attachment from quote image in text events", () => {
163
- const event = mapBotWsFrameToInboundEvent({
164
- account: createBotAccount(),
165
- frame: {
166
- cmd: "aibot_msg_callback",
167
- headers: { req_id: "req-5" },
168
- body: {
169
- msgid: "msg-5",
170
- msgtype: "text",
171
- chattype: "single",
172
- from: { userid: "user-5" },
173
- text: { content: "请看这张引用图片" },
174
- quote: {
175
- msgtype: "image",
176
- image: { url: "https://example.com/quoted.jpg", aeskey: "quoted-image-key" },
177
- },
178
- },
179
- },
180
- });
181
-
182
- expect(event.inboundKind).toBe("text");
183
- expect(event.attachments).toEqual([
184
- {
185
- name: "image",
186
- remoteUrl: "https://example.com/quoted.jpg",
187
- aesKey: "quoted-image-key",
188
- },
189
- ]);
190
- });
191
-
192
- it("extracts attachment from quote video in text events", () => {
193
- const event = mapBotWsFrameToInboundEvent({
194
- account: createBotAccount(),
195
- frame: {
196
- cmd: "aibot_msg_callback",
197
- headers: { req_id: "req-6" },
198
- body: {
199
- msgid: "msg-6",
200
- msgtype: "voice",
201
- chattype: "single",
202
- from: { userid: "user-6" },
203
- voice: { content: "语音转写内容" },
204
- quote: {
205
- msgtype: "video",
206
- video: { url: "https://example.com/quoted.mp4", aeskey: "quoted-video-key" },
207
- },
208
- },
209
- },
210
- });
211
-
212
- expect(event.inboundKind).toBe("voice");
213
- expect(event.attachments).toEqual([
214
- {
215
- name: "video",
216
- remoteUrl: "https://example.com/quoted.mp4",
217
- aesKey: "quoted-video-key",
218
- },
219
- ]);
220
- });
221
-
222
- it("prioritizes top-level media over quote media in WS events", () => {
223
- const event = mapBotWsFrameToInboundEvent({
224
- account: createBotAccount(),
225
- frame: {
226
- cmd: "aibot_msg_callback",
227
- headers: { req_id: "req-7" },
228
- body: {
229
- msgid: "msg-7",
230
- msgtype: "image",
231
- chattype: "group",
232
- chatid: "group-7",
233
- from: { userid: "user-7" },
234
- image: { url: "https://example.com/top.jpg", aeskey: "top-image-key" },
235
- quote: {
236
- msgtype: "file",
237
- file: { url: "https://example.com/quoted.pdf", aeskey: "quoted-file-key" },
238
- },
239
- },
240
- },
241
- });
242
-
243
- // Should only include top-level image, not quote file
244
- expect(event.inboundKind).toBe("image");
245
- expect(event.attachments).toEqual([
246
- {
247
- name: "image",
248
- remoteUrl: "https://example.com/top.jpg",
249
- aesKey: "top-image-key",
250
- },
251
- ]);
252
- });
253
-
254
- it("extracts first image from quote.mixed in text events", () => {
255
- const event = mapBotWsFrameToInboundEvent({
256
- account: createBotAccount(),
257
- frame: {
258
- cmd: "aibot_msg_callback",
259
- headers: { req_id: "req-8" },
260
- body: {
261
- msgid: "msg-8",
262
- msgtype: "text",
263
- chattype: "single",
264
- from: { userid: "user-8" },
265
- text: { content: "这个引用有图文内容" },
266
- quote: {
267
- msgtype: "mixed",
268
- mixed: {
269
- msg_item: [
270
- { msgtype: "text", text: { content: "一些文本" } },
271
- { msgtype: "image", image: { url: "https://example.com/mixed-img1.jpg", aeskey: "mixed-key-1" } },
272
- { msgtype: "image", image: { url: "https://example.com/mixed-img2.jpg", aeskey: "mixed-key-2" } },
273
- ],
274
- },
275
- },
276
- },
277
- },
278
- });
279
-
280
- expect(event.inboundKind).toBe("text");
281
- // Should only extract first image from quote.mixed
282
- expect(event.attachments).toEqual([
283
- {
284
- name: "image",
285
- remoteUrl: "https://example.com/mixed-img1.jpg",
286
- aesKey: "mixed-key-1",
287
- },
288
- ]);
289
- });
290
- });
@@ -1,163 +0,0 @@
1
- import type { BaseMessage, EventMessage, WsFrame } from "@wecom/aibot-node-sdk";
2
-
3
- import { buildInboundBody } from "../bot-webhook/message-shape.js";
4
- import type {
5
- ResolvedBotAccount,
6
- UnifiedInboundEvent,
7
- WecomBotInboundMessage,
8
- WecomInboundKind,
9
- } from "../../types/index.js";
10
-
11
- function resolveInboundKind(message: BaseMessage | EventMessage): WecomInboundKind {
12
- if (message.msgtype === "event") {
13
- const eventType = String((message as EventMessage).event?.eventtype ?? "").trim();
14
- if (eventType === "enter_chat") return "welcome";
15
- if (eventType === "template_card_event") return "template-card-event";
16
- return "event";
17
- }
18
- switch (message.msgtype) {
19
- case "image":
20
- return "image";
21
- case "file":
22
- return "file";
23
- case "voice":
24
- return "voice";
25
- case "video":
26
- return "video";
27
- case "mixed":
28
- return "mixed";
29
- default:
30
- return "text";
31
- }
32
- }
33
-
34
- function pushAttachment(
35
- list: NonNullable<UnifiedInboundEvent["attachments"]>,
36
- name: "image" | "file" | "video",
37
- remoteUrl?: string,
38
- aesKey?: string,
39
- ): void {
40
- if (!remoteUrl) {
41
- return;
42
- }
43
- list.push({ name, remoteUrl, aesKey });
44
- }
45
-
46
- function resolveEventText(message: BaseMessage | EventMessage, account: ResolvedBotAccount): string {
47
- if (message.msgtype !== "event") {
48
- return buildInboundBody(message as WecomBotInboundMessage);
49
- }
50
-
51
- const event = message as EventMessage;
52
- if (event.event?.eventtype === "enter_chat" && account.config.welcomeText) {
53
- return account.config.welcomeText;
54
- }
55
- return `[event:${String(event.event?.eventtype ?? "unknown")}]`;
56
- }
57
-
58
- export function mapBotWsFrameToInboundEvent(params: {
59
- account: ResolvedBotAccount;
60
- frame: WsFrame<BaseMessage | EventMessage>;
61
- }): UnifiedInboundEvent {
62
- const { account, frame } = params;
63
- const body = frame.body;
64
- if (!body) {
65
- throw new Error("Bot WS frame body is required");
66
- }
67
- const peerKind = body.chattype === "group" ? "group" : "direct";
68
- const senderId = body.from?.userid ?? "unknown";
69
- const peerId = peerKind === "group" ? body.chatid ?? senderId : senderId;
70
- const inboundKind = resolveInboundKind(body);
71
-
72
- let attachments: UnifiedInboundEvent["attachments"];
73
- const collected: NonNullable<UnifiedInboundEvent["attachments"]> = [];
74
- if (body.msgtype === "image") {
75
- pushAttachment(collected, "image", (body as any).image?.url, (body as any).image?.aeskey);
76
- } else if (body.msgtype === "file") {
77
- pushAttachment(collected, "file", (body as any).file?.url, (body as any).file?.aeskey);
78
- } else if (body.msgtype === "video") {
79
- pushAttachment(collected, "video", (body as any).video?.url, (body as any).video?.aeskey);
80
- } else if (body.msgtype === "mixed") {
81
- const items = (body as any).mixed?.msg_item;
82
- if (Array.isArray(items)) {
83
- for (const item of items) {
84
- const itemType = String(item.msgtype ?? "").toLowerCase();
85
- if (itemType === "image") {
86
- pushAttachment(collected, "image", item.image?.url, item.image?.aeskey);
87
- } else if (itemType === "file") {
88
- pushAttachment(collected, "file", item.file?.url, item.file?.aeskey);
89
- } else if (itemType === "video") {
90
- pushAttachment(collected, "video", item.video?.url, item.video?.aeskey);
91
- }
92
- }
93
- }
94
- }
95
-
96
- // 新增支持:如果没有顶层媒体,尝试从引用中提取媒体附件
97
- // 优先级:quote.image/file/video 优先,其次 quote.mixed 中第一个图片
98
- if (collected.length === 0) {
99
- const quote = (body as any).quote;
100
- if (quote) {
101
- const quoteType = String(quote.msgtype ?? "").toLowerCase();
102
- // 处理单个媒体类型的引用
103
- if (quoteType === "image") {
104
- pushAttachment(collected, "image", quote.image?.url, quote.image?.aeskey);
105
- } else if (quoteType === "file") {
106
- pushAttachment(collected, "file", quote.file?.url, quote.file?.aeskey);
107
- } else if (quoteType === "video") {
108
- pushAttachment(collected, "video", quote.video?.url, quote.video?.aeskey);
109
- }
110
- // 处理图文混合类型:只提取第一个图片以保持与 webhook 一致
111
- else if (quoteType === "mixed" && Array.isArray(quote.mixed?.msg_item)) {
112
- for (const item of quote.mixed.msg_item) {
113
- const itemType = String(item.msgtype ?? "").toLowerCase();
114
- if (itemType === "image") {
115
- pushAttachment(collected, "image", item.image?.url, item.image?.aeskey);
116
- break;
117
- }
118
- }
119
- }
120
- }
121
- }
122
-
123
- if (collected.length > 0) {
124
- attachments = collected;
125
- }
126
-
127
- return {
128
- accountId: account.accountId,
129
- capability: "bot",
130
- transport: "bot-ws",
131
- inboundKind,
132
- messageId: body.msgid,
133
- conversation: {
134
- accountId: account.accountId,
135
- peerKind,
136
- peerId,
137
- senderId,
138
- },
139
- senderName: senderId,
140
- text: resolveEventText(body, account),
141
- timestamp: typeof body.create_time === "number" ? body.create_time : Date.now(),
142
- raw: {
143
- transport: "bot-ws",
144
- command: frame.cmd,
145
- headers: frame.headers,
146
- body,
147
- envelopeType: "ws",
148
- },
149
- replyContext: {
150
- transport: "bot-ws",
151
- accountId: account.accountId,
152
- reqId: frame.headers.req_id,
153
- raw: {
154
- transport: "bot-ws",
155
- command: frame.cmd,
156
- headers: frame.headers,
157
- body,
158
- envelopeType: "ws",
159
- },
160
- },
161
- ...(attachments && { attachments }),
162
- };
163
- }
@@ -1,44 +0,0 @@
1
- import type { WSClient } from "@wecom/aibot-node-sdk";
2
- import { fetchRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
- import { uploadAndSendBotWsMedia } from "./media.js";
5
-
6
- vi.mock("openclaw/plugin-sdk/media-runtime", () => ({
7
- assertLocalMediaAllowed: vi.fn(),
8
- detectMime: vi.fn(),
9
- fetchRemoteMedia: vi.fn(),
10
- }));
11
-
12
- describe("uploadAndSendBotWsMedia", () => {
13
- const fetchRemoteMediaMock = vi.mocked(fetchRemoteMedia);
14
-
15
- beforeEach(() => {
16
- fetchRemoteMediaMock.mockReset();
17
- fetchRemoteMediaMock.mockResolvedValue({
18
- buffer: Buffer.from("png"),
19
- contentType: "image/png",
20
- fileName: "sample.png",
21
- } as never);
22
- });
23
-
24
- it("passes the configured maxBytes to outbound media loading", async () => {
25
- const wsClient = {
26
- uploadMedia: vi.fn().mockResolvedValue({ media_id: "media-1" }),
27
- sendMediaMessage: vi.fn().mockResolvedValue({ headers: { req_id: "req-1" } }),
28
- } as unknown as WSClient;
29
-
30
- await uploadAndSendBotWsMedia({
31
- wsClient,
32
- chatId: "hidao",
33
- mediaUrl: "https://example.com/sample.png",
34
- maxBytes: 42 * 1024 * 1024,
35
- });
36
-
37
- expect(fetchRemoteMediaMock).toHaveBeenCalledWith(
38
- expect.objectContaining({
39
- url: "https://example.com/sample.png",
40
- maxBytes: 42 * 1024 * 1024,
41
- }),
42
- );
43
- });
44
- });