起因:只是想推个直播

一切的开始,其实特别朴素。

我关注了一个 VTuber,群里的机器人又总是挂掉。我就想着,干脆自己搞一个 Bot 玩玩吧。

最开始的需求甚至简单到不能再简单:
能往群里推一下“某某开播了”“某某发动态了”就行。

技术栈也没什么特别的,Python + Flask + SQLAlchemy + SQLite,通过 OneBot 11 协议用 WebSocket 连到 QQ,B 站那边轮询接口。

当时其实也考虑过 Koishi、MaiBot 之类比较成熟的机器人框架,但最后还是没用。

一方面是觉得对于我这种需求来说有点重,另一方面也是因为自己写的话,想怎么改就怎么改。反正需求很简单,自己折腾起来反而方便。

那时候我压根没想过,这东西最后会膨胀成什么样。

最开始真的就是:

“我就想推个直播而已。”

结果第一关就碰上了 B 站风控。

轮询多了以后接口开始报异常,后来干脆换成了 CDP——启动一个带登录态的浏览器,让请求看起来更像正常用户。折腾了一番之后,推送总算稳定下来。

事情到这里,本来应该结束了。

但显然没有。


有趣的转折:机器人开始变有趣

推送功能稳定以后,群友开始不满足了。

“光推直播多没意思,能不能整点活?”

于是有了 /踢/撅/佬 之类的指令。

本质上其实没什么复杂的,就是图像合成。

比如把 A 的头像抠下来、裁成圆形,再贴到底图上,做成“一脚把 B 踢飞”的效果。

后来觉得静态图还不够,又加了多帧 GIF。

因为头像每一帧的位置不一样,还得逐帧计算坐标。再后来又加了一些小概率事件,比如“撅人失败,反被撅”。

然后群里直接笑疯了。

这时候我才慢慢意识到:

机器人真正有意思的地方,好像并不只是“能不能提供一个功能”。

推送当然是有用的,但这种偶尔蹦出来的东西,才是真正能让群里活起来的。

大家会拿机器人互相整活,会测试一些奇怪的指令,会研究什么情况下能触发彩蛋。

一个原本只是用来“推直播”的 Bot,开始慢慢变成了群里的一部分。

然后问题来了:

既然已经这么好玩了……

能不能玩点游戏?


地下城:为了不刷屏,我选了最“无聊”的玩法

最开始我其实想过做正经一点的文字冒险。

出招、判定、技能、剧情分支……

但很快就意识到了一个非常现实的问题:

QQ 群不是游戏客户端。

你在游戏里觉得一次攻击、一条战斗日志很正常,但如果全部变成群消息,很快就会变成:

“攻击!”

“造成 237 点伤害!”

“怪物反击!”

“造成 184 点伤害!”

“暴击!”

“你死了。”

然后整个群直接没了。

而且 QQ 本身也有消息频率方面的限制。

所以地下城从设计的第一天开始,核心原则反而不是“尽可能丰富”,而是:

克制。

最后选了一个看起来甚至有点无聊的方案:

放置进度条游戏。

玩家进入地下城之后,只会看到一条进度条。

它根据你的属性一点一点往前走。

层数越高,怪物越硬,推进自然也越来越慢。

没有花哨的战斗演出,也没有密集的指令交互。

玩家要做的事情非常简单:

进去,等,回来看看,结算,然后继续。

反而正好解决了刷屏的问题。

从某种意义上来说,地下城最重要的设计甚至不是游戏机制,而是:

省下来的消息额度,就是玩家可以自由挥霍的空间。

当然,群友显然不这么认为。

“进度条太无聊了。”

“走得太慢了。”

“就这?”

于是……

军备竞赛正式开始。


数值膨胀的快乐与代价

最开始真的只有一个进度条。

后来有了装备。

有了货币。

然后有了锻造。

有了炼金。

有了 Boss。

有了职业。

有了阶级。

有了玩家对战。

有了抽奖。

有了转转互助。

……

货币甚至从铜币一路往上换:

100 铜 = 1 银,100 银 = 1 金。

再往上还有王国币、圣王国币。

每加一个系统,就会多一群人开始研究。

有人研究装备。

有人研究怎么赚钱。

有人研究怎么刷层数。

有人开始计算哪一件装备性价比最高。

甚至有人专门研究数值到底有没有问题。

而最痛苦的,其实就是我自己。

因为数值这种东西,真的很容易失控。

一开始装备定价太便宜,玩家很快就攒出了一大堆钱。

然后经济系统开始膨胀。

那就改。

改完以后发现装备之间的差距不够。

再改。

改完发现后期没有货币回收渠道。

继续改。

装备档次要拉开,价格要指数级上升,还得想办法让玩家手里的钱有地方花。

最后甚至发展出了一个群里的经典梗:

“铁匠铺最次的装备都要比商店最强的好。”

开发也逐渐变成了一种很奇妙的状态。

群里有人突然说:

“这个不对吧?”

“这里是不是应该加一个?”

“这个装备是不是太便宜了?”

“能不能加个 XX?”

然后我就去改。

拉最新代码。

跑测试。

重启。

上线。

过两天又有人发现新的问题。

再改。

再上线。

有时候一天能来回折腾好几轮。

说实话,这里面相当一部分代码现在回头看,可能都挺离谱的。

但那时候并不会特别在意。

因为每次更新以后,真的有人在玩。

有人会第一时间跑去试新功能,有人会研究刚加的装备,有人会问为什么自己的数值不对,还有人会专门跑过来告诉我:

