我的 Sorabot 设计理念

我的 Sorabot 设计理念

缅怀 Sorabot #

最初的 Sorabot 已经死了,2020-02-29 -> 2023-12-25。
11 月的时候,羽希刚过完生日便决定终止这个项目,早在半年前,羽希就在 100oj 的 matrix 空间里表达过终止机器人的意向。
更早的诱因大概从羽希开始将 linux 作为主力系统便埋下了。
终止 Sorabot 有很多原因,主因不是技术原因。
羽希精神状态不好,不想再投入心智成本;羽希完全不使用 QQ,调试起来太麻烦了。

Polaris_Light 开源的 新的机器人 目前在社区里活跃。
后续发展要看社区的反馈土壤和维护者的心力,祝好。

Sorabot 是什么 #

Sorabot 是一个 QQ 机器人,设计初衷是服务 steam 游戏 100%OrangeJuice 的中文玩家社区。
后来 Sorabot 的一部分功能转移到网站 100oj.com 上,诱使了羽希去构建另外的橙汁资料管理系统、网站、数据库、以及其他橙汁工具。
Sorabot 目前的使用说明文档托管在 这里
下文中提到的 Sorabot 都只指代 QQ 机器人,而不指代其后端的网站、数据库等内容。

起初是 2020 年 2 月,羽希被封在家里无聊搓的。
动机很简单,羽希有服务器可以挂载,玩橙汁,使用 QQ,有一定的动手能力。
现在回看过去,这个项目训练了羽希不少运维能力,也饲喂很多好奇和虚荣。

历史记录 #

Sorabot 的开发历史记录于 2 个地址:

  • 2020 -> 2022: 记载在羽希的私人 discord。
  • 2022 -> 2024: 将旧的 discord 内容整理成 markdown 文件归档。新的内容随 git commit 内容记载在羽希的私人 gitlab 仓库。

羽希稍后会将旧的日志不作修改的发布到羽希的 Alist。 这部分的日志是 Sorabot 一周目时期的,Sorabot 一共经历过两次重构。
酷Q,C语系 -> Mirai,jvm(使用本地数据) -> Mirai,使用网站和数据库


功能构想 #

到目前为止,Sorabot 除了使用指南里提到的功能以外,还包括一个名为 hime 的 discord-selfbot,一些爬虫。
挑几个功能来讲讲羽希的设计理念,或者说这么做的原因。

以下原则不区分先后顺序:

  1. 所有的模块默认应当是关闭的,由管理员决定是否开启
  2. 管理员分为群内的 qq群管理 和 机器人操作员(op)
  3. 操作员分为拥有全部权限的 超级管理 和仅拥有部分权限的 次级管理
  4. 长消息和包含多条消息的内容,必定使用 转发聊天
  5. 功能性的内容应当尽量拆分到与机器人本身无关的组件上(后端分离)
  6. 来自 discord 的消息不受这些原则制约
  7. 所有功能都一定有纯文本格式,而且优先制作和使用纯文本的格式
  8. 贪婪的组件拥有贪婪的触发正则
  9. 越是经常被使用的组件、越是重要的组件,触发应当越简洁,越简洁越好(另一方面,正则也应该更加贪婪)
  10. 话语应当简洁明了,尽量减少来自用户的消息次数

1、4: #

QQ 作为一个 没有话题分割功能 的 im 软件,所有消息以瀑布流的形式呈现在时间线上,同一时间能容纳的话题数量非常小。
一旦出现长文本和多条消息会极大降低聊天体验,打破话题的专注度。发散的聊天话题会反射到发散的社区结构。

一个好的社区 im 软件应当至少包含两级目录,
在 matrix 表现为 空间>房间>串
在 telegram 表现为 群组>子区/订阅性的频道>串
在 discord 表现为 服务器>频道大区>频道>论坛/子区

telegram 和 matrix 在群组或房间内都有消息串的概念,可以在消息串内针对同一个话题进行回复。discord 层级更为鲜明,不仅可以使用频道划分话题的大方向,还可以针对不同的需求设置子区和论坛。
话题越是松散,社区越是松散。


4、5: #

一方面,在一款极具华夏特色的 im 软件上做机器人,羽希不忍心把功能性内容和机器人强耦合到一起。
另一方面,羽希觉得很多功能「它就该做成网站内容,它就不应当以聊天流的方式呈现,这太 tm 蠢了」,不管是写起来,还是呈现出来的效果,都太 tm 弱智了。

