Skip to content

Commit

Permalink
📝 Docs: 更新最佳实践的 Alconna 部分 (#2686)
Browse files Browse the repository at this point in the history
  • Loading branch information
RF-Tar-Railt committed May 1, 2024
1 parent 897498b commit 1b3cd7e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 23 deletions.
31 changes: 30 additions & 1 deletion website/docs/best-practice/alconna/matcher.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 之后,这部分响应器会被追加拓展)
Expand Down Expand Up @@ -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: "[图片]" },
]}
/>
75 changes: 53 additions & 22 deletions website/docs/best-practice/alconna/uniseg.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import TabItem from "@theme/TabItem";
```python
class Segment:
"""基类标注"""
children: List["Segment"]

class Text(Segment):
"""Text对象, 表示一类文本元素"""
Expand Down Expand Up @@ -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消息、小程序等"""
Expand All @@ -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` 属性。

```python
from nonebot_plugin_alconna.uniseg import Custom, custom_register
from nonebot.adapters.qq import Message as QQMessage, MessageSegment as QQMessageSegment
这是因为在 [`Satori`](https://satori.js.org/zh-CN/) 协议的规定下,一类元素可以用其子元素来代表一类兼容性消息
(例如,qq 的商场表情在某些平台上可以用图片代替)。

为此,本插件提供了两种方式来表达 "获取子元素" 的方法:

class Markdown(Custom):
```python
from nonebot_plugin_alconna.builtins.uniseg.chronocat import MarketFace
from nonebot_plugin_alconna import Args, Image, Alconna, select, select_first

def export(self, msg_type: Type[TM]) -> MessageSegment[TM]:
if msg_type is QQMessage:
return QQMessageSegment.markdown(self.content)
# 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image
alc1 = Alconna("make_meme", Args["img", [Image, Image.from_(MarketFace)]])

@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)
```

:::
Expand Down Expand Up @@ -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 的处理方法。

0 comments on commit 1b3cd7e

Please sign in to comment.