
尝试介绍一下我的 Mirai 酱
首先很抱歉先前那篇讲 BiliTools 的文章不更了,问就是太烂了讲不动一点
这次我想来讲一下我的 QQ 机器人,Mirai 酱
事先声明,本文中的信息并不可靠,请不要盲目相信……
Mirai 酱的故事
最初想要做一个自己的 QQ 机器人,应该是在某位群友在群里询问了关于机器人的事之后。后来他拉着他的机器人凯尔希
进了群,不过不知为何最后却似了。
最初的 Mirai 酱
翻了下我的程序存档,Mirai 酱最早是在 2022 年 4 月左右被我编写出来的,当时基于Ariadne
和mirai
。
Mirai 酱之所以叫 Mirai 酱,正是因为她最开始基于的框架是
mirai
(绝对不是因为我是个起名废物qwq)
这是 Mirai 酱的第一代,拥有 B站动态转发、随机涩图、天气查询、每日抽签、疫情查询(在之后移除) 功能。(其实当然可以写更多,只是我没点子了)
后来不知道什么时候,mirai
框架似乎似了,Mirai 酱也被我忘记了一段时间。直到……
我找到了go-cqhttp
这个实现。
在找引用链接的时候我发现
mirai
框架原来还活着,而且社区也跟进了签名服务器插件的开发(但框架换了就是换了,也没有换回去这种说法了)
重生的 Mirai 酱
Mirai 酱第二代的完成大约是在 2023 年 6 月 12 日左右,基于pycqBot
和go-cqhttp
。在 8 月 24 日左右追加了unidbg-fetch-qsign
来进行签名认证。
这一代的 Mirai 酱功能没有增加,甚至还减少了一个天气查询功能。(天气查询真没人用罢,交互还难写)不过群友们在 Mirai 酱复活赛打赢之后还是很高兴,甚至发起了电
现在的 Mirai 酱
Mirai 酱第三代的大致完成大致是在 2024 年 2 月 8 日零点左右
这一代的 Mirai 酱新增了方舟模拟抽卡的插件,并且每个插件都可以单独为某个群开启或关闭。
这次的框架迁移是因为go-cqhttp
的停止维护(详见:QQBot的未来以及迁移建议)。尽管 Mirai 酱还没因为这个出现什么问题,我还是根据他们的建议将 Mirai 酱迁移到了OpenShamrock
框架。
然后我就发现虽然go-cqhttp
和shamrock
都是遵循onebot
标准的,但是pycqBot
在与shamrock
交互的时候始终有莫名其妙的问题。于是我就仿照pycqBot
专门为shamrock
写了一个简易框架(有直接使用pycqBot
中的一些代码,比如cqcode
的实现),已经能够满足 Mirai 酱的需求了。不过因为实在是太简易了,以至于插件写起来与pycqBot
相比有亿点麻烦(恼)
emm 就当作是 Python 练习了罢(?):你说得对,但是这就是重复造轮子的沙贝操作(全恼)
shamrock
被我部署在了我的小主机上的 PVE 里,依靠BlissOS
运行。(参考:PVE下安装BlissOS11)
因为 tx 的作品实在是太“精致”了,为了 QQ 能够比较稳定地运行,我不得不给 BlissOS 开了 6GB 的 RAM
主要的 Python 程序被我部署在了 PVE 中的另一台 Debian 12 上 目前挂了一天一夜还没有出问题
关于简易框架
十分甚至九分感谢
pycqBot
项目,提供了思路和具体实现供我照搬参考
所以说这个简易框架,应该算是mini-pycqBot
?(大雾)
通信方式
和pycqBot
一样,都是
- 用
websocket
接收框架的事件上报 - 用
http
发送指令到框架
额外支持了cookies的长期储存,但是保存方式是pickle(逃
其实也尝试着写了json保存,但是不行,B站的接口不认。大概是我漏了什么细节罢
各部分关系
1 |
|
flowchart LR SocketBase --> SocketApp --> Bot HttpBase --> HttpApp --> Bot Plugin
插件系统的实现
使用Bot.load_plugin()
加载一个插件实例之后,这个实例会被保存在Bot._plugins
列表中
每当SocketApp
收到消息上报时,它会根据消息的类型挨个去尝试调用插件们的对应方法,如果插件定义了对应的方法,它就会把消息包传给这个方法。
(消息类型的名字的生成照抄了pycqBot
)
当插件想要发送指令时,它只需要调用自身对应名字的方法即可。这些方法在Plugin
父类中被批量预先定义(且分为普通和异步版本)
这些方法对Bot.call_api()
进行了封装,这样他们就只需要传入参数,而不用传入方法名了
如何使用
老实说,真的会有人来用这个框架吗……
就当是写给自己看的了,免得以后想搓新插件的时候又忘了
简单的使用
入口模块至少应该长这个样子:
1 |
|
连我自己都觉得麻烦了
往后可能会优化一下导入方法,写成像pycqBot
那样的:
1 |
|
真的方便吧我靠 再看看我这个简直就是原神
上面设置的管理员不同于群里的管理员,而是 Bot 的管理员。
插件可以通过这个来校验一些只有 Bot 主人才能有的权限。
(当然一个插件里都没写的话这个东西也就没用了)
插件开发
一个插件至少应该长这个样子:
1 |
|
普通版方法和异步版方法的名字只差一个后缀,是_func
还是_async
可以使用self.bot.add_task()
来借助简易框架内置的给aiohttp
用的事件循环运行自己的协程
(异步自己去学,我讲不清楚的w)
通过self.admins
可以得到 Bot 的管理员们的QQ,以此来实现鉴权之类的操作
插件有需要网络请求的部分可以搞依赖注入,来使用self.bot.request
这个写好的网络请求
可供参考的资料:
缺陷
如你所见,这个简易框架:
- 没有定义消息类之类的简化处理流程之类的东西,而是直接让开发者处理事件字典
- 没有把接口的参数之类的写到程序里,而是要你自己去查文档
- 没有快捷的回复功能,必须自己手动生成
cqcode
来加到回复消息里(应该算是第一条的一部分) - 没有像这个一样的方便的消息筛选系统、鉴权系统,全都要开发者自己写
- 没有统一的插件启用禁用系统,各个插件是割裂的
- 还有很多……
笑嘻了,我消息筛选用正则表达式用到似
总之就是非常的简陋就是了,你要什么得自己来,一点都不开箱即用。
不过反正都是自用的就是了 我怎么写也没人能管我(小声)
关于 Mirai 酱的插件们
涩图插件
插件名:EroPicSender
运行逻辑
flowchart TB askforsetu[/群友找Mirai酱要涩图/] --> checkperm checkperm{检查插件开关} -- 关 -->reject reject[拒绝] checkperm -- 开 --> checkcd checkcd{检查冷却时间} -- 到了 --> getsetudata checkcd -- 没到 --> reject getsetudata[获得涩图数据] --> download[下载涩图本体] --> checktag checktag{检查涩图标签\n(R18之类的)} -- 能发 --> sendwithimg[发送图片] --> withdraw sendwithimg --> checkassets checktag -- 不能发 --> sendwithoutimg[仅发送文字描述] --> checkassets withdraw[等待1分钟后撤回涩图] cache[(涩图缓存)] -.-> getsetudata checkassets{检查涩图缓存} -- 不足 --> 获取一包新的涩图 -.-> cache checkassets -- 充足 --> over[什么都不做] checkassets -.- cache init[/插件初始化/] --> loadcache loadcache[从文件加载保存的缓存] --> checkassets loadcache -.-> cache
(尝试用 mermaid 做了流程图,但是一坨)
- 管理员可以无视插件开关要涩图
- 涩图下载失败也会发送提示
- 涩图发送失败会改为仅发送文字描述
- 涩图发送成功后会在两分钟后撤回
- 程序退出时会保存缓存到文件
图源
来自 Lolicon API,非常感谢它的开发者
明日方舟抽卡模拟
插件名:ArknightsGacha
数据获取
干员列表来自 BiliGame Wiki
把获取的网页用 xpath 一翻,数据就来了
什么?为什么不是 PRTS ?
因为 B 站他直接把数据丢网页源代码里了,大好人属于是
根据干员的获取途径生成中坚和常驻两个池子限定池子理论上也能做,但是群友急了于是先这样了
能够通过指令让 Mirai 酱立即更新干员数据
运行逻辑
插件初始化时会从本地加载保存的干员数据
加载完毕后生成两个字典(代表中坚和标准两个池子),键为星级,值为对应的干员列表
每次请求抽卡时定义一个初始概率表:
1 |
|
很明显 键是星级,值是概率
抽卡会传入一个combo
值,代表连续抽了多少抽没出 6 星。这个值可以经由群员的QQ号查到,用以实现 “若连续 50 发未出 6 星,下次 6 星概率增加 2%,直到第 100 发时必出 6 星” 的设定
抽卡函数会根据上述逻辑对初始概率表进行修改,然后通过带权重的随机来确定抽到的星级,最后用初始化时生成的字典来得到具体抽到的干员数据,返回给负责交互的部分
combo
值由负责交互的那一坨保管,每当抽到 6 星时这个值就重设为0
在收到干员数据之后负责交互的那部分便生成文本,回复给群友
B 站动态转发
插件名:BiliDynamicForwarder
需要配合BiliLogin
插件食用
运行逻辑
每 5 分钟请求一遍监听对象们的历史动态,与上一次请求的数据比对,找出新发布的进行发送↑ emm我的“找出新发布的动态”这个部分似乎还有点问题,时不时就会把历史的某一条动态刨出来发了
如果动态内容命中了黑关键词,那么它将不会被发送;
但是如果同时又命中了白关键词,那么它还是会被发送。
这个插件的交互部分主要就是设置监听对象,逻辑还挺简单的,难写的原因是没有现成的鉴权和指令系统
其余部分都是主动发送了没什么好说的
自动维护一个用 B 站 uid 查昵称的字典,用来方便添加移除监听对象
关于接口
来自易姐的 bilibili-API-collect
,非常感谢 ta
函数封装由我实现,弄得一坨
emm注意到动态获取接口已经有了新的、更优雅的替代方案,但是我懒
萌属性抽取
插件名:MoeAttriLottery
之前写好这个插件的时候还有些沾沾自喜,然后看了别人家的机器人才发现,这其实是几年前就玩过时了的东西w果然是你能想得到的东西,别人早就想到了
数据源
由我整理自萌娘百科-萌属性
运行逻辑
非常简单的带权重的随机抽取而已,一天仅能抽一次
拥有权重机制,比如我能把年龄属性中的“萝莉”调到10
,其他全为1
😋
拥有主副属性机制,当抽到的主属性在副属性中有键时,会再抽一个副属性出来
比如抽到了渐变色瞳,还可以再抽到一个红->蓝渐变的细分支
B站登录
插件名:BiliLogin
因为最近B站对获取动态的接口加了风控,非登录情况下的请求难度大幅提升,于是加紧写了这个插件用于登录
登录后cookies会自动留在bot框架中,B站动态转发插件可以直接使用
仅能通过管理员私聊进行扫码登录,不知道时间久了会不会出问题
emm 大概就是这样,以后应该还会继续维护 Mirai 酱的,有种养女儿的快感 😋
最新情报(x)
(2024年⑨月16日追加)
由于实在是懒得维护那动不动就爆内存的 Android x86,于是 Mirai 酱又被我忘了一段时间。前段时间终于将它彻底扬了部署了一个 Lagrange,计划使用即将推出的 melobot v3 做插件开发,敬请期待(大雾)
- 到底了噢 -