OneBot 行为操作

相关知识

如果你不知道什么是“行为”和“行为操作”,建议先浏览:行为的相关知识

其他行为操作,和 send() 类似,都由对应的行为操作方法产生。关于这些方法和它们的参数,参考 OneBot v11 适配器的 API

它们的用法,与上一篇文章中的消息行为方法基本一致。可在事件处理方法中直接调用。

行为句柄

当直接使用行为方法时,默认是尽快完成的。即不等待也不关心 OneBot 实现端是否成功完成了行为:

# 发送消息而不等待,也不关心是否成功
await adapter.send(...)

# 因此在某些情况下,以下的一系列行为操作可能是无序的:
await adapter.send("我想要这条消息先被看到")
await adapter.send("但是这条可能才是先被发出去的")
await adapter.send("也可能是这条")

# 而且有些行为需要响应数据
await adapter.get_group_list()
# 如何等待返回的数据呢?

此时就需要行为句柄了:

# 使用 with_echo 装饰原行为方法
waited_send = adapter.with_echo(adapter.send)
# 获得一个元组,其中每个元素就是行为句柄
# 因为 melobot 支持多个输出源,因此会有多个句柄
handles = await waited_send(...)

# 如果像教程开始那样,只使用 add_io 添加了一个输入输出源
# 那么就是一个输入,一个输出源。因此取第一个元素即可
hs = await waited_send(...)
handle = hs[0]

# 等待句柄,即是等待被 OneBot 实现端通知行为已完成
# 等待并获取返回值,即是这个行为的响应结果,这在 melobot 中一般称作回应对象
echo = await handle
# 访问需要的响应数据(data 字段与 OneBot 中的数据结构一致)
# 依然建议使用下标访问,因为会有精确的类型注解
msg_id = echo.data['message_id']

关于回应对象,更多请参考 API 文档中的内容:OneBot v11 回应

当然,有时候你可能有大量的行为操作需要等待,那这时可以使用更方便的上下文管理器:

from melobot.protocols.onebot.v11 import Adapter, on_message, EchoRequireCtx

@on_message(...)
async def _(adapter: Adapter):
    with EchoRequireCtx().unfold(True):
        # 全部都会等待
        await adapter.send("我一定先被看到")
        await adapter.send("我一定是第二条消息")

一整个函数都需要等待时,可以使用 unfold_ctx() 装饰器:

from melobot.protocols.onebot.v11 import Adapter, on_message, EchoRequireCtx
from melobot.utils import unfold_ctx

@on_message(...)
@unfold_ctx(lambda: EchoRequireCtx().unfold(True))
async def _(adapter: Adapter):
    # 全部都会等待
    await adapter.send("我一定先被看到")
    await adapter.send("我一定是第二条消息")

提示

不建议频繁等待行为操作。等待总是需要更多时间,大量使用会降低运行效率。

建议只在行为操作必须有序,或需要返回数据时才去等待。

提示

句柄的本质是将操作和等待解耦。由此你可以发散自己的思维来使用它:

例如安排一批操作,后续再集中等待,实现并发操作。

自定义行为

和自定义消息段类似,有时候我们总是会需要自定义的 OneBot 行为类型的。一般这样构造:

from melobot.protocols.onebot.v11.adapter.action import Action

# 临时构造一个自定义行为
action = Action(type="action_type", params={"param1": 123456})

# 继承并构造一个新的 Action 类
class MyAction(Action):
    def __init__(self, param1: int) -> None:
        super().__init__("action_type", {"param1": 123456})

action = MyAction(123456)

# 通过 adapter 的通用 action 输出方法输出
await adapter.call_output(action)

# 需要等待时,这样设置:
action.set_echo(True)
handles = await adapter.call_output(action)

实际上,适配器所有行为操作,都是先在内部构建 Action 对象,再通过 call_output() 输出。

而所有内置行为对象,也可以在文档 OneBot v11 行为类型 中找到。你完全可以手动构造,再使用 call_output() 输出,这适用于更精细的控制需求。

总结

本篇主要说明了行为操作函数的用法,及行为操作的流程控制。

下一篇将重点说明:事件预处理。