OneBot 消息操作

相关知识

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

消息操作作为 melobot 中最主要的行为操作,十分常用。

单条消息的构造

一般来说,发送纯文本内容是最普遍的,可以直接使用 melobot 的通用文本输出接口:

from melobot import send_text
from melobot.protocols.onebot.v11 import on_message

@on_message(...)
async def _():
    await send_text("你好啊")

也可以使用具体协议的适配器的接口:

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

@on_message(...)
async def _(adapter: Adapter):
    await adapter.send("你好啊")

如果要发送多媒体内容,则只能使用适配器的 send() 接口。首先构造消息段对象,然后传入 send() 作为参数。例如:

from melobot.protocols.onebot.v11 import Adapter, on_message
from melobot.protocols.onebot.v11.adapter.segment import ImageSegment

@on_message(...)
async def _(adapter: Adapter):
    # 构造一个“图片”消息段,然后发送
    img = ImageSegment(file="https://www.glowmem.com/static/avatar.jpg")
    await adapter.send(img)

或者传递原始的 OneBot v11 消息段字典:

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

@on_message(...)
async def _(adapter: Adapter):
    # 构造一个“图片”消息段字典,然后发送
    img = {
        "type": "image",
        "data": {
            "url": "https://www.glowmem.com/static/avatar.jpg"
        }
    }
    await adapter.send(img)

其他消息段参考:消息段对象

单条消息中,自然可能有多种类型的消息段同时存在。此时这样处理:

from melobot.protocols.onebot.v11 import Adapter, on_message
from melobot.protocols.onebot.v11.adapter.segment import ImageSegment, TextSegment

@on_message(...)
async def _():
    # 例如文本和图片同时存在:
    await send([
        TextSegment("给你分享一张图片哦,这是 melobot 项目作者的头像"),
        ImageSegment(file="https://www.glowmem.com/static/avatar.jpg")
    ])

消息段的继承关系

在文档中,你会发现诸如 ImageSegment 这样的类型,还会存在子类(ImageSendSegmentImageRecvSegment)。这是为了提供更加精准的类型注解而产生的。所有这些类型只要文档中有 __init__ 方法,即可手动实例化并使用。

自定义消息段的构造

通过 Segment 创建自定义消息段

例如在某些 OneBot 实现端,支持一种自定义的消息段:touch 消息(戳一戳,双击头像)。对应的消息段数据结构如下:

{
    "type": "touch",
    "data": {
        "id": "1574260633"
    }
}

如何让 melobot 发送这种自定义的消息段?非常简单:

from melobot.protocols.onebot.v11.adapter.segment import Segment
# 临时构造自定义消息段
seg = Segment(type="touch", id="1574260633")

或者构造一种新的消息段类型:

from melobot.protocols.onebot.v11.adapter.segment import Segment
from typing import Literal, TypedDict

class _TouchData(TypedDict):
    id: str

# 返回新类型 TouchSegment
# 后续事件的消息段创建过程,会自动将对应消息段初始化为 TouchSegment 类型
TouchSegment = Segment.add_type(Literal['touch'], _TouchData)
# 使用新的消息段类型来构造:
seg = TouchSegment(id="1574260633")

单条消息的其他发送方法

send() 可根据当前触发事件,自动定位向何处发送消息。如果想要自定义发送目标,也很容易。只需要将 send() 换成 send_custom() 即可,它的第一参数与 send_custom() 完全相同。

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

@on_message(...)
async def _(adapter: Adapter):
    # 发送一个自定义目标的私聊消息,user_id 为 qq 号
    await adapter.send_custom(..., user_id=1574260633)
    # 发送一个自定义目标的群聊消息,group_id 为群号
    await adapter.send_custom(..., group_id=535705163)

获取 CQ 字符串

除使用消息段对象外,也可以使用CQ 字符串直接表示单条消息的所有消息内容。但只能从消息段对象生成 cq 字符串:

from melobot.protocols.onebot.v11.adapter.segment import ImageSegment

img_cq: str = ImageSegment(file="https://example.com/test.jpg").to_cq()

警告

CQ 字符串存在注入攻击的安全隐患。因此 melobot 不提供将 cq 字符串转为消息段的方法,也不允许接口直接发送 cq 字符串。

转发消息的构造

相关知识

如果你不知道转发消息的表示,主要依托于转发消息段和消息结点,建议先浏览:转发消息与消息结点

转发消息段构造

构造转发消息段:

from melobot.protocols.onebot.v11.adapter.segment import ForwardSegment

# forward_id 是转发 id,可通过消息事件的 get_datas("forward", "id") 获得
seg = ForwardSegment(forward_id)

此时,seg 变量已经是一条转发消息的等价表达了,直接使用适配器的 send()send_custom() 发送即可。

消息结点构造

构造合并转发结点:

from melobot.protocols.onebot.v11.adapter.segment import NodeSegment

# 这里的 msg_id 是已存在的消息的 id,可通过消息事件的 id 获得
refer_node = NodeSegment(id=msg_id)

构造合并转发自定义结点:

from melobot.protocols.onebot.v11.adapter.segment import NodeSegment

# content 是消息内容,与上述消息段发送方法(例如 send, send_custom)的第一参数相同
# 后续参数是在转发消息中显示的,发送人昵称 和 发送人的qq号(int 类型)
node1 = NodeSegment(content="你好", name="melobot instance", uin=10001)

node2 = NodeSegment(
    content=ImageSegment(...),
    name="melobot instance",
    uin=10001
)

node3 = NodeSegment(
    content=[TextSegment(...), ImageSegment(...)],
    name="melobot instance",
    uin=10001
)

将消息结点组成列表,就是一条转发消息的等价表达了,使用 send_forward() 来发送它:

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

@on_message(...)
async def _(adapter: Adapter):
    await adapter.send_forward([refer_node, node1, node2, node3])

转发消息的其他发送方法

send_forward() 可根据当前触发事件,自动定位要向何处发送消息。同理,要自定义发送目标,将 send_forward() 换成 send_forward_custom() 即可。

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

@on_message(...)
async def _(adapter: Adapter):
    # 发送一个自定义目标的私聊转发消息,user_id 为 qq 号
    await adapter.send_forward_custom(..., user_id=1574260633)
    # 发送一个自定义目标的群聊转发消息,group_id 为群号
    await adapter.send_forward_custom(..., group_id=535705163)

总结

本篇主要说明了如何构造和发送各种消息。

下一篇将重点说明:其他行为操作及行为操作的等待与响应。