跳转至

消息发送

本页适合你已经跑通 5 分钟上手,并且想发送文本、图片、私信、回复或撤回消息。

消息能力由 bot.messages 或者根据会话由 context 提供。频道消息、私信、撤回、置顶、历史消息拉取都在 Message Service oopz_sdk.services.message 中。

发送频道消息

bot实例中已经暴露所有service对象,我们可以使用 bot.messages.send_message() 发送频道消息:

areachannel 必填;为空会抛出 ValueError

import asyncio

from oopz_sdk import OopzBot, OopzConfig
from oopz_sdk.events import EventContext
from oopz_sdk.models import Message

bot = OopzBot(OopzConfig.from_env())


@bot.on_message
async def handle_message(message: Message, ctx: EventContext):
    area = message.area  # 域 ID
    channel = message.channel  # 频道 ID
    text = message.text  # 消息文本
    await bot.messages.send_message(text, area=area, channel=channel)


async def main() -> None:
    try:
        await bot.run()
    finally:
        await bot.stop()


asyncio.run(main())

发送私信

await bot.messages.send_private_message(
    "这是私信",
    target="目标 UID",
    channel="私信会话 ID",
)

如果不传 channel,SDK 会先调用 open_private_session(target) 打开或创建私信会话,再发送到该会话。

在事件中进行快捷动作

Tips

在事件回调里,ctx 还提供了几个快捷方法,可以直接对当前消息进行回复、撤回、添加反应等操作,无需再传 areachannel

发送消息 send

可以直接调用 ctx.send() 快捷发送消息:

@bot.on_message
async def handle_message(message: Message, ctx: EventContext):
    if message.text.strip() == "ping":
        await ctx.send("pong")

ctx.send() 会自动使用当前消息的:

  • area
  • channel
  • message_id

因此在 handler 里通常不需要手动取 areachannel

私信中也可以使用 ctx.send()

@bot.on_private_message
async def handle_message(message: Message, ctx: EventContext):
    await ctx.reply("这是私信回复")

回复消息 reply

可以在频道和私信中通过 ctx.reply() 快捷回复当前消息:

@bot.on_message
async def handle_message(message: Message, ctx: EventContext):
    if message.text.strip() == "ping":
        await ctx.reply("pong")

撤回消息 recall

频道消息中可以通过 ctx.recall() 快捷撤回当前消息:

@bot.on_message
async def handle_message(message: Message, ctx: EventContext):
    if message.text.strip() == "ping":
        await ctx.recall()

当前私信不支持撤回操作, 调用会导致 RuntimeError

给消息一个反应 reaction

可以在频道和私信中使用 ctx.react() 快捷给当前消息添加一个反应:

@bot.on_private_message
async def handle_message(message: Message, ctx: EventContext):
    await ctx.react("👍")

Segment 消息

SDK 支持用 Segment 组合消息内容。常用类型:

Segment 用途
Text("文本") 普通文本
Mention("用户 UID") at指定用户
MentionAll() at全体
Image("a.png") 图片输入,发送前会自动上传

示例:

from oopz_sdk.models.segment import Text, Mention, Image

await bot.messages.send_message(
    Text("你好 "),
    Mention("2ce12121207111ef9d5dc6b17a3481f1"),
    Text(" 这是一张图:\n"),
    Image("./demo.png"),
    area="域 ID",
    channel="频道 ID",
)

Image 的输入类型与 Media ServiceImageInput 一致,支持本地路径、bytes、base64 / data URL 和 file-like 对象。

当传入 Segment 时,不要同时传 attachments=,否则会抛出 ValueError。这是为了避免文本中的图片占位和附件列表不一致。

手动附件方式

Oopz的图片发送需要先上传图片获取 file_key,然后在消息文本中用 ![IMAGEw{width}h{height}]({file_key}) 占位,最后把附件信息放到 attachments 参数里。

Segment 的 Image 已经封装了这个流程,如果你不想用 Segment,也可以手动拼装信息:

详细请查看 Media Service 上传文件 的文档。

from oopz_sdk.utils.image import get_image_info

uploaded = await client.media.upload_file("./demo.png", file_type="IMAGE", ext="png")
width, height, file_size = get_image_info("./demo.png")

await client.messages.send_message(
    f"图片:![IMAGEw{width}h{height}]({uploaded.file_key})\n",
    area="域 ID",
    channel="频道 ID",
    attachments=[{
            "fileKey": uploaded.file_key,
            "url": uploaded.url,
            "attachmentType": "IMAGE",
            "displayName": "demo.png",
            "fileSize": file_size,
            "animated": uploaded.animated,
            "hash": "",
            "width": width,
            "height": height,
            "previewFileKey": "",
    }],
)

撤回频道消息

如果在事件回调里,直接调用 ctx.recall() 就能撤回当前消息:

await asyncio.sleep(5)  # 等 5 秒
await ctx.recall()
await bot.messages.recall_message(
    message_id="消息 ID",
    area="域 ID",
    channel="频道 ID",
)

撤回私信

await bot.messages.recall_private_message(
    message_id="消息 ID",
    channel="私信会话 ID",
    target="目标 UID",
)