@yanhaidao/wecom 2.4.120 → 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 (323) hide show
  1. package/README.md +4 -5
  2. package/dist/index.js +68 -0
  3. package/dist/src/accounts.js +20 -0
  4. package/dist/src/agent/handler.js +895 -0
  5. package/dist/src/agent/index.js +5 -0
  6. package/dist/src/app/account-runtime.js +216 -0
  7. package/dist/src/app/bootstrap.js +19 -0
  8. package/dist/src/app/index.js +118 -0
  9. package/dist/src/capability/agent/delivery-service.js +63 -0
  10. package/dist/src/capability/agent/fallback-policy.js +6 -0
  11. package/dist/src/capability/agent/ingress-service.js +33 -0
  12. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  13. package/dist/src/capability/bot/dispatch-config.js +45 -0
  14. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  15. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  16. package/dist/src/capability/bot/sandbox-media.js +138 -0
  17. package/dist/src/capability/bot/service.js +49 -0
  18. package/dist/src/capability/bot/stream-delivery.js +321 -0
  19. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  20. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  21. package/dist/src/capability/bot/types.js +1 -0
  22. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  23. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  24. package/dist/src/capability/calendar/tool.js +365 -0
  25. package/dist/src/capability/calendar/types.js +12 -0
  26. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  27. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  28. package/dist/src/capability/doc/tool.js +1556 -0
  29. package/dist/src/capability/doc/types.js +113 -0
  30. package/dist/src/capability/mcp/index.js +3 -0
  31. package/dist/src/capability/mcp/schema.js +102 -0
  32. package/dist/src/capability/mcp/tool.js +146 -0
  33. package/dist/src/capability/mcp/transport.js +293 -0
  34. package/dist/src/channel.js +224 -0
  35. package/dist/src/config/accounts.js +236 -0
  36. package/dist/src/config/derived-paths.js +31 -0
  37. package/dist/src/config/index.js +7 -0
  38. package/dist/src/config/media.js +110 -0
  39. package/dist/src/config/network.js +32 -0
  40. package/dist/src/config/routing.js +20 -0
  41. package/dist/src/config/runtime-config.js +25 -0
  42. package/dist/src/config/schema.js +4 -0
  43. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  44. package/dist/src/context-store.js +219 -0
  45. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  46. package/dist/src/crypto/index.js +9 -0
  47. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  48. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  49. package/dist/src/crypto.js +145 -0
  50. package/dist/src/domain/models.js +1 -0
  51. package/dist/src/domain/policies.js +32 -0
  52. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  53. package/dist/src/gateway-monitor.js +139 -0
  54. package/dist/src/http.js +114 -0
  55. package/{src/media.ts → dist/src/media.js} +21 -40
  56. package/dist/src/monitor/limits.js +7 -0
  57. package/dist/src/monitor/state.js +28 -0
  58. package/dist/src/monitor.js +84 -0
  59. package/dist/src/observability/audit-log.js +30 -0
  60. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  61. package/dist/src/observability/raw-envelope-log.js +24 -0
  62. package/dist/src/observability/status-registry.js +9 -0
  63. package/dist/src/observability/transport-session-view.js +14 -0
  64. package/dist/src/onboarding.js +546 -0
  65. package/dist/src/outbound.js +557 -0
  66. package/dist/src/runtime/dispatcher.js +57 -0
  67. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  68. package/dist/src/runtime/outbound-intent.js +1 -0
  69. package/dist/src/runtime/reply-orchestrator.js +38 -0
  70. package/dist/src/runtime/routing-bridge.js +26 -0
  71. package/dist/src/runtime/session-manager.js +112 -0
  72. package/dist/src/runtime/source-registry.js +174 -0
  73. package/dist/src/runtime.js +1 -0
  74. package/dist/src/shared/command-auth.js +57 -0
  75. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  76. package/dist/src/shared/media-asset.js +65 -0
  77. package/dist/src/shared/media-service.js +59 -0
  78. package/dist/src/shared/media-types.js +1 -0
  79. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  80. package/dist/src/store/active-reply-store.js +41 -0
  81. package/dist/src/store/interfaces.js +1 -0
  82. package/dist/src/store/memory-store.js +33 -0
  83. package/dist/src/store/stream-batch-store.js +319 -0
  84. package/{src/target.ts → dist/src/target.js} +15 -48
  85. package/dist/src/transport/agent-api/client.js +168 -0
  86. package/dist/src/transport/agent-api/core.js +337 -0
  87. package/dist/src/transport/agent-api/delivery.js +28 -0
  88. package/dist/src/transport/agent-api/media-upload.js +4 -0
  89. package/dist/src/transport/agent-api/reply.js +24 -0
  90. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  91. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  92. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  93. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  94. package/dist/src/transport/agent-callback/inbound.js +4 -0
  95. package/dist/src/transport/agent-callback/reply.js +8 -0
  96. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  97. package/dist/src/transport/agent-callback/session.js +15 -0
  98. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  99. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  100. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  101. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  102. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  103. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  104. package/dist/src/transport/bot-webhook/reply.js +9 -0
  105. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  106. package/dist/src/transport/bot-webhook/session.js +15 -0
  107. package/dist/src/transport/bot-ws/inbound.js +147 -0
  108. package/dist/src/transport/bot-ws/media.js +236 -0
  109. package/dist/src/transport/bot-ws/reply.js +310 -0
  110. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  111. package/dist/src/transport/bot-ws/session.js +15 -0
  112. package/dist/src/transport/http/common.js +78 -0
  113. package/dist/src/transport/http/registry.js +71 -0
  114. package/dist/src/transport/http/request-handler.js +51 -0
  115. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  116. package/dist/src/types/account.js +1 -0
  117. package/dist/src/types/config.js +1 -0
  118. package/dist/src/types/constants.js +28 -0
  119. package/dist/src/types/events.js +1 -0
  120. package/dist/src/types/index.js +1 -0
  121. package/dist/src/types/legacy-stream.js +1 -0
  122. package/dist/src/types/message.js +5 -0
  123. package/dist/src/types/runtime-context.js +1 -0
  124. package/dist/src/types/runtime.js +1 -0
  125. package/dist/src/types.js +1 -0
  126. package/dist/src/upstream/index.js +111 -0
  127. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  128. package/openclaw.plugin.json +15 -0
  129. package/package.json +18 -1
  130. package/.github/workflows/release.yml +0 -143
  131. package/GOVERNANCE.md +0 -26
  132. package/MENU_EVENT_CONF.md +0 -500
  133. package/MENU_EVENT_PLAN.md +0 -440
  134. package/SKILLS_CAL.md +0 -895
  135. package/SKILLS_DOC.md +0 -2288
  136. package/UPSTREAM_CONFIG.md +0 -170
  137. package/UPSTREAM_PLAN.md +0 -175
  138. package/assets/01.bot-add.png +0 -0
  139. package/assets/01.bot-setp2.png +0 -0
  140. package/assets/01.image.jpg +0 -0
  141. package/assets/02.agent.add.png +0 -0
  142. package/assets/02.agent.api-set.png +0 -0
  143. package/assets/02.image.jpg +0 -0
  144. package/assets/03.agent.page.png +0 -0
  145. package/assets/03.bot.page.png +0 -0
  146. package/assets/link-me.jpg +0 -0
  147. package/assets/register.png +0 -0
  148. package/changelog/v2.2.28.md +0 -70
  149. package/changelog/v2.3.10.md +0 -17
  150. package/changelog/v2.3.11.md +0 -19
  151. package/changelog/v2.3.12.md +0 -25
  152. package/changelog/v2.3.13.md +0 -19
  153. package/changelog/v2.3.14.md +0 -48
  154. package/changelog/v2.3.15.md +0 -15
  155. package/changelog/v2.3.16.md +0 -11
  156. package/changelog/v2.3.18.md +0 -22
  157. package/changelog/v2.3.19.md +0 -73
  158. package/changelog/v2.3.2.md +0 -28
  159. package/changelog/v2.3.26.md +0 -21
  160. package/changelog/v2.3.27.md +0 -33
  161. package/changelog/v2.3.4.md +0 -20
  162. package/changelog/v2.3.9.md +0 -22
  163. package/changelog/v2.4.12.md +0 -37
  164. package/compat-single-account.md +0 -148
  165. package/index.test.ts +0 -38
  166. package/scripts/test-proxy.ts +0 -70
  167. package/scripts/wecom/README.md +0 -123
  168. package/scripts/wecom/menu-click-help.js +0 -59
  169. package/scripts/wecom/menu-click-help.py +0 -55
  170. package/src/accounts.ts +0 -34
  171. package/src/agent/api-client.upload.test.ts +0 -109
  172. package/src/agent/event-router.test.ts +0 -421
  173. package/src/agent/event-router.ts +0 -272
  174. package/src/agent/handler.event-filter.test.ts +0 -135
  175. package/src/agent/handler.ts +0 -1250
  176. package/src/agent/index.ts +0 -12
  177. package/src/agent/script-runner.ts +0 -186
  178. package/src/agent/test-fixtures/invalid-json-script.mjs +0 -1
  179. package/src/agent/test-fixtures/reply-event-script.mjs +0 -29
  180. package/src/agent/test-fixtures/reply-event-script.py +0 -17
  181. package/src/app/account-runtime.ts +0 -276
  182. package/src/app/bootstrap.ts +0 -29
  183. package/src/app/index.ts +0 -192
  184. package/src/capability/agent/delivery-service.ts +0 -87
  185. package/src/capability/agent/fallback-policy.ts +0 -13
  186. package/src/capability/agent/ingress-service.ts +0 -38
  187. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  188. package/src/capability/bot/dispatch-config.ts +0 -47
  189. package/src/capability/bot/fallback-delivery.ts +0 -178
  190. package/src/capability/bot/local-path-delivery.ts +0 -215
  191. package/src/capability/bot/sandbox-media.test.ts +0 -221
  192. package/src/capability/bot/sandbox-media.ts +0 -176
  193. package/src/capability/bot/service.ts +0 -56
  194. package/src/capability/bot/stream-delivery.ts +0 -379
  195. package/src/capability/bot/stream-finalizer.ts +0 -120
  196. package/src/capability/bot/stream-orchestrator.ts +0 -371
  197. package/src/capability/bot/types.ts +0 -8
  198. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  199. package/src/capability/calendar/tool.ts +0 -417
  200. package/src/capability/calendar/types.ts +0 -309
  201. package/src/capability/doc/tool.ts +0 -1629
  202. package/src/capability/doc/types.ts +0 -792
  203. package/src/capability/mcp/index.ts +0 -10
  204. package/src/capability/mcp/schema.ts +0 -107
  205. package/src/capability/mcp/tool.ts +0 -174
  206. package/src/capability/mcp/transport.ts +0 -394
  207. package/src/channel.config.test.ts +0 -180
  208. package/src/channel.lifecycle.test.ts +0 -255
  209. package/src/channel.meta.test.ts +0 -26
  210. package/src/channel.ts +0 -256
  211. package/src/config/accounts.resolve.test.ts +0 -75
  212. package/src/config/accounts.ts +0 -312
  213. package/src/config/derived-paths.test.ts +0 -111
  214. package/src/config/derived-paths.ts +0 -41
  215. package/src/config/index.ts +0 -22
  216. package/src/config/media.test.ts +0 -113
  217. package/src/config/media.ts +0 -139
  218. package/src/config/network.ts +0 -20
  219. package/src/config/routing.test.ts +0 -88
  220. package/src/config/routing.ts +0 -26
  221. package/src/config/runtime-config.ts +0 -46
  222. package/src/config/schema.ts +0 -144
  223. package/src/context-store.ts +0 -297
  224. package/src/crypto/index.ts +0 -24
  225. package/src/crypto.test.ts +0 -32
  226. package/src/crypto.ts +0 -176
  227. package/src/domain/models.ts +0 -7
  228. package/src/domain/policies.ts +0 -36
  229. package/src/dynamic-agent.account-scope.test.ts +0 -17
  230. package/src/gateway-monitor.ts +0 -181
  231. package/src/http.ts +0 -137
  232. package/src/media.test.ts +0 -82
  233. package/src/monitor/limits.ts +0 -7
  234. package/src/monitor/state.queue.test.ts +0 -185
  235. package/src/monitor/state.ts +0 -34
  236. package/src/monitor.active.test.ts +0 -245
  237. package/src/monitor.inbound-filter.test.ts +0 -63
  238. package/src/monitor.integration.test.ts +0 -208
  239. package/src/monitor.ts +0 -121
  240. package/src/monitor.webhook.test.ts +0 -774
  241. package/src/observability/audit-log.ts +0 -48
  242. package/src/observability/legacy-operational-event-store.ts +0 -36
  243. package/src/observability/raw-envelope-log.ts +0 -28
  244. package/src/observability/status-registry.ts +0 -13
  245. package/src/observability/transport-session-view.ts +0 -14
  246. package/src/onboarding.test.ts +0 -336
  247. package/src/onboarding.ts +0 -704
  248. package/src/outbound.test.ts +0 -1271
  249. package/src/outbound.ts +0 -746
  250. package/src/runtime/dispatcher.ts +0 -71
  251. package/src/runtime/outbound-intent.ts +0 -4
  252. package/src/runtime/reply-orchestrator.test.ts +0 -71
  253. package/src/runtime/reply-orchestrator.ts +0 -67
  254. package/src/runtime/routing-bridge.test.ts +0 -115
  255. package/src/runtime/routing-bridge.ts +0 -44
  256. package/src/runtime/session-manager.test.ts +0 -174
  257. package/src/runtime/session-manager.ts +0 -139
  258. package/src/runtime/source-registry.ts +0 -249
  259. package/src/runtime.ts +0 -14
  260. package/src/shared/command-auth.ts +0 -87
  261. package/src/shared/media-asset.ts +0 -78
  262. package/src/shared/media-service.test.ts +0 -111
  263. package/src/shared/media-service.ts +0 -84
  264. package/src/shared/media-types.ts +0 -5
  265. package/src/shared/xml-parser.test.ts +0 -50
  266. package/src/store/active-reply-store.ts +0 -42
  267. package/src/store/interfaces.ts +0 -11
  268. package/src/store/memory-store.ts +0 -43
  269. package/src/store/stream-batch-store.ts +0 -350
  270. package/src/transport/agent-api/client.ts +0 -277
  271. package/src/transport/agent-api/core.ts +0 -463
  272. package/src/transport/agent-api/delivery.ts +0 -41
  273. package/src/transport/agent-api/media-upload.ts +0 -11
  274. package/src/transport/agent-api/reply.ts +0 -39
  275. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  276. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  277. package/src/transport/agent-api/upstream-reply.ts +0 -43
  278. package/src/transport/agent-callback/http-handler.ts +0 -47
  279. package/src/transport/agent-callback/inbound.ts +0 -5
  280. package/src/transport/agent-callback/reply.ts +0 -13
  281. package/src/transport/agent-callback/request-handler.ts +0 -244
  282. package/src/transport/agent-callback/session.ts +0 -23
  283. package/src/transport/bot-webhook/active-reply.ts +0 -39
  284. package/src/transport/bot-webhook/http-handler.ts +0 -48
  285. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -371
  286. package/src/transport/bot-webhook/inbound.ts +0 -5
  287. package/src/transport/bot-webhook/message-shape.ts +0 -89
  288. package/src/transport/bot-webhook/protocol.ts +0 -148
  289. package/src/transport/bot-webhook/reply.ts +0 -15
  290. package/src/transport/bot-webhook/request-handler.ts +0 -394
  291. package/src/transport/bot-webhook/session.ts +0 -23
  292. package/src/transport/bot-ws/inbound.test.ts +0 -96
  293. package/src/transport/bot-ws/inbound.ts +0 -116
  294. package/src/transport/bot-ws/media.test.ts +0 -44
  295. package/src/transport/bot-ws/media.ts +0 -321
  296. package/src/transport/bot-ws/reply.test.ts +0 -450
  297. package/src/transport/bot-ws/reply.ts +0 -365
  298. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  299. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  300. package/src/transport/bot-ws/session.ts +0 -28
  301. package/src/transport/http/common.ts +0 -109
  302. package/src/transport/http/registry.ts +0 -92
  303. package/src/transport/http/request-handler.ts +0 -84
  304. package/src/types/account.ts +0 -72
  305. package/src/types/config.ts +0 -166
  306. package/src/types/constants.ts +0 -31
  307. package/src/types/events.ts +0 -21
  308. package/src/types/global.d.ts +0 -9
  309. package/src/types/index.ts +0 -17
  310. package/src/types/legacy-stream.ts +0 -50
  311. package/src/types/message.ts +0 -187
  312. package/src/types/runtime-context.ts +0 -28
  313. package/src/types/runtime.ts +0 -165
  314. package/src/types.ts +0 -41
  315. package/src/upstream/index.ts +0 -150
  316. package/src/upstream.test.ts +0 -84
  317. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  318. package/tsconfig.json +0 -22
  319. package/vitest.config.ts +0 -26
  320. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  321. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  322. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  323. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -1,321 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import type { WeComMediaType, WsFrameHeaders, WSClient } from "@wecom/aibot-node-sdk";
