📝 Docs: 更新最佳实践的 Alconna 部分 (#2686)

This commit is contained in:
Tarrailt 2024-05-01 11:34:33 +08:00 committed by GitHub
parent 897498b7f5
commit 1b3cd7e2e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 23 deletions

View File

@ -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: "[图片]" },
]}
/>

View File

@ -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 的处理方法。