基于 NoneBot2 的 QQ 机器人,主要功能是查询 Steam 相关数据。
pip install requests beautifulsoup4 playwright httpx lxml
playwright install chromium
# NoneBot 插件
nb plugin install nonebot-plugin-alconna
nb plugin install nonebot_plugin_apschedulercp .env.example .env编辑 .env,配置 NapCat 连接信息。详见下方 配置。
nb run所有配置通过 .env 文件设置(已 gitignore)。参考 .env.example。
通过 WebSocket 连接 NapCat(或其他 OneBot V11 实现),无需 QQ 开放平台凭证。
| 变量 | 说明 |
|---|---|
DRIVER |
NoneBot 驱动,必须包含 ~websockets 以支持 ws 连接 |
ONEBOT_WS_URLS |
NapCat WebSocket 地址,格式 ["ws://ip:port"] |
ONEBOT_API_ROOTS |
NapCat HTTP API 地址,格式 {"bot_qq": "http://ip:port/"} |
HOST |
监听地址,默认 127.0.0.1 |
PORT |
监听端口 |
COMMAND_START |
指令前缀,默认 [""](无前缀) |
.env 示例:
DRIVER=~httpx+~websockets
ONEBOT_API_ROOTS={"2326291391": "http://127.0.0.1:26657/"}
ONEBOT_WS_URLS=["ws://192.168.10.14:26657"]
HOST=127.0.0.1
PORT=26657
COMMAND_START=[""] 登记游戏领取记录的后端数据库,两种后端可选:SQLite(默认)或 NocoDB。
不用数据库功能?可删除
plugins/db/目录,但需同时删除依赖它的命令插件(bind / get / remain / report / unfinished / unreported / queryWishlist / probe / calendar / wish;curator_monitor 需关闭其中的数据库同步)。Steam 查询、price比价、截图、代理等功能不需要数据库——配置读取已解耦到plugins/config.py,删除后不受影响。
| 变量 | 默认值 | 说明 |
|---|---|---|
DB_BACKEND |
智能默认 | 后端选择:sqlite(默认)或 noco。不设置时若检测到 NOCO_TOKEN 已配置则默认 noco,否则 sqlite |
SQLITE_PATH |
data/db/foxmeido.db |
SQLite 数据库文件路径(仅 sqlite 后端使用,首次使用自动建表) |
NocoDB 后端(DB_BACKEND=noco 时需自行部署 nocodb/nocodb):
| 变量 | 默认值 | 说明 |
|---|---|---|
NOCO_URL |
https://127.0.0.1:52533/api/v2/tables |
NocoDB API 地址 |
NOCO_TOKEN |
— | API Token(必填) |
NOCO_ACCOUNT_TABLE |
— | account 表格 ID |
NOCO_RECORD_TABLE |
— | record 表格 ID |
NOCO_REMAIN_TABLE |
— | remain 表格 ID |
NOCO_CHECK_REMAIN |
true |
get 指令是否检查 remain 表剩余副本数;false 时跳过检查直接登记 |
NOCO_WISHLIST_TABLE |
— | wishlist 表格 ID |
NOCO_VERIFY_SSL |
false |
是否验证 SSL(国内自建通常用自签名证书,默认关闭) |
后端迁移:两个后端可随时切换,数据迁移用 scripts/migrate_db.py:
# NocoDB → SQLite(换 sqlite 后端前执行)
python scripts/migrate_db.py --from noco --to sqlite
# SQLite → NocoDB(回迁)
python scripts/migrate_db.py --from sqlite --to noco数据库抽象层(插件开发用):sqlite / noco 各自实现为独立后端指令,统一继承 DatabaseBackend 抽象基类。命令插件通过 get_backend() 获取当前配置(DB_BACKEND)对应的后端实例,只依赖抽象接口,不感知具体后端。配置读取统一在 plugins/config.py(全局配置中心,与可选的 db 文件夹解耦):
from plugins.db import get_backend
from plugins import config as cfg # 全局配置中心(DB_BACKEND / SQLITE_PATH 等)
backend = get_backend() # DB_BACKEND=sqlite → SqliteBackend,DB_BACKEND=noco → NocoBackend
# where: [(field, op, value), ...],op ∈ {eq, gt, ne},value=None 表示 NULL 判断
rec = backend.get_record("account", [("account", "eq", "123456")])
rows = backend.get_records("remain", [("canBeClaimed", "gt", 0)], sort="created_at")
rec = backend.create_record("wishlist", {...})
rec = backend.update_record("records", 42, {"report": 1})逻辑表名:account / records / remain / wishlist。
返回约定:查询为空返回 "",失败返回 {"error": ...},列表返回 {"list": [...], "pageInfo": {"totalRows": n}}。
表结构参考 plugins/db/createTables.sql(sqlite 自动建表同源)。
国内访问 Steam 需要代理转发。只设 HTTP_PROXY 即可,HTTPS_PROXY 自动同步。
| 变量 | 默认值 | 说明 |
|---|---|---|
HTTP_PROXY |
— | 代理地址,如 http://127.0.0.1:7890。不设则不走代理 |
HTTPS_PROXY |
— | 跟随 HTTP_PROXY,通常无需单独设置 |
| 变量 | 说明 |
|---|---|
STEAM_COOKIE |
Steam 商店页面 Cookie,wish 功能和热门标签提取需要。从浏览器访问 store.steampowered.com → F12 → 复制请求头 Cookie: 整行。格式:sessionid=xxx; steamLogin=xxx; ... |
STEAM_CC |
目标国家/地区码,控制 Steam API 返回的货币和区域定价。如 cn → 人民币、us → 美元、jp → 日元。留空则按出口 IP 自动决定 |
PRICE_REGIONS |
price 命令查询的区域列表,逗号/空格分隔可多个。如 cn,us,jp。留空则用默认集合:cn,us,jp,kr,hk,tw,sg,gb,de,fr,br,au,ca |
CURATOR_ID |
Steam 鉴赏家 ID(unreported 功能需要) |
price 多区域比价:
price 1091500
比价 https://store.steampowered.com/app/1091500/Cyberpunk_2077/
输出按折合人民币升序排列,自动标出最低价区:
Cyberpunk 2077(AppID 1091500)各区现价,按折合人民币升序
US 美国 $19.99 ≈¥141.63 (-50%,原价 $39.99)
JP 日本 ¥3,280 ≈¥160.90
CN 中国 ¥ 298.00
无价格(锁区/未上架/网络异常):RU 俄罗斯
最低:US 美国 ¥141.63
说明:外币统一用实时汇率(open.er-api.com,基准人民币)折算便于比较;结果在内存缓存 15 分钟;区域码不区分大小写,重复自动去重。
Playwright 用于 Steam 页面截图(steamGoods、pub 等命令)。
| 变量 | 默认值 | 说明 |
|---|---|---|
PLAYWRIGHT_ENABLED |
true |
是否启用 Playwright 截图。设为 false 时跳过 Playwright,游戏详情截图改用 Steam Store API 回落 |
PLAYWRIGHT_HEADLESS |
true |
无头模式。设为 false 可在调试时看到浏览器窗口 |
PLAYWRIGHT_COOKIE_FILE |
— | Playwright 格式的 Cookie 文件路径(JSON),用于登录态截图。参考 data/cookies/steam_playwright.json.example |
获取 Playwright Cookie:
python scripts/get_steam_cookies.py弹出浏览器 → 手动登录 Steam → 回车 → cookie 自动写入 data/cookies/steam_playwright.json。然后在 .env 中添加:
PLAYWRIGHT_COOKIE_FILE=data/cookies/steam_playwright.jsonPlaywright 的 cookie 格式与
STEAM_COOKIE(requests 用)不通用,需要单独的文件。STEAM_COOKIE用于 wish 命令,PLAYWRIGHT_COOKIE_FILE用于截图功能。
截图失败调试:
截图遇到超时或页面加载失败时,系统会自动将当前页面可见内容保存为 PNG 快照,存放到 logs/failed_screenshot_*.png。文件名包含失败来源和时间戳,可用于排查页面结构变化或网络问题。
监控 Steam 鉴赏家后台的待处理游戏副本邀请,有新副本到达时通过 QQ 群消息(和/或 ntfy)推送通知。
| 变量 | 默认值 | 说明 |
|---|---|---|
CURATOR_COOKIE_FILE |
— | (推荐) Playwright 格式的 Cookie 文件路径(JSON),通过 python scripts/get_curator_cookies.py 获取。bot 运行时通过 Playwright 加载,session 维持更久 |
CURATOR_COOKIE |
— | (旧方式/降级) 静态 Cookie 字符串,从浏览器复制。仅在未设置 CURATOR_COOKIE_FILE 时使用 |
CURATOR_ENABLED |
false |
是否启用每日定时检查 |
CURATOR_NAME |
自动探测 | 鉴赏家显示名称。不设则自动从 Steam 页面抓取 |
CURATOR_NOTIFY_GROUP |
— | 每日定时推送的目标群号 |
CURATOR_NOTIFY_USER |
— | 每日定时推送的目标 QQ 号(可选,与 GROUP 可同时设置) |
CURATOR_CHECK_TIME |
09:00 |
每日定时检查时间,多个用逗号分隔,如 09:00,15:00,21:00 |
CURATOR_NTFY_TOPIC |
— | ntfy topic,设了则额外推送到手机 |
推荐设置方式:
# 1. 运行获取脚本(弹出浏览器 → 登录 Steam → 导航到鉴赏家后台 → 回车)
python scripts/get_curator_cookies.py
# 2. 在 .env 中添加:
CURATOR_COOKIE_FILE=data/cookies/curator_playwright.json
CURATOR_ID=你的鉴赏家ID命令:
| 命令 | 说明 |
|---|---|
pending |
手动触发一次检查,结果发送到当前群。有今日新到游戏时额外输出 📋 今日新到游戏 列表 |
pending init |
建立历史基线:抓取当前所有待处理游戏并标记为历史入库(不推送、不计入「今日新到」),用于首次接入监控时防止积压库存刷屏 |
pending init force |
数据库已有记录时强制重建基线(不给 force 会被拦截,避免把真正的新到游戏误设为历史) |
pending test |
发送测试推送(QQ 消息 + ntfy 如有配置) |
自动首跑:若数据库从未初始化过(SQLite
meta表无initialized标记),普通pending与定时检查也会自动走上述「静默基线」路径,不会刷屏;已有历史数据的旧库视为已初始化,不受影响。基线初始化时是否把库存同步进 remain 台账,沿用现有CURATOR_SYNC_REMAIN开关。
消息格式:
📦 鉴赏家名 新增待处理副本: ← 本次新增的游戏
🆕 游戏A(123456)(1 个副本)
📋 鉴赏家名 今日新到游戏: ← 今天所有新到游戏(含本次新增)
📅 游戏A(123456)(1 个副本)
📅 游戏B(789012)(2 个副本)
💡 共 5 款游戏待处理
日志输出:
每次检查在后台输出一条日志,三种情况:
鉴赏家检查完成: 待处理 5, 游戏 7 款, 新增 2, 累计 7
最新: 霍格沃茨之遗 ← 有新增时
鉴赏家检查完成: 待处理 3, 游戏 3 款, 新增 0, 累计 7
无新增 ← 有游戏但无变化
鉴赏家检查完成: 待处理 0, 游戏 0 款, 新增 0, 累计 7
探测结果为空 ← Cookie 过期或页面抓取失败
数据库修复:
如果因版本升级导致 📋 今日新到 显示了所有历史游戏(而非仅当天),执行:
python scripts/fix_curator_db.py这会重置 first_seen_at 错误记录,仅保留真正当天入库的游戏。
注:
pending init force可在 bot 内完成同类重置,无需停服执行脚本。
累计 为 SQLite 中累积的唯一游戏总数(不会回缩)。
Cookie 过期提示:
- 使用
CURATOR_COOKIE_FILE(Playwright 方式)时,如果 cookie 过期,系统会自动检测登录页面,提示❌ CURATOR_COOKIE_FILE 已过期,请重新运行 python scripts/get_curator_cookies.py - 使用
CURATOR_COOKIE(旧方式)时,同样会检测登录页并给出提示,建议迁移到 Playwright 方式
收到消息后立即添加 QQ 表情回应(如 ✅),类似 frontier 的 send_group_message_reaction 模式。仅 NapCat/OneBot V11 支持,不支持时自动忽略。
| 变量 | 默认值 | 说明 |
|---|---|---|
MESSAGE_REACTION_ENABLED |
false |
是否启用表情回复 |
MESSAGE_REACTION_FACE_ID |
32 |
QQ 表情 ID,详见常见 ID 表 |
MESSAGE_REACTION_AUTO_REMOVE |
false |
处理结束后是否自动撤回表情 |
常见 QQ 表情 ID:
| ID | 视觉 | 推荐场景 |
|---|---|---|
32 |
✅ 对勾 | 安全通过 |
26 |
❌ 叉 | 拒绝/不通过 |
212 |
🤔 思考 | 处理中 |
351 |
👍 强 | 赞同/完成 |
324 |
👌 好的 | 收到/等待 |
319 |
🎉 庆祝 | 任务完成 |
0 |
😲 惊讶 | — |
14 |
🙂 微笑 | — |
13 |
😁 呲牙 | — |
67 |
❤️ 爱心 | — |
107 |
⭕ OK | — |
105 |
👍 爱你 | — |
101 |
🙏 抱拳 | — |
74 |
💩 便便 | — |
log_crash() 调用时会额外推送 ntfy 通知。可自建 ntfy 服务器。
| 变量 | 默认值 | 说明 |
|---|---|---|
CRASH_NTFY_SERVER |
https://ntfy.sh |
ntfy 服务器地址 |
CRASH_NTFY_TOPIC |
— | ntfy topic,不设则不推送 |
定时或手动执行 git pull,检测到新提交后自动重启机器人以加载新代码。
更新流程: 检测到更新后,机器人先把更新状态写入项目根目录的
.lock标记文件,再发送「🔄 更新成功」提示。提示为尽力送达(best-effort):若nb run的自动重载恰好在拉取瞬间关闭了连接导致提示发送失败,不会报错——重启完成后会依据.lock自动补发「✅ 更新完成 + 本次提交列表」,保证用户一定能收到结果(群聊触发回发到该群,私聊触发回发到该用户私聊)。默认不主动退出进程 ——nb run自带自动重载(reload),检测到代码文件变化后会优雅重启并加载新代码。若以
--no-reload运行或部署在生产环境(进程管理器托管),请设置GIT_AUTO_PULL_FORCE_EXIT=true:拉取成功后会主动退出进程,由GIT_AUTO_PULL_RESTART_CMD/ 进程管理器 / 启动脚本接管拉起。
| 变量 | 默认值 | 说明 |
|---|---|---|
GIT_AUTO_PULL_ENABLED |
false |
是否启用自动检查 |
GIT_AUTO_PULL_INTERVAL |
30 |
间隔模式:每 N 分钟检查一次 |
GIT_AUTO_PULL_TIME |
06:00 |
定时模式:每天检查时间 |
GIT_AUTO_PULL_SCHEDULE_TYPE |
both |
调度类型:cron / interval / both |
GIT_AUTO_PULL_NOTIFY_GROUP |
— | 拉取结果通知的目标群号(可选) |
GIT_AUTO_PULL_REMOTE |
origin |
远程仓库名或 URL(如 origin 或 https://github.com/user/repo.git) |
GIT_AUTO_PULL_GIT_PATH |
git |
git 可执行文件路径(绝对路径或仅文件名) |
GIT_AUTO_PULL_RESTART_CMD |
— | 自定义重启命令(仅在 FORCE_EXIT=true 时生效;如 systemctl restart foxmeido) |
GIT_AUTO_PULL_FORCE_EXIT |
false |
是否主动退出进程。默认 false:交给 NoneBot 自动重载;true:主动退出,由 RESTART_CMD / 进程管理器接管 |
GIT_AUTO_PULL_BRANCH |
— | 目标分支(留空自动检测当前分支) |
Steam 非官方 API store.steampowered.com/api/appdetails 的参数说明、返回格式、注意事项和常用货币对照表 → scripts/steam_store_api.md
| 命令 | 别名 | 说明 |
|---|---|---|
id <appid/url> |
steam 查商店 steamGoods |
查询游戏详情(名称、类型、热门标签、厂商、发行日期、价格、截图) |
price <appid/url> |
比价 |
查询多个区域的游戏价格比价(区域由 PRICE_REGIONS 配置,输出按折合人民币升序) |
morepic <appid/url> |
更多截图 |
发送游戏全部截图缩略图(Steam API screenshots.path_thumbnail) |
pub <publisher> |
steamPublishers |
查询发行商页面截图(?flavor=all_upcoming,即将推出列表) |
find <关键字> |
搜索steam游戏 |
交互式搜索 Steam 游戏,搜索结果选编号后显示详情(名称、类型、热门标签、语言、日期、发行商、价格、截图) |
pending |
— | 鉴赏家副本监控,手动触发检查;首次运行自动静默建立基线 |
bind |
— | 将 Steam ID 绑定到 QQ 账号 |
get <游戏ID> [用户ID] |
— | 登记游戏领取记录(可代他人登记) |
remain <游戏ID> [份数] |
— | 剩余份数登记 / 查询当前领取情况 |
wish <游戏ID> |
— | 将游戏加入 Steam 愿望单 |
report <游戏ID> |
— | 将指定游戏的记录标记为已报告 |
probe |
— | 检测领取记录链接是否有效并更新提交时间 |
unfinished |
— | 输出未完成(未提交评测)的记录 |
unreported |
— | 输出未报告的游戏及用户 |
queryWishlist <游戏ID> |
queryWish qwish |
查询愿望单记录 |
calendar |
cale 愿望单 冤枉单 任务 |
Steam 个人日历截图 |
finder <url> |
— | 通用页面截图 |
update / pull |
— | 手动触发 git pull,检测到更新后自动重启机器人 |
update force |
— | 强制拉取(丢弃本地未提交更改)后重启 |
ping |
— | 心跳测试 |
help |
— | 显示帮助 |
Steam 商店链接会自动触发查询(如发送 https://store.steampowered.com/app/3251240)。
运行中的异常自动写入 logs/ 目录,每次错误一个时间戳文件。
- 文件格式:
error_<时间戳>.log或crash_<时间戳>.log - 内容:
[时间] [来源] 描述+ 异常堆栈 - 截图失败快照:
failed_screenshot_<来源>_<时间戳>.png(Playwright 截图失败时自动保存的页面快照,用于排查页面加载问题)
foxMeido/
├── .env.example # 配置模板
├── pyproject.toml # NoneBot 项目配置
├── scripts/ # 工具脚本
│ ├── get_steam_cookies.py # 获取 Playwright 格式的 Steam cookie
│ ├── get_curator_cookies.py # 获取鉴赏家后台 Playwright cookie
│ ├── fix_curator_db.py # 修复 curator 数据库 first_seen_at 记录
│ ├── migrate_db.py # NocoDB ↔ SQLite 双向迁移
│ ├── steam_store_api.md # Steam Store API 参考(appdetails / 多区比价用)
│ ├── steam_reviews.md # steam_reviews.py 使用说明
│ └── steam_reviews.py # 提取 Steam 用户评测中的鉴赏家链接 → [`doc`](scripts/steam_reviews.md)
├── data/ # 运行时数据(gitignore)
│ ├── cookies/ # Playwright cookie 文件
│ │ └── steam_playwright.json.example # cookie 格式模板
│ └── db/ # SQLite 数据库文件
├── logs/ # 错误日志(自动创建,gitignore)
└── plugins/
├── env_utils.py # 环境变量读取、代理配置(通用工具)
├── config.py # 全局配置中心(读取 .env:DB/Steam/代理/PRICE_REGIONS)
├── steam_utils.py # Steam 通用工具
├── curator_monitor.py# Steam 鉴赏家副本监控
├── finder.py # 通用页面截图
├── help.py # 帮助命令
├── ping.py # 心跳测试
├── steamFinder.py # Steam 游戏详情查询
├── steamFinderAuto.py# Steam 链接自动触发查询
├── steamPublisherFinder.py # 发行商页面查询
├── steamPublisherFinderAuto.py# 发行商链接自动触发
├── steamSearcher.py # Steam 游戏搜索
├── price.py # 多区域价格比价命令
├── playwright_utils.py # 共享 Playwright 工具
├── auto_pull.py # 自动拉取仓库更新(定时 + 手动命令)
├── message_reaction.py # 表情回复模块(核心函数 + 自动钩子)
├── error_logger.py # 错误日志模块(全模块共用)
├── bind.py # 登记 Steam ID(绑定账号)
├── get.py # 登记游戏领取记录
├── wish.py # 愿望单登记
├── remain.py # 剩余份数登记/查询
├── probe.py # 检测领取记录链接是否有效
├── report.py # 标记游戏已报告
├── unfinished.py # 输出未完成记录
├── unreported.py # 输出未报告记录
├── queryWishlist.py # 查询愿望单记录
├── calendar.py # Steam 个人日历截图
└── db/ # 数据库抽象层(sqlite / noco 双后端指令)
├── __init__.py # 抽象层:DatabaseBackend 接口 + get_backend() 工厂
├── base.py # DatabaseBackend 抽象基类(接口定义)
├── sqlite.py # SqliteBackend 指令(SQLite 后端实现)
├── noco_backend.py # NocoBackend 指令(NocoDB 后端实现)
└── createTables.sql# 4 张表结构参考(sqlite 自动建表同源)
- wish功能每隔一段时间自动探测游戏何时发售
- 获取领取记录,用于核对