使用样例¶
本页提供从入门到进阶的完整插件示例,覆盖消息收发、媒体发送、房间操作、事件处理等场景。
基础 echo 机器人¶
from nonebot import on_command
from nonebot.params import CommandArg
from nonebot.adapters.matrix import Bot, Message, MessageEvent, MessageSegment
matcher = on_command("echo")
@matcher.handle()
async def handle_echo(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):
text = msg.extract_plain_text().strip()
if text == "mention":
await matcher.finish(MessageSegment.mention_user(event.get_user_id()))
if text == "notice":
await matcher.finish(MessageSegment.notice("这是一条 Matrix notice"))
await bot.send(event, MessageSegment.text(text or "hello matrix"))
要点:
使用
on_command注册命令;用CommandArg提取命令参数。通过
bot.send(event, message)向事件来源房间发送消息。MessageSegment.text/content/notice/emote提供丰富的消息类型。
发送媒体¶
Matrix 媒体需要先上传到 media repository,再由消息正文引用 mxc:// URI。MessageSegment.image/file/audio/video 在传入 bytes 时自动执行上传流程,无需手动处理。
from pathlib import Path
from nonebot import on_command
from nonebot.adapters.matrix import Bot, MessageEvent, MessageSegment
matcher = on_command("image")
@matcher.handle()
async def handle_send_image(bot: Bot, event: MessageEvent):
img_bytes = Path("./assets/photo.jpg").read_bytes()
await bot.send(
event,
MessageSegment.image(
img_bytes,
filename="photo.jpg",
content_type="image/jpeg",
),
)
注意:
content_type必须传入正确的 MIME Type(如image/jpeg),否则客户端可能无法正常显示。
也可以直接传入已有的 mxc:// URI(不会触发上传):
MessageSegment.image("mxc://example.org/AbCdEf1234", filename="pic.jpg")
发送 HTML 富文本¶
matcher = on_command("html")
@matcher.handle()
async def handle_html(bot: Bot, event: MessageEvent):
await bot.send(
event,
MessageSegment.html(
"这是一条带格式的消息",
"这是一条<b>带格式</b>的<em>消息</em>",
),
)
@ 提及用户¶
matcher = on_command("call")
@matcher.handle()
async def handle_mention(bot: Bot, event: MessageEvent):
await bot.send(
event,
MessageSegment.text("你好,")
+ MessageSegment.mention_user("@alice:example.org", "Alice")
+ MessageSegment.text(",请看这个消息"),
)
消息反应¶
matcher = on_command("approve")
@matcher.handle()
async def handle_react(bot: Bot, event: MessageEvent):
await bot.react(event.room_id, event.event_id, "👍")
撤回消息¶
matcher = on_command("delete")
@matcher.handle()
async def handle_redact(bot: Bot, event: MessageEvent):
await bot.redact(event.room_id, event.event_id, reason="用户请求撤回")
输入状态¶
matcher = on_command("typing")
@matcher.handle()
async def handle_typing(bot: Bot, event: MessageEvent):
# 显示"正在输入……",5 秒后自动清除
await bot.set_typing_state(event.room_id, typing=True, timeout=5000)
await bot.send(event, MessageSegment.text("这是模拟输入的效果"))
标记已读¶
@matcher.handle()
async def mark_as_read(bot: Bot, event: MessageEvent):
await bot.mark_read(event.room_id, event.event_id)
加入房间¶
matcher = on_command("join")
@matcher.handle()
async def handle_join(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):
room_alias = msg.extract_plain_text().strip()
if room_alias:
await bot.join_room(room_id=room_alias, reason="通过命令加入")
处理房间邀请¶
适配器支持按白名单/黑名单自动接受邀请。收到邀请时触发 InviteEvent,你也可以编写插件手动处理:
from nonebot import on_type
from nonebot.adapters.matrix import Bot, InviteEvent
on_invite = on_type(InviteEvent, block=False)
@on_invite.handle()
async def handle_invite(bot: Bot, event: InviteEvent):
if event.room_id:
await bot.join_room(room_id=event.room_id, reason="自动加入")
构造原始消息¶
某些场景下需要直接发送符合 Matrix 规范的 JSON 消息体:
matcher = on_command("raw")
@matcher.handle()
async def handle_raw(bot: Bot, event: MessageEvent):
await bot.send(
event,
MessageSegment.raw(
{
"msgtype": "m.text",
"body": "这条消息通过原始格式发送",
},
event_type="m.room.message",
),
)
在加密房间工作¶
如果启用了 E2EE,适配器会自动对加密房间的消息进行加密/解密。插件代码与明文模式完全一致,无需任何特殊处理:
matcher = on_command("secret")
@matcher.handle()
async def handle_secret(bot: Bot, event: MessageEvent):
# 在加密房间中,此消息会自动用 Megolm 加密后发送
await bot.send(event, MessageSegment.text("这是一条加密消息"))
# 同理,收到的加密消息也会自动解密为明文后传入 event
处理设备验证¶
验证请求和状态会以带有 handle 的类型化事件分派。自动接受配置只会调用
VerificationRequest.accept(),不会替用户确认 SAS 或二维码;确认动作应由插件在
展示比较结果并获得用户明确操作后调用。
from nonebot import on_command, on_type
from nonebot.adapters.matrix import (
Bot,
QrVerification,
SasVerification,
VerificationEvent,
VerificationRequest,
)
verification_events = on_type(VerificationEvent, block=False)
confirm = on_command("verify-confirm")
pending: dict[tuple[str, str], SasVerification | QrVerification] = {}
@verification_events.handle()
async def show_verification(event: VerificationEvent):
handle = event.handle
if isinstance(handle, VerificationRequest):
if handle.is_ready():
# 交给插件 UI 展示可用方式;这里仅启动 SAS,不自动确认。
sas = await handle.start_sas()
if sas is not None:
pending[handle.other_user_id, handle.flow_id] = sas
print(sas.decimals(), sas.emoji())
elif isinstance(handle, SasVerification) and handle.can_be_presented():
pending[handle.request.other_user_id, handle.request.flow_id] = handle
print(handle.decimals(), handle.emoji())
@confirm.handle()
async def confirm_verification(bot: Bot, event):
# 生产插件应把确认动作接到自己的按钮、Web UI 或命令权限上。
for verification in pending.values():
if isinstance(verification, SasVerification):
await verification.confirm()
break
二维码由插件负责展示或扫码,适配器只提供标准 Matrix 字节编码:
qr = await request.generate_qr_code()
if qr is not None:
image_bytes = qr.to_bytes() # 交给二维码 UI 展示
# 扫描后的原始二维码字节交回对应请求;不会自动确认信任。
qr = await request.scan_qr_code(scanned_bytes)
if qr is not None:
await qr.confirm()
组合消息段¶
Message 支持 + 运算符拼接多个 MessageSegment:
msg = (
MessageSegment.notice("系统提示:")
+ MessageSegment.mention_user(user_id="alice@example.org", "Alice")
+ MessageSegment.text(" 完成了任务 ")
+ MessageSegment.emote("💡 太棒了")
)
await bot.send(event, msg)