“这个地方感觉不平衡。”

我一边当开发,一边当数值策划,一边当测试,有时候还得兼职客服。

但这种“有人在等更新”的感觉,其实挺奇妙的。

不是我一个人在决定这个游戏应该是什么样。

很多东西都是群友玩出来的。

他们提出想法,我去实现;实现之后他们再玩,再发现问题;然后继续改。

就这样一点一点往上堆。

原本一个小小的 Bot,莫名其妙就开始有了一个真正的“游戏”。


同学加入,地下城有了自己的家

一个人的时候还能勉强撑着。

后来一位同学也加入了开发。

于是事情终于稍微像那么回事了:

一个人主要折腾推送和各种趣味功能,另一个人负责地下城的玩法。

两个人一起改,效率自然高了不少。

但新的问题也随之出现:

群太吵了。

原来的群,本来就是大家聊天、水群、发图、吹牛的地方。

现在突然多了一堆人:

“我到 2237 层了。”

“谁有这个材料?”

“这个装备多少钱?”

“这Boss胜率怎么这么低??”

“我这个属性正常吗?”

然后地下城玩家聊地下城,其他人继续聊天。

最后整个聊天记录看起来就像两拨人在同时说两种语言。

所以干脆:

单独开一个地下城群。

想肝的去地下城群。

想聊天的留在水群。

两个群互不打扰。

现在回头看,这其实是整个过程中一个挺重要的决定。

因为它让我第一次意识到:

功能社区和社交社区,真的不一定应该挤在同一个房间里。


小插曲:被官方风控了

地下城越来越多人玩以后,还有一个我们迟早要面对的问题:

消息太多了。

有一段时间,机器人开始频繁出现消息发不出去的情况。

有时候消息直接被吞,有时候发送失败。

更刺激的是,QQ 账号甚至会直接掉登录,被踢下线。

那一阵子真的有点慌。

毕竟这东西本来只是拿来玩的,结果现在搞得像在运营什么大型线上服务。

排查了一圈以后,最后还是从“消息是稀缺资源”这个方向下手。

加消息频率限制。

而且不是简单地全局限制,而是按照群 × 用户去统计。

同一个人在短时间内疯狂触发,就限制他的频率。

冷却期间的重复请求直接静默过滤。

一些提示还增加作用时长,避免同一个提醒不断重复刷屏。

这套东西最后虽然解决了问题,但也让我真正意识到:

机器人发出去的每一条消息,都不是免费的。

它占用的是整个群的注意力。

如果你一直刷,哪怕大家一开始觉得好玩,最后也一定会烦。

所以地下城后来一直保留着最开始那个原则:

能不说话,就尽量别说话。


终章:拆分,走向官方

再往后,地下城越来越重。

它已经和最初那个“推直播 + 整活”的 Bot 完全不是一回事了。

继续全部塞在同一个程序里,维护起来越来越麻烦,功能之间也开始互相影响。

所以最后我们干脆做了一次大手术:

拆。

地下城从主程序里独立出来,单独部署、单独管理。

原来的 Bot 则继续负责推送、整活以及其他一些轻量功能。

这时候再回头看,最初那个小小的 Bot,其实已经长出了两个完全不同的方向。

再后来,我们又从第三方 OneBot 框架迁到了 QQ 官方的机器人平台。

从最开始相对“野生”的接入方式,变成了真正意义上的官方 QQ Bot。

某种意义上,也算是走完了一圈。


写在最后

回头看这一路,其实还是觉得有点不可思议。

一开始真的只是因为:

“我想推个直播。”

然后为了让机器人别那么无聊,做了几个整活功能。

然后群友说:

“能不能玩游戏?”

于是有了地下城。

然后又因为一句:

“这个是不是可以加一下?”

于是多了装备、货币、锻造、炼金、Boss、职业、阶级、PVP……

最后甚至发展出了独立的游戏群,还有了独立的版本。

技术上其实没什么特别高深的东西。

WebSocket 长连接、SQLite、图像合成、轮询接口、一些简单的游戏逻辑。

都是挺普通的东西。

真正让我觉得有意思的,反而是这个过程本身。

一个东西做出来以后,真的有人在玩。

然后他们会告诉你:

“这里可以再好一点。”

“能不能加这个?”

“这个数值是不是不太对?”

“要不我们试试这样?”

我就继续改。

他们继续玩。

然后再继续提意见。

就这样一点一点,把一个原本很小的 Bot 越做越大。

所以现在回头看,我觉得这个项目最有意思的地方,并不是它最后到底有多少功能,也不是它的代码写得有多漂亮。

而是它原本只是一个很小、很随意的东西,却真的因为一群人的参与,慢慢长成了一个大家一起玩、一起讨论、一起出谋划策的东西。

我负责把大家脑子里的那些奇奇怪怪的想法尽量变成现实。

他们负责告诉我:

“下一次更新,我们还想玩什么。”

至于最后为什么会变成这样……

只能说。

一开始谁也没想到,一个进度条,居然能玩成这样。

最后的最后

顺手开个源:GitHub · python_onebot_backend

虽然它并不是什么特别漂亮、特别工程化的项目,里面甚至还保留着很多“当时能跑就行”的历史痕迹,但它毕竟完整记录了这个 Bot 从最开始的推送、整活,一路长到地下城的过程。

如果你也对这种自己折腾 QQ Bot、OneBot 11,或者这种“一个小需求最后莫名其妙做大了”的项目感兴趣,可以来看看。