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,1615 @@
1
+ import fs from 'node:fs'
2
+
3
+ import {
4
+ BiliUserDynamic,
5
+ type BilibiliLiveRoomInfoResponse,
6
+ type BilibiliUserLiveStatusResponse,
7
+ BilibiliUserCardResponse,
8
+ BilibiliVideoStreamResponse,
9
+ DynamicType,
10
+ MajorType
11
+ } from '@ikenxuan/amagi'
12
+ import type { BilibiliForwardOriginalContentProps } from '@template/template/bilibili/dynamic/types'
13
+ import type { BilibiliUserListData } from '@template/template/bilibili/userlist/components/types'
14
+ import type { AdapterType, ImageElement, Message, SendMsgResults } from 'node-karin'
15
+ import karin, { common, logger, segment } from 'node-karin'
16
+
17
+ import {
18
+ Base,
19
+ baseHeaders,
20
+ bilibiliDB,
21
+ buildGoogleMotionPhoto,
22
+ cleanOldDynamicCache,
23
+ Common,
24
+ Count,
25
+ downloadFile,
26
+ downLoadFileOptions,
27
+ fileInfo,
28
+ loopVideoWithTransition,
29
+ mergeVideoAudio,
30
+ processLocalImageFile,
31
+ processImageUrl,
32
+ Render,
33
+ uploadFile
34
+ } from '@/module'
35
+ import { bilibiliFetcher } from '@/module/utils/amagiClient'
36
+ import { Config } from '@/module/utils/Config'
37
+ import { bilibiliProcessVideos, generateDecorationCard, getvideosize, parseAdditionalCard, TimeFormatter } from '@/platform/bilibili'
38
+ import {
39
+ buildBilibiliArticleRichText,
40
+ buildBilibiliDynamicRichText,
41
+ buildBilibiliRichTextForwardMessage,
42
+ buildBilibiliVideoDescRichText,
43
+ getUsernameMetadata
44
+ } from '@/platform/bilibili/dynamic-text'
45
+ import { buildBilibiliLiveSessionId, parseBilibiliLiveStartedAt } from '@/platform/bilibili/live-status'
46
+ import type { bilibiliPushItem, BilibiliPushType } from '@/types/config/pushlist'
47
+
48
+ /** BilibiliPushType 到 DynamicType 的映射 */
49
+ const pushTypeToDynamicType: Record<BilibiliPushType, DynamicType> = {
50
+ video: DynamicType.AV,
51
+ draw: DynamicType.DRAW,
52
+ word: DynamicType.WORD,
53
+ live: DynamicType.LIVE_RCMD,
54
+ forward: DynamicType.FORWARD,
55
+ article: DynamicType.ARTICLE
56
+ }
57
+
58
+ /** 所有支持的 BilibiliPushType */
59
+ const allBilibiliPushTypes: BilibiliPushType[] = ['video', 'draw', 'word', 'live', 'forward', 'article']
60
+
61
+ /**
62
+ * 空间动态列表里的一条动态。
63
+ *
64
+ * 用 amagi 手写快照树(`BiliUserDynamic`)而不是 `BilibiliUserDynamicListResponse`:生成树这条
65
+ * 响应没有按动态类型分形状(`type` 只是 `string`、各支共用同一套 `modules`,样本里没有的字段
66
+ * 还常被记成 `null`),而推送链路要按类型读各自的字段(图文读 `opus`、视频读 `archive`、
67
+ * 转发读 `orig`……)。快照树正是这个用法需要的判别联合,仍是 amagi 在导出的 v6 兼容面。
68
+ */
69
+ type DataItem = BiliUserDynamic['data']['items'][number]
70
+
71
+ type BilibiliPushItemMap = {
72
+ [T in DynamicType]: {
73
+ /** 该UP主的昵称 */
74
+ remark: string
75
+ /** UP主UID */
76
+ host_mid: number
77
+ /** 动态发布时间 */
78
+ create_time: number
79
+ /** 要推送到的群组和机器人ID */
80
+ targets: Array<{ groupId: string; botId: string }>
81
+ /** 动态详情信息 */
82
+ Dynamic_Data: Extract<DataItem, { type: T }>
83
+ /** UP主头像url */
84
+ avatar_img: string
85
+ /** 动态类型 */
86
+ dynamic_type: T
87
+ }
88
+ }
89
+
90
+ /** 每个推送项的类型定义 */
91
+ export type BilibiliPushItem = BilibiliPushItemMap[DynamicType]
92
+
93
+ /** 推送列表的类型定义 */
94
+ type WillBePushList = Record<string, BilibiliPushItem>
95
+
96
+ const bilibiliBaseHeaders: downLoadFileOptions['headers'] = {
97
+ ...baseHeaders,
98
+ Referer: 'https://www.bilibili.com',
99
+ Cookie: Config.amagi.cookies.bilibili
100
+ }
101
+
102
+ export class Bilibilipush extends Base {
103
+ private force = false
104
+
105
+ constructor(e = {} as Message, force: boolean = false) {
106
+ super(e)
107
+ this.force = force
108
+ }
109
+
110
+ private injectBotToEventForRender(targets: Array<{ groupId: string; botId: string }>): void {
111
+ const targetBotId = targets.find((item) => item.botId)?.botId
112
+ if (!targetBotId) return
113
+
114
+ const bot = karin.getBot(targetBotId) as AdapterType | undefined
115
+ if (!bot) return
116
+
117
+ const eventWithBot = this.e as Message & { bot?: AdapterType; selfId?: string }
118
+ eventWithBot.bot = bot
119
+ eventWithBot.selfId = eventWithBot.selfId ?? targetBotId
120
+ }
121
+
122
+ /**
123
+ * 检查并补全配置文件中缺失的字段
124
+ * @param pushList 推送配置列表
125
+ */
126
+ private ensureConfigFields(pushList: bilibiliPushItem[]): void {
127
+ if (!pushList || pushList.length === 0) return
128
+
129
+ let hasChanges = false
130
+
131
+ for (const item of pushList) {
132
+ // 检查并补全 pushTypes 字段
133
+ if (!item.pushTypes?.length) {
134
+ item.pushTypes = [...allBilibiliPushTypes]
135
+ hasChanges = true
136
+ logger.info(
137
+ `[koishi-plugin-kkk][v2.28.0] 为UP主 ${item.remark ?? item.host_mid} 自动补全推送类型:投稿视频、图文动态、纯文动态、直播动态、转发动态、投稿专栏`
138
+ )
139
+ }
140
+
141
+ // 检查并补全 switch 字段
142
+ if (item.switch === undefined) {
143
+ item.switch = true
144
+ hasChanges = true
145
+ }
146
+ }
147
+
148
+ // 如果有修改,保存到配置文件
149
+ if (hasChanges) {
150
+ Config.Modify('pushlist', 'bilibili', pushList)
151
+ logger.info('已自动补全B站推送配置文件中缺失的字段并保存')
152
+ }
153
+ }
154
+
155
+ /**
156
+ * 执行主要的操作流程
157
+ */
158
+ async action() {
159
+ await this.syncConfigToDatabase()
160
+
161
+ // 检查并补全配置文件中缺失的字段
162
+ this.ensureConfigFields(Config.pushlist.bilibili)
163
+ // 清理旧的动态缓存记录
164
+ const deletedCount = await cleanOldDynamicCache('bilibili')
165
+ if (deletedCount > 0) {
166
+ logger.info(`已清理 ${deletedCount} 条过期的B站动态缓存记录`)
167
+ }
168
+
169
+ // 获取已注册的 bot 列表,过滤未注册的 bot
170
+ const registeredBotIds = karin.getAllBotID()
171
+ const filteredPushList = this.filterPushListByRegisteredBots(Config.pushlist.bilibili, registeredBotIds)
172
+
173
+ if (filteredPushList.length === 0) {
174
+ // 推送列表本来就是空的属正常情况,别刷 warn(用户反馈日志一直被它刷屏)
175
+ if ((Config.pushlist as any).bilibili?.length) {
176
+ logger.warn('推送列表里的机器人都不在线,本次跳过B站推送')
177
+ } else {
178
+ logger.debug('B站推送列表为空,跳过')
179
+ }
180
+ return true
181
+ }
182
+
183
+ const data = await this.getDynamicList(filteredPushList)
184
+ const pushdata = await this.excludeAlreadyPushed(data.willbepushlist)
185
+
186
+ if (Object.keys(pushdata).length === 0) return true
187
+
188
+ if (this.force) {
189
+ return await this.forcepush(pushdata)
190
+ } else {
191
+ return await this.getdata(pushdata)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * 根据已注册的 bot 列表过滤推送配置
197
+ * @param pushList 原始推送配置列表
198
+ * @param registeredBotIds 已注册的 bot ID 列表
199
+ * @returns 过滤后的推送配置列表
200
+ */
201
+ private filterPushListByRegisteredBots(pushList: bilibiliPushItem[], registeredBotIds: string[]): bilibiliPushItem[] {
202
+ if (!pushList || pushList.length === 0) return []
203
+
204
+ const registeredSet = new Set(registeredBotIds)
205
+ const filteredList: bilibiliPushItem[] = []
206
+
207
+ for (const item of pushList) {
208
+ // 过滤 group_id 中未注册的 bot
209
+ const filteredGroupIds = item.group_id.filter((groupWithBot) => {
210
+ const botId = groupWithBot.split(':')[1]
211
+ const isRegistered = registeredSet.has(botId)
212
+ if (!isRegistered) {
213
+ logger.debug(`Bot ${botId} 未注册,跳过群组 ${groupWithBot.split(':')[0]} 的推送`)
214
+ }
215
+ return isRegistered
216
+ })
217
+
218
+ // 如果过滤后还有有效的群组,则保留该订阅项
219
+ if (filteredGroupIds.length > 0) {
220
+ filteredList.push({
221
+ ...item,
222
+ group_id: filteredGroupIds
223
+ })
224
+ } else {
225
+ logger.debug(`UP主 ${item.remark ?? item.host_mid} 的所有推送目标 bot 均未注册,跳过`)
226
+ }
227
+ }
228
+
229
+ return filteredList
230
+ }
231
+
232
+ /** 将 UID 直播状态接口的结果接入原有动态推送列表。 */
233
+ private async getDirectLivePushItems(
234
+ userList: bilibiliPushItem[]
235
+ ): Promise<{ handledUids: Set<number>; willBePushList: WillBePushList }> {
236
+ const handledUids = new Set<number>()
237
+ const willBePushList: WillBePushList = {}
238
+ const liveSubscriptions = userList.filter((item) => item.switch !== false && (item.pushTypes || allBilibiliPushTypes).includes('live'))
239
+
240
+ for (const item of liveSubscriptions) {
241
+ let liveStatus: BilibiliUserLiveStatusResponse['data']
242
+ try {
243
+ const liveStatusResult = await this.amagi.bilibili.fetcher.fetchUserLiveStatus({
244
+ host_mid: item.host_mid
245
+ })
246
+ liveStatus = liveStatusResult.data.data
247
+ } catch (error) {
248
+ logger.warn(
249
+ `[Bilibili 推送] UP主 ${item.remark}(${item.host_mid})直播状态直查失败,本轮停止直查并让剩余订阅回退到直播动态检测:${formatErrorMessage(error)}`
250
+ )
251
+ break
252
+ }
253
+
254
+ if (liveStatus.roomStatus !== 1 || liveStatus.liveStatus !== 1 || liveStatus.roomid <= 0) {
255
+ handledUids.add(item.host_mid)
256
+ continue
257
+ }
258
+
259
+ try {
260
+ const liveInfoResult = await this.amagi.bilibili.fetcher.fetchLiveRoomInfo({
261
+ room_id: String(liveStatus.roomid)
262
+ })
263
+ const liveInfo = liveInfoResult.data.data
264
+
265
+ /** 两个直播接口状态不一致时,以直播间详情为准。 */
266
+ if (liveInfo.live_status !== 1) {
267
+ handledUids.add(item.host_mid)
268
+ continue
269
+ }
270
+
271
+ const sessionId = buildBilibiliLiveSessionId(item.host_mid, liveInfo.room_id, liveInfo.live_time)
272
+ const liveStartedAt = parseBilibiliLiveStartedAt(liveInfo.live_time)
273
+ if (!sessionId || !liveStartedAt) {
274
+ throw new Error(`直播间 ${liveInfo.room_id} 未返回可用于场次去重的开播时间`)
275
+ }
276
+
277
+ const dynamic = createLiveDynamicItem(sessionId, liveStartedAt, liveInfo, liveStatus)
278
+ willBePushList[sessionId] = {
279
+ remark: item.remark,
280
+ host_mid: item.host_mid,
281
+ create_time: dynamic.modules.module_author.pub_ts,
282
+ targets: item.group_id.map((groupWithBot) => {
283
+ const [groupId, botId] = groupWithBot.split(':')
284
+ return { groupId, botId }
285
+ }),
286
+ Dynamic_Data: dynamic,
287
+ avatar_img: '',
288
+ dynamic_type: DynamicType.LIVE_RCMD
289
+ } as BilibiliPushItem
290
+ handledUids.add(item.host_mid)
291
+ } catch (error) {
292
+ logger.warn(
293
+ `[Bilibili 推送] UP主 ${item.remark}(${item.host_mid})直播场次信息不完整,本轮回退到直播动态检测:${formatErrorMessage(error)}`
294
+ )
295
+ }
296
+ }
297
+
298
+ return { handledUids, willBePushList }
299
+ }
300
+
301
+ /**
302
+ * 为降级路径中的直播动态解析与直查路径一致的场次缓存键。
303
+ *
304
+ * @param dynamic B站空间动态中的直播推荐项。
305
+ * @param hostMid UP 主 UID。
306
+ * @returns 可用时返回直播场次键,否则退回原始动态ID。
307
+ */
308
+ private async resolveLiveDynamicCacheId(dynamic: Extract<DataItem, { type: DynamicType.LIVE_RCMD }>, hostMid: number): Promise<string> {
309
+ try {
310
+ const liveData = JSON.parse(dynamic.modules.module_dynamic.major.live_rcmd.content)
311
+ const roomId = Number(liveData.live_play_info.room_id)
312
+ const liveInfo = await this.amagi.bilibili.fetcher.fetchLiveRoomInfo({
313
+ room_id: String(roomId)
314
+ })
315
+ const cacheId = buildBilibiliLiveSessionId(hostMid, liveInfo.data.data.room_id, liveInfo.data.data.live_time)
316
+ return cacheId || dynamic.id_str
317
+ } catch (error) {
318
+ logger.warn(`[Bilibili 推送] 直播动态 ${dynamic.id_str} 无法解析统一场次键,将使用动态ID去重:${formatErrorMessage(error)}`)
319
+ return dynamic.id_str
320
+ }
321
+ }
322
+
323
+ /**
324
+ * 同步配置文件中的订阅信息到数据库
325
+ */
326
+ async syncConfigToDatabase() {
327
+ // 如果配置文件中没有B站推送列表,直接返回
328
+ if (!Config.pushlist.bilibili || Config.pushlist.bilibili.length === 0) {
329
+ return
330
+ }
331
+
332
+ await bilibiliDB.syncConfigSubscriptions(Config.pushlist.bilibili)
333
+ }
334
+
335
+ /**
336
+ * 异步获取数据并根据动态类型处理和发送动态信息。
337
+ * @param data 包含动态相关信息的对象。
338
+ */
339
+ async getdata(data: WillBePushList) {
340
+ for (const dynamicId in data) {
341
+ logger.mark(`
342
+ ${logger.blue('开始处理并渲染B站动态图片')}
343
+ ${logger.cyan('UP')}: ${logger.green(data[dynamicId].remark)}
344
+ ${logger.cyan('动态id')}:${logger.yellow(dynamicId)}
345
+ ${logger.cyan('访问地址')}:${logger.green('https://t.bilibili.com/' + dynamicId)}`)
346
+
347
+ let skip = await skipDynamic(data[dynamicId])
348
+ if (skip) {
349
+ logger.warn(`动态 https://t.bilibili.com/${dynamicId} 已被处理,跳过`)
350
+ }
351
+ let send_video = true
352
+ let img: ImageElement[] = []
353
+ let articleForwardPayload: {
354
+ body: ReturnType<typeof buildBilibiliArticleRichText>
355
+ title?: string
356
+ summary?: string
357
+ shareUrl?: string
358
+ } | null = null
359
+ this.injectBotToEventForRender(data[dynamicId].targets)
360
+
361
+ if (!skip) {
362
+ const userINFO = await this.amagi.bilibili.fetcher.fetchUserCard({
363
+ host_mid: data[dynamicId].host_mid
364
+ })
365
+ switch (data[dynamicId].dynamic_type) {
366
+ /** 处理图文动态 */
367
+ case DynamicType.DRAW: {
368
+ if (
369
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.topic !== null &&
370
+ data[dynamicId].Dynamic_Data.modules.module_dynamic &&
371
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.topic !== null
372
+ ) {
373
+ const name = data[dynamicId].Dynamic_Data.modules.module_dynamic.topic!.name
374
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus.summary.rich_text_nodes.unshift({
375
+ orig_text: name,
376
+ text: name,
377
+ type: 'topic',
378
+ rid: data[dynamicId].Dynamic_Data.modules.module_dynamic.topic!.id.toString()
379
+ })
380
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major!.opus.summary.text =
381
+ `${name}\n\n` + data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.text
382
+ }
383
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_DRAW', {
384
+ image_url: Object.values(data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.pics ?? [])
385
+ .filter((item): item is { url: string } => typeof item?.url === 'string')
386
+ .map((item) => ({ image_src: item.url })),
387
+ title: data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.title ?? undefined,
388
+ text: buildBilibiliDynamicRichText(
389
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.text ?? '',
390
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.rich_text_nodes ?? []
391
+ ),
392
+ dianzan: Count(data[dynamicId].Dynamic_Data.modules.module_stat.like.count),
393
+ pinglun: Count(data[dynamicId].Dynamic_Data.modules.module_stat.comment.count),
394
+ share: Count(data[dynamicId].Dynamic_Data.modules.module_stat.forward.count),
395
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
396
+ avatar_url: data[dynamicId].Dynamic_Data.modules.module_author.face,
397
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
398
+ share_url: 'https://t.bilibili.com/' + data[dynamicId].Dynamic_Data.id_str,
399
+ usernameMeta: getUsernameMetadata(userINFO.data.data.card),
400
+ fans: Count(userINFO.data.data.follower),
401
+ user_shortid: data[dynamicId].host_mid,
402
+ total_favorited: Count(userINFO.data.data.like_num),
403
+ following_count: Count(userINFO.data.data.card.attention),
404
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.modules.module_author.decorate_card),
405
+ render_time: TimeFormatter.now(),
406
+ imageLayout: Config.bilibili.imageLayout,
407
+ additional: parseAdditionalCard(data[dynamicId].Dynamic_Data.modules.module_dynamic.additional),
408
+ dynamicTYPE: '图文动态推送',
409
+ dynamic_id: dynamicId
410
+ })
411
+ break
412
+ }
413
+ /** 处理纯文动态 */
414
+ case DynamicType.WORD: {
415
+ // 处理话题
416
+ // Koishi 移植修正:原实现这里读的是 `Dynamic_Data.orig.modules...`,
417
+ // 但纯文动态没有 orig(那是转发动态才有的字段),一旦命中就抛
418
+ // "Cannot read properties of undefined (reading 'modules')"。
419
+ // 按原意改为检查主动态自身的话题结构。
420
+ if (
421
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.topic != null &&
422
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary != null
423
+ ) {
424
+ const md = data[dynamicId].Dynamic_Data.modules.module_dynamic
425
+ const { name } = md.topic as unknown as { name: string; id: number }
426
+ const summary = md.major!.opus.summary
427
+ summary.rich_text_nodes ??= []
428
+ summary.rich_text_nodes.unshift({
429
+ orig_text: name,
430
+ text: name,
431
+ type: 'topic',
432
+ rid: (md.topic as unknown as { id: number }).id.toString()
433
+ })
434
+ summary.text = summary.text ? `${name}\n\n${summary.text}` : name
435
+ }
436
+
437
+ const text = buildBilibiliDynamicRichText(
438
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.text ?? '',
439
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.rich_text_nodes ?? []
440
+ )
441
+
442
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_WORD', {
443
+ text,
444
+ dianzan: Count(data[dynamicId].Dynamic_Data.modules.module_stat.like.count),
445
+ pinglun: Count(data[dynamicId].Dynamic_Data.modules.module_stat.comment.count),
446
+ share: Count(data[dynamicId].Dynamic_Data.modules.module_stat.forward.count),
447
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
448
+ avatar_url: data[dynamicId].Dynamic_Data.modules.module_author.face,
449
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
450
+ share_url: 'https://t.bilibili.com/' + data[dynamicId].Dynamic_Data.id_str,
451
+ usernameMeta: getUsernameMetadata(userINFO.data.data.card),
452
+ fans: Count(userINFO.data.data.follower),
453
+ user_shortid: data[dynamicId].host_mid,
454
+ total_favorited: Count(userINFO.data.data.like_num),
455
+ following_count: Count(userINFO.data.data.card.attention),
456
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.modules.module_author.decorate_card),
457
+ render_time: TimeFormatter.now(),
458
+ additional: parseAdditionalCard(data[dynamicId].Dynamic_Data.modules.module_dynamic.additional),
459
+ dynamicTYPE: '纯文动态推送',
460
+ dynamic_id: dynamicId
461
+ })
462
+ break
463
+ }
464
+ /** 处理视频动态 */
465
+ case DynamicType.AV: {
466
+ if (data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.type === 'MAJOR_TYPE_ARCHIVE') {
467
+ const bvid = data[dynamicId].Dynamic_Data?.modules.module_dynamic.major?.archive?.bvid ?? ''
468
+ const INFODATA = await bilibiliFetcher.fetchVideoInfo({ bvid })
469
+
470
+ /** 特殊字段,只有番剧和影视才会有,如果是该类型视频,默认不发送 */
471
+ if (INFODATA.data.data.redirect_url) {
472
+ send_video = false
473
+ logger.debug(
474
+ `UP主:${INFODATA.data.data.owner.name} 的该动态类型为${logger.yellow('番剧或影视')},默认跳过不下载,直达:${logger.green(INFODATA.data.data.redirect_url)}`
475
+ )
476
+ } else {
477
+ // const noCkData = await getBilibiliData('单个视频下载信息数据', { avid: Number(aid), cid: INFODATA.data.data.cid })
478
+ }
479
+
480
+ // 处理共创者信息
481
+ let staff = undefined
482
+ if (INFODATA.data.data.staff && Array.isArray(INFODATA.data.data.staff)) {
483
+ const currentMid = data[dynamicId].host_mid
484
+ // 提取共创者信息
485
+ staff = INFODATA.data.data.staff.map((member: any) => ({
486
+ mid: member.mid,
487
+ title: member.title,
488
+ name: member.name,
489
+ face: member.face,
490
+ follower: member.follower
491
+ }))
492
+
493
+ // 如果当前动态发布者是共创者之一,将其排到最前面
494
+ const currentUserIndex = staff.findIndex((member: any) => member.mid === currentMid)
495
+ if (currentUserIndex > 0) {
496
+ const currentUser = staff.splice(currentUserIndex, 1)[0]
497
+ staff.unshift(currentUser)
498
+ }
499
+ }
500
+
501
+ // 处理话题
502
+ const md = data[dynamicId].Dynamic_Data.modules.module_dynamic
503
+ if (md.topic) {
504
+ const { name } = md.topic
505
+ md.desc ??= { rich_text_nodes: [], text: '' }
506
+ md.desc.rich_text_nodes.unshift({ orig_text: name, jump_url: '', text: name, type: 'topic' })
507
+ md.desc.text = md.desc.text ? `${name}\n\n${md.desc.text}` : name
508
+ }
509
+
510
+ const dynamicText = buildBilibiliDynamicRichText(
511
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.desc?.text ?? '',
512
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.desc?.rich_text_nodes ?? []
513
+ )
514
+
515
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_AV', {
516
+ image_url: INFODATA.data.data.pic,
517
+ text: buildBilibiliDynamicRichText(INFODATA.data.data.title, []),
518
+ desc: INFODATA.data.data.desc_v2?.length
519
+ ? buildBilibiliVideoDescRichText(INFODATA.data.data.desc_v2)
520
+ : buildBilibiliDynamicRichText(INFODATA.data.data.desc || '', []),
521
+ dynamic_text: dynamicText,
522
+ dianzan: Count(INFODATA.data.data.stat.like),
523
+ pinglun: Count(INFODATA.data.data.stat.reply),
524
+ share: Count(INFODATA.data.data.stat.share),
525
+ view: Count(INFODATA.data.data.stat.view),
526
+ coin: Count(INFODATA.data.data.stat.coin),
527
+ duration_text: data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.archive?.duration_text ?? '0:00',
528
+ page_length: INFODATA.data.data.pages.length,
529
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
530
+ avatar_url: userINFO.data.data.card.face,
531
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
532
+ share_url: 'https://www.bilibili.com/video/' + bvid,
533
+ usernameMeta: getUsernameMetadata(userINFO.data.data.card),
534
+ fans: Count(userINFO.data.data.follower),
535
+ user_shortid: data[dynamicId].host_mid,
536
+ total_favorited: Count(userINFO.data.data.like_num),
537
+ following_count: Count(userINFO.data.data.card.attention),
538
+ render_time: TimeFormatter.now(),
539
+ dynamicTYPE: '视频动态推送',
540
+ dynamic_id: dynamicId,
541
+ staff
542
+ })
543
+ }
544
+ break
545
+ }
546
+ /** 处理直播动态 */
547
+ case DynamicType.LIVE_RCMD: {
548
+ const liveInfo = JSON.parse(data[dynamicId].Dynamic_Data.modules.module_dynamic.major.live_rcmd.content)
549
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD', {
550
+ image_url: liveInfo.live_play_info.cover,
551
+ text: buildBilibiliDynamicRichText(liveInfo.live_play_info.title, []),
552
+ liveinf: br(`${liveInfo.live_play_info.area_name} | 房间号: ${liveInfo.live_play_info.room_id}`),
553
+ usernameMeta: getUsernameMetadata(userINFO.data.data.card),
554
+ avatar_url: userINFO.data.data.card.face,
555
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
556
+ fans: Count(userINFO.data.data.follower),
557
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
558
+ now_time: TimeFormatter.now(),
559
+ share_url: 'https://live.bilibili.com/' + liveInfo.live_play_info.room_id,
560
+ dynamicTYPE: '直播动态推送'
561
+ })
562
+ break
563
+ }
564
+ /** 处理转发动态 */
565
+ case DynamicType.FORWARD: {
566
+ // 处理话题
567
+ const md = data[dynamicId].Dynamic_Data.modules.module_dynamic
568
+ if (md.topic) {
569
+ const { name } = md.topic
570
+ const desc = md.desc!
571
+ desc.rich_text_nodes ??= []
572
+ desc.rich_text_nodes.unshift({ orig_text: name, jump_url: '', text: name, type: 'topic' })
573
+ desc.text = desc.text ? `${name}\n\n${desc.text}` : name
574
+ }
575
+
576
+ const text = buildBilibiliDynamicRichText(
577
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.desc!.text,
578
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.desc!.rich_text_nodes
579
+ )
580
+ /** 富文本节点:查看图片 */
581
+ const imgList = []
582
+ if (data[dynamicId].Dynamic_Data.modules.module_dynamic.desc) {
583
+ for (const richTxtItem of data[dynamicId].Dynamic_Data.modules.module_dynamic.desc.rich_text_nodes) {
584
+ if (richTxtItem.type === 'RICH_TEXT_NODE_TYPE_VIEW_PICTURE') {
585
+ for (const pic of richTxtItem.pics) {
586
+ imgList.push(pic.src)
587
+ }
588
+ }
589
+ }
590
+ }
591
+
592
+ let original_content: BilibiliForwardOriginalContentProps['original_content'] = {}
593
+ switch (data[dynamicId].Dynamic_Data.orig.type) {
594
+ case DynamicType.AV: {
595
+ const desc = data[dynamicId].Dynamic_Data.orig.modules.module_dynamic?.desc || {
596
+ text: '',
597
+ rich_text_nodes: []
598
+ }
599
+
600
+ original_content = {
601
+ DYNAMIC_TYPE_AV: {
602
+ usernameMeta: getUsernameMetadata(data[dynamicId].Dynamic_Data.orig.modules.module_author),
603
+ avatar_url: data[dynamicId].Dynamic_Data.orig.modules.module_author.face,
604
+ duration_text: data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.archive?.duration_text,
605
+ text: buildBilibiliDynamicRichText(desc.text, desc.rich_text_nodes),
606
+ title: buildBilibiliDynamicRichText(
607
+ data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.archive?.title ?? '',
608
+ []
609
+ ),
610
+ danmaku: data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.archive?.stat.danmaku,
611
+ play: data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.archive?.stat.play,
612
+ cover: data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.archive?.cover,
613
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.orig.modules.module_author.pub_ts),
614
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.orig.modules.module_author.decorate_card),
615
+ frame: data[dynamicId].Dynamic_Data.orig.modules.module_author.pendant.image
616
+ }
617
+ }
618
+ break
619
+ }
620
+ case DynamicType.DRAW: {
621
+ // 处理话题
622
+ const origMd = data[dynamicId].Dynamic_Data.orig.modules.module_dynamic
623
+ if (origMd.topic) {
624
+ const { name } = origMd.topic as { name: string; id: number }
625
+ const summary = origMd.major?.opus?.summary
626
+ if (summary) {
627
+ summary.rich_text_nodes ??= []
628
+ summary.rich_text_nodes.unshift({
629
+ orig_text: name,
630
+ text: name,
631
+ type: 'topic',
632
+ rid: (origMd.topic as { id: number }).id.toString(),
633
+ jump_url: '114514',
634
+ style: { '114514': '1919810' }
635
+ })
636
+ summary.text = summary.text ? `${name}\n${summary.text}` : name
637
+ }
638
+ }
639
+
640
+ original_content = {
641
+ DYNAMIC_TYPE_DRAW: {
642
+ title: data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major?.opus?.title ?? undefined,
643
+ usernameMeta: getUsernameMetadata(data[dynamicId].Dynamic_Data.orig.modules.module_author),
644
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.orig.modules.module_author.pub_ts),
645
+ avatar_url: data[dynamicId].Dynamic_Data.orig.modules.module_author.face,
646
+ text: buildBilibiliDynamicRichText(
647
+ data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.opus.summary.text,
648
+ data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.opus.summary.rich_text_nodes
649
+ ),
650
+ image_url: Object.values(data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.opus.pics)
651
+ .filter((item): item is { url: string } => typeof item?.url === 'string')
652
+ .map((item) => ({ image_src: item.url })),
653
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.orig.modules.module_author.decorate_card),
654
+ frame: data[dynamicId].Dynamic_Data.orig.modules.module_author.pendant.image
655
+ }
656
+ }
657
+ break
658
+ }
659
+ case DynamicType.WORD: {
660
+ // 处理话题
661
+ const origMd = data[dynamicId].Dynamic_Data.orig.modules.module_dynamic
662
+ if (origMd.topic) {
663
+ const { name } = origMd.topic as { name: string; id: number }
664
+ const summary = origMd.major?.opus?.summary
665
+ if (summary) {
666
+ summary.rich_text_nodes ??= []
667
+ summary.rich_text_nodes.unshift({
668
+ orig_text: name,
669
+ text: name,
670
+ type: 'topic',
671
+ rid: (origMd.topic as { id: number }).id.toString()
672
+ })
673
+ summary.text = summary.text ? `${name}\n${summary.text}` : name
674
+ }
675
+ }
676
+
677
+ original_content = {
678
+ DYNAMIC_TYPE_WORD: {
679
+ usernameMeta: getUsernameMetadata(data[dynamicId].Dynamic_Data.orig.modules.module_author),
680
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.orig.modules.module_author.pub_ts),
681
+ avatar_url: data[dynamicId].Dynamic_Data.orig.modules.module_author.face,
682
+ text: buildBilibiliDynamicRichText(
683
+ data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.opus.summary.text,
684
+ data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.opus.summary.rich_text_nodes
685
+ ),
686
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.orig.modules.module_author.decorate_card),
687
+ frame: data[dynamicId].Dynamic_Data.orig.modules.module_author.pendant.image
688
+ }
689
+ }
690
+ break
691
+ }
692
+ case DynamicType.LIVE_RCMD: {
693
+ const liveData = JSON.parse(data[dynamicId].Dynamic_Data.orig.modules.module_dynamic.major.live_rcmd.content)
694
+ original_content = {
695
+ DYNAMIC_TYPE_LIVE_RCMD: {
696
+ usernameMeta: getUsernameMetadata(data[dynamicId].Dynamic_Data.orig.modules.module_author),
697
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.orig.modules.module_author.pub_ts),
698
+ avatar_url: data[dynamicId].Dynamic_Data.orig.modules.module_author.face,
699
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.orig.modules.module_author.decorate_card),
700
+ frame: data[dynamicId].Dynamic_Data.orig.modules.module_author.pendant.image,
701
+ cover: liveData.live_play_info.cover,
702
+ text_large: liveData.live_play_info.watched_show.text_large,
703
+ area_name: liveData.live_play_info.area_name,
704
+ title: buildBilibiliDynamicRichText(liveData.live_play_info.title, []),
705
+ online: liveData.live_play_info.online
706
+ }
707
+ }
708
+ break
709
+ }
710
+ default: {
711
+ const key = dynamicId
712
+ logger.warn(
713
+ `UP主:${data[key].remark}的${logger.green('转发动态')}转发的原动态类型为「${logger.yellow(data[key].Dynamic_Data.orig?.type)}」暂未支持解析`
714
+ )
715
+ break
716
+ }
717
+ }
718
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_FORWARD', {
719
+ text,
720
+ dianzan: Count(data[dynamicId].Dynamic_Data.modules.module_stat.like.count),
721
+ pinglun: Count(data[dynamicId].Dynamic_Data.modules.module_stat.comment.count),
722
+ share: Count(data[dynamicId].Dynamic_Data.modules.module_stat.forward.count),
723
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
724
+ avatar_url: data[dynamicId].Dynamic_Data.modules.module_author.face,
725
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
726
+ share_url: 'https://t.bilibili.com/' + data[dynamicId].Dynamic_Data.id_str,
727
+ usernameMeta: getUsernameMetadata(userINFO.data.data.card),
728
+ fans: Count(userINFO.data.data.follower),
729
+ user_shortid: data[dynamicId].Dynamic_Data.modules.module_author.mid,
730
+ total_favorited: Count(userINFO.data.data.like_num),
731
+ following_count: Count(userINFO.data.data.card.attention),
732
+ dynamicTYPE: '转发动态推送',
733
+ decoration_card: generateDecorationCard(data[dynamicId].Dynamic_Data.modules.module_author.decorate_card),
734
+ render_time: TimeFormatter.now(),
735
+ original_content,
736
+ imgList: imgList.length > 0 ? imgList : null,
737
+ dynamic_id: dynamicId
738
+ })
739
+ break
740
+ }
741
+ /** 文章/专栏动态 */
742
+ case DynamicType.ARTICLE: {
743
+ const articleInfoBase = await this.amagi.bilibili.fetcher.fetchArticleInfo({
744
+ id: data[dynamicId].Dynamic_Data.basic.rid_str
745
+ })
746
+ const articleInfo = await this.amagi.bilibili.fetcher.fetchArticleContent({
747
+ id: data[dynamicId].Dynamic_Data.basic.rid_str
748
+ })
749
+
750
+ // 提取专栏基本信息
751
+ const articleData = articleInfoBase.data.data
752
+ // 提取专栏正文内容(生成类型里 `data` 可空:拿不到正文就没东西可推,按未支持类型一样跳过)
753
+ const articleContent = articleInfo.data.data
754
+ if (!articleContent) {
755
+ skip = true
756
+ logger.warn(`UP主:${data[dynamicId].remark} 的专栏 ${data[dynamicId].Dynamic_Data.basic.rid_str} 未返回正文,跳过本次推送`)
757
+ break
758
+ }
759
+ const body = buildBilibiliArticleRichText(articleContent.opus, articleContent.content, Common.useDarkTheme())
760
+ const shareUrl = articleContent.dyn_id_str
761
+ ? `https://www.bilibili.com/opus/${articleContent.dyn_id_str}`
762
+ : `https://www.bilibili.com/read/cv${articleContent.id}`
763
+ articleForwardPayload = {
764
+ body,
765
+ title: articleData.title,
766
+ summary: articleData.summary,
767
+ shareUrl
768
+ }
769
+
770
+ // 构建渲染数据
771
+ img = await Render(this.e, 'bilibili/dynamic/DYNAMIC_TYPE_ARTICLE', {
772
+ // 用户信息
773
+ usernameMeta: getUsernameMetadata(data[dynamicId].Dynamic_Data.modules.module_author),
774
+ avatar_url: data[dynamicId].Dynamic_Data.modules.module_author.face,
775
+ frame: data[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
776
+ create_time: TimeFormatter.toDateTime(data[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
777
+ user_shortid: data[dynamicId].host_mid,
778
+ fans: Count(userINFO.data.data.follower),
779
+ total_favorited: Count(userINFO.data.data.like_num),
780
+ following_count: Count(userINFO.data.data.card.attention),
781
+
782
+ // 专栏内容信息
783
+ title: articleData.title,
784
+ summary: articleData.summary,
785
+ banner_url: articleData.banner_url || (articleData.image_urls && articleData.image_urls[0]) || '',
786
+ categories: articleData.categories || [],
787
+ words: articleData.words || 0,
788
+
789
+ // 专栏正文内容(richtext 格式)
790
+ body,
791
+
792
+ // 统计信息
793
+ stats: articleData.stats,
794
+ render_time: TimeFormatter.now(),
795
+ // 分享链接
796
+ share_url: shareUrl,
797
+ dynamicTYPE: '专栏动态推送'
798
+ })
799
+ break
800
+ }
801
+ /** 未处理的动态类型 */
802
+ default: {
803
+ skip = true
804
+ const key = dynamicId
805
+ logger.warn(
806
+ `UP主:${data[key].remark}「${data[key].dynamic_type}」动态类型的暂未支持推送\n动态地址:${'https://t.bilibili.com/' + data[key].Dynamic_Data.id_str}\n可通过 https://github.com/ikenxuan/karin-plugin-kkk/issues/new/choose 提交反馈`
807
+ )
808
+ break
809
+ }
810
+ }
811
+ }
812
+
813
+ // 遍历 targets 数组,并发送消息
814
+ for (const target of data[dynamicId].targets) {
815
+ let status: SendMsgResults | null = null
816
+ if (!skip) {
817
+ const { groupId, botId } = target
818
+ const bot = karin.getBot(botId) as AdapterType
819
+ const Contact = karin.contactGroup(groupId)
820
+
821
+ // 为当前目标注入 bot,后续解析下载沿用该 bot 身份
822
+ const eventWithBot = this.e as Message & { bot?: AdapterType; selfId?: string }
823
+ eventWithBot.bot = bot
824
+ eventWithBot.selfId = botId
825
+ const pushImg = img ?? []
826
+
827
+ // 仅 QQ 官方机器人支持按钮:非直播动态在卡片末尾追加「解析」回调按钮,点击后下发 #解析 + 动态/视频地址
828
+ const parseUrl = buildParseUrl(data[dynamicId])
829
+ const parseButton =
830
+ bot?.adapter?.name === 'QQ Official Bot' && parseUrl
831
+ ? [
832
+ segment.button([
833
+ { text: '解析', callback: true, data: `#解析${parseUrl}` },
834
+ { text: '帮助', callback: true, data: `#kkk帮助` }
835
+ ])
836
+ ]
837
+ : []
838
+
839
+ status = await karin.sendMsg(botId, Contact, [...pushImg, ...parseButton])
840
+ const shouldParseDynamic =
841
+ Config.bilibili.push.parsedynamic &&
842
+ Config.bilibili.push.parseDynamicTypes.includes(
843
+ data[dynamicId].dynamic_type as 'DYNAMIC_TYPE_AV' | 'DYNAMIC_TYPE_DRAW' | 'DYNAMIC_TYPE_ARTICLE'
844
+ )
845
+
846
+ if (shouldParseDynamic && status.messageId) {
847
+ switch (data[dynamicId].dynamic_type) {
848
+ case 'DYNAMIC_TYPE_AV': {
849
+ if (send_video) {
850
+ let correctList!: {
851
+ accept_description: string[]
852
+ videoList: BilibiliVideoStreamResponse['data']['dash']['video']
853
+ }
854
+ let videoSize = ''
855
+ const videoInfo = await this.amagi.bilibili.fetcher.fetchVideoInfo({
856
+ bvid: data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid
857
+ })
858
+ const playUrlData = await this.amagi.bilibili.fetcher.fetchVideoStreamUrl({
859
+ avid: parseInt(data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.aid),
860
+ cid: videoInfo.data.data.cid
861
+ })
862
+ /** 提取出视频流信息对象,并排除清晰度重复的视频流 */
863
+ const simplify = playUrlData.data.data.dash.video.filter((item, index: any, self: any[]) => {
864
+ return (
865
+ self.findIndex((t: { id: any }) => {
866
+ return t.id === item.id
867
+ }) === index
868
+ )
869
+ })
870
+ /** 替换原始的视频信息对象 */
871
+ playUrlData.data.data.dash.video = simplify
872
+ /** 没有音频流(如纯视频稿件)时拿不到音频地址,按只统计视频流大小处理 */
873
+ const audioUrl = playUrlData.data.data.dash.audio?.[0]?.base_url
874
+ /** 给视频信息对象删除不符合条件的视频流 */
875
+ correctList = await bilibiliProcessVideos(
876
+ {
877
+ accept_description: playUrlData.data.data.accept_description,
878
+ bvid: data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid,
879
+ qn: Config.bilibili.push.pushVideoQuality,
880
+ maxAutoVideoSize: Config.bilibili.push.pushMaxAutoVideoSize
881
+ },
882
+ simplify,
883
+ audioUrl
884
+ )
885
+ playUrlData.data.data.dash.video = correctList.videoList
886
+ playUrlData.data.data.accept_description = correctList.accept_description
887
+ /** 获取第一个视频流的大小 */
888
+ videoSize = await getvideosize(
889
+ correctList.videoList[0].base_url,
890
+ audioUrl,
891
+ data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid
892
+ )
893
+ if (Config.app.usefilelimit && Number(videoSize) > Number(Config.app.filelimit) && !Config.app.compress) {
894
+ await karin.sendMsg(botId, Contact, [
895
+ segment.text(
896
+ `设定的最大上传大小为 ${Config.app.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`
897
+ ),
898
+ segment.reply(status.messageId)
899
+ ])
900
+ break
901
+ }
902
+ logger.mark(`当前处于自动推送状态,解析到的视频大小为 ${logger.yellow(Number(videoSize))} MB`)
903
+ const infoData = await this.amagi.bilibili.fetcher.fetchVideoInfo({
904
+ bvid: data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid
905
+ })
906
+ const mp4File = await downloadFile(playUrlData.data?.data?.dash?.video[0].base_url, {
907
+ title: `Bil_V_${infoData.data.data.bvid}.mp4`,
908
+ headers: bilibiliBaseHeaders
909
+ })
910
+ const mp3File = audioUrl
911
+ ? await downloadFile(audioUrl, {
912
+ title: `Bil_A_${infoData.data.data.bvid}.mp3`,
913
+ headers: bilibiliBaseHeaders
914
+ })
915
+ : undefined
916
+
917
+ if (mp4File.filepath && (!mp3File || mp3File.filepath)) {
918
+ /** 没有音频流时无从合成,直接发送视频流 */
919
+ const resultPath = mp3File ? Common.tempDri.video + `Bil_Result_${infoData.data.data.bvid}.mp4` : mp4File.filepath
920
+ const success = mp3File ? await mergeVideoAudio(mp4File.filepath, mp3File.filepath, resultPath) : true
921
+
922
+ if (success) {
923
+ const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`
924
+ fs.renameSync(resultPath, filePath)
925
+ logger.mark(`视频文件重命名完成: ${resultPath.split('/').pop()} -> ${filePath.split('/').pop()}`)
926
+ logger.mark('正在尝试删除缓存文件')
927
+ if (fs.existsSync(mp4File.filepath)) await Common.removeFile(mp4File.filepath, true)
928
+ if (mp3File) await Common.removeFile(mp3File.filepath, true)
929
+
930
+ const stats = fs.statSync(filePath)
931
+ const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2))
932
+ if (fileSizeInMB > Config.app.groupfilevalue) {
933
+ // 使用文件上传
934
+ await uploadFile(
935
+ this.e,
936
+ {
937
+ filepath: filePath,
938
+ totalBytes: fileSizeInMB,
939
+ originTitle: `${infoData.data.data.desc.substring(0, 50).replace(/[\\/:\\*\\?"<>\\|\r\n\s]/g, ' ')}`
940
+ },
941
+ '',
942
+ { useGroupFile: true, active: true, activeOption: { group_id: groupId, uin: botId } }
943
+ )
944
+ } else {
945
+ /** 因为本地合成,没有视频直链 */
946
+ await uploadFile(this.e, { filepath: filePath, totalBytes: fileSizeInMB }, '', {
947
+ active: true,
948
+ activeOption: { group_id: groupId, uin: botId }
949
+ })
950
+ }
951
+ } else {
952
+ await Common.removeFile(mp4File.filepath, true)
953
+ if (mp3File) await Common.removeFile(mp3File.filepath, true)
954
+ }
955
+ }
956
+ }
957
+ break
958
+ }
959
+ case 'DYNAMIC_TYPE_DRAW': {
960
+ const imgArray = []
961
+ const temp: fileInfo[] = []
962
+ let hasGeneratedLivePhoto = false // 标记是否生成了实况图
963
+ const title = data[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.title || 'bilibili_dynamic'
964
+ const images =
965
+ (data[dynamicId].Dynamic_Data.modules.module_dynamic.major &&
966
+ data[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.draw?.items) ||
967
+ data[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.opus.pics
968
+ if (images.length === 0) break
969
+ for (const [index, img2] of images.entries()) {
970
+ const imageSrc = img2.src ?? img2.url
971
+ if (img2.live_url && imageSrc) {
972
+ const livePhoto = await downloadFile(img2.live_url, {
973
+ title: `Bilibili_tmp_V_${Date.now()}_${index}.mp4`,
974
+ headers: bilibiliBaseHeaders
975
+ })
976
+
977
+ if (livePhoto.filepath) {
978
+ const outputPath = Common.tempDri.video + `Bilibili_Live_${Date.now()}_${index}.mp4`
979
+ const staticImg = await downloadFile(imageSrc, {
980
+ title: `Bilibili_static_${Date.now()}_${index}.jpg`,
981
+ headers: bilibiliBaseHeaders,
982
+ filepath: Common.tempDri.images + `Bilibili_static_${Date.now()}_${index}.jpg`
983
+ })
984
+ if (staticImg.filepath) {
985
+ temp.push({ filepath: staticImg.filepath, totalBytes: 0 })
986
+ }
987
+
988
+ // 根据 livePhotoMode 配置决定处理方式
989
+ const livePhotoMode = Config.app.livePhotoMode ?? 'video_and_livephoto'
990
+ const shouldGenerateVideo = livePhotoMode === 'video_and_livephoto' || livePhotoMode === 'video_only'
991
+ const shouldGenerateLivePhoto = livePhotoMode === 'video_and_livephoto' || livePhotoMode === 'livephoto_only'
992
+
993
+ const loopCount = 3
994
+ if (!staticImg.filepath) {
995
+ await Common.removeFile(livePhoto.filepath, true)
996
+ continue
997
+ }
998
+
999
+ // 生成视频
1000
+ if (shouldGenerateVideo) {
1001
+ const result = await loopVideoWithTransition({
1002
+ inputPath: livePhoto.filepath,
1003
+ outputPath,
1004
+ loopCount,
1005
+ staticImagePath: staticImg.filepath,
1006
+ transitionEnabled: loopCount > 1
1007
+ })
1008
+ const success = result.success
1009
+
1010
+ if (success) {
1011
+ const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`
1012
+ fs.renameSync(outputPath, filePath)
1013
+ logger.mark(`视频文件重命名完成: ${outputPath.split('/').pop()} -> ${filePath.split('/').pop()}`)
1014
+ temp.push({ filepath: filePath, totalBytes: 0 })
1015
+ const videoPath =
1016
+ Config.app.videoSendMode === 'base64'
1017
+ ? `base64://${fs.readFileSync(filePath).toString('base64')}`
1018
+ : `file://${filePath}`
1019
+ imgArray.push(segment.video(videoPath))
1020
+ }
1021
+ }
1022
+
1023
+ // 生成实况图
1024
+ if (shouldGenerateLivePhoto) {
1025
+ let hasPushedMotionPhotoCover = false
1026
+ if (staticImg.filepath) {
1027
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${Date.now()}_${index}.jpg`
1028
+ const motionPhotoCreated = await buildGoogleMotionPhoto({
1029
+ imagePath: staticImg.filepath,
1030
+ videoPath: livePhoto.filepath,
1031
+ outputPath: motionPhotoCoverPath
1032
+ })
1033
+ if (motionPhotoCreated) {
1034
+ temp.push({ filepath: motionPhotoCoverPath, totalBytes: 0 })
1035
+ const motionPhotoCover = processLocalImageFile(motionPhotoCoverPath)
1036
+ imgArray.push(segment.image(motionPhotoCover))
1037
+ hasPushedMotionPhotoCover = true
1038
+ }
1039
+ }
1040
+ if (!hasPushedMotionPhotoCover) {
1041
+ const imageUrl = await processImageUrl(imageSrc, title, index)
1042
+ imgArray.push(segment.image(imageUrl))
1043
+ } else {
1044
+ hasGeneratedLivePhoto = true // 标记已生成实况图
1045
+ }
1046
+ }
1047
+
1048
+ logger.mark('正在尝试删除缓存文件')
1049
+ await Common.removeFile(livePhoto.filepath, true)
1050
+ continue
1051
+ }
1052
+ }
1053
+ if (imageSrc) {
1054
+ const imageUrl = await processImageUrl(imageSrc, title, index)
1055
+ imgArray.push(segment.image(imageUrl))
1056
+ }
1057
+ }
1058
+
1059
+ // 如果生成了实况图,添加提示文字
1060
+ if (hasGeneratedLivePhoto) {
1061
+ const tipImg = await Render(this.e, 'other/live-photo-tip', {
1062
+ title: '实况照片已生成',
1063
+ description: '保存原图到相册即可识别为实况图'
1064
+ })
1065
+ imgArray.push(...tipImg)
1066
+ }
1067
+
1068
+ const forwardMsg = common.makeForward(imgArray, botId, bot.account.name)
1069
+ try {
1070
+ await bot.sendForwardMsg(Contact, forwardMsg, {
1071
+ source: '图片合集',
1072
+ summary: `查看${imgArray.length}张图片消息`,
1073
+ prompt: 'B站图文动态解析结果',
1074
+ news: [{ text: '点击查看解析结果' }]
1075
+ })
1076
+ } finally {
1077
+ for (const item of temp) {
1078
+ await Common.removeFile(item.filepath, true)
1079
+ }
1080
+ }
1081
+ break
1082
+ }
1083
+ case 'DYNAMIC_TYPE_ARTICLE': {
1084
+ let payload = articleForwardPayload
1085
+ if (!payload) {
1086
+ const articleInfoBase = await this.amagi.bilibili.fetcher.fetchArticleInfo({
1087
+ id: data[dynamicId].Dynamic_Data.basic.rid_str
1088
+ })
1089
+ const articleInfo = await this.amagi.bilibili.fetcher.fetchArticleContent({
1090
+ id: data[dynamicId].Dynamic_Data.basic.rid_str
1091
+ })
1092
+ // 生成类型里 `data` 可空,没正文就没什么可转发的
1093
+ const articleContent = articleInfo.data.data
1094
+ if (!articleContent) {
1095
+ logger.warn(`专栏 ${data[dynamicId].Dynamic_Data.basic.rid_str} 未返回正文,跳过解析`)
1096
+ break
1097
+ }
1098
+ payload = {
1099
+ body: buildBilibiliArticleRichText(articleContent.opus, articleContent.content, Common.useDarkTheme()),
1100
+ title: articleInfoBase.data.data.title,
1101
+ summary: articleInfoBase.data.data.summary,
1102
+ shareUrl: articleContent.dyn_id_str
1103
+ ? `https://www.bilibili.com/opus/${articleContent.dyn_id_str}`
1104
+ : `https://www.bilibili.com/read/cv${articleContent.id}`
1105
+ }
1106
+ }
1107
+
1108
+ const title = payload.title || 'bilibili_article'
1109
+ const messageElements = await buildBilibiliRichTextForwardMessage(payload.body, {
1110
+ title: payload.title,
1111
+ summary: payload.summary,
1112
+ shareUrl: payload.shareUrl,
1113
+ imageResolver: (src, index) => processImageUrl(src, title, index)
1114
+ })
1115
+ if (messageElements.length > 0) {
1116
+ const forwardMsg = common.makeForward(messageElements, botId, bot.account.name)
1117
+ await bot.sendForwardMsg(Contact, forwardMsg, {
1118
+ source: '专栏内容',
1119
+ summary: `查看${messageElements.length}条专栏内容`,
1120
+ prompt: 'B站专栏动态解析结果',
1121
+ news: [{ text: '点击查看解析结果' }]
1122
+ })
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ if (skip || (status && status?.messageId)) {
1130
+ // 使用新的数据库API添加动态缓存
1131
+ await bilibiliDB.addDynamicCache(dynamicId, data[dynamicId].host_mid, target.groupId, data[dynamicId].dynamic_type)
1132
+ }
1133
+ }
1134
+ }
1135
+ }
1136
+
1137
+ /**
1138
+ * 根据配置文件获取UP当天的动态列表。
1139
+ * @param userList B站订阅列表。
1140
+ * @returns
1141
+ */
1142
+ async getDynamicList(userList: bilibiliPushItem[]) {
1143
+ const directLiveItems = await this.getDirectLivePushItems(userList)
1144
+ const willbepushlist: WillBePushList = { ...directLiveItems.willBePushList }
1145
+
1146
+ try {
1147
+ /** 过滤掉不启用的订阅项 */
1148
+ const filteredUserList = userList.filter((item) => item.switch !== false)
1149
+ for (const item of filteredUserList) {
1150
+ const pushTypes = item.pushTypes || allBilibiliPushTypes
1151
+ const allowedDynamicTypes = new Set(pushTypes.map((pt) => pushTypeToDynamicType[pt]))
1152
+ if (directLiveItems.handledUids.has(item.host_mid)) {
1153
+ allowedDynamicTypes.delete(DynamicType.LIVE_RCMD)
1154
+ }
1155
+ if (allowedDynamicTypes.size === 0) continue
1156
+
1157
+ await common.sleep(2000)
1158
+ logger.debug(`[Bilibili 推送] 开始获取UP: ${item.remark}(${item.host_mid}) 的动态列表`)
1159
+ const dynamic_list = await this.amagi.bilibili.fetcher.fetchUserDynamicList({
1160
+ host_mid: item.host_mid
1161
+ })
1162
+ // 生成树这条响应没按动态类型分形状,推送链路要的是判别联合(见 DataItem 的说明),这里换一次
1163
+ const dynamicItems = dynamic_list.data.data.items as unknown as DataItem[]
1164
+ if (dynamicItems.length > 0) {
1165
+ // 遍历接口返回的视频列表
1166
+ for (const dynamic of dynamicItems) {
1167
+ const nowSeconds = Math.floor(Date.now() / 1000) // 当前时间戳(秒)
1168
+ const createTime = dynamic.modules.module_author.pub_ts // 发布时间戳(秒)
1169
+ const timeDifference = nowSeconds - createTime // 时间差(秒)
1170
+
1171
+ const is_top = dynamic.modules.module_tag?.text === '置顶' // 是否为置顶
1172
+ let shouldPush = false // 是否列入推送数组
1173
+
1174
+ const timeDiffHours = Math.round((timeDifference / 3600) * 100) / 100 // 保留2位小数
1175
+
1176
+ // 条件判断,以下任何一项成立都将进行推送:如果是置顶且发布时间在一天内 || 如果是置顶作品且有新的群组且发布时间在一天内 || 如果有新的群组且发布时间在一天内
1177
+ logger.trace(`
1178
+ 前期获取该动态基本信息:
1179
+ UP主:${dynamic.modules.module_author.name}
1180
+ 动态ID:${dynamic.id_str}
1181
+ 发布时间:${TimeFormatter.toDateTime(createTime)}
1182
+ 发布时间戳(s):${createTime}
1183
+ 当前时间戳(s):${nowSeconds}
1184
+ 时间差(s):${timeDifference}s (${timeDiffHours}h)
1185
+ 是否置顶:${is_top}
1186
+ 是否在一天内:${timeDifference < 86400 ? logger.green('true') : logger.red('false')}
1187
+ `)
1188
+
1189
+ if ((is_top && timeDifference < 86400) || timeDifference < 86400) {
1190
+ shouldPush = true
1191
+ logger.trace(
1192
+ logger.green(`根据以上判断,shoulPush 为 true,将对该动态纳入当天推送列表:https://t.bilibili.com/${dynamic.id_str}\n`)
1193
+ )
1194
+ } else logger.trace(logger.yellow(`根据以上判断,shoulPush 为 false,跳过该动态:https://t.bilibili.com/${dynamic.id_str}\n`))
1195
+ // 如果 shouldPush 为 true,或该作品距现在的时间差小于一天,则将该动态添加到 willbepushlist 中
1196
+ if (timeDifference < 86400 || shouldPush) {
1197
+ // 根据推送类型过滤
1198
+ if (!allowedDynamicTypes.has(dynamic.type as DynamicType)) {
1199
+ logger.debug(
1200
+ `UP主 ${item.remark}(${item.host_mid})的动态 ${dynamic.id_str} 类型为「${dynamic.type}」,不在推送类型配置中,跳过`
1201
+ )
1202
+ continue
1203
+ }
1204
+
1205
+ // 将群组ID和机器人ID分离
1206
+ const targets = item.group_id.map((groupWithBot) => {
1207
+ const [groupId, botId] = groupWithBot.split(':')
1208
+ return { groupId, botId }
1209
+ })
1210
+ const pushId =
1211
+ dynamic.type === DynamicType.LIVE_RCMD
1212
+ ? await this.resolveLiveDynamicCacheId(dynamic as Extract<DataItem, { type: DynamicType.LIVE_RCMD }>, item.host_mid)
1213
+ : dynamic.id_str
1214
+
1215
+ // 确保 willbepushlist[pushId] 是一个对象
1216
+ if (!willbepushlist[pushId]) {
1217
+ willbepushlist[pushId] = {
1218
+ remark: item.remark,
1219
+ host_mid: item.host_mid,
1220
+ create_time: dynamic.modules.module_author.pub_ts,
1221
+ targets,
1222
+ Dynamic_Data: dynamic, // 存储 dynamic 对象
1223
+ avatar_img: dynamic.modules.module_author.face,
1224
+ dynamic_type: dynamic.type as DynamicType
1225
+ } as BilibiliPushItem
1226
+ }
1227
+ }
1228
+ }
1229
+ } else {
1230
+ }
1231
+ }
1232
+ } catch (error) {
1233
+ throw new Error(`获取动态列表失败: ${error}`)
1234
+ }
1235
+
1236
+ return { willbepushlist }
1237
+ }
1238
+
1239
+ /**
1240
+ * 排除已推送过的群组并返回更新后的推送列表
1241
+ * @param willBePushList 将要推送的列表
1242
+ * @returns 更新后的推送列表
1243
+ */
1244
+ async excludeAlreadyPushed(willBePushList: WillBePushList): Promise<WillBePushList> {
1245
+ // 遍历推送列表中的作品ID
1246
+ for (const dynamicId in willBePushList) {
1247
+ const pushItem = willBePushList[dynamicId]
1248
+ const newTargets: Array<{ groupId: string; botId: string }> = []
1249
+
1250
+ // 遍历作品对应的目标群组
1251
+ for (const target of pushItem.targets) {
1252
+ // 检查该动态是否已经推送给该群组
1253
+ const isPushed = await bilibiliDB.isDynamicPushed(dynamicId, pushItem.host_mid, target.groupId)
1254
+
1255
+ // 如果未被推送过,则保留此目标
1256
+ if (!isPushed) {
1257
+ newTargets.push(target)
1258
+ }
1259
+ }
1260
+
1261
+ // 更新作品的目标数组
1262
+ if (newTargets.length > 0) {
1263
+ pushItem.targets = newTargets
1264
+ } else {
1265
+ // 如果没有剩余目标,移除该作品
1266
+ delete willBePushList[dynamicId]
1267
+ }
1268
+ }
1269
+
1270
+ return willBePushList
1271
+ }
1272
+
1273
+ /**
1274
+ * 设置或更新特定 host_mid 的群组信息。
1275
+ * @param data 包含 card 对象。
1276
+ * @returns 操作成功或失败的消息字符串。
1277
+ */
1278
+ async setting(data: BilibiliUserCardResponse): Promise<void> {
1279
+ const groupInfo = await this.e.bot.getGroupInfo('groupId' in this.e && this.e.groupId ? this.e.groupId : '')
1280
+ const host_mid = Number(data.data.card.mid)
1281
+ const config = Config.pushlist // 读取配置文件
1282
+ const groupId = 'groupId' in this.e && this.e.groupId ? this.e.groupId : ''
1283
+ const botId = this.e.selfId
1284
+
1285
+ // 初始化或确保 bilibilipushlist 数组存在
1286
+ if (!config.bilibili) {
1287
+ config.bilibili = []
1288
+ }
1289
+
1290
+ // 检查是否存在相同的 host_mid
1291
+ const existingItem = config.bilibili.find((item: { host_mid: number }) => item.host_mid === host_mid)
1292
+
1293
+ // 检查该群组是否已订阅该UP主
1294
+ const isSubscribed = await bilibiliDB.isSubscribed(host_mid, groupId)
1295
+
1296
+ if (existingItem) {
1297
+ // 如果已经存在相同的 host_mid,则检查是否存在相同的 group_id
1298
+ let has = false
1299
+ let groupIndexToRemove = -1 // 用于记录要删除的 group_id 对象的索引
1300
+
1301
+ for (let index = 0; index < existingItem.group_id.length; index++) {
1302
+ const item = existingItem.group_id[index]
1303
+ const existingGroupId = item.split(':')[0]
1304
+
1305
+ if (existingGroupId === String(groupId)) {
1306
+ has = true
1307
+ groupIndexToRemove = index
1308
+ break
1309
+ }
1310
+ }
1311
+
1312
+ if (has) {
1313
+ // 如果已存在相同的 group_id,则删除它
1314
+ existingItem.group_id.splice(groupIndexToRemove, 1)
1315
+
1316
+ // 在数据库中取消订阅
1317
+ if (isSubscribed) {
1318
+ await bilibiliDB.unsubscribeBilibiliUser(groupId, host_mid)
1319
+ }
1320
+
1321
+ // 如果删除后 group_id 数组为空,则删除整个属性
1322
+ if (existingItem.group_id.length === 0) {
1323
+ const index = config.bilibili.indexOf(existingItem)
1324
+ config.bilibili.splice(index, 1)
1325
+ }
1326
+
1327
+ // 保存配置到文件
1328
+ Config.Modify('pushlist', 'bilibili', config.bilibili)
1329
+ await this.e.reply(`群:${groupInfo.groupName}(${groupId})\n删除成功!${data.data.card.name}\nUID:${host_mid}`)
1330
+ logger.info(`\n删除成功!${data.data.card.name}\nUID:${host_mid}`)
1331
+ } else {
1332
+ // 在数据库中添加订阅
1333
+ await bilibiliDB.subscribeBilibiliUser(groupId, botId, host_mid, data.data.card.name)
1334
+
1335
+ // 将新的 group_id 添加到该 host_mid 对应的数组中
1336
+ existingItem.group_id.push(`${groupId}:${botId}`)
1337
+
1338
+ // 确保 pushTypes 字段存在,如果不存在则补全为全部推送类型
1339
+ if (!existingItem.pushTypes || existingItem.pushTypes.length === 0) {
1340
+ existingItem.pushTypes = [...allBilibiliPushTypes]
1341
+ }
1342
+
1343
+ // 保存配置到文件
1344
+ Config.Modify('pushlist', 'bilibili', config.bilibili)
1345
+ await this.e.reply(`群:${groupInfo.groupName}(${groupId})\n添加成功!${data.data.card.name}\nUID:${host_mid}`)
1346
+ if (Config.bilibili.push.switch === false) await this.e.reply('请发送「#设置B站推送开启」以进行推送')
1347
+
1348
+ logger.info(`\n设置成功!${data.data.card.name}\nUID:${host_mid}`)
1349
+ }
1350
+ } else {
1351
+ // 在数据库中添加订阅
1352
+ await bilibiliDB.subscribeBilibiliUser(groupId, botId, host_mid, data.data.card.name)
1353
+
1354
+ // 不存在相同的 host_mid,新增一个配置项
1355
+ config.bilibili.push({
1356
+ switch: true,
1357
+ host_mid,
1358
+ group_id: [`${groupId}:${botId}`],
1359
+ remark: data.data.card.name,
1360
+ pushTypes: [...allBilibiliPushTypes]
1361
+ })
1362
+
1363
+ // 保存配置到文件
1364
+ Config.Modify('pushlist', 'bilibili', config.bilibili)
1365
+ await this.e.reply(`群:${groupInfo.groupName}(${groupId})\n添加成功!${data.data.card.name}\nUID:${host_mid}`)
1366
+ if (Config.bilibili.push.switch === false) await this.e.reply('请发送「#设置B站推送开启」以进行推送')
1367
+ logger.info(`\n设置成功!${data.data.card.name}\nUID:${host_mid}`)
1368
+ }
1369
+
1370
+ // 渲染状态图片
1371
+ await this.renderPushList()
1372
+ }
1373
+
1374
+ /**
1375
+ * 检查并更新配置文件中指定用户的备注信息。
1376
+ * 该函数会遍历配置文件中的用户列表,对于没有备注或备注为空的用户,会从外部数据源获取其备注信息,并更新到配置文件中。
1377
+ */
1378
+ async checkremark() {
1379
+ // 读取配置文件内容
1380
+ const config = Config.pushlist
1381
+ const abclist: { host_mid: number }[] | { host_mid: number; group_id: string[] }[] = []
1382
+ if (Config.pushlist.bilibili === null || Config.pushlist.bilibili.length === 0) return true
1383
+ // 遍历配置文件中的用户列表,收集需要更新备注信息的用户
1384
+ for (const i of Config.pushlist.bilibili) {
1385
+ const remark = i.remark
1386
+ const group_id = i.group_id
1387
+ const host_mid = i.host_mid
1388
+
1389
+ if (remark === undefined || remark === '') {
1390
+ abclist.push({ host_mid, group_id })
1391
+ }
1392
+ }
1393
+
1394
+ // 如果有需要更新备注的用户,则逐个获取备注信息并更新到配置文件中
1395
+ if (abclist.length > 0) {
1396
+ for (const i of abclist) {
1397
+ // 从外部数据源获取用户备注信息
1398
+ const resp = await this.amagi.bilibili.fetcher.fetchUserCard({ host_mid: i.host_mid })
1399
+ const remark = resp.data.data.card.name
1400
+ // 在配置文件中找到对应的用户,并更新其备注信息
1401
+ const matchingItemIndex = config.bilibili.findIndex((item) => item.host_mid === i.host_mid)
1402
+ if (matchingItemIndex !== -1) {
1403
+ config.bilibili[matchingItemIndex].remark = remark
1404
+ }
1405
+ }
1406
+ // 将更新后的配置文件内容写回文件
1407
+ Config.Modify('pushlist', 'bilibili', config.bilibili)
1408
+ }
1409
+ }
1410
+
1411
+ /**
1412
+ * 强制推送
1413
+ * @param data 处理完成的推送列表
1414
+ */
1415
+ async forcepush(data: WillBePushList) {
1416
+ const currentGroupId = 'groupId' in this.e && this.e.groupId ? this.e.groupId : ''
1417
+ const currentBotId = this.e.selfId
1418
+
1419
+ // 如果不是全部强制推送,需要过滤数据
1420
+ if (!this.e.msg.includes('全部')) {
1421
+ // 获取当前群组订阅的所有UP主
1422
+ const subscriptions = await bilibiliDB.getGroupSubscriptions(currentGroupId)
1423
+ const subscribedUids = subscriptions.map((sub) => sub.host_mid)
1424
+
1425
+ /** 创建一个新的推送列表,只包含当前群组订阅的UP主的动态 */
1426
+ const filteredData: WillBePushList = {}
1427
+
1428
+ for (const dynamicId in data) {
1429
+ // 检查该动态的UP主是否被当前群组订阅
1430
+ if (subscribedUids.includes(data[dynamicId].host_mid)) {
1431
+ // 复制该动态到过滤后的列表,并将目标设置为当前群组
1432
+ filteredData[dynamicId] = {
1433
+ ...data[dynamicId],
1434
+ targets: [
1435
+ {
1436
+ groupId: currentGroupId,
1437
+ botId: currentBotId
1438
+ }
1439
+ ]
1440
+ } as BilibiliPushItem
1441
+ }
1442
+ }
1443
+
1444
+ // 使用过滤后的数据进行推送
1445
+ await this.getdata(filteredData)
1446
+ } else {
1447
+ // 全部强制推送,保持原有逻辑
1448
+ await this.getdata(data)
1449
+ }
1450
+ }
1451
+
1452
+ /** 渲染推送列表图片 */
1453
+ async renderPushList() {
1454
+ await this.syncConfigToDatabase()
1455
+ const groupInfo = await this.e.bot.getGroupInfo('groupId' in this.e && this.e.groupId ? this.e.groupId : '')
1456
+
1457
+ // 获取当前群组的所有订阅
1458
+ const subscriptions = await bilibiliDB.getGroupSubscriptions(groupInfo.groupId)
1459
+
1460
+ if (subscriptions.length === 0) {
1461
+ await this.e.reply(
1462
+ `当前群:${groupInfo.groupName}(${groupInfo.groupId})\n没有设置任何B站UP推送!\n可使用「#设置B站推送 + UP主UID」进行设置`
1463
+ )
1464
+ return
1465
+ }
1466
+
1467
+ /** 用户的今日动态列表 */
1468
+ const renderOpt: BilibiliUserListData['renderOpt'] = []
1469
+
1470
+ // 获取所有订阅UP主的信息
1471
+ for (const subscription of subscriptions) {
1472
+ const host_mid = subscription.host_mid
1473
+ const userInfo = await this.amagi.bilibili.fetcher.fetchUserCard({ host_mid })
1474
+
1475
+ // 查找配置文件中对应的全局开关状态
1476
+ const configItem = Config.pushlist.bilibili?.find((item: bilibiliPushItem) => item.host_mid === host_mid)
1477
+ const switchStatus = configItem?.switch !== false // 默认为 true
1478
+ const pushTypes = configItem?.pushTypes || allBilibiliPushTypes
1479
+
1480
+ renderOpt.push({
1481
+ avatar_img: userInfo.data.data.card.face,
1482
+ username: userInfo.data.data.card.name,
1483
+ host_mid: userInfo.data.data.card.mid,
1484
+ fans: Count(userInfo.data.data.follower),
1485
+ total_favorited: Count(userInfo.data.data.like_num),
1486
+ following_count: Count(userInfo.data.data.card.attention),
1487
+ switch: switchStatus,
1488
+ pushTypes
1489
+ })
1490
+ }
1491
+
1492
+ const img = await Render(this.e, 'bilibili/userlist', {
1493
+ renderOpt,
1494
+ groupInfo: {
1495
+ groupId: groupInfo.groupId || '',
1496
+ groupName: groupInfo.groupName || '',
1497
+ groupAvatar: groupInfo.avatar || ''
1498
+ }
1499
+ })
1500
+ await this.e.reply(img)
1501
+ }
1502
+ }
1503
+
1504
+ /**
1505
+ * 把直播状态接口的数据适配为现有直播动态分支实际读取的字段。
1506
+ *
1507
+ * 后续过滤、渲染、发送和缓存均继续使用统一的动态推送链路。
1508
+ */
1509
+ const createLiveDynamicItem = (
1510
+ sessionId: string,
1511
+ liveStartedAt: string,
1512
+ liveInfo: BilibiliLiveRoomInfoResponse['data'],
1513
+ liveStatus: BilibiliUserLiveStatusResponse['data']
1514
+ ): Extract<DataItem, { type: DynamicType.LIVE_RCMD }> => {
1515
+ const content = JSON.stringify({
1516
+ live_play_info: {
1517
+ area_name: liveInfo.area_name,
1518
+ cover: liveInfo.user_cover || liveStatus.cover,
1519
+ room_id: liveInfo.room_id,
1520
+ title: liveInfo.title || liveStatus.title
1521
+ }
1522
+ })
1523
+
1524
+ return {
1525
+ id_str: sessionId,
1526
+ type: DynamicType.LIVE_RCMD,
1527
+ modules: {
1528
+ module_author: {
1529
+ face: '',
1530
+ pendant: { image: '' },
1531
+ pub_ts: Math.floor(Date.parse(liveStartedAt) / 1000)
1532
+ },
1533
+ module_dynamic: {
1534
+ additional: null,
1535
+ desc: null,
1536
+ major: {
1537
+ live_rcmd: { content, reserve_type: 0 },
1538
+ type: MajorType.LIVE_RCMD
1539
+ },
1540
+ topic: null
1541
+ }
1542
+ }
1543
+ } as unknown as Extract<DataItem, { type: DynamicType.LIVE_RCMD }>
1544
+ }
1545
+
1546
+ /** 将未知异常转换为适合业务日志展示的文本。 */
1547
+ const formatErrorMessage = (error: unknown): string => (error instanceof Error ? error.message : String(error))
1548
+
1549
+ /**
1550
+ * 将换行符替换为HTML的<br>标签。
1551
+ * @param data 需要进行换行符替换的字符串。
1552
+ * @returns 替换后的字符串,其中的换行符\n被<br>替换。
1553
+ */
1554
+ const br = (data: string): string => {
1555
+ // 使用正则表达式将所有换行符替换为<br>
1556
+ return (data = data.replace(/\n/g, '<br>'))
1557
+ }
1558
+
1559
+ /**
1560
+ * 根据动态类型构造用于「解析」按钮的地址。
1561
+ * 视频动态使用 BV 视频地址,其余动态使用动态地址;直播动态返回空字符串(不追加按钮)。
1562
+ * @param PushItem 推送项
1563
+ * @returns 解析地址,无法解析时返回空字符串
1564
+ */
1565
+ const buildParseUrl = (PushItem: BilibiliPushItem): string => {
1566
+ switch (PushItem.dynamic_type) {
1567
+ case DynamicType.AV: {
1568
+ const bvid = PushItem.Dynamic_Data.modules.module_dynamic.major?.archive?.bvid
1569
+ return bvid ? `https://www.bilibili.com/video/${bvid}` : ''
1570
+ }
1571
+ case DynamicType.LIVE_RCMD:
1572
+ return ''
1573
+ default:
1574
+ return `https://t.bilibili.com/${PushItem.Dynamic_Data.id_str}`
1575
+ }
1576
+ }
1577
+
1578
+ /**
1579
+ * 判断标题是否有屏蔽词或屏蔽标签
1580
+ * @param PushItem 推送项
1581
+ * @returns 是否应该跳过推送
1582
+ */
1583
+ const skipDynamic = async (PushItem: BilibiliPushItem): Promise<boolean> => {
1584
+ const tags: string[] = []
1585
+
1586
+ // 提取标签
1587
+ if (PushItem.Dynamic_Data.modules.module_dynamic?.desc?.rich_text_nodes) {
1588
+ for (const node of PushItem.Dynamic_Data.modules.module_dynamic.desc.rich_text_nodes) {
1589
+ if (node.type === 'topic') {
1590
+ if (node.orig_text) {
1591
+ tags.push(node.orig_text)
1592
+ }
1593
+ }
1594
+ }
1595
+ }
1596
+
1597
+ // 检查转发的原动态标签
1598
+ if (PushItem.Dynamic_Data.type === DynamicType.FORWARD && 'orig' in PushItem.Dynamic_Data) {
1599
+ if (
1600
+ PushItem.Dynamic_Data.orig.modules.module_dynamic.major.type === MajorType.DRAW ||
1601
+ PushItem.Dynamic_Data.orig.modules.module_dynamic.major.type === MajorType.OPUS ||
1602
+ PushItem.Dynamic_Data.orig.modules.module_dynamic.major.type === MajorType.LIVE_RCMD
1603
+ ) {
1604
+ for (const node of PushItem.Dynamic_Data.orig.modules.module_dynamic.major.opus.summary.rich_text_nodes) {
1605
+ if (node.type === 'topic') {
1606
+ tags.push(node.orig_text)
1607
+ }
1608
+ }
1609
+ }
1610
+ }
1611
+
1612
+ logger.debug(`检查动态是否需要过滤:https://t.bilibili.com/${PushItem.Dynamic_Data.id_str}`)
1613
+ const shouldFilter = await bilibiliDB.shouldFilter(PushItem, tags)
1614
+ return shouldFilter
1615
+ }