——QQ Bot、AstrBot、NapCat、OneBot 11、Docker,以及一堆越做越离谱的插件。
〇 前言
最开始做这个项目的时候,我其实没有想那么多。
就是单纯觉得:
能不能自己搞一个 QQ Bot?
最好它不只是会聊天,而是真的能帮我做点事情。
比如自动发图片、播放音乐、定时执行任务,甚至通过 QQ 远程控制服务器上的一些程序。
于是就开始折腾 AstrBot。
本来以为就是装个机器人,然后写几个插件。
结果真正做起来以后才发现,这东西比想象中有意思得多。
从最开始研究 NapCat、OneBot 11,到后来开始写自己的插件,再到定时任务、图片获取、音乐功能、远程控制……
做到最后,我突然发现:
我好像已经不太是在做一个 QQ Bot 了。
更像是在慢慢搭一个属于自己的自动化平台。
所以这篇就记录一下这个项目是怎么一步一步折腾出来的。
一、先从 QQ Bot 开始
1. 为什么想做 QQ Bot?
其实原因很简单。
平时经常会有一些重复性的事情。
比如:
- 想让机器人自动发一些图片
- 想定时推送一些内容
- 想通过 QQ 控制服务器上的任务
- 想调用 AI
- 想把自己写的一些小工具接进来
这些东西单独做都不算特别难。
但如果每一个功能都单独做一个程序,就会变得很麻烦。
所以一开始的想法就是:
能不能让 QQ 变成一个统一的操作入口?
比如我在 QQ 里输入:
/xxx
机器人就执行某个功能。
或者每天晚上固定时间,它自己完成某项任务。
这样平时甚至不需要登录服务器。
二、AstrBot、NapCat 和 OneBot 11
真正开始做之后,第一件事就是先搞清楚这几个东西到底是怎么连接起来的。
最后大概形成了这样的结构:
QQ
│
▼
NapCat
│
▼
OneBot 11
│
▼
AstrBot
│
├── AI
├── 插件
├── 定时任务
├── 图片
├── 音乐
└── 远程控制
简单理解的话:
NapCat 负责让 QQ 能够被程序控制。
OneBot 11 负责规定机器人和 QQ 之间怎么通信。
AstrBot 则负责处理机器人本身的逻辑,以及插件。
这样分开以后,整个系统就比较清楚了。
三、为什么最后选择 Docker
我的服务器本身就是 Ubuntu,所以最开始也考虑过直接把所有东西装到系统里。
但想了一下还是 Docker 更舒服。
最后服务器大概变成:
Ubuntu
│
└── Docker
├── NapCat
├── AstrBot
└── 其他服务
这样以后如果某个服务出了问题,直接看对应容器就行。
而且迁移服务器的时候也方便很多。
当然,Docker 也没有想象中那么省心。
四、第一次踩坑:容器启动到底为什么这么难
第一次真正部署的时候,我遇到了不少奇奇怪怪的问题。
有些日志一眼看过去非常吓人。
比如:
Failed to connect to the bus:
/run/dbus/system_bus_socket missing
还有 GPU、EGL 之类的报错。
以及:
usermod: invalid user ID 'napcat'
groupmod: invalid group ID
刚开始看到这些东西的时候,第一反应基本就是:
完了,程序炸了。
后来慢慢排查才发现,有些错误只是运行环境的问题,并不一定会导致 Bot 核心功能无法工作。
这也是这次项目让我比较有感触的一点:
日志里出现 ERROR,不代表整个程序就一定不能用。
还是得看它到底影响了什么。
五、OneBot:终于让 QQ 和程序连起来
容器跑起来之后,下一步就是测试 OneBot。
理论上只要通信正常,就应该可以通过接口获取 QQ 登录信息。
于是开始测试:
/get_login_info
结果:
连接不上。
当时还以为是 NapCat 出问题。
后来一路排查:
QQ
↓
NapCat
↓
OneBot
↓
HTTP / WebSocket
↓
端口
↓
AstrBot
中间任何一层没有配置好都不行。
尤其是 Docker 环境下,端口映射和容器内部端口很容易搞混。
这也是第一次比较完整地意识到:
一个“机器人收消息”的功能,实际上背后有好几个服务在互相通信。
六、插件化:不要把所有东西塞进一个文件
等 AstrBot 正常工作之后,就开始真正写功能。
最开始其实很容易写成这样:
bot.py
├── 音乐
├── 图片
├── AI
├── 定时任务
├── 网络请求
└── 远程控制
一开始功能少的时候没什么问题。
但我很快意识到一个问题:
以后功能肯定还会继续增加。
那这个文件最后大概率会变成一个巨大的垃圾场。
所以后来开始把功能拆成插件:
AstrBot
│
├── music
├── image
├── scheduler
├── network
├── remote
└── ...
每个插件只负责自己的事情。
例如图片插件只处理:
获取图片
↓
处理图片
↓
发送
定时任务插件则负责:
判断时间
↓
执行任务
↓
记录状态
这样以后想删掉一个功能,直接把插件关掉就行。
七、自动发图:这个项目真正开始变得有意思
我最开始比较想做的功能之一,就是:
让机器人每天自动发一些东方相关图片。
理想情况下应该是:
每天 21:00
↓
获取当天内容
↓
筛选
↓
下载图片
↓
发送到 QQ
↓
删除临时文件
听起来很简单。
然后我就开始研究图片来源。
八、Pixiv:想法很好,网络不太配合
最开始自然想到的是 Pixiv。
毕竟如果是二次元图片,Pixiv 的内容确实非常丰富。
于是最开始的设想就是:
Pixiv
↓
获取图片
↓
机器人发送
但是实际部署到服务器之后,问题就来了。
服务器的网络环境并不适合直接访问 Pixiv。
于是开始考虑:
换数据源。
后来把目标转向了 THBWiki 以及其他东方相关网站。
整个过程大概就是:
Pixiv
↓
网络访问不稳定
↓
寻找替代方案
↓
THBWiki
↓
继续实现自动发送
这其实是开发过程中很常见的一种情况。
很多时候不是你的代码有问题。
而是:
你依赖的东西不一定愿意配合你。
九、定时发送
图片获取的问题解决之后,又出现了一个非常现实的问题:
总不能每天自己输入命令吧。
于是开始做定时任务。
最开始:
/发送图片
需要手动执行。
后来:
每天 21:00
↓
自动执行
↓
获取内容
↓
发送
这时候机器人终于有了一点“自动化”的感觉。
十、每天 21:00 到第二天 21:00
后来又进一步调整了任务逻辑。
不是简单地判断“今天是哪一天”,而是希望把:
昨天晚上 21:00 ~ 今天晚上 21:00
作为一个完整周期。
也就是说:
昨天 21:00
│
│ 一个完整任务周期
│
▼
今天 21:00
这样处理一些每日推荐、每日统计之类的任务会更加自然。
也让我开始意识到:
定时任务并不只是一个 Timer。
它其实还涉及:
- 周期
- 状态
- 任务是否执行
- 执行结果
- 重复执行
- 服务重启
- 错过执行时间
十一、然后我忘记开 AstrBot 了
这个事情其实挺蠢的。
有一天到了晚上 21:00。
理论上应该:
21:00
↓
自动执行
实际上:
21:00
↓
……
因为我根本没开 AstrBot。
于是问题来了:
错过的任务怎么办?
如果只是单纯使用:
if 当前时间 == 21:00:
执行任务
那错过就是错过了。
所以后面就开始考虑任务恢复机制。
例如:
服务启动
↓
检查当前时间
↓
检查任务周期
↓
判断本次任务是否已经执行
↓
如果没有
↓
补执行
这件事情看起来很小,但实际上非常重要。
因为一个真正的自动化系统,不能只考虑:
“一切正常的时候怎么办?”
还要考虑:
“如果程序没启动、服务器重启、网络断了怎么办?”
十二、发送完图片之后,删掉
还有一个很实际的问题:
服务器空间。
如果每天都下载几十张图片,然后全部保存下来:
Day 1 → 100 MB
Day 2 → 100 MB
Day 3 → 100 MB
...
时间长了肯定会越来越大。
但实际上这些图片只是作为发送过程中的临时文件。
所以没必要永久保存。
于是任务流程变成:
获取图片
↓
保存临时文件
↓
发送 QQ
↓
确认发送
↓
删除临时文件
这也是我比较喜欢的一种设计:
能不存,就不存。
服务器空间留给真正需要保存的东西。
十三、音乐功能
除了自动发图之外,后来又开始折腾音乐。
希望机器人可以:
搜索歌曲
↓
获取歌曲信息
↓
获取音频
↓
发送
然后又开始往里面塞一些东西:
- 歌曲封面
- 歌词
- 歌曲信息
- 当前播放歌曲
做到这里之后,我突然发现一个很有意思的变化。
原本 QQ Bot 是:
有人说话
↓
机器人回复
现在已经变成:
QQ
↓
AstrBot
↓
调用不同服务
↓
完成不同任务
QQ 慢慢变成了一个“控制面板”。
十四、远程控制服务器
既然都已经能通过 QQ 控制机器人了,那能不能顺便控制服务器?
于是又开始折腾远程任务。
理想状态:
QQ
↓
机器人
↓
服务器
↓
执行任务
↓
返回结果
例如一些固定的:
查看服务状态
查看 Docker
执行某个任务
重启某个服务
这样平时即使不在电脑前,也可以通过 QQ 做一些简单的服务器管理。
不过这里有个非常重要的问题:
安全。
绝对不能把 QQ 用户发送的文字直接丢给 Shell。
比如这种东西:
os.system(user_message)
基本等于:
给服务器开了个 QQ Shell。
所以正确的思路应该是:
QQ 消息
↓
身份 / 权限检查
↓
命令白名单
↓
参数验证
↓
执行
↓
返回结果
目前这个方向还可以继续完善,比如权限等级、任务队列、日志记录等等。
十五、项目开始慢慢失控
做到这里,我发现这个项目已经和最开始想象的完全不一样了。
一开始:
“我想做一个 QQ Bot。”
后来:
“给它写几个插件。”
再后来:
“让它每天自动执行任务。”
然后:
“能不能让它控制服务器?”
最后变成:
“既然这些功能都能接进去,那为什么不能做成一个统一的软件?”
于是就出现了现在这个想法。
十六、QQ 其实只是入口
如果把现在的结构抽象一下:
QQ
│
▼
AstrBot
│
┌───────┼───────┐
│ │ │
AI 音乐 图片
│ │ │
├───────┼───────┤
│ │ │
自动化 服务器 其他插件
那么 QQ 只是其中一个入口。
如果再往前一步:
Backend
│
┌─────────────┼─────────────┐
│ │ │
AI Bot Automation
│ │ │
└─────────────┼─────────────┘
│
API
│
┌───────┴───────┐
│ │
Desktop Web
那么以后完全可以做一个自己的客户端。
打开软件以后,左边是:
- 聊天
- 音乐
- 图片
- AI
- 自动化
- 服务器
- 文件
- 工具
需要什么直接点什么。
十七、我真正想做的东西
所以现在回头看,这个项目的目标已经不是:
“做一个 QQ Bot。”
而更像是:
做一个属于自己的个人集成平台。
把平时会用到的各种东西全部塞进一个地方。
比如:
| Personal Hub | |
| 聊天 | AI |
| 音乐 | 图片 |
| 自动化 | 服务器 |
| 文件 | 各种小工具 |
甚至以后我自己做的其他项目,也可以通过插件接进来。
这样以后写一个新工具,不一定需要单独做一个完整的软件。
只需要做一个模块,然后接进这个平台。
十八、目前的项目状态
目前大概可以整理成这样:
| 功能 | 状态 |
|---|---|
| QQ Bot | ✅ |
| NapCat | ✅ |
| OneBot 11 | ✅ |
| AstrBot | ✅ |
| 插件化 | ✅ |
| 音乐功能 | ✅ |
| 图片获取 | ✅ / 持续优化 |
| 定时发送 | ✅ |
| 临时文件清理 | ✅ |
| 任务恢复 | 🚧 |
| 远程任务 | 🚧 |
| 独立客户端 | 💡 |
| Personal Hub | 💡 |
很多东西其实还没有做到真正完善。
但现在这个项目已经具备了一个比较完整的基础。
十九、踩坑总结
回头看,这个项目最让我印象比较深的几个坑大概是这些。
1. 网络环境
程序本身没问题,不代表它能访问目标网站。
尤其是服务器环境下,网络问题经常比代码问题更加麻烦。
2. Docker
容器化确实方便,但端口、权限、网络、挂载目录这些东西也需要慢慢搞清楚。
3. Bot 通信链
QQ → NapCat → OneBot → AstrBot。
任何一层出问题,最后表现出来都有可能是:
“机器人怎么没反应?”
4. 定时任务
定时任务真正麻烦的是异常情况。
正常执行很简单。
真正需要考虑的是:
- 没启动怎么办?
- 重启怎么办?
- 重复执行怎么办?
- 网络断了怎么办?
- 任务执行失败怎么办?
5. 不要一开始就把东西写死
插件化虽然前期麻烦一点,但功能一多以后,它的优势就非常明显。
二十、最后
这个项目到现在其实还算不上一个完整的软件。
它甚至连一个正式的名字都还没有。
但从最开始的一句:
“能不能弄个 QQ Bot?”
到现在开始思考:
“能不能做一个把这些东西全部集成起来的软件?”
这个变化本身就挺有意思。
我以前做一些小项目的时候,往往都是:
想到一个东西
↓
做出来
↓
结束
而这次不太一样。
一个 QQ Bot 做出来以后,又自然产生了插件。
插件做多以后,又产生了自动化。
自动化做起来以后,又想接服务器。
最后又开始想着干脆做一个统一的软件。
所以现在看来:
QQ Bot 可能只是这个项目的第一步。
真正想做的,是一个可以不断往里面添加东西的个人工具平台。
以后不管是 AI、音乐、图片、自动化、服务器管理,还是我自己做出来的其他小工具,都可以慢慢接进来。
至于最后它会变成什么样……
现在还不知道。
不过这倒也挺好。
毕竟有些项目,本来就是一边做,一边长出来的。

