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
package/src/index.ts
ADDED
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* koishi-plugin-kkk —— koishi-plugin-kkk 的 Koishi 移植版入口。
|
|
3
|
+
*
|
|
4
|
+
* 迁移策略:
|
|
5
|
+
* 1. `src/karin/` 是从 karin-plugin-kkk 源码逐文件搬过来的业务代码,import 路径保持不变;
|
|
6
|
+
* 2. `src/compat/` 用 Koishi 实现 karin 框架 API(`node-karin`),编译产物里通过 node_modules/node-karin 转发;
|
|
7
|
+
* 3. 本文件是 Koishi 插件入口:绑定运行时 → 加载移植的 apps(它们会注册命令/任务)→ 落地为 Koishi 中间件与定时任务。
|
|
8
|
+
*/
|
|
9
|
+
import dns from 'node:dns'
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import net from 'node:net'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
|
|
14
|
+
import { Context, Schema } from 'koishi'
|
|
15
|
+
|
|
16
|
+
import { startScheduler } from './compat/cron'
|
|
17
|
+
import { setLogger } from './compat/logger'
|
|
18
|
+
import { Message, NEXT } from './compat/node-karin'
|
|
19
|
+
import { bindRuntime, commandPrefixes, commandQueue, eventQueue, taskQueue, tryGetRuntime } from './compat/runtime'
|
|
20
|
+
import { buildQqSchema, QQ_KEYS, readQqOptions } from './qqOptions'
|
|
21
|
+
import { registerWebUi } from './webui'
|
|
22
|
+
import { applyUpstreamOverrides } from './configBridge'
|
|
23
|
+
import { resolveQqCardContent } from './karin/module/utils/QqCardResolve'
|
|
24
|
+
import { buildUpstreamSchema } from './schema'
|
|
25
|
+
|
|
26
|
+
/** 插件根目录(编译产物在 lib/ 下,向上一级即包根) */
|
|
27
|
+
const pluginRootDir = path.resolve(__dirname, '..')
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 网络兜底:优先 IPv4。
|
|
31
|
+
*
|
|
32
|
+
* 这台机器到 B站的 IPv6 链路是坏的(连上就被对端 reset),日志里表现为
|
|
33
|
+
* `read ECONNRESET`(getBilibiliID、弹幕接口、playurl 都会中招),
|
|
34
|
+
* 而且 Node 默认的 Happy Eyeballs 会**先试 IPv6**、连接建立后再被 reset,
|
|
35
|
+
* 不会自动回退到 IPv4,于是整条解析随机失败。
|
|
36
|
+
*
|
|
37
|
+
* 这里把 DNS 结果顺序改成 IPv4 优先、并关掉 autoSelectFamily(避免 IPv6 先连),
|
|
38
|
+
* 让所有出站请求都走 IPv4。改动是进程级的(Koishi 宿主里所有插件共用),
|
|
39
|
+
* 但只影响「优先用哪一族地址」,不影响正常 IPv4 网络。
|
|
40
|
+
*/
|
|
41
|
+
try {
|
|
42
|
+
dns.setDefaultResultOrder('ipv4first')
|
|
43
|
+
} catch { /* 老版本 Node 没有这个 API,忽略 */ }
|
|
44
|
+
try {
|
|
45
|
+
(net as any).setDefaultAutoSelectFamily?.(false)
|
|
46
|
+
} catch { /* 同上 */ }
|
|
47
|
+
export const name = 'kkk'
|
|
48
|
+
|
|
49
|
+
export const inject = {
|
|
50
|
+
// server 只给「插件自带控制台」用,已经移除;这里留着不需要的服务反而会拖慢加载
|
|
51
|
+
// ffmpeg:由 koishi-plugin-ffmpeg-path 提供,兼容层的 ffmpeg()/ffprobe() 会优先用它
|
|
52
|
+
// assets:koishi-plugin-assets-qqbot-part-file 之类提供的图床服务,
|
|
53
|
+
// 面板卡片要上传成 https 地址才能放进 QQ 的 markdown 图片里
|
|
54
|
+
optional: ['puppeteer', 'database', 'http', 'ffmpeg', 'assets', 'server', 'console']
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Config {
|
|
58
|
+
/** 主人账号(对应 karin 的 master),用于接收报错通知等 */
|
|
59
|
+
masters: string[]
|
|
60
|
+
/** 数据目录,默认 <baseDir>/data/kkk */
|
|
61
|
+
dataPath: string
|
|
62
|
+
/** 输出调试日志 */
|
|
63
|
+
debug: boolean
|
|
64
|
+
/** 消息里的链接自动解析 */
|
|
65
|
+
autoParse: boolean
|
|
66
|
+
/** QQ 平台解析前先发交互面板(Markdown + 按钮)让用户选解析内容和画质 */
|
|
67
|
+
qqPanel: boolean
|
|
68
|
+
/** QQ 面板里隐藏超过该体积(MB)的画质按钮 */
|
|
69
|
+
qqFileLimitMB: number
|
|
70
|
+
/** 是否在图片过大/发送失败时自动切片(默认开) */
|
|
71
|
+
sliceImageOnDemand: boolean
|
|
72
|
+
/** 切片高度(像素) */
|
|
73
|
+
sliceImageHeight: number
|
|
74
|
+
/** 卡片解析的 OCR 密钥(OCR.space) */
|
|
75
|
+
ocrApiKey: string
|
|
76
|
+
/** 操作后撤回上一条面板消息(默认开) */
|
|
77
|
+
recallPanel: boolean
|
|
78
|
+
/** 番剧分集表格的列数(默认 5) */
|
|
79
|
+
bangumiPanelCols: number
|
|
80
|
+
/** 番剧分集表格的行数(默认 4,含作为第一行的表头,即一页 5×4 = 20 集) */
|
|
81
|
+
bangumiPanelRows: number
|
|
82
|
+
/** 插件自身的配置(与 Karin 版 config.json 同构,保存时写回数据目录的 config.json) */
|
|
83
|
+
upstream: Record<string, any>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 配置表单 = 「Koishi 集成选项」 + 「上游插件配置」。
|
|
88
|
+
*
|
|
89
|
+
* 后者按 \`config/default_config/config.json\` 的形状动态生成,所以清晰度(\`douyin.videoQuality\`)、
|
|
90
|
+
* 发送内容(\`*.sendContent\`)、渲染(\`app.renderScale\`)、推送(\`pushlist\`)等上游选项都会出现在控制台里。
|
|
91
|
+
* 这里能改的项会被写回数据目录的 config.json(见 configBridge),在那之前 config.json 仍是权威来源。
|
|
92
|
+
*/
|
|
93
|
+
/** 摊平后由「Koishi 原生设置」分组提供的字段 */
|
|
94
|
+
const NATIVE_KEYS = ['masters', 'dataPath', 'debug', 'autoParse']
|
|
95
|
+
|
|
96
|
+
export const Config: Schema<Config> = Schema.intersect([
|
|
97
|
+
Schema.object({
|
|
98
|
+
webuiGuide: Schema.const('').description(
|
|
99
|
+
'**QQ 相关的开关都在下面的「QQ 适配器」分组里**(解析面板、画质档、图片切片、番剧选集表格、卡片识别)。' +
|
|
100
|
+
'也可以直接在浏览器打开 **/kkk** 改 —— 左侧边栏「kkk 配置」就是它,免登录、改完立即生效。'
|
|
101
|
+
),
|
|
102
|
+
qq: buildQqSchema(Schema).description('QQ 适配器(只对 QQ 平台生效)'),
|
|
103
|
+
advanced: Schema.object({
|
|
104
|
+
masters: Schema.array(Schema.string()).default([]).description('主人账号(QQ 号):接收报错通知,以及执行只有主人能用的指令'),
|
|
105
|
+
dataPath: Schema.string().default('data').description('数据目录:配置、数据库、临时文件都放在这里'),
|
|
106
|
+
debug: Schema.boolean().default(false).description('在日志里输出调试信息,排查问题时才需要打开'),
|
|
107
|
+
autoParse: Schema.boolean().default(true).description('群里有人发链接(或回复一条带链接的消息)就自动解析,不用打指令'),
|
|
108
|
+
}).collapse().description('Koishi 原生设置(一般不用改,已折叠)'),
|
|
109
|
+
}),
|
|
110
|
+
Schema.object({
|
|
111
|
+
upstream: buildUpstreamSchema(pluginRootDir).description(
|
|
112
|
+
'插件自身的配置(与 Karin 版 config.json 一致)。每项都带着上游默认值,枚举型字段是下拉框;' +
|
|
113
|
+
'**与默认值不同**的项会在启动时写回 config.json,保持默认值的项不写(这样你直接改文件的内容不会被覆盖)'
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
export const usage = `
|
|
119
|
+
本插件是 **koishi-plugin-kkk** 的 Koishi 移植版:抖音 / B站 / 快手 / 小红书 视频解析与动态推送。
|
|
120
|
+
|
|
121
|
+
## 指令
|
|
122
|
+
|
|
123
|
+
已经注册成真正的 Koishi 指令(**不带 \`#\` 前缀**,前缀按 Koishi 的配置来),例如
|
|
124
|
+
\`解析\` / \`弹幕解析\` / \`kkk帮助\` / \`kkk版本\` / \`kkk解析统计\` / \`B站登录\` / \`设置抖音推送\` …
|
|
125
|
+
发送**裸链接**(或回复一条含链接的消息)也会自动解析,由 \`autoParse\` 控制。
|
|
126
|
+
旧的 \`#解析\` 这种写法仍可用(兼容保留)。
|
|
127
|
+
|
|
128
|
+
在 QQ 平台,解析前会先回一条 Markdown + 按钮的面板,按钮点的就是上面这些指令
|
|
129
|
+
(\`解析 <链接> --qn=80\` 之类),可选解析内容(纯视频 / 弹幕)与画质,见 \`qqPanel\`。
|
|
130
|
+
|
|
131
|
+
配置分三块(控制台表单里就是三个分组):
|
|
132
|
+
|
|
133
|
+
1. **QQ 适配器**:只对 QQ 平台生效的开关 —— 解析面板、自动撤回、画质档体积、番剧选集表格、图片切片、卡片识别密钥;
|
|
134
|
+
2. **Koishi 原生设置**(默认折叠):主人账号、数据目录、调试日志、链接自动解析;
|
|
135
|
+
3. **\`upstream\`**:与 Karin 版 \`config.json\` 同构的全部业务配置(清晰度、发送内容、渲染、推送列表…),
|
|
136
|
+
每个字段的说明都来自上游 Karin 版的字段注释,**默认值也来自上游那份默认配置**(所以表单里不会是空白),
|
|
137
|
+
枚举型字段(清晰度、发送内容、权限 all/admin/master/group.owner/group.admin 等)是从注释里解析出来的下拉框。
|
|
138
|
+
|
|
139
|
+
三块都能在浏览器面板 **/kkk** 里改(左侧边栏「kkk 配置」打开的也是它,免登录,改完立即生效、不用重启):
|
|
140
|
+
面板里除了原版那几个分类,还多了一个「QQ 适配器」分类 —— 它和上面第 1 组共用同一份字段定义(\`src/qqFields.json\`),
|
|
141
|
+
保存时 QQ 适配器写回插件配置、其余写回 config.json。
|
|
142
|
+
|
|
143
|
+
**改过的项**(与上游默认值不同)会在启动时写回数据目录下的 \`config/config.json\`;
|
|
144
|
+
保持默认值的项一律不写,所以你也可以直接编辑那个文件,不会被表单里的默认值覆盖。
|
|
145
|
+
代价:在表单里把某项**改回默认值**不会写回文件 —— 真要改回去请直接编辑那个文件。
|
|
146
|
+
|
|
147
|
+
想做结构化编辑(例如清空推送列表)时,直接编辑数据目录下的
|
|
148
|
+
\`<dataPath>/koishi-plugin-kkk/config/config.json\`,重启后生效。
|
|
149
|
+
`
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* karin 的权限模型 → Koishi 实现。
|
|
153
|
+
*
|
|
154
|
+
* - `master`:插件配置里的 masters,或 Koishi 管理员(user.admin / authority >= 4)
|
|
155
|
+
* - `admin`:以上任一条,或群管理员(roles 里含 owner/admin)
|
|
156
|
+
* - `all` / 未配置:任何人
|
|
157
|
+
*/
|
|
158
|
+
/** Koishi 宿主(koishi.yml)里配置的主人们 —— 之前只认插件自己的 masters,所以宿主的主人反而没权限 */
|
|
159
|
+
function koishiMasterIds (): string[] {
|
|
160
|
+
try {
|
|
161
|
+
const ctx: any = (tryGetRuntime() as any)?.ctx
|
|
162
|
+
const config: any = ctx?.root?.config ?? ctx?.config ?? {}
|
|
163
|
+
const list = Array.isArray(config.masters) ? config.masters : []
|
|
164
|
+
return list.map((id: any) => String(id))
|
|
165
|
+
} catch {
|
|
166
|
+
return []
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** 取与会话相关的 Koishi 权限信息(authority / admin / masters) */
|
|
171
|
+
function koishiAuthority (session: any): number {
|
|
172
|
+
const candidates = [
|
|
173
|
+
session?.user?.authority,
|
|
174
|
+
session?.event?.user?.authority,
|
|
175
|
+
session?.author?.authority,
|
|
176
|
+
session?.bot?.user?.authority
|
|
177
|
+
]
|
|
178
|
+
for (const value of candidates) {
|
|
179
|
+
const num = Number(value)
|
|
180
|
+
if (Number.isFinite(num) && num > 0) return num
|
|
181
|
+
}
|
|
182
|
+
return 0
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function checkPermission (session: any, perm?: string): boolean {
|
|
186
|
+
if (!perm || perm === 'all') return true
|
|
187
|
+
|
|
188
|
+
// 两边的名单都认:插件自己的 masters + Koishi 宿主配置的 masters
|
|
189
|
+
const masters: string[] = [
|
|
190
|
+
...(tryGetRuntime()?.config.masters ?? []),
|
|
191
|
+
...koishiMasterIds()
|
|
192
|
+
].map((id: any) => String(id))
|
|
193
|
+
const user = session.user ?? {}
|
|
194
|
+
const authority = koishiAuthority(session)
|
|
195
|
+
const isMaster = masters.includes(String(session.userId))
|
|
196
|
+
|| user.admin === true
|
|
197
|
+
|| authority >= 4 // Koishi 的权限等级里 4 = 主人
|
|
198
|
+
if (perm === 'master') return isMaster
|
|
199
|
+
|
|
200
|
+
if (perm === 'admin') {
|
|
201
|
+
if (isMaster) return true
|
|
202
|
+
if (authority >= 3) return true // 3 = 管理员
|
|
203
|
+
const roles: string[] = session.author?.roles ?? []
|
|
204
|
+
return roles.includes('owner') || roles.includes('admin') || roles.includes('administrator')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return true
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* ------------------------------------------------------------------ *
|
|
211
|
+
* 指令注册
|
|
212
|
+
* ------------------------------------------------------------------ */
|
|
213
|
+
|
|
214
|
+
/** 这几个注册不是「用户敲的指令」,而是「消息里出现链接就自动解析」 */
|
|
215
|
+
const AUTO_PARSE_REGISTRATIONS = new Set([
|
|
216
|
+
'kkk-视频功能-抖音',
|
|
217
|
+
'kkk-视频功能-B站',
|
|
218
|
+
'kkk-视频功能-快手',
|
|
219
|
+
'kkk-视频功能-小红书'
|
|
220
|
+
])
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 正则里含 \s、字符类等写法、推导不出指令名的,手工补一份。
|
|
224
|
+
* 键是 \`reg.source\`。
|
|
225
|
+
*/
|
|
226
|
+
const MANUAL_COMMAND_NAMES: Record<string, string[]> = {
|
|
227
|
+
'^#?(kkk)?\\s*B站\\s*(扫码)?\\s*登录$': ['B站登录', 'B站扫码登录', 'kkkB站登录', 'kkkB站扫码登录'],
|
|
228
|
+
'^#?(kkk)?抖音(扫码)?登录$': ['抖音登录', '抖音扫码登录', 'kkk抖音登录', 'kkk抖音扫码登录'],
|
|
229
|
+
'^#设置[bB]站推送': ['设置B站推送'],
|
|
230
|
+
'^#?[bB]站推送列表$': ['B站推送列表']
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** 控制台里显示的指令说明 */
|
|
234
|
+
const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
235
|
+
解析: '解析消息或引用里的链接(抖音 / B站 / 快手 / 小红书)',
|
|
236
|
+
kkk解析: '「解析」的别名',
|
|
237
|
+
弹幕解析: '解析链接并把弹幕烧录进视频(当前移植版未接入 ffmpeg,会退化为纯视频)',
|
|
238
|
+
kkk帮助: '查看插件帮助',
|
|
239
|
+
kkk版本: '查看运行环境与版本信息',
|
|
240
|
+
kkk更新日志: '查看更新日志',
|
|
241
|
+
kkk更新: '更新插件(需要宿主支持)',
|
|
242
|
+
kkk解析统计: '查看本群解析统计',
|
|
243
|
+
kkk全局解析统计: '查看全局解析统计',
|
|
244
|
+
登录: '扫码登录各平台账号',
|
|
245
|
+
B站登录: '扫码登录 B站账号',
|
|
246
|
+
抖音登录: '扫码登录抖音账号',
|
|
247
|
+
设置抖音推送: '设置抖音推送(用法:设置抖音推送 <抖音号>)',
|
|
248
|
+
设置B站推送: '设置B站推送(用法:设置B站推送 <UID>)',
|
|
249
|
+
抖音推送列表: '查看本群抖音推送列表',
|
|
250
|
+
B站推送列表: '查看本群B站推送列表',
|
|
251
|
+
kkk设置推送机器人: '指定推送使用的机器人账号',
|
|
252
|
+
kkk推送全局忽略: '全局忽略某个推送对象'
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** 把正则里的分组展开成具体名字,例如 \`(抖音|B站)(全部)?强制推送\` → 4 个名字 */
|
|
256
|
+
function expandPattern (input: string): string[] {
|
|
257
|
+
const group = input.match(/\(([^()]*)\)(\?)?/)
|
|
258
|
+
if (!group || group.index === undefined) return [input]
|
|
259
|
+
const before = input.slice(0, group.index)
|
|
260
|
+
const after = input.slice(group.index + group[0].length)
|
|
261
|
+
const variants = group[1].includes('|') ? group[1].split('|') : [group[1]]
|
|
262
|
+
const results: string[] = []
|
|
263
|
+
for (const variant of variants) {
|
|
264
|
+
if (group[2]) results.push(...expandPattern(before + after))
|
|
265
|
+
results.push(...expandPattern(before + variant + after))
|
|
266
|
+
}
|
|
267
|
+
return results
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* 从 karin 的 reg 里推导出「用户能敲的 Koishi 指令名」。
|
|
272
|
+
*
|
|
273
|
+
* 上游的 reg 是 karin 风格的正则(\`/^#?(解析|kkk解析|弹幕解析)/\`),这里把
|
|
274
|
+
* 开头锚点、结尾锚点和旧的 \`#\` 前缀剥掉,再把分组展开 —— 推不出来就返回空数组,
|
|
275
|
+
* 由调用方退化成 \`#\` 前缀的中间件,不影响功能。
|
|
276
|
+
* @param reg karin 的命令匹配式
|
|
277
|
+
* @returns 指令名列表
|
|
278
|
+
*/
|
|
279
|
+
function commandNames (reg: RegExp | string): string[] {
|
|
280
|
+
const source = typeof reg === 'string' ? reg : reg.source
|
|
281
|
+
const manual = MANUAL_COMMAND_NAMES[source]
|
|
282
|
+
if (manual) return manual
|
|
283
|
+
const text = source.replace(/^\^/, '').replace(/\$$/, '').replace(/^#\??/, '')
|
|
284
|
+
// 还剩 \s、字符类、量词这类写法的,说明不是「纯字面量」,交给手工表
|
|
285
|
+
if (!/^[\w\u4e00-\u9fa5|()?]+$/.test(text)) return []
|
|
286
|
+
return [...new Set(expandPattern(text))].filter((name) => /^[A-Za-z\u4e00-\u9fa5][\w\u4e00-\u9fa5]*$/.test(name))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** 去掉 Koishi 命令前缀(\`/解析 x\` → \`解析 x\`) */
|
|
290
|
+
function stripCommandPrefix (content: string): string {
|
|
291
|
+
for (const prefix of commandPrefixes()) {
|
|
292
|
+
if (prefix && content.startsWith(prefix)) return content.slice(prefix.length)
|
|
293
|
+
}
|
|
294
|
+
return content
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** 这条消息是不是「在调指令」(带 Koishi 前缀,或以某个已注册指令名开头) */
|
|
298
|
+
function isCommandMessage (content: string, names: Set<string>): boolean {
|
|
299
|
+
for (const prefix of commandPrefixes()) {
|
|
300
|
+
if (prefix && content.startsWith(prefix)) return true
|
|
301
|
+
}
|
|
302
|
+
for (const name of names) {
|
|
303
|
+
if (content === name || content.startsWith(name + ' ') || content.startsWith(name + ' ')) return true
|
|
304
|
+
}
|
|
305
|
+
return false
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* 注册指令。
|
|
310
|
+
*
|
|
311
|
+
* 上游 karin 用「正则 + 中间件」分发命令,Koishi 里这样注册出来的命令**不出现在指令列表里**、
|
|
312
|
+
* 也没法用 Koishi 的前缀体系。所以这里分两条路:
|
|
313
|
+
* 1. **能推导出名字的注册 → 注册成真正的 Koishi 指令**(\`解析\` / \`弹幕解析\` / \`kkk帮助\` …),
|
|
314
|
+
* 控制台能看到、\`/解析\` 和(配置了空前缀时)\`解析\` 都能触发;
|
|
315
|
+
* 2. **链接识别类注册 → 仍然是中间件**(它们不是「用户敲的命令」,而是「消息里有链接就解析」),
|
|
316
|
+
* 遇到指令消息会让路;
|
|
317
|
+
* 3. 旧的 \`#解析\` 写法保留一条中间件兜底,老用户不会突然用不了。
|
|
318
|
+
*
|
|
319
|
+
* karin 的业务代码都假设 \`e.msg\` 以 \`#\` 开头(\`e.msg.replace(/^#设置抖音推送/, '')\` 这种),
|
|
320
|
+
* 所以给指令构造 Message 时会把 Koishi 前缀换回 \`#\` —— 20 多处命令解析不用改一行。
|
|
321
|
+
* @param ctx Koishi 上下文
|
|
322
|
+
* @param logger 插件日志
|
|
323
|
+
* @param autoParse 是否自动解析消息里的链接
|
|
324
|
+
*/
|
|
325
|
+
/** 已经提示过「没有权限」的消息 ID(防止指令 + 文本兜底两条路重复提示) */
|
|
326
|
+
const permissionNotified = new Set<string>()
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 指令 action 的「已消费」返回值。
|
|
330
|
+
* Koishi 的消息入口是 `if (result) await session.send(result)` —— 返回真值会被当成消息发出去
|
|
331
|
+
* (之前返回 true,群里就真的收到一条「true」),所以这里用空串:既截断后续中间件,又不会被发送。
|
|
332
|
+
*/
|
|
333
|
+
const EMPTY_RESULT = ''
|
|
334
|
+
|
|
335
|
+
function registerCommands (
|
|
336
|
+
ctx: Context,
|
|
337
|
+
logger: ReturnType<Context['logger']>,
|
|
338
|
+
autoParse: boolean
|
|
339
|
+
) {
|
|
340
|
+
const registrations = [...commandQueue].sort((a, b) => {
|
|
341
|
+
const pa = Number(a.options?.priority ?? 0)
|
|
342
|
+
const pb = Number(b.options?.priority ?? 0)
|
|
343
|
+
return pb - pa
|
|
344
|
+
})
|
|
345
|
+
const isAutoParse = (registration: typeof registrations[number]) =>
|
|
346
|
+
AUTO_PARSE_REGISTRATIONS.has(String(registration.options?.name ?? ''))
|
|
347
|
+
const linkRegistrations = registrations.filter(isAutoParse)
|
|
348
|
+
const typedRegistrations = registrations.filter((item) => !isAutoParse(item))
|
|
349
|
+
|
|
350
|
+
/** 跑一个 karin 注册;返回「这条消息是否已被消费」(handler 没调 next 就是消费了) */
|
|
351
|
+
const runRegistration = async (
|
|
352
|
+
registration: typeof registrations[number],
|
|
353
|
+
session: any,
|
|
354
|
+
msg: string
|
|
355
|
+
): Promise<boolean> => {
|
|
356
|
+
const { handler, options, reg } = registration
|
|
357
|
+
// karin 的 event 选项:限定只在这些会话类型里生效(默认 message = 群聊 + 私聊)
|
|
358
|
+
const eventScope = options?.event
|
|
359
|
+
if (eventScope === 'message.group' && !session.guildId) return false
|
|
360
|
+
if (eventScope === 'message.private' && session.guildId) return false
|
|
361
|
+
|
|
362
|
+
// karin 的 perm 选项:master / admin / all
|
|
363
|
+
if (!checkPermission(session, options?.perm)) {
|
|
364
|
+
// 同一条消息可能被「Koishi 指令」和「文本兜底中间件」各跑一次,导致重复提示;
|
|
365
|
+
// 按消息 ID 去重,保证用户只看到一次
|
|
366
|
+
const messageId = String(session?.messageId ?? session?.event?.message?.id ?? '')
|
|
367
|
+
// 没有 messageId 时退化成「会话 + 内容 + 3 秒时间桶」,总之同一次触发只提示一次
|
|
368
|
+
const key = messageId || [
|
|
369
|
+
session?.selfId, session?.channelId, session?.userId,
|
|
370
|
+
String(session?.content ?? '').slice(0, 40),
|
|
371
|
+
Math.floor(Date.now() / 3000)
|
|
372
|
+
].join('|')
|
|
373
|
+
if (!permissionNotified.has(key)) {
|
|
374
|
+
permissionNotified.add(key)
|
|
375
|
+
if (permissionNotified.size > 500) permissionNotified.clear()
|
|
376
|
+
await session.send('你没有权限执行该命令').catch(() => {})
|
|
377
|
+
}
|
|
378
|
+
return true
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let continued = false
|
|
382
|
+
try {
|
|
383
|
+
const result = await handler(Message.fromSession(session, msg), () => {
|
|
384
|
+
continued = true
|
|
385
|
+
return NEXT
|
|
386
|
+
})
|
|
387
|
+
if (result === NEXT) continued = true
|
|
388
|
+
} catch (error: any) {
|
|
389
|
+
logger.error('命令 %s 执行失败: %s', options?.name ?? String(reg), error?.stack ?? error)
|
|
390
|
+
throw error
|
|
391
|
+
}
|
|
392
|
+
return !continued
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// 1) 注册成真正的 Koishi 指令
|
|
396
|
+
const registered = new Set<string>()
|
|
397
|
+
for (const registration of typedRegistrations) {
|
|
398
|
+
const names = commandNames(registration.reg)
|
|
399
|
+
if (!names.length) {
|
|
400
|
+
logger.debug('推导不出指令名,只保留 # 写法: %s', String(registration.reg))
|
|
401
|
+
continue
|
|
402
|
+
}
|
|
403
|
+
for (const name of names) {
|
|
404
|
+
if (registered.has(name)) continue
|
|
405
|
+
/**
|
|
406
|
+
* 弹幕功能已整体移除(用户要求):弹幕解析指令不注册,发出来没有任何反应,
|
|
407
|
+
* 控制台的指令列表里也不会出现它。卡片上方的热门弹幕不受影响。
|
|
408
|
+
*/
|
|
409
|
+
if (/^#?弹幕解析$/.test(name.trim())) {
|
|
410
|
+
logger.debug('弹幕功能已移除,跳过注册指令 %s', name)
|
|
411
|
+
continue
|
|
412
|
+
}
|
|
413
|
+
registered.add(name)
|
|
414
|
+
const description = COMMAND_DESCRIPTIONS[name] ?? ('koishi-plugin-kkk: ' + (registration.options?.name ?? name))
|
|
415
|
+
// 解析类指令接一段自由文本(链接 / BV 号 / 参数),声明出来控制台里能看清用法;
|
|
416
|
+
// Koishi 的 checkArgCount / checkUnknown 默认都是关的,多传也不会报错
|
|
417
|
+
const parseCommand = name === '解析' || name === '弹幕解析' || name === 'kkk解析'
|
|
418
|
+
const command = ctx.command(parseCommand ? name + ' [input:text]' : name, description)
|
|
419
|
+
// 面板按钮发过来的是「解析 <链接> --qn=80」这类文本,声明一下选项免得被当成参数报错
|
|
420
|
+
if (parseCommand) {
|
|
421
|
+
command
|
|
422
|
+
.option('qn', '--qn <qn:number> B站画质 qn')
|
|
423
|
+
.option('q', '--q <quality:string> 抖音/小红书画质档位')
|
|
424
|
+
.option('dm', '--dm <value:string> 烧录弹幕')
|
|
425
|
+
.option('panel', '--panel <mode:number> 只重发解析面板')
|
|
426
|
+
.option('bgp', '--bgp <page:number> 番剧分集表格翻页')
|
|
427
|
+
}
|
|
428
|
+
command.action(async ({ session }) => {
|
|
429
|
+
if (!session) return
|
|
430
|
+
// karin 的代码都假设 e.msg 以 # 开头,这里把 Koishi 前缀换回 #(见函数注释)
|
|
431
|
+
const consumed = await runRegistration(registration, session, '#' + stripCommandPrefix(session.content ?? ''))
|
|
432
|
+
/**
|
|
433
|
+
* 消费掉的消息要返回一个**非空值**:Koishi 的 \`Command.execute\` 在 action 返回空值时会
|
|
434
|
+
* 继续跑后面的队列(也就是把消息交回中间件链),那条链上的链接自动解析会**再解析一次** ——
|
|
435
|
+
* 表现就是「检测到链接,开始解析」出现两次、第二次还失败。
|
|
436
|
+
*/
|
|
437
|
+
// 注意:Koishi 的实现是「返回值是真值就 send 出去」,所以这里只能返回空串:
|
|
438
|
+
// 空串能截断后续中间件,又不会被当成消息发出去(返回 true 会真的回一个「true」)
|
|
439
|
+
return consumed ? EMPTY_RESULT : undefined
|
|
440
|
+
})
|
|
441
|
+
}
|
|
442
|
+
logger.debug('注册指令 %s ← %s', names.join(' / '), String(registration.reg))
|
|
443
|
+
}
|
|
444
|
+
if (registered.size) logger.info('已注册 Koishi 指令 %d 个:%s', registered.size, [...registered].join(' '))
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* 「下载进度」指令:面板上的 📊 按钮点一下就发它。
|
|
448
|
+
* 解析大文件时群里只有一句「收到请求,开始下载」,有这个按钮就能随时看进度。
|
|
449
|
+
*/
|
|
450
|
+
if (!registered.has('下载进度')) {
|
|
451
|
+
registered.add('下载进度')
|
|
452
|
+
ctx.command('下载进度', '查看当前解析下载进度').action(async ({ session }) => {
|
|
453
|
+
const { listActiveDownloads } = require('./karin/module/utils/Network/Downloader')
|
|
454
|
+
const tasks = listActiveDownloads()
|
|
455
|
+
const formatMB = (bytes: number) => (bytes / 1024 / 1024).toFixed(1)
|
|
456
|
+
const lines = tasks.length
|
|
457
|
+
? tasks.map((task: any) => {
|
|
458
|
+
// 「还没开始传输字节」的阶段(获取下载链接 / 合并音轨)直接显示阶段文案
|
|
459
|
+
if (task.stage) return '• ' + task.name + ' ' + task.stage
|
|
460
|
+
const percent = task.total > 0 ? Math.floor((task.bytes / task.total) * 100) + '%' : '?'
|
|
461
|
+
return '• ' + task.name + ' ' + formatMB(task.bytes) + (task.total > 0 ? '/' + formatMB(task.total) + ' MB' : ' MB') + '(' + percent + ')'
|
|
462
|
+
})
|
|
463
|
+
: ['当前没有正在进行的下载']
|
|
464
|
+
await session?.send('📥 下载进度\n' + lines.join('\n'))
|
|
465
|
+
return EMPTY_RESULT
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* 按 karin 的注册表跑一条「文本命令」。
|
|
471
|
+
* 旧的 \`#\` 写法和 QQ 的按钮交互事件都走这里 —— 它们都绕过了 Koishi 的指令解析。
|
|
472
|
+
* @returns 是否已消费这条消息
|
|
473
|
+
*/
|
|
474
|
+
const runTextCommand = async (session: any, text: string): Promise<boolean> => {
|
|
475
|
+
for (const registration of typedRegistrations) {
|
|
476
|
+
const { reg } = registration
|
|
477
|
+
let matched = false
|
|
478
|
+
try {
|
|
479
|
+
matched = typeof reg === 'string' ? text.startsWith(reg) : new RegExp(reg.source, reg.flags).test(text)
|
|
480
|
+
} catch (error) {
|
|
481
|
+
logger.warn('命令匹配失败 %s: %s', registration.options?.name ?? String(reg), error)
|
|
482
|
+
continue
|
|
483
|
+
}
|
|
484
|
+
if (!matched) continue
|
|
485
|
+
return await runRegistration(registration, session, text)
|
|
486
|
+
}
|
|
487
|
+
return false
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 2) 文本兜底:旧的 \`#\` 写法 + 「Koishi 没认出来的指令文本」都从这里走。
|
|
491
|
+
//
|
|
492
|
+
// 为什么还需要它:Koishi 的指令匹配依赖 prefix 配置 —— 面板按钮发的是不带前缀的
|
|
493
|
+
// \`解析 --p=xxx --qn=80\`,用户也可能照着手敲。要是宿主的 prefix 没配空串,
|
|
494
|
+
// 这条文本就既不是指令、又不像链接,插件会**完全没反应**。
|
|
495
|
+
// 这里在 Koishi 没有认领(argv.command 为空)时按同一张注册表再匹配一次,行为与指令完全一致。
|
|
496
|
+
ctx.middleware(async (session: any, next) => {
|
|
497
|
+
const raw = session.content ?? ''
|
|
498
|
+
if (!raw) return next()
|
|
499
|
+
if (session.argv?.command) return next()
|
|
500
|
+
/**
|
|
501
|
+
* 卡片消息:**既没有链接、也不是指令**,平台正则和指令表都匹配不到,
|
|
502
|
+
* 所以必须兜在链路最后一环。
|
|
503
|
+
*
|
|
504
|
+
* 关键:定位到作品后**不能只 next()** —— 后面已经没有处理器了。
|
|
505
|
+
* 正确做法是把消息文本换成「解析 <链接>」重新跑一遍匹配,复用完整解析流程。
|
|
506
|
+
*/
|
|
507
|
+
if (/卡片消息/.test(raw)) {
|
|
508
|
+
try {
|
|
509
|
+
const { extractCardInfo, resolveCardToUrl } = await import('./karin/module/utils/CardParser')
|
|
510
|
+
/**
|
|
511
|
+
* **只处理 B站卡片。**
|
|
512
|
+
*
|
|
513
|
+
* 卡片摘要里自带平台名(实测 source: 哔哩哔哩),网易云音乐、QQ音乐、淘宝之类的卡片
|
|
514
|
+
* 也会被这段逻辑接住,白白跑一遍 OCR + 搜索,还会发「正在提取卡片信息…」打扰用户。
|
|
515
|
+
* 认不出平台、或者不是 B站,直接放行(那些卡片本来也不该由我们解析)。
|
|
516
|
+
*/
|
|
517
|
+
const cardPlatform = String(extractCardInfo(raw)?.source ?? '')
|
|
518
|
+
if (!/哔哩|bilibili|B站/i.test(cardPlatform)) {
|
|
519
|
+
logger.debug('卡片来源不是 B站(%s),跳过卡片解析', cardPlatform || '未知')
|
|
520
|
+
return next()
|
|
521
|
+
}
|
|
522
|
+
const send = async (content: any) => {
|
|
523
|
+
try {
|
|
524
|
+
await (session as any).send(content)
|
|
525
|
+
} catch (error: any) {
|
|
526
|
+
logger.debug('卡片解析回话失败: %s', String(error?.message ?? error))
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// 提取 + OCR + 搜索要几秒,先给个反馈
|
|
530
|
+
await send('正在提取卡片信息…')
|
|
531
|
+
const resolved = await resolveCardToUrl(raw)
|
|
532
|
+
if (resolved && resolved.url) {
|
|
533
|
+
// 注意:这里的 logger 是 Koishi 的 ctx.logger,没有 mark 方法(只有 info/warn/debug)
|
|
534
|
+
logger.info('卡片解析命中,按链接重新解析: %s', resolved.url)
|
|
535
|
+
if (await runTextCommand(session, '#解析 ' + resolved.url)) return
|
|
536
|
+
}
|
|
537
|
+
if (resolved && resolved.candidates && resolved.candidates.length) {
|
|
538
|
+
const { cmdInput } = await import('./karin/module/utils/QqPanel')
|
|
539
|
+
const table = ['| # | 标题 | UP / 作者 | 操作 |', '| :---: | :--- | :--- | :---: |']
|
|
540
|
+
resolved.candidates.slice(0, 6).forEach((item: any, index: number) => {
|
|
541
|
+
const link = item.platform === 'bilibili'
|
|
542
|
+
? 'https://www.bilibili.com/video/' + item.id
|
|
543
|
+
: 'https://www.douyin.com/video/' + item.id
|
|
544
|
+
const title = String(item.title || '(无标题)').replace(/[|\n]/g, ' ').slice(0, 26)
|
|
545
|
+
const author = String(item.author || '-').replace(/[|\n]/g, ' ').slice(0, 12)
|
|
546
|
+
table.push('| ' + (index + 1) + ' | ' + title + ' | ' + author + ' | ' + cmdInput('解析 ' + link, '解析') + ' |')
|
|
547
|
+
})
|
|
548
|
+
const tip = '没能唯一确定这个作品(识别到:' + ((resolved.upName) || '未知') + '),下面是候选,点按钮直接解析:'
|
|
549
|
+
await send([{ type: 'markdown', attrs: { content: tip + String.fromCharCode(10) + table.join(String.fromCharCode(10)) } }])
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
await send('没能从这张卡片里认出作品,直接发链接给我吧')
|
|
553
|
+
return
|
|
554
|
+
} catch (error: any) {
|
|
555
|
+
logger.warn('卡片解析失败: ' + String(error?.message ?? error))
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const text = raw.startsWith('#') ? raw : '#' + stripCommandPrefix(raw)
|
|
559
|
+
if (await runTextCommand(session, text)) return
|
|
560
|
+
return next()
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* 3) QQ 按钮点击的另一种落地方式。
|
|
565
|
+
*
|
|
566
|
+
* QQ 官方适配器对按钮有两种投递:**指令按钮**(我们用的 type=2)会变成一条用户消息,
|
|
567
|
+
* 走上面的 Koishi 指令;**回调按钮**(type=1)则是 \`interaction/button\` 事件,
|
|
568
|
+
* 点击内容在 \`session.event.button.data\` 里、**不会**变成消息。
|
|
569
|
+
* 两条都接上,用户点哪种按钮都能解析(也兼容以后把按钮换成回调类型)。
|
|
570
|
+
*/
|
|
571
|
+
ctx.on('interaction/button', (session: any) => {
|
|
572
|
+
const data = String(session?.event?.button?.data ?? '').trim()
|
|
573
|
+
if (!data) return
|
|
574
|
+
const text = data.startsWith('#') ? data : '#' + data
|
|
575
|
+
logger.debug('收到 QQ 按钮交互: %s', text)
|
|
576
|
+
runTextCommand(session, text).catch((error) => {
|
|
577
|
+
logger.error('处理按钮交互失败: %s', error?.stack ?? error)
|
|
578
|
+
})
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
// 4) 链接自动解析:消息里出现链接就解析(不是指令,遇到指令消息让路)
|
|
582
|
+
if (!autoParse) {
|
|
583
|
+
logger.info('自动解析已关闭(autoParse=false),消息里的链接不会再自动解析')
|
|
584
|
+
return
|
|
585
|
+
}
|
|
586
|
+
ctx.middleware(async (session, next) => {
|
|
587
|
+
const raw = session.content ?? ''
|
|
588
|
+
if (!raw || isCommandMessage(raw, registered)) return next()
|
|
589
|
+
// 上面那条兜底中间件已经按注册表匹配过一遍了,这里只处理「链接识别」
|
|
590
|
+
// QQ 新版分享卡片可能一个链接都不带,必须在**匹配前**先还原出链接,
|
|
591
|
+
// 否则平台的链接正则命中不了,命令根本不会触发(普通消息走同步快路径,无额外开销)
|
|
592
|
+
const content = await resolveQqCardContent(raw).catch((error) => {
|
|
593
|
+
logger.debug('QQ 卡片还原失败: %s', error)
|
|
594
|
+
return raw
|
|
595
|
+
})
|
|
596
|
+
for (const registration of linkRegistrations) {
|
|
597
|
+
const { reg } = registration
|
|
598
|
+
let matched = false
|
|
599
|
+
try {
|
|
600
|
+
matched = typeof reg === 'string' ? content.startsWith(reg) : new RegExp(reg.source, reg.flags).test(content)
|
|
601
|
+
} catch (error) {
|
|
602
|
+
logger.warn('命令匹配失败 %s: %s', registration.options?.name ?? String(reg), error)
|
|
603
|
+
continue
|
|
604
|
+
}
|
|
605
|
+
if (!matched) continue
|
|
606
|
+
if (await runRegistration(registration, session, content)) return
|
|
607
|
+
}
|
|
608
|
+
return next()
|
|
609
|
+
})
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export async function apply (ctx: Context, rawConfig: Config) {
|
|
613
|
+
const logger = ctx.logger('kkk')
|
|
614
|
+
setLogger(logger)
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* 控制台表单把选项分成两个折叠组(「QQ 适配器」和「Koishi 原生设置」),
|
|
618
|
+
* 这里统一摊平回顶层 —— 代码里照旧读 config.qqPanel / config.sliceImageOnDemand 等。
|
|
619
|
+
* 同时把引导项(webuiGuide)丢掉,它只是个提示。
|
|
620
|
+
* qq 组放最后:它优先于早期直接写在顶层的同名字段。
|
|
621
|
+
*/
|
|
622
|
+
const raw = (rawConfig ?? {}) as any
|
|
623
|
+
const { webuiGuide: _guide, advanced, qq, upstream, ...rest } = raw
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* 早期的版本把这些开关直接写在配置顶层(`masters` / `qqPanel` …),
|
|
627
|
+
* 现在它们分别在「Koishi 原生设置」和「QQ 适配器」分组里。
|
|
628
|
+
* 顶层的旧值要**压过**分组里的默认值 —— 否则 schema 的 default 会把用户原来的设置盖掉
|
|
629
|
+
* (踩过一次:masters / debug / ocrApiKey 被默认值吃掉了)。
|
|
630
|
+
*/
|
|
631
|
+
const legacy: any = {}
|
|
632
|
+
for (const key of [...NATIVE_KEYS, ...QQ_KEYS]) {
|
|
633
|
+
if (raw[key] !== undefined) legacy[key] = raw[key]
|
|
634
|
+
}
|
|
635
|
+
const groupQq: any = { ...(qq ?? {}) }
|
|
636
|
+
const groupNative: any = { ...(advanced ?? {}) }
|
|
637
|
+
for (const key of QQ_KEYS) if (legacy[key] !== undefined) groupQq[key] = legacy[key]
|
|
638
|
+
for (const key of NATIVE_KEYS) if (legacy[key] !== undefined) groupNative[key] = legacy[key]
|
|
639
|
+
|
|
640
|
+
// 注意 upstream 要放回去:它被解构出来了,漏掉的话 WebUI / configBridge 就拿不到上游配置了
|
|
641
|
+
const config = { ...rest, upstream, ...groupNative, ...groupQq } as Config
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* 顺手把顶层遗留项搬进分组并写回 koishi.yml(只做一次:写回后顶层就没有这些键了)。
|
|
645
|
+
* 不搬的话,控制台表单里显示的是默认值、和实际生效的值不一致,很容易改错。
|
|
646
|
+
*/
|
|
647
|
+
if (Object.keys(legacy).length) {
|
|
648
|
+
// 放到 ready 之后再写:scope.update 会热重载本插件,在 apply 里直接写会和本次启动抢注册
|
|
649
|
+
// (踩过:控制台报 duplicate option name "qn" for command "解析")
|
|
650
|
+
const migrate = async () => {
|
|
651
|
+
const scope: any = (ctx as any).scope
|
|
652
|
+
if (typeof scope?.update !== 'function') return
|
|
653
|
+
const next: any = { ...rest, upstream }
|
|
654
|
+
for (const key of [...NATIVE_KEYS, ...QQ_KEYS]) delete next[key]
|
|
655
|
+
if (Object.keys(groupQq).length) next.qq = groupQq
|
|
656
|
+
if (Object.keys(groupNative).length) next.advanced = groupNative
|
|
657
|
+
try {
|
|
658
|
+
await scope.update(next)
|
|
659
|
+
logger.info('[kkk] 已把配置文件顶层的旧选项迁移到「QQ 适配器」/「Koishi 原生设置」分组')
|
|
660
|
+
} catch (error: any) {
|
|
661
|
+
logger.warn('[kkk] 迁移旧配置项失败(不影响本次运行): ' + String(error?.message ?? error))
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
ctx.on('ready', () => { ctx.setTimeout(() => { void migrate() }, 2000) })
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* 配置 WebUI:一个独立小页面(/kkk,口令见 webUiPassword,默认 131425),
|
|
669
|
+
* 保存时走 ctx.scope.update 写回 koishi.yml 并热重载;控制台里也注册了入口页面(见 client/index.js)。
|
|
670
|
+
*/
|
|
671
|
+
registerWebUi({ ctx, config, rawConfig: rawConfig as any, logger, pluginRoot: pluginRootDir })
|
|
672
|
+
try {
|
|
673
|
+
const consoleService: any = (ctx as any).console
|
|
674
|
+
if (consoleService && typeof consoleService.addEntry === 'function') {
|
|
675
|
+
/**
|
|
676
|
+
* 指向 **dist**:官方工具 `koishi-console build .` 把浏览器端产物打到 dist/index.js。
|
|
677
|
+
* 之前指向 client,加载到的是源码 TS,浏览器解析不了 —— 侧边栏就一直看不到入口。
|
|
678
|
+
*/
|
|
679
|
+
consoleService.addEntry({ prod: path.resolve(pluginRootDir, 'dist') })
|
|
680
|
+
}
|
|
681
|
+
} catch (error) {
|
|
682
|
+
logger.debug('[kkk] 注册控制台入口失败: ' + String(error))
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const pluginRoot = path.resolve(__dirname, '..')
|
|
686
|
+
const dataRoot = path.isAbsolute(config.dataPath) ? config.dataPath : path.resolve(ctx.baseDir ?? process.cwd(), config.dataPath)
|
|
687
|
+
|
|
688
|
+
bindRuntime({
|
|
689
|
+
ctx,
|
|
690
|
+
config: {
|
|
691
|
+
masters: config.masters ?? [],
|
|
692
|
+
debug: config.debug,
|
|
693
|
+
dataPath: config.dataPath,
|
|
694
|
+
qqPanel: config.qqPanel !== false,
|
|
695
|
+
qqFileLimitMB: Number(config.qqFileLimitMB) || 200,
|
|
696
|
+
recallPanel: config.recallPanel !== false,
|
|
697
|
+
bangumiPanelCols: Number(config.bangumiPanelCols) || 5,
|
|
698
|
+
bangumiPanelRows: Number(config.bangumiPanelRows) || 4
|
|
699
|
+
},
|
|
700
|
+
pluginRoot,
|
|
701
|
+
dataRoot
|
|
702
|
+
})
|
|
703
|
+
|
|
704
|
+
fs.mkdirSync(dataRoot, { recursive: true })
|
|
705
|
+
|
|
706
|
+
// 控制台里填过的上游配置写回 config.json —— 必须赶在加载上游 apps 之前,
|
|
707
|
+
// 因为部分模块会在 import 时就把配置读进闭包(例如 apps/tools.ts 里的优先级判断)。
|
|
708
|
+
if (config.upstream && Object.keys(config.upstream).length > 0) {
|
|
709
|
+
try {
|
|
710
|
+
const { changed, file } = applyUpstreamOverrides(config.upstream)
|
|
711
|
+
if (changed.length) {
|
|
712
|
+
logger.info('已把控制台里的 %d 项上游配置写入 %s(%s%s)', changed.length, file,
|
|
713
|
+
changed.slice(0, 6).join(', '), changed.length > 6 ? ' …' : '')
|
|
714
|
+
}
|
|
715
|
+
} catch (error: any) {
|
|
716
|
+
logger.error('写入上游配置失败: %s', error?.stack ?? error)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// 加载移植过来的 Karin 应用(模块顶层会调用 karin.command / karin.task 入队)
|
|
721
|
+
const apps = ['tools', 'help', 'admin', 'push', 'qrlogin', 'statistics', 'testPush', 'update']
|
|
722
|
+
for (const app of apps) {
|
|
723
|
+
try {
|
|
724
|
+
await import('./karin/apps/' + app)
|
|
725
|
+
} catch (error: any) {
|
|
726
|
+
logger.warn('加载 app %s 失败(该功能暂不可用): %s', app, error?.message ?? error)
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// 初始化数据库实例(对应 Karin 版 setup.ts 的 initAllDatabases)
|
|
731
|
+
// 原实现用顶层 await 立即初始化,CJS 下改成在这里显式引导,否则 module/db 导出的实例是 null
|
|
732
|
+
try {
|
|
733
|
+
const { bootstrapDatabases } = await import('./karin/module/db')
|
|
734
|
+
const { douyinDB, bilibiliDB, statisticsDB } = await bootstrapDatabases()
|
|
735
|
+
logger.debug('数据库初始化完成:%s / %s / %s', douyinDB.constructor.name, bilibiliDB.constructor.name, statisticsDB.constructor.name)
|
|
736
|
+
} catch (error: any) {
|
|
737
|
+
logger.error('数据库初始化失败: %s', error?.stack ?? error)
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// 创建运行期需要的目录(对应 Karin 版 setup.ts 里的 mkdirSync)
|
|
741
|
+
try {
|
|
742
|
+
const { Common } = await import('./karin/module/utils/Common')
|
|
743
|
+
for (const dir of Object.values(Common.tempDri)) {
|
|
744
|
+
if (typeof dir === 'string') fs.mkdirSync(dir, { recursive: true })
|
|
745
|
+
}
|
|
746
|
+
} catch (error: any) {
|
|
747
|
+
logger.warn('初始化临时目录失败: %s', error?.message ?? error)
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// 控制台里配的 OCR key 覆盖到上游配置上(CardParser 读的是 Config.app.ocrApiKey)
|
|
751
|
+
if (config.ocrApiKey) { try { (Config.app as any).ocrApiKey = config.ocrApiKey } catch { /* 忽略 */ } }
|
|
752
|
+
registerCommands(ctx, logger, config.autoParse !== false)
|
|
753
|
+
startScheduler(ctx, logger, taskQueue)
|
|
754
|
+
|
|
755
|
+
// 兼容 karin 的 BOT_CONNECT:Koishi 侧用 bot-status-update 近似
|
|
756
|
+
ctx.on('bot-status-update', (bot: any) => {
|
|
757
|
+
for (const item of eventQueue) {
|
|
758
|
+
if (item.event !== 'bot-connect' && item.event !== 'BOT_CONNECT') continue
|
|
759
|
+
Promise.resolve()
|
|
760
|
+
.then(() => item.handler(bot))
|
|
761
|
+
.catch((error: any) => logger.error('bot-connect 处理器失败: %s', error?.stack ?? error))
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
logger.info('koishi-plugin-kkk 已加载:命令 %d 个,定时任务 %d 个', commandQueue.length, taskQueue.length)
|
|
766
|
+
if (!tryGetRuntime()?.ctx.bots?.length) {
|
|
767
|
+
logger.debug('当前没有已连接的机器人,等待适配器上线')
|
|
768
|
+
}
|
|
769
|
+
}
|