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,368 @@
1
+ import Client, {
2
+ type AmagiError as AmagiErrorContract,
3
+ type AmagiFailure,
4
+ type AmagiSuccess,
5
+ type SuccessBilibiliFetcher,
6
+ type SuccessDouyinFetcher,
7
+ type SuccessKuaishouFetcher,
8
+ type SuccessXiaohongshuFetcher,
9
+ // 包级 fetcher:抖音的 passport(扫码登录)只在它上面,client 上绑定过的精简版没有这些方法
10
+ douyinFetcher as amagiDouyinFetcher
11
+ } from '@ikenxuan/amagi'
12
+ import { logger } from 'node-karin'
13
+
14
+ import { Config } from './Config'
15
+ import { createBilibiliLoginSession, createDouyinLoginSession } from './loginSession'
16
+
17
+ /** v7 客户端实例 */
18
+ export type AmagiClient = ReturnType<typeof Client>
19
+
20
+ /** 四个平台的键,用来在类型层重建 client 形状 */
21
+ type PlatformKey = 'bilibili' | 'douyin' | 'kuaishou' | 'xiaohongshu'
22
+
23
+ /** 把一个平台模块的 `fetcher` 换成「只保留成功分支」的那份类型 */
24
+ type WithSuccessFetcher<M, F> = Omit<M, 'fetcher'> & { fetcher: F }
25
+
26
+ /**
27
+ * 失败必抛的 client:四个平台的 `fetcher` 换成成功信封形态,其余键原样。
28
+ *
29
+ * 运行时由 {@link throwOnFailure} 保证「失败一律抛」,类型上必须同步声明成成功分支 —— 否则
30
+ * 未收窄的 `AmagiResult` 让每一处 `.data` 都带 `| undefined`。`Success*Fetcher` 只能用 amagi
31
+ * 提供的那份:fetcher 方法是泛型签名,自己 infer 会按约束实例化类型参数,默认值丢失,`data` 退化成 `unknown`。
32
+ */
33
+ type ThrowingClient = Omit<AmagiClient, PlatformKey> & {
34
+ bilibili: WithSuccessFetcher<AmagiClient['bilibili'], SuccessBilibiliFetcher>
35
+ douyin: WithSuccessFetcher<AmagiClient['douyin'], SuccessDouyinFetcher>
36
+ kuaishou: WithSuccessFetcher<AmagiClient['kuaishou'], SuccessKuaishouFetcher>
37
+ xiaohongshu: WithSuccessFetcher<AmagiClient['xiaohongshu'], SuccessXiaohongshuFetcher>
38
+ }
39
+
40
+ /**
41
+ * 平台业务码。v7 的三种码各归各位:平台业务码 `error.platform.code`、HTTP 状态 `error.http.status`、
42
+ * amagi 自己的码 `error.code`(字符串)。按平台码分流的地方(B站 `-352` 风控、`-111` csrf 失效、
43
+ * `12061` 关闭评论区)要的是第一种;平台没给就退 HTTP 状态,再退 0。
44
+ */
45
+ const legacyCode = (error: AmagiErrorContract, envelope?: AmagiFailure): number => {
46
+ const platformCode = error.platform?.code
47
+ if (typeof platformCode === 'number') return platformCode
48
+ if (typeof platformCode === 'string' && platformCode.trim() !== '' && Number.isFinite(Number(platformCode))) {
49
+ return Number(platformCode)
50
+ }
51
+ // Koishi 移植补充:已发布的 amagi 6.6.0 失败信封是
52
+ // { success:false, error:{ errorDescription, requestType, requestUrl, responseCode }, message, code, data }
53
+ // —— 平台业务码在 error.responseCode / 顶层 code 上,没有 v7 的 kind/platform/http 字段。
54
+ const anyError = error as any
55
+ if (typeof anyError.responseCode === 'number') return anyError.responseCode
56
+ if (typeof (envelope as any)?.code === 'number') return (envelope as any).code
57
+ return error.http?.status ?? 0
58
+ }
59
+
60
+ /**
61
+ * 一行说清一次失败:`[大类/码] 平台原文 (端点 平台码 HTTP requestId attempts)`。
62
+ * `message` 会被 `logger.warn(...)` 直接拼进日志行,塞整个信封的转储会把日志顶成一屏 ANSI。
63
+ */
64
+ const describeFailure = (envelope: AmagiFailure): string => {
65
+ const error: any = envelope.error ?? {}
66
+ const meta: any = (envelope as any).meta
67
+ const kind = error.kind ?? (error.requestType ? 'api' : 'unknown')
68
+ const code = error.code ?? (envelope as any).code
69
+ const reason = error.errorDescription ?? error.message ?? (envelope as any).message ?? '未知错误'
70
+ const facts = [
71
+ error.requestType ?? meta?.endpoint,
72
+ error.requestUrl ? String(error.requestUrl).split('?')[0].slice(0, 80) : undefined,
73
+ error.responseCode === undefined ? undefined : `平台码 ${error.responseCode}`,
74
+ error.platform?.code === undefined ? undefined : `平台码 ${error.platform.code}`,
75
+ error.http?.status === undefined ? undefined : `HTTP ${error.http.status}`,
76
+ meta?.requestId === undefined ? undefined : `requestId=${meta.requestId}`,
77
+ meta?.attempts === undefined ? undefined : `attempts=${meta.attempts}`
78
+ ].filter((part): part is string => typeof part === 'string' && part !== '')
79
+
80
+ return `[${kind}/${code ?? '-'}] ${reason}${facts.length > 0 ? ` (${facts.join(' ')})` : ''}`
81
+ }
82
+
83
+ /** Amagi 失败异常:`message` 是一行摘要,结构化字段各有属性,完整信封在 {@link AmagiError.envelope} */
84
+ export class AmagiError extends Error {
85
+ /** 平台业务码,见 {@link legacyCode} */
86
+ code: number
87
+ /** 平台原始响应体。v7 只在 `debug: true` 下填 `error.raw`(B站风控要读里面的 `v_voucher`) */
88
+ data: any
89
+ /** v7 错误契约本体,等价于失败信封的 `error` */
90
+ rawError: AmagiErrorContract
91
+ /** 错误大类 */
92
+ kind: AmagiErrorContract['kind']
93
+ /** amagi 自己的字符串错误码 */
94
+ amagiCode: AmagiErrorContract['code']
95
+ /** 平台返回的原文,不带前缀与归因 */
96
+ reason: string
97
+ /** 是否值得重试 */
98
+ retryable: boolean
99
+ /** 真实发生的 HTTP 状态 */
100
+ httpStatus?: number
101
+ /** 参数校验的字段级错误,仅 `kind === 'validation'` 时有 */
102
+ issues?: AmagiErrorContract['issues']
103
+ /** 整条失败信封,`meta.requestId` / `attempts` / `durationMs` 在里面 */
104
+ envelope: AmagiFailure
105
+
106
+ constructor(envelope: AmagiFailure) {
107
+ const error: any = (envelope as any).error ?? {}
108
+ super(describeFailure(envelope))
109
+ this.name = 'AmagiError'
110
+ this.code = legacyCode(error, envelope)
111
+ // 平台原始响应体:v7 在 error.raw,6.6.0 在信封顶层 data(B站风控要读里面的 v_voucher)
112
+ this.data = error.raw ?? (envelope as any).data
113
+ this.rawError = error
114
+ this.kind = (error.kind ?? 'api') as AmagiErrorContract['kind']
115
+ this.amagiCode = error.code ?? (envelope as any).code
116
+ this.reason = error.errorDescription ?? error.message ?? (envelope as any).message ?? '未知错误'
117
+ this.retryable = typeof error.retryable === 'boolean' ? error.retryable : false
118
+ this.httpStatus = error.http?.status
119
+ this.issues = error.issues
120
+ this.envelope = envelope
121
+ }
122
+ }
123
+
124
+ /**
125
+ * 判断一个值是不是失败信封。**只认 `success`**:v7 信封顶层没有 `code`(三种码各归各位),
126
+ * 拿 `code` 当特征判别恒假 —— 失败被原样透传、`try/catch` 全失效、取数失败但流程继续,且零编译错误。
127
+ */
128
+ const isFailureEnvelope = (value: unknown): value is AmagiFailure => {
129
+ if (!value || typeof value !== 'object') return false
130
+ const envelope = value as Partial<AmagiFailure>
131
+ return envelope.success === false && typeof envelope.message === 'string' && !!envelope.error
132
+ }
133
+
134
+ /** 判断是不是 thenable,用来只包装异步方法 */
135
+ const isThenable = (value: unknown): value is PromiseLike<unknown> =>
136
+ !!value && (typeof value === 'object' || typeof value === 'function') && typeof (value as PromiseLike<unknown>).then === 'function'
137
+
138
+ /**
139
+ * 递归代理一个 fetcher 对象,把失败信封转成 `throw AmagiError`。返回类型与入参同形 ——
140
+ * 「只保留成功分支」是**类型层**由 {@link ThrowingClient} 声明的,这里只管运行时行为。
141
+ */
142
+ const throwOnFailure = <T extends object>(target: T): T =>
143
+ new Proxy(target, {
144
+ get(obj: any, prop: string | symbol) {
145
+ const value = obj[prop]
146
+
147
+ if (typeof value === 'function') {
148
+ return (...args: unknown[]) => {
149
+ const returned = value.apply(obj, args)
150
+ // 同步方法原样放行:包成 async 会把返回值套一层 Promise,破坏语义
151
+ if (!isThenable(returned)) return returned
152
+ return returned.then((result: unknown) => {
153
+ if (isFailureEnvelope(result)) throw new AmagiError(result)
154
+ return result
155
+ })
156
+ }
157
+ }
158
+
159
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
160
+ return throwOnFailure(value)
161
+ }
162
+
163
+ return value
164
+ }
165
+ })
166
+
167
+ /** 解析库基类 */
168
+ export class AmagiBase {
169
+ /**
170
+ * 原始 v7 客户端。
171
+ *
172
+ * `events` / `on` / `once` / `login` / `startServer` 从这里取 —— 它们不是 fetcher,
173
+ * 不该被「失败必抛」的代理碰(`on` 返回退订函数,包成 async 就废了)。
174
+ */
175
+ rawAmagi: AmagiClient
176
+ /** 解析库实例,四个平台的 fetcher 失败即抛 */
177
+ amagi: ThrowingClient
178
+ /** 当前客户端使用的配置快照,用于避免文件监听与显式重载造成重复初始化 */
179
+ private configSignature: string
180
+
181
+ constructor() {
182
+ const client = this.createAmagiClient()
183
+ this.rawAmagi = client
184
+ this.amagi = this.wrapAmagiClient(client)
185
+ this.configSignature = this.getConfigSignature()
186
+ }
187
+
188
+ /** 获取会影响 Amagi 运行状态的配置快照 */
189
+ private getConfigSignature = () => JSON.stringify(Config.amagi)
190
+
191
+ /** 创建解析库实例 */
192
+ protected createAmagiClient = (): AmagiClient => {
193
+ const amagi = Config.amagi
194
+ return Client({
195
+ cookies: amagi.cookies || {},
196
+ request: {
197
+ timeout: amagi.timeout,
198
+ headers: { 'User-Agent': amagi['User-Agent'] },
199
+ proxy: amagi.proxy?.switch ? amagi.proxy : false
200
+ },
201
+ // B站风控要读失败响应里的 `v_voucher`,而 v7 只在 debug 下才填 `error.raw`
202
+ // —— 不开的话 `AmagiError.data` 连键都没有,整条风控验证流程静默失效
203
+ debug: true
204
+ })
205
+ }
206
+
207
+ /**
208
+ * 重载配置 - 重新创建 Amagi Client 实例
209
+ * @returns 配置发生变化并完成重载时返回 true
210
+ */
211
+ reloadConfig() {
212
+ const nextConfigSignature = this.getConfigSignature()
213
+ if (nextConfigSignature === this.configSignature) {
214
+ logger.debug('[AmagiClient] 配置未变化,跳过重复重载')
215
+ return false
216
+ }
217
+
218
+ logger.debug('[AmagiClient] 检测到配置变化,正在重载...')
219
+ const client = this.createAmagiClient()
220
+ this.rawAmagi = client
221
+ this.amagi = this.wrapAmagiClient(client)
222
+ this.configSignature = nextConfigSignature
223
+ logger.debug('[AmagiClient] 配置重载完成')
224
+ return true
225
+ }
226
+
227
+ /** 只把四个平台的 fetcher 换成失败必抛形态,client 的其余键原样带过去 */
228
+ protected wrapAmagiClient = (client: AmagiClient): ThrowingClient =>
229
+ ({
230
+ ...client,
231
+ bilibili: { ...client.bilibili, fetcher: throwOnFailure(client.bilibili.fetcher) },
232
+ douyin: { ...client.douyin, fetcher: throwOnFailure(client.douyin.fetcher) },
233
+ kuaishou: { ...client.kuaishou, fetcher: throwOnFailure(client.kuaishou.fetcher) },
234
+ xiaohongshu: { ...client.xiaohongshu, fetcher: throwOnFailure(client.xiaohongshu.fetcher) }
235
+ }) as ThrowingClient
236
+ }
237
+
238
+ /** 软错误码:命中这些平台业务码时不抛异常,按失败信封返回。Bilibili `12061` - UP主已关闭评论区 */
239
+ export const SOFT_ERROR_CODES = {
240
+ BILIBILI_COMMENTS_DISABLED: 12061
241
+ } as const
242
+
243
+ /** 被 {@link softFetch} 放行的软失败:失败信封 + 压平后的平台业务码 */
244
+ export type SoftFailure = AmagiFailure & { code: number }
245
+
246
+ /** {@link softFetch} 的返回:要么成功信封,要么被放行的软失败 */
247
+ export type SoftResult<T> = AmagiSuccess<T> | SoftFailure
248
+
249
+ /**
250
+ * 调用 amagi fetcher 方法,允许特定平台业务码以失败信封返回而不是抛异常。
251
+ * @param fn - 经过代理包装的 amagi 方法调用
252
+ * @param allowedCodes - 不应抛出异常的平台业务码列表
253
+ * @returns 成功信封,或命中 `allowedCodes` 的软失败
254
+ */
255
+ export const softFetch = async <T>(fn: () => Promise<AmagiSuccess<T>>, allowedCodes: number[]): Promise<SoftResult<T>> => {
256
+ try {
257
+ return await fn()
258
+ } catch (err) {
259
+ if (err instanceof AmagiError && allowedCodes.includes(err.code)) {
260
+ return { ...err.envelope, code: err.code }
261
+ }
262
+ throw err
263
+ }
264
+ }
265
+
266
+ /**
267
+ * {@link softFetch} 的结果是否命中某个软错误码。
268
+ *
269
+ * 是类型守卫而不是 `result.code === x`:`code` 的类型是 `number` 不是字面量,
270
+ * 直接比较不会收窄联合,`else` 分支里的 `data` 仍是 `T | undefined`。
271
+ */
272
+ export const isSoftFailure = <T>(result: SoftResult<T>, ...codes: number[]): result is SoftFailure =>
273
+ !result.success && codes.includes(result.code)
274
+
275
+ const amagiClientInstance = new AmagiBase()
276
+
277
+ type AmagiReloadListener = () => void
278
+
279
+ /** 需要随 Amagi Client 一同刷新的运行时资源,例如已经挂载的 HTTP Router */
280
+ const amagiReloadListeners = new Set<AmagiReloadListener>()
281
+
282
+ /**
283
+ * 注册 Amagi 配置重载监听器。
284
+ * @param listener - 重载后要执行的回调
285
+ * @returns 注销当前监听器的函数
286
+ */
287
+ export const registerAmagiReloadListener = (listener: AmagiReloadListener) => {
288
+ amagiReloadListeners.add(listener)
289
+ return () => amagiReloadListeners.delete(listener)
290
+ }
291
+
292
+ /**
293
+ * 四个平台的「失败必抛」fetcher。
294
+ *
295
+ * 类型必须显式标注:不标的话 TS 要在声明产物里展开 fetcher 的结构,而响应类型桶只导出
296
+ * `BilibiliVideoInfoResponse` 这样的具名别名、不导出底层的 `*_V0`,展开时叫不出名来报 TS2883。
297
+ */
298
+ export let bilibiliFetcher: SuccessBilibiliFetcher = amagiClientInstance.amagi.bilibili.fetcher
299
+
300
+ export let douyinFetcher: SuccessDouyinFetcher = amagiClientInstance.amagi.douyin.fetcher
301
+
302
+ export let kuaishouFetcher: SuccessKuaishouFetcher = amagiClientInstance.amagi.kuaishou.fetcher
303
+
304
+ export let xiaohongshuFetcher: SuccessXiaohongshuFetcher = amagiClientInstance.amagi.xiaohongshu.fetcher
305
+
306
+ /**
307
+ * 原始 v7 客户端。扫码登录会话(`douyin.login` / `bilibili.login`)、实例级事件总线
308
+ * (`events` / `on` / `once`)与 `startServer` 都从这里取。
309
+ *
310
+ * 返回类型显式写成 {@link AmagiClient}:不写的话 TS 要在声明产物里展开这个结构,
311
+ * 会碰到 amagi 内部才叫得出名字的类型(如 `SearchNoteType`),报 TS4023。
312
+ */
313
+ /**
314
+ * 原始 v7 客户端,并补上 v7 才有的登录会话命名空间(`bilibili.login` / `douyin.login`)。
315
+ *
316
+ * 已发布的 amagi 6.6.0 的 client 上没有 `login`,上游登录代码会直接读崩,
317
+ * 因此这里用 Proxy 在读取 `bilibili` / `douyin` 时挂上本地实现的会话(见 ./loginSession)。
318
+ * 其余属性原样转发,并对函数做 bind,保证 `events` / `on` / `once` / `startServer` 行为不变。
319
+ */
320
+ const clientWrapperCache = new WeakMap<object, AmagiClient>()
321
+
322
+ export const getAmagiClient = (): AmagiClient => {
323
+ const raw = amagiClientInstance.rawAmagi as unknown as object
324
+ const cached = clientWrapperCache.get(raw)
325
+ if (cached) return cached
326
+
327
+ // 登录相关接口在**模块级 fetcher** 上(client.douyin.fetcher 是绑定过的精简版,没有 passport 方法);
328
+ // 这里用惰性代理,保证 reloadAmagiConfig() 重建 client 后拿到的仍是新实例。
329
+ const lazyFetcher = (get: () => any) => new Proxy({}, {
330
+ get: (_target, prop) => Reflect.get(get(), prop)
331
+ })
332
+
333
+ const wrapped = new Proxy(raw, {
334
+ get (target: any, prop: string | symbol) {
335
+ if (prop === 'bilibili') {
336
+ return { ...target.bilibili, login: createBilibiliLoginSession(lazyFetcher(() => bilibiliFetcher)) }
337
+ }
338
+ if (prop === 'douyin') {
339
+ return { ...target.douyin, login: createDouyinLoginSession(lazyFetcher(() => amagiDouyinFetcher)) }
340
+ }
341
+ const value = Reflect.get(target, prop, target)
342
+ return typeof value === 'function' ? value.bind(target) : value
343
+ }
344
+ }) as unknown as AmagiClient
345
+
346
+ clientWrapperCache.set(raw, wrapped)
347
+ return wrapped
348
+ }
349
+
350
+ export const reloadAmagiConfig = () => {
351
+ if (!amagiClientInstance.reloadConfig()) return false
352
+
353
+ // ESM 的 `export let` 是实时绑定:Client 重建后要同步替换,否则调用方还持有旧实例截取的引用
354
+ bilibiliFetcher = amagiClientInstance.amagi.bilibili.fetcher
355
+ douyinFetcher = amagiClientInstance.amagi.douyin.fetcher
356
+ kuaishouFetcher = amagiClientInstance.amagi.kuaishou.fetcher
357
+ xiaohongshuFetcher = amagiClientInstance.amagi.xiaohongshu.fetcher
358
+
359
+ for (const listener of amagiReloadListeners) {
360
+ try {
361
+ listener()
362
+ } catch (error) {
363
+ logger.error(`[AmagiClient] 运行时资源重载失败: ${error}`)
364
+ }
365
+ }
366
+
367
+ return true
368
+ }
@@ -0,0 +1,220 @@
1
+ import { deflateSync } from 'node:zlib'
2
+
3
+ import { drawDither } from '@outpacelabs/avatars'
4
+ import { logger } from 'node-karin'
5
+
6
+ /**
7
+ * 头像解析:优先用平台给的真实头像,拿不到就**实时生成**一个 dither 头像。
8
+ *
9
+ * 为什么需要它:部分适配器(比如 QQBot)有概率拿不到群头像,
10
+ * 榜单里就会空出一排位置,看起来像数据缺了。生成头像保证**每一行都一定有个头像**,
11
+ * 而且种子取自群号/用户ID,同一个人每次渲染出的图案是稳定的,不会刷一次变一个样。
12
+ *
13
+ * ## 为什么不用库自带的 `<GradientAvatar>` 组件
14
+ *
15
+ * 那是个 `<canvas>` + effect 绘制的 React 组件,库自己也标了 "Browser only"。
16
+ * 而海报产物是**纯静态 HTML、不含任何 `<script>`**(ktr 模板链路的硬约束),
17
+ * 浏览器加载后没有任何 JS 去执行那个 effect,canvas 里会是一片空白。
18
+ *
19
+ * 所以这里走库的底层 API `drawDither`:它只用到 `fillStyle` 和 `fillRect`
20
+ * (纯格子填充,没有渐变也没有合成),而库把绘制接口抽象成了 `GradientContext`
21
+ * —— 注释里写明「HTMLCanvasElement 和 OffscreenCanvas 的 2D context 都满足它」,
22
+ * 也就是说这是个公开的扩展点。用一个只记录矩形的小对象驱动它即可,
23
+ * 不必为了画个头像往 Node 里塞一整个 Canvas 实现。
24
+ */
25
+
26
+ /**
27
+ * 生成头像时喂给库的尺寸。
28
+ *
29
+ * **这个值直接决定图案的精细度**,不是"画多大":库内部按 `clamp(floor(size/3), 8, 64)` 算格数,
30
+ * 按 `log2(size/16)/log2(160/16)` 算用几种颜色 —— 传 40 只会得到 13×13 格、**3 种颜色**,
31
+ * 看起来就是一块粗糙的马赛克;160 才是它调色板拉满(4 色、53 格)的档位。
32
+ * 海报上头像是缩放着显示的,所以这里按满细节渲染,由显示端缩到 40~48px。
33
+ */
34
+ const RENDER_SIZE = 160
35
+ /** 拉取真实头像的超时 */
36
+ const FETCH_TIMEOUT_MS = 4000
37
+ /** 解析结果缓存条数上限,防止长时间运行把内存吃满 */
38
+ const CACHE_LIMIT = 200
39
+
40
+ /** 已解析过的头像(URL → data URI),避免反复打同一个 CDN */
41
+ const cache = new Map<string, string>()
42
+
43
+ /** 记录下来的一个填充矩形 */
44
+ interface RecordedRect {
45
+ x: number
46
+ y: number
47
+ w: number
48
+ h: number
49
+ /** `#RRGGBB` 或 `#RRGGBBAA` */
50
+ color: string
51
+ }
52
+
53
+ /**
54
+ * 跑一遍库的 dither 绘制,把方块录下来。
55
+ *
56
+ * 传进去的 context 只需满足 `GradientContext`:dither 路径只用 `fillStyle` + `fillRect`,
57
+ * `createRadialGradient` 是 mesh 路径才需要的,这里直接抛错 —— 真被调到说明库的用法变了。
58
+ */
59
+ const recordDither = (seed: string, size: number): RecordedRect[] => {
60
+ const rects: RecordedRect[] = []
61
+ const context = {
62
+ fillStyle: '#000000',
63
+ fillRect(x: number, y: number, w: number, h: number) {
64
+ rects.push({ x, y, w, h, color: context.fillStyle })
65
+ },
66
+ createRadialGradient(): never {
67
+ throw new Error('dither 路径不应调用 createRadialGradient')
68
+ }
69
+ }
70
+ drawDither(context as unknown as Parameters<typeof drawDither>[0], seed, size)
71
+ return rects
72
+ }
73
+
74
+ /** 把录到的方块画进一张 RGBA 位图,并裁成圆形 */
75
+ const rasterize = (rects: RecordedRect[], size: number): Buffer => {
76
+ // 起点全透明,圆形之外的部分自然留空
77
+ const rgba = Buffer.alloc(size * size * 4)
78
+ for (const rect of rects) {
79
+ const raw = rect.color.replace('#', '')
80
+ const r = parseInt(raw.slice(0, 2), 16)
81
+ const g = parseInt(raw.slice(2, 4), 16)
82
+ const b = parseInt(raw.slice(4, 6), 16)
83
+ const a = raw.length >= 8 ? parseInt(raw.slice(6, 8), 16) : 255
84
+ for (let y = rect.y; y < rect.y + rect.h && y < size; y++) {
85
+ for (let x = rect.x; x < rect.x + rect.w && x < size; x++) {
86
+ const offset = (y * size + x) * 4
87
+ rgba[offset] = r
88
+ rgba[offset + 1] = g
89
+ rgba[offset + 2] = b
90
+ rgba[offset + 3] = a
91
+ }
92
+ }
93
+ }
94
+
95
+ // 圆形裁切。边缘不做抗锯齿 —— 海报里头像是缩小显示的,降采样本身就会把边缘磨平
96
+ const half = size / 2
97
+ for (let y = 0; y < size; y++) {
98
+ for (let x = 0; x < size; x++) {
99
+ const dx = x + 0.5 - half
100
+ const dy = y + 0.5 - half
101
+ if (dx * dx + dy * dy > half * half) rgba[(y * size + x) * 4 + 3] = 0
102
+ }
103
+ }
104
+ return rgba
105
+ }
106
+
107
+ /** PNG 的 CRC32 查表 */
108
+ const CRC_TABLE = (() => {
109
+ const table = new Uint32Array(256)
110
+ for (let n = 0; n < 256; n++) {
111
+ let c = n
112
+ for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
113
+ table[n] = c >>> 0
114
+ }
115
+ return table
116
+ })()
117
+
118
+ /** 算一段字节的 CRC32 */
119
+ const crc32 = (buffer: Buffer): number => {
120
+ let c = 0xffffffff
121
+ for (const byte of buffer) c = CRC_TABLE[(c ^ byte) & 0xff] ^ (c >>> 8)
122
+ return (c ^ 0xffffffff) >>> 0
123
+ }
124
+
125
+ /** 拼一个 PNG 数据块:长度 + 类型 + 内容 + CRC */
126
+ const pngChunk = (type: string, data: Buffer): Buffer => {
127
+ const length = Buffer.alloc(4)
128
+ length.writeUInt32BE(data.length)
129
+ const typeBuffer = Buffer.from(type, 'ascii')
130
+ const crc = Buffer.alloc(4)
131
+ crc.writeUInt32BE(crc32(Buffer.concat([typeBuffer, data])))
132
+ return Buffer.concat([length, typeBuffer, data, crc])
133
+ }
134
+
135
+ /**
136
+ * 把 RGBA 位图编码成 PNG。
137
+ *
138
+ * 手写而不引依赖:这里只需要最朴素的 8 位真彩 + 无滤波,格式本身很短,
139
+ * 压缩交给 Node 自带的 zlib。**是无损的** —— 不存在因为"图太大"而掉画质的问题。
140
+ */
141
+ const encodePng = (rgba: Buffer, size: number): Buffer => {
142
+ const header = Buffer.alloc(13)
143
+ header.writeUInt32BE(size, 0)
144
+ header.writeUInt32BE(size, 4)
145
+ header[8] = 8 // 位深
146
+ header[9] = 6 // 颜色类型:真彩 + alpha
147
+ // 剩下三个字节保持 0:deflate 压缩、自适应滤波、非隔行
148
+
149
+ // 每一行前面要加一个滤波类型字节(0 = 不滤波)
150
+ const stride = size * 4
151
+ const raw = Buffer.alloc((stride + 1) * size)
152
+ for (let y = 0; y < size; y++) {
153
+ raw[y * (stride + 1)] = 0
154
+ rgba.copy(raw, y * (stride + 1) + 1, y * stride, (y + 1) * stride)
155
+ }
156
+
157
+ return Buffer.concat([
158
+ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
159
+ pngChunk('IHDR', header),
160
+ pngChunk('IDAT', deflateSync(raw, { level: 9 })),
161
+ pngChunk('IEND', Buffer.alloc(0))
162
+ ])
163
+ }
164
+
165
+ /**
166
+ * 生成一个确定性头像(dither 类型)
167
+ * @param seed 种子,传群号或用户ID,保证同一实体每次得到的图案一致
168
+ */
169
+ export const buildDitherAvatar = (seed: string): string =>
170
+ `data:image/png;base64,${encodePng(rasterize(recordDither(seed, RENDER_SIZE), RENDER_SIZE), RENDER_SIZE).toString('base64')}`
171
+
172
+ /**
173
+ * 把真实头像包成一张圆形裁切的 SVG。
174
+ *
175
+ * 圆角只能在包装层做:ECharts 富文本的 `backgroundColor.image` 不支持 `borderRadius`
176
+ * (实测不会生成 clipPath),不包的话头像在图里是方的。
177
+ * 这里**不重新编码图片**,只是套一层带 clipPath 的 SVG 外壳。
178
+ */
179
+ const buildRemoteAvatar = (bytes: Buffer, mime: string, size: number): string => {
180
+ const half = size / 2
181
+ const svg =
182
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">` +
183
+ `<defs><clipPath id="c"><circle cx="${half}" cy="${half}" r="${half}"/></clipPath></defs>` +
184
+ `<g clip-path="url(#c)"><image href="data:${mime};base64,${bytes.toString('base64')}" width="${size}" height="${size}" preserveAspectRatio="xMidYMid slice"/></g>` +
185
+ `</svg>`
186
+ return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`
187
+ }
188
+
189
+ /**
190
+ * 解析一个头像:有 URL 就拉下来,任何一步失败都回落到生成头像。
191
+ *
192
+ * **绝不抛错** —— 统计海报是锦上添花的功能,不能因为某个 CDN 抽风就整张渲染不出来。
193
+ * @param seed 种子(群号 / 用户ID),决定生成头像的图案
194
+ * @param url 平台给的头像地址;拿不到时传 undefined
195
+ * @param size 圆形包装的边长(px),只影响真实头像那条路径
196
+ */
197
+ export const resolveAvatar = async (seed: string, url?: string, size = 48): Promise<string> => {
198
+ if (url) {
199
+ const cached = cache.get(url)
200
+ if (cached) return cached
201
+
202
+ try {
203
+ const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) })
204
+ if (response.ok) {
205
+ const bytes = Buffer.from(await response.arrayBuffer())
206
+ if (bytes.byteLength > 0) {
207
+ // 原样内联,不重新编码也不缩放 —— 尺寸由 `getAvatarUrl(id, size)` 那头定好了
208
+ const dataUri = buildRemoteAvatar(bytes, response.headers.get('content-type')?.split(';')[0] || 'image/png', size)
209
+ if (cache.size >= CACHE_LIMIT) cache.delete(cache.keys().next().value!)
210
+ cache.set(url, dataUri)
211
+ return dataUri
212
+ }
213
+ }
214
+ } catch (error) {
215
+ logger.debug('[统计] 获取头像失败,改用生成头像:', error)
216
+ }
217
+ }
218
+
219
+ return buildDitherAvatar(seed)
220
+ }