举例来说,Sorabot 的最开始的查询统计信息的功能,是以文本形式呈现的。
最开始哪有图给你画啊,fbf 的统计命名一个个跟天书一样,今年刨出来个甲骨文统计破译半天,下次更新成篆书,命名格式既不规范又不统一;时不时还出点差错把统计记录错位了、统计名敲错了个字符、删俩人物的统计、、、。
当初仅仅是整理资料就很辛苦了。
一个人的角色胜率,一行一个很影响观感,而且还有其他杂七杂八的内容。

这里还有一个问题,QQ 是不支持富文本的,不要说是富文本了,QQ 连自己家的组件都显示不好:

此消息类型无法查看
此消息只能在最新手机 qq 上查看
[聊天表情]
[qq红包]
[戳一戳]请打开最新手机 qq 查看

你在 qq 上面 at 个人你甚至都分不清,文本里哪些是那个人的名字 哪些是你发的字


7: #

机器人发送文本消息应当是最快的,响应最快,构建最快,上传最快,用户收到有用信息最快。
文本组件会被羽希优先考虑设计和使用。

另一个例子是卡牌信息的查询->

7、8、9: #

羽希认为,机器人的核心功能就是查卡; 卡牌作为 100oj 这个骰子游戏的核心存在,卡牌的信息应当是玩家和社区关注信息的核心之一,这也是一个反射结构。

所以,查卡拥有所有功能中最简洁的触发方式,卡牌名字的正则匹配是最贪婪的。
查卡的触发原则是,如果任意卡名的正则,被用任意数量个空格包裹住前后,出现在文本的任意位置,就会被触发。
通俗来讲下面几种方式都会触发 ->

  • 你说了一长段话,把这段话里的所有卡名前后加上了个空格
  • 「n m d 怎 么 有 人 说 话 带 空 格」会匹配优空
  • 你什么也没说,单纯发了个「空格+冲刺」
  • tish tsih fish shit 全都能匹配上杏
  • 梅斯卡尔 煤撕卡尔 酶铌 煤泥 冰淇淋 冰激凌 冰麒麟 冰欺凌

匹配的贪婪体现在你游玩家不论在口糊什么,不论用了什么错字别字,你就是小学没毕业卡名忘了打个大雪糕板子也能查出卡来,Sorabot 永远知道你在说什么。
这样的一个正则表需要长时间的维护,并且需要有游戏经验的人不断在社区进行沟通。
匹配的吝啬则在于,为了尽量防止正常聊天被勿触,不该有的东西不要有。
而空格是个很好的选择,因为正经人说话谁带空格啊?很好的避免了勿触, 因为空格在绝大多数键盘上都有,在大多数手机上不用二次切换键盘模式,可以最快速的打出来。
其他的通配符很可能在某些平台需要二次甚至更多次的切换输入模式。

同一条消息会触发不止一条的卡文,遵循原则 4,这当然是转发消息。

由于核心是卡牌,卡牌的信息正确性是羽希维护工作的首要内容。
卡牌消息一定会随官方更新游戏而实时更新。

这里引出了一个问题:最初的机器人所有实质性内容是以配置文件的形式保存在本地的,主要是 json。
羽希不仅有机器人这一个橙汁项目,也不仅是一个地方要用到橙汁的资源内容。
机器人的回复用文本、回复的卡图、组卡器的文本和卡图、生成统计界面用到的人物资料、以及鸽了的 wiki 和 wiki 维护脚本/机器人、杂七杂八的其他项目比如什么橙汁老黄历/二择喜爱度排名/whereisoj/地图操作工具等等。
最终的设计决定是,它们都不采用「一个项目一个资源库」,而是全部共用同一个数据库。
一次更新,到处应用
这一设计使得 100oj.com 可以批量快速的生成大量同质化内容,也方便后续写接口对接其他程序。

「为什么卡牌的主键采用翻译键,而且不另外设计一个简单的唯一识别符? 比如数字编号。」

  1. 翻译键已经有「被游戏本身」使用这样一个使用案例
  2. 翻译键几乎不会变更(数年变更一个)
  3. 几乎没有重复(同组内的上千个的翻译键,只有一张合作卡是被复用的)
  4. 所有语言所有其他程序都可以根据这个同样的原则达到同样的结果
  5. 在基于 1-4,fbf 的键命名非常不规范非常随意
  6. 在基于 1-5,游戏常量里找不到更合适的作为识别符的 flag

设想,假设羽希自己给每张卡编了号来作为识别用键,编号原则称之为「羽希规则」,那么其他人的程序想要对接的时候,就要经历「羽希规则」的学习成本、写的程序会有对应的技术债。
如果其他人也有自己的「其他规则」,十个项目就会有十个给卡牌编号的规则,使得相互统一对接几乎不可能。
如果通过给「不同原则」打补丁的方式进行对接(或者「不同原则」本身要随着游戏更新打补丁),那为什么不一开始就选择一个大家都能找到依据的键呢。


