首页 / 资讯中心 / 文章详情

NoneBot2 通用消息段(uniseg Segment)完全指南:跨适配器消息抽象、模型定义与自定义扩展

NoneBot2 通用消息段(uniseg Segment)完全指南:跨适配器消息抽象、模型定义与自定义扩展 ★ FEATURED ARTICLE
后端即时通讯【免费下载链接】nonebot2跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python项目地址https://gitcode.com/gh_mirrors/no/nonebot2点击查看免费下载uniseg通用消息段是 nonebot-plugin-alconna 插件对 NoneBot2 各适配器消息段MessageSegment的抽象总结既可作为 Alconna 命令的参数类型参与命令解析也可配合UniMessage完成跨平台消息的构建、解析与发送。读完本文你将完整掌握Segment及其全部子类的字段语义、children嵌套机制与select选取方法并能够通过custom_register/custom_handler为特定平台协议注册自定义消息段的序列化与反序列化。通用消息段是什么NoneBot2 是一个多适配器框架每个适配器OneBot、Telegram、Satori、QQ 等都有自己的消息段模型例如 nonebot/internal/adapter/message.py 中定义的抽象基类MessageSegment与消息序列Message各适配器在此基础上派生各自的实现。当插件需要同时运行在多个平台上时直接操作某一适配器的消息段会导致代码不可移植。nonebot-plugin-alconna的unisegUniversal Segment通用消息段正是为解决这一问题而生它是对各适配器消息段的抽象总结形成一套与平台无关的消息段模型。这套模型有两个核心应用场景Alconna 命令的参数定义将通用消息段如Image作为Args中的参数类型命令解析时会从用户消息中提取对应元素消息的构建和解析配合UniMessage见 通用消息序列进行跨平台消息构造、转换与发送。下面是最直观的用法示例——定义一条名为make_meme的命令它接收一个字符串name和一张图片imgfrom nonebot_plugin_alconna import Alconna, Args, Image, on_alconna meme on_alconna(Alconna(make_meme, Args[name, str][img, Image])) meme.handle() async def _(img: Image): ...这里的Image即通用消息段解析器会在用户消息中查找图片元素并自动转换为Image对象随后通过依赖注入直接注入到事件处理函数中。这种能力源自uniseg对Segment与 AlconnaArgs解析机制的整合关于 Alconna 命令解析的更多基础可参考 Alconna 本体。通用消息段模型定义uniseg定义了一套完整的消息段类层级。官方文档提示“本节的内容经过简化实际情况以源码为准”但下述模型已足够覆盖绝大多数使用场景。Segment 基类所有通用消息段都继承自Segment它定义了三个只读属性class Segment: 基类标注 property def type(self) - str: ... property def data(self) - [str, Any]: ... property def children(self) - list[Segment]: ...type消息段的类型标识字符串与 NoneBot2 核心中MessageSegment.type见 nonebot/internal/adapter/message.py 附近的is_text等类型判断逻辑概念一致data消息段携带的结构化数据键值对children该消息段的子消息段列表用于表达嵌套结构后文详述。文本TextText表示一类文本元素是消息中最基础的消息段class Text(Segment): Text对象, 表示一类文本元素 text: str styles: dict[tuple[int, int], list[str]] def cover(self, text: str): ... def mark( self, start: Optional[int] None, end: Optional[int] None, *styles: str ): ...text文本内容styles样式标注字典键为(start, end)的区间元组值为样式名称列表可用于富文本表达cover(text)覆盖替换文本内容mark(start, end, *styles)为指定区间默认整个文本标注样式。提醒At 与 AtAllAt表示提醒某用户的元素AtAll表示提醒所有人的元素class At(Segment): At对象, 表示一类提醒某用户的元素 flag: Literal[user, role, channel] target: str display: Optional[str] class AtAll(Segment): AtAll对象, 表示一类提醒所有人的元素 here: boolAt.flag指明提醒对象的类别user用户、role身份组、channel频道At.target为被提醒对象的 IDAt.display为展示用文本某些平台允许自定义 显示名AtAll.here表示是否只提醒“当前在线/当前频道内”的所有人。表情Emojiclass Emoji(Segment): Emoji对象, 表示一类表情元素 id: str name: Optional[str]id为表情的唯一标识name为表情名称平台支持时可用。媒体Media 及其子类Media是媒体类消息段的基类统一承载媒体的多种来源与元信息class Media(Segment): id: Optional[str] url: Optional[str] path: Optional[Union[str, Path]] raw: Optional[Union[bytes, BytesIO]] mimetype: Optional[str] name: str to_url: ClassVar[Optional[MediaToUrl]]媒体来源有四种表达方式按需取其一即可id平台侧媒体资源 IDurl媒体资源的网络地址path本地文件路径str或pathlib.Pathraw原始二进制数据bytes或BytesIO。mimetype为媒体 MIME 类型name为文件名类属性to_url用于注册“媒体转 URL”的回调MediaToUrl供需要直链的场景使用。Media派生出一系列具体媒体消息段class Image(Media): Image对象, 表示一类图片元素 width: Optional[int] height: Optional[int] class Audio(Media): Audio对象, 表示一类音频元素 duration: Optional[float] class Voice(Media): Voice对象, 表示一类语音元素 duration: Optional[float] class Video(Media): Video对象, 表示一类视频元素 thumbnail: Optional[Image] duration: Optional[float] class File(Media): File对象, 表示一类文件元素Image额外携带width/height尺寸信息Audio、Voice携带duration时长信息Video除时长外还可通过thumbnail携带一张Image缩略图File为通用文件消息段。回复与引用Reply、Referenceclass Reply(Segment): Reply对象表示一类回复消息 id: str 此处不一定是消息ID可能是其他ID如消息序号等 msg: Optional[Union[Message, str]] origin: Optional[Any] class Reference(Segment): Reference对象表示一类引用消息。转发消息 (Forward) 也属于此类 id: Optional[str] 此处不一定是消息ID可能是其他ID如消息序号等 children: List[Union[RefNode, CustomNode]]Reply的id用于定位被回复的消息注意文档特别说明该字段不一定是消息 ID可能是消息序号等其他标识msg可携带被回复消息的内容Message或字符串origin保留原始对象。Reference表示引用消息转发消息 Forward 也归为此类其子节点为RefNode/CustomNode构成的节点列表。超级消息HyperHyper表示一类无法被常规消息段覆盖的“超级消息”如卡片消息、ark 消息、小程序等class Hyper(Segment): Hyper对象表示一类超级消息。如卡片消息、ark消息、小程序等 format: Literal[xml, json] raw: Optional[str] content: Optional[Union[dict, list]]format指明内容格式为xml或jsonraw为原始字符串内容content为结构化内容字典或列表。交互组件Button 与 KeyboardButton表示一个按钮消息Keyboard表示一行按钮元素class Button(Segment): Button对象表示一类按钮消息 flag: Literal[action, link, input, enter] - 点击 action 类型的按钮时会触发一个关于 按钮回调 事件该事件的 button 资源会包含上述 id - 点击 link 类型的按钮时会打开一个链接或者小程序该链接的地址为 url - 点击 input 类型的按钮时会在用户的输入框中填充 text - 点击 enter 类型的按钮时会直接发送 text label: Union[str, Text] 按钮上的文字 clicked_label: Optional[str] 点击后按钮上的文字 id: Optional[str] url: Optional[str] text: Optional[str] style: Optional[str] 仅建议使用下列值primary, secondary, success, warning, danger, info, link, grey, blue 此处规定 grey 与 secondary 等同, blue 与 primary 等同 permission: Union[Literal[admin, all], list[At]] all - admin: 仅管理者可操作 - all: 所有人可操作 - list[At]: 指定用户/身份组可操作 Button.flag决定按钮行为四种取值语义如下flag行为action点击触发一个“按钮回调”事件事件中的 button 资源会包含上述idlink点击打开url指向的链接或小程序input点击后在用户输入框中填充textenter点击后直接发送textlabel为按钮文字可以是str或带样式的Textclicked_label为点击后显示的文字style建议仅使用primary, secondary, success, warning, danger, info, link, grey, blue其中grey等价于secondary、blue等价于primary。permission控制可操作人群默认为all所有人也可设为admin仅管理者或list[At]指定用户/身份组。class Keyboard(Segment): Keyboard对象表示一行按钮元素 id: Optional[str] 此处一般用来表示模板id特殊情况下可能表示例如 bot_appid 等 buttons: Optional[list[Button]] row: Optional[int] 当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数Keyboard.id一般表示模板 ID特殊情况下可能表示bot_appid等row用于在消息中只有一个Keyboard时约定按钮组的列数。构造含按钮的消息可参考 通用消息组件示例例如UniMessage.text(hello world).keyboard(Button(link1, url...), row3)。其他Otherclass Other(Segment): 其他 Segment origin: MessageSegmentOther用于包裹暂未被抽象覆盖的原始适配器消息段origin保存原始的MessageSegment对象起到“兜底”作用。国际化I18nclass I18n(Segment): 特殊的 Segment用于 i18n 消息 item_or_scope: Union[LangItem, str] type_: Optional[str] None def tp(self) - UniMessageTemplate: ...I18n是用于 i18n国际化消息的特殊消息段item_or_scope接收语言项LangItem或语言作用域字符串type_为可选的类型标识tp()方法将其转换为UniMessageTemplate模板。该消息段与插件内置的lang插件见 内置组件配合可实现多语言命令消息。children 嵌套机制与 select 选取细心的读者会发现Segment基类上有一个children属性。这源于 Satori 协议的规定一类元素可以用其子元素来表示一类兼容性消息。例如 QQ 的商场表情在某些平台上可以用图片代替于是该消息段内部会包含一个Image子消息段。为此插件提供了select方法用于表达“从命令消息中获取嵌套子元素”的需求。下面两个命令都基于“制作表情包”场景from nonebot_plugin_alconna import Args, Image, Alconna, select from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace # 表示这个指令需要的图片会在目标元素下进行搜索将所有符合 Image 的元素选出来并将第一个作为结果 alc1 Alconna( make_meme, Args[name, str][img, select(Image).first] ) # 也可以使用 select(Image).nth(0) # 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image alc2 Alconna( make_meme, Args[name, str][img, [Image, select(Image).from_(MarketFace)]] )select(Image).first在目标元素此处指命令对应的消息段树下搜索所有符合Image的元素取第一个作为结果等价写法为select(Image).nth(0)[Image, select(Image).from_(MarketFace)]参数类型声明为列表表示该图片要么直接是Image要么是MarketFace元素内部的Imagefrom_限定搜索范围。同样的嵌套提取能力也存在于UniMessage.select方法中递归地从消息中选择指定类型的消息段详见通用消息序列的“嵌套提取”一节。自定义消息段虽然uniseg内置了覆盖绝大多数场景的消息段但总有平台特有元素无法被抽象覆盖。为此uniseg提供了两个装饰器允许用户自定义Segment的序列化Segment → MessageSegment与反序列化MessageSegment → Segmentcustom_register注册一个从 MessageSegment 到 Segment的构建方法custom_handler注册一个从 Segment 到 MessageSegment的导出方法。下面以 Satori 适配器上的MarketFaceQQ 商城表情为例完整演示自定义消息段的流程from dataclasses import dataclass from nonebot.adapters import Bot from nonebot.adapters import MessageSegment as BaseMessageSegment from nonebot.adapters.satori import Custom, Message, MessageSegment from nonebot_plugin_alconna.uniseg.builder import MessageBuilder from nonebot_plugin_alconna.uniseg.exporter import MessageExporter from nonebot_plugin_alconna.uniseg import Segment, custom_handler, custom_register dataclass class MarketFace(Segment): tabId: str faceId: str key: str custom_register(MarketFace, chronocat:marketface) def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment): if not isinstance(seg, Custom): raise ValueError(MarketFace can only be built from Satori Message) return MarketFace(**seg.data)(*builder.generate(seg.children)) custom_handler(MarketFace) async def mfexport( exporter: MessageExporter, seg: MarketFace, bot: Bot, fallback: bool ): if exporter.get_message_type() is Message: return MessageSegment(chronocat:marketface, seg.data)( await exporter.export(seg.children, bot, fallback) )拆解这个示例定义 Segment 子类用dataclass声明MarketFace(Segment)字段tabId、faceId、key对应商城表情的数据结构反序列化方向custom_register(MarketFace, chronocat:marketface)将 Satori 平台中type为chronocat:marketface的Custom消息段绑定到MarketFace。构建函数mfbuild接收MessageBuilder与原始消息段先校验其确为Custom类型再以seg.data构造MarketFace对象并调用builder.generate(seg.children)递归构建其子消息段MarketFace(**seg.data)(...)的调用形式表示挂载子元素序列化方向custom_handler(MarketFace)注册导出函数mfexport接收MessageExporter、MarketFace对象、Bot与fallback标记当导出目标消息类型确为 Satori 的Message时构造MessageSegment(chronocat:marketface, seg.data)并递归导出其子元素。通过custom_register/custom_handler注册后自定义消息段即可在UniMessage构建、Alconna 参数解析、跨平台导出等环节中与内置消息段一样被透明处理。若不想从零编写插件也内置了若干常用自定义消息段位于nonebot_plugin_alconna.builtins.segments下包括Markdownmarkdown 模板元素、MarketFaceQQ 商城表情与MusicShareQQ 音乐分享卡片详见 内置组件。与 UniMessage 及生态的配合Segment不是孤立存在的它与uniseg模块的其他能力共同构成完整的通用消息体系消息构建UniMessage可接收单个字符串/消息段或它们的可迭代对象也支持.text()、.at()、.image()等链式快捷方法内部元素即本文所述的各类Segment见 通用消息组件消息解析与发送通过UniMessage.export/.send可跨平台导出和发送配合Receipt实现撤回、编辑、表态见 通用消息序列辅助能力MsgId、MsgTarget、Target、message_recall、message_edit、message_reaction、at_me等依赖注入与工具函数围绕消息操作展开见 辅助功能命令解析Segment子类可直接作为Args参数类型如开篇的Image也可与select、Match、on_alconna等组合实现复杂的跨平台指令整体架构参见 Alconna 插件总览。在 NoneBot2 核心层面通用消息段最终仍需落到各适配器的原生MessageSegment上完成收发理解 nonebot/internal/adapter/message.py 中MessageSegment.type、Message列表语义与extract_plain_textnonebot/internal/adapter/message.py等机制有助于在调试跨平台消息时厘清“通用段”与“原生段”之间的转换边界。掌握通用消息段模型后你便能在多适配器项目中以一套代码完成消息的构建、解析、发送与平台特有元素扩展。赞分享后端即时通讯【免费下载链接】nonebot2跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python项目地址https://gitcode.com/gh_mirrors/no/nonebot2点击查看免费下载相关推荐NoneBot2 通用消息段uniseg Segment完全指南模型定义、嵌套提取与自定义消息段NoneBot2 通用消息段uniseg Segment完全指南模型定义、嵌套提取与自定义消息段 通用消息段UniSegment是 nonebot p后端即时通讯NoneBot2 通用消息段uniseg Segment完全指南跨平台消息模型、嵌套提取与自定义消息段NoneBot2 通用消息段uniseg Segment完全指南跨平台消息模型、嵌套提取与自定义消息段 通用消息段 uniseg 中的 Segment后端即时通讯NoneBot2 跨平台消息段详解nonebot-plugin-alconna uniseg 通用消息段Segment使用指南NoneBot2 跨平台消息段详解nonebot plugin alconna uniseg 通用消息段Segment使用指南 通用消息段uniseg后端即时通讯上一篇claude-skills 实战指南使用 Expo Router 构建 React Native 文件路由与导航体系下一篇Steam Deck终极模拟器配置指南EmuDeck一键安装30游戏平台创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站