| .husky | ||
| ci | ||
| docs | ||
| k8s/prod | ||
| scripts | ||
| sql | ||
| src | ||
| test | ||
| .dockerignore | ||
| .env.example | ||
| .eslintrc.js | ||
| .gitattributes | ||
| .gitignore | ||
| .prettierrc | ||
| API.md | ||
| dockerfile | ||
| dockerfile.gateway | ||
| Jenkinsfile | ||
| LICENSE | ||
| nest-cli.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
KT Template Online API
kt-template-online-api 是 KT 工作区的 NestJS 后端服务,承接 Admin 后台、博客内容、组件模板、MinIO 文件、系统日志、QQBot/NapCat 和游戏查询插件能力。
技术栈
- Node.js 22 / TypeScript 5.9
- NestJS 11 / Express 5
- TypeORM 0.3 / MySQL
- Swagger / Knife4j
- nestjs-pino / pino-loki / Loki
- MinIO
- MQTT / OneBot v11 reverse WebSocket / NapCat
- skia-canvas / Chart.js
- pnpm 9
功能模块
| 模块 | 说明 |
|---|---|
admin |
Vben Admin 认证、用户、菜单、角色、部门、时区、字典、组件模板、系统日志、环境总览面板和网络端口映射管理 |
blog |
本地博客文章、分类、标签、Argon 主题配置和 WordPress 导入 |
wordpress |
WordPress REST 代理、登录态透传、文章/分类/标签/主题配置 |
qqbot |
QQBot 账号、NapCat 扫码登录、运行态 Profile、OneBot 反向 WS、在线命令、规则、权限、系统消息源/订阅/模板/账号绑定、耐久投递、发送/接收日志和插件平台 |
modules/qqbot/plugin-platform |
QQBot 插件 manifest 校验、版本安装、运行事件、定时任务、受控 SDK 和 CLI 脚手架 |
qqbot/plugins/bangdream |
BanG Dream 查曲、查卡、查活动、试炼、玩家、卡池、抽卡模拟、档线、谱面出图 |
qqbot/plugins/bilibili-card |
解析 QQ/NapCat Bilibili 卡片和短链,按账号事件绑定回复封面图和视频文字摘要 |
qqbot/plugins/ff14-market |
XIVAPI + Universalis 物品解析和 FF14 市场查价 |
qqbot/plugins/fflogs |
FFLogs v2 GraphQL 角色排名和指定高难最近记录查询 |
minio |
Bucket 检查、上传、列表、临时 URL、代理下载、删除,以及 Blog Live2D 运行包受控读取入口 |
common |
响应封装、异常过滤、请求日志、日期格式化、字典解码、Snowflake、工具服务 |
目录结构
src/
admin/ Admin 后台接口和实体
blog/ 本地博客内容与主题配置
common/ 全局装饰器、过滤器、拦截器、logger、工具和类型
minio/ MinIO 文件服务
modules/ 第三期重构后的业务边界模块
qqbot/ QQBot 运行态、管理接口和插件生态
wordpress/ WordPress REST 代理
app.module.ts
main.ts
test/ Jest 单元测试,统一放在 test 下
sql/ 初始化、菜单、迁移和修复 SQL
scripts/ smoke、husky 快速检查等脚本
k8s/ K8s 生产部署清单
ci/ Jenkins Agent/Docker 辅助文件
环境变量
项目按 NODE_ENV 读取 .env.${NODE_ENV},未指定时默认 .env.development。仓库只跟踪 .env.example;真实 .env.development、.env.production、数据库密码、Token、OAuth secret 和 SSH key 不提交。
主要配置分组:
| 分组 | 变量 |
|---|---|
| MySQL | DB_HOST、DB_PORT、DB_USERNAME、DB_PASSWORD、DB_DATABASE、DB_SYNC |
| MinIO | MINIO_ENDPOINT、MINIO_PORT、MINIO_ACCESS_KEY、MINIO_SECRET_KEY、MINIO_BUCKET、BLOG_LIVE2D_ALLOWED_ORIGINS、BLOG_LIVE2D_BUCKET、BLOG_LIVE2D_ROOT_PREFIX、BLOG_LIVE2D_PREFIX |
| Admin | ADMIN_TOKEN_SECRET、ADMIN_COOKIE_SECURE、SNOWFLAKE_WORKER_ID、SNOWFLAKE_DATACENTER_ID |
| WordPress | WORDPRESS_BASE_URL、WORDPRESS_HOST_HEADER、WORDPRESS_ADMIN_USERNAME、WORDPRESS_ADMIN_PASSWORD、WORDPRESS_*_TIMEOUT_MS |
| Logging/Loki | LOG_LEVEL、LOG_APP_NAME、LOKI_URL、LOKI_QUERY_HOST、LOKI_* |
| QQBot/NapCat | QQBOT_ENABLED、QQBOT_ACCOUNT_SECRET_KEY、QQBOT_REVERSE_WS_*、QQBOT_SEND_*、QQBOT_PLUGIN_QUEUE_REDIS_*、QQBOT_PLUGIN_TASK_QUEUE_REDIS_*、QQBOT_PLUGIN_QUEUE_WAIT_TIMEOUT_MS、QQBOT_COMMAND_MIN_COOLDOWN_MS、QQBOT_RULE_MIN_COOLDOWN_MS、QQBOT_REPEATER_*、NAPCAT_*、QQBOT_NAPCAT_*、MQTT_* |
| Environment Dashboard | ENV_DASHBOARD_CACHE_TTL_MS、ENV_DASHBOARD_SIGNAL_TIMEOUT_MS、ENV_DASHBOARD_EVENT_BUS、ENV_DASHBOARD_MQTT_*、ENV_DASHBOARD_SSE_*、ENV_DASHBOARD_JENKINS_*、ENV_DASHBOARD_K8S_*、ENV_DASHBOARD_TENCENT_*、ENV_DASHBOARD_CADDY_*、ENV_DASHBOARD_R4SE_* |
| Network Management | NETWORK_AGENT_ID、NETWORK_AGENT_TARGET_IPV4、NETWORK_AGENT_MQTT_URL、NETWORK_AGENT_MQTT_CLIENT_ID、NETWORK_AGENT_MQTT_USERNAME、NETWORK_AGENT_MQTT_PASSWORD、NETWORK_AGENT_MQTT_RETRY_MS、NETWORK_MANAGEMENT_SSE_HEARTBEAT_MS、NETWORK_MANAGEMENT_SSE_REPLAY_LIMIT、NETWORK_DDNS_DNSPOD_*、NETWORK_DDNS_RECONCILE_INTERVAL_MS、NETWORK_DDNS_AGENT_IPV6_MAX_AGE_MS |
| BangDream | BANGDREAM_TSUGU_MAIN_SERVER、BANGDREAM_TSUGU_DISPLAYED_SERVERS、BANGDREAM_TSUGU_CACHE_ROOT |
| FF14 Market | FF14_XIVAPI_BASE_URL、FF14_UNIVERSALIS_BASE_URL、FF14_MARKET_CACHE_TTL_MS |
| FFLogs | FFLOGS_BASE_URL、FFLOGS_GRAPHQL_URL、FFLOGS_TOKEN_URL、FFLOGS_CLIENT_ID、FFLOGS_CLIENT_SECRET |
DB_SYNC=true 只适合本地开发或明确允许自动同步表结构的环境;生产应关闭并使用 SQL/迁移脚本。
Blog Live2D 运行包存放在 MinIO,公开读取入口为 /blog/live2d/:character/catalog.json 和 /blog/live2d/:character/:family/*assetPath。character 只允许 pio、tia,family 只允许 moc 和 moc3:moc/ 提供旧 WordPress 同款 Cubism2 index.json、model.moc、.mtn 动作和贴图,moc3/ 保留当前重建 Cubism3 包(Tia 当前只发布 moc/,不会在 catalog 声明不存在的 MOC3)。BLOG_LIVE2D_ALLOWED_ORIGINS 是允许加载 Live2D runtime 的 Blog 域名白名单;BLOG_LIVE2D_ROOT_PREFIX 指向角色根目录(默认 blog/live2d),旧 BLOG_LIVE2D_PREFIX=blog/live2d/pio 会自动派生到同一根前缀以兼容现有环境;未通过 Referer/Origin 白名单的请求会在读取 MinIO 前拒绝。
QQBot 插件 worker 使用 BullMQ 队列串行执行同一插件安装实例的请求。K8s 生产清单包含内部服务 kt-qqbot-plugin-redis,生产 env 可将 QQBOT_PLUGIN_QUEUE_REDIS_HOST 配为该服务名。QQBOT_PLUGIN_QUEUE_WAIT_TIMEOUT_MS 控制排队等待窗口,插件 operation.timeoutMs 仍表示单次执行预算。
QQBot 插件定时任务由 manifest 的 tasks 声明,平台持久化到 qqbot_plugin_task / qqbot_plugin_task_run,通过 BullMQ Job Scheduler 调度并经插件 worker 的 executeTask 边界执行。sql/qqbot-init.sql 可为既有环境增量创建任务表和 Admin 菜单。Admin 页面路径为 /qqbot/plugin-task。定时任务队列可用 QQBOT_PLUGIN_TASK_QUEUE_REDIS_* 单独配置;留空时复用插件 worker 队列的 Redis 连接。BangDream Bestdori 主数据缓存使用 BANGDREAM_TSUGU_CACHE_ROOT,生产清单挂载到容器内 /data/qqbot/plugins/bangdream/cache,对应 k3d 节点可写 hostPath /var/lib/rancher/k3s/kt-template-online-api/qqbot-plugins。
Admin 环境总览面板使用 ENV_DASHBOARD_* 只读配置聚合 local-dev、NAS 线上、腾讯云和 r4se 状态。ENV_DASHBOARD_ADMIN_LOCAL_URL / ENV_DASHBOARD_ADMIN_PUBLIC_URL 只用于展示 Admin 本机与线上入口证据。HTTP 快照提供当前拓扑,后端 local/MQTT 事件总线通过 SSE 推送增量事件给 Admin;前端不直连 MQTT,也不轮询刷新。Jenkins、K8s、Tencent Cloud、Caddy、WireGuard、Mihomo/OpenClash 未配置时会显示 unwired 证据,不能渲染成健康假象;第一版不暴露重启、部署、迁移、容器重建、插件启停或代理切换等写操作。
System 网络管理以 MySQL 中的 TCP/UDP 端口转发期望状态为唯一事实源。super 通过统一 CRUD 和 UDP Keeper 动作修改期望状态;API 在事务内单调提升 revision,提交后使用固定 kt/network/v1/agents/{agentId} MQTT topic、QoS 1 retained 完整快照通知 NAS kt-network-agent,自身不登录路由器、不接收路由器密码,也不执行 raw socket。Agent 失联或 MQTT 暂不可用时合法请求仍保存为 pending,恢复后按 revision 自动收敛;消费端发生瞬时数据库错误或 SUBACK 失败时主动重连并依赖 broker 重投,非法负载则确认后丢弃,避免 poison message 阻塞。API 仅在入站 MQTT 事务提交且语义状态实际变化后通过 /system/network/events/stream 向 Admin 发布 SSE;QoS 1 幂等重投、status 心跳时间推进和 reported 租约时间续期仍写入数据库,但不触发页面刷新。公网 IP/端口、Keeper/同步/错误/删除状态或 Agent 在线会话变化仍发布事件。SSE 心跳复用最近一次真实状态事件游标,尚无状态事件时显式发送空游标,避免 Nest 自动生成的 ID 污染重放位置;有限重放缺口只要求一次 HTTP 快照。TCP 目前仅保存 CRUD 期望,真实路由器写入仍受设备协议证据门禁并回报 tcp_router_write_gated;只有外部端口等于内部端口的 UDP 记录允许启停 Keeper 和立即探测。当前公网端点受 currentValidUntil 租约约束,过期后列表隐藏当前值但保留最近观测与历史。生产发布同时把完整 NETWORK_AGENT_* 连接配置作为 Jenkins 私有 env 和 /health/runtime 必需项,任一项缺失时拒绝发布或报告运行态阻断,避免页面可见但 MQTT 控制链路未接线。
同一模块提供腾讯云云解析 DNS 的双栈自动 DDNS。A 记录只从合格 UDP Keeper 的有效公网 IPv4 取值,AAAA 记录只从在线 Agent 最近上报的全局 IPv6 取值;DNS 值始终不包含端口。协调器只修改已存在、已启用、默认线路且唯一的 A/AAAA 记录,保留 RecordId、线路和 TTL,并在写入后回读确认。删除 Admin 绑定只停止本地自动更新,不删除云端 DNS 记录。凭据只从 API 私有运行环境的 NETWORK_DDNS_DNSPOD_SECRET_ID/SECRET_KEY 读取,不进入 Admin、数据库、MQTT、Agent、日志或 Git;DNSPOD 是腾讯云官方 SDK 的技术服务名。
QQBot 系统消息推送由全局消息订阅和模板、账号范围发布绑定及耐久事件/投递共同管理;订阅表单字段、候选集合和资源匹配由所选系统消息源 adapter 声明,核心模块不写死 STUN 的 portForwardId/ddnsRecordId。账号接口严格使用路由 selfId,不会回退到其他机器人。群聊和私聊目标分别使用 group / private,Snowflake、QQ 账号和目标 ID 在 HTTP 与数据库边界始终保持字符串。系统事件只能通过内部 Outbox stager 暂存,不提供 publish/event/worker HTTP 路由;管理响应仅返回字段白名单,不暴露账号凭据、Provider/OneBot/MQTT 运行对象、原始事件载荷或内部持久化键。
qqbot_message_event 同时承担事件 Outbox:扇出状态为 accepted、processing、retry、completed、failed;按目标冻结的 qqbot_message_delivery 状态为 waiting_ddns、pending、processing、retry、success、failed、superseded、cancelled。扇出和投递每次各领取最多 50 行,处理租约为 30 秒;进程启动后立即恢复,并每 5 秒扫描一次。waiting_ddns 每 60 秒持久化复检;临时错误从 10 秒开始指数退避,单次最长 15 分钟,并以事件发生后 24 小时为截止时间。
Network 端点历史事务提交后才调用 requestDrain();DDNS 只有在 optimistic synced / appliedAddress 状态成功持久化后才调用 notifyDdnsSynced() 提前唤醒相关等待任务,周期扫描仍负责恢复漏唤醒。每次正式发送都重新检查来源和配置,只使用投递冻结的准确 selfId,正文作为一个 OneBot text segment 下发。数据库唯一键避免重复事件和重复事件-目标任务,但 OneBot 超时后的重试具有至少一次语义:超时结果不明确时,收件端仍可能收到重复消息。
NapCat Runtime/Protocol Profile 已完成本地 API/Admin 实施,线上发布和账号闭环按 docs/plans/2026-06-18-qqbot-napcat-runtime-protocol-profile-implementation-plan.md 的 Task 10 执行。当前实现覆盖运行态/协议/会话行为/历史登录事件兼容表/风险模式表,真实物理设备风格 hostname/MAC,NapCat/OneBot 配置 hash,KT zh_CN.UTF-8 中国桌面派生镜像资产,只读 /qqbot/napcat/runtime/detail 证据接口,watchdog 离线巡检告警,以及 Admin 账号页“运行态”抽屉;不绕过 QQ/Tencent 验证码、不修改 QQ/NTQQ 签名协议、不启用 privileged/host network,也不做账号级每小时/每日累计发送预算。NapCat Chinese Desktop Runtime v20 使用 KT NapCatQQ fork 源码构建出的 NapCat.Shell artifact,并在 QQ KickedOffLine 后标记 native login service stale;API 在源 Docker 容器在线但 WebUI 明确 QQ 离线时会同容器调用 RestartNapCat 重启 NapCat worker,重建 QQCore login service 后再推进 quick/password/qrcode,不做 Docker 重建、补 env 或设备身份迁移,且同一个更新登录 session 只消费一次 worker restart 预算;v14 起还会对 QQ/NapCat/Xvfb 长期进程的 /proc/<pid>/mountinfo 做 PID 级遮蔽,防止 overlay、/vol1/docker、docker-init、/docker/containers、napcat-instances 等宿主路径泄露;v15 修复扫码成功时 QQLoginInfo 晚于登录态写入造成的 QQ 号回读空窗;v16 在 native reset 缺少 offline() 时改用 destroy() 硬重置半登录服务,并让镜像 verify 等待 mountinfo guard 收敛;v17/v18 增加 WebUI 鉴权的 /api/Debug/RuntimeViewProbe 同进程诊断并修正 native maps 截断导致的 hook 证据假阴性;v19 保留 WebUI RestartNapCat 重启 worker 时的 -q <uin> 快速登录参数,避免重启后退回无账号扫码;v20 保护 API 预写的 /app/napcat/config,避免上游首次解包 NapCat.Shell/* 覆盖 bypass.*=true 与 o3HookMode=0。镜像必须先用 scripts/napcat-desktop-cn-stage-build.mjs staged build context,生产 QQBOT_NAPCAT_IMAGE 应指向验证过的 kt-napcat-desktop-cn:desktop-cn-v20 digest。k8s/prod/api.yaml 保留 desktop-cn-v20 稳定默认值;Jenkins QQBOT_NAPCAT_IMAGE_OVERRIDE 和 QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE 仅在填写时通过 kubectl set env 推广已验证运行时镜像/profile,空值会继续使用 manifest/default env。回滚时重新运行 Jenkins 并填入上一版 digest/profile,或清空两个 override 后重新部署 manifest 默认值。
运行时发布时,API 仓库不提交 NapCat.Shell.zip;生产镜像必须从 staged context 构建,fork-artifact.json 必须带完整 marker metadata,包括 upstream release tag/commit、fork commit、base image digest、Jenkins URL 和 artifact hashes。release evidence 里的 NapCat base image 必须用 digest pin。API Jenkins 只消费人工确认后的运行时推广参数,不自动合并上游、不自动构建隐藏镜像,也不在 override 为空时覆盖 K8s manifest 中的默认 env。
node scripts/napcat-desktop-cn-stage-build.mjs \
--napcat-root /home/yemu2/KT/GitHub/NapCatQQ \
--upstream-release-tag v4.8.0 \
--upstream-release-commit 0000000000000000000000000000000000000000 \
--napcat-base-image-digest mlikiowa/napcat-docker@sha256:0000000000000000000000000000000000000000000000000000000000000000 \
--jenkins-build-url https://jenkins.kwitsukasa.top/job/KT-NapCatQQ-Runtime-Release/1/
NapCat WebUI Gateway 是独立运行的 NestJS 入口,生产镜像使用 dockerfile.gateway 打包 dist/apps/napcat-webui-gateway/main.js 并监听 48086。API 通过内部地址 NAPCAT_WEBUI_GATEWAY_INTERNAL_BASE_URL=http://kt-napcat-webui-gateway:48086 创建/续期/撤销 WebUI 会话,Admin 浏览器只访问公开前缀 NAPCAT_WEBUI_GATEWAY_PUBLIC_BASE_URL=/napcat-webui。Gateway 运行时需要 NAPCAT_WEBUI_GATEWAY_INTERNAL_SECRET、NAPCAT_WEBUI_GATEWAY_REDIS_HOST、NAPCAT_WEBUI_GATEWAY_REDIS_PORT、NAPCAT_WEBUI_GATEWAY_SESSION_TTL_MS、NAPCAT_WEBUI_GATEWAY_TICKET_TTL_MS、NAPCAT_WEBUI_GATEWAY_UPSTREAM_TIMEOUT_MS;生产 secret 由 Jenkins 从私有 .env.production 重建到 kt-template-online-api-env,不得写入 Git。验收命令:pnpm exec jest --runTestsByPath test/modules/qqbot/napcat-webui-gateway/gateway-deployment.spec.ts --runInBand、pnpm run typecheck、pnpm run build、test -f dist/apps/napcat-webui-gateway/main.js、git diff --check。安全边界:浏览器不得收到 WebUI token、Credential、上游 URL/端口、Docker 拓扑、Redis 地址或内部 secret。
启动
pnpm install
pnpm start:dev
服务固定监听 48085。
常用命令:
pnpm start
pnpm start:prod
pnpm run typecheck
pnpm run lint
pnpm test
pnpm run build
pnpm qqbot-plugin create <pluginKey>
pnpm qqbot-plugin validate <pluginDir>
pnpm qqbot-plugin pack <pluginDir>
pnpm qqbot-plugin install-local <packageFile>
Jest 只扫描 test/**/*.spec.ts。如果在 Windows 下指定测试文件,使用:
pnpm exec jest --runInBand --runTestsByPath test/path/to/file.spec.ts
接口文档
- Swagger 全量:
http://localhost:48085/api - OpenAPI JSON:
http://localhost:48085/api-json - 分组文档:
/api/admin、/api/qqbot、/api/wordpress、/api/basic - Knife4j:服务启动后同样使用上述 OpenAPI 服务列表
- 手工接口索引:API.md
业务接口统一返回 Vben 结构,文件下载/流式接口除外:
{
"code": 200,
"msg": "操作成功",
"data": {}
}
错误响应里的 err 必须是字符串,避免前端解析 JSON 对象时报错:
{
"code": 400,
"msg": "操作失败",
"err": "错误原因"
}
运行时健康检查
API 暴露 GET /health/runtime 作为本地 smoke、Jenkins/K8s 和 ktWorkflow 观测入口。该接口返回 plain JSON,不使用 Vben 响应包装,便于脚本直接读取。
返回内容包括:
status:live、ready、degraded或blocked。checks:进程存活和运行时配置检查状态。
该公开入口不返回数据库、WordPress、Loki、NapCat SSH 等运行拓扑配置快照;配置检查只暴露 key 级别、是否存在和缺失说明。blocked 表示关键配置缺失;degraded 表示可选运行时配置缺失,核心 API 仍可继续工作。本地未配置 Loki、WordPress、NapCat 等可选依赖时,健康状态可能保持 degraded。
核心规则
- 后台主键使用 Snowflake 数字 ID,数据库字段为
BIGINT,接口按字符串返回。 - 后端响应时间统一用
KtDateTime extends Date承接序列化语义;Entity 使用@KtDateTimeColumn(format)、@KtCreateDateColumn(format)、@KtUpdateDateColumn(format)在 TypeORM hydrate 边界转换,DTO/外部数据源使用@KtDateTimeField(format)+transformKtDateTimeFields()转换,默认格式为YYYY-MM-DD HH:mm:ss。vbenSuccess/ToolsService.res不做全量递归格式化。 - 字典维护在
admin_dict,Admin 字典管理按dictCode分组展示;可运营映射优先走字典或静态配置,不硬编码到业务函数。 - 全局
SaveBodyInterceptor会删除POST */save请求体里的id;需要保留时使用@SkipSaveBodyNormalize()。 - Admin、Component、Dict、MinIO、Blog 管理、WordPress 管理和 QQBot 管理接口默认走
JwtAuthGuard;公开接口用@Public()。 - WordPress 自动登录失败不会阻断 Admin 主登录,会通过菜单和权限码过滤不可用的 Blog 管理入口。
- 系统日志由 pino 输出,Loki 查询统一通过后端
/system/logs/*代理,前端不直连 Loki。 - 日志级站内信只承接运行期事件:接口 5xx、QQBot 下线 notice、NapCat 容器最新离线日志会自动聚合通知
super角色;服务端强制super访问,Admin 不再暴露人工新增/编辑入口;长路径接口错误会压缩dedupeKey/title到表字段长度内,避免通知入库失败。 - QQBot 扫码登录通过 SSE
/qqbot/account/scan/events暴露进度,耗时链路不应阻塞普通 HTTP 响应;新增账号扫码会先返回 pendingsessionId,后台再创建 NapCat 容器并生成二维码。CheckLoginStatus.isLogin=true只代表 NapCat 登录阳性,创建账号必须继续等GetQQLoginInfo返回uin/selfId后才绑定真实 QQ 号;短暂缺号时会话保持 pending 并显示正在读取 QQ 号,不能重建容器或猜号。 - QQBot 外发统一走发送排队:默认全局间隔
2500ms、同会话间隔8000ms、排队抖动0-800ms,超过QQBOT_SEND_MAX_QUEUE_WAIT_MS时拒绝本次发送,避免高频自动回复形成突发流量。 - QQBot 在线命令和自动回复规则都有运行时保底冷却:默认命令
5000ms、规则30000ms;即使数据库里旧数据冷却值更低,也按保底值判定,降低频繁触发风控的概率。 - QQBot 复读机默认阈值为 4,同一会话默认 10 分钟只复读一次,默认只复读 120 字以内普通文本,避免群聊重复内容导致机器人过于频繁地模拟真人发言。
- QQBot 插件平台统一使用
plugin.jsonmanifest 描述插件 key、版本、操作、事件、权限、运行预算和包入口;CLI 负责 create/validate/pack/install-local,后端只暴露受控 SDK 能力并通过插件维度记录安装、配置、账号绑定和运行事件。 - Bilibili Card 是事件型内置插件:
bilibili-card.message只在账号绑定后监听 QQ/NapCatshare/json/xml/lightapp卡片或文本里的 Bilibili 链接,b23.tv短链通过平台resolveRedirect受控 host 能力解析,视频信息从 Bilibilix/web-interface/view获取后回复首行封面图和文本摘要。 - QQBot 同一账号只允许一个有效 NapCat 主容器;绑定新容器时会释放旧绑定和不再共享的旧容器,机器人下线 notice、
isOnline:false和 NapCat 容器最新离线日志都会写入账号lastError,普通群成员 kick 不属于账号离线信号;写入last_error前按 500 字符截断,后续无错误的普通断连不能清空该原因;账号列表拆开展示 OneBot、容器、WebUI 和 QQ 登录态,心跳只代表 OneBot/容器通信,不能推导 QQ 登录态;近期连接只用于避免重连瞬间被旧缓存误伤,后续仍必须以 NapCat WebUI/日志检查判断 QQ 登录态;qqLoginMessage只展示 QQ 登录态消息,WebUI 配置或请求错误留在lastError。 - NapCat 托管容器必须显式配置
QQBOT_NAPCAT_IMAGE,不要依赖latest默认镜像;生产切换镜像前先 pin 明确版本或 digest 并单账号观察。desktop-cn-v20镜像从 KTNapCatQQfork 的 source-builtNapCat.Shell构建,不再在镜像内对上游 bundle 做字符串 patch,并修复非自动重试 QR failure 后下次 WebUI 登录动作不重置、QQCore 通过进程级 mountinfo 探针看到 Docker/宿主路径、扫码登录成功后 API 立即读不到 QQ 号、生产 native reset 缺少offline()时半登录态无法清理、runtime view native maps 取证假阴性、WebUIRestartNapCat重启 worker 丢失快速登录账号参数,以及首次解包覆盖 API 预写 NapCat config 导致 bypass 开关回落默认关闭的问题。踢下线后的半登录态不能只靠旧 native reset 兜底;源 Docker 容器在线时 API 会先同容器RestartNapCat重建 NapCat worker,再继续登录流程,同一个更新登录 session 不能反复重启 worker。 - NapCat 账号新增/编辑支持可选 QQ 登录密码:Admin 只提交 RSA-OAEP 加密后的
encryptedLoginPassword,后端解密后必须用显式配置的QQBOT_ACCOUNT_SECRET_KEY(或非默认ADMIN_TOKEN_SECRET)二次加密保存到qqbot_account.napcat_login_password_secret;空值、change-me和历史公开默认值会被拒绝;列表和详情不回显密码,日志会脱敏密码字段。 - NapCat 容器为已知
selfId创建/重建时会一次性注入ACCOUNT等必要 env;容器重启(崩溃/重启策略/宿主重启)可复用持久化会话,但硬踢登录已失效仍需人工登录。Admin「更新登录」不通过 Docker 重建、重启或补 env 刷新登录态:只要源容器在线,就保持同一 Docker 容器;若 WebUI 明确返回 QQ 离线,会先调用同容器/api/QQLogin/RestartNapCat重启 NapCat worker,随后通过 NapCat WebUISetQuickLogin -> PasswordLogin -> RefreshQRcode/GetQQLoginQrcode推进原弹窗流程;同一个更新登录 session 只允许一次 worker restart,后续状态轮询只能继续 WebUI 登录/二维码刷新,不能反复重启 worker。只有 Docker 容器离线或缺失时,容器准备阶段才创建/重建并一次性注入 env。快速登录失败后,如果账号保存了登录密码,后端使用解密密码计算 MD5 调/api/QQLogin/PasswordLogin,不会把密码写入运行态 env,也没有成功后的 env 清理步骤;密码登录按QQBOT_NAPCAT_PASSWORD_LOGIN_WAIT_MS/QQBOT_NAPCAT_LOGIN_POLL_INTERVAL_MS轮询结果。准备中的扫码会话会续期,重复调用更新登录会复用同一 pendingsessionId,不会再次启动 quick/password/二维码准备;但如果该 pending 会话是在账号维护登录密码前创建、且尚未进入密码/验证码/新设备上下文,后续更新登录必须退役这条无密码会话并新建 refresh session,以重新读取账号表中的最新密码。取消扫码会话会在接口返回前把持久化 session 标成error/cancelled并写完成时间,避免已取消二维码从 DB 恢复为 active pending。若 API Pod 在准备阶段重启,持久化的preparingRelogin超过QQBOT_NAPCAT_RELOGIN_PREPARING_STALE_MS后会自动恢复为普通状态检测。pending refresh 会话没有二维码、验证码或新设备挑战时,状态轮询会按NAPCAT_LOGIN_QR_AUTO_REFRESH_COOLDOWN_MS冷却在同一容器自动重试二维码刷新,避免 SSE 长时间停在生成中。验证码和新设备验证保持同一会话 pending:腾讯验证码结果ticket/randstr/sid通过/qqbot/account/scan/captcha/submit回交到同一容器的/api/QQLogin/CaptchaLogin;状态轮询遇到验证码文案但缺少 URL 时会先从当前容器日志恢复proofWaterUrl,没有 URL 也保持验证码处理中而不切到二维码兜底。扫码或密码链路拿到 NapCat 登录阳性但 QQ 号暂不可读时,会按NAPCAT_LOGIN_SELF_ID_WAIT_MS保持 pending,超过窗口才失败。密码登录仍失败、验证码未完成、离线、账号不匹配或缺少 QQ 号时,直接通过 WebUI 二维码接口进入扫码兜底,不 reset 登录态。Admin SSE 步骤顺序按实际路径为quick-login-*->password-login-*/password-login-captcha/ 新设备验证 ->qrcode/waiting-scan->login-success|login-failed;SSE 事件缓存因 Pod 重启丢失时,新订阅会收到当前会话快照。 - NapCat 设备身份按账号持久化到
napcat_device_identity:同一账号重建容器会复用数据目录、pc-<8hex>hostname、machine-id 和实体 OUI 风格 MAC,明确排除 Docker02:42、QEMU/KVM52:54:00、VMware、Hyper-V 等虚拟化前缀;新增账号首次扫码会先用预留容器 id 创建临时设备身份并应用到第一次 Docker run,扫码成功后归属到真实账号并同步 runtime/protocol profile;Docker run 会注入--hostname、--mac-address、只读/etc/machine-id,并同步写入 QQNT Linuxmachine-info,使/etc/machine-id、Docker MAC 和 QQNT 本地设备缓存保持一致。当前策略名为qqnt-visible-hostname-v1/physical-oui-mac-v1。 - NapCat 新设备验证走同一 scan session:
CaptchaLogin返回needNewDevice后,后端继续调用GetNewDeviceQRCode -> PollNewDeviceQR -> NewDeviceLogin,Admin/SSE 分开展示captchaUrl、newDeviceQrcode、已扫码、确认中、验证成功、登录成功/失败等中文进度,不把jumpUrl当作唯一完成入口。 - NapCat 离线看门狗按
QQBOT_NAPCAT_WATCHDOG_INTERVAL_MS(默认120000,最小30000,QQBOT_NAPCAT_WATCHDOG_ENABLED=false关闭)定时巡检在线账号,使掉线/被踢无需管理员打开列表页即可及时发现;检测到离线后只写入离线原因并复用super站内信告警,恢复登录必须由管理员在 Admin 手动触发「更新登录」。 - BangDream 当前源码根目录是
src/modules/qqbot/plugins/bangdream/src;按第三期插件结构放置真实职责代码:业务在domain/*,编排在application,操作在operations,外部 API 在infrastructure/integration,缓存/静态修正在infrastructure/storage,字典和静态配置在config,视觉渲染公共件在theme;不要恢复旧tsugu层级、旧大桶目录、纯 re-export 转接文件或空.gitkeep目录壳。 - BangDream 在线命令以
plugins/bangdream/plugin.json为单一来源,新增命令必须同步 SQL/在线命令表并跑 manifest/command-SQL 测试。 - BangDream event stage 大图必须保持分页拆图行为,线上 smoke 关注
imageCount=5,避免大 canvas OOM 回归。
轻量验证
文档、小范围配置或低风险改动:
git diff --check
后端代码改动:
pnpm run typecheck
pnpm run lint
pnpm test
BangDream 图片能力改动:
bash scripts/bangdream-render-smoke.sh --operation-key bangdream.song.search --text "夏祭り" --out-file ".kt-workspace/bangdream-smoke/song.jpg"
bash scripts/bangdream-render-smoke.sh --operation-key bangdream.event.stage --text "310" --out-file ".kt-workspace/bangdream-smoke/stage.jpg" --expected-image-count 5
接口改动必须启动或复用本地服务,并真实调用一次对应接口。
发布
主线发布由 Jenkins 构建镜像、推送 NAS 本地 Registry,并滚动更新 K8s kt-prod/kt-template-online-api。推送后不能只看 Git push 成功,需要继续观察 Jenkins、K8s rollout、新 Pod 状态和至少一条真实运行态 smoke。
QQBot 系统消息推送按以下顺序发布和回滚:
- 备份
qqbot_message_subscription、qqbot_message_template、qqbot_message_publish_binding、qqbot_message_publish_target、qqbot_message_event、qqbot_message_delivery,以及本功能相关的admin_menu/admin_role_menu行。 - 既有环境只应用本功能的幂等增量入口
sql/qqbot-message-push-init.sql,随后执行只读的sql/qqbot-message-push-verify.sql;不要把包含历史迁移的sql/qqbot-init.sql作为本功能生产迁移。仅一次性、可丢弃的全量初始化环境按顺序使用sql/refactor-v3/00-full-schema.sql、01-seed-core.sql、99-verify.sql。 - 验证六表、17 个精确索引、默认模板、18 个页面/按钮菜单和活动
super/admin角色授权。 - 先发布并验证 API 健康检查、旧 QQBot 发送能力和新只读接口,再发布并验证 Admin。
- 管理员显式创建订阅,并在每个发布账号中选择模板、配置群聊/私聊目标和启用绑定;随后用授权的非生产目标做一次有界 A→B 端口变化、DDNS 门禁、发送日志和幂等验收。
- 回滚时先停用全部消息推送绑定,再回滚 Admin 和 API;保留事件、投递和
qqbot_send_log历史供审计,不停止 Network Agent、端口转发、STUN Keeper 或 DDNS。 - Jenkins/K8s 成功只属于部署证据,不能代替真实 CRUD、页面、Outbox/DDNS 或 QQ 投递功能验收。
每次发布记录必须分别写明代码部署、生产 SQL、真实 CRUD、Admin 页面、Outbox/DDNS 和授权 QQ 投递的证据;没有明确授权的 QQ 群或 QQ 号时不得任选目标,且必须把真实投递标记为未验证,不能用 Jenkins/K8s 成功替代。
来源与许可证
| 一级来源 | 使用方式 | License |
|---|---|---|
| Tsugu BangDream Bot | BangDream QQBot 后端能力已重构合入 src/modules/qqbot/plugins/bangdream/src,保留本地 TSUGU-LICENSE |
MIT |