9、10: #

每条指令的用法,几乎都被写成除了全名,还可以使用简称。
在说明文档里,使用不同的括号来区分参数和参数名本身。
这两点符合 posix 指令的设计要求。

获取玩家统计的消息:
说明文本的消息如下 ->

本命令用于生成百橙玩家的个人统计数据图片。
使用前请确认自己steam的隐私设置。steam->编辑个人资料->隐私设置->游戏详情,设置为公开|所有人可见。(该设置有一定的延迟。)
使用方法>
#stat [steam64id] [行数。可不填,默认为5]
#stat bind [steam64id]    用于将当前qq绑定到对应steam账户。重复使用会更新绑定。
#stat unbind    删除自己的绑定。
#stat me [行数。可不填,默认为5]   在qq绑定steam64id后,使用本命令来快速生成自己的资料。
#stat type [类型编号]   更改出图类型。

生成失败时给出具体原因,尽量打消用户的疑惑。
在能通过 steamapi 查询到隐私状态的情况下给出具体的 2,并且给出具体操作。

生成失败!可能原因>
1>网络原因,本机无法连接至steam。
2>请将steam->编辑个人资料->隐私设置->游戏详情,设置为公开|所有人可见。(该设置有一定的延迟。)

在绑定时,优先校验用户输入的 steam64id 是否合规。
不合规则给出具体示例。

这不是一个有效的steam64id。它应该是以7656开头、总长度为17位的纯数字。
如果你不知道什么是steam64id,推荐在网络上了解更多的相关知识。

「不把话说成这样,你永远不知道你的用户有多抽象。」

  • 见过带括号一起发的,这已经是最正常的了。
  • 有把参数名当作参数本身发的,这位更上一层楼。
  • 还有更重量级的,把你机器人发的东西复读一份,或者复读一部分的。
  • 半角全角混着发,说话或者名字里带零宽字符、带输入控制符,乱打空格,打错单词。
  • 上面全都踩一遍,然后来 cue 你机器人是不是死了,为什么没反应。

2、3: #

羽希主张,机器人的管理和社区的管理应当分开。
这就好像玩 mc 服务器时候,运维服务器的 op 不一定是声望比较高的玩家,而是拥有更多服务器 debug 经验、更了解游戏运行的技术人员。
它们可能确实有很大的重叠度,但绝不混淆两者。
群管理可以开关机器人的模块,因为群管理是社群话题的主导者,机器人作为话题工具,显然应当给予主导者工具的支配权。
而操作员可以进行机器人模块里细节信息的调整,比如设置正则回复、查询和操作数据库。


6: #

discord 作为一款优秀的社区 im 软件,包括 fbf 在内的很多平台把社交流托管在上面。
要获取 100oj 最新的游戏消息,除了 steam 新闻,就是做 discord 机器人了。
由于添加机器人需要管理员权限,而羽希在 fbf 的 discord 没什么分量,所以选用了自己不熟悉的 基于 node.js 的 selfbot。
将个人账户托管为机器人来监听和转发消息,这个机器人就名为 hime
hime 会实时转发来自 fbf 服务器的新闻、剧透内容、社区大奖等消息,也会转发来自阿凄的服务器的画廊图片。
涉及到 twitter 和 pixiv 的链接时,则会再动用另外两个爬虫去获取对应链接里的图片。


除了上面提到的话题分割应当至少包含两级目录最基础的富文本,这里再来对比一下 matrix、discord、telegram 和 qq 在设计上的参差。

产品都不是一日之功,能否满足用户需求、能否跟进用户反馈会很大程度上决定产品的后续发展。开放的社区会比封闭的社区拥有更多的反馈优势。

discord 拥有最好的话题分割;
telegram 拥有最好的外部 api 和机器人等自动化内容。这一点 discord 也很强,但稍不及 tg。matrix 则是有条件,但成品不多,很多东西要自己造轮子。

聊天的基本功能,比如富文本、表情/贴纸、对消息进行 emoji 反应、url 预览、外部组件、小文件传输,前三者各有各的实现。

Sorabot 和橙汁的相关工具是羽希接触很多内容的契机 #

java 水平有所提升,接触学习了 kotlin,一些前端知识,html、css、js、学习了 jquery 和 php,数据库相关的 mysql、jdbc,网络相关的、、、
羽希使用 linux、持续写代码的两个比较大的诱导因素,其一为 Minecraft,其二便是橙汁了。