5
- import {
6
- assertLocalMediaAllowed,
7
- detectMime,
8
- fetchRemoteMedia,
9
- } from "openclaw/plugin-sdk/media-runtime";
10
-
11
- const IMAGE_MAX_BYTES = 10 * 1024 * 1024;
12
- const VIDEO_MAX_BYTES = 10 * 1024 * 1024;
13
- const VOICE_MAX_BYTES = 2 * 1024 * 1024;
14
- const FILE_MAX_BYTES = 20 * 1024 * 1024;
15
-
16
- type FileSizeCheckResult = {
17
- finalType: WeComMediaType;
18
- shouldReject: boolean;
19
- rejectReason?: string;
20
- downgraded: boolean;
21
- downgradeNote?: string;
22
- };
23
-
24
- export type BotWsMediaSendResult = {
25
- ok: boolean;
26
- messageId?: string;
27
- finalType?: WeComMediaType;
28
- rejected?: boolean;
29
- rejectReason?: string;
30
- downgraded?: boolean;
31
- downgradeNote?: string;
32
- error?: string;
33
- };
34
-
35
- type ResolvedMediaFile = {
36
- buffer: Buffer;
37
- contentType: string;
38
- fileName: string;
39
- };
40
-
41
- const VOICE_SUPPORTED_MIMES = new Set(["audio/amr"]);
42
-
43
- function detectWeComMediaType(mimeType: string): WeComMediaType {
44
- const mime = mimeType.toLowerCase();
45
- if (mime.startsWith("image/")) return "image";
46
- if (mime.startsWith("video/")) return "video";
47
- if (mime.startsWith("audio/") || mime === "application/ogg") return "voice";
48
- return "file";
49
- }
50
-
51
- function mimeToExtension(mime: string): string {
52
- const map: Record<string, string> = {
53
- "image/jpeg": ".jpg",
54
- "image/png": ".png",
55
- "image/gif": ".gif",
56
- "image/webp": ".webp",
57
- "image/bmp": ".bmp",
58
- "image/svg+xml": ".svg",
59
- "video/mp4": ".mp4",
60
- "video/quicktime": ".mov",
61
- "video/x-msvideo": ".avi",
62
- "video/webm": ".webm",
63
- "audio/mpeg": ".mp3",
64
- "audio/ogg": ".ogg",
65
- "audio/wav": ".wav",
66
- "audio/amr": ".amr",
67
- "audio/aac": ".aac",
68
- "application/pdf": ".pdf",
69
- "application/zip": ".zip",
70
- "application/msword": ".doc",
71
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
72
- "application/vnd.ms-excel": ".xls",
73
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
74
- "text/plain": ".txt",
75
- };
76
- return map[mime] || ".bin";
77
- }
78
-
79
- function extractFileName(
80
- mediaUrl: string,
81
- providedFileName?: string,
82
- contentType?: string,
83
- ): string {
84
- if (providedFileName) return providedFileName;
85
- try {
86
- const url = new URL(mediaUrl, "file://");
87
- const lastPart = url.pathname.split("/").filter(Boolean).pop();
88
- if (lastPart && lastPart.includes(".")) {
89
- return decodeURIComponent(lastPart);
90
- }
91
- } catch {
92
- const lastPart = mediaUrl.split("/").filter(Boolean).pop();
93
- if (lastPart && lastPart.includes(".")) {
94
- return lastPart;
95
- }
96
- }
97
- return `media_${Date.now()}${mimeToExtension(contentType || "application/octet-stream")}`;
98
- }
99
-
100
- function resolveLocalMediaPath(mediaUrl: string): string {
101
- if (mediaUrl.startsWith("file://")) {
102
- return fileURLToPath(mediaUrl);
103
- }
104
- return mediaUrl;
105
- }
106
-
107
- async function loadOutboundMediaFile(params: {
108
- mediaUrl: string;
109
- mediaLocalRoots?: readonly string[];
110
- maxBytes: number;
111
- }): Promise<{
112
- buffer: Buffer;
113
- contentType?: string;
114
- fileName?: string;
115
- }> {
116
- if (/^https?:\/\//i.test(params.mediaUrl)) {
117
- return await fetchRemoteMedia({
118
- url: params.mediaUrl,
119
- maxBytes: params.maxBytes,
120
- filePathHint: params.mediaUrl,
121
- });
122
- }
123
-
124
- const mediaPath = resolveLocalMediaPath(params.mediaUrl);
125
- await assertLocalMediaAllowed(mediaPath, params.mediaLocalRoots);
126
- const buffer = await readFile(mediaPath);
127
- if (buffer.length > params.maxBytes) {
128
- throw new Error(
129
- `Media size ${(buffer.length / (1024 * 1024)).toFixed(2)}MB exceeds max ${(
130
- params.maxBytes /
131
- (1024 * 1024)
132
- ).toFixed(2)}MB`,
133
- );
134
- }
135
- return {
136
- buffer,
137
- fileName: path.basename(mediaPath),
138
- };
139
- }
140
-
141
- function applyFileSizeLimits(
142
- fileSize: number,
143
- detectedType: WeComMediaType,
144
- contentType?: string,
145
- ): FileSizeCheckResult {
146
- const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);
147
- if (fileSize > FILE_MAX_BYTES) {
148
- return {
149
- finalType: detectedType,
150
- shouldReject: true,
151
- rejectReason: `文件大小 ${fileSizeMB}MB 超过了企业微信允许的最大限制 20MB,无法发送。`,
152
- downgraded: false,
153
- };
154
- }
155
-
156
- switch (detectedType) {
157
- case "image":
158
- if (fileSize > IMAGE_MAX_BYTES) {
159
- return {
160
- finalType: "file",
161
- shouldReject: false,
162
- downgraded: true,
163
- downgradeNote: `图片大小 ${fileSizeMB}MB 超过 10MB 限制,已转为文件格式发送`,
164
- };
165
- }
166
- break;
167
- case "video":
168
- if (fileSize > VIDEO_MAX_BYTES) {
169
- return {
170
- finalType: "file",
171
- shouldReject: false,
172
- downgraded: true,
173
- downgradeNote: `视频大小 ${fileSizeMB}MB 超过 10MB 限制,已转为文件格式发送`,
174
- };
175
- }
176
- break;
177
- case "voice":
178
- if (contentType && !VOICE_SUPPORTED_MIMES.has(contentType.toLowerCase())) {
179
- return {
180
- finalType: "file",
181
- shouldReject: false,
182
- downgraded: true,
183
- downgradeNote: `语音格式 ${contentType} 不支持,企微仅支持 AMR 格式,已转为文件格式发送`,
184
- };
185
- }
186
- if (fileSize > VOICE_MAX_BYTES) {
187
- return {
188
- finalType: "file",
189
- shouldReject: false,
190
- downgraded: true,
191
- downgradeNote: `语音大小 ${fileSizeMB}MB 超过 2MB 限制,已转为文件格式发送`,
192
- };
193
- }
194
- break;
195
- default:
196
- break;
197
- }
198
-
199
- return {
200
- finalType: detectedType,
201
- shouldReject: false,
202
- downgraded: false,
203
- };
204
- }
205
-
206
- async function resolveMediaFile(
207
- mediaUrl: string,
208
- mediaLocalRoots?: readonly string[],
209
- maxBytes?: number,
210
- ): Promise<ResolvedMediaFile> {
211
- const result = await loadOutboundMediaFile({
212
- mediaUrl,
213
- maxBytes: maxBytes ?? FILE_MAX_BYTES,
214
- mediaLocalRoots,
215
- });
216
- let contentType = result.contentType || "application/octet-stream";
217
- if (contentType === "application/octet-stream" || contentType === "text/plain") {
218
- const detected = await detectMime({
219
- buffer: result.buffer,
220
- filePath: result.fileName ?? mediaUrl,
221
- });
222
- if (detected) {
223
- contentType = detected;
224
- }
225
- }
226
- return {
227
- buffer: result.buffer,
228
- contentType,
229
- fileName: extractFileName(mediaUrl, result.fileName, contentType),
230
- };
231
- }
232
-
233
- export async function uploadAndSendBotWsMedia(params: {
234
- wsClient: WSClient;
235
- mediaUrl: string;
236
- chatId: string;
237
- mediaLocalRoots?: readonly string[];
238
- maxBytes?: number;
239
- }): Promise<BotWsMediaSendResult> {
240
- try {
241
- const media = await resolveMediaFile(params.mediaUrl, params.mediaLocalRoots, params.maxBytes);
242
- const detectedType = detectWeComMediaType(media.contentType);
243
- const sizeCheck = applyFileSizeLimits(media.buffer.length, detectedType, media.contentType);
244
- if (sizeCheck.shouldReject) {
245
- return {
246
- ok: false,
247
- rejected: true,
248
- rejectReason: sizeCheck.rejectReason,
249
- finalType: sizeCheck.finalType,
250
- };
251
- }
252
-
253
- const uploadResult = await params.wsClient.uploadMedia(media.buffer, {
254
- type: sizeCheck.finalType,
255
- filename: media.fileName,
256
- });
257
- const sendResult = await params.wsClient.sendMediaMessage(
258
- params.chatId,
259
- sizeCheck.finalType,
260
- uploadResult.media_id,
261
- );
262
-
263
- return {
264
- ok: true,
265
- messageId: sendResult?.headers?.req_id ?? `wecom-media-${Date.now()}`,
266
- finalType: sizeCheck.finalType,
267
- downgraded: sizeCheck.downgraded,
268
- downgradeNote: sizeCheck.downgradeNote,
269
- };
270
- } catch (error) {
271
- return {
272
- ok: false,
273
- error: error instanceof Error ? error.message : String(error),
274
- };
275
- }
276
- }
277
-
278
- export async function uploadAndReplyBotWsMedia(params: {
279
- wsClient: WSClient;
280
- frame: WsFrameHeaders;
281
- mediaUrl: string;
282
- mediaLocalRoots?: readonly string[];
283
- maxBytes?: number;
284
- }): Promise<BotWsMediaSendResult> {
285
- try {
286
- const media = await resolveMediaFile(params.mediaUrl, params.mediaLocalRoots, params.maxBytes);
287
- const detectedType = detectWeComMediaType(media.contentType);
288
- const sizeCheck = applyFileSizeLimits(media.buffer.length, detectedType, media.contentType);
289
- if (sizeCheck.shouldReject) {
290
- return {
291
- ok: false,
292
- rejected: true,
293
- rejectReason: sizeCheck.rejectReason,
294
- finalType: sizeCheck.finalType,
295
- };
296
- }
297
-
298
- const uploadResult = await params.wsClient.uploadMedia(media.buffer, {
299
- type: sizeCheck.finalType,
300
- filename: media.fileName,
301
- });
302
- const replyResult = await params.wsClient.replyMedia(
303
- params.frame,
304
- sizeCheck.finalType,
305
- uploadResult.media_id,
306
- );
307
-
308
- return {
309
- ok: true,
310
- messageId: replyResult?.headers?.req_id ?? `wecom-reply-media-${Date.now()}`,
311
- finalType: sizeCheck.finalType,
312
- downgraded: sizeCheck.downgraded,
313
- downgradeNote: sizeCheck.downgradeNote,
314
- };
315
- } catch (error) {
316
- return {
317
- ok: false,
318
- error: error instanceof Error ? error.message : String(error),
319
- };
320
- }
321
- }