ErisPulse适配器开发经验分享
这段时间搞了好几个适配器,踩了不少坑。随便聊聊适配器开发的经验吧
适配器是啥
适配器是ErisPulse和各个消息平台之间的桥梁,每个平台的API都不一样,事件格式也千奇百怪。适配器的任务就是把这些"方言"翻译成ErisPulse能听懂的"普通话"(OneBot12标准),然后再把ErisPulse的"普通话"翻译回各个平台的"方言"。
这样用户写的模块就可以在所有平台上跑了,一次编写,全平台运行
SendDSL的设计思路
链式调用的好处
想法是让调用更顺手,提供一定的语法糖让调用事半功倍:
1await adapter.Send.To("group", "123").Text("Hello")
也是可以这样,使用一些修饰方法的:
1await adapter.Send.To("group", "123").At("456").Reply("msg_id").Text("回复@的消息")
修饰方法和发送方法的区别
这点设计的时候想了挺久。
修饰方法(At、Reply、AtAll)返回self,这样才能继续链式调用。发送方法(Text、Image这些)返回asyncio.Task,这样用户就能自行选择是否等待结果之类的了
其实这也有技术债上的影子(,,, 如果现在设计的话,会在最终添加一个 .send() 总方法再进行链式的最终发送,而不是一个最终发送方法 保持兼容性的话,就只能这样了
1class Send:
2 # 修饰方法返回 self
3 def At(self, user_id: str):
4 self._at_users.append(user_id)
5 return self
6
7 def Reply(self, msg_id: str):
8 self._reply_to = msg_id
9 return self
10
11 # 发送方法返回 Task
12 def Text(self, text: str):
13 return asyncio.create_task(
14 self._adapter.call_api(
15 endpoint="/send",
16 content=text,
17 recvId=self._target_id,
18 recvType=self._target_type
19 )
20 )
为什么返回Task而不是直接await
这个也是考虑了很久。
如果发送方法直接await,那每次调用都会阻塞,返回Task的话,用户可以选择:
1# 不等结果,后台发送
2adapter.Send.To("user", "123").Text("Hello")
3
4# 或者等结果
5await adapter.Send.To("user", "123").Text("Hello")
6
7# 或者先保存Task,稍后等待
8task = adapter.Send.To("user", "123").Text("Hello")
9# ... 做其他事 ...
10result = await task
开发过程中的踩坑经历
WebSocket事件提交的异步链路
这个坑真的踩了好几次www
最开始做的时候,以为事件提交就是同步的,结果发现整个事件链路都是异步的。如果不注意,事件提交的顺序可能会乱,甚至丢失。
正确的做法是每次事件提交都用Task:
1async def _ws_handler(self, websocket):
2 async for message in websocket:
3 # 转换事件
4 event = self.convert(message)
5
6 # 提交事件到框架
7 asyncio.create_task(
8 self.sdk.adapter.emit(event)
9 )
或者每个ws_handler都用Task
这样每个事件都在独立的Task里处理,互不干扰,也不会阻塞WebSocket的接收循环。框架会保证事件的顺序处理
多账号管理的麻烦事
这个是OneBot11适配器搞出来的。
OneBot11支持多个Bot实例,每个Bot有自己的账号ID。适配器要能指定用哪个Bot发消息:
1# 用默认Bot发
2await adapter.Send.To("group", "123").Text("Hello")
3
4# 用指定Bot发
5await adapter.Send.Using("bot1").To("group", "123").Text("Hello")
一开始以为就加个Using方法就行,结果发现call_api方法也要知道用哪个Bot调用API,配置也要分开管理,连日志都要标明是哪个Bot的。
最后搞了个复杂的配置结构,才勉强搞定。
参考现有适配器
云湖、Telegram、OneBot11这几个适配器的代码都是开源的,遇到问题可以看看是怎么处理的。
特别是OneBot11适配器,配置和账号管理比较复杂,可以参考一下实现,但ob11的适配器是只有ws的,没有webhook。webhook的适配器实现确实简单了一些,也可以参考云湖的实现,如果是pulling的话,可以参考Telegram的实现
评论