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,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
|
+
}
|