事件与事件处理

相关知识

如果你不知道什么是“事件”,建议先浏览:事件的相关知识

绑定方法与处理方法

除了刚才使用的 on_start_match() 方法,还有很多类似的方法,可以用于绑定事件处理的逻辑。

本文档将这些方法称为“绑定方法”,同时将绑定方法绑定的函数称为:“处理方法”或“处理函数”。

这些绑定方法的参数很多,你可以先简单浏览。关于这些方法的使用,后续会详细讲解。现在让我们先学习一些基础知识。

绑定方法的使用都一样,直接用作装饰器即可:

from melobot import BotPlugin
plugin = BotPlugin("test", "1.0.0")

@plugin.on_start_match(...)
async def func() -> None:
    # func 就是事件处理方法,必须为异步函数,且无返回值
    # func 的内容就是事件处理的逻辑
    ...

获取事件对象

现在我们已经学会绑定事件处理方法了。如果事件处理方法,能获得触发事件的一些信息,基于这些信息做针对性处理就更好了。

通过 any_event()msg_event()req_event()notice_event()meta_event() 函数,即可在事件处理方法中获得触发的事件。获得的事件是一个事件对象。

当然这五个方法都只做一件事:获得当前的事件。它们唯一的不同在于,返回值的类型注解不一样。建议在指定类型的事件处理方法中,用对应的事件获取方法。这样可以获得精准的类型提示:

@plugin.on_event(...)
async def func1():
    # on_event 绑定的处理方法,可能被各种类型的事件触发
    # 使用 any_event 可以获得所有事件类型的补全提示
    e = any_event()
    ...

@plugin.on_start_match(...)
async def func2():
    # on_start_match 绑定的处理方法,只可能被消息事件触发
    # 使用 msg_event 可以获得消息事件类型的补全提示
    e = msg_event()
    ...

不同类型的事件对象有哪些属性和方法可用,请参考:事件类型

所有类型的事件对象,都可通过 raw 属性获得原始的 OneBot 标准事件字典。这可能在极少数情况下有用。一般使用事件对象的属性和方法即可。

基于事件信息的处理逻辑

通过事件对象提供的信息,可以实现更有趣的处理逻辑:

 1"""
 2通过消息事件对象的 time, sender.id 属性,产生不同的处理逻辑
 3"""
 4from datetime import datetime
 5from melobot import BotPlugin, ForwardWsConn, MeloBot, msg_event, send
 6
 7plugin = BotPlugin(__name__, "1.0.0")
 8# 机器人主人的 qq 号
 9OWNER_QID = 1574260633
10
11@plugin.on_start_match(".hello")
12async def say_hi_to_owner() -> None:
13    e = msg_event()
14    # 如果消息不来源于机器人主人,则不做任何处理
15    if e.sender.id != OWNER_QID:
16        return
17    # 否则根据时间,回复不同的内容
18    hour = datetime.fromtimestamp(e.time).hour
19    if 0 <= hour < 6:
20        await send("主人凌晨好~")
21    elif 6 <= hour < 12:
22        await send("主人早上好~")
23    elif 12 <= hour < 18:
24        await send("主人下午好~")
25    else:
26        await send("主人晚上好~")
27
28if __name__ == "__main__":
29    bot = MeloBot(__name__)
30    bot.init(ForwardWsConn("127.0.0.1", 8080))
31    bot.load_plugin(plugin)
32    bot.run()

消息事件相关

提示

以下代码示例中的 on_xxx 代指各种事件绑定方法,实际并不存在名为 on_xxx 的方法。

对于消息事件,有一些额外的方法可以使用,这里先说最常用的 msg_text()

一个消息事件就象征一条消息,它最重要的信息就是消息内容。消息中的文本内容使用 msg_text() 获取:

from melobot.context import msg_text

@plugin.on_xxx(...)
async def _():
    # 获取消息中的文本内容
    text = msg_text()  # 等价于:msg_event().text

如果要获取多媒体内容,例如图片、表情等,可以使用 get_segments() 方法获得多媒体内容对应的消息段对象列表

相关知识

如果你不知道,消息段对象是消息内容的表示方式之一,建议先浏览:消息内容的数据结构

需要哪种类型的消息段,就传递哪种消息段的 type 作为参数:

from melobot.context import msg_event

@plugin.on_xxx(...)
async def _():
    e = msg_event()
    # 获取当前这条消息所有的图片(image 是图片消息段的类型名)
    images = e.get_segments("image")
    # 获取当前这条消息所有的 qq 表情(face 是 qq 表情消息段的类型名)
    faces = e.get_segments("face")

随后,读取列表中消息段对象的 data 字段可获得相关数据:

# 遍历所有图片的 url 和文件名
for img in msg_event().get_segments("image"):
    print(img["data"]["url"])
    print(img["data"]["file"])

相关知识

所有媒体内容对应的消息段表示:OneBot 消息段

此链接也包含了每种消息段的 type 标识,及 data 字段拥有的参数。

如果只需要 data 字段的某一参数,使用 get_datas() 即可:

@plugin.on_xxx(...)
async def _():
    # 获取当前这条消息,所有图片的 url
    img_urls = msg_event().get_datas("image", "url")

上下文的自动传播

实际上,以上所有获取事件相关信息的方法,可以在任意深度的,同步或异步函数调用中使用。

只要这些函数的调用都是事件处理方法发起的,它们就能获得对应的上下文信息:

@plugin.on_xxx(...)
async def f1():
    e = msg_event()      # ok
    # 上下文自动传播给同步函数 a
    a()

def a():
    text = msg_text()    # ok
    # 上下文自动传播给异步函数 b
    await b()

async def b():
    e = msg_event()      # ok

当然,你也可以显式传参:

@plugin.on_xxx(...)
async def func1():
    e = msg_event()
    func_a(e)

def func_a(event):
    ...

使用的相关方法太多,不想一个个导入?没问题,我们可以直接导入模块!

from melobot import context as ctx

@plugin.on_xxx(...)
async def _():
    e1 = ctx.any_event()
    e2 = ctx.msg_event()
    text = ctx.msg_text()

习惯事件处理方法接受参数?喜欢像 nonebot2 那样的依赖注入?没问题,我们也支持!

from melobot.context import msg_event, any_event, msg_text

@plugin.on_xxx(...)
async def _(e1 = any_event(), e2 = msg_event(), text = msg_text()):
    ...

总结

本篇主要说明了如何绑定事件处理方法,以及如何通过事件对象的属性和方法,来实现更丰富的处理逻辑。

下一篇将重点说明:如何构造和发送各种消息。