Skip to main content

事件总线

事件总线是一个简单的消息队列,用于接收、广播、处理事件。

创建与使用事件总线

EventBus 定义在 mirai.bus 模块中,也存在于 mirai 命名空间。

from mirai import EventBus

bus = EventBus()

使用 on 装饰器定义事件处理器。

on 接收两个参数,第一个是事件名称,第二个是优先级。

@bus.on('MyEvent')
def my_event_handler(event):
print('MyEvent:', event)

bot.on 相同,事件处理器可以是同步的或异步的。

使用 emit 发送事件。

await bus.emit('MyEvent', {'foo': 'bar'})

事件传播

事件总线通过事件链实现事件传播。当一个事件被触发时,它所在的事件链的所有事件也会被触发。

事件总线在创建时,可以接受一个参数 event_chain_generator,规定事件链如何构建。event_chain_generator 是一个函数,它接收事件名,返回一个生成事件链中各事件的生成器。

例如,如下代码规定了按点号分隔的事件链:

def func(event: str):
while True:
yield event
event, *sub_event = event.rsplit('.', maxsplit=1) # 由下到上依次触发
if not sub_event: # 顶层事件触发完成
break

bus = EventBus(func)

@bus.on('MyEvent')
def my_event_handler(event):
print('MyEvent:', event)

@bus.on('MyEvent.SubEvent')
def my_sub_event_handler(event):
print('SubEvent:', event)

await bus.emit('MyEvent.SubEvent', {'foo': 'bar'})

在上面的代码中,MyEvent.SubEvent 触发时,根据事件链生成器,得到的是事件链是 ['MyEvent.SubEvent', 'MyEvent'],因此,事件总线会依次执行 MyEvent.SubEventMyEvent 的事件处理器。

实际应用中,我们提供了对这一类事件链生成器的封装:

bus = EventBus(event_chain_generator=event_chain_separator('.'))

这和上面的代码等价,都是按点号分隔的事件链。

另一种常用的事件链是继承事件链。由于继承关系涉及到了事件的具体内容,按照设计逻辑,继承时间链隶属于模型事件总线。

模型事件总线与继承事件链

模型事件总线定义于 mirai.models.bus 模块,它拓展了事件总线的功能,使其支持使用事件类型注册和触发事件,以及支持继承事件链。

继承事件链是基于事件的继承关系,包含事件及所有父事件的事件链。

例如:FriendMessage 的事件链为 ['FriendMessage', 'MessageEvent', 'Event']

以下是 YiriMirai 定义的事件继承关系:

  • Event
    • BotEvent
      • BotOfflineEventActive
      • BotOfflineEventDropped
      • BotOfflineEventForce
      • BotOnlineEvent
      • BotReloginEvent
    • CommandEvent
      • CommandExecutedEvent
    • FriendEvent
      • FriendInputStatusChangedEvent
      • FriendNickChangedEvent
    • FriendRecallEvent
    • GroupEvent
      • BotGroupPermissionChangeEvent
      • BotJoinGroupEvent
      • BotLeaveEventActive
      • BotLeaveEventKick
      • BotMuteEvent
      • BotUnmuteEvent
      • GroupAllowAnonymousChatEvent
      • GroupAllowConfessTalkEvent
      • GroupAllowMemberInviteEvent
      • GroupEntranceAnnouncementChangeEvent
      • GroupMuteAllEvent
      • GroupNameChangeEvent
      • GroupRecallEvent
      • MemberCardChangeEvent
      • MemberHonorChangeEvent
      • MemberJoinEvent
      • MemberLeaveEventKick
      • MemberLeaveEventQuit
      • MemberMuteEvent
      • MemberPermissionChangeEvent
      • MemberSpecialTitleChangeEvent
      • MemberUnmuteEvent
    • MessageEvent
      • FriendMessage
      • GroupMessage
      • OtherClientMessage
      • StrangerMessage
      • TempMessage
    • NudgeEvent
    • RequestEvent
      • BotInvitedJoinGroupRequestEvent
      • MemberJoinRequestEvent
      • NewFriendRequestEvent
    • OtherClientEvent
      • OtherClientOnlineEvent
      • OtherClientOfflineEvent

模型事件总线仅支持继承事件链,在创建时不再接受事件链生成器参数。

模型事件总线支持用事件类型调用 on 方法,也可用事件名字符串。

from mirai.models.bus import ModelEventBus

mbus = ModelEventBus()

@mbus.on(GroupMessage) # 或 @mbus.on('GroupMessage')
async def handle_group_message(event: GroupMessage):
print('GroupMessage')

@mbus.on('MessageEvent')
async def handle_message_event(event: MessageEvent):
print('MessageEvent')

不论使用事件类型还是字符串,事件都会按照继承事件链传播。比如上例中,收到群消息时,handle_message_event 也会被触发。

模型事件总线的 emit 方法仅能发送类型为 Event 的子类的事件。

await mbus.emit(Event(type='Event'))

模型事件总线提供了 base_bus 属性,用于访问其内部的普通事件总线。

快速响应

如果事件处理器返回了一个 awaitable,事件总线会把它作为一个 Task 开始运行。

这可以用于快速响应事件,比如:

@mbus.on(FriendMessage)
def handle_friend_message(event: FriendMessage):
return bot.send_friend_message(event.sender.id, [Plain('Hello, World!')])

自定义事件

事件总线

事件总线的 emit 方法可以发送自定义事件。

@bus.on('CustomEvent')
def handle_custom_event(x, y, z):
print('CustomEvent', x, y, z)

await bus.emit('CustomEvent', 1, 2, 3)

自定义事件可以携带一个或多个参数。

模型事件总线

使用模型事件总线时,自定义事件需要继承 Event 类。

class CustomEvent(Event):
type: str = 'CustomEvent'
param: int

@mbus.on(CustomEvent)
def handle_custom_event(event: CustomEvent):
print('CustomEvent', event.param)

await mbus.emit(CustomEvent(param=1))

继承 Event 类时,需要定义 type 属性,并指明默认值为事件名。