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,582 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Render = exports.getImageMetadata = void 0;
|
|
7
|
+
exports.buildFallbackHtml = buildFallbackHtml;
|
|
8
|
+
exports.resolveFrameLogo = resolveFrameLogo;
|
|
9
|
+
exports.buildPosterContext = buildPosterContext;
|
|
10
|
+
exports.renderTemplateHtml = renderTemplateHtml;
|
|
11
|
+
/**
|
|
12
|
+
* 渲染模块(Koishi 移植版)。
|
|
13
|
+
*
|
|
14
|
+
* 上游链路:ktr(\`@karinjs/template-react\`)把 \`template/**\` 打成 SSR 外壳 → 出 HTML → Karin render 截图。
|
|
15
|
+
* Koishi 侧不引入 ktr 的构建管线(需要 vite/tailwind 打包),改为:
|
|
16
|
+
* 1. 直接用 **react-dom/server** 对上游模板组件做 SSR(模板只依赖 \`{ data, ctx }\` props);
|
|
17
|
+
* 2. 内联上游构建产物 \`resources/template/style.css\`(tailwind 编译结果,省掉 tailwind 构建);
|
|
18
|
+
* 3. 用 koishi-plugin-puppeteer 截图。
|
|
19
|
+
* 路由不在注册表里、或 SSR 失败时,回退到内置通用信息卡片,保证解析结果始终有图。
|
|
20
|
+
*/
|
|
21
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
23
|
+
const node_url_1 = require("node:url");
|
|
24
|
+
const node_karin_1 = require("../../../../compat/node-karin.js");
|
|
25
|
+
const root_1 = require("../../../../compat/root.js");
|
|
26
|
+
const node_stream_1 = require("node:stream");
|
|
27
|
+
const react_1 = __importDefault(require("react"));
|
|
28
|
+
const server_1 = require("react-dom/server");
|
|
29
|
+
const esm_1 = require("../../../../compat/esm");
|
|
30
|
+
const registry_1 = require("../../../../ktr/registry");
|
|
31
|
+
const utils_1 = require("../../../module/utils");
|
|
32
|
+
const Config_1 = require("../../../module/utils/Config");
|
|
33
|
+
const coverTheme_1 = require("./coverTheme");
|
|
34
|
+
/* ------------------------------------------------------------------ *
|
|
35
|
+
* 样式
|
|
36
|
+
* ------------------------------------------------------------------ */
|
|
37
|
+
let cachedCss = null;
|
|
38
|
+
/** 读取并修正模板样式(上游产物里的字体指向 Karin 本地服务,换成相对路径,加载失败则回退系统字体) */
|
|
39
|
+
function loadTemplateCss() {
|
|
40
|
+
if (cachedCss !== null)
|
|
41
|
+
return cachedCss;
|
|
42
|
+
try {
|
|
43
|
+
const cssPath = node_path_1.default.resolve(utils_1.Root.pluginPath, 'resources', 'template', 'style.css');
|
|
44
|
+
const raw = node_fs_1.default.readFileSync(cssPath, 'utf8');
|
|
45
|
+
cachedCss = raw.replace(/url\("?http:\/\/localhost:\d+\/config\/commonResource\//g, 'url("./template-fonts/');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
node_karin_1.logger.debug('[Render] 读取模板样式失败,将使用无样式渲染: ' + String(error?.message ?? error));
|
|
49
|
+
cachedCss = '';
|
|
50
|
+
}
|
|
51
|
+
return cachedCss;
|
|
52
|
+
}
|
|
53
|
+
/* ------------------------------------------------------------------ *
|
|
54
|
+
* 兜底卡片
|
|
55
|
+
* ------------------------------------------------------------------ */
|
|
56
|
+
const escapeHtml = (input) => String(input ?? '')
|
|
57
|
+
.replace(/&/g, '&')
|
|
58
|
+
.replace(/</g, '<')
|
|
59
|
+
.replace(/>/g, '>')
|
|
60
|
+
.replace(/"/g, '"');
|
|
61
|
+
/** 递归时不往里看的键(Koishi 的 ctx / 适配器实例等,读属性会触发「未注册」警告且毫无意义) */
|
|
62
|
+
const PICK_SKIP_KEYS = new Set([
|
|
63
|
+
'ctx', 'app', 'root', 'scope', 'bot', 'session', 'event', 'logger', 'config',
|
|
64
|
+
'_events', '_eventsCount', 'adapter', 'http', 'internal', 'socket', 'parser',
|
|
65
|
+
'target', 'parent', 'children', 'prototype', 'constructor'
|
|
66
|
+
]);
|
|
67
|
+
/** 只递归纯对象与数组:类实例(ctx / Bot / Session…)一律不深入 */
|
|
68
|
+
const isPlainSource = (value) => {
|
|
69
|
+
if (Array.isArray(value))
|
|
70
|
+
return true;
|
|
71
|
+
if (!value || typeof value !== 'object')
|
|
72
|
+
return false;
|
|
73
|
+
const proto = Object.getPrototypeOf(value);
|
|
74
|
+
return proto === Object.prototype || proto === null;
|
|
75
|
+
};
|
|
76
|
+
const pickFirst = (source, keys, depth = 3, seen = new Set()) => {
|
|
77
|
+
if (!source || typeof source !== 'object' || depth < 0)
|
|
78
|
+
return undefined;
|
|
79
|
+
// 防环(对象互相引用时会无限递归)
|
|
80
|
+
if (seen.has(source))
|
|
81
|
+
return undefined;
|
|
82
|
+
seen.add(source);
|
|
83
|
+
for (const key of keys) {
|
|
84
|
+
if (PICK_SKIP_KEYS.has(key))
|
|
85
|
+
continue;
|
|
86
|
+
const value = source[key];
|
|
87
|
+
if (value !== undefined && value !== null && value !== '')
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
for (const [key, value] of Object.entries(source)) {
|
|
91
|
+
if (PICK_SKIP_KEYS.has(key))
|
|
92
|
+
continue;
|
|
93
|
+
if (isPlainSource(value)) {
|
|
94
|
+
const found = pickFirst(value, keys, depth - 1, seen);
|
|
95
|
+
if (found !== undefined)
|
|
96
|
+
return found;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
};
|
|
101
|
+
const asText = (value) => {
|
|
102
|
+
if (value === undefined || value === null)
|
|
103
|
+
return '';
|
|
104
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
105
|
+
return String(value);
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
const parts = value.map((item) => (item && typeof item === 'object' ? item.text ?? item.desc ?? '' : item)).filter(Boolean);
|
|
108
|
+
return parts.join('');
|
|
109
|
+
}
|
|
110
|
+
return String(value.text ?? value.desc ?? value.title ?? '');
|
|
111
|
+
};
|
|
112
|
+
const formatCount = (value) => {
|
|
113
|
+
const num = Number(value);
|
|
114
|
+
if (!Number.isFinite(num))
|
|
115
|
+
return '';
|
|
116
|
+
if (num >= 100000000)
|
|
117
|
+
return (num / 100000000).toFixed(1) + '亿';
|
|
118
|
+
if (num >= 10000)
|
|
119
|
+
return (num / 10000).toFixed(1) + '万';
|
|
120
|
+
return String(num);
|
|
121
|
+
};
|
|
122
|
+
/** 从渲染数据里尽力提取通用卡片需要的字段 */
|
|
123
|
+
function extractCardData(data) {
|
|
124
|
+
const title = asText(pickFirst(data, ['title', 'desc', 'dynamicTitle', 'name']));
|
|
125
|
+
const author = asText(pickFirst(data, ['nickname', 'nick', 'author', 'name', 'uname']));
|
|
126
|
+
const avatar = asText(pickFirst(data, ['avatar', 'face', 'avatarUrl', 'head_url']));
|
|
127
|
+
const cover = asText(pickFirst(data, ['cover', 'coverUrl', 'originCover', 'pic', 'dynamicCover', 'image']));
|
|
128
|
+
const description = asText(pickFirst(data, ['desc', 'description', 'dynamicText', 'summary', 'content']));
|
|
129
|
+
const stats = [
|
|
130
|
+
['点赞', pickFirst(data, ['digg_count', 'like', 'likes'])],
|
|
131
|
+
['评论', pickFirst(data, ['comment_count', 'comments', 'reply'])],
|
|
132
|
+
['收藏', pickFirst(data, ['collect_count', 'favorite', 'favorites'])],
|
|
133
|
+
['分享', pickFirst(data, ['share_count', 'share', 'repost'])],
|
|
134
|
+
['播放', pickFirst(data, ['play', 'view', 'views', 'playCount'])]
|
|
135
|
+
]
|
|
136
|
+
.map(([label, value]) => [label, formatCount(value)])
|
|
137
|
+
.filter(([, value]) => !!value);
|
|
138
|
+
return { title, author, avatar, cover, description, stats };
|
|
139
|
+
}
|
|
140
|
+
/** 兜底卡片:不依赖模板,直接用通用字段渲染一张信息卡 */
|
|
141
|
+
function buildFallbackHtml(route, data, dark, scale) {
|
|
142
|
+
const card = extractCardData(data);
|
|
143
|
+
const bg = dark ? '#101014' : '#f4f5f7';
|
|
144
|
+
const fg = dark ? '#f7f8fa' : '#1c1f23';
|
|
145
|
+
const sub = dark ? '#a7abb3' : '#6b7280';
|
|
146
|
+
const coverHtml = card.cover ? '<div class="cover" style="background-image:url(' + escapeHtml(card.cover) + ')"></div>' : '';
|
|
147
|
+
const statsHtml = card.stats.length
|
|
148
|
+
? '<div class="stats">' + card.stats.map(([label, value]) => '<span><b>' + escapeHtml(value) + '</b>' + escapeHtml(label) + '</span>').join('') + '</div>'
|
|
149
|
+
: '';
|
|
150
|
+
return '<!DOCTYPE html>\n<html lang="zh-CN"><head><meta charset="utf-8"><style>\n' +
|
|
151
|
+
' * { box-sizing: border-box; margin: 0; padding: 0; }\n' +
|
|
152
|
+
' body { background: ' + bg + '; color: ' + fg + '; font-family: "Microsoft YaHei", "PingFang SC", system-ui, sans-serif; }\n' +
|
|
153
|
+
' #container { width: 720px; padding: 20px; }\n' +
|
|
154
|
+
' .card { border-radius: 18px; background: ' + (dark ? '#1b1c20' : '#ffffff') + '; overflow: hidden; box-shadow: 0 12px 32px rgba(0,0,0,.12); zoom: ' + scale + '; }\n' +
|
|
155
|
+
' .cover { width: 100%; height: 380px; background-size: cover; background-position: center; background-color: #2a2c31; }\n' +
|
|
156
|
+
' .body { padding: 20px 24px 26px; }\n' +
|
|
157
|
+
' .route { font-size: 13px; color: ' + sub + '; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 10px; }\n' +
|
|
158
|
+
' .title { font-size: 26px; font-weight: 700; line-height: 1.35; word-break: break-word; }\n' +
|
|
159
|
+
' .author { display: flex; align-items: center; gap: 10px; margin-top: 14px; color: ' + sub + '; font-size: 16px; }\n' +
|
|
160
|
+
' .author img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; }\n' +
|
|
161
|
+
' .desc { margin-top: 16px; font-size: 16px; line-height: 1.7; color: ' + sub + '; white-space: pre-wrap; word-break: break-word; }\n' +
|
|
162
|
+
' .stats { display: flex; gap: 26px; margin-top: 20px; font-size: 14px; color: ' + sub + '; }\n' +
|
|
163
|
+
' .stats b { display: block; font-size: 20px; color: ' + fg + '; }\n' +
|
|
164
|
+
' .footer { margin-top: 18px; font-size: 12px; color: ' + sub + '; }\n' +
|
|
165
|
+
'</style></head>\n<body><div id="container"><div class="card">\n' +
|
|
166
|
+
coverHtml +
|
|
167
|
+
'\n <div class="body">\n' +
|
|
168
|
+
' <div class="route">' + escapeHtml(route) + '</div>\n' +
|
|
169
|
+
' <div class="title">' + escapeHtml(card.title || '解析结果') + '</div>\n' +
|
|
170
|
+
(card.author
|
|
171
|
+
? ' <div class="author">' + (card.avatar ? '<img src="' + escapeHtml(card.avatar) + '" />' : '') + '<span>' + escapeHtml(card.author) + '</span></div>\n'
|
|
172
|
+
: '') +
|
|
173
|
+
(card.description ? ' <div class="desc">' + escapeHtml(card.description.slice(0, 600)) + '</div>\n' : '') +
|
|
174
|
+
(statsHtml ? ' ' + statsHtml + '\n' : '') +
|
|
175
|
+
' <div class="footer">koishi-plugin-kkk v' + escapeHtml(utils_1.Root.pluginVersion) + ' · 模板渲染未启用,当前为内置通用卡片</div>\n' +
|
|
176
|
+
' </div>\n</div></div></body></html>';
|
|
177
|
+
}
|
|
178
|
+
/* ------------------------------------------------------------------ *
|
|
179
|
+
* 模板 SSR
|
|
180
|
+
* ------------------------------------------------------------------ */
|
|
181
|
+
/**
|
|
182
|
+
* 页脚那个「框架 logo」。
|
|
183
|
+
*
|
|
184
|
+
* 上游 karin 是起 HTTP 服务把 `/image/frame-logo.png` 喂给浏览器的;
|
|
185
|
+
* Koishi 移植版渲染的是**本地 HTML 文件**,绝对路径 `/image/...` 会指向磁盘根目录,
|
|
186
|
+
* 于是卡片页脚只剩下一个裂图(alt 文字 `logo`)。这里直接内联成 data URI。
|
|
187
|
+
*/
|
|
188
|
+
let frameLogoCache = null;
|
|
189
|
+
function resolveFrameLogo() {
|
|
190
|
+
if (frameLogoCache !== null)
|
|
191
|
+
return frameLogoCache;
|
|
192
|
+
/**
|
|
193
|
+
* 页脚 logo:用 **Koishi 自己的图标**(@koishijs/plugin-console 里那张,
|
|
194
|
+
* 也就是控制台左上角那个),而不是上游 karin 的 frame-logo。
|
|
195
|
+
*/
|
|
196
|
+
const candidates = [
|
|
197
|
+
node_path_1.default.join(utils_1.Root.pluginPath, '..', '@koishijs', 'plugin-console', 'dist', 'logo.png'),
|
|
198
|
+
node_path_1.default.join(utils_1.Root.pluginPath, '..', '@koishijs', 'client', 'app', 'assets', 'logo.png'),
|
|
199
|
+
node_path_1.default.join(utils_1.Root.pluginPath, 'resources', 'image', 'frame-logo.png')
|
|
200
|
+
];
|
|
201
|
+
for (const filePath of candidates) {
|
|
202
|
+
try {
|
|
203
|
+
if (!node_fs_1.default.existsSync(filePath))
|
|
204
|
+
continue;
|
|
205
|
+
frameLogoCache = 'data:image/png;base64,' + node_fs_1.default.readFileSync(filePath).toString('base64');
|
|
206
|
+
return frameLogoCache;
|
|
207
|
+
}
|
|
208
|
+
catch { /* 换下一个 */ }
|
|
209
|
+
}
|
|
210
|
+
frameLogoCache = '';
|
|
211
|
+
return frameLogoCache;
|
|
212
|
+
}
|
|
213
|
+
/** 组装模板上下文(与上游 Render/index.ts 一致) */
|
|
214
|
+
function buildPosterContext(dark) {
|
|
215
|
+
return {
|
|
216
|
+
scale: Math.min(2, Math.max(0.5, Number(Config_1.Config.app?.renderScale ?? 100) / 100)),
|
|
217
|
+
theme: { mode: dark ? 'dark' : 'light' },
|
|
218
|
+
version: {
|
|
219
|
+
plugin: 'koishi-plugin',
|
|
220
|
+
pluginName: 'kkk',
|
|
221
|
+
pluginVersion: utils_1.Root.pluginVersion,
|
|
222
|
+
releaseType: /^\d+\.\d+\.\d+$/.test(utils_1.Root.pluginVersion) ? 'Stable' : 'Preview',
|
|
223
|
+
poweredBy: 'Koishi',
|
|
224
|
+
frameworkVersion: utils_1.Root.karinVersion,
|
|
225
|
+
// 页脚 logo(data URI,见 resolveFrameLogo)
|
|
226
|
+
frameLogo: resolveFrameLogo(),
|
|
227
|
+
hasUpdate: false
|
|
228
|
+
},
|
|
229
|
+
ambientCover: {
|
|
230
|
+
coverOpacity: Config_1.Config.app?.ambientCover?.coverOpacity,
|
|
231
|
+
overlayEdgeOpacity: Config_1.Config.app?.ambientCover?.overlayEdgeOpacity,
|
|
232
|
+
overlayMiddleOpacity: Config_1.Config.app?.ambientCover?.overlayMiddleOpacity
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** 二维码生成器只加载一次(ESM-only 包,只能在 SSR 前异步 import) */
|
|
237
|
+
let qrcodeReady = null;
|
|
238
|
+
async function ensureQrcodeGenerator() {
|
|
239
|
+
if (!qrcodeReady) {
|
|
240
|
+
qrcodeReady = (async () => {
|
|
241
|
+
const [qrcode, utils] = await Promise.all([
|
|
242
|
+
(0, esm_1.importEsm)('@ikenxuan/qrcode'),
|
|
243
|
+
Promise.resolve(require('../../../../ktr/utils/QRcode'))
|
|
244
|
+
]);
|
|
245
|
+
if (typeof qrcode.generateSync === 'function') {
|
|
246
|
+
utils.setQrcodeGenerator(qrcode.generateSync);
|
|
247
|
+
}
|
|
248
|
+
})().catch((error) => {
|
|
249
|
+
node_karin_1.logger.debug('[Render] 二维码生成器加载失败: ' + String(error?.message ?? error));
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return qrcodeReady;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 把 React 元素渲染成 HTML 字符串。
|
|
256
|
+
*
|
|
257
|
+
* 用流式 API 而不是同步的 renderToStaticMarkup:部分上游模板会 suspend
|
|
258
|
+
* (React 19 的 `use()` / 异步资源),同步渲染会直接抛
|
|
259
|
+
* "A component suspended while responding to synchronous input" 而整块回退成通用卡片。
|
|
260
|
+
* 流式渲染会等挂起的内容就绪(onAllReady)再产出完整 HTML。
|
|
261
|
+
*/
|
|
262
|
+
async function renderToHtml(element, timeoutMs = 15000) {
|
|
263
|
+
return await new Promise((resolve, reject) => {
|
|
264
|
+
let html = '';
|
|
265
|
+
const stream = new node_stream_1.PassThrough();
|
|
266
|
+
stream.on('data', (chunk) => { html += chunk.toString(); });
|
|
267
|
+
stream.on('end', () => resolve(html));
|
|
268
|
+
stream.on('error', reject);
|
|
269
|
+
let settled = false;
|
|
270
|
+
const fail = (error) => {
|
|
271
|
+
if (settled)
|
|
272
|
+
return;
|
|
273
|
+
settled = true;
|
|
274
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
275
|
+
};
|
|
276
|
+
const { pipe, abort } = (0, server_1.renderToPipeableStream)(element, {
|
|
277
|
+
onError: (error) => node_karin_1.logger.debug('[Render] SSR 渲染期错误: ' + String(error?.message ?? error)),
|
|
278
|
+
onShellError: fail,
|
|
279
|
+
onAllReady: () => {
|
|
280
|
+
if (settled)
|
|
281
|
+
return;
|
|
282
|
+
settled = true;
|
|
283
|
+
pipe(stream);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
if (settled)
|
|
288
|
+
return;
|
|
289
|
+
abort();
|
|
290
|
+
fail(new Error('SSR 渲染超时'));
|
|
291
|
+
}, timeoutMs).unref?.();
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
/** 用上游模板做 SSR;路由不支持或失败时返回 null */
|
|
295
|
+
async function renderTemplateHtml(route, data, dark) {
|
|
296
|
+
await ensureQrcodeGenerator();
|
|
297
|
+
let template;
|
|
298
|
+
try {
|
|
299
|
+
template = await (0, registry_1.loadTemplate)(route);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
node_karin_1.logger.debug('[Render] 模板 ' + route + ' 加载失败(可能缺少依赖): ' + String(error?.message ?? error));
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
if (!template?.component)
|
|
306
|
+
return null;
|
|
307
|
+
if (typeof template.validate === 'function' && !template.validate(data)) {
|
|
308
|
+
node_karin_1.logger.debug('[Render] 模板 ' + route + ' 的数据校验未通过,回退通用卡片');
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const ctx = buildPosterContext(dark);
|
|
313
|
+
const element = react_1.default.createElement(template.component, { data, ctx });
|
|
314
|
+
const body = await renderToHtml(react_1.default.createElement('div', { id: 'container' }, element));
|
|
315
|
+
const css = loadTemplateCss();
|
|
316
|
+
const themeClass = dark ? 'dark' : '';
|
|
317
|
+
/**
|
|
318
|
+
* 截图专用的覆盖样式:
|
|
319
|
+
* - 页面不要外边距/内边距,也不要白色背景(否则卡片圆角外面会透出白边、底部会多一条空白);
|
|
320
|
+
* - 卡片**不要圆角** —— 圆角会让四个角露出页面底色,看起来就是卡片缺了一角。
|
|
321
|
+
* 只压掉最外层的圆角,内部小元素(头像、标签)保持原样。
|
|
322
|
+
*/
|
|
323
|
+
const shotCss = 'html,body{margin:0!important;padding:0!important;background:transparent!important;overflow:hidden!important}'
|
|
324
|
+
+ '#container{margin:0!important;padding:0!important;border-radius:0!important}'
|
|
325
|
+
+ '#container>div,#container>div>div{border-radius:0!important}'
|
|
326
|
+
+ 'body{display:inline-block}';
|
|
327
|
+
return '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><style>' + css + shotCss + '</style></head>'
|
|
328
|
+
+ '<body class="' + themeClass + '">' + body + '</body></html>';
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
node_karin_1.logger.warn('[Render] 模板 ' + route + ' SSR 失败,回退通用卡片: ' + String(error?.message ?? error));
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/* ------------------------------------------------------------------ *
|
|
336
|
+
* 截图
|
|
337
|
+
* ------------------------------------------------------------------ */
|
|
338
|
+
/**
|
|
339
|
+
* 用 koishi-plugin-puppeteer 对一个 HTML 文件截图,返回 base64。
|
|
340
|
+
*
|
|
341
|
+
* `scale` 会作为 deviceScaleFactor(配置项 `app.renderScale`,100 = 1x,200 = 2x):
|
|
342
|
+
* 不设的话高分辨率卡片会被截成 1x,群里看着又小又糊;顺带把视口撑到整张卡片大小,
|
|
343
|
+
* 否则比视口高的卡片会被裁掉一截(表现就是「截图只有一部分」)。
|
|
344
|
+
*/
|
|
345
|
+
async function screenshot(htmlPath, selector, timeout, scale = 1, format = 'jpeg') {
|
|
346
|
+
const puppeteer = (0, node_karin_1.getKoishiContext)()?.puppeteer;
|
|
347
|
+
if (!puppeteer)
|
|
348
|
+
throw new Error('未安装 koishi-plugin-puppeteer,无法渲染图片');
|
|
349
|
+
// 卡片是给手机看的:**低于 2x 会明显发虚**,所以下限锁 2(上限 3)。
|
|
350
|
+
// 想更大更清晰就调 `app.renderScale`(100 → 2x,150 → 3x),配 100 时保持 2x 不出错。
|
|
351
|
+
const requested = Number.isFinite(scale) && scale > 0 ? scale * 2 : 2;
|
|
352
|
+
let deviceScaleFactor = Math.min(3, Math.max(2, requested));
|
|
353
|
+
// 安全阀:超大页面降一档缩放,避免单次渲染吃掉几 GB 内存(曾经把实例 OOM 崩掉)
|
|
354
|
+
try {
|
|
355
|
+
if (node_fs_1.default.statSync(htmlPath).size > 6 * 1024 * 1024)
|
|
356
|
+
deviceScaleFactor = Math.max(1, deviceScaleFactor - 1);
|
|
357
|
+
}
|
|
358
|
+
catch { /* 忽略 */ }
|
|
359
|
+
/**
|
|
360
|
+
* 视口用卡片的**设计宽度**(模板都按 1440 排版),高度按内容量一次。
|
|
361
|
+
*
|
|
362
|
+
* 之前试过「量出内容自然尺寸再把视口撑到那么大」:Chrome 会为此分配超大帧缓冲,
|
|
363
|
+
* 单次渲染把 node 堆吃到 3GB+ 并直接 OOM 崩掉实例。现在宽度固定、高度只增不减,
|
|
364
|
+
* 并且不再用 `captureBeyondViewport`(它在大页面上内存表现同样很差)。
|
|
365
|
+
*/
|
|
366
|
+
const DESIGN_WIDTH = 1440;
|
|
367
|
+
/** 在给定页面上完成「导航 → 量高度 → 截图」,返回 base64 */
|
|
368
|
+
const capture = async (page) => {
|
|
369
|
+
if (page.setViewport) {
|
|
370
|
+
// 宽度就用卡片设计宽度:多给 24px 会多出一条白边(用户看到的「右边超出去一点」)
|
|
371
|
+
await page.setViewport({ width: DESIGN_WIDTH, height: 900, deviceScaleFactor });
|
|
372
|
+
}
|
|
373
|
+
await page.goto((0, node_url_1.pathToFileURL)(htmlPath).href, { waitUntil: 'networkidle0', timeout });
|
|
374
|
+
const metrics = await page.evaluate((sel) => {
|
|
375
|
+
const node = document.querySelector(sel) || document.body;
|
|
376
|
+
const rect = node.getBoundingClientRect ? node.getBoundingClientRect() : null;
|
|
377
|
+
return {
|
|
378
|
+
// 用**元素自己的盒子**而不是整页 scrollHeight:整页量出来的高度会带上 body 边距,
|
|
379
|
+
// 截图底部就多一条空白
|
|
380
|
+
width: Math.ceil(rect?.width || node.scrollWidth || document.body.scrollWidth || 0),
|
|
381
|
+
height: Math.ceil(rect?.height || node.scrollHeight || document.body.scrollHeight || 0)
|
|
382
|
+
};
|
|
383
|
+
}, selector).catch(() => ({ width: 0, height: 0 }));
|
|
384
|
+
if (page.setViewport) {
|
|
385
|
+
/**
|
|
386
|
+
* 宽度按**内容实测**来:正常模板是 1440 宽,而兜底卡片只有 720 ——
|
|
387
|
+
* 固定 1440 会让兜底卡片右边留一大片白。这里两者都能贴合,
|
|
388
|
+
* 同时高度按内容撑够,避免长卡片被裁。
|
|
389
|
+
*/
|
|
390
|
+
const width = Math.min(Math.max(metrics.width || DESIGN_WIDTH, 320), 1600);
|
|
391
|
+
/**
|
|
392
|
+
* 高度按卡片实际盒子取整(不留容差)。
|
|
393
|
+
* 另外把 **页面底色设成卡片自己的底色** —— 视口哪怕只比卡片高一两个像素,
|
|
394
|
+
* JPEG 里那一条也会是白色页面底,看起来就是「下面有个小白边」。
|
|
395
|
+
*/
|
|
396
|
+
const height = Math.min(Math.max(Math.round(metrics.height), 200), 20000);
|
|
397
|
+
await page.setViewport({ width, height, deviceScaleFactor });
|
|
398
|
+
await page.evaluate((sel) => {
|
|
399
|
+
try {
|
|
400
|
+
const node = document.querySelector(sel);
|
|
401
|
+
if (!node)
|
|
402
|
+
return;
|
|
403
|
+
const card = node.firstElementChild || node;
|
|
404
|
+
const bg = getComputedStyle(card).backgroundColor;
|
|
405
|
+
if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent') {
|
|
406
|
+
document.documentElement.style.background = bg;
|
|
407
|
+
document.body.style.background = bg;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch { /* 取不到就算了 */ }
|
|
411
|
+
}, selector).catch(() => undefined);
|
|
412
|
+
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* 用「卡片元素自己的范围」裁切,而不是整屏截图。
|
|
416
|
+
*
|
|
417
|
+
* 视口宽度比卡片设计宽度多了 24px(给滚动条留余量),整屏截图会在右边多出一条白边,
|
|
418
|
+
* 用户看到的就是「卡片右边超出了/有白边」。clip 只取 #container 的盒子,正好贴合卡片。
|
|
419
|
+
*/
|
|
420
|
+
const handle = (await page.$(selector)) ?? (await page.$('body'));
|
|
421
|
+
const clip = handle ? await handle.boundingBox() : null;
|
|
422
|
+
/**
|
|
423
|
+
* 默认输出 JPEG:2x 的卡片 PNG 动辄十几 MB(带照片背景时尤其夸张),
|
|
424
|
+
* 超过 QQ 的图片上传限制就会被**当成文件**发出去(用户看到的是「[文件]」而不是图片)。
|
|
425
|
+
* 二维码那类需要像素级清晰的场景仍用 PNG(见 Render 里的判断)。
|
|
426
|
+
*/
|
|
427
|
+
const buffer = await page.screenshot({
|
|
428
|
+
omitBackground: format === 'png',
|
|
429
|
+
type: format,
|
|
430
|
+
quality: format === 'jpeg' ? 92 : undefined
|
|
431
|
+
});
|
|
432
|
+
return buffer.toString('base64');
|
|
433
|
+
};
|
|
434
|
+
/**
|
|
435
|
+
* 自己开一个页面截图,**不要走插件的 render() 包装**。
|
|
436
|
+
*
|
|
437
|
+
* 实测:1440x4895 的运行环境诊断卡片,走 `puppeteer.render()` 时仅导航一步
|
|
438
|
+
* 就让 node 堆从 206MB 涨到 1407MB(首次渲染累计 +3GB,直接把宿主 OOM 崩掉);
|
|
439
|
+
* 自己 newPage + goto + 截图只涨几十 MB。所以这里优先直连浏览器,用完关掉页面。
|
|
440
|
+
*/
|
|
441
|
+
const newPage = async () => {
|
|
442
|
+
try {
|
|
443
|
+
if (typeof puppeteer.page === 'function')
|
|
444
|
+
return await puppeteer.page();
|
|
445
|
+
if (puppeteer.browser && typeof puppeteer.browser.newPage === 'function')
|
|
446
|
+
return await puppeteer.browser.newPage();
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
node_karin_1.logger.debug('[Render] 直连浏览器失败,退回插件 render(): ' + (error?.message ?? error));
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
};
|
|
453
|
+
const page = await newPage();
|
|
454
|
+
if (page) {
|
|
455
|
+
try {
|
|
456
|
+
return await capture(page);
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
try {
|
|
460
|
+
await page.close();
|
|
461
|
+
}
|
|
462
|
+
catch { /* 页面可能已被插件关掉,忽略 */ }
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
node_karin_1.logger.debug('[Render] 插件未暴露 page/browser,退回 puppeteer.render()');
|
|
466
|
+
return await puppeteer.render('', async (page) => capture(page));
|
|
467
|
+
}
|
|
468
|
+
const getImageMetadata = (buffer) => {
|
|
469
|
+
if (buffer.length >= 24 && buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
470
|
+
return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
|
|
471
|
+
}
|
|
472
|
+
// JPEG:QQ 的 markdown 图片要写死「#宽px #高px」,所以必须能读出来
|
|
473
|
+
if (buffer.length > 4 && buffer[0] === 0xff && buffer[1] === 0xd8) {
|
|
474
|
+
let offset = 2;
|
|
475
|
+
while (offset + 9 < buffer.length) {
|
|
476
|
+
if (buffer[offset] !== 0xff) {
|
|
477
|
+
offset++;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const marker = buffer[offset + 1];
|
|
481
|
+
// SOF0..SOF15(跳过 DHT/DAC 等非 SOF 标记)
|
|
482
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
483
|
+
return { height: buffer.readUInt16BE(offset + 5), width: buffer.readUInt16BE(offset + 7) };
|
|
484
|
+
}
|
|
485
|
+
const length = buffer.readUInt16BE(offset + 2);
|
|
486
|
+
if (length <= 0)
|
|
487
|
+
break;
|
|
488
|
+
offset += 2 + length;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return {};
|
|
492
|
+
};
|
|
493
|
+
exports.getImageMetadata = getImageMetadata;
|
|
494
|
+
/**
|
|
495
|
+
* 渲染函数(上游签名):\`Render(event, '平台/模板', data)\` → 图片消息段数组。
|
|
496
|
+
*/
|
|
497
|
+
/**
|
|
498
|
+
* 把模板里的第三方图床代理换回原图地址。
|
|
499
|
+
*
|
|
500
|
+
* 上游模板给头像/装扮这类图套了一层 images.weserv.nl/?url=<encoded>(本意是绕开 B站的防盗链),
|
|
501
|
+
* 但那个代理在有些网络下**根本连不上** —— 表现就是卡片里 UP 主头像是个裂图。
|
|
502
|
+
* 实测 B站原图直连(i0/i2.hdslb.com)不带 Referer 也是 200,所以这里直接还原成原地址。
|
|
503
|
+
*/
|
|
504
|
+
/**
|
|
505
|
+
* 把模板里 `src="/image/xxx"` 这种**本地绝对路径**的图片内联成 data URI。
|
|
506
|
+
*
|
|
507
|
+
* 上游是 karin 的 HTTP 服务在提供 `/image/*`(插件 resources/image 目录);Koishi 移植版渲染的是
|
|
508
|
+
* 本地 HTML 文件,绝对路径会指向磁盘根目录 → 裂图。评论卡片里的 B站 logo、大会员标记、等级图标
|
|
509
|
+
* 就是这么丢的(页脚 logo 之前也踩过同一个坑)。
|
|
510
|
+
*/
|
|
511
|
+
function inlineLocalImages(html) {
|
|
512
|
+
const imageRoot = node_path_1.default.join(utils_1.Root.pluginPath, 'resources');
|
|
513
|
+
const mimeOf = (file) => {
|
|
514
|
+
const ext = node_path_1.default.extname(file).toLowerCase();
|
|
515
|
+
if (ext === '.svg')
|
|
516
|
+
return 'image/svg+xml';
|
|
517
|
+
if (ext === '.jpg' || ext === '.jpeg')
|
|
518
|
+
return 'image/jpeg';
|
|
519
|
+
if (ext === '.webp')
|
|
520
|
+
return 'image/webp';
|
|
521
|
+
if (ext === '.gif')
|
|
522
|
+
return 'image/gif';
|
|
523
|
+
return 'image/png';
|
|
524
|
+
};
|
|
525
|
+
return html.replace(/(src=")(\/image\/[^"]+)(")/g, (all, head, url, tail) => {
|
|
526
|
+
try {
|
|
527
|
+
const relative = decodeURIComponent(url.replace(/^\/+/, ''));
|
|
528
|
+
const full = node_path_1.default.join(imageRoot, relative);
|
|
529
|
+
if (!full.startsWith(node_path_1.default.resolve(imageRoot)))
|
|
530
|
+
return all;
|
|
531
|
+
if (!node_fs_1.default.existsSync(full))
|
|
532
|
+
return all;
|
|
533
|
+
const buffer = node_fs_1.default.readFileSync(full);
|
|
534
|
+
const mime = mimeOf(full);
|
|
535
|
+
// svg 是文本,用 utf8 base64 也一样能显示,这里统一 base64 省事
|
|
536
|
+
return head + 'data:' + mime + ';base64,' + buffer.toString('base64') + tail;
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
return all;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
function unwrapImageProxy(html) {
|
|
544
|
+
return html.replace(/https:\/\/images\.weserv\.nl\/\?url=([^"'&\s]+)/gi, (all, encoded) => {
|
|
545
|
+
try {
|
|
546
|
+
const decoded = decodeURIComponent(encoded);
|
|
547
|
+
return /^https?:\/\//i.test(decoded) ? decoded : all;
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
return all;
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
const Render = async (_event, route, data) => {
|
|
555
|
+
const dark = await (0, coverTheme_1.resolveUseDarkTheme)(route, data).catch(() => false);
|
|
556
|
+
const scale = Math.min(2, Math.max(0.5, Number(Config_1.Config.app?.renderScale ?? 100) / 100));
|
|
557
|
+
let html = await renderTemplateHtml(route, data, dark);
|
|
558
|
+
if (!html) {
|
|
559
|
+
html = buildFallbackHtml(route, data, dark, scale);
|
|
560
|
+
}
|
|
561
|
+
html = inlineLocalImages(unwrapImageProxy(html));
|
|
562
|
+
const htmlPath = node_path_1.default.resolve(root_1.karinPathHtml, utils_1.Root.pluginName, route.replace(/[\\/]/g, '_') + '.html');
|
|
563
|
+
try {
|
|
564
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(htmlPath), { recursive: true });
|
|
565
|
+
node_fs_1.default.writeFileSync(htmlPath, html, 'utf8');
|
|
566
|
+
// 二维码:必须像素级清晰(JPEG 压缩会影响扫码),继续用 PNG;其它卡片用 JPEG 压体积
|
|
567
|
+
const isQrCode = /qrcode/i.test(route);
|
|
568
|
+
const format = isQrCode ? 'png' : 'jpeg';
|
|
569
|
+
const base64 = await screenshot(htmlPath, '#container', Number(Config_1.Config.app?.RenderWaitTime ?? 10) * 1000, scale, format);
|
|
570
|
+
const buffer = Buffer.from(base64, 'base64');
|
|
571
|
+
const meta = (0, exports.getImageMetadata)(buffer);
|
|
572
|
+
node_karin_1.logger.debug('[Render] ' + route + ' 渲染完成 ' + format + ' ' + (meta.width ?? '?') + 'x' + (meta.height ?? '?') + ' ' + Math.round(buffer.length / 1024) + 'KB');
|
|
573
|
+
// 用带 mime 的 data URL,适配器才知道按 JPEG 上传(base64:// 会被当成 PNG)
|
|
574
|
+
return [node_karin_1.segment.image('data:image/' + format + ';base64,' + base64)];
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
node_karin_1.logger.warn('[Render] ' + route + ' 渲染失败:' + (error?.message ?? error));
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
exports.Render = Render;
|
|
582
|
+
exports.default = exports.Render;
|