@colinlu50/openclaw-lark-stream 2026.3.17

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 (361) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/README.zh.md +70 -0
  4. package/bin/openclaw-lark.js +48 -0
  5. package/index.d.ts +36 -0
  6. package/index.js +118 -0
  7. package/openclaw.plugin.json +10 -0
  8. package/package.json +66 -0
  9. package/skills/feishu-bitable/SKILL.md +248 -0
  10. package/skills/feishu-bitable/references/examples.md +813 -0
  11. package/skills/feishu-bitable/references/field-properties.md +763 -0
  12. package/skills/feishu-bitable/references/record-values.md +911 -0
  13. package/skills/feishu-calendar/SKILL.md +244 -0
  14. package/skills/feishu-channel-rules/SKILL.md +24 -0
  15. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  16. package/skills/feishu-create-doc/SKILL.md +719 -0
  17. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  18. package/skills/feishu-im-read/SKILL.md +163 -0
  19. package/skills/feishu-task/SKILL.md +293 -0
  20. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  21. package/skills/feishu-update-doc/SKILL.md +285 -0
  22. package/src/card/builder.d.ts +106 -0
  23. package/src/card/builder.js +443 -0
  24. package/src/card/cardkit.d.ts +90 -0
  25. package/src/card/cardkit.js +181 -0
  26. package/src/card/flush-controller.d.ts +45 -0
  27. package/src/card/flush-controller.js +134 -0
  28. package/src/card/image-resolver.d.ts +45 -0
  29. package/src/card/image-resolver.js +112 -0
  30. package/src/card/markdown-style.d.ts +16 -0
  31. package/src/card/markdown-style.js +97 -0
  32. package/src/card/reply-dispatcher-types.d.ts +120 -0
  33. package/src/card/reply-dispatcher-types.js +57 -0
  34. package/src/card/reply-dispatcher.d.ts +15 -0
  35. package/src/card/reply-dispatcher.js +299 -0
  36. package/src/card/reply-mode.d.ts +38 -0
  37. package/src/card/reply-mode.js +65 -0
  38. package/src/card/streaming-card-controller.d.ts +101 -0
  39. package/src/card/streaming-card-controller.js +810 -0
  40. package/src/card/unavailable-guard.d.ts +35 -0
  41. package/src/card/unavailable-guard.js +83 -0
  42. package/src/channel/abort-detect.d.ts +34 -0
  43. package/src/channel/abort-detect.js +124 -0
  44. package/src/channel/chat-queue.d.ts +41 -0
  45. package/src/channel/chat-queue.js +58 -0
  46. package/src/channel/config-adapter.d.ts +23 -0
  47. package/src/channel/config-adapter.js +101 -0
  48. package/src/channel/directory.d.ts +57 -0
  49. package/src/channel/directory.js +191 -0
  50. package/src/channel/event-handlers.d.ts +15 -0
  51. package/src/channel/event-handlers.js +221 -0
  52. package/src/channel/monitor.d.ts +17 -0
  53. package/src/channel/monitor.js +129 -0
  54. package/src/channel/onboarding-config.d.ts +17 -0
  55. package/src/channel/onboarding-config.js +88 -0
  56. package/src/channel/onboarding-migrate.d.ts +25 -0
  57. package/src/channel/onboarding-migrate.js +67 -0
  58. package/src/channel/onboarding.d.ts +12 -0
  59. package/src/channel/onboarding.js +296 -0
  60. package/src/channel/plugin.d.ts +13 -0
  61. package/src/channel/plugin.js +278 -0
  62. package/src/channel/probe.d.ts +14 -0
  63. package/src/channel/probe.js +21 -0
  64. package/src/channel/types.d.ts +36 -0
  65. package/src/channel/types.js +7 -0
  66. package/src/commands/auth.d.ts +21 -0
  67. package/src/commands/auth.js +161 -0
  68. package/src/commands/diagnose.d.ts +69 -0
  69. package/src/commands/diagnose.js +807 -0
  70. package/src/commands/doctor.d.ts +26 -0
  71. package/src/commands/doctor.js +584 -0
  72. package/src/commands/index.d.ts +25 -0
  73. package/src/commands/index.js +212 -0
  74. package/src/commands/locale.d.ts +7 -0
  75. package/src/commands/locale.js +7 -0
  76. package/src/core/accounts.d.ts +37 -0
  77. package/src/core/accounts.js +163 -0
  78. package/src/core/agent-config.d.ts +100 -0
  79. package/src/core/agent-config.js +139 -0
  80. package/src/core/api-error.d.ts +48 -0
  81. package/src/core/api-error.js +112 -0
  82. package/src/core/app-owner-fallback.d.ts +21 -0
  83. package/src/core/app-owner-fallback.js +38 -0
  84. package/src/core/app-scope-checker.d.ts +87 -0
  85. package/src/core/app-scope-checker.js +190 -0
  86. package/src/core/auth-errors.d.ts +144 -0
  87. package/src/core/auth-errors.js +154 -0
  88. package/src/core/chat-info-cache.d.ts +57 -0
  89. package/src/core/chat-info-cache.js +152 -0
  90. package/src/core/config-schema.d.ts +448 -0
  91. package/src/core/config-schema.js +200 -0
  92. package/src/core/device-flow.d.ts +77 -0
  93. package/src/core/device-flow.js +212 -0
  94. package/src/core/domains.d.ts +18 -0
  95. package/src/core/domains.js +28 -0
  96. package/src/core/feishu-fetch.d.ts +18 -0
  97. package/src/core/feishu-fetch.js +25 -0
  98. package/src/core/footer-config.d.ts +24 -0
  99. package/src/core/footer-config.js +39 -0
  100. package/src/core/lark-client.d.ts +108 -0
  101. package/src/core/lark-client.js +353 -0
  102. package/src/core/lark-logger.d.ts +23 -0
  103. package/src/core/lark-logger.js +154 -0
  104. package/src/core/lark-ticket.d.ts +29 -0
  105. package/src/core/lark-ticket.js +35 -0
  106. package/src/core/message-unavailable.d.ts +53 -0
  107. package/src/core/message-unavailable.js +130 -0
  108. package/src/core/owner-policy.d.ts +31 -0
  109. package/src/core/owner-policy.js +52 -0
  110. package/src/core/permission-url.d.ts +22 -0
  111. package/src/core/permission-url.js +72 -0
  112. package/src/core/raw-request.d.ts +27 -0
  113. package/src/core/raw-request.js +62 -0
  114. package/src/core/scope-manager.d.ts +168 -0
  115. package/src/core/scope-manager.js +213 -0
  116. package/src/core/security-check.d.ts +72 -0
  117. package/src/core/security-check.js +174 -0
  118. package/src/core/shutdown-hooks.d.ts +22 -0
  119. package/src/core/shutdown-hooks.js +56 -0
  120. package/src/core/targets.d.ts +60 -0
  121. package/src/core/targets.js +164 -0
  122. package/src/core/token-store.d.ts +54 -0
  123. package/src/core/token-store.js +314 -0
  124. package/src/core/tool-client.d.ts +176 -0
  125. package/src/core/tool-client.js +380 -0
  126. package/src/core/tool-scopes.d.ts +153 -0
  127. package/src/core/tool-scopes.js +326 -0
  128. package/src/core/tools-config.d.ts +55 -0
  129. package/src/core/tools-config.js +137 -0
  130. package/src/core/types.d.ts +87 -0
  131. package/src/core/types.js +11 -0
  132. package/src/core/uat-client.d.ts +46 -0
  133. package/src/core/uat-client.js +187 -0
  134. package/src/core/version.d.ts +25 -0
  135. package/src/core/version.js +49 -0
  136. package/src/messaging/converters/audio.d.ts +8 -0
  137. package/src/messaging/converters/audio.js +21 -0
  138. package/src/messaging/converters/calendar.d.ts +13 -0
  139. package/src/messaging/converters/calendar.js +50 -0
  140. package/src/messaging/converters/content-converter.d.ts +41 -0
  141. package/src/messaging/converters/content-converter.js +106 -0
  142. package/src/messaging/converters/file.d.ts +8 -0
  143. package/src/messaging/converters/file.js +20 -0
  144. package/src/messaging/converters/folder.d.ts +8 -0
  145. package/src/messaging/converters/folder.js +20 -0
  146. package/src/messaging/converters/hongbao.d.ts +8 -0
  147. package/src/messaging/converters/hongbao.js +16 -0
  148. package/src/messaging/converters/image.d.ts +8 -0
  149. package/src/messaging/converters/image.js +18 -0
  150. package/src/messaging/converters/index.d.ts +8 -0
  151. package/src/messaging/converters/index.js +50 -0
  152. package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
  153. package/src/messaging/converters/interactive/card-converter.js +1173 -0
  154. package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
  155. package/src/messaging/converters/interactive/card-utils.js +42 -0
  156. package/src/messaging/converters/interactive/index.d.ts +8 -0
  157. package/src/messaging/converters/interactive/index.js +21 -0
  158. package/src/messaging/converters/interactive/legacy.d.ts +11 -0
  159. package/src/messaging/converters/interactive/legacy.js +57 -0
  160. package/src/messaging/converters/interactive/types.d.ts +23 -0
  161. package/src/messaging/converters/interactive/types.js +24 -0
  162. package/src/messaging/converters/location.d.ts +8 -0
  163. package/src/messaging/converters/location.js +19 -0
  164. package/src/messaging/converters/merge-forward.d.ts +32 -0
  165. package/src/messaging/converters/merge-forward.js +225 -0
  166. package/src/messaging/converters/post.d.ts +11 -0
  167. package/src/messaging/converters/post.js +135 -0
  168. package/src/messaging/converters/share.d.ts +9 -0
  169. package/src/messaging/converters/share.js +23 -0
  170. package/src/messaging/converters/sticker.d.ts +8 -0
  171. package/src/messaging/converters/sticker.js +18 -0
  172. package/src/messaging/converters/system.d.ts +12 -0
  173. package/src/messaging/converters/system.js +32 -0
  174. package/src/messaging/converters/text.d.ts +8 -0
  175. package/src/messaging/converters/text.js +14 -0
  176. package/src/messaging/converters/todo.d.ts +8 -0
  177. package/src/messaging/converters/todo.js +41 -0
  178. package/src/messaging/converters/types.d.ts +107 -0
  179. package/src/messaging/converters/types.js +7 -0
  180. package/src/messaging/converters/unknown.d.ts +8 -0
  181. package/src/messaging/converters/unknown.js +16 -0
  182. package/src/messaging/converters/utils.d.ts +22 -0
  183. package/src/messaging/converters/utils.js +51 -0
  184. package/src/messaging/converters/video-chat.d.ts +8 -0
  185. package/src/messaging/converters/video-chat.js +23 -0
  186. package/src/messaging/converters/video.d.ts +8 -0
  187. package/src/messaging/converters/video.js +32 -0
  188. package/src/messaging/converters/vote.d.ts +8 -0
  189. package/src/messaging/converters/vote.js +24 -0
  190. package/src/messaging/inbound/dedup.d.ts +59 -0
  191. package/src/messaging/inbound/dedup.js +116 -0
  192. package/src/messaging/inbound/dispatch-builders.d.ts +84 -0
  193. package/src/messaging/inbound/dispatch-builders.js +152 -0
  194. package/src/messaging/inbound/dispatch-commands.d.ts +27 -0
  195. package/src/messaging/inbound/dispatch-commands.js +112 -0
  196. package/src/messaging/inbound/dispatch-context.d.ts +67 -0
  197. package/src/messaging/inbound/dispatch-context.js +136 -0
  198. package/src/messaging/inbound/dispatch.d.ts +47 -0
  199. package/src/messaging/inbound/dispatch.js +264 -0
  200. package/src/messaging/inbound/enrich.d.ts +102 -0
  201. package/src/messaging/inbound/enrich.js +227 -0
  202. package/src/messaging/inbound/gate-effects.d.ts +23 -0
  203. package/src/messaging/inbound/gate-effects.js +43 -0
  204. package/src/messaging/inbound/gate.d.ts +60 -0
  205. package/src/messaging/inbound/gate.js +233 -0
  206. package/src/messaging/inbound/handler.d.ts +35 -0
  207. package/src/messaging/inbound/handler.js +173 -0
  208. package/src/messaging/inbound/media-resolver.d.ts +32 -0
  209. package/src/messaging/inbound/media-resolver.js +87 -0
  210. package/src/messaging/inbound/mention.d.ts +39 -0
  211. package/src/messaging/inbound/mention.js +81 -0
  212. package/src/messaging/inbound/parse-io.d.ts +50 -0
  213. package/src/messaging/inbound/parse-io.js +81 -0
  214. package/src/messaging/inbound/parse.d.ts +28 -0
  215. package/src/messaging/inbound/parse.js +106 -0
  216. package/src/messaging/inbound/permission.d.ts +17 -0
  217. package/src/messaging/inbound/permission.js +40 -0
  218. package/src/messaging/inbound/policy.d.ts +94 -0
  219. package/src/messaging/inbound/policy.js +160 -0
  220. package/src/messaging/inbound/reaction-handler.d.ts +61 -0
  221. package/src/messaging/inbound/reaction-handler.js +221 -0
  222. package/src/messaging/inbound/user-name-cache.d.ts +82 -0
  223. package/src/messaging/inbound/user-name-cache.js +241 -0
  224. package/src/messaging/outbound/actions.d.ts +16 -0
  225. package/src/messaging/outbound/actions.js +309 -0
  226. package/src/messaging/outbound/chat-manage.d.ts +64 -0
  227. package/src/messaging/outbound/chat-manage.js +111 -0
  228. package/src/messaging/outbound/deliver.d.ts +155 -0
  229. package/src/messaging/outbound/deliver.js +298 -0
  230. package/src/messaging/outbound/fetch.d.ts +12 -0
  231. package/src/messaging/outbound/fetch.js +12 -0
  232. package/src/messaging/outbound/forward.d.ts +26 -0
  233. package/src/messaging/outbound/forward.js +48 -0
  234. package/src/messaging/outbound/media-url-utils.d.ts +29 -0
  235. package/src/messaging/outbound/media-url-utils.js +130 -0
  236. package/src/messaging/outbound/media.d.ts +260 -0
  237. package/src/messaging/outbound/media.js +758 -0
  238. package/src/messaging/outbound/outbound.d.ts +89 -0
  239. package/src/messaging/outbound/outbound.js +121 -0
  240. package/src/messaging/outbound/reactions.d.ts +124 -0
  241. package/src/messaging/outbound/reactions.js +378 -0
  242. package/src/messaging/outbound/send.d.ts +152 -0
  243. package/src/messaging/outbound/send.js +355 -0
  244. package/src/messaging/outbound/typing.d.ts +71 -0
  245. package/src/messaging/outbound/typing.js +179 -0
  246. package/src/messaging/shared/message-lookup.d.ts +54 -0
  247. package/src/messaging/shared/message-lookup.js +117 -0
  248. package/src/messaging/types.d.ts +176 -0
  249. package/src/messaging/types.js +10 -0
  250. package/src/tools/auto-auth.d.ts +56 -0
  251. package/src/tools/auto-auth.js +919 -0
  252. package/src/tools/helpers.d.ts +260 -0
  253. package/src/tools/helpers.js +364 -0
  254. package/src/tools/mcp/doc/create.d.ts +12 -0
  255. package/src/tools/mcp/doc/create.js +44 -0
  256. package/src/tools/mcp/doc/fetch.d.ts +12 -0
  257. package/src/tools/mcp/doc/fetch.js +36 -0
  258. package/src/tools/mcp/doc/index.d.ts +12 -0
  259. package/src/tools/mcp/doc/index.js +41 -0
  260. package/src/tools/mcp/doc/update.d.ts +12 -0
  261. package/src/tools/mcp/doc/update.js +61 -0
  262. package/src/tools/mcp/shared.d.ts +59 -0
  263. package/src/tools/mcp/shared.js +226 -0
  264. package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
  265. package/src/tools/oapi/bitable/app-table-field.js +222 -0
  266. package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
  267. package/src/tools/oapi/bitable/app-table-record.js +436 -0
  268. package/src/tools/oapi/bitable/app-table-view.d.ts +17 -0
  269. package/src/tools/oapi/bitable/app-table-view.js +195 -0
  270. package/src/tools/oapi/bitable/app-table.d.ts +19 -0
  271. package/src/tools/oapi/bitable/app-table.js +247 -0
  272. package/src/tools/oapi/bitable/app.d.ts +18 -0
  273. package/src/tools/oapi/bitable/app.js +186 -0
  274. package/src/tools/oapi/bitable/index.d.ts +9 -0
  275. package/src/tools/oapi/bitable/index.js +9 -0
  276. package/src/tools/oapi/calendar/calendar.d.ts +15 -0
  277. package/src/tools/oapi/calendar/calendar.js +122 -0
  278. package/src/tools/oapi/calendar/event-attendee.d.ts +16 -0
  279. package/src/tools/oapi/calendar/event-attendee.js +263 -0
  280. package/src/tools/oapi/calendar/event.d.ts +16 -0
  281. package/src/tools/oapi/calendar/event.js +709 -0
  282. package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
  283. package/src/tools/oapi/calendar/freebusy.js +111 -0
  284. package/src/tools/oapi/calendar/index.d.ts +8 -0
  285. package/src/tools/oapi/calendar/index.js +8 -0
  286. package/src/tools/oapi/chat/chat.d.ts +16 -0
  287. package/src/tools/oapi/chat/chat.js +124 -0
  288. package/src/tools/oapi/chat/index.d.ts +10 -0
  289. package/src/tools/oapi/chat/index.js +15 -0
  290. package/src/tools/oapi/chat/members.d.ts +11 -0
  291. package/src/tools/oapi/chat/members.js +81 -0
  292. package/src/tools/oapi/common/get-user.d.ts +12 -0
  293. package/src/tools/oapi/common/get-user.js +106 -0
  294. package/src/tools/oapi/common/index.d.ts +6 -0
  295. package/src/tools/oapi/common/index.js +6 -0
  296. package/src/tools/oapi/common/search-user.d.ts +11 -0
  297. package/src/tools/oapi/common/search-user.js +73 -0
  298. package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
  299. package/src/tools/oapi/drive/doc-comments.js +279 -0
  300. package/src/tools/oapi/drive/doc-media.d.ts +19 -0
  301. package/src/tools/oapi/drive/doc-media.js +335 -0
  302. package/src/tools/oapi/drive/file.d.ts +19 -0
  303. package/src/tools/oapi/drive/file.js +483 -0
  304. package/src/tools/oapi/drive/index.d.ts +12 -0
  305. package/src/tools/oapi/drive/index.js +36 -0
  306. package/src/tools/oapi/helpers.d.ts +182 -0
  307. package/src/tools/oapi/helpers.js +354 -0
  308. package/src/tools/oapi/im/format-messages.d.ts +50 -0
  309. package/src/tools/oapi/im/format-messages.js +165 -0
  310. package/src/tools/oapi/im/index.d.ts +10 -0
  311. package/src/tools/oapi/im/index.js +17 -0
  312. package/src/tools/oapi/im/message-read.d.ts +13 -0
  313. package/src/tools/oapi/im/message-read.js +411 -0
  314. package/src/tools/oapi/im/message.d.ts +16 -0
  315. package/src/tools/oapi/im/message.js +149 -0
  316. package/src/tools/oapi/im/resource.d.ts +13 -0
  317. package/src/tools/oapi/im/resource.js +150 -0
  318. package/src/tools/oapi/im/time-utils.d.ts +46 -0
  319. package/src/tools/oapi/im/time-utils.js +201 -0
  320. package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
  321. package/src/tools/oapi/im/user-name-uat.js +140 -0
  322. package/src/tools/oapi/index.d.ts +11 -0
  323. package/src/tools/oapi/index.js +58 -0
  324. package/src/tools/oapi/sdk-types.d.ts +96 -0
  325. package/src/tools/oapi/sdk-types.js +12 -0
  326. package/src/tools/oapi/search/doc-search.d.ts +13 -0
  327. package/src/tools/oapi/search/doc-search.js +191 -0
  328. package/src/tools/oapi/search/index.d.ts +12 -0
  329. package/src/tools/oapi/search/index.js +33 -0
  330. package/src/tools/oapi/sheets/index.d.ts +12 -0
  331. package/src/tools/oapi/sheets/index.js +31 -0
  332. package/src/tools/oapi/sheets/sheet.d.ts +16 -0
  333. package/src/tools/oapi/sheets/sheet.js +652 -0
  334. package/src/tools/oapi/task/comment.d.ts +15 -0
  335. package/src/tools/oapi/task/comment.js +140 -0
  336. package/src/tools/oapi/task/index.d.ts +8 -0
  337. package/src/tools/oapi/task/index.js +8 -0
  338. package/src/tools/oapi/task/subtask.d.ts +14 -0
  339. package/src/tools/oapi/task/subtask.js +162 -0
  340. package/src/tools/oapi/task/task.d.ts +16 -0
  341. package/src/tools/oapi/task/task.js +344 -0
  342. package/src/tools/oapi/task/tasklist.d.ts +21 -0
  343. package/src/tools/oapi/task/tasklist.js +321 -0
  344. package/src/tools/oapi/wiki/index.d.ts +12 -0
  345. package/src/tools/oapi/wiki/index.js +34 -0
  346. package/src/tools/oapi/wiki/space-node.d.ts +17 -0
  347. package/src/tools/oapi/wiki/space-node.js +230 -0
  348. package/src/tools/oapi/wiki/space.d.ts +15 -0
  349. package/src/tools/oapi/wiki/space.js +130 -0
  350. package/src/tools/oauth-batch-auth.d.ts +11 -0
  351. package/src/tools/oauth-batch-auth.js +142 -0
  352. package/src/tools/oauth-cards.d.ts +39 -0
  353. package/src/tools/oauth-cards.js +315 -0
  354. package/src/tools/oauth.d.ts +47 -0
  355. package/src/tools/oauth.js +620 -0
  356. package/src/tools/onboarding-auth.d.ts +27 -0
  357. package/src/tools/onboarding-auth.js +130 -0
  358. package/src/tools/tat/im/index.d.ts +15 -0
  359. package/src/tools/tat/im/index.js +18 -0
  360. package/src/tools/tat/im/resource.d.ts +15 -0
  361. package/src/tools/tat/im/resource.js +157 -0
