Ayaka是如何工作的?¶
可能一些概念暂时无法完全理解,请向后看,因为一些概念需要在不同的地方多次阐述才能讲解清楚
插件、群组、消息¶
在ayaka
的世界中,插件、群组、消息的关系如下
AyakaGroup¶
当ayaka
收到一个新的群组的消息时,就会自动新建一个AyakaGroup
对象(后简称为group
),该对象内部保存有一个状态指针和一个字典(用来保存该群组内的临时数据,bot重启后丢失)
AyakaApp、AyakaState和唯一状态树¶
每个ayaka衍生插件
里都有一个或若干个AyakaApp
对象(后简称为app
)。在插件代码加载阶段,生成这些app
的时候,ayaka
会根据它们身上注册的回调函数的情况,生成唯一一棵状态树,所有的状态结点都位于这棵状态树上
而group
内部的状态指针
则指向该树上的某个结点,表示该群组当前所处的状态。默认指向root
状态(又叫闲置状态)
状态转移¶
状态可以通过调用app.goto()
或app.set_state()
方法进行转移,状态转移时,如果存在相应的进入回调
和退出回调
,还会顺次执行它们
从root
状态,变更为root.插件2
状态,再变回root
状态(相当于打开了插件2后又关闭插件2)
从root.插件2.游戏中.出牌
状态,变更为root.插件1.睡眠
状态:
从root
状态转移到root.插件2
状态,相当于群组开启了插件2,从闲置状态(没有任何插件在运行)转移到运行插件2的状态
从root.插件2
状态转移到root
状态,相当于群组关闭了插件2,恢复到闲置状态
命令回调、消息回调、上溯查找和阻断¶
每个状态下都可以注册命令回调
(对具有命令抬头
(例如#
)的消息进行响应)和普通消息回调
对应的装饰器:
说明 | 代码 |
---|---|
在闲置状态下进行注册 | @app.on_idle() |
在指定状态下进行注册 | @app.on_state() |
注册命令回调 | @app.on_cmd() |
注册消息回调 | @app.on_text() |
在消息从ayaka
来到group
后,ayaka
会根据group
当前状态指针所指的状态,进行上溯查找
,查找满足触发条件的回调来处理消息
假如当前状态是root.插件2
,但是在该状态中并没有找到符合的命令回调(例如:消息是#睡觉
,但是root.插件2
并没有命令为睡觉的命令回调),则继续查找父状态,可一路上溯至root
结点
若均没有符合的命令回调,则该消息(即使有命令抬头)退化成普通消息来处理,并且处理方式同样是一路上溯至root
结点
注意,对于状态结点而言,其命令回调和消息回调可能有0个,1个或若干个
若为0个,则说明不符合,需要继续上溯查找,直到root
结点
在注册回调的时候同时可以指定是否阻断,注册回调时均默认阻断
对应装饰器:
说明 | 代码 |
---|---|
不阻断 | @app.on_no_block() |
阻断 | 无需装饰器,默认为阻断 |
若有符合的回调,但是其设置为不阻断,那么在执行了该回调后,ayaka
会继续上述的查找流程,直到结束
deep¶
但是上节所说的上溯查找
被施加了一定的限制
大部分情况下,我们不会期望父状态结点的回调会在群组处于子状态时被触发,它们就像是幽灵一样,令人困惑
因此,这些回调在注册时,默认其可向下影响的深度为0,也就说,它对下一层状态(以及更深的状态)来说是不可见的,因而上溯查找时也不会找到它
如果设置为1,则其对直系子结点可见,对孙结点不可见
如果设置为any,则其对所有后代结点可见
对应装饰器:
说明 | 代码 |
---|---|
设置深度为any | @app.on_deep_all() |
设置深度为n | @app.on_deep_all(n) |
设置深度为0 | 无需装饰器,默认为0 |
这些AyakaState到底有什么用呢?¶
提供独立的命令空间
通过这些状态,把各种各样的回调隔离开,不再会有 一人高呼万人响应 的尴尬
不同状态下,可以保存有相同命令的不同回调。这些回调仅在对应状态下才会响应指令,执行任务。这样保证了命令空间的纯净,各个插件的指令不会相互污染
同时,通过deep
和上溯查找
的特点,又可以让一些父状态的方法复用给子状态使用,兼具灵活性
这些AyakaGroup到底有什么用呢?¶
提供独立的数据空间
显然,不同群组的状态是独立的,互不影响的,仅一点就这需要我们设计AyakaGroup
来保存不同群组的状态
同时,不同群组还有保存其他数据的需求,比如某投票插件需要保存目前所有参与人员的投票情况,显然不同群组的投票必须是独立的,这同样需要AyakaGroup
来保存不同群组的数据
这些AyakaApp到底有什么用呢?¶
通过AyakaApp
,把上述概念都打包到一个app
身上,只需调用相应装饰器注册回调即可实现上述效果
同时,app
也提供了一些有益的属性,在下一章详细介绍这些属性