mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 06:56:39 +00:00 
			
		
		
		
	📝 Docs: 更新最佳实践的 Alconna 部分 (#2686)
This commit is contained in:
		| @@ -487,7 +487,7 @@ matcher = on_alconna( | ||||
|  | ||||
| ```python | ||||
| from nonebot_plugin_alconna import Match, on_alconna | ||||
| from nonebot_plugin_alconna.adapters.discord import DiscordSlashExtension | ||||
| from nonebot_plugin_alconna.builtins.plugins.discord import DiscordSlashExtension | ||||
|  | ||||
|  | ||||
| alc = Alconna( | ||||
| @@ -509,6 +509,12 @@ async def remove(plugin: Match[str], time: Match[int]): | ||||
|     await matcher.finish(f"removed {plugin.result} with {time.result if time.available else -1}") | ||||
| ``` | ||||
|  | ||||
| 目前插件提供了 4 个内置的 `Extension`,它们在 `nonebot_plugin_alconna.builtins.extensions` 下: | ||||
| - `ReplyRecordExtension`: 将消息事件中的回复暂存在 extension 中,使得解析用的消息不带回复信息,同时可以在后续的处理中获取回复信息。 | ||||
| - `DiscordSlashExtension`: 将 Alconna 的命令自动转换为 Discord 的 Slash Command,并将 Slash Command 的交互事件转换为消息交给 Alconna 处理。 | ||||
| - `MarkdownOutputExtension`: 将 Alconna 的自动输出转换为 Markdown 格式 | ||||
| - `TelegramSlashExtension`: 将 Alconna 的命令注册在 Telegram 上以获得提示。 | ||||
|  | ||||
| :::tip | ||||
|  | ||||
| 全局的 Extension 可延迟加载 (即若有全局拓展加载于部分 AlconnaMatcher 之后,这部分响应器会被追加拓展) | ||||
| @@ -572,3 +578,26 @@ class CompConfig(TypedDict): | ||||
|     lite: NotRequired[bool] | ||||
|     """是否使用简洁版本的补全会话(相当于同时配置 disables、hides、hide_tabs)""" | ||||
| ``` | ||||
|  | ||||
| ## 内置插件 | ||||
|  | ||||
| 类似于 Nonebot 本身提供的内置插件,`nonebot_plugin_alconna` 提供了两个内置插件:`echo` 和 `help`。 | ||||
|  | ||||
| 你可以用本插件的 `load_builtin_plugin(s)` 来加载它们: | ||||
|  | ||||
| ```python | ||||
| from nonebot_plugin_alconna import load_builtin_plugins | ||||
|  | ||||
| load_builtin_plugins("echo", "help") | ||||
| ``` | ||||
|  | ||||
| 其中 `help` 仅能列出所有 Alconna 指令。 | ||||
|  | ||||
| <Messenger | ||||
|   msgs={[ | ||||
|     { position: "right", msg: "/帮助" }, | ||||
|     { position: "left", msg: "# 当前可用的命令有:\n 0 /echo : echo 指令\n 1 /help : 显示所有命令帮助\n# 输入'命令名 -h|--help' 查看特定命令的语法" }, | ||||
|     { position: "right", msg: "/echo [图片]" }, | ||||
|     { position: "left", msg: "[图片]" }, | ||||
|   ]} | ||||
| /> | ||||
|   | ||||
| @@ -20,6 +20,7 @@ import TabItem from "@theme/TabItem"; | ||||
| ```python | ||||
| class Segment: | ||||
|     """基类标注""" | ||||
|     children: List["Segment"] | ||||
|  | ||||
| class Text(Segment): | ||||
|     """Text对象, 表示一类文本元素""" | ||||
| @@ -81,7 +82,7 @@ class Reference(Segment): | ||||
|     """Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类""" | ||||
|     id: Optional[str] | ||||
|     """此处不一定是消息ID,可能是其他ID,如消息序号等""" | ||||
|     content: Optional[Union[Message, str, List[Union[RefNode, CustomNode]]]] | ||||
|     children: List[Union[RefNode, CustomNode]] | ||||
|  | ||||
| class Hyper(Segment): | ||||
|     """Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等""" | ||||
| @@ -93,35 +94,26 @@ class Other(Segment): | ||||
|     """其他 Segment""" | ||||
|     origin: MessageSegment | ||||
|  | ||||
| class Custom(Segment, abc.ABC): | ||||
|     """Custom对象,表示一类自定义消息""" | ||||
|  | ||||
|     mstype: str | ||||
|     content: Any | ||||
|  | ||||
|     @abc.abstractmethod | ||||
|     def export(self, msg_type: Type[TM]) -> MessageSegment[TM]: ... | ||||
| ``` | ||||
|  | ||||
| 此类消息段通过 `UniMessage.export` 可以转为特定的 `MessageSegment` | ||||
|  | ||||
| :::tips | ||||
|  | ||||
| `Custom` 是一个抽象类,你可以通过继承 `Custom` 来实现自定义消息段: | ||||
| 或许你注意到了 `Segment` 上有一个 `children` 属性。 | ||||
|  | ||||
| 这是因为在 [`Satori`](https://satori.js.org/zh-CN/) 协议的规定下,一类元素可以用其子元素来代表一类兼容性消息 | ||||
| (例如,qq 的商场表情在某些平台上可以用图片代替)。 | ||||
|  | ||||
| 为此,本插件提供了两种方式来表达 "获取子元素" 的方法: | ||||
|  | ||||
| ```python | ||||
| from nonebot_plugin_alconna.uniseg import Custom, custom_register | ||||
| from nonebot.adapters.qq import Message as QQMessage, MessageSegment as QQMessageSegment | ||||
| from nonebot_plugin_alconna.builtins.uniseg.chronocat import MarketFace | ||||
| from nonebot_plugin_alconna import Args, Image, Alconna, select, select_first | ||||
|  | ||||
| class Markdown(Custom): | ||||
| # 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image | ||||
| alc1 = Alconna("make_meme", Args["img", [Image, Image.from_(MarketFace)]]) | ||||
|  | ||||
|     def export(self, msg_type: Type[TM]) -> MessageSegment[TM]: | ||||
|         if msg_type is QQMessage: | ||||
|             return QQMessageSegment.markdown(self.content) | ||||
|  | ||||
| @custom_register(Markdown, "markdown") | ||||
| def markdown(seg: QQMessageSegment) -> Markdown: | ||||
|     return Markdown("markdown", content=seg.data["content"]) | ||||
| # 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果 | ||||
| alc2 = Alconna("make_meme", Args["img", select(Image, index=0)])  # 也可以使用 select_first(Image) | ||||
| ``` | ||||
|  | ||||
| ::: | ||||
| @@ -557,3 +549,42 @@ async def on_startup(): | ||||
|     target = Target("xxxx", scope=SupportScope.qq_client) | ||||
|     await UniMessage("Hello!").send(target=target) | ||||
| ``` | ||||
|  | ||||
| ## 自定义消息段 | ||||
|  | ||||
| `uniseg` 提供了部分方法来允许用户自定义 Segment 的序列化和反序列化: | ||||
|  | ||||
| ```python | ||||
| 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)) | ||||
|  | ||||
| ``` | ||||
|  | ||||
| 具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user