diff --git a/docs/api/adapters/README.md b/docs/api/adapters/README.md index f9c99f6b..98692156 100644 --- a/docs/api/adapters/README.md +++ b/docs/api/adapters/README.md @@ -320,9 +320,9 @@ Message(MessageSegment(type='text', data={'text': 'hello world'})) Message(MessageSegment(type='image', data={'file': 'file///...'}), MessageSegment(type='text', data={'text': 'world'})) >>> Message.template( # 支持以Message对象作为消息模板 ... MessageSegment.text('test {event.user_id}') + MessageSegment.face(233) + -... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'}) -Message(MessageSegment(type='text', data={'text': 'test 123456'}), - MessageSegment(type='face', data={'face': 233}), +... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'}) +Message(MessageSegment(type='text', data={'text': 'test 123456'}), + MessageSegment(type='face', data={'face': 233}), MessageSegment(type='text', data={'text': 'test hello world'})) >>> Message.template("{link:image}").format(link='https://...') # 支持拓展格式化控制符 Message(MessageSegment(type='image', data={'file': 'https://...'})) @@ -547,11 +547,30 @@ Event 基类。提供获取关键信息的方法,其余信息可直接获取 ## _class_ `MessageTemplate` -基类:`string.Formatter`, `Generic`[`nonebot.adapters._template.TM`] +基类:`string.Formatter`, `Generic`[`nonebot.adapters._template.TF`] 消息模板格式化实现类 +### `__init__(template, factory=)` + + +* **说明** + + 创建一个模板 + + + +* **参数** + + + * `template: Union[str, Message]`: 模板 + + + * `factory: Union[str, Message]`: 消息构造类型,默认为 str + + + ### `format(*args, **kwargs)` diff --git a/nonebot/adapters/_message.py b/nonebot/adapters/_message.py index 3c8ee79e..b05a35a6 100644 --- a/nonebot/adapters/_message.py +++ b/nonebot/adapters/_message.py @@ -121,9 +121,9 @@ class Message(List[TMS], abc.ABC): Message(MessageSegment(type='image', data={'file': 'file///...'}), MessageSegment(type='text', data={'text': 'world'})) >>> Message.template( # 支持以Message对象作为消息模板 ... MessageSegment.text('test {event.user_id}') + MessageSegment.face(233) + - ... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'}) - Message(MessageSegment(type='text', data={'text': 'test 123456'}), - MessageSegment(type='face', data={'face': 233}), + ... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'}) + Message(MessageSegment(type='text', data={'text': 'test 123456'}), + MessageSegment(type='face', data={'face': 233}), MessageSegment(type='text', data={'text': 'test hello world'})) >>> Message.template("{link:image}").format(link='https://...') # 支持拓展格式化控制符 Message(MessageSegment(type='image', data={'file': 'https://...'})) @@ -136,7 +136,7 @@ class Message(List[TMS], abc.ABC): - ``MessageFormatter[TM]``: 消息格式化器 """ - return MessageTemplate(cls, format_string) + return MessageTemplate(format_string, cls) @classmethod @abc.abstractmethod diff --git a/nonebot/adapters/_template.py b/nonebot/adapters/_template.py index d914ee8a..94472018 100644 --- a/nonebot/adapters/_template.py +++ b/nonebot/adapters/_template.py @@ -2,22 +2,44 @@ import inspect import functools from string import Formatter from typing import (TYPE_CHECKING, Any, Set, List, Type, Tuple, Union, Generic, - Mapping, TypeVar, Sequence) + Mapping, TypeVar, Sequence, cast, overload) if TYPE_CHECKING: from . import Message, MessageSegment TM = TypeVar("TM", bound="Message") +TF = TypeVar("TF", str, "Message") -class MessageTemplate(Formatter, Generic[TM]): +class MessageTemplate(Formatter, Generic[TF]): """消息模板格式化实现类""" - def __init__(self, factory: Type[TM], template: Union[str, TM]) -> None: - self.template = template - self.factory = factory + @overload + def __init__(self: "MessageTemplate[str]", + template: str, + factory: Type[str] = str) -> None: + ... - def format(self, *args: Any, **kwargs: Any) -> TM: + @overload + def __init__(self: "MessageTemplate[TM]", template: Union[str, TM], + factory: Type[TM]) -> None: + ... + + def __init__(self, template, factory=str) -> None: + """ + :说明: + + 创建一个模板 + + :参数: + + * ``template: Union[str, Message]``: 模板 + * ``factory: Union[str, Message]``: 消息构造类型,默认为 `str` + """ + self.template: Union[str, TF] = template + self.factory: Type[TF] = factory + + def format(self, *args: Any, **kwargs: Any) -> TF: """ :说明: @@ -27,8 +49,8 @@ class MessageTemplate(Formatter, Generic[TM]): if isinstance(self.template, str): msg += self.vformat(self.template, args, kwargs) elif isinstance(self.template, self.factory): - for seg in self.template: - seg: "MessageSegment" + template = cast("Message[MessageSegment]", self.template) + for seg in template: msg += self.vformat(str(seg), args, kwargs) if seg.is_text() else seg else: @@ -37,7 +59,7 @@ class MessageTemplate(Formatter, Generic[TM]): return msg def vformat(self, format_string: str, args: Sequence[Any], - kwargs: Mapping[str, Any]) -> TM: + kwargs: Mapping[str, Any]) -> TF: used_args = set() result, _ = self._vformat(format_string, args, kwargs, used_args, 2) self.check_unused_args(list(used_args), args, kwargs) @@ -51,8 +73,7 @@ class MessageTemplate(Formatter, Generic[TM]): used_args: Set[Union[int, str]], recursion_depth: int, auto_arg_index: int = 0, - ) -> Tuple[TM, int]: - + ) -> Tuple[TF, int]: if recursion_depth < 0: raise ValueError("Max string recursion exceeded") @@ -115,6 +136,9 @@ class MessageTemplate(Formatter, Generic[TM]): [""])), auto_arg_index def format_field(self, value: Any, format_spec: str) -> Any: + if issubclass(self.factory, str): + return super().format_field(value, format_spec) + segment_class: Type[MessageSegment] = self.factory.get_segment_class() method = getattr(segment_class, format_spec, None) method_type = inspect.getattr_static(segment_class, format_spec, None) diff --git a/pages/changelog.md b/pages/changelog.md index 9b609dac..b5012389 100644 --- a/pages/changelog.md +++ b/pages/changelog.md @@ -4,6 +4,10 @@ sidebar: auto # 更新日志 +## v2.0.0a17 + +- 新增 `MessageTemplate` 对于 `str` 普通模板的支持 + ## v2.0.0a16 - 新增 `MessageTemplate` 可用于 `Message` 的模板生成 @@ -13,6 +17,8 @@ sidebar: auto - 修复 `fastapi` Driver Websocket 未能正确提供请求头部 - 新增 `fastapi` Driver 更多的 uvicorn 相关配置项 - 新增 `quart` Driver 更多的 uvicorn 相关配置项 +- 修复 `endswith` Rule 错误的正则匹配 +- 修复 `cqhttp` Adapter `image`, `record`, `video` 对 `BytesIO` 不正常的读取操作 ## v2.0.0a15