@@ -0,0 +1,1173 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { EMOJI_MAP, CHART_TYPE_NAMES } from './types';
6
+ import { escapeAttr, formatMillisecondsToISO8601, normalizeTimeFormat } from './card-utils';
7
+ import { safeParse } from '../utils';
8
+ export const MODE = { Concise: 0, Detailed: 1 };
9
+ const elementConverters = new Map([
10
+ ['plain_text', (c, _elem, prop) => c.convertPlainText(prop)],
11
+ ['markdown', (c, _elem, prop) => c.convertMarkdown(prop)],
12
+ ['markdown_v1', (c, elem, prop) => c.convertMarkdownV1(elem, prop)],
13
+ ['text', (c, _elem, prop) => c.convertPlainText(prop)],
14
+ ['div', (c, _elem, prop, id) => c.convertDiv(prop, id)],
15
+ ['note', (c, _elem, prop) => c.convertNote(prop)],
16
+ ['hr', () => '---'],
17
+ ['br', () => '\n'],
18
+ ['column_set', (c, _elem, prop, _id, depth) => c.convertColumnSet(prop, depth)],
19
+ ['column', (c, _elem, prop, _id, depth) => c.convertColumn(prop, depth)],
20
+ ['person', (c, _elem, prop, id) => c.convertPerson(prop, id)],
21
+ ['person_v1', (c, _elem, prop, id) => c.convertPersonV1(prop, id)],
22
+ ['person_list', (c, _elem, prop) => c.convertPersonList(prop)],
23
+ ['avatar', (c, _elem, prop, id) => c.convertAvatar(prop, id)],
24
+ ['at', (c, _elem, prop) => c.convertAt(prop)],
25
+ ['at_all', () => '@所有人'],
26
+ ['button', (c, _elem, prop, id) => c.convertButton(prop, id)],
27
+ ['actions', (c, _elem, prop) => c.convertActions(prop)],
28
+ ['action', (c, _elem, prop) => c.convertActions(prop)],
29
+ ['overflow', (c, _elem, prop) => c.convertOverflow(prop)],
30
+ ['select_static', (c, _elem, prop, id) => c.convertSelect(prop, id, false)],
31
+ ['multi_select_static', (c, _elem, prop, id) => c.convertSelect(prop, id, true)],
32
+ ['select_person', (c, _elem, prop, id) => c.convertSelect(prop, id, false)],
33
+ ['multi_select_person', (c, _elem, prop, id) => c.convertSelect(prop, id, true)],
34
+ ['select_img', (c, _elem, prop, id) => c.convertSelectImg(prop, id)],
35
+ ['input', (c, _elem, prop, id) => c.convertInput(prop, id)],
36
+ ['date_picker', (c, _elem, prop, id) => c.convertDatePicker(prop, id, 'date')],
37
+ ['picker_time', (c, _elem, prop, id) => c.convertDatePicker(prop, id, 'time')],
38
+ ['picker_datetime', (c, _elem, prop, id) => c.convertDatePicker(prop, id, 'datetime')],
39
+ ['checker', (c, _elem, prop, id) => c.convertChecker(prop, id)],
40
+ ['img', (c, _elem, prop, id) => c.convertImage(prop, id)],
41
+ ['image', (c, _elem, prop, id) => c.convertImage(prop, id)],
42
+ ['img_combination', (c, _elem, prop) => c.convertImgCombination(prop)],
43
+ ['table', (c, _elem, prop) => c.convertTable(prop)],
44
+ ['chart', (c, _elem, prop, id) => c.convertChart(prop, id)],
45
+ ['audio', (c, _elem, prop, id) => c.convertAudio(prop, id)],
46
+ ['video', (c, _elem, prop, id) => c.convertVideo(prop, id)],
47
+ ['collapsible_panel', (c, _elem, prop, id) => c.convertCollapsiblePanel(prop, id)],
48
+ ['form', (c, _elem, prop, id) => c.convertForm(prop, id)],
49
+ ['interactive_container', (c, _elem, prop, id) => c.convertInteractiveContainer(prop, id)],
50
+ ['text_tag', (c, _elem, prop) => c.convertTextTag(prop)],
51
+ ['number_tag', (c, _elem, prop) => c.convertNumberTag(prop)],
52
+ ['link', (c, _elem, prop) => c.convertLink(prop)],
53
+ ['emoji', (c, _elem, prop) => c.convertEmoji(prop)],
54
+ ['local_datetime', (c, _elem, prop) => c.convertLocalDatetime(prop)],
55
+ ['list', (c, _elem, prop) => c.convertList(prop)],
56
+ ['blockquote', (c, _elem, prop) => c.convertBlockquote(prop)],
57
+ ['code_block', (c, _elem, prop) => c.convertCodeBlock(prop)],
58
+ ['code_span', (c, _elem, prop) => c.convertCodeSpan(prop)],
59
+ ['heading', (c, _elem, prop) => c.convertHeading(prop)],
60
+ ['fallback_text', (c, _elem, prop) => c.convertFallbackText(prop)],
61
+ ['repeat', (c, _elem, prop) => c.convertRepeat(prop)],
62
+ ['card_header', () => ''],
63
+ ['custom_icon', () => ''],
64
+ ['standard_icon', () => ''],
65
+ ]);
66
+ export class CardConverter {
67
+ mode;
68
+ attachment;
69
+ constructor(mode) {
70
+ this.mode = mode;
71
+ }
72
+ convert(input) {
73
+ const card = safeParse(input.json_card);
74
+ if (!card) {
75
+ return { content: '<card>\n[无法解析卡片内容]\n</card>', schema: 0 };
76
+ }
77
+ if (input.json_attachment) {
78
+ this.attachment = safeParse(input.json_attachment);
79
+ }
80
+ let schema = input.card_schema ?? 0;
81
+ if (schema === 0) {
82
+ const s = card.schema;
83
+ schema = typeof s === 'number' ? s : 1;
84
+ }
85
+ const header = card.header;
86
+ const title = header ? this.extractHeaderTitle(header, schema) : '';
87
+ const body = this.extractBody(card, schema);
88
+ const bodyContent = body ? this.convertBody(body, schema) : '';
89
+ let out = title ? `<card title="${escapeAttr(title)}">\n` : '<card>\n';
90
+ if (bodyContent)
91
+ out += bodyContent + '\n';
92
+ out += '</card>';
93
+ return { content: out, schema };
94
+ }
95
+ extractBody(card, _schema) {
96
+ if (card.body && typeof card.body === 'object') {
97
+ return card.body;
98
+ }
99
+ return undefined;
100
+ }
101
+ extractHeaderTitle(header, _schema) {
102
+ const prop = header.property;
103
+ if (prop) {
104
+ const titleElem = prop.title;
105
+ if (titleElem)
106
+ return this.extractTextContent(titleElem);
107
+ }
108
+ else {
109
+ const titleElem = header.title;
110
+ if (titleElem)
111
+ return this.extractTextContent(titleElem);
112
+ }
113
+ return '';
114
+ }
115
+ convertBody(body, _schema) {
116
+ let elements;
117
+ const prop = body.property;
118
+ if (prop) {
119
+ const e = prop.elements;
120
+ if (Array.isArray(e) && e.length > 0)
121
+ elements = e;
122
+ }
123
+ if (!elements || elements.length === 0) {
124
+ const e = body.elements;
125
+ if (Array.isArray(e))
126
+ elements = e;
127
+ }
128
+ if (!elements || elements.length === 0)
129
+ return '';
130
+ return this.convertElements(elements, 0);
131
+ }
132
+ convertElements(elements, depth) {
133
+ const results = [];
134
+ for (const elem of elements) {
135
+ if (typeof elem !== 'object' || elem === null)
136
+ continue;
137
+ const result = this.convertElement(elem, depth);
138
+ if (result)
139
+ results.push(result);
140
+ }
141
+ return results.join('\n');
142
+ }
143
+ convertElement(elem, depth) {
144
+ const tag = elem.tag ?? '';
145
+ const id = elem.id ?? '';
146
+ const prop = this.extractProperty(elem);
147
+ const fn = elementConverters.get(tag);
148
+ if (fn)
149
+ return fn(this, elem, prop, id, depth);
150
+ return this.convertUnknown(prop, tag);
151
+ }
152
+ extractProperty(elem) {
153
+ if (elem.property && typeof elem.property === 'object') {
154
+ return elem.property;
155
+ }
156
+ return elem;
157
+ }
158
+ extractTextContent(textElem) {
159
+ if (textElem == null)
160
+ return '';
161
+ if (typeof textElem === 'string')
162
+ return textElem;
163
+ if (typeof textElem === 'object') {
164
+ const m = textElem;
165
+ if (m.property && typeof m.property === 'object') {
166
+ return this.extractTextFromProperty(m.property);
167
+ }
168
+ return this.extractTextFromProperty(m);
169
+ }
170
+ return '';
171
+ }
172
+ extractTextFromProperty(prop) {
173
+ const i18n = prop.i18nContent;
174
+ if (i18n && typeof i18n === 'object') {
175
+ for (const lang of ['zh_cn', 'en_us', 'ja_jp']) {
176
+ const t = i18n[lang];
177
+ if (typeof t === 'string' && t)
178
+ return t;
179
+ }
180
+ }
181
+ if (typeof prop.content === 'string')
182
+ return prop.content;
183
+ const elements = prop.elements;
184
+ if (Array.isArray(elements) && elements.length > 0) {
185
+ const texts = [];
186
+ for (const elem of elements) {
187
+ if (typeof elem === 'object' && elem !== null) {
188
+ const t = this.extractTextContent(elem);
189
+ if (t)
190
+ texts.push(t);
191
+ }
192
+ }
193
+ return texts.join('');
194
+ }
195
+ if (typeof prop.text === 'string')
196
+ return prop.text;
197
+ return '';
198
+ }
199
+ convertPlainText(prop) {
200
+ const content = prop.content;
201
+ if (!content)
202
+ return '';
203
+ const style = this.extractTextStyle(prop);
204
+ return this.applyTextStyle(content, style);
205
+ }
206
+ convertMarkdown(prop) {
207
+ const elements = prop.elements;
208
+ if (Array.isArray(elements) && elements.length > 0) {
209
+ return this.convertMarkdownElements(elements);
210
+ }
211
+ if (typeof prop.content === 'string')
212
+ return prop.content;
213
+ return '';
214
+ }
215
+ convertMarkdownV1(elem, prop) {
216
+ const elements = prop.elements;
217
+ if (Array.isArray(elements) && elements.length > 0) {
218
+ return this.convertMarkdownElements(elements);
219
+ }
220
+ const fallback = elem.fallback;
221
+ if (fallback && typeof fallback === 'object') {
222
+ return this.convertElement(fallback, 0);
223
+ }
224
+ if (typeof prop.content === 'string')
225
+ return prop.content;
226
+ return '';
227
+ }
228
+ convertMarkdownElements(elements) {
229
+ const parts = [];
230
+ for (const elem of elements) {
231
+ if (typeof elem !== 'object' || elem === null)
232
+ continue;
233
+ const result = this.convertElement(elem, 0);
234
+ if (result)
235
+ parts.push(result);
236
+ }
237
+ return parts.join('');
238
+ }
239
+ convertDiv(prop, _id) {
240
+ const results = [];
241
+ const textElem = prop.text;
242
+ if (textElem && typeof textElem === 'object') {
243
+ const text = this.convertElement(textElem, 0);
244
+ if (text)
245
+ results.push(text);
246
+ }
247
+ const fields = prop.fields;
248
+ if (Array.isArray(fields) && fields.length > 0) {
249
+ const fieldTexts = [];
250
+ for (const field of fields) {
251
+ if (typeof field !== 'object' || field === null)
252
+ continue;
253
+ const fm = field;
254
+ const te = fm.text;
255
+ if (te && typeof te === 'object') {
256
+ const ft = this.convertElement(te, 0);
257
+ if (ft)
258
+ fieldTexts.push(ft);
259
+ }
260
+ }
261
+ if (fieldTexts.length > 0)
262
+ results.push(fieldTexts.join('\n'));
263
+ }
264
+ const extraElem = prop.extra;
265
+ if (extraElem && typeof extraElem === 'object') {
266
+ const extra = this.convertElement(extraElem, 0);
267
+ if (extra)
268
+ results.push(extra);
269
+ }
270
+ return results.join('\n');
271
+ }
272
+ convertNote(prop) {
273
+ const elements = prop.elements;
274
+ if (!Array.isArray(elements) || elements.length === 0)
275
+ return '';
276
+ const texts = [];
277
+ for (const elem of elements) {
278
+ if (typeof elem !== 'object' || elem === null)
279
+ continue;
280
+ const text = this.convertElement(elem, 0);
281
+ if (text)
282
+ texts.push(text);
283
+ }
284
+ if (texts.length === 0)
285
+ return '';
286
+ return `📝 ${texts.join(' ')}`;
287
+ }
288
+ convertLink(prop) {
289
+ const content = prop.content || '链接';
290
+ let url = '';
291
+ const urlObj = prop.url;
292
+ if (urlObj && typeof urlObj === 'object') {
293
+ url = urlObj.url || '';
294
+ }
295
+ if (url)
296
+ return `[${content}](${url})`;
297
+ return content;
298
+ }
299
+ convertEmoji(prop) {
300
+ const key = prop.key || '';
301
+ return EMOJI_MAP[key] ?? `:${key}:`;
302
+ }
303
+ convertLocalDatetime(prop) {
304
+ const milliseconds = prop.milliseconds;
305
+ const fallbackText = prop.fallbackText;
306
+ if (milliseconds) {
307
+ const formatted = formatMillisecondsToISO8601(milliseconds);
308
+ if (formatted)
309
+ return formatted;
310
+ }
311
+ return fallbackText || '';
312
+ }
313
+ convertList(prop) {
314
+ const items = prop.items;
315
+ if (!Array.isArray(items) || items.length === 0)
316
+ return '';
317
+ const lines = [];
318
+ for (const item of items) {
319
+ if (typeof item !== 'object' || item === null)
320
+ continue;
321
+ const im = item;
322
+ const level = im.level || 0;
323
+ const listType = im.type || '';
324
+ const order = im.order || 0;
325
+ const indent = ' '.repeat(level);
326
+ const marker = listType === 'ol' ? `${Math.floor(order)}.` : '-';
327
+ const elements = im.elements;
328
+ if (Array.isArray(elements)) {
329
+ const content = this.convertMarkdownElements(elements);
330
+ lines.push(`${indent}${marker} ${content}`);
331
+ }
332
+ }
333
+ return lines.join('\n');
334
+ }
335
+ convertBlockquote(prop) {
336
+ let content = '';
337
+ if (typeof prop.content === 'string') {
338
+ content = prop.content;
339
+ }
340
+ else {
341
+ const elements = prop.elements;
342
+ if (Array.isArray(elements)) {
343
+ content = this.convertMarkdownElements(elements);
344
+ }
345
+ }
346
+ if (!content)
347
+ return '';
348
+ return content
349
+ .split('\n')
350
+ .map((line) => `> ${line}`)
351
+ .join('\n');
352
+ }
353
+ convertCodeBlock(prop) {
354
+ const language = prop.language || 'plaintext';
355
+ let code = '';
356
+ const contents = prop.contents;
357
+ if (Array.isArray(contents)) {
358
+ for (const line of contents) {
359
+ if (typeof line !== 'object' || line === null)
360
+ continue;
361
+ const lm = line;
362
+ const lineContents = lm.contents;
363
+ if (Array.isArray(lineContents)) {
364
+ for (const c of lineContents) {
365
+ if (typeof c !== 'object' || c === null)
366
+ continue;
367
+ const cm = c;
368
+ if (typeof cm.content === 'string')
369
+ code += cm.content;
370
+ }
371
+ }
372
+ }
373
+ }
374
+ return `\`\`\`${language}\n${code}\`\`\``;
375
+ }
376
+ convertCodeSpan(prop) {
377
+ const content = prop.content || '';
378
+ return `\`${content}\``;
379
+ }
380
+ convertHeading(prop) {
381
+ let level = prop.level || 1;
382
+ if (level < 1)
383
+ level = 1;
384
+ if (level > 6)
385
+ level = 6;
386
+ let content = '';
387
+ if (typeof prop.content === 'string') {
388
+ content = prop.content;
389
+ }
390
+ else {
391
+ const elements = prop.elements;
392
+ if (Array.isArray(elements)) {
393
+ content = this.convertMarkdownElements(elements);
394
+ }
395
+ }
396
+ return `${'#'.repeat(level)} ${content}`;
397
+ }
398
+ convertFallbackText(prop) {
399
+ const textElem = prop.text;
400
+ if (textElem && typeof textElem === 'object') {
401
+ return this.extractTextContent(textElem);
402
+ }
403
+ const elements = prop.elements;
404
+ if (Array.isArray(elements)) {
405
+ return this.convertMarkdownElements(elements);
406
+ }
407
+ return '';
408
+ }
409
+ convertTextTag(prop) {
410
+ const textElem = prop.text;
411
+ let text = '';
412
+ if (textElem && typeof textElem === 'object') {
413
+ text = this.extractTextContent(textElem);
414
+ }
415
+ if (!text)
416
+ return '';
417
+ return `「${text}」`;
418
+ }
419
+ convertNumberTag(prop) {
420
+ const textElem = prop.text;
421
+ let text = '';
422
+ if (textElem && typeof textElem === 'object') {
423
+ text = this.extractTextContent(textElem);
424
+ }
425
+ if (!text)
426
+ return '';
427
+ const urlObj = prop.url;
428
+ if (urlObj && typeof urlObj === 'object') {
429
+ const url = urlObj.url;
430
+ if (url)
431
+ return `[${text}](${url})`;
432
+ }
433
+ return text;
434
+ }
435
+ convertUnknown(prop, tag) {
436
+ if (!prop) {
437
+ if (this.mode === MODE.Detailed)
438
+ return `[未知内容](tag:${tag})`;
439
+ return '[未知内容]';
440
+ }
441
+ const paths = ['content', 'text', 'title', 'label', 'placeholder'];
442
+ for (const path of paths) {
443
+ if (prop[path] != null) {
444
+ const text = this.extractTextContent(prop[path]);
445
+ if (text)
446
+ return text;
447
+ }
448
+ }
449
+ const elements = prop.elements;
450
+ if (Array.isArray(elements) && elements.length > 0) {
451
+ return this.convertElements(elements, 0);
452
+ }
453
+ if (this.mode === MODE.Detailed)
454
+ return `[未知内容](tag:${tag})`;
455
+ return '[未知内容]';
456
+ }
457
+ convertColumnSet(prop, depth) {
458
+ const columns = prop.columns;
459
+ if (!Array.isArray(columns) || columns.length === 0)
460
+ return '';
461
+ const results = [];
462
+ for (const col of columns) {
463
+ if (typeof col !== 'object' || col === null)
464
+ continue;
465
+ const result = this.convertElement(col, depth + 1);
466
+ if (result)
467
+ results.push(result);
468
+ }
469
+ return results.join('\n\n');
470
+ }
471
+ convertColumn(prop, depth) {
472
+ const elements = prop.elements;
473
+ if (!Array.isArray(elements) || elements.length === 0)
474
+ return '';
475
+ return this.convertElements(elements, depth);
476
+ }
477
+ convertForm(prop, _id) {
478
+ let out = '<form>\n';
479
+ const elements = prop.elements;
480
+ if (Array.isArray(elements)) {
481
+ out += this.convertElements(elements, 0);
482
+ }
483
+ out += '\n</form>';
484
+ return out;
485
+ }
486
+ convertCollapsiblePanel(prop, _id) {
487
+ const expanded = prop.expanded === true;
488
+ let title = '详情';
489
+ const header = prop.header;
490
+ if (header && typeof header === 'object') {
491
+ const titleElem = header.title;
492
+ if (titleElem) {
493
+ const t = this.extractTextContent(titleElem);
494
+ if (t)
495
+ title = t;
496
+ }
497
+ }
498
+ const shouldExpand = expanded || this.mode === MODE.Detailed;
499
+ if (shouldExpand) {
500
+ let out = `▼ ${title}\n`;
501
+ const elements = prop.elements;
502
+ if (Array.isArray(elements)) {
503
+ const content = this.convertElements(elements, 1);
504
+ for (const line of content.split('\n')) {
505
+ if (line)
506
+ out += ` ${line}\n`;
507
+ }
508
+ }
509
+ out += '▲';
510
+ return out;
511
+ }
512
+ return `▶ ${title}`;
513
+ }
514
+ convertInteractiveContainer(prop, _id) {
515
+ let url = '';
516
+ const actions = prop.actions;
517
+ if (Array.isArray(actions) && actions.length > 0) {
518
+ const action = actions[0];
519
+ if (action && typeof action === 'object') {
520
+ const actionType = action.type;
521
+ if (actionType === 'open_url') {
522
+ const actionData = action.action;
523
+ if (actionData && typeof actionData === 'object') {
524
+ url = actionData.url || '';
525
+ }
526
+ }
527
+ }
528
+ }
529
+ let out = '<clickable';
530
+ if (url)
531
+ out += ` url="${escapeAttr(url)}"`;
532
+ if (this.mode === MODE.Detailed && _id)
533
+ out += ` id="${_id}"`;
534
+ out += '>\n';
535
+ const elements = prop.elements;
536
+ if (Array.isArray(elements)) {
537
+ out += this.convertElements(elements, 0);
538
+ }
539
+ out += '\n</clickable>';
540
+ return out;
541
+ }
542
+ convertRepeat(prop) {
543
+ const elements = prop.elements;
544
+ if (Array.isArray(elements)) {
545
+ return this.convertElements(elements, 0);
546
+ }
547
+ return '';
548
+ }
549
+ convertButton(prop, _id) {
550
+ let buttonText = '';
551
+ const textElem = prop.text;
552
+ if (textElem && typeof textElem === 'object') {
553
+ buttonText = this.extractTextContent(textElem);
554
+ }
555
+ if (!buttonText)
556
+ buttonText = '按钮';
557
+ const disabled = prop.disabled === true;
558
+ if (disabled && this.mode === MODE.Concise) {
559
+ return `[${buttonText} ✗]`;
560
+ }
561
+ const actions = prop.actions;
562
+ if (Array.isArray(actions)) {
563
+ for (const action of actions) {
564
+ if (typeof action !== 'object' || action === null)
565
+ continue;
566
+ const am = action;
567
+ if (am.type === 'open_url') {
568
+ const ad = am.action;
569
+ if (ad && typeof ad === 'object') {
570
+ const url = ad.url;
571
+ if (url)
572
+ return `[${buttonText}](${url})`;
573
+ }
574
+ }
575
+ }
576
+ }
577
+ if (disabled && this.mode === MODE.Detailed) {
578
+ let result = `[${buttonText} ✗]`;
579
+ const tips = prop.disabledTips;
580
+ if (tips && typeof tips === 'object') {
581
+ const tipsText = this.extractTextContent(tips);
582
+ if (tipsText)
583
+ result += `(tips:"${tipsText}")`;
584
+ }
585
+ return result;
586
+ }
587
+ return `[${buttonText}]`;
588
+ }
589
+ convertActions(prop) {
590
+ const actions = prop.actions;
591
+ if (!Array.isArray(actions) || actions.length === 0)
592
+ return '';
593
+ const results = [];
594
+ for (const action of actions) {
595
+ if (typeof action !== 'object' || action === null)
596
+ continue;
597
+ const result = this.convertElement(action, 0);
598
+ if (result)
599
+ results.push(result);
600
+ }
601
+ return results.join(' ');
602
+ }
603
+ convertSelect(prop, _id, isMulti) {
604
+ const options = prop.options || [];
605
+ const selectedValues = new Set();
606
+ if (isMulti) {
607
+ const vals = prop.selectedValues;
608
+ if (Array.isArray(vals)) {
609
+ for (const v of vals) {
610
+ if (typeof v === 'string')
611
+ selectedValues.add(v);
612
+ }
613
+ }
614
+ }
615
+ else {
616
+ const initialOption = prop.initialOption;
617
+ if (typeof initialOption === 'string')
618
+ selectedValues.add(initialOption);
619
+ const initialIndex = prop.initialIndex;
620
+ if (typeof initialIndex === 'number' && initialIndex >= 0 && initialIndex < options.length) {
621
+ const opt = options[initialIndex];
622
+ if (opt && typeof opt === 'object') {
623
+ const val = opt.value;
624
+ if (val)
625
+ selectedValues.add(val);
626
+ }
627
+ }
628
+ }
629
+ const optionTexts = [];
630
+ let hasSelected = false;
631
+ for (const opt of options) {
632
+ if (typeof opt !== 'object' || opt === null)
633
+ continue;
634
+ const om = opt;
635
+ let optText = '';
636
+ const textElem = om.text;
637
+ if (textElem && typeof textElem === 'object') {
638
+ optText = this.extractTextContent(textElem);
639
+ }
640
+ if (!optText)
641
+ optText = om.value || '';
642
+ if (!optText)
643
+ continue;
644
+ const value = om.value || '';
645
+ if (selectedValues.has(value)) {
646
+ optText = '✓' + optText;
647
+ hasSelected = true;
648
+ }
649
+ optionTexts.push(optText);
650
+ }
651
+ if (optionTexts.length === 0) {
652
+ let placeholder = '请选择';
653
+ const phElem = prop.placeholder;
654
+ if (phElem && typeof phElem === 'object') {
655
+ const ph = this.extractTextContent(phElem);
656
+ if (ph)
657
+ placeholder = ph;
658
+ }
659
+ optionTexts.push(placeholder + ' ▼');
660
+ }
661
+ else if (!hasSelected) {
662
+ optionTexts[optionTexts.length - 1] += ' ▼';
663
+ }
664
+ let result = `{${optionTexts.join(' / ')}}`;
665
+ if (this.mode === MODE.Detailed) {
666
+ const attrs = [];
667
+ if (isMulti)
668
+ attrs.push('multi');
669
+ if (_id.includes('person') || prop.type === 'person')
670
+ attrs.push('type:person');
671
+ if (attrs.length > 0)
672
+ result += `(${attrs.join(' ')})`;
673
+ }
674
+ return result;
675
+ }
676
+ convertSelectImg(prop, _id) {
677
+ const options = prop.options;
678
+ if (!Array.isArray(options))
679
+ return '';
680
+ const selectedValues = new Set();
681
+ const vals = prop.selectedValues;
682
+ if (Array.isArray(vals)) {
683
+ for (const v of vals) {
684
+ if (typeof v === 'string')
685
+ selectedValues.add(v);
686
+ }
687
+ }
688
+ const optTexts = [];
689
+ for (let i = 0; i < options.length; i++) {
690
+ const opt = options[i];
691
+ if (!opt || typeof opt !== 'object')
692
+ continue;
693
+ const value = opt.value || '';
694
+ let text = `🖼️图${i + 1}`;
695
+ if (selectedValues.has(value))
696
+ text = '✓' + text;
697
+ optTexts.push(text);
698
+ }
699
+ return `{${optTexts.join(' / ')}}`;
700
+ }
701
+ convertInput(prop, _id) {
702
+ let label = '';
703
+ const labelElem = prop.label;
704
+ if (labelElem && typeof labelElem === 'object') {
705
+ label = this.extractTextContent(labelElem);
706
+ }
707
+ const defaultValue = prop.defaultValue || '';
708
+ let placeholder = '';
709
+ const phElem = prop.placeholder;
710
+ if (phElem && typeof phElem === 'object') {
711
+ placeholder = this.extractTextContent(phElem);
712
+ }
713
+ let result;
714
+ if (defaultValue) {
715
+ result = defaultValue + '___';
716
+ }
717
+ else if (placeholder) {
718
+ result = placeholder + '_____';
719
+ }
720
+ else {
721
+ result = '_____';
722
+ }
723
+ if (label)
724
+ result = label + ': ' + result;
725
+ const inputType = prop.inputType;
726
+ if (inputType === 'multiline_text') {
727
+ result = result.replace(/_____/g, '...');
728
+ }
729
+ return result;
730
+ }
731
+ convertDatePicker(prop, _id, pickerType) {
732
+ let emoji;
733
+ let value = '';
734
+ switch (pickerType) {
735
+ case 'date':
736
+ emoji = '📅';
737
+ value = prop.initialDate || '';
738
+ break;
739
+ case 'time':
740
+ emoji = '🕐';
741
+ value = prop.initialTime || '';
742
+ break;
743
+ case 'datetime':
744
+ emoji = '📅';
745
+ value = prop.initialDatetime || '';
746
+ break;
747
+ default:
748
+ emoji = '📅';
749
+ }
750
+ if (value)
751
+ value = normalizeTimeFormat(value);
752
+ if (!value) {
753
+ let placeholder = '选择';
754
+ const phElem = prop.placeholder;
755
+ if (phElem && typeof phElem === 'object') {
756
+ const ph = this.extractTextContent(phElem);
757
+ if (ph)
758
+ placeholder = ph;
759
+ }
760
+ value = placeholder;
761
+ }
762
+ return `${emoji} ${value}`;
763
+ }
764
+ convertChecker(prop, _id) {
765
+ const checked = prop.checked === true;
766
+ const checkMark = checked ? '[x]' : '[ ]';
767
+ let text = '';
768
+ const textElem = prop.text;
769
+ if (textElem && typeof textElem === 'object') {
770
+ text = this.extractTextContent(textElem);
771
+ }
772
+ let result = `${checkMark} ${text}`;
773
+ if (this.mode === MODE.Detailed && _id) {
774
+ result += `(id:${_id})`;
775
+ }
776
+ return result;
777
+ }
778
+ convertOverflow(prop) {
779
+ const options = prop.options;
780
+ if (!Array.isArray(options) || options.length === 0)
781
+ return '';
782
+ const optTexts = [];
783
+ for (const opt of options) {
784
+ if (typeof opt !== 'object' || opt === null)
785
+ continue;
786
+ const om = opt;
787
+ const textElem = om.text;
788
+ if (textElem && typeof textElem === 'object') {
789
+ const text = this.extractTextContent(textElem);
790
+ if (text)
791
+ optTexts.push(text);
792
+ }
793
+ }
794
+ return `⋮ ${optTexts.join(', ')}`;
795
+ }
796
+ convertPerson(prop, _id) {
797
+ const userID = prop.userID || '';
798
+ if (!userID)
799
+ return '';
800
+ let personName = '';
801
+ if (this.attachment) {
802
+ const persons = this.attachment.persons;
803
+ if (persons && typeof persons === 'object') {
804
+ const person = persons[userID];
805
+ if (person && typeof person === 'object') {
806
+ const content = person.content;
807
+ if (content)
808
+ personName = content;
809
+ }
810
+ }
811
+ }
812
+ if (!personName) {
813
+ const notation = prop.notation;
814
+ if (notation && typeof notation === 'object') {
815
+ personName = this.extractTextContent(notation);
816
+ }
817
+ }
818
+ if (personName) {
819
+ if (this.mode === MODE.Detailed)
820
+ return `@${personName}(open_id:${userID})`;
821
+ return `@${personName}`;
822
+ }
823
+ if (this.mode === MODE.Detailed)
824
+ return `@用户(open_id:${userID})`;
825
+ return `@${userID}`;
826
+ }
827
+ convertPersonV1(prop, _id) {
828
+ const userID = prop.userID || '';
829
+ if (!userID)
830
+ return '';
831
+ let personName = '';
832
+ if (this.attachment) {
833
+ const persons = this.attachment.persons;
834
+ if (persons && typeof persons === 'object') {
835
+ const person = persons[userID];
836
+ if (person && typeof person === 'object') {
837
+ const content = person.content;
838
+ if (content)
839
+ personName = content;
840
+ }
841
+ }
842
+ }
843
+ if (personName) {
844
+ if (this.mode === MODE.Detailed)
845
+ return `@${personName}(open_id:${userID})`;
846
+ return `@${personName}`;
847
+ }
848
+ if (this.mode === MODE.Detailed)
849
+ return `@用户(open_id:${userID})`;
850
+ return `@${userID}`;
851
+ }
852
+ convertPersonList(prop) {
853
+ const persons = prop.persons;
854
+ if (!Array.isArray(persons) || persons.length === 0)
855
+ return '';
856
+ const names = [];
857
+ for (const person of persons) {
858
+ if (typeof person !== 'object' || person === null)
859
+ continue;
860
+ const pm = person;
861
+ const personID = pm.id || '';
862
+ const name = '用户';
863
+ if (this.mode === MODE.Detailed && personID) {
864
+ names.push(`@${name}(id:${personID})`);
865
+ }
866
+ else {
867
+ names.push(`@${name}`);
868
+ }
869
+ }
870
+ return names.join(', ');
871
+ }
872
+ convertAvatar(prop, _id) {
873
+ const userID = prop.userID || '';
874
+ let result = '👤';
875
+ if (this.mode === MODE.Detailed && userID) {
876
+ result += `(id:${userID})`;
877
+ }
878
+ return result;
879
+ }
880
+ convertAt(prop) {
881
+ const userID = prop.userID || '';
882
+ if (!userID)
883
+ return '';
884
+ let userName = '';
885
+ let actualUserID = '';
886
+ if (this.attachment) {
887
+ const atUsers = this.attachment.at_users;
888
+ if (atUsers && typeof atUsers === 'object') {
889
+ const userInfo = atUsers[userID];
890
+ if (userInfo && typeof userInfo === 'object') {
891
+ const content = userInfo.content;
892
+ if (content)
893
+ userName = content;
894
+ const uid = userInfo.user_id;
895
+ if (uid)
896
+ actualUserID = uid;
897
+ }
898
+ }
899
+ }
900
+ if (userName) {
901
+ if (this.mode === MODE.Detailed) {
902
+ if (actualUserID)
903
+ return `@${userName}(user_id:${actualUserID})`;
904
+ return `@${userName}(open_id:${userID})`;
905
+ }
906
+ return `@${userName}`;
907
+ }
908
+ if (this.mode === MODE.Detailed) {
909
+ if (actualUserID)
910
+ return `@用户(user_id:${actualUserID})`;
911
+ return `@用户(open_id:${userID})`;
912
+ }
913
+ return `@${userID}`;
914
+ }
915
+ convertImage(prop, _id) {
916
+ let alt = '图片';
917
+ const altElem = prop.alt;
918
+ if (altElem && typeof altElem === 'object') {
919
+ const altText = this.extractTextContent(altElem);
920
+ if (altText)
921
+ alt = altText;
922
+ }
923
+ const titleElem = prop.title;
924
+ if (titleElem && typeof titleElem === 'object') {
925
+ const titleText = this.extractTextContent(titleElem);
926
+ if (titleText)
927
+ alt = titleText;
928
+ }
929
+ let result = `🖼️ ${alt}`;
930
+ if (this.mode === MODE.Detailed) {
931
+ const imageID = prop.imageID;
932
+ if (imageID) {
933
+ const token = this.getImageToken(imageID);
934
+ if (token) {
935
+ result += `(img_token:${token})`;
936
+ }
937
+ else {
938
+ result += `(img_key:${imageID})`;
939
+ }
940
+ }
941
+ }
942
+ return result;
943
+ }
944
+ convertImgCombination(prop) {
945
+ const imgList = prop.imgList;
946
+ if (!Array.isArray(imgList) || imgList.length === 0)
947
+ return '';
948
+ let result = `🖼️ ${imgList.length}张图片`;
949
+ if (this.mode === MODE.Detailed) {
950
+ const keys = [];
951
+ for (const img of imgList) {
952
+ if (typeof img !== 'object' || img === null)
953
+ continue;
954
+ const im = img;
955
+ const imageID = im.imageID;
956
+ if (imageID)
957
+ keys.push(imageID);
958
+ }
959
+ if (keys.length > 0)
960
+ result += `(keys:${keys.join(',')})`;
961
+ }
962
+ return result;
963
+ }
964
+ convertChart(prop, _id) {
965
+ let title = '图表';
966
+ let chartType = '';
967
+ const chartSpec = prop.chartSpec;
968
+ if (chartSpec && typeof chartSpec === 'object') {
969
+ const titleObj = chartSpec.title;
970
+ if (titleObj && typeof titleObj === 'object') {
971
+ const text = titleObj.text;
972
+ if (text)
973
+ title = text;
974
+ }
975
+ const ct = chartSpec.type;
976
+ if (ct) {
977
+ chartType = ct;
978
+ const typeName = CHART_TYPE_NAMES[chartType];
979
+ if (typeName)
980
+ title = `${title}${typeName}`;
981
+ }
982
+ }
983
+ const summary = this.extractChartSummary(prop, chartType);
984
+ let result = `📊 ${title}`;
985
+ if (summary)
986
+ result += `\n数据摘要: ${summary}`;
987
+ return result;
988
+ }
989
+ extractChartSummary(prop, chartType) {
990
+ const chartSpec = prop.chartSpec;
991
+ if (!chartSpec || typeof chartSpec !== 'object')
992
+ return '';
993
+ const dataObj = chartSpec.data;
994
+ if (!dataObj || typeof dataObj !== 'object')
995
+ return '';
996
+ const values = dataObj.values;
997
+ if (!Array.isArray(values) || values.length === 0)
998
+ return '';
999
+ switch (chartType) {
1000
+ case 'line':
1001
+ case 'bar':
1002
+ case 'area':
1003
+ return this.extractLineBarSummary(chartSpec, values);
1004
+ case 'pie':
1005
+ return this.extractPieSummary(chartSpec, values);
1006
+ default:
1007
+ return this.extractGenericSummary(values);
1008
+ }
1009
+ }
1010
+ extractLineBarSummary(chartSpec, values) {
1011
+ const xField = chartSpec.xField;
1012
+ const yField = chartSpec.yField;
1013
+ if (!xField || !yField || values.length === 0) {
1014
+ return this.extractGenericSummary(values);
1015
+ }
1016
+ const parts = [];
1017
+ for (const v of values) {
1018
+ if (typeof v !== 'object' || v === null)
1019
+ continue;
1020
+ const vm = v;
1021
+ parts.push(`${vm[xField]}:${vm[yField]}`);
1022
+ }
1023
+ return parts.length > 0 ? parts.join(', ') : this.extractGenericSummary(values);
1024
+ }
1025
+ extractPieSummary(chartSpec, values) {
1026
+ const categoryField = chartSpec.categoryField;
1027
+ const valueField = chartSpec.valueField;
1028
+ if (!categoryField || !valueField || values.length === 0) {
1029
+ return this.extractGenericSummary(values);
1030
+ }
1031
+ const parts = [];
1032
+ for (const v of values) {
1033
+ if (typeof v !== 'object' || v === null)
1034
+ continue;
1035
+ const vm = v;
1036
+ parts.push(`${vm[categoryField]}:${vm[valueField]}`);
1037
+ }
1038
+ return parts.length > 0 ? parts.join(', ') : this.extractGenericSummary(values);
1039
+ }
1040
+ extractGenericSummary(values) {
1041
+ return `${values.length}个数据点`;
1042
+ }
1043
+ convertAudio(prop, _id) {
1044
+ let result = '🎵 音频';
1045
+ if (this.mode === MODE.Detailed) {
1046
+ const fileID = prop.fileID || prop.audioID || '';
1047
+ if (fileID)
1048
+ result += `(key:${fileID})`;
1049
+ }
1050
+ return result;
1051
+ }
1052
+ convertVideo(prop, _id) {
1053
+ let result = '🎬 视频';
1054
+ if (this.mode === MODE.Detailed) {
1055
+ const fileID = prop.fileID || prop.videoID || '';
1056
+ if (fileID)
1057
+ result += `(key:${fileID})`;
1058
+ }
1059
+ return result;
1060
+ }
1061
+ convertTable(prop) {
1062
+ const columns = prop.columns;
1063
+ if (!Array.isArray(columns) || columns.length === 0)
1064
+ return '';
1065
+ const rows = prop.rows || [];
1066
+ const colNames = [];
1067
+ const colKeys = [];
1068
+ for (const col of columns) {
1069
+ if (typeof col !== 'object' || col === null)
1070
+ continue;
1071
+ const cm = col;
1072
+ let displayName = cm.displayName || '';
1073
+ const name = cm.name || '';
1074
+ if (!displayName)
1075
+ displayName = name;
1076
+ colNames.push(displayName);
1077
+ colKeys.push(name);
1078
+ }
1079
+ const lines = [];
1080
+ lines.push('| ' + colNames.join(' | ') + ' |');
1081
+ lines.push('|' + colNames.map(() => '------|').join(''));
1082
+ for (const row of rows) {
1083
+ if (typeof row !== 'object' || row === null)
1084
+ continue;
1085
+ const rm = row;
1086
+ const cells = [];
1087
+ for (const key of colKeys) {
1088
+ let cellValue = '';
1089
+ const cellData = rm[key];
1090
+ if (cellData && typeof cellData === 'object') {
1091
+ if (cellData.data != null) {
1092
+ cellValue = this.extractTableCellValue(cellData.data);
1093
+ }
1094
+ }
1095
+ cells.push(cellValue);
1096
+ }
1097
+ lines.push('| ' + cells.join(' | ') + ' |');
1098
+ }
1099
+ return lines.join('\n');
1100
+ }
1101
+ extractTableCellValue(data) {
1102
+ if (typeof data === 'string')
1103
+ return data;
1104
+ if (typeof data === 'number')
1105
+ return data.toFixed(2);
1106
+ if (Array.isArray(data)) {
1107
+ const texts = [];
1108
+ for (const item of data) {
1109
+ if (typeof item === 'object' && item !== null) {
1110
+ const im = item;
1111
+ if (typeof im.text === 'string')
1112
+ texts.push(`「${im.text}」`);
1113
+ }
1114
+ }
1115
+ return texts.join(' ');
1116
+ }
1117
+ if (typeof data === 'object' && data !== null) {
1118
+ return this.extractTextContent(data);
1119
+ }
1120
+ return '';
1121
+ }
1122
+ extractTextStyle(prop) {
1123
+ const style = {
1124
+ bold: false,
1125
+ italic: false,
1126
+ strikethrough: false,
1127
+ };
1128
+ const textStyle = prop.textStyle;
1129
+ if (!textStyle || typeof textStyle !== 'object')
1130
+ return style;
1131
+ const attrs = textStyle.attributes;
1132
+ if (Array.isArray(attrs)) {
1133
+ for (const attr of attrs) {
1134
+ if (typeof attr !== 'string')
1135
+ continue;
1136
+ switch (attr) {
1137
+ case 'bold':
1138
+ style.bold = true;
1139
+ break;
1140
+ case 'italic':
1141
+ style.italic = true;
1142
+ break;
1143
+ case 'strikethrough':
1144
+ style.strikethrough = true;
1145
+ break;
1146
+ }
1147
+ }
1148
+ }
1149
+ return style;
1150
+ }
1151
+ applyTextStyle(content, style) {
1152
+ if (!content)
1153
+ return content;
1154
+ if (style.strikethrough)
1155
+ content = `~~${content}~~`;
1156
+ if (style.italic)
1157
+ content = `*${content}*`;
1158
+ if (style.bold)
1159
+ content = `**${content}**`;
1160
+ return content;
1161
+ }
1162
+ getImageToken(imageID) {
1163
+ if (!this.attachment)
1164
+ return '';
1165
+ const images = this.attachment.images;
1166
+ if (!images || typeof images !== 'object')
1167
+ return '';
1168
+ const imageInfo = images[imageID];
1169
+ if (!imageInfo || typeof imageInfo !== 'object')
1170
+ return '';
1171
+ return imageInfo.token || '';
1172
+ }
1173
+ }