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,1218 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ import { logger } from 'node-karin'
5
+ import { karinPathBase } from 'node-karin/root'
6
+ import sqlite3, { sqlite3 as sqlite3Types } from 'node-karin/sqlite3'
7
+
8
+ import { Root } from '@/module/utils'
9
+ import { Config } from '@/module/utils/Config'
10
+ import { DouyinWorkPushItem } from '@/platform/douyin/push'
11
+ import { douyinPushItem } from '@/types/config/pushlist'
12
+
13
+ /**
14
+ * 机器人接口 - 存储机器人信息
15
+ */
16
+ interface Bot {
17
+ /** 机器人ID */
18
+ id: string
19
+ /** 创建时间 */
20
+ createdAt: string
21
+ /** 更新时间 */
22
+ updatedAt: string
23
+ }
24
+
25
+ /**
26
+ * 群组接口 - 存储群组信息
27
+ */
28
+ interface Group {
29
+ /** 群组ID */
30
+ id: string
31
+ /** 所属机器人ID */
32
+ botId: string
33
+ /** 创建时间 */
34
+ createdAt: string
35
+ /** 更新时间 */
36
+ updatedAt: string
37
+ }
38
+
39
+ /**
40
+ * 抖音用户接口 - 存储抖音用户信息
41
+ */
42
+ interface DouyinUser {
43
+ /** 抖音用户sec_uid */
44
+ sec_uid: string
45
+ /** 抖音号 */
46
+ short_id?: string
47
+ /** 抖音用户昵称 */
48
+ remark?: string
49
+ /** 是否正在直播 */
50
+ living: boolean
51
+ /** 过滤模式:黑名单或白名单 */
52
+ filterMode: 'blacklist' | 'whitelist'
53
+ /** 创建时间 */
54
+ createdAt: string
55
+ /** 更新时间 */
56
+ updatedAt: string
57
+ }
58
+
59
+ /**
60
+ * 群组用户订阅关系接口 - 存储群组订阅的抖音用户关系
61
+ */
62
+ interface GroupUserSubscription {
63
+ /** 群组ID */
64
+ groupId: string
65
+ /** 抖音用户sec_uid */
66
+ sec_uid: string
67
+ /** 创建时间 */
68
+ createdAt: string
69
+ /** 更新时间 */
70
+ updatedAt: string
71
+ }
72
+
73
+ /**
74
+ * 作品缓存接口 - 存储已推送的作品ID
75
+ */
76
+ interface AwemeCache {
77
+ /** 缓存ID */
78
+ id: number
79
+ /** 作品ID */
80
+ aweme_id: string
81
+ /** 抖音用户sec_uid */
82
+ sec_uid: string
83
+ /** 群组ID */
84
+ groupId: string
85
+ /** 推送类型:post(作品列表)、favorite(喜欢列表)、recommend(推荐列表)、live(直播) */
86
+ pushType: string
87
+ /** 创建时间 */
88
+ createdAt: string
89
+ /** 更新时间 */
90
+ updatedAt: string
91
+ }
92
+
93
+ /**
94
+ * 过滤词接口 - 存储过滤词
95
+ */
96
+ interface FilterWord {
97
+ /** 过滤词ID */
98
+ id: number
99
+ /** 抖音用户sec_uid */
100
+ sec_uid: string
101
+ /** 过滤词 */
102
+ word: string
103
+ /** 创建时间 */
104
+ createdAt: string
105
+ /** 更新时间 */
106
+ updatedAt: string
107
+ }
108
+
109
+ /**
110
+ * 过滤标签接口 - 存储过滤标签
111
+ */
112
+ interface FilterTag {
113
+ /** 过滤标签ID */
114
+ id: number
115
+ /** 抖音用户sec_uid */
116
+ sec_uid: string
117
+ /** 过滤标签 */
118
+ tag: string
119
+ /** 创建时间 */
120
+ createdAt: string
121
+ /** 更新时间 */
122
+ updatedAt: string
123
+ }
124
+
125
+ /** 数据库操作类 */
126
+ export class DouyinDBBase {
127
+ private db!: sqlite3Types['Database']
128
+ private dbPath: string
129
+
130
+ constructor() {
131
+ this.dbPath = path.join(`${karinPathBase}/${Root.pluginName}/data`, 'douyin.db')
132
+ }
133
+
134
+ /**
135
+ * 初始化数据库
136
+ */
137
+ async init(): Promise<DouyinDBBase> {
138
+ try {
139
+ logger.debug(logger.green('--------------------------[DouyinDB] 开始初始化数据库--------------------------'))
140
+ logger.debug('[DouyinDB] 正在连接数据库...')
141
+
142
+ // 创建数据库连接
143
+ await fs.promises.mkdir(path.dirname(this.dbPath), { recursive: true })
144
+ this.db = new sqlite3.Database(this.dbPath)
145
+
146
+ // 创建表结构
147
+ await this.createTables()
148
+
149
+ // 检查并升级数据库结构
150
+ await this.migrate()
151
+
152
+ logger.debug('[DouyinDB] 数据库模型同步成功')
153
+
154
+ logger.debug('[DouyinDB] 正在同步配置订阅...')
155
+ logger.debug('[DouyinDB] 配置项数量:', Config.pushlist.douyin?.length || 0)
156
+ await this.syncConfigSubscriptions(Config.pushlist.douyin)
157
+ logger.debug('[DouyinDB] 配置订阅同步成功')
158
+ logger.debug(logger.green('--------------------------[DouyinDB] 初始化数据库完成--------------------------'))
159
+ } catch (error) {
160
+ logger.error('[DouyinDB] 数据库初始化失败:', error)
161
+ throw error
162
+ }
163
+
164
+ return this
165
+ }
166
+
167
+ /**
168
+ * 数据库迁移
169
+ */
170
+ private async migrate(): Promise<void> {
171
+ try {
172
+ // 检查 AwemeCaches 表是否有 pushType 字段
173
+ try {
174
+ await this.runQuery('SELECT pushType FROM AwemeCaches LIMIT 1')
175
+ } catch {
176
+ logger.info('[DouyinDB] 检测到 AwemeCaches 表缺少 pushType 字段,开始执行迁移...')
177
+
178
+ // 1. 添加 pushType 字段(默认值为 'post')
179
+ await this.runQuery("ALTER TABLE AwemeCaches ADD COLUMN pushType TEXT DEFAULT 'post'")
180
+
181
+ // 2. 由于 SQLite 不支持修改 UNIQUE 约束,我们需要重建表
182
+ // 步骤:重命名旧表 -> 创建新表 -> 复制数据 -> 删除旧表
183
+
184
+ await this.runQuery('ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old')
185
+
186
+ await this.runQuery(`
187
+ CREATE TABLE IF NOT EXISTS AwemeCaches (
188
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
189
+ aweme_id TEXT NOT NULL,
190
+ sec_uid TEXT NOT NULL,
191
+ groupId TEXT NOT NULL,
192
+ pushType TEXT DEFAULT 'post',
193
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
194
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
195
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
196
+ UNIQUE(aweme_id, sec_uid, groupId, pushType)
197
+ )
198
+ `)
199
+
200
+ await this.runQuery(`
201
+ INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)
202
+ SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt
203
+ FROM AwemeCaches_old
204
+ `)
205
+
206
+ await this.runQuery('DROP TABLE AwemeCaches_old')
207
+
208
+ logger.info('[DouyinDB] AwemeCaches 表迁移完成')
209
+ }
210
+
211
+ // 检查并修复外键约束问题
212
+ try {
213
+ // 尝试获取表结构
214
+ const tableInfo = await this.allQuery<{ sql: string }>("SELECT sql FROM sqlite_master WHERE type='table' AND name='AwemeCaches'")
215
+
216
+ if (tableInfo.length > 0 && tableInfo[0].sql.includes('FOREIGN KEY (groupId) REFERENCES Groups(id)')) {
217
+ logger.info('[DouyinDB] 检测到 AwemeCaches 表存在错误的外键约束,开始修复...')
218
+
219
+ // 重建表以移除错误的外键约束
220
+ await this.runQuery('ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old')
221
+
222
+ await this.runQuery(`
223
+ CREATE TABLE IF NOT EXISTS AwemeCaches (
224
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
225
+ aweme_id TEXT NOT NULL,
226
+ sec_uid TEXT NOT NULL,
227
+ groupId TEXT NOT NULL,
228
+ pushType TEXT DEFAULT 'post',
229
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
230
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
231
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
232
+ UNIQUE(aweme_id, sec_uid, groupId, pushType)
233
+ )
234
+ `)
235
+
236
+ await this.runQuery(`
237
+ INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)
238
+ SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt
239
+ FROM AwemeCaches_old
240
+ `)
241
+
242
+ await this.runQuery('DROP TABLE AwemeCaches_old')
243
+
244
+ logger.info('[DouyinDB] AwemeCaches 表外键约束修复完成')
245
+ }
246
+ } catch (error) {
247
+ logger.debug('[DouyinDB] 外键约束检查/修复跳过:', error)
248
+ }
249
+ } catch (error) {
250
+ logger.error('[DouyinDB] 数据库迁移失败:', error)
251
+ // 不抛出错误,以免影响程序启动,但记录日志
252
+ }
253
+ }
254
+
255
+ /**
256
+ * 创建数据库表结构
257
+ */
258
+ private async createTables(): Promise<void> {
259
+ const queries = [
260
+ // 创建机器人表
261
+ `CREATE TABLE IF NOT EXISTS Bots (
262
+ id TEXT PRIMARY KEY,
263
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
264
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
265
+ )`,
266
+
267
+ // 创建群组表
268
+ `CREATE TABLE IF NOT EXISTS Groups (
269
+ id TEXT NOT NULL,
270
+ botId TEXT NOT NULL,
271
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
272
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
273
+ PRIMARY KEY (id, botId),
274
+ FOREIGN KEY (botId) REFERENCES Bots(id)
275
+ )`,
276
+
277
+ // 创建抖音用户表
278
+ `CREATE TABLE IF NOT EXISTS DouyinUsers (
279
+ sec_uid TEXT PRIMARY KEY,
280
+ short_id TEXT,
281
+ remark TEXT,
282
+ living INTEGER DEFAULT 0,
283
+ filterMode TEXT DEFAULT 'blacklist',
284
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
285
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
286
+ )`,
287
+
288
+ // 创建群组用户订阅关系表
289
+ `CREATE TABLE IF NOT EXISTS GroupUserSubscriptions (
290
+ groupId TEXT,
291
+ sec_uid TEXT,
292
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
293
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
294
+ PRIMARY KEY (groupId, sec_uid),
295
+ FOREIGN KEY (groupId) REFERENCES Groups(id),
296
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid)
297
+ )`,
298
+
299
+ // 创建作品缓存表
300
+ `CREATE TABLE IF NOT EXISTS AwemeCaches (
301
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
302
+ aweme_id TEXT NOT NULL,
303
+ sec_uid TEXT NOT NULL,
304
+ groupId TEXT NOT NULL,
305
+ pushType TEXT DEFAULT 'post',
306
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
307
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
308
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
309
+ UNIQUE(aweme_id, sec_uid, groupId, pushType)
310
+ )`,
311
+
312
+ // 创建过滤词表
313
+ `CREATE TABLE IF NOT EXISTS FilterWords (
314
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
315
+ sec_uid TEXT NOT NULL,
316
+ word TEXT NOT NULL,
317
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
318
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
319
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
320
+ UNIQUE(sec_uid, word)
321
+ )`,
322
+
323
+ // 创建过滤标签表
324
+ `CREATE TABLE IF NOT EXISTS FilterTags (
325
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
326
+ sec_uid TEXT NOT NULL,
327
+ tag TEXT NOT NULL,
328
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
329
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
330
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
331
+ UNIQUE(sec_uid, tag)
332
+ )`,
333
+
334
+ // 创建列表快照表(用于喜欢列表和推荐列表)
335
+ `CREATE TABLE IF NOT EXISTS ListSnapshots (
336
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
337
+ sec_uid TEXT NOT NULL,
338
+ pushType TEXT NOT NULL,
339
+ aweme_id TEXT NOT NULL,
340
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
341
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
342
+ FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
343
+ UNIQUE(sec_uid, pushType, aweme_id)
344
+ )`
345
+ ]
346
+
347
+ for (const query of queries) {
348
+ await this.runQuery(query)
349
+ }
350
+ }
351
+
352
+ /**
353
+ * 执行SQL查询
354
+ */
355
+ private runQuery(sql: string, params: any[] = []): Promise<any> {
356
+ return new Promise((resolve, reject) => {
357
+ this.db.run(sql, params, function (err) {
358
+ if (err) {
359
+ reject(err)
360
+ } else {
361
+ resolve({ lastID: this.lastID, changes: this.changes })
362
+ }
363
+ })
364
+ })
365
+ }
366
+
367
+ /**
368
+ * 执行SQL查询并获取单个结果
369
+ */
370
+ private getQuery<T>(sql: string, params: any[] = []): Promise<T | undefined> {
371
+ return new Promise((resolve, reject) => {
372
+ this.db.get(sql, params, (err, row) => {
373
+ if (err) {
374
+ reject(err)
375
+ } else {
376
+ resolve(row as T)
377
+ }
378
+ })
379
+ })
380
+ }
381
+
382
+ /**
383
+ * 执行SQL查询并获取所有结果
384
+ */
385
+ private allQuery<T>(sql: string, params: any[] = []): Promise<T[]> {
386
+ return new Promise((resolve, reject) => {
387
+ this.db.all(sql, params, (err, rows) => {
388
+ if (err) {
389
+ reject(err)
390
+ } else {
391
+ resolve(rows as T[])
392
+ }
393
+ })
394
+ })
395
+ }
396
+
397
+ /**
398
+ * 获取或创建机器人记录
399
+ * @param botId 机器人ID
400
+ */
401
+ async getOrCreateBot(botId: string): Promise<Bot> {
402
+ let bot = await this.getQuery<Bot>('SELECT * FROM Bots WHERE id = ?', [botId])
403
+
404
+ if (!bot) {
405
+ const now = new Date().toISOString()
406
+ await this.runQuery('INSERT INTO Bots (id, createdAt, updatedAt) VALUES (?, ?, ?)', [botId, now, now])
407
+ bot = { id: botId, createdAt: now, updatedAt: now }
408
+ }
409
+
410
+ return bot
411
+ }
412
+
413
+ /**
414
+ * 获取或创建群组记录
415
+ * @param groupId 群组ID
416
+ * @param botId 机器人ID
417
+ */
418
+ async getOrCreateGroup(groupId: string, botId: string): Promise<Group> {
419
+ await this.getOrCreateBot(botId)
420
+
421
+ let group = await this.getQuery<Group>('SELECT * FROM Groups WHERE id = ? AND botId = ?', [groupId, botId])
422
+
423
+ if (!group) {
424
+ const now = new Date().toISOString()
425
+ await this.runQuery('INSERT INTO Groups (id, botId, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [groupId, botId, now, now])
426
+ group = { id: groupId, botId, createdAt: now, updatedAt: now }
427
+ }
428
+
429
+ return group
430
+ }
431
+
432
+ /**
433
+ * 获取或创建抖音用户记录
434
+ * @param sec_uid 抖音用户sec_uid
435
+ * @param short_id 抖音号
436
+ * @param remark 用户昵称
437
+ */
438
+ async getOrCreateDouyinUser(sec_uid: string, short_id: string = '', remark: string = ''): Promise<DouyinUser> {
439
+ let user = await this.getQuery<DouyinUser>('SELECT * FROM DouyinUsers WHERE sec_uid = ?', [sec_uid])
440
+
441
+ if (!user) {
442
+ const now = new Date().toISOString()
443
+ await this.runQuery(
444
+ 'INSERT INTO DouyinUsers (sec_uid, short_id, remark, living, filterMode, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?)',
445
+ [sec_uid, short_id, remark, 0, 'blacklist', now, now]
446
+ )
447
+ user = {
448
+ sec_uid,
449
+ short_id,
450
+ remark,
451
+ living: false,
452
+ filterMode: 'blacklist',
453
+ createdAt: now,
454
+ updatedAt: now
455
+ }
456
+ } else {
457
+ // 如果提供了新的信息,更新用户记录
458
+ let needUpdate = false
459
+ const updates: string[] = []
460
+ const params: any[] = []
461
+
462
+ if (remark && user.remark !== remark) {
463
+ updates.push('remark = ?')
464
+ params.push(remark)
465
+ user.remark = remark
466
+ needUpdate = true
467
+ }
468
+
469
+ if (short_id && user.short_id !== short_id) {
470
+ updates.push('short_id = ?')
471
+ params.push(short_id)
472
+ user.short_id = short_id
473
+ needUpdate = true
474
+ }
475
+
476
+ if (needUpdate) {
477
+ const now = new Date().toISOString()
478
+ updates.push('updatedAt = ?')
479
+ params.push(now)
480
+ params.push(sec_uid)
481
+
482
+ await this.runQuery(`UPDATE DouyinUsers SET ${updates.join(', ')} WHERE sec_uid = ?`, params)
483
+ user.updatedAt = now
484
+ }
485
+ }
486
+
487
+ return user
488
+ }
489
+
490
+ /**
491
+ * 订阅抖音用户
492
+ * @param groupId 群组ID
493
+ * @param botId 机器人ID
494
+ * @param sec_uid 抖音用户sec_uid
495
+ * @param short_id 抖音号
496
+ * @param remark 用户昵称
497
+ */
498
+ async subscribeDouyinUser(
499
+ groupId: string,
500
+ botId: string,
501
+ sec_uid: string,
502
+ short_id: string = '',
503
+ remark: string = ''
504
+ ): Promise<GroupUserSubscription> {
505
+ await this.getOrCreateGroup(groupId, botId)
506
+ await this.getOrCreateDouyinUser(sec_uid, short_id, remark)
507
+
508
+ let subscription = await this.getQuery<GroupUserSubscription>(
509
+ 'SELECT * FROM GroupUserSubscriptions WHERE groupId = ? AND sec_uid = ?',
510
+ [groupId, sec_uid]
511
+ )
512
+
513
+ if (!subscription) {
514
+ const now = new Date().toISOString()
515
+ await this.runQuery('INSERT INTO GroupUserSubscriptions (groupId, sec_uid, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
516
+ groupId,
517
+ sec_uid,
518
+ now,
519
+ now
520
+ ])
521
+ subscription = { groupId, sec_uid, createdAt: now, updatedAt: now }
522
+ }
523
+
524
+ return subscription
525
+ }
526
+
527
+ /**
528
+ * 取消订阅抖音用户
529
+ * @param groupId 群组ID
530
+ * @param sec_uid 抖音用户sec_uid
531
+ */
532
+ async unsubscribeDouyinUser(groupId: string, sec_uid: string): Promise<boolean> {
533
+ const result = await this.runQuery('DELETE FROM GroupUserSubscriptions WHERE groupId = ? AND sec_uid = ?', [groupId, sec_uid])
534
+
535
+ // 清除相关的作品缓存
536
+ await this.runQuery('DELETE FROM AwemeCaches WHERE groupId = ? AND sec_uid = ?', [groupId, sec_uid])
537
+
538
+ // 检查该用户是否还有其他群组订阅
539
+ const remainingSubscriptions = await this.getQuery<{ count: number }>(
540
+ 'SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE sec_uid = ?',
541
+ [sec_uid]
542
+ )
543
+
544
+ // 如果没有任何群组订阅该用户,删除用户记录及相关数据
545
+ if (remainingSubscriptions && remainingSubscriptions.count === 0) {
546
+ logger.info(`[DouyinDB] 用户 ${sec_uid} 已无任何群组订阅,清理相关数据`)
547
+
548
+ // 删除用户记录
549
+ await this.runQuery('DELETE FROM DouyinUsers WHERE sec_uid = ?', [sec_uid])
550
+
551
+ // 删除过滤词
552
+ await this.runQuery('DELETE FROM FilterWords WHERE sec_uid = ?', [sec_uid])
553
+
554
+ // 删除过滤标签
555
+ await this.runQuery('DELETE FROM FilterTags WHERE sec_uid = ?', [sec_uid])
556
+
557
+ // 删除所有相关的作品缓存(所有群组的)
558
+ await this.runQuery('DELETE FROM AwemeCaches WHERE sec_uid = ?', [sec_uid])
559
+ }
560
+
561
+ return result.changes > 0
562
+ }
563
+
564
+ /**
565
+ * 添加作品缓存
566
+ * @param aweme_id 作品ID
567
+ * @param sec_uid 抖音用户sec_uid
568
+ * @param groupId 群组ID
569
+ * @param pushType 推送类型:post(作品列表)、favorite(喜欢列表)、recommend(推荐列表)、live(直播)
570
+ */
571
+ async addAwemeCache(aweme_id: string, sec_uid: string, groupId: string, pushType: string = 'post'): Promise<AwemeCache> {
572
+ let cache = await this.getQuery<AwemeCache>(
573
+ 'SELECT * FROM AwemeCaches WHERE aweme_id = ? AND sec_uid = ? AND groupId = ? AND pushType = ?',
574
+ [aweme_id, sec_uid, groupId, pushType]
575
+ )
576
+
577
+ if (!cache) {
578
+ const now = new Date().toISOString()
579
+ const result = await this.runQuery(
580
+ 'INSERT INTO AwemeCaches (aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)',
581
+ [aweme_id, sec_uid, groupId, pushType, now, now]
582
+ )
583
+ cache = {
584
+ id: result.lastID,
585
+ aweme_id,
586
+ sec_uid,
587
+ groupId,
588
+ pushType,
589
+ createdAt: now,
590
+ updatedAt: now
591
+ }
592
+ }
593
+
594
+ return cache
595
+ }
596
+
597
+ /**
598
+ * 检查作品是否已推送
599
+ * @param aweme_id 作品ID
600
+ * @param sec_uid 抖音用户sec_uid
601
+ * @param groupId 群组ID
602
+ * @param pushType 推送类型:post(作品列表)、favorite(喜欢列表)、recommend(推荐列表)、live(直播)
603
+ */
604
+ async isAwemePushed(aweme_id: string, sec_uid: string, groupId: string, pushType: string = 'post'): Promise<boolean> {
605
+ const result = await this.getQuery<{ count: number }>(
606
+ 'SELECT COUNT(*) as count FROM AwemeCaches WHERE aweme_id = ? AND sec_uid = ? AND groupId = ? AND pushType = ?',
607
+ [aweme_id, sec_uid, groupId, pushType]
608
+ )
609
+
610
+ return (result?.count || 0) > 0
611
+ }
612
+
613
+ /**
614
+ * 检查群组是否有推送历史(用于判断是否为新订阅)
615
+ * @param sec_uid 抖音用户sec_uid
616
+ * @param groupId 群组ID
617
+ * @param pushType 推送类型
618
+ */
619
+ async hasHistory(sec_uid: string, groupId: string, pushType: string): Promise<boolean> {
620
+ const result = await this.getQuery<{ count: number }>(
621
+ 'SELECT COUNT(*) as count FROM AwemeCaches WHERE sec_uid = ? AND groupId = ? AND pushType = ? LIMIT 1',
622
+ [sec_uid, groupId, pushType]
623
+ )
624
+ return (result?.count || 0) > 0
625
+ }
626
+
627
+ /**
628
+ * 获取机器人管理的所有群组
629
+ * @param botId 机器人ID
630
+ */
631
+ async getBotGroups(botId: string): Promise<Group[]> {
632
+ return await this.allQuery<Group>('SELECT * FROM Groups WHERE botId = ?', [botId])
633
+ }
634
+
635
+ /**
636
+ * 更新群组的机器人ID
637
+ * @param groupId 群组ID
638
+ * @param oldBotId 旧的机器人ID
639
+ * @param newBotId 新的机器人ID
640
+ */
641
+ async updateGroupBotId(groupId: string, oldBotId: string, newBotId: string): Promise<void> {
642
+ await this.getOrCreateBot(newBotId)
643
+ const now = new Date().toISOString()
644
+ await this.runQuery('UPDATE Groups SET botId = ?, updatedAt = ? WHERE id = ? AND botId = ?', [newBotId, now, groupId, oldBotId])
645
+ }
646
+
647
+ /**
648
+ * 获取群组订阅的所有抖音用户
649
+ * @param groupId 群组ID
650
+ */
651
+ async getGroupSubscriptions(groupId: string): Promise<(GroupUserSubscription & { douyinUser: DouyinUser })[]> {
652
+ const subscriptions = await this.allQuery<any>(
653
+ `SELECT
654
+ gus.groupId, gus.sec_uid, gus.createdAt, gus.updatedAt,
655
+ du.short_id, du.remark, du.living, du.filterMode,
656
+ du.createdAt as du_createdAt, du.updatedAt as du_updatedAt
657
+ FROM GroupUserSubscriptions gus
658
+ LEFT JOIN DouyinUsers du ON gus.sec_uid = du.sec_uid
659
+ WHERE gus.groupId = ?`,
660
+ [groupId]
661
+ )
662
+
663
+ return subscriptions.map((sub) => ({
664
+ groupId: sub.groupId,
665
+ sec_uid: sub.sec_uid,
666
+ createdAt: sub.createdAt,
667
+ updatedAt: sub.updatedAt,
668
+ douyinUser: {
669
+ sec_uid: sub.sec_uid,
670
+ short_id: sub.short_id,
671
+ remark: sub.remark,
672
+ living: !!sub.living,
673
+ filterMode: sub.filterMode as 'blacklist' | 'whitelist',
674
+ createdAt: sub.du_createdAt,
675
+ updatedAt: sub.du_updatedAt
676
+ }
677
+ }))
678
+ }
679
+
680
+ /**
681
+ * 获取抖音用户的所有订阅群组
682
+ * @param sec_uid 抖音用户sec_uid
683
+ */
684
+ async getUserSubscribedGroups(sec_uid: string): Promise<Group[]> {
685
+ return await this.allQuery<Group>(
686
+ `SELECT g.* FROM Groups g
687
+ INNER JOIN GroupUserSubscriptions gus ON g.id = gus.groupId
688
+ WHERE gus.sec_uid = ?`,
689
+ [sec_uid]
690
+ )
691
+ }
692
+
693
+ /**
694
+ * 检查群组是否已订阅抖音用户
695
+ * @param sec_uid 抖音用户sec_uid
696
+ * @param groupId 群组ID
697
+ */
698
+ async isSubscribed(sec_uid: string, groupId: string): Promise<boolean> {
699
+ const result = await this.getQuery<{ count: number }>(
700
+ 'SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE sec_uid = ? AND groupId = ?',
701
+ [sec_uid, groupId]
702
+ )
703
+
704
+ return (result?.count || 0) > 0
705
+ }
706
+
707
+ /**
708
+ * 获取抖音用户信息
709
+ * @param sec_uid 抖音用户sec_uid
710
+ * @returns 返回用户信息,如果不存在则返回null
711
+ */
712
+ async getDouyinUser(sec_uid: string): Promise<DouyinUser | null> {
713
+ const user = await this.getQuery<DouyinUser>('SELECT * FROM DouyinUsers WHERE sec_uid = ?', [sec_uid])
714
+
715
+ if (user) {
716
+ user.living = !!user.living // 转换为boolean
717
+ }
718
+
719
+ return user || null
720
+ }
721
+
722
+ /**
723
+ * 更新用户直播状态
724
+ * @param sec_uid 抖音用户sec_uid
725
+ * @param living 是否正在直播
726
+ */
727
+ async updateLiveStatus(sec_uid: string, living: boolean): Promise<boolean> {
728
+ const user = await this.getDouyinUser(sec_uid)
729
+ if (!user) return false
730
+
731
+ const now = new Date().toISOString()
732
+ const result = await this.runQuery('UPDATE DouyinUsers SET living = ?, updatedAt = ? WHERE sec_uid = ?', [living ? 1 : 0, now, sec_uid])
733
+
734
+ return result.changes > 0
735
+ }
736
+
737
+ /**
738
+ * 获取用户直播状态
739
+ * @param sec_uid 抖音用户sec_uid
740
+ */
741
+ async getLiveStatus(sec_uid: string): Promise<{ living: boolean }> {
742
+ const user = await this.getDouyinUser(sec_uid)
743
+ return { living: user?.living || false }
744
+ }
745
+
746
+ /**
747
+ * 批量同步配置文件中的订阅到数据库
748
+ * @param configItems 配置文件中的订阅项
749
+ */
750
+ async syncConfigSubscriptions(configItems: douyinPushItem[]): Promise<void> {
751
+ // 1. 收集配置文件中的所有订阅关系
752
+ const configSubscriptions: Map<string, Set<string>> = new Map()
753
+
754
+ // 初始化每个群组的订阅用户集合
755
+ for (const item of configItems) {
756
+ const sec_uid = item.sec_uid
757
+ const short_id = item.short_id ?? ''
758
+ const remark = item.remark ?? ''
759
+
760
+ // 创建或更新抖音用户记录
761
+ await this.getOrCreateDouyinUser(sec_uid, short_id, remark)
762
+
763
+ // 处理该用户的所有群组订阅
764
+ for (const groupWithBot of item.group_id) {
765
+ const [groupId, botId] = groupWithBot.split(':')
766
+ if (!groupId || !botId) continue
767
+
768
+ // 确保群组存在
769
+ await this.getOrCreateGroup(groupId, botId)
770
+
771
+ // 记录配置文件中的订阅关系
772
+ if (!configSubscriptions.has(groupId)) {
773
+ configSubscriptions.set(groupId, new Set())
774
+ }
775
+ configSubscriptions.get(groupId)?.add(sec_uid)
776
+
777
+ // 检查是否已订阅
778
+ const isSubscribed = await this.isSubscribed(sec_uid, groupId)
779
+
780
+ // 如果未订阅,创建订阅关系
781
+ if (!isSubscribed) {
782
+ await this.subscribeDouyinUser(groupId, botId, sec_uid, short_id, remark)
783
+ }
784
+ }
785
+ }
786
+
787
+ // 2. 获取数据库中的所有订阅关系,并与配置文件比较,删除不在配置文件中的订阅
788
+ // 获取所有群组
789
+ const allGroups = await this.allQuery<Group>('SELECT * FROM Groups')
790
+
791
+ for (const group of allGroups) {
792
+ const groupId = group.id
793
+ const configUsers = configSubscriptions.get(groupId) ?? new Set()
794
+
795
+ // 获取该群组在数据库中的所有订阅
796
+ const dbSubscriptions = await this.getGroupSubscriptions(groupId)
797
+
798
+ // 找出需要删除的订阅(在数据库中存在但配置文件中不存在)
799
+ for (const subscription of dbSubscriptions) {
800
+ const sec_uid = subscription.sec_uid
801
+
802
+ if (!configUsers.has(sec_uid)) {
803
+ // 删除订阅关系
804
+ await this.unsubscribeDouyinUser(groupId, sec_uid)
805
+ logger.mark(`已删除群组 ${groupId} 对抖音用户 ${sec_uid} 的订阅`)
806
+ }
807
+ }
808
+ }
809
+
810
+ // 3. 清理不再被任何群组订阅的抖音用户记录及其过滤词和过滤标签
811
+ // 获取所有抖音用户
812
+ const allUsers = await this.allQuery<DouyinUser>('SELECT * FROM DouyinUsers')
813
+
814
+ for (const user of allUsers) {
815
+ const sec_uid = user.sec_uid
816
+
817
+ // 检查该用户是否还有群组订阅
818
+ const subscribedGroups = await this.getUserSubscribedGroups(sec_uid)
819
+
820
+ if (subscribedGroups.length === 0) {
821
+ // 删除该用户的过滤词和过滤标签
822
+ await this.runQuery('DELETE FROM FilterWords WHERE sec_uid = ?', [sec_uid])
823
+ await this.runQuery('DELETE FROM FilterTags WHERE sec_uid = ?', [sec_uid])
824
+
825
+ // 删除该用户记录
826
+ await this.runQuery('DELETE FROM DouyinUsers WHERE sec_uid = ?', [sec_uid])
827
+
828
+ logger.mark(`已删除抖音用户 ${sec_uid} 的记录及相关过滤设置(不再被任何群组订阅)`)
829
+ }
830
+ }
831
+ }
832
+
833
+ /**
834
+ * 通过ID获取群组信息
835
+ * @param groupId 群组ID
836
+ */
837
+ async getGroupById(groupId: string): Promise<Group | null> {
838
+ return (await this.getQuery<Group>('SELECT * FROM Groups WHERE id = ?', [groupId])) || null
839
+ }
840
+
841
+ /**
842
+ * 更新用户的过滤模式
843
+ * @param sec_uid 抖音用户sec_uid
844
+ * @param filterMode 过滤模式
845
+ */
846
+ async updateFilterMode(sec_uid: string, filterMode: 'blacklist' | 'whitelist'): Promise<DouyinUser> {
847
+ const user = await this.getOrCreateDouyinUser(sec_uid)
848
+ const now = new Date().toISOString()
849
+
850
+ await this.runQuery('UPDATE DouyinUsers SET filterMode = ?, updatedAt = ? WHERE sec_uid = ?', [filterMode, now, sec_uid])
851
+
852
+ return { ...user, filterMode, updatedAt: now }
853
+ }
854
+
855
+ /**
856
+ * 添加过滤词
857
+ * @param sec_uid 抖音用户sec_uid
858
+ * @param word 过滤词
859
+ */
860
+ async addFilterWord(sec_uid: string, word: string): Promise<FilterWord> {
861
+ await this.getOrCreateDouyinUser(sec_uid)
862
+
863
+ let filterWord = await this.getQuery<FilterWord>('SELECT * FROM FilterWords WHERE sec_uid = ? AND word = ?', [sec_uid, word])
864
+
865
+ if (!filterWord) {
866
+ const now = new Date().toISOString()
867
+ const result = await this.runQuery('INSERT INTO FilterWords (sec_uid, word, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
868
+ sec_uid,
869
+ word,
870
+ now,
871
+ now
872
+ ])
873
+ filterWord = {
874
+ id: result.lastID,
875
+ sec_uid,
876
+ word,
877
+ createdAt: now,
878
+ updatedAt: now
879
+ }
880
+ }
881
+
882
+ return filterWord
883
+ }
884
+
885
+ /**
886
+ * 删除过滤词
887
+ * @param sec_uid 抖音用户sec_uid
888
+ * @param word 过滤词
889
+ */
890
+ async removeFilterWord(sec_uid: string, word: string): Promise<boolean> {
891
+ const result = await this.runQuery('DELETE FROM FilterWords WHERE sec_uid = ? AND word = ?', [sec_uid, word])
892
+ return result.changes > 0
893
+ }
894
+
895
+ /**
896
+ * 添加过滤标签
897
+ * @param sec_uid 抖音用户sec_uid
898
+ * @param tag 过滤标签
899
+ */
900
+ async addFilterTag(sec_uid: string, tag: string): Promise<FilterTag> {
901
+ await this.getOrCreateDouyinUser(sec_uid)
902
+
903
+ let filterTag = await this.getQuery<FilterTag>('SELECT * FROM FilterTags WHERE sec_uid = ? AND tag = ?', [sec_uid, tag])
904
+
905
+ if (!filterTag) {
906
+ const now = new Date().toISOString()
907
+ const result = await this.runQuery('INSERT INTO FilterTags (sec_uid, tag, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
908
+ sec_uid,
909
+ tag,
910
+ now,
911
+ now
912
+ ])
913
+ filterTag = {
914
+ id: result.lastID,
915
+ sec_uid,
916
+ tag,
917
+ createdAt: now,
918
+ updatedAt: now
919
+ }
920
+ }
921
+
922
+ return filterTag
923
+ }
924
+
925
+ /**
926
+ * 删除过滤标签
927
+ * @param sec_uid 抖音用户sec_uid
928
+ * @param tag 过滤标签
929
+ */
930
+ async removeFilterTag(sec_uid: string, tag: string): Promise<boolean> {
931
+ const result = await this.runQuery('DELETE FROM FilterTags WHERE sec_uid = ? AND tag = ?', [sec_uid, tag])
932
+ return result.changes > 0
933
+ }
934
+
935
+ /**
936
+ * 获取用户的所有过滤词
937
+ * @param sec_uid 抖音用户sec_uid
938
+ */
939
+ async getFilterWords(sec_uid: string): Promise<string[]> {
940
+ const filterWords = await this.allQuery<FilterWord>('SELECT * FROM FilterWords WHERE sec_uid = ?', [sec_uid])
941
+ return filterWords.map((word) => word.word)
942
+ }
943
+
944
+ /**
945
+ * 获取用户的所有过滤标签
946
+ * @param sec_uid 抖音用户sec_uid
947
+ */
948
+ async getFilterTags(sec_uid: string): Promise<string[]> {
949
+ const filterTags = await this.allQuery<FilterTag>('SELECT * FROM FilterTags WHERE sec_uid = ?', [sec_uid])
950
+ return filterTags.map((tag) => tag.tag)
951
+ }
952
+
953
+ /**
954
+ * 获取用户的过滤配置
955
+ * @param sec_uid 抖音用户sec_uid
956
+ */
957
+ async getFilterConfig(sec_uid: string): Promise<{ filterMode: 'blacklist' | 'whitelist'; filterWords: string[]; filterTags: string[] }> {
958
+ const user = await this.getOrCreateDouyinUser(sec_uid)
959
+ const filterWords = await this.getFilterWords(sec_uid)
960
+ const filterTags = await this.getFilterTags(sec_uid)
961
+
962
+ return {
963
+ filterMode: user.filterMode,
964
+ filterWords,
965
+ filterTags
966
+ }
967
+ }
968
+
969
+ /**
970
+ * 检查内容是否应该被过滤
971
+ * @param PushItem 推送项
972
+ * @param tags 标签列表
973
+ */
974
+ async shouldFilter(PushItem: DouyinWorkPushItem, tags: string[] = []): Promise<boolean> {
975
+ // 使用 PushItem.sec_uid 而不是 PushItem.Detail_Data.sec_uid
976
+ const sec_uid = PushItem.sec_uid
977
+ if (!sec_uid) {
978
+ logger.warn(`推送项缺少 sec_uid 参数: ${JSON.stringify(PushItem)}`)
979
+ return false // 如果没有 sec_uid,默认不过滤
980
+ }
981
+
982
+ const { filterMode, filterWords, filterTags } = await this.getFilterConfig(sec_uid)
983
+ logger.debug(`
984
+ 获取用户${PushItem.remark}(${PushItem.sec_uid})的过滤配置:
985
+ 过滤模式:${filterMode}
986
+ 过滤词:${filterWords}
987
+ 过滤标签:${filterTags}
988
+ `)
989
+ const desc = PushItem.Detail_Data.desc ?? ''
990
+
991
+ // 检查内容中是否包含过滤词
992
+ const hasFilterWord = filterWords.some((word) => desc.includes(word))
993
+
994
+ // 检查标签中是否包含过滤标签
995
+ const hasFilterTag = filterTags.some((filterTag) => tags.some((tag) => tag === filterTag))
996
+
997
+ logger.debug(`
998
+ 作者:${PushItem.remark}
999
+ 检查内容:${desc}
1000
+ 命中词:[${filterWords.join('], [')}]
1001
+ 命中标签:[${filterTags.join('], [')}]
1002
+ 过滤模式:${filterMode}
1003
+ 是否过滤:${hasFilterWord || hasFilterTag ? logger.red(`${hasFilterWord || hasFilterTag}`) : logger.green(`${hasFilterWord || hasFilterTag}`)}
1004
+ 作品地址:${logger.green(`https://www.douyin.com/video/${PushItem.Detail_Data.aweme_id}`)}
1005
+ `)
1006
+
1007
+ // 根据过滤模式决定是否过滤
1008
+ if (filterMode === 'blacklist') {
1009
+ // 黑名单模式:如果包含过滤词或过滤标签,则过滤
1010
+ if (hasFilterWord || hasFilterTag) {
1011
+ logger.warn(`
1012
+ 作品内容命中黑名单规则,已过滤该作品不再推送
1013
+ 作品地址:${logger.yellow(PushItem.Detail_Data.share_url)}
1014
+ 命中的黑名单词:[${filterWords.join('], [')}]
1015
+ 命中的黑名单标签:[${filterTags.join('], [')}]
1016
+ `)
1017
+ return true
1018
+ }
1019
+ return false
1020
+ } else {
1021
+ // 白名单模式:如果不包含任何白名单词或白名单标签,则过滤
1022
+ // 注意:如果白名单为空,则不过滤任何内容
1023
+ if (filterWords.length === 0 && filterTags.length === 0) {
1024
+ return false
1025
+ }
1026
+
1027
+ if (hasFilterWord || hasFilterTag) {
1028
+ return false // 不过滤
1029
+ }
1030
+ logger.warn(`
1031
+ 作品内容未命中白名单规则,已过滤该作品不再推送
1032
+ 作品地址:${logger.yellow(PushItem.Detail_Data.share_url)}
1033
+ 命中的黑名单词:[${filterWords.join('], [')}]
1034
+ 命中的黑名单标签:[${filterTags.join('], [')}]
1035
+ `)
1036
+ return true
1037
+ }
1038
+ }
1039
+
1040
+ /**
1041
+ * 清理旧的作品缓存记录
1042
+ * @param days 保留最近几天的记录
1043
+ * @returns 删除的记录数量
1044
+ */
1045
+ async cleanOldAwemeCache(days: number = 7): Promise<number> {
1046
+ const cutoffDate = new Date()
1047
+ cutoffDate.setDate(cutoffDate.getDate() - days)
1048
+ const cutoffDateStr = cutoffDate.toISOString()
1049
+
1050
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE createdAt < ?', [cutoffDateStr])
1051
+ return result.changes ?? 0
1052
+ }
1053
+
1054
+ /** 为了向后兼容,保留groupRepository和awemeCacheRepository属性 */
1055
+ get groupRepository() {
1056
+ return {
1057
+ find: async (options?: {
1058
+ where?: {
1059
+ botId?: string
1060
+ id?: string
1061
+ }
1062
+ }) => {
1063
+ if (options?.where?.botId) {
1064
+ return await this.getBotGroups(options.where.botId)
1065
+ }
1066
+ return await this.allQuery<Group>('SELECT * FROM Groups')
1067
+ }
1068
+ }
1069
+ }
1070
+
1071
+ get awemeCacheRepository() {
1072
+ return {
1073
+ find: async <T = AwemeCache & { createdAt: Date; updatedAt: Date }>(
1074
+ options: {
1075
+ where?: {
1076
+ groupId?: string
1077
+ sec_uid?: string
1078
+ aweme_id?: string
1079
+ }
1080
+ order?: Record<string, 'ASC' | 'DESC'>
1081
+ take?: number
1082
+ relations?: string[]
1083
+ } = {}
1084
+ ): Promise<T[]> => {
1085
+ const { where = {}, order, take, relations } = options
1086
+ let sql = 'SELECT * FROM AwemeCaches'
1087
+ const params: string[] = []
1088
+
1089
+ // 构建WHERE条件
1090
+ const conditions: string[] = []
1091
+ if (where.groupId) {
1092
+ conditions.push('groupId = ?')
1093
+ params.push(where.groupId)
1094
+ }
1095
+ if (where.sec_uid) {
1096
+ conditions.push('sec_uid = ?')
1097
+ params.push(where.sec_uid)
1098
+ }
1099
+ if (where.aweme_id) {
1100
+ conditions.push('aweme_id = ?')
1101
+ params.push(where.aweme_id)
1102
+ }
1103
+
1104
+ if (conditions.length > 0) {
1105
+ sql += ' WHERE ' + conditions.join(' AND ')
1106
+ }
1107
+
1108
+ // 构建ORDER BY
1109
+ if (order) {
1110
+ const orderClauses: string[] = []
1111
+ const allowedFields = ['id', 'aweme_id', 'sec_uid', 'groupId', 'createdAt', 'updatedAt']
1112
+ const allowedDirections = ['ASC', 'DESC']
1113
+
1114
+ for (const [field, direction] of Object.entries(order)) {
1115
+ // 验证字段名和排序方向,防止SQL注入
1116
+ if (allowedFields.includes(field) && allowedDirections.includes(direction)) {
1117
+ orderClauses.push(`${field} ${direction}`)
1118
+ }
1119
+ }
1120
+ if (orderClauses.length > 0) {
1121
+ sql += ' ORDER BY ' + orderClauses.join(', ')
1122
+ }
1123
+ }
1124
+
1125
+ // 构建LIMIT
1126
+ if (take) {
1127
+ sql += ' LIMIT ?'
1128
+ params.push(take.toString())
1129
+ }
1130
+
1131
+ const caches = await this.allQuery<AwemeCache>(sql, params)
1132
+
1133
+ // 如果需要关联douyinUser数据
1134
+ if (relations && relations.includes('douyinUser')) {
1135
+ const result = []
1136
+ for (const cache of caches) {
1137
+ const douyinUser = await this.getDouyinUser(cache.sec_uid)
1138
+ result.push({
1139
+ ...cache,
1140
+ douyinUser,
1141
+ createdAt: new Date(cache.createdAt), // 转换为Date对象
1142
+ updatedAt: new Date(cache.updatedAt)
1143
+ })
1144
+ }
1145
+ return result as T[]
1146
+ }
1147
+
1148
+ // 转换日期字符串为Date对象
1149
+ return caches.map((cache) => ({
1150
+ ...cache,
1151
+ createdAt: new Date(cache.createdAt),
1152
+ updatedAt: new Date(cache.updatedAt)
1153
+ })) as T[]
1154
+ },
1155
+ delete: async (conditions: { groupId?: string; sec_uid?: string; aweme_id?: string }) => {
1156
+ const { groupId, sec_uid, aweme_id } = conditions
1157
+
1158
+ // 优先处理 aweme_id + groupId 的精确删除(单条记录)
1159
+ if (aweme_id && groupId) {
1160
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE aweme_id = ? AND groupId = ?', [aweme_id, groupId])
1161
+ return { affected: result.changes }
1162
+ }
1163
+ if (groupId && sec_uid) {
1164
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE groupId = ? AND sec_uid = ?', [groupId, sec_uid])
1165
+ return { affected: result.changes }
1166
+ }
1167
+ if (groupId) {
1168
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE groupId = ?', [groupId])
1169
+ return { affected: result.changes }
1170
+ }
1171
+ if (sec_uid) {
1172
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE sec_uid = ?', [sec_uid])
1173
+ return { affected: result.changes }
1174
+ }
1175
+ if (aweme_id) {
1176
+ const result = await this.runQuery('DELETE FROM AwemeCaches WHERE aweme_id = ?', [aweme_id])
1177
+ return { affected: result.changes }
1178
+ }
1179
+ return { affected: 0 }
1180
+ }
1181
+ }
1182
+ }
1183
+
1184
+ /**
1185
+ * 检查作品是否在列表快照中(用于喜欢列表和推荐列表)
1186
+ * @param aweme_id 作品ID
1187
+ * @param sec_uid 用户sec_uid
1188
+ * @param pushType 推送类型
1189
+ */
1190
+ async isAwemeInList(aweme_id: string, sec_uid: string, pushType: string): Promise<boolean> {
1191
+ const result = await this.getQuery<{ count: number }>(
1192
+ 'SELECT COUNT(*) as count FROM ListSnapshots WHERE aweme_id = ? AND sec_uid = ? AND pushType = ?',
1193
+ [aweme_id, sec_uid, pushType]
1194
+ )
1195
+ return (result?.count || 0) > 0
1196
+ }
1197
+
1198
+ /**
1199
+ * 更新列表快照(用于喜欢列表和推荐列表)
1200
+ * @param sec_uid 用户sec_uid
1201
+ * @param pushType 推送类型
1202
+ * @param aweme_ids 作品ID列表
1203
+ */
1204
+ async updateListSnapshot(sec_uid: string, pushType: string, aweme_ids: string[]): Promise<void> {
1205
+ const now = new Date().toISOString()
1206
+
1207
+ // 先删除该用户该类型的所有旧快照
1208
+ await this.runQuery('DELETE FROM ListSnapshots WHERE sec_uid = ? AND pushType = ?', [sec_uid, pushType])
1209
+
1210
+ // 插入新快照
1211
+ for (const aweme_id of aweme_ids) {
1212
+ await this.runQuery(
1213
+ 'INSERT OR IGNORE INTO ListSnapshots (sec_uid, pushType, aweme_id, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)',
1214
+ [sec_uid, pushType, aweme_id, now, now]
1215
+ )
1216
+ }
1217
+ }
1218
+ }