Manga Time Kirara 系列作者 X (Twitter) 推文监控与 QQ 群同步机器人。
- 🔍 通过 twscrape 非官方接口抓取作者推文(多账号池轮换)
- 🌐 翻译降级链:X 内置 → DeepL → Google
- 📦 完整媒体归档:图片 / GIF / 视频 + 缩略图
- 💬 OneBot 11 推送:文本 + 多图 + 视频到 QQ 群,支持反向 WS 服务端和正向 WS 客户端
- 🤖 QQ 群命令:作者列表、作者资料、指定作者最新推文
- 📚 ComicFuz MiniDownloader 子模块:每日 23:30 检查更新并上传 ZIP/CBZ
- ⚡ 所有作者统一轮询:每 10 分钟扫描,单作者至少 3 小时同步一次
- 🖥 Vue 3 + PrimeVue 管理台
- Python >= 3.12, < 3.15
- Node.js >= 18(pnpm)
- PostgreSQL 16+
开发时也可以先在 .env 使用 DATABASE_URL=sqlite://./kirara.db,Tortoise 会自动创建本地表。
# 1. 创建 venv 并安装依赖
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install -e ".[dev]"
# 2. 配置环境变量
Copy-Item .env.example .env
# 编辑 .env 修改数据库连接等配置
# 2.1 初始化 ComicFuz 子模块后安装其运行依赖(启用 ComicFuz 时)
git submodule update --init --recursive
pip install -r .\ComicFuz-MiniDownloader\requirements.txt
# 3. 安装前端依赖
cd frontend
pnpm install
pnpm build
cd ..
# 4. 启动
python -m tortoise -c app.db.tortoise_config.TORTOISE_ORM makemigrations models -n <name>
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000管理台: http://localhost:8000 (默认 admin / admin123)
仓库通过 Git submodule 引入 ComicFuz 源码:
git submodule update --init --recursive管理台可配置项目目录、输出目录和上传群;ComicFuz 邮箱、密码、token 文件从 .env 的 COMICFUZ_* 配置读取。Docker 构建时会把已初始化的 ComicFuz-MiniDownloader 一起复制到镜像中,任务使用源码导入,不需要启动第二个服务。
Cookie 模式需要浏览器导出的 X Cookie,至少包含 auth_token 和 ct0。可以在浏览器开发者工具的 Application / Storage -> Cookies -> https://x.com 中复制这两个值,也可以使用 Cookie 导出扩展导出的 JSON,或直接粘贴 Cookie Header:auth_token=...; ct0=...。不要把完整 Cookie 提交到公开聊天或提交进 Git。
auth_token 与 ct0 是 X 的登录会话值,不是长期 refresh token。twscrape 不能对 Cookie 模式做无感刷新;当账号状态变为“需要登录”时,在管理台编辑账号并重新导入 Cookie 即可。管理台的“刷新”按钮只对账号密码模式有效,它会调用 twscrape 重新登录,可能仍然需要邮箱验证码或 MFA。
项目默认安装 twscrape[curl] 并使用 TWS_HTTP_BACKEND=curl,通过 curl-cffi 的浏览器级 TLS 指纹降低密码登录被 Cloudflare 拦截的概率。如需排查兼容问题,可临时设置 TWS_HTTP_BACKEND=httpx 回退到原后端。项目调用 raw API 时只使用 twscrape Response 的兼容接口,不依赖 httpx.Response 类型。
NapCat 必须能够访问 MEDIA_PUBLIC_BASE_URL 生成的媒体地址。NapCat 位于容器内时不能填写 127.0.0.1:与后端处于同一 Docker 网络时可使用 http://app:8000,独立容器可使用 http://host.docker.internal:8000、宿主机局域网地址或反向代理域名。推文通知默认聚合一小时;达到 PUSH_BATCH_MAX_ITEMS(默认 10)时会提前发送合并转发。
账号密码模式需要 X 密码、绑定邮箱和邮箱密码。由于 X 的登录风控、验证码和 MFA 不稳定,生产环境仍优先建议 Cookie 模式。
YAML 导入只负责批量创建作者并把作者 ID 放入同步队列,不会在 API 请求内并发抓取全部作者。同步 worker 在访问 X 前必须从账号池独占一个账号,账号归还后会固定生成一次 ACCOUNT_MIN_REQUEST_INTERVAL 到 ACCOUNT_MAX_REQUEST_INTERVAL 之间的随机等待时间。默认值为每个账号每次作者同步间隔 30~90 秒。
当所有账号都在使用、等待间隔或冷却中时,worker 会等待账号可用,而不会把“没有可用账号”当作“没有新推文”。等待超过 ACCOUNT_ACQUIRE_TIMEOUT_SECONDS(默认 1800 秒)或 X 请求失败时,本次同步记录为失败,并保留作者原来的 last_synced_at,后续扫描会再次尝试。时间线请求已经取得的作者资料会直接复用,不再额外发起资料请求。
并发数由 SYNC_WORKERS 限制,同一账号不会被两个 worker 同时使用,同一作者处于队列或执行中时也不会重复入队。多账号可以各自独立工作,但每个账号仍遵守自己的随机间隔。
作者资料和时间线请求分开限频:首次同步或距离上次资料刷新超过 ARTIST_PROFILE_REFRESH_HOURS(默认 24 小时)时,才通过 user_by_login 刷新显示名、简介、头像 URL、Banner URL 等资料;普通轮询使用已保存的数字用户 ID。头像和 Banner 只保存 X 返回的 URL,不会每轮重新下载。
按默认平均间隔约 60 秒估算,一个账号每小时约处理 60 位作者,10 个账号约处理 600 位作者。1000 位作者完整轮询约需 1.7 小时;实际速度还会受 X 返回页数、媒体下载和失败重试影响。
YAML 格式参见 artists.example.yaml。导入响应包含 imported、skipped 和 queued,首次同步只归档最近推文,不推送 QQ 群。
同步队列当前保存在进程内存中,作者状态、推文去重和 last_tweet_id 保存在 PostgreSQL。应用中断后,启动时会立即扫描 last_synced_at 为空或已到期的作者并重新入队;已经写入的推文会通过唯一约束去重,且本轮看到的最大推文 ID 会推进水位,不会永久卡住初次同步。v1 不引入 Redis:如果使用 Redis,还需要 Celery/RQ、持久化、重试和任务状态表才能获得可靠恢复能力,当前 PostgreSQL 状态重建方案更简单可靠。
docker compose up -d默认 OneBot 模式为服务端模式。启用后,NapCat 的反向 WebSocket 填写为:
ws://宿主机IP:3001/onebot/ws
若 NapCat 与本项目使用同一个 Docker 网络,地址使用项目服务名,例如
ws://app:3001/onebot/ws。需要 token 时,在双方配置相同的
ONEBOT_ACCESS_TOKEN。也可以在管理台切换为客户端模式,此时后端会主动连接
ONEBOT_WS_URL。
管理台可以设置 OneBot Access Token。token 使用项目 SECRET_KEY 加密后保存,GET 设置接口只返回是否已配置,不返回明文;服务端模式会校验 Authorization: Bearer <token>。
OneBot 推送使用后端媒体缓存,不把 X 的 pbs.twimg.com 地址直接交给 NapCat。设置 MEDIA_PUBLIC_BASE_URL 为 NapCat 能访问的后端绝对地址:本机 NapCat 可填 http://127.0.0.1:8000,与 app 同一 Docker 网络可填 http://app:8000。推文媒体和作者头像会先缓存;缓存失败时只发送文字和原推链接。Docker Compose 默认使用 http://app:8000,NapCat 若运行在宿主机或其他网络中,需要把它改成宿主机可访问的地址。
QQ 命令 #kirara 作者列表 [页码] 每页返回 60 位启用作者,优先使用 OneBot 合并转发,并在实现不支持该 action 时自动拆分为普通消息。#kirara 作者 <名称/@账号> 和 #kirara 最新 <名称/@账号> 可查询作者资料与最新归档推文。
资料刷新时若 X 明确返回用户不存在,系统会自动禁用该作者并记录原因;超时、限流和账号池错误不会触发自动禁用。管理员确认作者更换账号后,可以在作者目录修正资料并重新启用。
X/Grok 译文目标语言由 X_CLIENT_LANGUAGE 控制,默认 zh-cn。项目会覆盖 twscrape 当前硬编码的英文请求头,优先保存 X 返回的简体中文 Grok 译文;只有 X 没有返回中文译文时才降级到 DeepL/Google。推文的 possibly_sensitive 与 is_translatable 会单独入库并由 API 返回,其中 possibly_sensitive 表示 X 的敏感媒体警告,不严格等同于 R18 分类。
possibly_sensitive=true 的推文仍会完整归档,但 QQ 自动推送和“最新”命令只发送作者提醒与原推链接,不发送正文、翻译、图片、视频或 GIF。
├── app/ # FastAPI 后端
│ ├── main.py # 应用入口 (lifespan 管理)
│ ├── config.py # pydantic-settings 配置
│ ├── core/ # 日志 / JWT 安全
│ ├── db/ # Tortoise ORM 模型
│ ├── api/ # REST API 路由
│ └── services/
│ ├── x/ # twscrape 客户端 + 账号池
│ ├── sync/ # APScheduler + 异步工作队列
│ ├── media/ # 存储抽象 (本地 / S3)
│ ├── translation/ # 翻译提供者链
│ ├── push/ # OneBot 11 WebSocket 客户端与 QQ 命令
│ └── comicfuz/ # ComicFuz 子模块源码调用与上传
├── frontend/ # Vue 3 + PrimeVue + Tailwind CSS
├── tests/ # pytest
├── docker-compose.yml
├── artists.example.yaml # 作者导入示例
└── ComicFuz-MiniDownloader/ # Git submodule
启动后访问 http://localhost:8000/docs (Swagger UI)