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

This commit is contained in:
Tarrailt
2024-05-01 11:34:33 +08:00
committed by GitHub
parent 897498b7f5
commit 1b3cd7e2e2
2 changed files with 83 additions and 23 deletions

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