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_state() |
| 注册命令回调 | @app.on_cmd() |
| 注册消息回调 | 无需装饰器,默认视为注册消息回调 |
在消息从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也提供了一些有益的属性,在下一章详细介绍这些属性