koishi-plugin-kkk 1.0.0

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 (702) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/LICENSE +674 -0
  3. package/README.md +98 -0
  4. package/assets/web/assets/CronEditor-CYgFpX-g.js +1 -0
  5. package/assets/web/assets/DesktopLayout-iI43ij11.js +1 -0
  6. package/assets/web/assets/MobileLayout-CziU8MCR.js +1 -0
  7. package/assets/web/assets/ThemeSwitch-DFohhwd9.js +1 -0
  8. package/assets/web/assets/index-BRROK0vh.js +47 -0
  9. package/assets/web/assets/index-Dj-StEiS.css +2 -0
  10. package/assets/web/assets/separator-CxcFlCzz.js +1 -0
  11. package/assets/web/favicon.svg +1 -0
  12. package/assets/web/icons.svg +24 -0
  13. package/assets/web/index.html +15 -0
  14. package/assets/webui.html +115 -0
  15. package/client/index.ts +63 -0
  16. package/config/default_config/app.yaml +64 -0
  17. package/config/default_config/bilibili.yaml +116 -0
  18. package/config/default_config/config.json +157 -0
  19. package/config/default_config/cookies.yaml +11 -0
  20. package/config/default_config/douyin.yaml +99 -0
  21. package/config/default_config/kuaishou.yaml +8 -0
  22. package/config/default_config/pushlist.yaml +37 -0
  23. package/config/default_config/request.yaml +22 -0
  24. package/config/default_config/upload.yaml +50 -0
  25. package/config/default_config/xiaohongshu.yaml +23 -0
  26. package/dist/index.js +1 -0
  27. package/lib/compat/axios.js +23 -0
  28. package/lib/compat/cron.js +77 -0
  29. package/lib/compat/esm.js +15 -0
  30. package/lib/compat/express.js +23 -0
  31. package/lib/compat/heroui.js +12 -0
  32. package/lib/compat/lodash.js +33 -0
  33. package/lib/compat/logger.js +118 -0
  34. package/lib/compat/node-karin.js +1053 -0
  35. package/lib/compat/phosphor-icons.js +27 -0
  36. package/lib/compat/qqcard.js +256 -0
  37. package/lib/compat/root.js +44 -0
  38. package/lib/compat/runtime.js +87 -0
  39. package/lib/compat/segment.js +167 -0
  40. package/lib/compat/sqlite3.js +117 -0
  41. package/lib/compat/start.js +7 -0
  42. package/lib/compat/syncConfig.js +65 -0
  43. package/lib/compat/template-react.js +30 -0
  44. package/lib/compat/text.js +65 -0
  45. package/lib/compat/yaml.js +13 -0
  46. package/lib/configBridge.js +91 -0
  47. package/lib/index.js +775 -0
  48. package/lib/karin/app.js +34 -0
  49. package/lib/karin/apps/__ready.js +9 -0
  50. package/lib/karin/apps/admin.js +136 -0
  51. package/lib/karin/apps/help.js +233 -0
  52. package/lib/karin/apps/push.js +291 -0
  53. package/lib/karin/apps/qrlogin.js +176 -0
  54. package/lib/karin/apps/statistics.js +209 -0
  55. package/lib/karin/apps/testPush.js +247 -0
  56. package/lib/karin/apps/tools.js +473 -0
  57. package/lib/karin/apps/update.js +252 -0
  58. package/lib/karin/index.js +37 -0
  59. package/lib/karin/module/db/bilibili.js +831 -0
  60. package/lib/karin/module/db/douyin.js +918 -0
  61. package/lib/karin/module/db/index.js +165 -0
  62. package/lib/karin/module/db/migration.js +199 -0
  63. package/lib/karin/module/db/statistics.js +559 -0
  64. package/lib/karin/module/index.js +94 -0
  65. package/lib/karin/module/server/constants/routes.js +48 -0
  66. package/lib/karin/module/server/controllers/bots.js +230 -0
  67. package/lib/karin/module/server/controllers/config.js +75 -0
  68. package/lib/karin/module/server/controllers/video.js +208 -0
  69. package/lib/karin/module/server/index.js +26 -0
  70. package/lib/karin/module/server/middlewares/auth.js +133 -0
  71. package/lib/karin/module/server/routes/amagi.js +57 -0
  72. package/lib/karin/module/server/routes/api.js +40 -0
  73. package/lib/karin/module/server/routes/index.js +97 -0
  74. package/lib/karin/module/server/routes/ssr.js +16 -0
  75. package/lib/karin/module/server/routes/static.js +43 -0
  76. package/lib/karin/module/utils/Base.js +403 -0
  77. package/lib/karin/module/utils/CardParser.js +487 -0
  78. package/lib/karin/module/utils/Common.js +418 -0
  79. package/lib/karin/module/utils/Config.js +434 -0
  80. package/lib/karin/module/utils/EmojiReaction.js +242 -0
  81. package/lib/karin/module/utils/ErrorHandler/handler.js +114 -0
  82. package/lib/karin/module/utils/ErrorHandler/index.js +55 -0
  83. package/lib/karin/module/utils/ErrorHandler/render.js +182 -0
  84. package/lib/karin/module/utils/ErrorHandler/sender.js +206 -0
  85. package/lib/karin/module/utils/ErrorHandler/strategy.js +41 -0
  86. package/lib/karin/module/utils/ErrorHandler/types.js +2 -0
  87. package/lib/karin/module/utils/ErrorHandler/utils.js +108 -0
  88. package/lib/karin/module/utils/FFmpeg.js +298 -0
  89. package/lib/karin/module/utils/ImageHelper.js +91 -0
  90. package/lib/karin/module/utils/ImageSlice.js +429 -0
  91. package/lib/karin/module/utils/MotionPhoto.js +250 -0
  92. package/lib/karin/module/utils/Network/Downloader.js +413 -0
  93. package/lib/karin/module/utils/Network/ImageDownloader.js +250 -0
  94. package/lib/karin/module/utils/Network/Network.js +332 -0
  95. package/lib/karin/module/utils/Network/ThrottleStream.js +71 -0
  96. package/lib/karin/module/utils/Network/constants.js +129 -0
  97. package/lib/karin/module/utils/Network/helpers.js +174 -0
  98. package/lib/karin/module/utils/Network/index.js +34 -0
  99. package/lib/karin/module/utils/Network/types.js +12 -0
  100. package/lib/karin/module/utils/Networks.js +17 -0
  101. package/lib/karin/module/utils/ParseLock.js +34 -0
  102. package/lib/karin/module/utils/ParseOverride.js +88 -0
  103. package/lib/karin/module/utils/QqCardResolve.js +114 -0
  104. package/lib/karin/module/utils/QqPanel.js +879 -0
  105. package/lib/karin/module/utils/Render/coverTheme.js +256 -0
  106. package/lib/karin/module/utils/Render/index.js +582 -0
  107. package/lib/karin/module/utils/amagiClient.js +338 -0
  108. package/lib/karin/module/utils/avatar.js +201 -0
  109. package/lib/karin/module/utils/bot.js +141 -0
  110. package/lib/karin/module/utils/build-metadata.js +50 -0
  111. package/lib/karin/module/utils/changelog.js +166 -0
  112. package/lib/karin/module/utils/index.js +26 -0
  113. package/lib/karin/module/utils/loginSession.js +259 -0
  114. package/lib/karin/module/utils/parseTip.js +93 -0
  115. package/lib/karin/module/utils/runtime-report.js +140 -0
  116. package/lib/karin/module/utils/semver.js +72 -0
  117. package/lib/karin/module/utils/statisticsAggregate.js +415 -0
  118. package/lib/karin/platform/bilibili/bilibili.js +1805 -0
  119. package/lib/karin/platform/bilibili/comments.js +294 -0
  120. package/lib/karin/platform/bilibili/danmaku.js +555 -0
  121. package/lib/karin/platform/bilibili/dynamic-text.js +827 -0
  122. package/lib/karin/platform/bilibili/genParams.js +37 -0
  123. package/lib/karin/platform/bilibili/getID.js +159 -0
  124. package/lib/karin/platform/bilibili/index.js +24 -0
  125. package/lib/karin/platform/bilibili/live-status.js +40 -0
  126. package/lib/karin/platform/bilibili/login.js +132 -0
  127. package/lib/karin/platform/bilibili/push.js +1397 -0
  128. package/lib/karin/platform/bilibili/riskControl.js +133 -0
  129. package/lib/karin/platform/bilibili/search.js +299 -0
  130. package/lib/karin/platform/common/danmaku-types.js +12 -0
  131. package/lib/karin/platform/douyin/comments.js +310 -0
  132. package/lib/karin/platform/douyin/danmaku.js +893 -0
  133. package/lib/karin/platform/douyin/douyin.js +1145 -0
  134. package/lib/karin/platform/douyin/getID.js +94 -0
  135. package/lib/karin/platform/douyin/index.js +24 -0
  136. package/lib/karin/platform/douyin/login.js +230 -0
  137. package/lib/karin/platform/douyin/push/favorite.js +75 -0
  138. package/lib/karin/platform/douyin/push/index.js +21 -0
  139. package/lib/karin/platform/douyin/push/live.js +69 -0
  140. package/lib/karin/platform/douyin/push/post.js +60 -0
  141. package/lib/karin/platform/douyin/push/recommend.js +74 -0
  142. package/lib/karin/platform/douyin/push/render.js +621 -0
  143. package/lib/karin/platform/douyin/push/types.js +2 -0
  144. package/lib/karin/platform/douyin/push.js +1128 -0
  145. package/lib/karin/platform/douyin/types.js +13 -0
  146. package/lib/karin/platform/douyin/videoQuality.js +222 -0
  147. package/lib/karin/platform/douyin/workType.js +182 -0
  148. package/lib/karin/platform/index.js +20 -0
  149. package/lib/karin/platform/kuaishou/comments.js +91 -0
  150. package/lib/karin/platform/kuaishou/getID.js +43 -0
  151. package/lib/karin/platform/kuaishou/getdata.js +41 -0
  152. package/lib/karin/platform/kuaishou/index.js +20 -0
  153. package/lib/karin/platform/kuaishou/kuaishou.js +122 -0
  154. package/lib/karin/platform/xiaohongshu/comments.js +162 -0
  155. package/lib/karin/platform/xiaohongshu/getID.js +129 -0
  156. package/lib/karin/platform/xiaohongshu/index.js +18 -0
  157. package/lib/karin/platform/xiaohongshu/xiaohongshu.js +643 -0
  158. package/lib/karin/root.js +73 -0
  159. package/lib/karin/setup.js +99 -0
  160. package/lib/karin/types/config/amagi.js +2 -0
  161. package/lib/karin/types/config/app.js +2 -0
  162. package/lib/karin/types/config/bilibili.js +2 -0
  163. package/lib/karin/types/config/cookies.js +2 -0
  164. package/lib/karin/types/config/douyin.js +2 -0
  165. package/lib/karin/types/config/index.js +2 -0
  166. package/lib/karin/types/config/kuaishou.js +2 -0
  167. package/lib/karin/types/config/pushlist.js +2 -0
  168. package/lib/karin/types/config/request.js +2 -0
  169. package/lib/karin/types/config/upload.js +2 -0
  170. package/lib/karin/types/config/xiaohongshu.js +2 -0
  171. package/lib/karin/types/datatypes.js +2 -0
  172. package/lib/karin/types/index.js +18 -0
  173. package/lib/karin/types/netrorks/NetworksConfigType.js +2 -0
  174. package/lib/karin/types/netrorks/index.js +17 -0
  175. package/lib/karin/web.config.js +49 -0
  176. package/lib/ktr/registry.js +82 -0
  177. package/lib/ktr/template/_preview/components/PreviewInfoCard.js +10 -0
  178. package/lib/ktr/template/_preview/components/PreviewLayout.js +10 -0
  179. package/lib/ktr/template/_preview/components/PreviewVideoCard.js +8 -0
  180. package/lib/ktr/template/_preview/render.js +142 -0
  181. package/lib/ktr/template/_preview/types.js +2 -0
  182. package/lib/ktr/template/_preview/utils/status.js +31 -0
  183. package/lib/ktr/template/_preview/utils/time.js +12 -0
  184. package/lib/ktr/template/bilibili/bangumi/components/bangumi.js +127 -0
  185. package/lib/ktr/template/bilibili/bangumi/components/types.js +2 -0
  186. package/lib/ktr/template/bilibili/bangumi/index.js +12 -0
  187. package/lib/ktr/template/bilibili/comment/components/Comment.js +193 -0
  188. package/lib/ktr/template/bilibili/comment/components/types.js +2 -0
  189. package/lib/ktr/template/bilibili/comment/index.js +10 -0
  190. package/lib/ktr/template/bilibili/components/Icons.js +66 -0
  191. package/lib/ktr/template/bilibili/components/shared.js +93 -0
  192. package/lib/ktr/template/bilibili/components/types.js +2 -0
  193. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/DYNAMIC_TYPE_ARTICLE.js +66 -0
  194. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/types.js +2 -0
  195. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/index.js +10 -0
  196. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/DYNAMIC_TYPE_AV.js +87 -0
  197. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/types.js +2 -0
  198. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/index.js +10 -0
  199. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/DYNAMIC_TYPE_DRAW.js +93 -0
  200. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/types.js +2 -0
  201. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/index.js +10 -0
  202. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/DYNAMIC_TYPE_FORWARD.js +132 -0
  203. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/types.js +2 -0
  204. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/index.js +10 -0
  205. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/DYNAMIC_TYPE_LIVE_RCMD.js +84 -0
  206. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/types.js +2 -0
  207. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/index.js +10 -0
  208. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/DYNAMIC_TYPE_WORD.js +38 -0
  209. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/types.js +2 -0
  210. package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/index.js +10 -0
  211. package/lib/ktr/template/bilibili/dynamic/components/AdditionalCard.js +59 -0
  212. package/lib/ktr/template/bilibili/dynamic/components/CommonComponents.js +30 -0
  213. package/lib/ktr/template/bilibili/dynamic/types.js +2 -0
  214. package/lib/ktr/template/bilibili/qrcodeImg/components/qrcodeImg.js +48 -0
  215. package/lib/ktr/template/bilibili/qrcodeImg/components/types.js +3 -0
  216. package/lib/ktr/template/bilibili/qrcodeImg/index.js +10 -0
  217. package/lib/ktr/template/bilibili/userlist/components/UserList.js +53 -0
  218. package/lib/ktr/template/bilibili/userlist/components/types.js +3 -0
  219. package/lib/ktr/template/bilibili/userlist/index.js +13 -0
  220. package/lib/ktr/template/bilibili/videoInfo/components/types.js +2 -0
  221. package/lib/ktr/template/bilibili/videoInfo/components/videoInfo.js +62 -0
  222. package/lib/ktr/template/bilibili/videoInfo/index.js +10 -0
  223. package/lib/ktr/template/components/AmbientCover.js +36 -0
  224. package/lib/ktr/template/components/DefaultLayout.js +33 -0
  225. package/lib/ktr/template/components/GlowImage.js +276 -0
  226. package/lib/ktr/template/components/QRCodeWithAvatar.js +34 -0
  227. package/lib/ktr/template/components/RolldownLogo.js +6 -0
  228. package/lib/ktr/template/components/ViteLogo.js +6 -0
  229. package/lib/ktr/template/components/iconRegistry.js +173 -0
  230. package/lib/ktr/template/douyin/article-work/components/ArticleWork.js +99 -0
  231. package/lib/ktr/template/douyin/article-work/components/types.js +3 -0
  232. package/lib/ktr/template/douyin/article-work/index.js +10 -0
  233. package/lib/ktr/template/douyin/comment/components/Comment.js +189 -0
  234. package/lib/ktr/template/douyin/comment/components/types.js +2 -0
  235. package/lib/ktr/template/douyin/comment/index.js +10 -0
  236. package/lib/ktr/template/douyin/components/Icons.js +29 -0
  237. package/lib/ktr/template/douyin/components/types.js +3 -0
  238. package/lib/ktr/template/douyin/dynamic/components/Dynamic.js +90 -0
  239. package/lib/ktr/template/douyin/dynamic/components/types.js +3 -0
  240. package/lib/ktr/template/douyin/dynamic/index.js +10 -0
  241. package/lib/ktr/template/douyin/favorite-list/components/FavoriteList.js +30 -0
  242. package/lib/ktr/template/douyin/favorite-list/components/types.js +3 -0
  243. package/lib/ktr/template/douyin/favorite-list/index.js +10 -0
  244. package/lib/ktr/template/douyin/image-work/components/ImageWork.js +136 -0
  245. package/lib/ktr/template/douyin/image-work/components/types.js +2 -0
  246. package/lib/ktr/template/douyin/image-work/index.js +10 -0
  247. package/lib/ktr/template/douyin/live/components/Live.js +51 -0
  248. package/lib/ktr/template/douyin/live/components/types.js +3 -0
  249. package/lib/ktr/template/douyin/live/index.js +10 -0
  250. package/lib/ktr/template/douyin/musicinfo/components/MusicInfo.js +60 -0
  251. package/lib/ktr/template/douyin/musicinfo/components/types.js +3 -0
  252. package/lib/ktr/template/douyin/musicinfo/index.js +10 -0
  253. package/lib/ktr/template/douyin/qrcodeImg/components/qrcodeImg.js +39 -0
  254. package/lib/ktr/template/douyin/qrcodeImg/components/types.js +3 -0
  255. package/lib/ktr/template/douyin/qrcodeImg/index.js +9 -0
  256. package/lib/ktr/template/douyin/recommend-list/components/RecommendList.js +30 -0
  257. package/lib/ktr/template/douyin/recommend-list/components/types.js +3 -0
  258. package/lib/ktr/template/douyin/recommend-list/index.js +10 -0
  259. package/lib/ktr/template/douyin/user_profile/components/UserVideoList.js +54 -0
  260. package/lib/ktr/template/douyin/user_profile/components/types.js +3 -0
  261. package/lib/ktr/template/douyin/user_profile/index.js +9 -0
  262. package/lib/ktr/template/douyin/userlist/components/UserList.js +50 -0
  263. package/lib/ktr/template/douyin/userlist/components/types.js +3 -0
  264. package/lib/ktr/template/douyin/userlist/index.js +13 -0
  265. package/lib/ktr/template/douyin/video-work/components/VideoWork.js +127 -0
  266. package/lib/ktr/template/douyin/video-work/components/types.js +2 -0
  267. package/lib/ktr/template/douyin/video-work/index.js +10 -0
  268. package/lib/ktr/template/kuaishou/comment/components/Comment.js +88 -0
  269. package/lib/ktr/template/kuaishou/comment/components/types.js +2 -0
  270. package/lib/ktr/template/kuaishou/comment/index.js +10 -0
  271. package/lib/ktr/template/other/changelog/components/changelog.js +117 -0
  272. package/lib/ktr/template/other/changelog/components/types.js +3 -0
  273. package/lib/ktr/template/other/changelog/index.js +9 -0
  274. package/lib/ktr/template/other/handlerError/components/errorTitles.js +227 -0
  275. package/lib/ktr/template/other/handlerError/components/handlerError.js +392 -0
  276. package/lib/ktr/template/other/handlerError/components/stackHighlight.js +109 -0
  277. package/lib/ktr/template/other/handlerError/components/types.js +2 -0
  278. package/lib/ktr/template/other/handlerError/index.js +9 -0
  279. package/lib/ktr/template/other/help/components/Help.js +79 -0
  280. package/lib/ktr/template/other/help/components/types.js +3 -0
  281. package/lib/ktr/template/other/help/index.js +12 -0
  282. package/lib/ktr/template/other/live-photo-tip/components/LivePhotoTip.js +80 -0
  283. package/lib/ktr/template/other/live-photo-tip/components/types.js +3 -0
  284. package/lib/ktr/template/other/live-photo-tip/index.js +9 -0
  285. package/lib/ktr/template/other/qrlogin/components/qrlogin.js +58 -0
  286. package/lib/ktr/template/other/qrlogin/components/types.js +3 -0
  287. package/lib/ktr/template/other/qrlogin/index.js +9 -0
  288. package/lib/ktr/template/other/runtime/components/runtime.js +101 -0
  289. package/lib/ktr/template/other/runtime/components/types.js +3 -0
  290. package/lib/ktr/template/other/runtime/index.js +9 -0
  291. package/lib/ktr/template/other/version_warning/components/VersionWarning.js +38 -0
  292. package/lib/ktr/template/other/version_warning/components/types.js +3 -0
  293. package/lib/ktr/template/other/version_warning/index.js +9 -0
  294. package/lib/ktr/template/statistics/components/BoxplotGuide.js +17 -0
  295. package/lib/ktr/template/statistics/components/ChartCard.js +15 -0
  296. package/lib/ktr/template/statistics/components/KpiCard.js +14 -0
  297. package/lib/ktr/template/statistics/components/PlatformTable.js +20 -0
  298. package/lib/ktr/template/statistics/components/RankList.js +61 -0
  299. package/lib/ktr/template/statistics/components/SSRChart.js +22 -0
  300. package/lib/ktr/template/statistics/components/chartPresets.js +630 -0
  301. package/lib/ktr/template/statistics/components/chartTheme.js +226 -0
  302. package/lib/ktr/template/statistics/components/format.js +38 -0
  303. package/lib/ktr/template/statistics/components/types.js +9 -0
  304. package/lib/ktr/template/statistics/global/components/GlobalStatistics.js +246 -0
  305. package/lib/ktr/template/statistics/global/components/types.js +3 -0
  306. package/lib/ktr/template/statistics/global/index.js +9 -0
  307. package/lib/ktr/template/statistics/group/components/GroupStatistics.js +181 -0
  308. package/lib/ktr/template/statistics/group/components/types.js +3 -0
  309. package/lib/ktr/template/statistics/group/index.js +9 -0
  310. package/lib/ktr/template/types/ctx.js +2 -0
  311. package/lib/ktr/template/xiaohongshu/comment/components/Comment.js +99 -0
  312. package/lib/ktr/template/xiaohongshu/comment/components/types.js +3 -0
  313. package/lib/ktr/template/xiaohongshu/comment/index.js +9 -0
  314. package/lib/ktr/template/xiaohongshu/noteInfo/components/noteInfo.js +93 -0
  315. package/lib/ktr/template/xiaohongshu/noteInfo/components/types.js +2 -0
  316. package/lib/ktr/template/xiaohongshu/noteInfo/index.js +9 -0
  317. package/lib/ktr/utils/QRCodeAvatar.js +99 -0
  318. package/lib/ktr/utils/QRcode.js +56 -0
  319. package/lib/ktr/utils/avatarProxy.js +45 -0
  320. package/lib/ktr/utils/cn.js +11 -0
  321. package/lib/ktr/utils/theme.js +12 -0
  322. package/lib/qqFields.json +75 -0
  323. package/lib/qqOptions.js +44 -0
  324. package/lib/richtext/index.js +25 -0
  325. package/lib/richtext/parse/index.js +232 -0
  326. package/lib/richtext/react/cn.js +11 -0
  327. package/lib/richtext/react/index.js +221 -0
  328. package/lib/richtext/types.js +2 -0
  329. package/lib/schema.js +352 -0
  330. package/lib/webui.js +341 -0
  331. package/package.json +88 -0
  332. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_BV.svg +1 -0
  333. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_GOODS.svg +1 -0
  334. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_LOTTERY.svg +1 -0
  335. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_VIEW_PICTURE.svg +1 -0
  336. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_VOTE.svg +1 -0
  337. package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_WEB.svg +1 -0
  338. package/resources/image/bilibili/bilibili-dark.png +0 -0
  339. package/resources/image/bilibili/bilibili-light.png +0 -0
  340. package/resources/image/bilibili/bilibili.png +0 -0
  341. package/resources/image/bilibili/id-dark.svg +1 -0
  342. package/resources/image/bilibili/id-light.svg +1 -0
  343. package/resources/image/bilibili/level/lv0.svg +1 -0
  344. package/resources/image/bilibili/level/lv1.svg +1 -0
  345. package/resources/image/bilibili/level/lv2.svg +1 -0
  346. package/resources/image/bilibili/level/lv3.svg +1 -0
  347. package/resources/image/bilibili/level/lv4.svg +1 -0
  348. package/resources/image/bilibili/level/lv5.svg +1 -0
  349. package/resources/image/bilibili/level/lv6.svg +1 -0
  350. package/resources/image/bilibili/level/lv7.svg +1 -0
  351. package/resources/image/bilibili/like-dark.svg +6 -0
  352. package/resources/image/bilibili/like-light.svg +6 -0
  353. package/resources/image/bilibili/play.svg +28 -0
  354. package/resources/image/bilibili/res-local1.png +0 -0
  355. package/resources/image/bilibili/up_pb.svg +1 -0
  356. package/resources/image/bilibili//347/233/264/346/222/255/344/270/255.png +0 -0
  357. package/resources/image/douyin/douyin.png +0 -0
  358. package/resources/image/douyin/dylogo-dark.svg +23 -0
  359. package/resources/image/douyin/dylogo-light.svg +38 -0
  360. package/resources/image/douyin/search-dark.svg +1 -0
  361. package/resources/image/douyin/search-light.svg +3 -0
  362. package/resources/image/douyin//346/212/226/351/237/263-/347/233/264/346/222/255/344/270/255.png +0 -0
  363. package/resources/image/frame-logo.png +0 -0
  364. package/resources/image/kuaishou/logo.png +0 -0
  365. package/resources/image/other/handlerError/Milky.png +0 -0
  366. package/resources/image/other/handlerError/chronocat.svg +43 -0
  367. package/resources/image/other/handlerError/conwechat.webp +0 -0
  368. package/resources/image/other/handlerError/gocq-http.webp +0 -0
  369. package/resources/image/other/handlerError/lagrange.webp +0 -0
  370. package/resources/image/other/handlerError/llonebot.webp +0 -0
  371. package/resources/image/other/handlerError/napcat.webp +0 -0
  372. package/resources/image/other/handlerError/onebot.png +0 -0
  373. package/resources/image/other/handlerError/satori.png +0 -0
  374. package/resources/image/xiaohongshu/logo.png +0 -0
  375. package/resources/template/style.css +23708 -0
  376. package/resources/vite.svg +130 -0
  377. package/scripts/build.mjs +129 -0
  378. package/scripts/check-schema.cjs +55 -0
  379. package/scripts/deploy.mjs +78 -0
  380. package/scripts/make-demo-placeholders.mjs +112 -0
  381. package/scripts/patch-deps.mjs +46 -0
  382. package/scripts/patch-webui.mjs +112 -0
  383. package/scripts/smoke-commands.cjs +39 -0
  384. package/scripts/smoke-config.cjs +122 -0
  385. package/scripts/smoke-douyin.cjs +170 -0
  386. package/scripts/smoke-koishi-commands.cjs +210 -0
  387. package/scripts/smoke-login.cjs +110 -0
  388. package/scripts/smoke-parse.cjs +54 -0
  389. package/scripts/smoke-push-mock.cjs +114 -0
  390. package/scripts/smoke-push.cjs +104 -0
  391. package/scripts/smoke-qq.cjs +147 -0
  392. package/scripts/smoke-qqcard.cjs +108 -0
  393. package/scripts/smoke-qqpanel.cjs +205 -0
  394. package/scripts/smoke-render.cjs +106 -0
  395. package/scripts/smoke-server.cjs +94 -0
  396. package/src/compat/axios.ts +5 -0
  397. package/src/compat/cron.ts +86 -0
  398. package/src/compat/esm.ts +13 -0
  399. package/src/compat/express.ts +5 -0
  400. package/src/compat/heroui.tsx +53 -0
  401. package/src/compat/lodash.ts +32 -0
  402. package/src/compat/logger.ts +121 -0
  403. package/src/compat/node-karin.ts +1064 -0
  404. package/src/compat/phosphor-icons.tsx +25 -0
  405. package/src/compat/qqcard.ts +251 -0
  406. package/src/compat/root.ts +44 -0
  407. package/src/compat/runtime.ts +120 -0
  408. package/src/compat/segment.ts +161 -0
  409. package/src/compat/sqlite3.ts +123 -0
  410. package/src/compat/start.ts +3 -0
  411. package/src/compat/syncConfig.ts +64 -0
  412. package/src/compat/template-react.ts +54 -0
  413. package/src/compat/text.ts +58 -0
  414. package/src/compat/yaml.ts +6 -0
  415. package/src/configBridge.ts +94 -0
  416. package/src/index.ts +769 -0
  417. package/src/karin/app.ts +1 -0
  418. package/src/karin/apps/__ready.ts +14 -0
  419. package/src/karin/apps/admin.ts +127 -0
  420. package/src/karin/apps/help.ts +228 -0
  421. package/src/karin/apps/push.ts +327 -0
  422. package/src/karin/apps/qrlogin.ts +160 -0
  423. package/src/karin/apps/statistics.ts +203 -0
  424. package/src/karin/apps/testPush.ts +226 -0
  425. package/src/karin/apps/tools.ts +510 -0
  426. package/src/karin/apps/update.ts +239 -0
  427. package/src/karin/index.ts +9 -0
  428. package/src/karin/module/db/bilibili.ts +1089 -0
  429. package/src/karin/module/db/douyin.ts +1218 -0
  430. package/src/karin/module/db/index.ts +159 -0
  431. package/src/karin/module/db/migration.ts +249 -0
  432. package/src/karin/module/db/statistics.ts +817 -0
  433. package/src/karin/module/index.ts +37 -0
  434. package/src/karin/module/server/constants/routes.ts +53 -0
  435. package/src/karin/module/server/controllers/bots.ts +289 -0
  436. package/src/karin/module/server/controllers/config.ts +85 -0
  437. package/src/karin/module/server/controllers/video.ts +212 -0
  438. package/src/karin/module/server/index.ts +11 -0
  439. package/src/karin/module/server/middlewares/auth.ts +147 -0
  440. package/src/karin/module/server/routes/amagi.ts +57 -0
  441. package/src/karin/module/server/routes/api.ts +42 -0
  442. package/src/karin/module/server/routes/index.ts +68 -0
  443. package/src/karin/module/server/routes/ssr.ts +14 -0
  444. package/src/karin/module/server/routes/static.ts +43 -0
  445. package/src/karin/module/utils/Base.ts +497 -0
  446. package/src/karin/module/utils/CardParser.ts +503 -0
  447. package/src/karin/module/utils/Common.ts +448 -0
  448. package/src/karin/module/utils/Config.ts +443 -0
  449. package/src/karin/module/utils/EmojiReaction.ts +268 -0
  450. package/src/karin/module/utils/ErrorHandler/handler.ts +124 -0
  451. package/src/karin/module/utils/ErrorHandler/index.ts +48 -0
  452. package/src/karin/module/utils/ErrorHandler/render.ts +180 -0
  453. package/src/karin/module/utils/ErrorHandler/sender.ts +192 -0
  454. package/src/karin/module/utils/ErrorHandler/strategy.ts +41 -0
  455. package/src/karin/module/utils/ErrorHandler/types.ts +122 -0
  456. package/src/karin/module/utils/ErrorHandler/utils.ts +108 -0
  457. package/src/karin/module/utils/FFmpeg.ts +357 -0
  458. package/src/karin/module/utils/ImageHelper.ts +90 -0
  459. package/src/karin/module/utils/ImageSlice.ts +383 -0
  460. package/src/karin/module/utils/MotionPhoto.ts +297 -0
  461. package/src/karin/module/utils/Network/Downloader.ts +481 -0
  462. package/src/karin/module/utils/Network/ImageDownloader.ts +291 -0
  463. package/src/karin/module/utils/Network/Network.ts +344 -0
  464. package/src/karin/module/utils/Network/ThrottleStream.ts +76 -0
  465. package/src/karin/module/utils/Network/constants.ts +135 -0
  466. package/src/karin/module/utils/Network/helpers.ts +183 -0
  467. package/src/karin/module/utils/Network/index.ts +28 -0
  468. package/src/karin/module/utils/Network/types.ts +46 -0
  469. package/src/karin/module/utils/Networks.ts +20 -0
  470. package/src/karin/module/utils/ParseLock.ts +33 -0
  471. package/src/karin/module/utils/ParseOverride.ts +128 -0
  472. package/src/karin/module/utils/QqCardResolve.ts +116 -0
  473. package/src/karin/module/utils/QqPanel.ts +880 -0
  474. package/src/karin/module/utils/Render/coverTheme.ts +294 -0
  475. package/src/karin/module/utils/Render/index.ts +574 -0
  476. package/src/karin/module/utils/amagiClient.ts +368 -0
  477. package/src/karin/module/utils/avatar.ts +220 -0
  478. package/src/karin/module/utils/bot.ts +115 -0
  479. package/src/karin/module/utils/build-metadata.ts +60 -0
  480. package/src/karin/module/utils/changelog.ts +175 -0
  481. package/src/karin/module/utils/index.ts +10 -0
  482. package/src/karin/module/utils/loginSession.ts +291 -0
  483. package/src/karin/module/utils/parseTip.ts +59 -0
  484. package/src/karin/module/utils/runtime-report.ts +142 -0
  485. package/src/karin/module/utils/semver.ts +60 -0
  486. package/src/karin/module/utils/statisticsAggregate.ts +507 -0
  487. package/src/karin/platform/bilibili/bilibili.ts +2004 -0
  488. package/src/karin/platform/bilibili/comments.ts +397 -0
  489. package/src/karin/platform/bilibili/danmaku.ts +653 -0
  490. package/src/karin/platform/bilibili/dynamic-text.ts +926 -0
  491. package/src/karin/platform/bilibili/genParams.ts +30 -0
  492. package/src/karin/platform/bilibili/getID.ts +163 -0
  493. package/src/karin/platform/bilibili/index.ts +8 -0
  494. package/src/karin/platform/bilibili/live-status.ts +36 -0
  495. package/src/karin/platform/bilibili/login.ts +130 -0
  496. package/src/karin/platform/bilibili/push.ts +1615 -0
  497. package/src/karin/platform/bilibili/riskControl.ts +114 -0
  498. package/src/karin/platform/bilibili/search.ts +305 -0
  499. package/src/karin/platform/common/danmaku-types.ts +18 -0
  500. package/src/karin/platform/douyin/comments.ts +404 -0
  501. package/src/karin/platform/douyin/danmaku.ts +1187 -0
  502. package/src/karin/platform/douyin/douyin.ts +1244 -0
  503. package/src/karin/platform/douyin/getID.ts +101 -0
  504. package/src/karin/platform/douyin/index.ts +8 -0
  505. package/src/karin/platform/douyin/login.ts +251 -0
  506. package/src/karin/platform/douyin/push/favorite.ts +97 -0
  507. package/src/karin/platform/douyin/push/index.ts +5 -0
  508. package/src/karin/platform/douyin/push/live.ts +86 -0
  509. package/src/karin/platform/douyin/push/post.ts +75 -0
  510. package/src/karin/platform/douyin/push/recommend.ts +96 -0
  511. package/src/karin/platform/douyin/push/render.ts +768 -0
  512. package/src/karin/platform/douyin/push/types.ts +41 -0
  513. package/src/karin/platform/douyin/push.ts +1241 -0
  514. package/src/karin/platform/douyin/types.ts +151 -0
  515. package/src/karin/platform/douyin/videoQuality.ts +234 -0
  516. package/src/karin/platform/douyin/workType.ts +238 -0
  517. package/src/karin/platform/index.ts +4 -0
  518. package/src/karin/platform/kuaishou/comments.ts +114 -0
  519. package/src/karin/platform/kuaishou/getID.ts +48 -0
  520. package/src/karin/platform/kuaishou/getdata.ts +78 -0
  521. package/src/karin/platform/kuaishou/index.ts +4 -0
  522. package/src/karin/platform/kuaishou/kuaishou.ts +130 -0
  523. package/src/karin/platform/xiaohongshu/comments.ts +227 -0
  524. package/src/karin/platform/xiaohongshu/getID.ts +127 -0
  525. package/src/karin/platform/xiaohongshu/index.ts +2 -0
  526. package/src/karin/platform/xiaohongshu/xiaohongshu.ts +733 -0
  527. package/src/karin/root.ts +78 -0
  528. package/src/karin/setup.ts +77 -0
  529. package/src/karin/types/config/amagi.ts +46 -0
  530. package/src/karin/types/config/app.ts +129 -0
  531. package/src/karin/types/config/bilibili.ts +121 -0
  532. package/src/karin/types/config/cookies.ts +11 -0
  533. package/src/karin/types/config/douyin.ts +93 -0
  534. package/src/karin/types/config/index.ts +41 -0
  535. package/src/karin/types/config/kuaishou.ts +10 -0
  536. package/src/karin/types/config/pushlist.ts +55 -0
  537. package/src/karin/types/config/request.ts +27 -0
  538. package/src/karin/types/config/upload.ts +49 -0
  539. package/src/karin/types/config/xiaohongshu.ts +24 -0
  540. package/src/karin/types/datatypes.ts +42 -0
  541. package/src/karin/types/index.ts +4 -0
  542. package/src/karin/types/netrorks/NetworksConfigType.ts +61 -0
  543. package/src/karin/types/netrorks/index.ts +1 -0
  544. package/src/karin/web.config.ts +48 -0
  545. package/src/ktr/registry.ts +55 -0
  546. package/src/ktr/template/_preview/components/PreviewInfoCard.tsx +33 -0
  547. package/src/ktr/template/_preview/components/PreviewLayout.tsx +65 -0
  548. package/src/ktr/template/_preview/components/PreviewVideoCard.tsx +22 -0
  549. package/src/ktr/template/_preview/render.ts +158 -0
  550. package/src/ktr/template/_preview/types.ts +15 -0
  551. package/src/ktr/template/_preview/utils/status.ts +29 -0
  552. package/src/ktr/template/_preview/utils/time.ts +8 -0
  553. package/src/ktr/template/bilibili/bangumi/components/bangumi.tsx +462 -0
  554. package/src/ktr/template/bilibili/bangumi/components/types.ts +36 -0
  555. package/src/ktr/template/bilibili/bangumi/index.tsx +9 -0
  556. package/src/ktr/template/bilibili/comment/components/Comment.tsx +657 -0
  557. package/src/ktr/template/bilibili/comment/components/types.ts +26 -0
  558. package/src/ktr/template/bilibili/comment/index.tsx +11 -0
  559. package/src/ktr/template/bilibili/components/Icons.tsx +209 -0
  560. package/src/ktr/template/bilibili/components/shared.tsx +162 -0
  561. package/src/ktr/template/bilibili/components/types.ts +388 -0
  562. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/DYNAMIC_TYPE_ARTICLE.tsx +251 -0
  563. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/types.ts +67 -0
  564. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/index.tsx +12 -0
  565. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/DYNAMIC_TYPE_AV.tsx +238 -0
  566. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/types.ts +39 -0
  567. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/index.tsx +12 -0
  568. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/DYNAMIC_TYPE_DRAW.tsx +205 -0
  569. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/types.ts +20 -0
  570. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/index.tsx +11 -0
  571. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/DYNAMIC_TYPE_FORWARD.tsx +348 -0
  572. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/types.ts +16 -0
  573. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/index.tsx +12 -0
  574. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/DYNAMIC_TYPE_LIVE_RCMD.tsx +264 -0
  575. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/types.ts +32 -0
  576. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/index.tsx +12 -0
  577. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/DYNAMIC_TYPE_WORD.tsx +68 -0
  578. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/types.ts +14 -0
  579. package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/index.tsx +12 -0
  580. package/src/ktr/template/bilibili/dynamic/components/AdditionalCard.tsx +165 -0
  581. package/src/ktr/template/bilibili/dynamic/components/CommonComponents.tsx +142 -0
  582. package/src/ktr/template/bilibili/dynamic/types.ts +382 -0
  583. package/src/ktr/template/bilibili/qrcodeImg/components/qrcodeImg.tsx +179 -0
  584. package/src/ktr/template/bilibili/qrcodeImg/components/types.ts +9 -0
  585. package/src/ktr/template/bilibili/qrcodeImg/index.tsx +12 -0
  586. package/src/ktr/template/bilibili/userlist/components/UserList.tsx +246 -0
  587. package/src/ktr/template/bilibili/userlist/components/types.ts +35 -0
  588. package/src/ktr/template/bilibili/userlist/index.tsx +11 -0
  589. package/src/ktr/template/bilibili/videoInfo/components/types.ts +28 -0
  590. package/src/ktr/template/bilibili/videoInfo/components/videoInfo.tsx +217 -0
  591. package/src/ktr/template/bilibili/videoInfo/index.tsx +12 -0
  592. package/src/ktr/template/components/AmbientCover.tsx +50 -0
  593. package/src/ktr/template/components/DefaultLayout.tsx +166 -0
  594. package/src/ktr/template/components/GlowImage.tsx +572 -0
  595. package/src/ktr/template/components/QRCodeWithAvatar.tsx +52 -0
  596. package/src/ktr/template/components/RolldownLogo.tsx +110 -0
  597. package/src/ktr/template/components/ViteLogo.tsx +134 -0
  598. package/src/ktr/template/components/iconRegistry.ts +294 -0
  599. package/src/ktr/template/douyin/article-work/components/ArticleWork.tsx +301 -0
  600. package/src/ktr/template/douyin/article-work/components/types.ts +53 -0
  601. package/src/ktr/template/douyin/article-work/index.tsx +12 -0
  602. package/src/ktr/template/douyin/comment/components/Comment.tsx +612 -0
  603. package/src/ktr/template/douyin/comment/components/types.ts +85 -0
  604. package/src/ktr/template/douyin/comment/index.tsx +11 -0
  605. package/src/ktr/template/douyin/components/Icons.tsx +75 -0
  606. package/src/ktr/template/douyin/components/types.ts +83 -0
  607. package/src/ktr/template/douyin/dynamic/components/Dynamic.tsx +283 -0
  608. package/src/ktr/template/douyin/dynamic/components/types.ts +46 -0
  609. package/src/ktr/template/douyin/dynamic/index.tsx +11 -0
  610. package/src/ktr/template/douyin/favorite-list/components/FavoriteList.tsx +201 -0
  611. package/src/ktr/template/douyin/favorite-list/components/types.ts +38 -0
  612. package/src/ktr/template/douyin/favorite-list/index.tsx +12 -0
  613. package/src/ktr/template/douyin/image-work/components/ImageWork.tsx +437 -0
  614. package/src/ktr/template/douyin/image-work/components/types.ts +78 -0
  615. package/src/ktr/template/douyin/image-work/index.tsx +11 -0
  616. package/src/ktr/template/douyin/live/components/Live.tsx +229 -0
  617. package/src/ktr/template/douyin/live/components/types.ts +47 -0
  618. package/src/ktr/template/douyin/live/index.tsx +11 -0
  619. package/src/ktr/template/douyin/musicinfo/components/MusicInfo.tsx +187 -0
  620. package/src/ktr/template/douyin/musicinfo/components/types.ts +31 -0
  621. package/src/ktr/template/douyin/musicinfo/index.tsx +11 -0
  622. package/src/ktr/template/douyin/qrcodeImg/components/qrcodeImg.tsx +170 -0
  623. package/src/ktr/template/douyin/qrcodeImg/components/types.ts +9 -0
  624. package/src/ktr/template/douyin/qrcodeImg/index.tsx +9 -0
  625. package/src/ktr/template/douyin/recommend-list/components/RecommendList.tsx +201 -0
  626. package/src/ktr/template/douyin/recommend-list/components/types.ts +37 -0
  627. package/src/ktr/template/douyin/recommend-list/index.tsx +12 -0
  628. package/src/ktr/template/douyin/user_profile/components/UserVideoList.tsx +225 -0
  629. package/src/ktr/template/douyin/user_profile/components/types.ts +71 -0
  630. package/src/ktr/template/douyin/user_profile/index.tsx +9 -0
  631. package/src/ktr/template/douyin/userlist/components/UserList.tsx +229 -0
  632. package/src/ktr/template/douyin/userlist/components/types.ts +35 -0
  633. package/src/ktr/template/douyin/userlist/index.tsx +11 -0
  634. package/src/ktr/template/douyin/video-work/components/VideoWork.tsx +398 -0
  635. package/src/ktr/template/douyin/video-work/components/types.ts +80 -0
  636. package/src/ktr/template/douyin/video-work/index.tsx +11 -0
  637. package/src/ktr/template/font.css +6071 -0
  638. package/src/ktr/template/global.d.ts +69 -0
  639. package/src/ktr/template/kuaishou/comment/components/Comment.tsx +207 -0
  640. package/src/ktr/template/kuaishou/comment/components/types.ts +47 -0
  641. package/src/ktr/template/kuaishou/comment/index.tsx +11 -0
  642. package/src/ktr/template/other/changelog/components/changelog.tsx +411 -0
  643. package/src/ktr/template/other/changelog/components/types.ts +21 -0
  644. package/src/ktr/template/other/changelog/index.tsx +9 -0
  645. package/src/ktr/template/other/handlerError/components/errorTitles.ts +224 -0
  646. package/src/ktr/template/other/handlerError/components/handlerError.tsx +1076 -0
  647. package/src/ktr/template/other/handlerError/components/stackHighlight.ts +127 -0
  648. package/src/ktr/template/other/handlerError/components/types.ts +124 -0
  649. package/src/ktr/template/other/handlerError/index.tsx +9 -0
  650. package/src/ktr/template/other/help/components/Help.tsx +249 -0
  651. package/src/ktr/template/other/help/components/types.ts +47 -0
  652. package/src/ktr/template/other/help/index.tsx +9 -0
  653. package/src/ktr/template/other/live-photo-tip/components/LivePhotoTip.tsx +295 -0
  654. package/src/ktr/template/other/live-photo-tip/components/types.ts +8 -0
  655. package/src/ktr/template/other/live-photo-tip/index.tsx +9 -0
  656. package/src/ktr/template/other/qrlogin/components/qrlogin.tsx +295 -0
  657. package/src/ktr/template/other/qrlogin/components/types.ts +11 -0
  658. package/src/ktr/template/other/qrlogin/index.tsx +9 -0
  659. package/src/ktr/template/other/runtime/components/runtime.tsx +347 -0
  660. package/src/ktr/template/other/runtime/components/types.ts +75 -0
  661. package/src/ktr/template/other/runtime/index.tsx +9 -0
  662. package/src/ktr/template/other/version_warning/components/VersionWarning.tsx +392 -0
  663. package/src/ktr/template/other/version_warning/components/types.ts +8 -0
  664. package/src/ktr/template/other/version_warning/index.tsx +9 -0
  665. package/src/ktr/template/statistics/components/BoxplotGuide.tsx +57 -0
  666. package/src/ktr/template/statistics/components/ChartCard.tsx +40 -0
  667. package/src/ktr/template/statistics/components/KpiCard.tsx +39 -0
  668. package/src/ktr/template/statistics/components/PlatformTable.tsx +83 -0
  669. package/src/ktr/template/statistics/components/RankList.tsx +127 -0
  670. package/src/ktr/template/statistics/components/SSRChart.tsx +41 -0
  671. package/src/ktr/template/statistics/components/chartPresets.ts +700 -0
  672. package/src/ktr/template/statistics/components/chartTheme.ts +234 -0
  673. package/src/ktr/template/statistics/components/format.ts +33 -0
  674. package/src/ktr/template/statistics/components/types.ts +92 -0
  675. package/src/ktr/template/statistics/global/components/GlobalStatistics.tsx +597 -0
  676. package/src/ktr/template/statistics/global/components/types.ts +138 -0
  677. package/src/ktr/template/statistics/global/index.tsx +9 -0
  678. package/src/ktr/template/statistics/group/components/GroupStatistics.tsx +468 -0
  679. package/src/ktr/template/statistics/group/components/types.ts +113 -0
  680. package/src/ktr/template/statistics/group/index.tsx +9 -0
  681. package/src/ktr/template/style.css +65 -0
  682. package/src/ktr/template/types/ctx.ts +42 -0
  683. package/src/ktr/template/xiaohongshu/comment/components/Comment.tsx +264 -0
  684. package/src/ktr/template/xiaohongshu/comment/components/types.ts +119 -0
  685. package/src/ktr/template/xiaohongshu/comment/index.tsx +9 -0
  686. package/src/ktr/template/xiaohongshu/noteInfo/components/noteInfo.tsx +271 -0
  687. package/src/ktr/template/xiaohongshu/noteInfo/components/types.ts +66 -0
  688. package/src/ktr/template/xiaohongshu/noteInfo/index.tsx +9 -0
  689. package/src/ktr/utils/QRCodeAvatar.ts +102 -0
  690. package/src/ktr/utils/QRcode.ts +65 -0
  691. package/src/ktr/utils/avatarProxy.ts +42 -0
  692. package/src/ktr/utils/cn.ts +6 -0
  693. package/src/ktr/utils/theme.ts +10 -0
  694. package/src/qqFields.json +75 -0
  695. package/src/qqOptions.ts +64 -0
  696. package/src/richtext/index.ts +9 -0
  697. package/src/richtext/parse/index.ts +271 -0
  698. package/src/richtext/react/cn.ts +6 -0
  699. package/src/richtext/react/index.tsx +582 -0
  700. package/src/richtext/types.ts +320 -0
  701. package/src/schema.ts +372 -0
  702. package/src/webui.ts +352 -0
