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.
- package/CHANGELOG.md +57 -0
- package/LICENSE +674 -0
- package/README.md +98 -0
- package/assets/web/assets/CronEditor-CYgFpX-g.js +1 -0
- package/assets/web/assets/DesktopLayout-iI43ij11.js +1 -0
- package/assets/web/assets/MobileLayout-CziU8MCR.js +1 -0
- package/assets/web/assets/ThemeSwitch-DFohhwd9.js +1 -0
- package/assets/web/assets/index-BRROK0vh.js +47 -0
- package/assets/web/assets/index-Dj-StEiS.css +2 -0
- package/assets/web/assets/separator-CxcFlCzz.js +1 -0
- package/assets/web/favicon.svg +1 -0
- package/assets/web/icons.svg +24 -0
- package/assets/web/index.html +15 -0
- package/assets/webui.html +115 -0
- package/client/index.ts +63 -0
- package/config/default_config/app.yaml +64 -0
- package/config/default_config/bilibili.yaml +116 -0
- package/config/default_config/config.json +157 -0
- package/config/default_config/cookies.yaml +11 -0
- package/config/default_config/douyin.yaml +99 -0
- package/config/default_config/kuaishou.yaml +8 -0
- package/config/default_config/pushlist.yaml +37 -0
- package/config/default_config/request.yaml +22 -0
- package/config/default_config/upload.yaml +50 -0
- package/config/default_config/xiaohongshu.yaml +23 -0
- package/dist/index.js +1 -0
- package/lib/compat/axios.js +23 -0
- package/lib/compat/cron.js +77 -0
- package/lib/compat/esm.js +15 -0
- package/lib/compat/express.js +23 -0
- package/lib/compat/heroui.js +12 -0
- package/lib/compat/lodash.js +33 -0
- package/lib/compat/logger.js +118 -0
- package/lib/compat/node-karin.js +1053 -0
- package/lib/compat/phosphor-icons.js +27 -0
- package/lib/compat/qqcard.js +256 -0
- package/lib/compat/root.js +44 -0
- package/lib/compat/runtime.js +87 -0
- package/lib/compat/segment.js +167 -0
- package/lib/compat/sqlite3.js +117 -0
- package/lib/compat/start.js +7 -0
- package/lib/compat/syncConfig.js +65 -0
- package/lib/compat/template-react.js +30 -0
- package/lib/compat/text.js +65 -0
- package/lib/compat/yaml.js +13 -0
- package/lib/configBridge.js +91 -0
- package/lib/index.js +775 -0
- package/lib/karin/app.js +34 -0
- package/lib/karin/apps/__ready.js +9 -0
- package/lib/karin/apps/admin.js +136 -0
- package/lib/karin/apps/help.js +233 -0
- package/lib/karin/apps/push.js +291 -0
- package/lib/karin/apps/qrlogin.js +176 -0
- package/lib/karin/apps/statistics.js +209 -0
- package/lib/karin/apps/testPush.js +247 -0
- package/lib/karin/apps/tools.js +473 -0
- package/lib/karin/apps/update.js +252 -0
- package/lib/karin/index.js +37 -0
- package/lib/karin/module/db/bilibili.js +831 -0
- package/lib/karin/module/db/douyin.js +918 -0
- package/lib/karin/module/db/index.js +165 -0
- package/lib/karin/module/db/migration.js +199 -0
- package/lib/karin/module/db/statistics.js +559 -0
- package/lib/karin/module/index.js +94 -0
- package/lib/karin/module/server/constants/routes.js +48 -0
- package/lib/karin/module/server/controllers/bots.js +230 -0
- package/lib/karin/module/server/controllers/config.js +75 -0
- package/lib/karin/module/server/controllers/video.js +208 -0
- package/lib/karin/module/server/index.js +26 -0
- package/lib/karin/module/server/middlewares/auth.js +133 -0
- package/lib/karin/module/server/routes/amagi.js +57 -0
- package/lib/karin/module/server/routes/api.js +40 -0
- package/lib/karin/module/server/routes/index.js +97 -0
- package/lib/karin/module/server/routes/ssr.js +16 -0
- package/lib/karin/module/server/routes/static.js +43 -0
- package/lib/karin/module/utils/Base.js +403 -0
- package/lib/karin/module/utils/CardParser.js +487 -0
- package/lib/karin/module/utils/Common.js +418 -0
- package/lib/karin/module/utils/Config.js +434 -0
- package/lib/karin/module/utils/EmojiReaction.js +242 -0
- package/lib/karin/module/utils/ErrorHandler/handler.js +114 -0
- package/lib/karin/module/utils/ErrorHandler/index.js +55 -0
- package/lib/karin/module/utils/ErrorHandler/render.js +182 -0
- package/lib/karin/module/utils/ErrorHandler/sender.js +206 -0
- package/lib/karin/module/utils/ErrorHandler/strategy.js +41 -0
- package/lib/karin/module/utils/ErrorHandler/types.js +2 -0
- package/lib/karin/module/utils/ErrorHandler/utils.js +108 -0
- package/lib/karin/module/utils/FFmpeg.js +298 -0
- package/lib/karin/module/utils/ImageHelper.js +91 -0
- package/lib/karin/module/utils/ImageSlice.js +429 -0
- package/lib/karin/module/utils/MotionPhoto.js +250 -0
- package/lib/karin/module/utils/Network/Downloader.js +413 -0
- package/lib/karin/module/utils/Network/ImageDownloader.js +250 -0
- package/lib/karin/module/utils/Network/Network.js +332 -0
- package/lib/karin/module/utils/Network/ThrottleStream.js +71 -0
- package/lib/karin/module/utils/Network/constants.js +129 -0
- package/lib/karin/module/utils/Network/helpers.js +174 -0
- package/lib/karin/module/utils/Network/index.js +34 -0
- package/lib/karin/module/utils/Network/types.js +12 -0
- package/lib/karin/module/utils/Networks.js +17 -0
- package/lib/karin/module/utils/ParseLock.js +34 -0
- package/lib/karin/module/utils/ParseOverride.js +88 -0
- package/lib/karin/module/utils/QqCardResolve.js +114 -0
- package/lib/karin/module/utils/QqPanel.js +879 -0
- package/lib/karin/module/utils/Render/coverTheme.js +256 -0
- package/lib/karin/module/utils/Render/index.js +582 -0
- package/lib/karin/module/utils/amagiClient.js +338 -0
- package/lib/karin/module/utils/avatar.js +201 -0
- package/lib/karin/module/utils/bot.js +141 -0
- package/lib/karin/module/utils/build-metadata.js +50 -0
- package/lib/karin/module/utils/changelog.js +166 -0
- package/lib/karin/module/utils/index.js +26 -0
- package/lib/karin/module/utils/loginSession.js +259 -0
- package/lib/karin/module/utils/parseTip.js +93 -0
- package/lib/karin/module/utils/runtime-report.js +140 -0
- package/lib/karin/module/utils/semver.js +72 -0
- package/lib/karin/module/utils/statisticsAggregate.js +415 -0
- package/lib/karin/platform/bilibili/bilibili.js +1805 -0
- package/lib/karin/platform/bilibili/comments.js +294 -0
- package/lib/karin/platform/bilibili/danmaku.js +555 -0
- package/lib/karin/platform/bilibili/dynamic-text.js +827 -0
- package/lib/karin/platform/bilibili/genParams.js +37 -0
- package/lib/karin/platform/bilibili/getID.js +159 -0
- package/lib/karin/platform/bilibili/index.js +24 -0
- package/lib/karin/platform/bilibili/live-status.js +40 -0
- package/lib/karin/platform/bilibili/login.js +132 -0
- package/lib/karin/platform/bilibili/push.js +1397 -0
- package/lib/karin/platform/bilibili/riskControl.js +133 -0
- package/lib/karin/platform/bilibili/search.js +299 -0
- package/lib/karin/platform/common/danmaku-types.js +12 -0
- package/lib/karin/platform/douyin/comments.js +310 -0
- package/lib/karin/platform/douyin/danmaku.js +893 -0
- package/lib/karin/platform/douyin/douyin.js +1145 -0
- package/lib/karin/platform/douyin/getID.js +94 -0
- package/lib/karin/platform/douyin/index.js +24 -0
- package/lib/karin/platform/douyin/login.js +230 -0
- package/lib/karin/platform/douyin/push/favorite.js +75 -0
- package/lib/karin/platform/douyin/push/index.js +21 -0
- package/lib/karin/platform/douyin/push/live.js +69 -0
- package/lib/karin/platform/douyin/push/post.js +60 -0
- package/lib/karin/platform/douyin/push/recommend.js +74 -0
- package/lib/karin/platform/douyin/push/render.js +621 -0
- package/lib/karin/platform/douyin/push/types.js +2 -0
- package/lib/karin/platform/douyin/push.js +1128 -0
- package/lib/karin/platform/douyin/types.js +13 -0
- package/lib/karin/platform/douyin/videoQuality.js +222 -0
- package/lib/karin/platform/douyin/workType.js +182 -0
- package/lib/karin/platform/index.js +20 -0
- package/lib/karin/platform/kuaishou/comments.js +91 -0
- package/lib/karin/platform/kuaishou/getID.js +43 -0
- package/lib/karin/platform/kuaishou/getdata.js +41 -0
- package/lib/karin/platform/kuaishou/index.js +20 -0
- package/lib/karin/platform/kuaishou/kuaishou.js +122 -0
- package/lib/karin/platform/xiaohongshu/comments.js +162 -0
- package/lib/karin/platform/xiaohongshu/getID.js +129 -0
- package/lib/karin/platform/xiaohongshu/index.js +18 -0
- package/lib/karin/platform/xiaohongshu/xiaohongshu.js +643 -0
- package/lib/karin/root.js +73 -0
- package/lib/karin/setup.js +99 -0
- package/lib/karin/types/config/amagi.js +2 -0
- package/lib/karin/types/config/app.js +2 -0
- package/lib/karin/types/config/bilibili.js +2 -0
- package/lib/karin/types/config/cookies.js +2 -0
- package/lib/karin/types/config/douyin.js +2 -0
- package/lib/karin/types/config/index.js +2 -0
- package/lib/karin/types/config/kuaishou.js +2 -0
- package/lib/karin/types/config/pushlist.js +2 -0
- package/lib/karin/types/config/request.js +2 -0
- package/lib/karin/types/config/upload.js +2 -0
- package/lib/karin/types/config/xiaohongshu.js +2 -0
- package/lib/karin/types/datatypes.js +2 -0
- package/lib/karin/types/index.js +18 -0
- package/lib/karin/types/netrorks/NetworksConfigType.js +2 -0
- package/lib/karin/types/netrorks/index.js +17 -0
- package/lib/karin/web.config.js +49 -0
- package/lib/ktr/registry.js +82 -0
- package/lib/ktr/template/_preview/components/PreviewInfoCard.js +10 -0
- package/lib/ktr/template/_preview/components/PreviewLayout.js +10 -0
- package/lib/ktr/template/_preview/components/PreviewVideoCard.js +8 -0
- package/lib/ktr/template/_preview/render.js +142 -0
- package/lib/ktr/template/_preview/types.js +2 -0
- package/lib/ktr/template/_preview/utils/status.js +31 -0
- package/lib/ktr/template/_preview/utils/time.js +12 -0
- package/lib/ktr/template/bilibili/bangumi/components/bangumi.js +127 -0
- package/lib/ktr/template/bilibili/bangumi/components/types.js +2 -0
- package/lib/ktr/template/bilibili/bangumi/index.js +12 -0
- package/lib/ktr/template/bilibili/comment/components/Comment.js +193 -0
- package/lib/ktr/template/bilibili/comment/components/types.js +2 -0
- package/lib/ktr/template/bilibili/comment/index.js +10 -0
- package/lib/ktr/template/bilibili/components/Icons.js +66 -0
- package/lib/ktr/template/bilibili/components/shared.js +93 -0
- package/lib/ktr/template/bilibili/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/DYNAMIC_TYPE_ARTICLE.js +66 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/DYNAMIC_TYPE_AV.js +87 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/DYNAMIC_TYPE_DRAW.js +93 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/DYNAMIC_TYPE_FORWARD.js +132 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/DYNAMIC_TYPE_LIVE_RCMD.js +84 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/DYNAMIC_TYPE_WORD.js +38 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/types.js +2 -0
- package/lib/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/index.js +10 -0
- package/lib/ktr/template/bilibili/dynamic/components/AdditionalCard.js +59 -0
- package/lib/ktr/template/bilibili/dynamic/components/CommonComponents.js +30 -0
- package/lib/ktr/template/bilibili/dynamic/types.js +2 -0
- package/lib/ktr/template/bilibili/qrcodeImg/components/qrcodeImg.js +48 -0
- package/lib/ktr/template/bilibili/qrcodeImg/components/types.js +3 -0
- package/lib/ktr/template/bilibili/qrcodeImg/index.js +10 -0
- package/lib/ktr/template/bilibili/userlist/components/UserList.js +53 -0
- package/lib/ktr/template/bilibili/userlist/components/types.js +3 -0
- package/lib/ktr/template/bilibili/userlist/index.js +13 -0
- package/lib/ktr/template/bilibili/videoInfo/components/types.js +2 -0
- package/lib/ktr/template/bilibili/videoInfo/components/videoInfo.js +62 -0
- package/lib/ktr/template/bilibili/videoInfo/index.js +10 -0
- package/lib/ktr/template/components/AmbientCover.js +36 -0
- package/lib/ktr/template/components/DefaultLayout.js +33 -0
- package/lib/ktr/template/components/GlowImage.js +276 -0
- package/lib/ktr/template/components/QRCodeWithAvatar.js +34 -0
- package/lib/ktr/template/components/RolldownLogo.js +6 -0
- package/lib/ktr/template/components/ViteLogo.js +6 -0
- package/lib/ktr/template/components/iconRegistry.js +173 -0
- package/lib/ktr/template/douyin/article-work/components/ArticleWork.js +99 -0
- package/lib/ktr/template/douyin/article-work/components/types.js +3 -0
- package/lib/ktr/template/douyin/article-work/index.js +10 -0
- package/lib/ktr/template/douyin/comment/components/Comment.js +189 -0
- package/lib/ktr/template/douyin/comment/components/types.js +2 -0
- package/lib/ktr/template/douyin/comment/index.js +10 -0
- package/lib/ktr/template/douyin/components/Icons.js +29 -0
- package/lib/ktr/template/douyin/components/types.js +3 -0
- package/lib/ktr/template/douyin/dynamic/components/Dynamic.js +90 -0
- package/lib/ktr/template/douyin/dynamic/components/types.js +3 -0
- package/lib/ktr/template/douyin/dynamic/index.js +10 -0
- package/lib/ktr/template/douyin/favorite-list/components/FavoriteList.js +30 -0
- package/lib/ktr/template/douyin/favorite-list/components/types.js +3 -0
- package/lib/ktr/template/douyin/favorite-list/index.js +10 -0
- package/lib/ktr/template/douyin/image-work/components/ImageWork.js +136 -0
- package/lib/ktr/template/douyin/image-work/components/types.js +2 -0
- package/lib/ktr/template/douyin/image-work/index.js +10 -0
- package/lib/ktr/template/douyin/live/components/Live.js +51 -0
- package/lib/ktr/template/douyin/live/components/types.js +3 -0
- package/lib/ktr/template/douyin/live/index.js +10 -0
- package/lib/ktr/template/douyin/musicinfo/components/MusicInfo.js +60 -0
- package/lib/ktr/template/douyin/musicinfo/components/types.js +3 -0
- package/lib/ktr/template/douyin/musicinfo/index.js +10 -0
- package/lib/ktr/template/douyin/qrcodeImg/components/qrcodeImg.js +39 -0
- package/lib/ktr/template/douyin/qrcodeImg/components/types.js +3 -0
- package/lib/ktr/template/douyin/qrcodeImg/index.js +9 -0
- package/lib/ktr/template/douyin/recommend-list/components/RecommendList.js +30 -0
- package/lib/ktr/template/douyin/recommend-list/components/types.js +3 -0
- package/lib/ktr/template/douyin/recommend-list/index.js +10 -0
- package/lib/ktr/template/douyin/user_profile/components/UserVideoList.js +54 -0
- package/lib/ktr/template/douyin/user_profile/components/types.js +3 -0
- package/lib/ktr/template/douyin/user_profile/index.js +9 -0
- package/lib/ktr/template/douyin/userlist/components/UserList.js +50 -0
- package/lib/ktr/template/douyin/userlist/components/types.js +3 -0
- package/lib/ktr/template/douyin/userlist/index.js +13 -0
- package/lib/ktr/template/douyin/video-work/components/VideoWork.js +127 -0
- package/lib/ktr/template/douyin/video-work/components/types.js +2 -0
- package/lib/ktr/template/douyin/video-work/index.js +10 -0
- package/lib/ktr/template/kuaishou/comment/components/Comment.js +88 -0
- package/lib/ktr/template/kuaishou/comment/components/types.js +2 -0
- package/lib/ktr/template/kuaishou/comment/index.js +10 -0
- package/lib/ktr/template/other/changelog/components/changelog.js +117 -0
- package/lib/ktr/template/other/changelog/components/types.js +3 -0
- package/lib/ktr/template/other/changelog/index.js +9 -0
- package/lib/ktr/template/other/handlerError/components/errorTitles.js +227 -0
- package/lib/ktr/template/other/handlerError/components/handlerError.js +392 -0
- package/lib/ktr/template/other/handlerError/components/stackHighlight.js +109 -0
- package/lib/ktr/template/other/handlerError/components/types.js +2 -0
- package/lib/ktr/template/other/handlerError/index.js +9 -0
- package/lib/ktr/template/other/help/components/Help.js +79 -0
- package/lib/ktr/template/other/help/components/types.js +3 -0
- package/lib/ktr/template/other/help/index.js +12 -0
- package/lib/ktr/template/other/live-photo-tip/components/LivePhotoTip.js +80 -0
- package/lib/ktr/template/other/live-photo-tip/components/types.js +3 -0
- package/lib/ktr/template/other/live-photo-tip/index.js +9 -0
- package/lib/ktr/template/other/qrlogin/components/qrlogin.js +58 -0
- package/lib/ktr/template/other/qrlogin/components/types.js +3 -0
- package/lib/ktr/template/other/qrlogin/index.js +9 -0
- package/lib/ktr/template/other/runtime/components/runtime.js +101 -0
- package/lib/ktr/template/other/runtime/components/types.js +3 -0
- package/lib/ktr/template/other/runtime/index.js +9 -0
- package/lib/ktr/template/other/version_warning/components/VersionWarning.js +38 -0
- package/lib/ktr/template/other/version_warning/components/types.js +3 -0
- package/lib/ktr/template/other/version_warning/index.js +9 -0
- package/lib/ktr/template/statistics/components/BoxplotGuide.js +17 -0
- package/lib/ktr/template/statistics/components/ChartCard.js +15 -0
- package/lib/ktr/template/statistics/components/KpiCard.js +14 -0
- package/lib/ktr/template/statistics/components/PlatformTable.js +20 -0
- package/lib/ktr/template/statistics/components/RankList.js +61 -0
- package/lib/ktr/template/statistics/components/SSRChart.js +22 -0
- package/lib/ktr/template/statistics/components/chartPresets.js +630 -0
- package/lib/ktr/template/statistics/components/chartTheme.js +226 -0
- package/lib/ktr/template/statistics/components/format.js +38 -0
- package/lib/ktr/template/statistics/components/types.js +9 -0
- package/lib/ktr/template/statistics/global/components/GlobalStatistics.js +246 -0
- package/lib/ktr/template/statistics/global/components/types.js +3 -0
- package/lib/ktr/template/statistics/global/index.js +9 -0
- package/lib/ktr/template/statistics/group/components/GroupStatistics.js +181 -0
- package/lib/ktr/template/statistics/group/components/types.js +3 -0
- package/lib/ktr/template/statistics/group/index.js +9 -0
- package/lib/ktr/template/types/ctx.js +2 -0
- package/lib/ktr/template/xiaohongshu/comment/components/Comment.js +99 -0
- package/lib/ktr/template/xiaohongshu/comment/components/types.js +3 -0
- package/lib/ktr/template/xiaohongshu/comment/index.js +9 -0
- package/lib/ktr/template/xiaohongshu/noteInfo/components/noteInfo.js +93 -0
- package/lib/ktr/template/xiaohongshu/noteInfo/components/types.js +2 -0
- package/lib/ktr/template/xiaohongshu/noteInfo/index.js +9 -0
- package/lib/ktr/utils/QRCodeAvatar.js +99 -0
- package/lib/ktr/utils/QRcode.js +56 -0
- package/lib/ktr/utils/avatarProxy.js +45 -0
- package/lib/ktr/utils/cn.js +11 -0
- package/lib/ktr/utils/theme.js +12 -0
- package/lib/qqFields.json +75 -0
- package/lib/qqOptions.js +44 -0
- package/lib/richtext/index.js +25 -0
- package/lib/richtext/parse/index.js +232 -0
- package/lib/richtext/react/cn.js +11 -0
- package/lib/richtext/react/index.js +221 -0
- package/lib/richtext/types.js +2 -0
- package/lib/schema.js +352 -0
- package/lib/webui.js +341 -0
- package/package.json +88 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_BV.svg +1 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_GOODS.svg +1 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_LOTTERY.svg +1 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_VIEW_PICTURE.svg +1 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_VOTE.svg +1 -0
- package/resources/image/bilibili/RICH_TEXT_NODE_TYPE/RICH_TEXT_NODE_TYPE_WEB.svg +1 -0
- package/resources/image/bilibili/bilibili-dark.png +0 -0
- package/resources/image/bilibili/bilibili-light.png +0 -0
- package/resources/image/bilibili/bilibili.png +0 -0
- package/resources/image/bilibili/id-dark.svg +1 -0
- package/resources/image/bilibili/id-light.svg +1 -0
- package/resources/image/bilibili/level/lv0.svg +1 -0
- package/resources/image/bilibili/level/lv1.svg +1 -0
- package/resources/image/bilibili/level/lv2.svg +1 -0
- package/resources/image/bilibili/level/lv3.svg +1 -0
- package/resources/image/bilibili/level/lv4.svg +1 -0
- package/resources/image/bilibili/level/lv5.svg +1 -0
- package/resources/image/bilibili/level/lv6.svg +1 -0
- package/resources/image/bilibili/level/lv7.svg +1 -0
- package/resources/image/bilibili/like-dark.svg +6 -0
- package/resources/image/bilibili/like-light.svg +6 -0
- package/resources/image/bilibili/play.svg +28 -0
- package/resources/image/bilibili/res-local1.png +0 -0
- package/resources/image/bilibili/up_pb.svg +1 -0
- package/resources/image/bilibili//347/233/264/346/222/255/344/270/255.png +0 -0
- package/resources/image/douyin/douyin.png +0 -0
- package/resources/image/douyin/dylogo-dark.svg +23 -0
- package/resources/image/douyin/dylogo-light.svg +38 -0
- package/resources/image/douyin/search-dark.svg +1 -0
- package/resources/image/douyin/search-light.svg +3 -0
- package/resources/image/douyin//346/212/226/351/237/263-/347/233/264/346/222/255/344/270/255.png +0 -0
- package/resources/image/frame-logo.png +0 -0
- package/resources/image/kuaishou/logo.png +0 -0
- package/resources/image/other/handlerError/Milky.png +0 -0
- package/resources/image/other/handlerError/chronocat.svg +43 -0
- package/resources/image/other/handlerError/conwechat.webp +0 -0
- package/resources/image/other/handlerError/gocq-http.webp +0 -0
- package/resources/image/other/handlerError/lagrange.webp +0 -0
- package/resources/image/other/handlerError/llonebot.webp +0 -0
- package/resources/image/other/handlerError/napcat.webp +0 -0
- package/resources/image/other/handlerError/onebot.png +0 -0
- package/resources/image/other/handlerError/satori.png +0 -0
- package/resources/image/xiaohongshu/logo.png +0 -0
- package/resources/template/style.css +23708 -0
- package/resources/vite.svg +130 -0
- package/scripts/build.mjs +129 -0
- package/scripts/check-schema.cjs +55 -0
- package/scripts/deploy.mjs +78 -0
- package/scripts/make-demo-placeholders.mjs +112 -0
- package/scripts/patch-deps.mjs +46 -0
- package/scripts/patch-webui.mjs +112 -0
- package/scripts/smoke-commands.cjs +39 -0
- package/scripts/smoke-config.cjs +122 -0
- package/scripts/smoke-douyin.cjs +170 -0
- package/scripts/smoke-koishi-commands.cjs +210 -0
- package/scripts/smoke-login.cjs +110 -0
- package/scripts/smoke-parse.cjs +54 -0
- package/scripts/smoke-push-mock.cjs +114 -0
- package/scripts/smoke-push.cjs +104 -0
- package/scripts/smoke-qq.cjs +147 -0
- package/scripts/smoke-qqcard.cjs +108 -0
- package/scripts/smoke-qqpanel.cjs +205 -0
- package/scripts/smoke-render.cjs +106 -0
- package/scripts/smoke-server.cjs +94 -0
- package/src/compat/axios.ts +5 -0
- package/src/compat/cron.ts +86 -0
- package/src/compat/esm.ts +13 -0
- package/src/compat/express.ts +5 -0
- package/src/compat/heroui.tsx +53 -0
- package/src/compat/lodash.ts +32 -0
- package/src/compat/logger.ts +121 -0
- package/src/compat/node-karin.ts +1064 -0
- package/src/compat/phosphor-icons.tsx +25 -0
- package/src/compat/qqcard.ts +251 -0
- package/src/compat/root.ts +44 -0
- package/src/compat/runtime.ts +120 -0
- package/src/compat/segment.ts +161 -0
- package/src/compat/sqlite3.ts +123 -0
- package/src/compat/start.ts +3 -0
- package/src/compat/syncConfig.ts +64 -0
- package/src/compat/template-react.ts +54 -0
- package/src/compat/text.ts +58 -0
- package/src/compat/yaml.ts +6 -0
- package/src/configBridge.ts +94 -0
- package/src/index.ts +769 -0
- package/src/karin/app.ts +1 -0
- package/src/karin/apps/__ready.ts +14 -0
- package/src/karin/apps/admin.ts +127 -0
- package/src/karin/apps/help.ts +228 -0
- package/src/karin/apps/push.ts +327 -0
- package/src/karin/apps/qrlogin.ts +160 -0
- package/src/karin/apps/statistics.ts +203 -0
- package/src/karin/apps/testPush.ts +226 -0
- package/src/karin/apps/tools.ts +510 -0
- package/src/karin/apps/update.ts +239 -0
- package/src/karin/index.ts +9 -0
- package/src/karin/module/db/bilibili.ts +1089 -0
- package/src/karin/module/db/douyin.ts +1218 -0
- package/src/karin/module/db/index.ts +159 -0
- package/src/karin/module/db/migration.ts +249 -0
- package/src/karin/module/db/statistics.ts +817 -0
- package/src/karin/module/index.ts +37 -0
- package/src/karin/module/server/constants/routes.ts +53 -0
- package/src/karin/module/server/controllers/bots.ts +289 -0
- package/src/karin/module/server/controllers/config.ts +85 -0
- package/src/karin/module/server/controllers/video.ts +212 -0
- package/src/karin/module/server/index.ts +11 -0
- package/src/karin/module/server/middlewares/auth.ts +147 -0
- package/src/karin/module/server/routes/amagi.ts +57 -0
- package/src/karin/module/server/routes/api.ts +42 -0
- package/src/karin/module/server/routes/index.ts +68 -0
- package/src/karin/module/server/routes/ssr.ts +14 -0
- package/src/karin/module/server/routes/static.ts +43 -0
- package/src/karin/module/utils/Base.ts +497 -0
- package/src/karin/module/utils/CardParser.ts +503 -0
- package/src/karin/module/utils/Common.ts +448 -0
- package/src/karin/module/utils/Config.ts +443 -0
- package/src/karin/module/utils/EmojiReaction.ts +268 -0
- package/src/karin/module/utils/ErrorHandler/handler.ts +124 -0
- package/src/karin/module/utils/ErrorHandler/index.ts +48 -0
- package/src/karin/module/utils/ErrorHandler/render.ts +180 -0
- package/src/karin/module/utils/ErrorHandler/sender.ts +192 -0
- package/src/karin/module/utils/ErrorHandler/strategy.ts +41 -0
- package/src/karin/module/utils/ErrorHandler/types.ts +122 -0
- package/src/karin/module/utils/ErrorHandler/utils.ts +108 -0
- package/src/karin/module/utils/FFmpeg.ts +357 -0
- package/src/karin/module/utils/ImageHelper.ts +90 -0
- package/src/karin/module/utils/ImageSlice.ts +383 -0
- package/src/karin/module/utils/MotionPhoto.ts +297 -0
- package/src/karin/module/utils/Network/Downloader.ts +481 -0
- package/src/karin/module/utils/Network/ImageDownloader.ts +291 -0
- package/src/karin/module/utils/Network/Network.ts +344 -0
- package/src/karin/module/utils/Network/ThrottleStream.ts +76 -0
- package/src/karin/module/utils/Network/constants.ts +135 -0
- package/src/karin/module/utils/Network/helpers.ts +183 -0
- package/src/karin/module/utils/Network/index.ts +28 -0
- package/src/karin/module/utils/Network/types.ts +46 -0
- package/src/karin/module/utils/Networks.ts +20 -0
- package/src/karin/module/utils/ParseLock.ts +33 -0
- package/src/karin/module/utils/ParseOverride.ts +128 -0
- package/src/karin/module/utils/QqCardResolve.ts +116 -0
- package/src/karin/module/utils/QqPanel.ts +880 -0
- package/src/karin/module/utils/Render/coverTheme.ts +294 -0
- package/src/karin/module/utils/Render/index.ts +574 -0
- package/src/karin/module/utils/amagiClient.ts +368 -0
- package/src/karin/module/utils/avatar.ts +220 -0
- package/src/karin/module/utils/bot.ts +115 -0
- package/src/karin/module/utils/build-metadata.ts +60 -0
- package/src/karin/module/utils/changelog.ts +175 -0
- package/src/karin/module/utils/index.ts +10 -0
- package/src/karin/module/utils/loginSession.ts +291 -0
- package/src/karin/module/utils/parseTip.ts +59 -0
- package/src/karin/module/utils/runtime-report.ts +142 -0
- package/src/karin/module/utils/semver.ts +60 -0
- package/src/karin/module/utils/statisticsAggregate.ts +507 -0
- package/src/karin/platform/bilibili/bilibili.ts +2004 -0
- package/src/karin/platform/bilibili/comments.ts +397 -0
- package/src/karin/platform/bilibili/danmaku.ts +653 -0
- package/src/karin/platform/bilibili/dynamic-text.ts +926 -0
- package/src/karin/platform/bilibili/genParams.ts +30 -0
- package/src/karin/platform/bilibili/getID.ts +163 -0
- package/src/karin/platform/bilibili/index.ts +8 -0
- package/src/karin/platform/bilibili/live-status.ts +36 -0
- package/src/karin/platform/bilibili/login.ts +130 -0
- package/src/karin/platform/bilibili/push.ts +1615 -0
- package/src/karin/platform/bilibili/riskControl.ts +114 -0
- package/src/karin/platform/bilibili/search.ts +305 -0
- package/src/karin/platform/common/danmaku-types.ts +18 -0
- package/src/karin/platform/douyin/comments.ts +404 -0
- package/src/karin/platform/douyin/danmaku.ts +1187 -0
- package/src/karin/platform/douyin/douyin.ts +1244 -0
- package/src/karin/platform/douyin/getID.ts +101 -0
- package/src/karin/platform/douyin/index.ts +8 -0
- package/src/karin/platform/douyin/login.ts +251 -0
- package/src/karin/platform/douyin/push/favorite.ts +97 -0
- package/src/karin/platform/douyin/push/index.ts +5 -0
- package/src/karin/platform/douyin/push/live.ts +86 -0
- package/src/karin/platform/douyin/push/post.ts +75 -0
- package/src/karin/platform/douyin/push/recommend.ts +96 -0
- package/src/karin/platform/douyin/push/render.ts +768 -0
- package/src/karin/platform/douyin/push/types.ts +41 -0
- package/src/karin/platform/douyin/push.ts +1241 -0
- package/src/karin/platform/douyin/types.ts +151 -0
- package/src/karin/platform/douyin/videoQuality.ts +234 -0
- package/src/karin/platform/douyin/workType.ts +238 -0
- package/src/karin/platform/index.ts +4 -0
- package/src/karin/platform/kuaishou/comments.ts +114 -0
- package/src/karin/platform/kuaishou/getID.ts +48 -0
- package/src/karin/platform/kuaishou/getdata.ts +78 -0
- package/src/karin/platform/kuaishou/index.ts +4 -0
- package/src/karin/platform/kuaishou/kuaishou.ts +130 -0
- package/src/karin/platform/xiaohongshu/comments.ts +227 -0
- package/src/karin/platform/xiaohongshu/getID.ts +127 -0
- package/src/karin/platform/xiaohongshu/index.ts +2 -0
- package/src/karin/platform/xiaohongshu/xiaohongshu.ts +733 -0
- package/src/karin/root.ts +78 -0
- package/src/karin/setup.ts +77 -0
- package/src/karin/types/config/amagi.ts +46 -0
- package/src/karin/types/config/app.ts +129 -0
- package/src/karin/types/config/bilibili.ts +121 -0
- package/src/karin/types/config/cookies.ts +11 -0
- package/src/karin/types/config/douyin.ts +93 -0
- package/src/karin/types/config/index.ts +41 -0
- package/src/karin/types/config/kuaishou.ts +10 -0
- package/src/karin/types/config/pushlist.ts +55 -0
- package/src/karin/types/config/request.ts +27 -0
- package/src/karin/types/config/upload.ts +49 -0
- package/src/karin/types/config/xiaohongshu.ts +24 -0
- package/src/karin/types/datatypes.ts +42 -0
- package/src/karin/types/index.ts +4 -0
- package/src/karin/types/netrorks/NetworksConfigType.ts +61 -0
- package/src/karin/types/netrorks/index.ts +1 -0
- package/src/karin/web.config.ts +48 -0
- package/src/ktr/registry.ts +55 -0
- package/src/ktr/template/_preview/components/PreviewInfoCard.tsx +33 -0
- package/src/ktr/template/_preview/components/PreviewLayout.tsx +65 -0
- package/src/ktr/template/_preview/components/PreviewVideoCard.tsx +22 -0
- package/src/ktr/template/_preview/render.ts +158 -0
- package/src/ktr/template/_preview/types.ts +15 -0
- package/src/ktr/template/_preview/utils/status.ts +29 -0
- package/src/ktr/template/_preview/utils/time.ts +8 -0
- package/src/ktr/template/bilibili/bangumi/components/bangumi.tsx +462 -0
- package/src/ktr/template/bilibili/bangumi/components/types.ts +36 -0
- package/src/ktr/template/bilibili/bangumi/index.tsx +9 -0
- package/src/ktr/template/bilibili/comment/components/Comment.tsx +657 -0
- package/src/ktr/template/bilibili/comment/components/types.ts +26 -0
- package/src/ktr/template/bilibili/comment/index.tsx +11 -0
- package/src/ktr/template/bilibili/components/Icons.tsx +209 -0
- package/src/ktr/template/bilibili/components/shared.tsx +162 -0
- package/src/ktr/template/bilibili/components/types.ts +388 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/DYNAMIC_TYPE_ARTICLE.tsx +251 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/components/types.ts +67 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_ARTICLE/index.tsx +12 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/DYNAMIC_TYPE_AV.tsx +238 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/components/types.ts +39 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_AV/index.tsx +12 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/DYNAMIC_TYPE_DRAW.tsx +205 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/components/types.ts +20 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_DRAW/index.tsx +11 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/DYNAMIC_TYPE_FORWARD.tsx +348 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/components/types.ts +16 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_FORWARD/index.tsx +12 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/DYNAMIC_TYPE_LIVE_RCMD.tsx +264 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/components/types.ts +32 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_LIVE_RCMD/index.tsx +12 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/DYNAMIC_TYPE_WORD.tsx +68 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/components/types.ts +14 -0
- package/src/ktr/template/bilibili/dynamic/DYNAMIC_TYPE_WORD/index.tsx +12 -0
- package/src/ktr/template/bilibili/dynamic/components/AdditionalCard.tsx +165 -0
- package/src/ktr/template/bilibili/dynamic/components/CommonComponents.tsx +142 -0
- package/src/ktr/template/bilibili/dynamic/types.ts +382 -0
- package/src/ktr/template/bilibili/qrcodeImg/components/qrcodeImg.tsx +179 -0
- package/src/ktr/template/bilibili/qrcodeImg/components/types.ts +9 -0
- package/src/ktr/template/bilibili/qrcodeImg/index.tsx +12 -0
- package/src/ktr/template/bilibili/userlist/components/UserList.tsx +246 -0
- package/src/ktr/template/bilibili/userlist/components/types.ts +35 -0
- package/src/ktr/template/bilibili/userlist/index.tsx +11 -0
- package/src/ktr/template/bilibili/videoInfo/components/types.ts +28 -0
- package/src/ktr/template/bilibili/videoInfo/components/videoInfo.tsx +217 -0
- package/src/ktr/template/bilibili/videoInfo/index.tsx +12 -0
- package/src/ktr/template/components/AmbientCover.tsx +50 -0
- package/src/ktr/template/components/DefaultLayout.tsx +166 -0
- package/src/ktr/template/components/GlowImage.tsx +572 -0
- package/src/ktr/template/components/QRCodeWithAvatar.tsx +52 -0
- package/src/ktr/template/components/RolldownLogo.tsx +110 -0
- package/src/ktr/template/components/ViteLogo.tsx +134 -0
- package/src/ktr/template/components/iconRegistry.ts +294 -0
- package/src/ktr/template/douyin/article-work/components/ArticleWork.tsx +301 -0
- package/src/ktr/template/douyin/article-work/components/types.ts +53 -0
- package/src/ktr/template/douyin/article-work/index.tsx +12 -0
- package/src/ktr/template/douyin/comment/components/Comment.tsx +612 -0
- package/src/ktr/template/douyin/comment/components/types.ts +85 -0
- package/src/ktr/template/douyin/comment/index.tsx +11 -0
- package/src/ktr/template/douyin/components/Icons.tsx +75 -0
- package/src/ktr/template/douyin/components/types.ts +83 -0
- package/src/ktr/template/douyin/dynamic/components/Dynamic.tsx +283 -0
- package/src/ktr/template/douyin/dynamic/components/types.ts +46 -0
- package/src/ktr/template/douyin/dynamic/index.tsx +11 -0
- package/src/ktr/template/douyin/favorite-list/components/FavoriteList.tsx +201 -0
- package/src/ktr/template/douyin/favorite-list/components/types.ts +38 -0
- package/src/ktr/template/douyin/favorite-list/index.tsx +12 -0
- package/src/ktr/template/douyin/image-work/components/ImageWork.tsx +437 -0
- package/src/ktr/template/douyin/image-work/components/types.ts +78 -0
- package/src/ktr/template/douyin/image-work/index.tsx +11 -0
- package/src/ktr/template/douyin/live/components/Live.tsx +229 -0
- package/src/ktr/template/douyin/live/components/types.ts +47 -0
- package/src/ktr/template/douyin/live/index.tsx +11 -0
- package/src/ktr/template/douyin/musicinfo/components/MusicInfo.tsx +187 -0
- package/src/ktr/template/douyin/musicinfo/components/types.ts +31 -0
- package/src/ktr/template/douyin/musicinfo/index.tsx +11 -0
- package/src/ktr/template/douyin/qrcodeImg/components/qrcodeImg.tsx +170 -0
- package/src/ktr/template/douyin/qrcodeImg/components/types.ts +9 -0
- package/src/ktr/template/douyin/qrcodeImg/index.tsx +9 -0
- package/src/ktr/template/douyin/recommend-list/components/RecommendList.tsx +201 -0
- package/src/ktr/template/douyin/recommend-list/components/types.ts +37 -0
- package/src/ktr/template/douyin/recommend-list/index.tsx +12 -0
- package/src/ktr/template/douyin/user_profile/components/UserVideoList.tsx +225 -0
- package/src/ktr/template/douyin/user_profile/components/types.ts +71 -0
- package/src/ktr/template/douyin/user_profile/index.tsx +9 -0
- package/src/ktr/template/douyin/userlist/components/UserList.tsx +229 -0
- package/src/ktr/template/douyin/userlist/components/types.ts +35 -0
- package/src/ktr/template/douyin/userlist/index.tsx +11 -0
- package/src/ktr/template/douyin/video-work/components/VideoWork.tsx +398 -0
- package/src/ktr/template/douyin/video-work/components/types.ts +80 -0
- package/src/ktr/template/douyin/video-work/index.tsx +11 -0
- package/src/ktr/template/font.css +6071 -0
- package/src/ktr/template/global.d.ts +69 -0
- package/src/ktr/template/kuaishou/comment/components/Comment.tsx +207 -0
- package/src/ktr/template/kuaishou/comment/components/types.ts +47 -0
- package/src/ktr/template/kuaishou/comment/index.tsx +11 -0
- package/src/ktr/template/other/changelog/components/changelog.tsx +411 -0
- package/src/ktr/template/other/changelog/components/types.ts +21 -0
- package/src/ktr/template/other/changelog/index.tsx +9 -0
- package/src/ktr/template/other/handlerError/components/errorTitles.ts +224 -0
- package/src/ktr/template/other/handlerError/components/handlerError.tsx +1076 -0
- package/src/ktr/template/other/handlerError/components/stackHighlight.ts +127 -0
- package/src/ktr/template/other/handlerError/components/types.ts +124 -0
- package/src/ktr/template/other/handlerError/index.tsx +9 -0
- package/src/ktr/template/other/help/components/Help.tsx +249 -0
- package/src/ktr/template/other/help/components/types.ts +47 -0
- package/src/ktr/template/other/help/index.tsx +9 -0
- package/src/ktr/template/other/live-photo-tip/components/LivePhotoTip.tsx +295 -0
- package/src/ktr/template/other/live-photo-tip/components/types.ts +8 -0
- package/src/ktr/template/other/live-photo-tip/index.tsx +9 -0
- package/src/ktr/template/other/qrlogin/components/qrlogin.tsx +295 -0
- package/src/ktr/template/other/qrlogin/components/types.ts +11 -0
- package/src/ktr/template/other/qrlogin/index.tsx +9 -0
- package/src/ktr/template/other/runtime/components/runtime.tsx +347 -0
- package/src/ktr/template/other/runtime/components/types.ts +75 -0
- package/src/ktr/template/other/runtime/index.tsx +9 -0
- package/src/ktr/template/other/version_warning/components/VersionWarning.tsx +392 -0
- package/src/ktr/template/other/version_warning/components/types.ts +8 -0
- package/src/ktr/template/other/version_warning/index.tsx +9 -0
- package/src/ktr/template/statistics/components/BoxplotGuide.tsx +57 -0
- package/src/ktr/template/statistics/components/ChartCard.tsx +40 -0
- package/src/ktr/template/statistics/components/KpiCard.tsx +39 -0
- package/src/ktr/template/statistics/components/PlatformTable.tsx +83 -0
- package/src/ktr/template/statistics/components/RankList.tsx +127 -0
- package/src/ktr/template/statistics/components/SSRChart.tsx +41 -0
- package/src/ktr/template/statistics/components/chartPresets.ts +700 -0
- package/src/ktr/template/statistics/components/chartTheme.ts +234 -0
- package/src/ktr/template/statistics/components/format.ts +33 -0
- package/src/ktr/template/statistics/components/types.ts +92 -0
- package/src/ktr/template/statistics/global/components/GlobalStatistics.tsx +597 -0
- package/src/ktr/template/statistics/global/components/types.ts +138 -0
- package/src/ktr/template/statistics/global/index.tsx +9 -0
- package/src/ktr/template/statistics/group/components/GroupStatistics.tsx +468 -0
- package/src/ktr/template/statistics/group/components/types.ts +113 -0
- package/src/ktr/template/statistics/group/index.tsx +9 -0
- package/src/ktr/template/style.css +65 -0
- package/src/ktr/template/types/ctx.ts +42 -0
- package/src/ktr/template/xiaohongshu/comment/components/Comment.tsx +264 -0
- package/src/ktr/template/xiaohongshu/comment/components/types.ts +119 -0
- package/src/ktr/template/xiaohongshu/comment/index.tsx +9 -0
- package/src/ktr/template/xiaohongshu/noteInfo/components/noteInfo.tsx +271 -0
- package/src/ktr/template/xiaohongshu/noteInfo/components/types.ts +66 -0
- package/src/ktr/template/xiaohongshu/noteInfo/index.tsx +9 -0
- package/src/ktr/utils/QRCodeAvatar.ts +102 -0
- package/src/ktr/utils/QRcode.ts +65 -0
- package/src/ktr/utils/avatarProxy.ts +42 -0
- package/src/ktr/utils/cn.ts +6 -0
- package/src/ktr/utils/theme.ts +10 -0
- package/src/qqFields.json +75 -0
- package/src/qqOptions.ts +64 -0
- package/src/richtext/index.ts +9 -0
- package/src/richtext/parse/index.ts +271 -0
- package/src/richtext/react/cn.ts +6 -0
- package/src/richtext/react/index.tsx +582 -0
- package/src/richtext/types.ts +320 -0
- package/src/schema.ts +372 -0
- package/src/webui.ts +352 -0
|
@@ -0,0 +1,817 @@
|
|
|
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
|
+
// 直接吃 @/root 而不是 @/module/utils 桶:桶会把 ./Common 拖进来,
|
|
9
|
+
// 而 Common 又回头 import @/module(含本文件),形成环。
|
|
10
|
+
import { Root } from '@/root'
|
|
11
|
+
|
|
12
|
+
import { MigrationManager } from './migration'
|
|
13
|
+
|
|
14
|
+
/** 统计覆盖的平台 */
|
|
15
|
+
export type ParsePlatform = 'douyin' | 'bilibili' | 'kuaishou' | 'xiaohongshu'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 解析内容形态。
|
|
19
|
+
*
|
|
20
|
+
* 跨平台取统一值域,便于出「平台 × 形态」的对比图:
|
|
21
|
+
* 抖音走 `getWorkTypeInfo()`,B站取链接解析出的类型,快手/小红书按作品数据里有无视频流判定。
|
|
22
|
+
* `unknown` 只是兜底,正常路径不应落进来。
|
|
23
|
+
*/
|
|
24
|
+
export type ParseWorkType = 'video' | 'gallery' | 'collection' | 'article' | 'live' | 'bangumi' | 'dynamic' | 'music' | 'unknown'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 解析过程里采到的量化指标。
|
|
28
|
+
*
|
|
29
|
+
* 目前只保留「解析耗时」—— 作品时长、作品点赞属于**内容**属性,不是解析服务本身的表现,
|
|
30
|
+
* 放进这张海报会跑题;而且它们的单位在各平台并不统一(抖音毫秒、B站秒、快手/小红书未知),
|
|
31
|
+
* 口径本来也站不住。全部按分桶存(见 `METRIC_BUCKETS`),避免行数随解析次数无限增长。
|
|
32
|
+
*/
|
|
33
|
+
export type ParseMetric = 'duration'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 指标的分桶边界(左闭右开)。`duration` 单位毫秒。
|
|
37
|
+
*
|
|
38
|
+
* 每条桶有两个标签:
|
|
39
|
+
* - `label` 是**区间**口径(「这一档是什么范围」)
|
|
40
|
+
* - `upper` 是**累计**口径,画累计曲线时用它 —— 「≤5s」可以直接读成「5 秒内跑完的占多少」;
|
|
41
|
+
* 最后一档是兜底档,累计标签写「全部」(累计到这里必然是 100%,写 `>10min` 会被读反)
|
|
42
|
+
*
|
|
43
|
+
* 档位刻意拉宽到 `10min+`:早先只到 `30s+`,只要部署环境整体偏慢,
|
|
44
|
+
* 所有数据就都掉进最后一格,累计曲线退化成「左边一长条 0%、右边直接 100%」,整张图作废。
|
|
45
|
+
* 加密到 12 档是因为累计曲线要有足够折点,档太少那条线会变成几段直线。
|
|
46
|
+
* 读侧会把两端没数据的档裁掉(见 `buildMetricDistributions`),所以档多不会让图变挤。
|
|
47
|
+
*/
|
|
48
|
+
export const METRIC_BUCKETS: Record<ParseMetric, Array<{ label: string; upper: string; max: number }>> = {
|
|
49
|
+
duration: [
|
|
50
|
+
{ label: '<0.5s', upper: '≤0.5s', max: 500 },
|
|
51
|
+
{ label: '0.5-1s', upper: '≤1s', max: 1000 },
|
|
52
|
+
{ label: '1-2s', upper: '≤2s', max: 2000 },
|
|
53
|
+
{ label: '2-3s', upper: '≤3s', max: 3000 },
|
|
54
|
+
{ label: '3-5s', upper: '≤5s', max: 5000 },
|
|
55
|
+
{ label: '5-10s', upper: '≤10s', max: 10000 },
|
|
56
|
+
{ label: '10-20s', upper: '≤20s', max: 20000 },
|
|
57
|
+
{ label: '20-30s', upper: '≤30s', max: 30000 },
|
|
58
|
+
{ label: '30-60s', upper: '≤60s', max: 60000 },
|
|
59
|
+
{ label: '1-3min', upper: '≤3min', max: 180000 },
|
|
60
|
+
{ label: '3-10min', upper: '≤10min', max: 600000 },
|
|
61
|
+
// 兜底档没有上界,累计口径上它代表「其余全部」,写成「>10min」会被误读成「100% 都超过 10 分钟」
|
|
62
|
+
{ label: '10min+', upper: '全部', max: Number.MAX_SAFE_INTEGER }
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 取数值落在哪个桶。负数与 NaN 都归到第一桶 —— 采到脏数据时宁可低估也不要抛错。
|
|
68
|
+
* @param metric 指标名
|
|
69
|
+
* @param value 原始数值
|
|
70
|
+
*/
|
|
71
|
+
export const resolveMetricBucket = (metric: ParseMetric, value: number): string => {
|
|
72
|
+
const buckets = METRIC_BUCKETS[metric]
|
|
73
|
+
if (!Number.isFinite(value) || value < 0) return buckets[0].label
|
|
74
|
+
return (buckets.find((bucket) => value < bucket.max) ?? buckets[buckets.length - 1]).label
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 指标分布的一行(群海报与全局海报共用) */
|
|
78
|
+
export interface ParseMetricBucketRow {
|
|
79
|
+
/** 指标名 */
|
|
80
|
+
metric: ParseMetric
|
|
81
|
+
/** 分桶标签 */
|
|
82
|
+
bucket: string
|
|
83
|
+
/** 次数 */
|
|
84
|
+
count: number
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 解析统计接口 - 存储各平台解析统计数据
|
|
89
|
+
*/
|
|
90
|
+
interface ParseStatistics {
|
|
91
|
+
/** 统计ID */
|
|
92
|
+
id: number
|
|
93
|
+
/** 群组ID */
|
|
94
|
+
groupId: string
|
|
95
|
+
/** 用户ID */
|
|
96
|
+
userId: string
|
|
97
|
+
/** 平台类型:douyin、bilibili、kuaishou、xiaohongshu */
|
|
98
|
+
platform: ParsePlatform
|
|
99
|
+
/** 解析次数 */
|
|
100
|
+
parseCount: number
|
|
101
|
+
/** 创建时间 */
|
|
102
|
+
createdAt: string
|
|
103
|
+
/** 更新时间 */
|
|
104
|
+
updatedAt: string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** 群维度日粒度统计(群趋势图的数据源) */
|
|
108
|
+
export interface GroupParseHistory {
|
|
109
|
+
/** 群组ID */
|
|
110
|
+
groupId: string
|
|
111
|
+
/** 日期 (YYYY-MM-DD) */
|
|
112
|
+
date: string
|
|
113
|
+
/** 平台类型 */
|
|
114
|
+
platform: ParsePlatform
|
|
115
|
+
/** 当日解析次数 */
|
|
116
|
+
parseCount: number
|
|
117
|
+
/** 更新时间 */
|
|
118
|
+
updatedAt: string
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 小时粒度统计(活跃时段图的数据源) */
|
|
122
|
+
export interface ParseHourStats {
|
|
123
|
+
/** 群组ID */
|
|
124
|
+
groupId: string
|
|
125
|
+
/** 小时 (0-23,服务器本地时区) */
|
|
126
|
+
hour: number
|
|
127
|
+
/** 平台类型 */
|
|
128
|
+
platform: ParsePlatform
|
|
129
|
+
/** 解析次数 */
|
|
130
|
+
parseCount: number
|
|
131
|
+
/** 更新时间 */
|
|
132
|
+
updatedAt: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 内容形态统计(平台 × 形态图的数据源) */
|
|
136
|
+
export interface ParseWorkTypeStats {
|
|
137
|
+
/** 群组ID */
|
|
138
|
+
groupId: string
|
|
139
|
+
/** 平台类型 */
|
|
140
|
+
platform: ParsePlatform
|
|
141
|
+
/** 内容形态 */
|
|
142
|
+
workType: ParseWorkType
|
|
143
|
+
/** 解析次数 */
|
|
144
|
+
parseCount: number
|
|
145
|
+
/** 更新时间 */
|
|
146
|
+
updatedAt: string
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 解析历史接口 - 存储每日解析统计数据
|
|
151
|
+
*/
|
|
152
|
+
interface ParseHistory {
|
|
153
|
+
/** 统计ID */
|
|
154
|
+
id: number
|
|
155
|
+
/** 日期 (YYYY-MM-DD) */
|
|
156
|
+
date: string
|
|
157
|
+
/** 总解析次数 */
|
|
158
|
+
totalParses: number
|
|
159
|
+
/** 抖音解析次数 */
|
|
160
|
+
douyin: number
|
|
161
|
+
/** 哔哩哔哩解析次数 */
|
|
162
|
+
bilibili: number
|
|
163
|
+
/** 快手解析次数 */
|
|
164
|
+
kuaishou: number
|
|
165
|
+
/** 小红书解析次数 */
|
|
166
|
+
xiaohongshu: number
|
|
167
|
+
/** 创建时间 */
|
|
168
|
+
createdAt: string
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 全局统计接口 - 存储插件全局统计数据
|
|
173
|
+
*/
|
|
174
|
+
interface GlobalStatistics {
|
|
175
|
+
/** 统计键 */
|
|
176
|
+
key: string
|
|
177
|
+
/** 统计值 */
|
|
178
|
+
value: string
|
|
179
|
+
/** 更新时间 */
|
|
180
|
+
updatedAt: string
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** 统计数据库操作类 */
|
|
184
|
+
export class StatisticsDBBase {
|
|
185
|
+
private db!: sqlite3Types['Database']
|
|
186
|
+
private dbPath: string
|
|
187
|
+
private migrationManager: MigrationManager
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param dbPath 数据库文件路径;缺省用插件数据目录。
|
|
191
|
+
* 显式传入只为测试:生产链路由 `getStatisticsDB()` 单例统一构造,不该走这个参数。
|
|
192
|
+
*/
|
|
193
|
+
constructor(dbPath?: string) {
|
|
194
|
+
this.dbPath = dbPath ?? path.join(`${karinPathBase}/${Root.pluginName}/data`, 'statistics.db')
|
|
195
|
+
this.migrationManager = new MigrationManager(this.dbPath)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 关闭数据库连接。
|
|
200
|
+
* 生产链路上进程退出即可,不需要显式调用;测试里必须关,否则 Windows 下临时目录删不掉。
|
|
201
|
+
*/
|
|
202
|
+
async close(): Promise<void> {
|
|
203
|
+
if (!this.db) return
|
|
204
|
+
await new Promise<void>((resolve, reject) => {
|
|
205
|
+
this.db.close((err) => (err ? reject(err) : resolve()))
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 初始化数据库
|
|
211
|
+
*/
|
|
212
|
+
async init(): Promise<StatisticsDBBase> {
|
|
213
|
+
try {
|
|
214
|
+
logger.debug(logger.green('--------------------------[StatisticsDB] 开始初始化数据库--------------------------'))
|
|
215
|
+
logger.debug('[StatisticsDB] 正在连接数据库...')
|
|
216
|
+
|
|
217
|
+
// 创建数据库连接
|
|
218
|
+
fs.mkdirSync(path.dirname(this.dbPath), { recursive: true })
|
|
219
|
+
this.db = new sqlite3.Database(this.dbPath)
|
|
220
|
+
|
|
221
|
+
// 创建表结构
|
|
222
|
+
await this.createTables()
|
|
223
|
+
|
|
224
|
+
logger.debug('[StatisticsDB] 数据库模型同步成功')
|
|
225
|
+
|
|
226
|
+
// 初始化全局统计数据
|
|
227
|
+
await this.initGlobalStatistics()
|
|
228
|
+
|
|
229
|
+
// 同步历史数据(仅在首次迁移后执行)
|
|
230
|
+
await this.syncHistoryFromStats()
|
|
231
|
+
|
|
232
|
+
logger.debug(logger.green('--------------------------[StatisticsDB] 初始化数据库完成--------------------------'))
|
|
233
|
+
} catch (error) {
|
|
234
|
+
logger.error('[StatisticsDB] 数据库初始化失败:', error)
|
|
235
|
+
throw error
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return this
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 创建数据库表结构
|
|
243
|
+
*/
|
|
244
|
+
private async createTables(): Promise<void> {
|
|
245
|
+
const queries = [
|
|
246
|
+
// 创建解析统计表
|
|
247
|
+
`CREATE TABLE IF NOT EXISTS ParseStatistics (
|
|
248
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
249
|
+
groupId TEXT NOT NULL,
|
|
250
|
+
userId TEXT NOT NULL,
|
|
251
|
+
platform TEXT NOT NULL,
|
|
252
|
+
parseCount INTEGER DEFAULT 0,
|
|
253
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
254
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
255
|
+
UNIQUE(groupId, userId, platform)
|
|
256
|
+
)`,
|
|
257
|
+
|
|
258
|
+
// 创建解析历史表
|
|
259
|
+
`CREATE TABLE IF NOT EXISTS ParseHistory (
|
|
260
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
261
|
+
date TEXT NOT NULL UNIQUE,
|
|
262
|
+
totalParses INTEGER DEFAULT 0,
|
|
263
|
+
douyin INTEGER DEFAULT 0,
|
|
264
|
+
bilibili INTEGER DEFAULT 0,
|
|
265
|
+
kuaishou INTEGER DEFAULT 0,
|
|
266
|
+
xiaohongshu INTEGER DEFAULT 0,
|
|
267
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
268
|
+
)`,
|
|
269
|
+
|
|
270
|
+
// 创建全局统计表
|
|
271
|
+
`CREATE TABLE IF NOT EXISTS GlobalStatistics (
|
|
272
|
+
key TEXT PRIMARY KEY,
|
|
273
|
+
value TEXT NOT NULL,
|
|
274
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
275
|
+
)`,
|
|
276
|
+
|
|
277
|
+
// 群维度日粒度:ParseHistory 只有全局日粒度,画不出单群趋势
|
|
278
|
+
`CREATE TABLE IF NOT EXISTS GroupParseHistory (
|
|
279
|
+
groupId TEXT NOT NULL,
|
|
280
|
+
date TEXT NOT NULL,
|
|
281
|
+
platform TEXT NOT NULL,
|
|
282
|
+
parseCount INTEGER DEFAULT 0,
|
|
283
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
284
|
+
PRIMARY KEY (groupId, date, platform)
|
|
285
|
+
)`,
|
|
286
|
+
|
|
287
|
+
// 小时粒度活跃分布:行数有界(群数 × 24 × 4),可以按小时永久聚合
|
|
288
|
+
`CREATE TABLE IF NOT EXISTS ParseHourStats (
|
|
289
|
+
groupId TEXT NOT NULL,
|
|
290
|
+
hour INTEGER NOT NULL,
|
|
291
|
+
platform TEXT NOT NULL,
|
|
292
|
+
parseCount INTEGER DEFAULT 0,
|
|
293
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
294
|
+
PRIMARY KEY (groupId, hour, platform)
|
|
295
|
+
)`,
|
|
296
|
+
|
|
297
|
+
// 内容形态分布:支撑「平台 × 形态」图
|
|
298
|
+
`CREATE TABLE IF NOT EXISTS ParseWorkTypeStats (
|
|
299
|
+
groupId TEXT NOT NULL,
|
|
300
|
+
platform TEXT NOT NULL,
|
|
301
|
+
workType TEXT NOT NULL,
|
|
302
|
+
parseCount INTEGER DEFAULT 0,
|
|
303
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
304
|
+
PRIMARY KEY (groupId, platform, workType)
|
|
305
|
+
)`,
|
|
306
|
+
|
|
307
|
+
// 解析耗时 / 作品时长 / 作品点赞的分桶分布。
|
|
308
|
+
// 存桶而不是原值:原值每次解析都不同,行数会随解析次数无限涨;
|
|
309
|
+
// 分桶后行数上界是 群数 × 指标数 × 桶数。
|
|
310
|
+
`CREATE TABLE IF NOT EXISTS ParseMetricStats (
|
|
311
|
+
groupId TEXT NOT NULL,
|
|
312
|
+
metric TEXT NOT NULL,
|
|
313
|
+
bucket TEXT NOT NULL,
|
|
314
|
+
parseCount INTEGER DEFAULT 0,
|
|
315
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
316
|
+
PRIMARY KEY (groupId, metric, bucket)
|
|
317
|
+
)`
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
for (const query of queries) {
|
|
321
|
+
await this.runQuery(query)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 初始化全局统计数据
|
|
327
|
+
*/
|
|
328
|
+
private async initGlobalStatistics(): Promise<void> {
|
|
329
|
+
const keys = ['totalGroups', 'totalParses']
|
|
330
|
+
for (const key of keys) {
|
|
331
|
+
const exists = await this.getQuery<GlobalStatistics>('SELECT * FROM GlobalStatistics WHERE key = ?', [key])
|
|
332
|
+
if (!exists) {
|
|
333
|
+
await this.runQuery('INSERT INTO GlobalStatistics (key, value, updatedAt) VALUES (?, ?, ?)', [key, '0', new Date().toISOString()])
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 执行SQL查询
|
|
340
|
+
*/
|
|
341
|
+
private runQuery(sql: string, params: any[] = []): Promise<any> {
|
|
342
|
+
return new Promise((resolve, reject) => {
|
|
343
|
+
this.db.run(sql, params, function (err) {
|
|
344
|
+
if (err) {
|
|
345
|
+
reject(err)
|
|
346
|
+
} else {
|
|
347
|
+
resolve({ lastID: this.lastID, changes: this.changes })
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 执行SQL查询并获取单个结果
|
|
355
|
+
*/
|
|
356
|
+
private getQuery<T>(sql: string, params: any[] = []): Promise<T | undefined> {
|
|
357
|
+
return new Promise((resolve, reject) => {
|
|
358
|
+
this.db.get(sql, params, (err, row) => {
|
|
359
|
+
if (err) {
|
|
360
|
+
reject(err)
|
|
361
|
+
} else {
|
|
362
|
+
resolve(row as T)
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* 执行SQL查询并获取所有结果
|
|
370
|
+
*/
|
|
371
|
+
private allQuery<T>(sql: string, params: any[] = []): Promise<T[]> {
|
|
372
|
+
return new Promise((resolve, reject) => {
|
|
373
|
+
this.db.all(sql, params, (err, rows) => {
|
|
374
|
+
if (err) {
|
|
375
|
+
reject(err)
|
|
376
|
+
} else {
|
|
377
|
+
resolve(rows as T[])
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* 记录解析统计
|
|
385
|
+
* @param groupId 群组ID
|
|
386
|
+
* @param userId 用户ID
|
|
387
|
+
* @param platform 平台类型
|
|
388
|
+
* @param options.workType 本次解析的内容形态,取不到时该维度不计数
|
|
389
|
+
* @param options.durationMs 本次解析耗时(毫秒)
|
|
390
|
+
*/
|
|
391
|
+
async recordParse(
|
|
392
|
+
groupId: string,
|
|
393
|
+
userId: string,
|
|
394
|
+
platform: ParsePlatform,
|
|
395
|
+
options: { workType?: ParseWorkType; durationMs?: number } = {}
|
|
396
|
+
): Promise<void> {
|
|
397
|
+
const now = new Date().toISOString()
|
|
398
|
+
const today = new Date().toISOString().split('T')[0]
|
|
399
|
+
|
|
400
|
+
// 检查是否已存在该用户在该群组的统计记录
|
|
401
|
+
const existing = await this.getQuery<ParseStatistics>(
|
|
402
|
+
'SELECT * FROM ParseStatistics WHERE groupId = ? AND userId = ? AND platform = ?',
|
|
403
|
+
[groupId, userId, platform]
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
if (existing) {
|
|
407
|
+
// 更新解析次数
|
|
408
|
+
await this.runQuery(
|
|
409
|
+
'UPDATE ParseStatistics SET parseCount = parseCount + 1, updatedAt = ? WHERE groupId = ? AND userId = ? AND platform = ?',
|
|
410
|
+
[now, groupId, userId, platform]
|
|
411
|
+
)
|
|
412
|
+
} else {
|
|
413
|
+
// 创建新记录
|
|
414
|
+
await this.runQuery(
|
|
415
|
+
'INSERT INTO ParseStatistics (groupId, userId, platform, parseCount, createdAt, updatedAt) VALUES (?, ?, ?, 1, ?, ?)',
|
|
416
|
+
[groupId, userId, platform, now, now]
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
// 检查是否是新群组
|
|
420
|
+
const groupExists = await this.getQuery<{ count: number }>(
|
|
421
|
+
'SELECT COUNT(DISTINCT groupId) as count FROM ParseStatistics WHERE groupId = ?',
|
|
422
|
+
[groupId]
|
|
423
|
+
)
|
|
424
|
+
if (groupExists && groupExists.count === 1) {
|
|
425
|
+
await this.incrementTotalGroups()
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 更新总解析次数
|
|
430
|
+
await this.incrementTotalParses()
|
|
431
|
+
|
|
432
|
+
// 更新每日历史记录
|
|
433
|
+
await this.updateDailyHistory(today, platform)
|
|
434
|
+
|
|
435
|
+
// 以下是本次新增的维度。任何一张写失败都不该让解析主流程或其它维度受影响,
|
|
436
|
+
// 所以逐条兜住,只记日志。
|
|
437
|
+
await this.safeIncrement('群维度日粒度', () => this.incrementGroupHistory(groupId, today, platform))
|
|
438
|
+
await this.safeIncrement('活跃时段', () => this.incrementHourStats(groupId, new Date().getHours(), platform))
|
|
439
|
+
if (options.workType) {
|
|
440
|
+
await this.safeIncrement('内容形态', () => this.incrementWorkTypeStats(groupId, platform, options.workType!))
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// 解析耗时按桶落库。取不到就跳过 —— handler 在埋点之前抛错时就没有这个值
|
|
444
|
+
if (options.durationMs !== undefined) {
|
|
445
|
+
await this.safeIncrement('解析耗时', () =>
|
|
446
|
+
this.incrementMetricStats(groupId, 'duration', resolveMetricBucket('duration', options.durationMs!))
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* 跑一段自增写入,失败只记日志。
|
|
453
|
+
* 新增维度都是「锦上添花」,不能反过来把解析统计主流程带崩。
|
|
454
|
+
*/
|
|
455
|
+
private async safeIncrement(label: string, fn: () => Promise<void>): Promise<void> {
|
|
456
|
+
try {
|
|
457
|
+
await fn()
|
|
458
|
+
} catch (error) {
|
|
459
|
+
logger.error(`[StatisticsDB] 更新${label}统计失败:`, error)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* 群维度日粒度自增
|
|
465
|
+
*/
|
|
466
|
+
private async incrementGroupHistory(groupId: string, date: string, platform: ParsePlatform): Promise<void> {
|
|
467
|
+
await this.runQuery(
|
|
468
|
+
`INSERT INTO GroupParseHistory (groupId, date, platform, parseCount, updatedAt) VALUES (?, ?, ?, 1, ?)
|
|
469
|
+
ON CONFLICT(groupId, date, platform) DO UPDATE SET parseCount = parseCount + 1, updatedAt = excluded.updatedAt`,
|
|
470
|
+
[groupId, date, platform, new Date().toISOString()]
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 小时粒度自增
|
|
476
|
+
*/
|
|
477
|
+
private async incrementHourStats(groupId: string, hour: number, platform: ParsePlatform): Promise<void> {
|
|
478
|
+
await this.runQuery(
|
|
479
|
+
`INSERT INTO ParseHourStats (groupId, hour, platform, parseCount, updatedAt) VALUES (?, ?, ?, 1, ?)
|
|
480
|
+
ON CONFLICT(groupId, hour, platform) DO UPDATE SET parseCount = parseCount + 1, updatedAt = excluded.updatedAt`,
|
|
481
|
+
[groupId, hour, platform, new Date().toISOString()]
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* 内容形态自增
|
|
487
|
+
*/
|
|
488
|
+
private async incrementWorkTypeStats(groupId: string, platform: ParsePlatform, workType: ParseWorkType): Promise<void> {
|
|
489
|
+
await this.runQuery(
|
|
490
|
+
`INSERT INTO ParseWorkTypeStats (groupId, platform, workType, parseCount, updatedAt) VALUES (?, ?, ?, 1, ?)
|
|
491
|
+
ON CONFLICT(groupId, platform, workType) DO UPDATE SET parseCount = parseCount + 1, updatedAt = excluded.updatedAt`,
|
|
492
|
+
[groupId, platform, workType, new Date().toISOString()]
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* 指标分桶自增
|
|
498
|
+
*/
|
|
499
|
+
private async incrementMetricStats(groupId: string, metric: ParseMetric, bucket: string): Promise<void> {
|
|
500
|
+
await this.runQuery(
|
|
501
|
+
`INSERT INTO ParseMetricStats (groupId, metric, bucket, parseCount, updatedAt) VALUES (?, ?, ?, 1, ?)
|
|
502
|
+
ON CONFLICT(groupId, metric, bucket) DO UPDATE SET parseCount = parseCount + 1, updatedAt = excluded.updatedAt`,
|
|
503
|
+
[groupId, metric, bucket, new Date().toISOString()]
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* 更新每日历史记录
|
|
509
|
+
* @param date 日期 (YYYY-MM-DD)
|
|
510
|
+
* @param platform 平台类型
|
|
511
|
+
*/
|
|
512
|
+
private async updateDailyHistory(date: string, platform: ParsePlatform): Promise<void> {
|
|
513
|
+
const now = new Date().toISOString()
|
|
514
|
+
|
|
515
|
+
try {
|
|
516
|
+
const existing = await this.getQuery<ParseHistory>('SELECT * FROM ParseHistory WHERE date = ?', [date])
|
|
517
|
+
|
|
518
|
+
if (existing) {
|
|
519
|
+
await this.runQuery(`UPDATE ParseHistory SET totalParses = totalParses + 1, ${platform} = ${platform} + 1 WHERE date = ?`, [date])
|
|
520
|
+
} else {
|
|
521
|
+
await this.runQuery(
|
|
522
|
+
'INSERT INTO ParseHistory (date, totalParses, douyin, bilibili, kuaishou, xiaohongshu, createdAt) VALUES (?, 1, ?, ?, ?, ?, ?)',
|
|
523
|
+
[
|
|
524
|
+
date,
|
|
525
|
+
platform === 'douyin' ? 1 : 0,
|
|
526
|
+
platform === 'bilibili' ? 1 : 0,
|
|
527
|
+
platform === 'kuaishou' ? 1 : 0,
|
|
528
|
+
platform === 'xiaohongshu' ? 1 : 0,
|
|
529
|
+
now
|
|
530
|
+
]
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
} catch (error) {
|
|
534
|
+
logger.error('[StatisticsDB] 更新每日历史记录失败:', error)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* 获取最近N天的解析历史
|
|
540
|
+
* @param days 天数,默认30天
|
|
541
|
+
*/
|
|
542
|
+
async getRecentHistory(days: number = 30): Promise<ParseHistory[]> {
|
|
543
|
+
return await this.allQuery<ParseHistory>('SELECT * FROM ParseHistory ORDER BY date DESC LIMIT ?', [days])
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* 从现有统计数据同步历史记录(用于迁移后的数据修复)
|
|
548
|
+
*/
|
|
549
|
+
async syncHistoryFromStats(): Promise<void> {
|
|
550
|
+
try {
|
|
551
|
+
// 检查 ParseHistory 表是否为空
|
|
552
|
+
const historyCount = await this.getQuery<{ count: number }>('SELECT COUNT(*) as count FROM ParseHistory')
|
|
553
|
+
|
|
554
|
+
// 如果已有历史数据,不需要同步
|
|
555
|
+
if (historyCount && historyCount.count > 0) {
|
|
556
|
+
return
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// 获取所有统计数据
|
|
560
|
+
const allStats = await this.getAllStatistics()
|
|
561
|
+
|
|
562
|
+
// 按日期和平台聚合
|
|
563
|
+
const dateMap = new Map<
|
|
564
|
+
string,
|
|
565
|
+
{
|
|
566
|
+
douyin: number
|
|
567
|
+
bilibili: number
|
|
568
|
+
kuaishou: number
|
|
569
|
+
xiaohongshu: number
|
|
570
|
+
}
|
|
571
|
+
>()
|
|
572
|
+
|
|
573
|
+
for (const stat of allStats) {
|
|
574
|
+
const date = stat.createdAt.split('T')[0]
|
|
575
|
+
|
|
576
|
+
if (!dateMap.has(date)) {
|
|
577
|
+
dateMap.set(date, {
|
|
578
|
+
douyin: 0,
|
|
579
|
+
bilibili: 0,
|
|
580
|
+
kuaishou: 0,
|
|
581
|
+
xiaohongshu: 0
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const dateData = dateMap.get(date)!
|
|
586
|
+
dateData[stat.platform] += stat.parseCount
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// 插入历史记录
|
|
590
|
+
for (const [date, platforms] of dateMap.entries()) {
|
|
591
|
+
const totalParses = platforms.douyin + platforms.bilibili + platforms.kuaishou + platforms.xiaohongshu
|
|
592
|
+
|
|
593
|
+
await this.runQuery(
|
|
594
|
+
'INSERT OR IGNORE INTO ParseHistory (date, totalParses, douyin, bilibili, kuaishou, xiaohongshu, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
|
595
|
+
[date, totalParses, platforms.douyin, platforms.bilibili, platforms.kuaishou, platforms.xiaohongshu, new Date().toISOString()]
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
logger.info(`[StatisticsDB] 已同步 ${dateMap.size} 天的历史数据`)
|
|
600
|
+
} catch (error) {
|
|
601
|
+
logger.error('[StatisticsDB] 同步历史数据失败:', error)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* 获取群组的解析统计
|
|
607
|
+
* @param groupId 群组ID
|
|
608
|
+
*/
|
|
609
|
+
async getGroupStatistics(groupId: string): Promise<ParseStatistics[]> {
|
|
610
|
+
return await this.allQuery<ParseStatistics>('SELECT * FROM ParseStatistics WHERE groupId = ? ORDER BY platform, userId', [groupId])
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* 获取群组的唯一用户数
|
|
615
|
+
* @param groupId 群组ID
|
|
616
|
+
*/
|
|
617
|
+
async getGroupUniqueUsers(groupId: string): Promise<number> {
|
|
618
|
+
const result = await this.getQuery<{ count: number }>('SELECT COUNT(DISTINCT userId) as count FROM ParseStatistics WHERE groupId = ?', [
|
|
619
|
+
groupId
|
|
620
|
+
])
|
|
621
|
+
return result?.count || 0
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* 获取全局唯一用户数
|
|
626
|
+
*/
|
|
627
|
+
async getTotalUniqueUsers(): Promise<number> {
|
|
628
|
+
const result = await this.getQuery<{ count: number }>('SELECT COUNT(DISTINCT userId) as count FROM ParseStatistics')
|
|
629
|
+
return result?.count || 0
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* 获取所有群组的解析统计
|
|
634
|
+
*/
|
|
635
|
+
async getAllStatistics(): Promise<ParseStatistics[]> {
|
|
636
|
+
return await this.allQuery<ParseStatistics>('SELECT * FROM ParseStatistics ORDER BY groupId, platform')
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* 获取平台总解析次数
|
|
641
|
+
* @param platform 平台类型
|
|
642
|
+
*/
|
|
643
|
+
async getPlatformTotalParses(platform: ParsePlatform): Promise<number> {
|
|
644
|
+
const result = await this.getQuery<{ total: number }>('SELECT SUM(parseCount) as total FROM ParseStatistics WHERE platform = ?', [
|
|
645
|
+
platform
|
|
646
|
+
])
|
|
647
|
+
return result?.total || 0
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* 获取某个群最近 N 天的日粒度记录(按日期升序,便于直接喂折线图)
|
|
652
|
+
*
|
|
653
|
+
* 日期口径与写入端一致,都取 `toISOString()` 的 UTC 日期;
|
|
654
|
+
* SQLite 的 `date('now')` 同样是 UTC,两边不会错位。
|
|
655
|
+
* @param groupId 群组ID
|
|
656
|
+
* @param days 天数,默认 30 天
|
|
657
|
+
*/
|
|
658
|
+
async getGroupRecentHistory(groupId: string, days: number = 30): Promise<GroupParseHistory[]> {
|
|
659
|
+
return await this.allQuery<GroupParseHistory>(
|
|
660
|
+
`SELECT * FROM GroupParseHistory
|
|
661
|
+
WHERE groupId = ? AND date >= date('now', ?)
|
|
662
|
+
ORDER BY date`,
|
|
663
|
+
[groupId, `-${Math.max(0, days - 1)} days`]
|
|
664
|
+
)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* 获取某个群有解析记录的天数(不限于最近 N 天)
|
|
669
|
+
* @param groupId 群组ID
|
|
670
|
+
*/
|
|
671
|
+
async getGroupActiveDays(groupId: string): Promise<number> {
|
|
672
|
+
const result = await this.getQuery<{ count: number }>(
|
|
673
|
+
'SELECT COUNT(DISTINCT date) as count FROM GroupParseHistory WHERE groupId = ?',
|
|
674
|
+
[groupId]
|
|
675
|
+
)
|
|
676
|
+
return result?.count || 0
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* 获取某个群的小时粒度分布(0-23,缺失的小时由调用方补零)
|
|
681
|
+
* @param groupId 群组ID
|
|
682
|
+
*/
|
|
683
|
+
async getGroupHourStats(groupId: string): Promise<ParseHourStats[]> {
|
|
684
|
+
return await this.allQuery<ParseHourStats>('SELECT * FROM ParseHourStats WHERE groupId = ? ORDER BY hour', [groupId])
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* 获取某个群的内容形态分布
|
|
689
|
+
* @param groupId 群组ID
|
|
690
|
+
*/
|
|
691
|
+
async getGroupWorkTypeStats(groupId: string): Promise<ParseWorkTypeStats[]> {
|
|
692
|
+
return await this.allQuery<ParseWorkTypeStats>(
|
|
693
|
+
'SELECT * FROM ParseWorkTypeStats WHERE groupId = ? ORDER BY parseCount DESC',
|
|
694
|
+
[groupId]
|
|
695
|
+
)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* 获取全局内容形态分布(按平台 × 形态聚合)
|
|
700
|
+
*/
|
|
701
|
+
async getGlobalWorkTypeStats(): Promise<ParseWorkTypeStats[]> {
|
|
702
|
+
return await this.allQuery<ParseWorkTypeStats>(
|
|
703
|
+
`SELECT groupId, platform, workType, SUM(parseCount) as parseCount, MAX(updatedAt) as updatedAt
|
|
704
|
+
FROM ParseWorkTypeStats GROUP BY platform, workType ORDER BY platform, parseCount DESC`
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* 获取某个群的指标分桶分布(耗时 / 作品时长 / 点赞)
|
|
710
|
+
* @param groupId 群组ID
|
|
711
|
+
*/
|
|
712
|
+
async getGroupMetricStats(groupId: string): Promise<ParseMetricBucketRow[]> {
|
|
713
|
+
return await this.allQuery<ParseMetricBucketRow>(
|
|
714
|
+
'SELECT metric, bucket, SUM(parseCount) as count FROM ParseMetricStats WHERE groupId = ? GROUP BY metric, bucket',
|
|
715
|
+
[groupId]
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* 获取全局指标分桶分布(耗时 / 作品时长 / 点赞)
|
|
721
|
+
*/
|
|
722
|
+
async getGlobalMetricStats(): Promise<ParseMetricBucketRow[]> {
|
|
723
|
+
return await this.allQuery<ParseMetricBucketRow>(
|
|
724
|
+
'SELECT metric, bucket, SUM(parseCount) as count FROM ParseMetricStats GROUP BY metric, bucket'
|
|
725
|
+
)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* 取全局日粒度趋势里「数据可信」的起始日期。
|
|
730
|
+
* @returns 可信起始日(YYYY-MM-DD);一行可信数据都没有时返回 undefined
|
|
731
|
+
*/
|
|
732
|
+
async getHistoryCompleteFrom(): Promise<string | undefined> { // 老库回填(syncHistoryFromStats)会把用户的历史总次数全记在他首次解析那天,
|
|
733
|
+
// 那批行的特征是 date 与写入时间 createdAt 不在同一天;
|
|
734
|
+
// 增量写入的行两者必然同天(都取 UTC)。据此切出可信区间的起点。
|
|
735
|
+
const result = await this.getQuery<{ date: string | null }>(
|
|
736
|
+
'SELECT MIN(date) as date FROM ParseHistory WHERE date = substr(createdAt, 1, 10)'
|
|
737
|
+
)
|
|
738
|
+
return result?.date || undefined
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* 获取每个群首次被记录解析的日期(群增长曲线的数据源)
|
|
743
|
+
*/
|
|
744
|
+
async getGroupFirstSeen(): Promise<Array<{ groupId: string; firstSeen: string }>> {
|
|
745
|
+
return await this.allQuery<{ groupId: string; firstSeen: string }>(
|
|
746
|
+
'SELECT groupId, MIN(createdAt) as firstSeen FROM ParseStatistics GROUP BY groupId'
|
|
747
|
+
)
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* 获取总群组数
|
|
752
|
+
*/
|
|
753
|
+
async getTotalGroups(): Promise<number> {
|
|
754
|
+
const result = await this.getQuery<{ count: number }>('SELECT COUNT(DISTINCT groupId) as count FROM ParseStatistics')
|
|
755
|
+
return result?.count || 0
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* 获取总解析次数
|
|
760
|
+
*/
|
|
761
|
+
async getTotalParses(): Promise<number> {
|
|
762
|
+
const result = await this.getQuery<GlobalStatistics>('SELECT value FROM GlobalStatistics WHERE key = ?', ['totalParses'])
|
|
763
|
+
return parseInt(result?.value || '0', 10)
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* 增加总群组数
|
|
768
|
+
*/
|
|
769
|
+
private async incrementTotalGroups(): Promise<void> {
|
|
770
|
+
const totalGroups = await this.getTotalGroups()
|
|
771
|
+
await this.runQuery('UPDATE GlobalStatistics SET value = ?, updatedAt = ? WHERE key = ?', [
|
|
772
|
+
totalGroups.toString(),
|
|
773
|
+
new Date().toISOString(),
|
|
774
|
+
'totalGroups'
|
|
775
|
+
])
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* 增加总解析次数
|
|
780
|
+
*/
|
|
781
|
+
private async incrementTotalParses(): Promise<void> {
|
|
782
|
+
await this.runQuery('UPDATE GlobalStatistics SET value = value + 1, updatedAt = ? WHERE key = ?', [
|
|
783
|
+
new Date().toISOString(),
|
|
784
|
+
'totalParses'
|
|
785
|
+
])
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* 获取全局统计摘要
|
|
790
|
+
*/
|
|
791
|
+
async getGlobalSummary(): Promise<{
|
|
792
|
+
totalGroups: number
|
|
793
|
+
totalParses: number
|
|
794
|
+
platformStats: {
|
|
795
|
+
douyin: number
|
|
796
|
+
bilibili: number
|
|
797
|
+
kuaishou: number
|
|
798
|
+
xiaohongshu: number
|
|
799
|
+
}
|
|
800
|
+
}> {
|
|
801
|
+
const totalGroups = await this.getTotalGroups()
|
|
802
|
+
const totalParses = await this.getTotalParses()
|
|
803
|
+
|
|
804
|
+
const platformStats = {
|
|
805
|
+
douyin: await this.getPlatformTotalParses('douyin'),
|
|
806
|
+
bilibili: await this.getPlatformTotalParses('bilibili'),
|
|
807
|
+
kuaishou: await this.getPlatformTotalParses('kuaishou'),
|
|
808
|
+
xiaohongshu: await this.getPlatformTotalParses('xiaohongshu')
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return {
|
|
812
|
+
totalGroups,
|
|
813
|
+
totalParses,
|
|
814
|
+
platformStats
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|