@@ -0,0 +1,574 @@
1
+ /**
2
+ * 渲染模块(Koishi 移植版)。
3
+ *
4
+ * 上游链路:ktr(\`@karinjs/template-react\`)把 \`template/**\` 打成 SSR 外壳 → 出 HTML → Karin render 截图。
5
+ * Koishi 侧不引入 ktr 的构建管线(需要 vite/tailwind 打包),改为:
6
+ * 1. 直接用 **react-dom/server** 对上游模板组件做 SSR(模板只依赖 \`{ data, ctx }\` props);
7
+ * 2. 内联上游构建产物 \`resources/template/style.css\`(tailwind 编译结果,省掉 tailwind 构建);
8
+ * 3. 用 koishi-plugin-puppeteer 截图。
9
+ * 路由不在注册表里、或 SSR 失败时,回退到内置通用信息卡片,保证解析结果始终有图。
10
+ */
11
+ import fs from 'node:fs'
12
+ import path from 'node:path'
13
+ import { pathToFileURL } from 'node:url'
14
+
15
+ import { getKoishiContext, logger, segment } from 'node-karin'
16
+ import { karinPathHtml } from 'node-karin/root'
17
+ import { PassThrough } from 'node:stream'
18
+
19
+ import React from 'react'
20
+ import { renderToPipeableStream } from 'react-dom/server'
21
+
22
+ import { importEsm } from '../../../../compat/esm'
23
+ import { loadTemplate } from '../../../../ktr/registry'
24
+ import { Root } from '@/module/utils'
25
+ import { Config } from '@/module/utils/Config'
26
+
27
+ import { resolveUseDarkTheme } from './coverTheme'
28
+
29
+ type ImageMetadata = { width?: number; height?: number }
30
+
31
+ /* ------------------------------------------------------------------ *
32
+ * 样式
33
+ * ------------------------------------------------------------------ */
34
+
35
+ let cachedCss: string | null = null
36
+
37
+ /** 读取并修正模板样式(上游产物里的字体指向 Karin 本地服务,换成相对路径,加载失败则回退系统字体) */
38
+ function loadTemplateCss (): string {
39
+ if (cachedCss !== null) return cachedCss
40
+ try {
41
+ const cssPath = path.resolve(Root.pluginPath, 'resources', 'template', 'style.css')
42
+ const raw = fs.readFileSync(cssPath, 'utf8')
43
+ cachedCss = raw.replace(/url\("?http:\/\/localhost:\d+\/config\/commonResource\//g, 'url("./template-fonts/')
44
+ } catch (error: any) {
45
+ logger.debug('[Render] 读取模板样式失败,将使用无样式渲染: ' + String(error?.message ?? error))
46
+ cachedCss = ''
47
+ }
48
+ return cachedCss
49
+ }
50
+
51
+ /* ------------------------------------------------------------------ *
52
+ * 兜底卡片
53
+ * ------------------------------------------------------------------ */
54
+
55
+ const escapeHtml = (input: unknown): string =>
56
+ String(input ?? '')
57
+ .replace(/&/g, '&')
58
+ .replace(/</g, '&lt;')
59
+ .replace(/>/g, '&gt;')
60
+ .replace(/"/g, '&quot;')
61
+
62
+ /** 递归时不往里看的键(Koishi 的 ctx / 适配器实例等,读属性会触发「未注册」警告且毫无意义) */
63
+ const PICK_SKIP_KEYS = new Set([
64
+ 'ctx', 'app', 'root', 'scope', 'bot', 'session', 'event', 'logger', 'config',
65
+ '_events', '_eventsCount', 'adapter', 'http', 'internal', 'socket', 'parser',
66
+ 'target', 'parent', 'children', 'prototype', 'constructor'
67
+ ])
68
+
69
+ /** 只递归纯对象与数组:类实例(ctx / Bot / Session…)一律不深入 */
70
+ const isPlainSource = (value: any): boolean => {
71
+ if (Array.isArray(value)) return true
72
+ if (!value || typeof value !== 'object') return false
73
+ const proto = Object.getPrototypeOf(value)
74
+ return proto === Object.prototype || proto === null
75
+ }
76
+
77
+ const pickFirst = (source: any, keys: string[], depth = 3, seen: Set<any> = new Set()): any => {
78
+ if (!source || typeof source !== 'object' || depth < 0) return undefined
79
+ // 防环(对象互相引用时会无限递归)
80
+ if (seen.has(source)) return undefined
81
+ seen.add(source)
82
+ for (const key of keys) {
83
+ if (PICK_SKIP_KEYS.has(key)) continue
84
+ const value = source[key]
85
+ if (value !== undefined && value !== null && value !== '') return value
86
+ }
87
+ for (const [key, value] of Object.entries(source)) {
88
+ if (PICK_SKIP_KEYS.has(key)) continue
89
+ if (isPlainSource(value)) {
90
+ const found = pickFirst(value, keys, depth - 1, seen)
91
+ if (found !== undefined) return found
92
+ }
93
+ }
94
+ return undefined
95
+ }
96
+
97
+ const asText = (value: any): string => {
98
+ if (value === undefined || value === null) return ''
99
+ if (typeof value === 'string' || typeof value === 'number') return String(value)
100
+ if (Array.isArray(value)) {
101
+ const parts = value.map((item) => (item && typeof item === 'object' ? item.text ?? item.desc ?? '' : item)).filter(Boolean)
102
+ return parts.join('')
103
+ }
104
+ return String(value.text ?? value.desc ?? value.title ?? '')
105
+ }
106
+
107
+ const formatCount = (value: any): string => {
108
+ const num = Number(value)
109
+ if (!Number.isFinite(num)) return ''
110
+ if (num >= 100000000) return (num / 100000000).toFixed(1) + '亿'
111
+ if (num >= 10000) return (num / 10000).toFixed(1) + '万'
112
+ return String(num)
113
+ }
114
+
115
+ /** 从渲染数据里尽力提取通用卡片需要的字段 */
116
+ function extractCardData (data: any) {
117
+ const title = asText(pickFirst(data, ['title', 'desc', 'dynamicTitle', 'name']))
118
+ const author = asText(pickFirst(data, ['nickname', 'nick', 'author', 'name', 'uname']))
119
+ const avatar = asText(pickFirst(data, ['avatar', 'face', 'avatarUrl', 'head_url']))
120
+ const cover = asText(pickFirst(data, ['cover', 'coverUrl', 'originCover', 'pic', 'dynamicCover', 'image']))
121
+ const description = asText(pickFirst(data, ['desc', 'description', 'dynamicText', 'summary', 'content']))
122
+ const stats = [
123
+ ['点赞', pickFirst(data, ['digg_count', 'like', 'likes'])],
124
+ ['评论', pickFirst(data, ['comment_count', 'comments', 'reply'])],
125
+ ['收藏', pickFirst(data, ['collect_count', 'favorite', 'favorites'])],
126
+ ['分享', pickFirst(data, ['share_count', 'share', 'repost'])],
127
+ ['播放', pickFirst(data, ['play', 'view', 'views', 'playCount'])]
128
+ ]
129
+ .map(([label, value]) => [label as string, formatCount(value)] as const)
130
+ .filter(([, value]) => !!value)
131
+
132
+ return { title, author, avatar, cover, description, stats }
133
+ }
134
+
135
+ /** 兜底卡片:不依赖模板,直接用通用字段渲染一张信息卡 */
136
+ export function buildFallbackHtml (route: string, data: any, dark: boolean, scale: number): string {
137
+ const card = extractCardData(data)
138
+ const bg = dark ? '#101014' : '#f4f5f7'
139
+ const fg = dark ? '#f7f8fa' : '#1c1f23'
140
+ const sub = dark ? '#a7abb3' : '#6b7280'
141
+ const coverHtml = card.cover ? '<div class="cover" style="background-image:url(' + escapeHtml(card.cover) + ')"></div>' : ''
142
+ const statsHtml = card.stats.length
143
+ ? '<div class="stats">' + card.stats.map(([label, value]) => '<span><b>' + escapeHtml(value) + '</b>' + escapeHtml(label) + '</span>').join('') + '</div>'
144
+ : ''
145
+
146
+ return '<!DOCTYPE html>\n<html lang="zh-CN"><head><meta charset="utf-8"><style>\n' +
147
+ ' * { box-sizing: border-box; margin: 0; padding: 0; }\n' +
148
+ ' body { background: ' + bg + '; color: ' + fg + '; font-family: "Microsoft YaHei", "PingFang SC", system-ui, sans-serif; }\n' +
149
+ ' #container { width: 720px; padding: 20px; }\n' +
150
+ ' .card { border-radius: 18px; background: ' + (dark ? '#1b1c20' : '#ffffff') + '; overflow: hidden; box-shadow: 0 12px 32px rgba(0,0,0,.12); zoom: ' + scale + '; }\n' +
151
+ ' .cover { width: 100%; height: 380px; background-size: cover; background-position: center; background-color: #2a2c31; }\n' +
152
+ ' .body { padding: 20px 24px 26px; }\n' +
153
+ ' .route { font-size: 13px; color: ' + sub + '; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 10px; }\n' +
154
+ ' .title { font-size: 26px; font-weight: 700; line-height: 1.35; word-break: break-word; }\n' +
155
+ ' .author { display: flex; align-items: center; gap: 10px; margin-top: 14px; color: ' + sub + '; font-size: 16px; }\n' +
156
+ ' .author img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; }\n' +
157
+ ' .desc { margin-top: 16px; font-size: 16px; line-height: 1.7; color: ' + sub + '; white-space: pre-wrap; word-break: break-word; }\n' +
158
+ ' .stats { display: flex; gap: 26px; margin-top: 20px; font-size: 14px; color: ' + sub + '; }\n' +
159
+ ' .stats b { display: block; font-size: 20px; color: ' + fg + '; }\n' +
160
+ ' .footer { margin-top: 18px; font-size: 12px; color: ' + sub + '; }\n' +
161
+ '</style></head>\n<body><div id="container"><div class="card">\n' +
162
+ coverHtml +
163
+ '\n <div class="body">\n' +
164
+ ' <div class="route">' + escapeHtml(route) + '</div>\n' +
165
+ ' <div class="title">' + escapeHtml(card.title || '解析结果') + '</div>\n' +
166
+ (card.author
167
+ ? ' <div class="author">' + (card.avatar ? '<img src="' + escapeHtml(card.avatar) + '" />' : '') + '<span>' + escapeHtml(card.author) + '</span></div>\n'
168
+ : '') +
169
+ (card.description ? ' <div class="desc">' + escapeHtml(card.description.slice(0, 600)) + '</div>\n' : '') +
170
+ (statsHtml ? ' ' + statsHtml + '\n' : '') +
171
+ ' <div class="footer">koishi-plugin-kkk v' + escapeHtml(Root.pluginVersion) + ' · 模板渲染未启用,当前为内置通用卡片</div>\n' +
172
+ ' </div>\n</div></div></body></html>'
173
+ }
174
+
175
+ /* ------------------------------------------------------------------ *
176
+ * 模板 SSR
177
+ * ------------------------------------------------------------------ */
178
+
179
+ /**
180
+ * 页脚那个「框架 logo」。
181
+ *
182
+ * 上游 karin 是起 HTTP 服务把 `/image/frame-logo.png` 喂给浏览器的;
183
+ * Koishi 移植版渲染的是**本地 HTML 文件**,绝对路径 `/image/...` 会指向磁盘根目录,
184
+ * 于是卡片页脚只剩下一个裂图(alt 文字 `logo`)。这里直接内联成 data URI。
185
+ */
186
+ let frameLogoCache: string | null = null
187
+ export function resolveFrameLogo (): string {
188
+ if (frameLogoCache !== null) return frameLogoCache
189
+ /**
190
+ * 页脚 logo:用 **Koishi 自己的图标**(@koishijs/plugin-console 里那张,
191
+ * 也就是控制台左上角那个),而不是上游 karin 的 frame-logo。
192
+ */
193
+ const candidates = [
194
+ path.join(Root.pluginPath, '..', '@koishijs', 'plugin-console', 'dist', 'logo.png'),
195
+ path.join(Root.pluginPath, '..', '@koishijs', 'client', 'app', 'assets', 'logo.png'),
196
+ path.join(Root.pluginPath, 'resources', 'image', 'frame-logo.png')
197
+ ]
198
+ for (const filePath of candidates) {
199
+ try {
200
+ if (!fs.existsSync(filePath)) continue
201
+ frameLogoCache = 'data:image/png;base64,' + fs.readFileSync(filePath).toString('base64')
202
+ return frameLogoCache
203
+ } catch { /* 换下一个 */ }
204
+ }
205
+ frameLogoCache = ''
206
+ return frameLogoCache
207
+ }
208
+
209
+ /** 组装模板上下文(与上游 Render/index.ts 一致) */
210
+ export function buildPosterContext (dark: boolean) {
211
+ return {
212
+ scale: Math.min(2, Math.max(0.5, Number(Config.app?.renderScale ?? 100) / 100)),
213
+ theme: { mode: dark ? ('dark' as const) : ('light' as const) },
214
+ version: {
215
+ plugin: 'koishi-plugin',
216
+ pluginName: 'kkk',
217
+ pluginVersion: Root.pluginVersion,
218
+ releaseType: /^\d+\.\d+\.\d+$/.test(Root.pluginVersion) ? ('Stable' as const) : ('Preview' as const),
219
+ poweredBy: 'Koishi',
220
+ frameworkVersion: Root.karinVersion,
221
+ // 页脚 logo(data URI,见 resolveFrameLogo)
222
+ frameLogo: resolveFrameLogo(),
223
+ hasUpdate: false
224
+ },
225
+ ambientCover: {
226
+ coverOpacity: (Config.app as any)?.ambientCover?.coverOpacity,
227
+ overlayEdgeOpacity: (Config.app as any)?.ambientCover?.overlayEdgeOpacity,
228
+ overlayMiddleOpacity: (Config.app as any)?.ambientCover?.overlayMiddleOpacity
229
+ }
230
+ }
231
+ }
232
+
233
+ /** 二维码生成器只加载一次(ESM-only 包,只能在 SSR 前异步 import) */
234
+ let qrcodeReady: Promise<void> | null = null
235
+
236
+ async function ensureQrcodeGenerator (): Promise<void> {
237
+ if (!qrcodeReady) {
238
+ qrcodeReady = (async () => {
239
+ const [qrcode, utils] = await Promise.all([
240
+ importEsm<any>('@ikenxuan/qrcode'),
241
+ Promise.resolve(require('../../../../ktr/utils/QRcode'))
242
+ ])
243
+ if (typeof qrcode.generateSync === 'function') {
244
+ (utils as any).setQrcodeGenerator(qrcode.generateSync)
245
+ }
246
+ })().catch((error: any) => {
247
+ logger.debug('[Render] 二维码生成器加载失败: ' + String(error?.message ?? error))
248
+ })
249
+ }
250
+ return qrcodeReady
251
+ }
252
+
253
+ /**
254
+ * 把 React 元素渲染成 HTML 字符串。
255
+ *
256
+ * 用流式 API 而不是同步的 renderToStaticMarkup:部分上游模板会 suspend
257
+ * (React 19 的 `use()` / 异步资源),同步渲染会直接抛
258
+ * "A component suspended while responding to synchronous input" 而整块回退成通用卡片。
259
+ * 流式渲染会等挂起的内容就绪(onAllReady)再产出完整 HTML。
260
+ */
261
+ async function renderToHtml (element: React.ReactElement, timeoutMs = 15000): Promise<string> {
262
+ return await new Promise<string>((resolve, reject) => {
263
+ let html = ''
264
+ const stream = new PassThrough()
265
+ stream.on('data', (chunk) => { html += chunk.toString() })
266
+ stream.on('end', () => resolve(html))
267
+ stream.on('error', reject)
268
+
269
+ let settled = false
270
+ const fail = (error: unknown) => {
271
+ if (settled) return
272
+ settled = true
273
+ reject(error instanceof Error ? error : new Error(String(error)))
274
+ }
275
+
276
+ const { pipe, abort } = renderToPipeableStream(element, {
277
+ onError: (error) => logger.debug('[Render] SSR 渲染期错误: ' + String((error as any)?.message ?? error)),
278
+ onShellError: fail,
279
+ onAllReady: () => {
280
+ if (settled) return
281
+ settled = true
282
+ pipe(stream)
283
+ }
284
+ })
285
+
286
+ setTimeout(() => {
287
+ if (settled) return
288
+ abort()
289
+ fail(new Error('SSR 渲染超时'))
290
+ }, timeoutMs).unref?.()
291
+ })
292
+ }
293
+
294
+ /** 用上游模板做 SSR;路由不支持或失败时返回 null */
295
+ export async function renderTemplateHtml (route: string, data: any, dark: boolean): Promise<string | null> {
296
+ await ensureQrcodeGenerator()
297
+ let template: any
298
+ try {
299
+ template = await loadTemplate(route)
300
+ } catch (error: any) {
301
+ logger.debug('[Render] 模板 ' + route + ' 加载失败(可能缺少依赖): ' + String(error?.message ?? error))
302
+ return null
303
+ }
304
+ if (!template?.component) return null
305
+ if (typeof template.validate === 'function' && !template.validate(data)) {
306
+ logger.debug('[Render] 模板 ' + route + ' 的数据校验未通过,回退通用卡片')
307
+ return null
308
+ }
309
+
310
+ try {
311
+ const ctx = buildPosterContext(dark)
312
+ const element = React.createElement(template.component, { data, ctx })
313
+ const body = await renderToHtml(React.createElement('div', { id: 'container' }, element))
314
+ const css = loadTemplateCss()
315
+ const themeClass = dark ? 'dark' : ''
316
+ /**
317
+ * 截图专用的覆盖样式:
318
+ * - 页面不要外边距/内边距,也不要白色背景(否则卡片圆角外面会透出白边、底部会多一条空白);
319
+ * - 卡片**不要圆角** —— 圆角会让四个角露出页面底色,看起来就是卡片缺了一角。
320
+ * 只压掉最外层的圆角,内部小元素(头像、标签)保持原样。
321
+ */
322
+ const shotCss = 'html,body{margin:0!important;padding:0!important;background:transparent!important;overflow:hidden!important}'
323
+ + '#container{margin:0!important;padding:0!important;border-radius:0!important}'
324
+ + '#container>div,#container>div>div{border-radius:0!important}'
325
+ + 'body{display:inline-block}'
326
+ return '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><style>' + css + shotCss + '</style></head>'
327
+ + '<body class="' + themeClass + '">' + body + '</body></html>'
328
+ } catch (error: any) {
329
+ logger.warn('[Render] 模板 ' + route + ' SSR 失败,回退通用卡片: ' + String(error?.message ?? error))
330
+ return null
331
+ }
332
+ }
333
+
334
+ /* ------------------------------------------------------------------ *
335
+ * 截图
336
+ * ------------------------------------------------------------------ */
337
+
338
+ /**
339
+ * 用 koishi-plugin-puppeteer 对一个 HTML 文件截图,返回 base64。
340
+ *
341
+ * `scale` 会作为 deviceScaleFactor(配置项 `app.renderScale`,100 = 1x,200 = 2x):
342
+ * 不设的话高分辨率卡片会被截成 1x,群里看着又小又糊;顺带把视口撑到整张卡片大小,
343
+ * 否则比视口高的卡片会被裁掉一截(表现就是「截图只有一部分」)。
344
+ */
345
+ async function screenshot (htmlPath: string, selector: string, timeout: number, scale = 1, format: 'png' | 'jpeg' = 'jpeg'): Promise<string> {
346
+ const puppeteer: any = (getKoishiContext() as any)?.puppeteer
347
+ if (!puppeteer) throw new Error('未安装 koishi-plugin-puppeteer,无法渲染图片')
348
+
349
+ // 卡片是给手机看的:**低于 2x 会明显发虚**,所以下限锁 2(上限 3)。
350
+ // 想更大更清晰就调 `app.renderScale`(100 → 2x,150 → 3x),配 100 时保持 2x 不出错。
351
+ const requested = Number.isFinite(scale) && scale > 0 ? scale * 2 : 2
352
+ let deviceScaleFactor = Math.min(3, Math.max(2, requested))
353
+ // 安全阀:超大页面降一档缩放,避免单次渲染吃掉几 GB 内存(曾经把实例 OOM 崩掉)
354
+ try {
355
+ if (fs.statSync(htmlPath).size > 6 * 1024 * 1024) deviceScaleFactor = Math.max(1, deviceScaleFactor - 1)
356
+ } catch { /* 忽略 */ }
357
+
358
+ /**
359
+ * 视口用卡片的**设计宽度**(模板都按 1440 排版),高度按内容量一次。
360
+ *
361
+ * 之前试过「量出内容自然尺寸再把视口撑到那么大」:Chrome 会为此分配超大帧缓冲,
362
+ * 单次渲染把 node 堆吃到 3GB+ 并直接 OOM 崩掉实例。现在宽度固定、高度只增不减,
363
+ * 并且不再用 `captureBeyondViewport`(它在大页面上内存表现同样很差)。
364
+ */
365
+ const DESIGN_WIDTH = 1440
366
+
367
+ /** 在给定页面上完成「导航 → 量高度 → 截图」,返回 base64 */
368
+ const capture = async (page: any): Promise<string> => {
369
+ if (page.setViewport) {
370
+ // 宽度就用卡片设计宽度:多给 24px 会多出一条白边(用户看到的「右边超出去一点」)
371
+ await page.setViewport({ width: DESIGN_WIDTH, height: 900, deviceScaleFactor })
372
+ }
373
+ await page.goto(pathToFileURL(htmlPath).href, { waitUntil: 'networkidle0', timeout })
374
+
375
+ const metrics = await page.evaluate((sel: string) => {
376
+ const node = document.querySelector(sel) || document.body
377
+ const rect = node.getBoundingClientRect ? node.getBoundingClientRect() : null
378
+ return {
379
+ // 用**元素自己的盒子**而不是整页 scrollHeight:整页量出来的高度会带上 body 边距,
380
+ // 截图底部就多一条空白
381
+ width: Math.ceil(rect?.width || node.scrollWidth || document.body.scrollWidth || 0),
382
+ height: Math.ceil(rect?.height || node.scrollHeight || document.body.scrollHeight || 0)
383
+ }
384
+ }, selector).catch(() => ({ width: 0, height: 0 }))
385
+
386
+ if (page.setViewport) {
387
+ /**
388
+ * 宽度按**内容实测**来:正常模板是 1440 宽,而兜底卡片只有 720 ——
389
+ * 固定 1440 会让兜底卡片右边留一大片白。这里两者都能贴合,
390
+ * 同时高度按内容撑够,避免长卡片被裁。
391
+ */
392
+ const width = Math.min(Math.max(metrics.width || DESIGN_WIDTH, 320), 1600)
393
+ /**
394
+ * 高度按卡片实际盒子取整(不留容差)。
395
+ * 另外把 **页面底色设成卡片自己的底色** —— 视口哪怕只比卡片高一两个像素,
396
+ * JPEG 里那一条也会是白色页面底,看起来就是「下面有个小白边」。
397
+ */
398
+ const height = Math.min(Math.max(Math.round(metrics.height), 200), 20000)
399
+ await page.setViewport({ width, height, deviceScaleFactor })
400
+ await page.evaluate((sel: string) => {
401
+ try {
402
+ const node = document.querySelector(sel) as HTMLElement | null
403
+ if (!node) return
404
+ const card = (node.firstElementChild as HTMLElement) || node
405
+ const bg = getComputedStyle(card).backgroundColor
406
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent') {
407
+ document.documentElement.style.background = bg
408
+ document.body.style.background = bg
409
+ }
410
+ } catch { /* 取不到就算了 */ }
411
+ }, selector).catch(() => undefined)
412
+ await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))
413
+ }
414
+
415
+ /**
416
+ * 用「卡片元素自己的范围」裁切,而不是整屏截图。
417
+ *
418
+ * 视口宽度比卡片设计宽度多了 24px(给滚动条留余量),整屏截图会在右边多出一条白边,
419
+ * 用户看到的就是「卡片右边超出了/有白边」。clip 只取 #container 的盒子,正好贴合卡片。
420
+ */
421
+ const handle = (await page.$(selector)) ?? (await page.$('body'))
422
+ const clip = handle ? await handle.boundingBox() : null
423
+
424
+ /**
425
+ * 默认输出 JPEG:2x 的卡片 PNG 动辄十几 MB(带照片背景时尤其夸张),
426
+ * 超过 QQ 的图片上传限制就会被**当成文件**发出去(用户看到的是「[文件]」而不是图片)。
427
+ * 二维码那类需要像素级清晰的场景仍用 PNG(见 Render 里的判断)。
428
+ */
429
+ const buffer = await page.screenshot({
430
+ omitBackground: format === 'png',
431
+ type: format,
432
+ quality: format === 'jpeg' ? 92 : undefined
433
+ } as any)
434
+ return buffer.toString('base64')
435
+ }
436
+
437
+ /**
438
+ * 自己开一个页面截图,**不要走插件的 render() 包装**。
439
+ *
440
+ * 实测:1440x4895 的运行环境诊断卡片,走 `puppeteer.render()` 时仅导航一步
441
+ * 就让 node 堆从 206MB 涨到 1407MB(首次渲染累计 +3GB,直接把宿主 OOM 崩掉);
442
+ * 自己 newPage + goto + 截图只涨几十 MB。所以这里优先直连浏览器,用完关掉页面。
443
+ */
444
+ const newPage = async (): Promise<any> => {
445
+ try {
446
+ if (typeof puppeteer.page === 'function') return await puppeteer.page()
447
+ if (puppeteer.browser && typeof puppeteer.browser.newPage === 'function') return await puppeteer.browser.newPage()
448
+ } catch (error: any) {
449
+ logger.debug('[Render] 直连浏览器失败,退回插件 render(): ' + (error?.message ?? error))
450
+ }
451
+ return null
452
+ }
453
+
454
+ const page = await newPage()
455
+ if (page) {
456
+ try {
457
+ return await capture(page)
458
+ } finally {
459
+ try {
460
+ await page.close()
461
+ } catch { /* 页面可能已被插件关掉,忽略 */ }
462
+ }
463
+ }
464
+
465
+ logger.debug('[Render] 插件未暴露 page/browser,退回 puppeteer.render()')
466
+ return await puppeteer.render('', async (page: any) => capture(page))
467
+ }
468
+
469
+ export const getImageMetadata = (buffer: Buffer): ImageMetadata => {
470
+ if (buffer.length >= 24 && buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
471
+ return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) }
472
+ }
473
+ // JPEG:QQ 的 markdown 图片要写死「#宽px #高px」,所以必须能读出来
474
+ if (buffer.length > 4 && buffer[0] === 0xff && buffer[1] === 0xd8) {
475
+ let offset = 2
476
+ while (offset + 9 < buffer.length) {
477
+ if (buffer[offset] !== 0xff) { offset++; continue }
478
+ const marker = buffer[offset + 1]
479
+ // SOF0..SOF15(跳过 DHT/DAC 等非 SOF 标记)
480
+ if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
481
+ return { height: buffer.readUInt16BE(offset + 5), width: buffer.readUInt16BE(offset + 7) }
482
+ }
483
+ const length = buffer.readUInt16BE(offset + 2)
484
+ if (length <= 0) break
485
+ offset += 2 + length
486
+ }
487
+ }
488
+ return {}
489
+ }
490
+
491
+ /**
492
+ * 渲染函数(上游签名):\`Render(event, '平台/模板', data)\` → 图片消息段数组。
493
+ */
494
+ /**
495
+ * 把模板里的第三方图床代理换回原图地址。
496
+ *
497
+ * 上游模板给头像/装扮这类图套了一层 images.weserv.nl/?url=<encoded>(本意是绕开 B站的防盗链),
498
+ * 但那个代理在有些网络下**根本连不上** —— 表现就是卡片里 UP 主头像是个裂图。
499
+ * 实测 B站原图直连(i0/i2.hdslb.com)不带 Referer 也是 200,所以这里直接还原成原地址。
500
+ */
501
+ /**
502
+ * 把模板里 `src="/image/xxx"` 这种**本地绝对路径**的图片内联成 data URI。
503
+ *
504
+ * 上游是 karin 的 HTTP 服务在提供 `/image/*`(插件 resources/image 目录);Koishi 移植版渲染的是
505
+ * 本地 HTML 文件,绝对路径会指向磁盘根目录 → 裂图。评论卡片里的 B站 logo、大会员标记、等级图标
506
+ * 就是这么丢的(页脚 logo 之前也踩过同一个坑)。
507
+ */
508
+ function inlineLocalImages (html: string): string {
509
+ const imageRoot = path.join(Root.pluginPath, 'resources')
510
+ const mimeOf = (file: string): string => {
511
+ const ext = path.extname(file).toLowerCase()
512
+ if (ext === '.svg') return 'image/svg+xml'
513
+ if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg'
514
+ if (ext === '.webp') return 'image/webp'
515
+ if (ext === '.gif') return 'image/gif'
516
+ return 'image/png'
517
+ }
518
+ return html.replace(/(src=")(\/image\/[^"]+)(")/g, (all: string, head: string, url: string, tail: string) => {
519
+ try {
520
+ const relative = decodeURIComponent(url.replace(/^\/+/, ''))
521
+ const full = path.join(imageRoot, relative)
522
+ if (!full.startsWith(path.resolve(imageRoot))) return all
523
+ if (!fs.existsSync(full)) return all
524
+ const buffer = fs.readFileSync(full)
525
+ const mime = mimeOf(full)
526
+ // svg 是文本,用 utf8 base64 也一样能显示,这里统一 base64 省事
527
+ return head + 'data:' + mime + ';base64,' + buffer.toString('base64') + tail
528
+ } catch {
529
+ return all
530
+ }
531
+ })
532
+ }
533
+
534
+ function unwrapImageProxy (html: string): string {
535
+ return html.replace(/https:\/\/images\.weserv\.nl\/\?url=([^"'&\s]+)/gi, (all: string, encoded: string) => {
536
+ try {
537
+ const decoded = decodeURIComponent(encoded)
538
+ return /^https?:\/\//i.test(decoded) ? decoded : all
539
+ } catch {
540
+ return all
541
+ }
542
+ })
543
+ }
544
+
545
+ export const Render = async (_event: any, route: string, data: any): Promise<any[]> => {
546
+ const dark = await resolveUseDarkTheme(route, data).catch(() => false)
547
+ const scale = Math.min(2, Math.max(0.5, Number(Config.app?.renderScale ?? 100) / 100))
548
+
549
+ let html = await renderTemplateHtml(route, data, dark)
550
+ if (!html) {
551
+ html = buildFallbackHtml(route, data, dark, scale)
552
+ }
553
+ html = inlineLocalImages(unwrapImageProxy(html))
554
+
555
+ const htmlPath = path.resolve(karinPathHtml, Root.pluginName, route.replace(/[\\/]/g, '_') + '.html')
556
+ try {
557
+ fs.mkdirSync(path.dirname(htmlPath), { recursive: true })
558
+ fs.writeFileSync(htmlPath, html, 'utf8')
559
+ // 二维码:必须像素级清晰(JPEG 压缩会影响扫码),继续用 PNG;其它卡片用 JPEG 压体积
560
+ const isQrCode = /qrcode/i.test(route)
561
+ const format: 'png' | 'jpeg' = isQrCode ? 'png' : 'jpeg'
562
+ const base64 = await screenshot(htmlPath, '#container', Number(Config.app?.RenderWaitTime ?? 10) * 1000, scale, format)
563
+ const buffer = Buffer.from(base64, 'base64')
564
+ const meta = getImageMetadata(buffer)
565
+ logger.debug('[Render] ' + route + ' 渲染完成 ' + format + ' ' + (meta.width ?? '?') + 'x' + (meta.height ?? '?') + ' ' + Math.round(buffer.length / 1024) + 'KB')
566
+ // 用带 mime 的 data URL,适配器才知道按 JPEG 上传(base64:// 会被当成 PNG)
567
+ return [segment.image('data:image/' + format + ';base64,' + base64)]
568
+ } catch (error: any) {
569
+ logger.warn('[Render] ' + route + ' 渲染失败:' + (error?.message ?? error))
570
+ return []
571
+ }
572
+ }
573
+
574
+ export default Render