From da8e988ee99bf833cae152195e44361b00fd56eb Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Fri, 24 Mar 2023 21:17:52 +0800 Subject: [PATCH] :recycle: use saa --- nonebot_bison/config_manager.py | 14 +- nonebot_bison/post/abstract_post.py | 20 +-- nonebot_bison/post/custom_post.py | 46 +++--- nonebot_bison/post/post.py | 23 +-- nonebot_bison/scheduler/scheduler.py | 3 +- nonebot_bison/send.py | 87 +++-------- nonebot_bison/utils/__init__.py | 8 +- poetry.lock | 47 ++++-- pyproject.toml | 3 +- tests/conftest.py | 7 + tests/test_config_manager_abort.py | 12 +- tests/test_config_manager_query_del.py | 28 ++-- tests/test_custom_post.py | 23 ++- tests/test_render.py | 4 +- tests/test_send.py | 206 ++++++++----------------- 15 files changed, 223 insertions(+), 308 deletions(-) diff --git a/nonebot_bison/config_manager.py b/nonebot_bison/config_manager.py index 87ff843..b80aae3 100644 --- a/nonebot_bison/config_manager.py +++ b/nonebot_bison/config_manager.py @@ -15,7 +15,12 @@ from nonebot.params import Depends, EventPlainText, EventToMe from nonebot.permission import SUPERUSER from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot_plugin_saa import PlatformTarget, TargetQQGroup, extract_target +from nonebot_plugin_saa import ( + MessageFactory, + PlatformTarget, + TargetQQGroup, + extract_target, +) from .apis import check_sub_target from .config import config @@ -220,7 +225,7 @@ def do_query_sub(query_sub: Type[Matcher]): query_sub.handle()(ensure_user_info(query_sub)) @query_sub.handle() - async def _(state: T_State): + async def _(bot: Bot, state: T_State): user_info = state["target_user_info"] assert isinstance(user_info, PlatformTarget) sub_list = await config.list_subscribe(user_info) @@ -242,7 +247,8 @@ def do_query_sub(query_sub: Type[Matcher]): if platform.enable_tag: res += " {}".format(", ".join(sub.tags)) res += "\n" - await query_sub.finish(Message(await parse_text(res))) + await MessageFactory(await parse_text(res)).send() + await query_sub.finish() def do_del_sub(del_sub: Type[Matcher]): @@ -285,7 +291,7 @@ def do_del_sub(del_sub: Type[Matcher]): res += " {}".format(", ".join(sub.tags)) res += "\n" res += "请输入要删除的订阅的序号\n输入'取消'中止" - await bot.send(event=event, message=Message(await parse_text(res))) + await MessageFactory(await parse_text(res)).send() @del_sub.receive() async def do_del(event: Event, state: T_State): diff --git a/nonebot_bison/post/abstract_post.py b/nonebot_bison/post/abstract_post.py index c0902f3..ee055d5 100644 --- a/nonebot_bison/post/abstract_post.py +++ b/nonebot_bison/post/abstract_post.py @@ -3,7 +3,7 @@ from dataclasses import dataclass, field from functools import reduce from typing import Optional -from nonebot.adapters.onebot.v11.message import Message, MessageSegment +from nonebot_plugin_saa import MessageFactory, MessageSegmentFactory from ..plugin_config import plugin_config @@ -11,13 +11,13 @@ from ..plugin_config import plugin_config @dataclass class BasePost: @abstractmethod - async def generate_text_messages(self) -> list[MessageSegment]: - "Generate Message list from this instance" + async def generate_text_messages(self) -> list[MessageSegmentFactory]: + "Generate MessageFactory list from this instance" ... @abstractmethod - async def generate_pic_messages(self) -> list[MessageSegment]: - "Generate Message list from this instance with `use_pic`" + async def generate_pic_messages(self) -> list[MessageSegmentFactory]: + "Generate MessageFactory list from this instance with `use_pic`" ... @@ -27,7 +27,7 @@ class OptionalMixin: override_use_pic: Optional[bool] = None compress: bool = False - extra_msg: list[Message] = field(default_factory=list) + extra_msg: list[MessageFactory] = field(default_factory=list) def _use_pic(self): if not self.override_use_pic is None: @@ -37,17 +37,19 @@ class OptionalMixin: @dataclass class AbstractPost(OptionalMixin, BasePost): - async def generate_messages(self) -> list[Message]: + async def generate_messages(self) -> list[MessageFactory]: if self._use_pic(): msg_segments = await self.generate_pic_messages() else: msg_segments = await self.generate_text_messages() if msg_segments: if self.compress: - msgs = [reduce(lambda x, y: x.append(y), msg_segments, Message())] + msgs = [ + reduce(lambda x, y: x.append(y), msg_segments, MessageFactory([])) + ] else: msgs = list( - map(lambda msg_segment: Message([msg_segment]), msg_segments) + map(lambda msg_segment: MessageFactory([msg_segment]), msg_segments) ) else: msgs = [] diff --git a/nonebot_bison/post/custom_post.py b/nonebot_bison/post/custom_post.py index 3f343be..951a9c8 100644 --- a/nonebot_bison/post/custom_post.py +++ b/nonebot_bison/post/custom_post.py @@ -1,10 +1,10 @@ from dataclasses import dataclass, field -from pathlib import Path from typing import Optional -from nonebot.adapters.onebot.v11.message import Message, MessageSegment +from nonebot.adapters.onebot.v11 import MessageSegment from nonebot.log import logger from nonebot.plugin import require +from nonebot_plugin_saa import Image, MessageFactory, MessageSegmentFactory, Text from .abstract_post import AbstractPost, BasePost @@ -12,37 +12,35 @@ from .abstract_post import AbstractPost, BasePost @dataclass class _CustomPost(BasePost): - message_segments: list[MessageSegment] = field(default_factory=list) + ms_factories: list[MessageSegmentFactory] = field(default_factory=list) css_path: Optional[str] = None # 模板文件所用css路径 - async def generate_text_messages(self) -> list[MessageSegment]: - return self.message_segments + async def generate_text_messages(self) -> list[MessageSegmentFactory]: + return self.ms_factories - async def generate_pic_messages(self) -> list[MessageSegment]: + async def generate_pic_messages(self) -> list[MessageSegmentFactory]: require("nonebot_plugin_htmlrender") from nonebot_plugin_htmlrender import md_to_pic pic_bytes = await md_to_pic(md=self._generate_md(), css_path=self.css_path) - return [MessageSegment.image(pic_bytes)] + return [Image(pic_bytes)] def _generate_md(self) -> str: md = "" - for message_segment in self.message_segments: - if message_segment.type == "text": - md += "{}
".format(message_segment.data.get("text", "")) - elif message_segment.type == "image": - # 先尝试获取file的值,没有再尝试获取url的值,都没有则为空 - pic_res = message_segment.data.get("file") or message_segment.data.get( - "url", "" - ) - if not pic_res: - logger.warning("无法获取到图片资源:MessageSegment.image中file/url字段均为空") - else: - md += "![Image]({})\n".format(pic_res) - else: - logger.warning("custom_post不支持处理类型:{}".format(message_segment.type)) - continue + for message_segment in self.ms_factories: + match message_segment: + case Text(data={"text": text}): + md += "{}
".format(text) + case Image(data={"image": image}): + # use onebot v11 to convert image into url + ob11_image = MessageSegment.image(image) + md += "![Image]({})\n".format(ob11_image.data["file"]) + case _: + logger.warning( + "custom_post不支持处理类型:{}".format(type(message_segment)) + ) + continue return md @@ -52,14 +50,14 @@ class CustomPost(_CustomPost, AbstractPost): """基于 markdown 语法的,自由度较高的推送内容格式 简介: - 支持处理text/image两种MessageSegment, + 支持处理text/image两种MessageSegmentFactory, 通过将text/image转换成对应的markdown语法以生成markdown文本。 理论上text类型中可以直接使用markdown语法,例如`##第一章`。 但会导致不启用`override_use_pic`时, 发送不会被渲染的纯文本消息。 图片渲染最终由htmlrender执行。 注意: - 每一个MessageSegment元素都会被解释为单独的一行 + 每一个MessageSegmentFactory元素都会被解释为单独的一行 可选参数: `override_use_pic`:是否覆盖`bison_use_pic`全局配置 diff --git a/nonebot_bison/post/post.py b/nonebot_bison/post/post.py index fe0db9a..afec2ce 100644 --- a/nonebot_bison/post/post.py +++ b/nonebot_bison/post/post.py @@ -3,8 +3,9 @@ from functools import reduce from io import BytesIO from typing import Optional, Union -from nonebot.adapters.onebot.v11.message import Message, MessageSegment +import nonebot_plugin_saa as saa from nonebot.log import logger +from nonebot_plugin_saa.utils import MessageFactory, MessageSegmentFactory from PIL import Image from ..utils import http_client, parse_text @@ -20,8 +21,8 @@ class _Post(BasePost): target_name: Optional[str] = None pics: list[Union[str, bytes]] = field(default_factory=list) - _message: Optional[list[MessageSegment]] = None - _pic_message: Optional[list[MessageSegment]] = None + _message: Optional[list[MessageSegmentFactory]] = None + _pic_message: Optional[list[MessageSegmentFactory]] = None async def _pic_url_to_image(self, data: Union[str, bytes]) -> Image.Image: pic_buffer = BytesIO() @@ -99,11 +100,11 @@ class _Post(BasePost): self.pics = self.pics[matrix[0] * matrix[1] :] self.pics.insert(0, target_io.getvalue()) - async def generate_text_messages(self) -> list[MessageSegment]: + async def generate_text_messages(self) -> list[MessageSegmentFactory]: if self._message is None: await self._pic_merge() - msg_segments: list[MessageSegment] = [] + msg_segments: list[MessageSegmentFactory] = [] text = "" if self.text: text += "{}".format( @@ -116,17 +117,17 @@ class _Post(BasePost): text += " {}".format(self.target_name) if self.url: text += " \n详情: {}".format(self.url) - msg_segments.append(MessageSegment.text(text)) + msg_segments.append(saa.Text(text)) for pic in self.pics: - msg_segments.append(MessageSegment.image(pic)) + msg_segments.append(saa.Image(pic)) self._message = msg_segments return self._message - async def generate_pic_messages(self) -> list[MessageSegment]: + async def generate_pic_messages(self) -> list[MessageSegmentFactory]: if self._pic_message is None: await self._pic_merge() - msg_segments: list[MessageSegment] = [] + msg_segments: list[MessageSegmentFactory] = [] text = "" if self.text: text += "{}".format(self.text) @@ -136,9 +137,9 @@ class _Post(BasePost): text += " {}".format(self.target_name) msg_segments.append(await parse_text(text)) if not self.target_type == "rss" and self.url: - msg_segments.append(MessageSegment.text(self.url)) + msg_segments.append(saa.Text(self.url)) for pic in self.pics: - msg_segments.append(MessageSegment.image(pic)) + msg_segments.append(saa.Image(pic)) self._pic_message = msg_segments return self._pic_message diff --git a/nonebot_bison/scheduler/scheduler.py b/nonebot_bison/scheduler/scheduler.py index e55c608..1a7bae4 100644 --- a/nonebot_bison/scheduler/scheduler.py +++ b/nonebot_bison/scheduler/scheduler.py @@ -115,8 +115,7 @@ class Scheduler: else: await send_msgs( bot, - user.user, - user.user_type, + user, await send_post.generate_messages(), ) diff --git a/nonebot_bison/send.py b/nonebot_bison/send.py index 25479dd..14ba366 100644 --- a/nonebot_bison/send.py +++ b/nonebot_bison/send.py @@ -1,45 +1,28 @@ import asyncio -from asyncio.tasks import sleep from collections import deque -from typing import Deque, Literal, Union +from typing import Deque -from nonebot.adapters.onebot.v11.bot import Bot +from nonebot.adapters import Bot from nonebot.adapters.onebot.v11.exception import ActionFailed -from nonebot.adapters.onebot.v11.message import Message, MessageSegment from nonebot.log import logger +from nonebot_plugin_saa import AggregatedMessageFactory, MessageFactory, PlatformTarget from .plugin_config import plugin_config from .utils.get_bot import refresh_bots -QUEUE: Deque[ - tuple[ - Bot, - int, - Literal["private", "group", "group-forward"], - Union[str, Message], - int, - ] -] = deque() +Sendable = MessageFactory | AggregatedMessageFactory + +QUEUE: Deque[tuple[Bot, PlatformTarget, Sendable, int]] = deque() MESSGE_SEND_INTERVAL = 1.5 -async def _do_send( - bot: "Bot", - user: int, - user_type: Literal["group", "private", "group-forward"], - msg: Union[str, Message], -): +async def _do_send(bot: "Bot", send_target: PlatformTarget, msg: Sendable): try: - if user_type == "group": - await bot.send_group_msg(group_id=user, message=msg) - elif user_type == "private": - await bot.send_private_msg(user_id=user, message=msg) - elif user_type == "group-forward": - await bot.send_group_forward_msg(group_id=user, messages=msg) - except ActionFailed: + await msg.send_to(send_target, bot) + except ActionFailed: # TODO: catch exception of other adapters await refresh_bots() - logger.warning(f"send msg failed, refresh bots") + logger.warning("send msg failed, refresh bots") async def do_send_msgs(): @@ -49,18 +32,18 @@ async def do_send_msgs(): # why read from queue then pop item from queue? # if there is only 1 item in queue, pop it and await send # the length of queue will be 0. - # At that time, adding items to queue will trigger a new execution of this func, which is wrong. + # At that time, adding items to queue will trigger a new execution of this func, which is not expected. # So, read from queue first then pop from it - bot, user, user_type, msg, retry_time = QUEUE[0] + bot, send_target, msg_factory, retry_time = QUEUE[0] try: - await _do_send(bot, user, user_type, msg) + await _do_send(bot, send_target, msg_factory) except Exception as e: await asyncio.sleep(MESSGE_SEND_INTERVAL) QUEUE.popleft() if retry_time > 0: - QUEUE.appendleft((bot, user, user_type, msg, retry_time - 1)) + QUEUE.appendleft((bot, send_target, msg_factory, retry_time - 1)) else: - msg_str = str(msg) + msg_str = str(msg_factory) if len(msg_str) > 50: msg_str = msg_str[:50] + "..." logger.warning(f"send msg err {e} {msg_str}") @@ -73,47 +56,27 @@ async def do_send_msgs(): return -async def _send_msgs_dispatch( - bot: Bot, - user, - user_type: Literal["private", "group", "group-forward"], - msg: Union[str, Message], -): +async def _send_msgs_dispatch(bot: Bot, send_target: PlatformTarget, msg: Sendable): if plugin_config.bison_use_queue: - QUEUE.append((bot, user, user_type, msg, plugin_config.bison_resend_times)) + QUEUE.append((bot, send_target, msg, plugin_config.bison_resend_times)) # len(QUEUE) before append was 0 if len(QUEUE) == 1: asyncio.create_task(do_send_msgs()) else: - await _do_send(bot, user, user_type, msg) + await _do_send(bot, send_target, msg) -async def send_msgs( - bot: Bot, user, user_type: Literal["private", "group"], msgs: list[Message] -): - if not plugin_config.bison_use_pic_merge or user_type == "private": +async def send_msgs(bot: Bot, send_target: PlatformTarget, msgs: list[MessageFactory]): + if not plugin_config.bison_use_pic_merge: for msg in msgs: - await _send_msgs_dispatch(bot, user, user_type, msg) + await _send_msgs_dispatch(bot, send_target, msg) return msgs = msgs.copy() if plugin_config.bison_use_pic_merge == 1: - await _send_msgs_dispatch(bot, user, "group", msgs.pop(0)) + await _send_msgs_dispatch(bot, send_target, msgs.pop(0)) if msgs: if len(msgs) == 1: # 只有一条消息序列就不合并转发 - await _send_msgs_dispatch(bot, user, "group", msgs.pop(0)) + await _send_msgs_dispatch(bot, send_target, msgs.pop(0)) else: - group_bot_info = await bot.get_group_member_info( - group_id=user, user_id=int(bot.self_id), no_cache=True - ) # 调用api获取群内bot的相关参数 - forward_msg = Message( - [ - MessageSegment.node_custom( - group_bot_info["user_id"], - nickname=group_bot_info["card"] or group_bot_info["nickname"], - content=msg, - ) - for msg in msgs - ] - ) - - await _send_msgs_dispatch(bot, user, "group-forward", forward_msg) + forward_message = AggregatedMessageFactory(list(msgs)) + await _send_msgs_dispatch(bot, send_target, forward_message) diff --git a/nonebot_bison/utils/__init__.py b/nonebot_bison/utils/__init__.py index c3013fd..007b989 100644 --- a/nonebot_bison/utils/__init__.py +++ b/nonebot_bison/utils/__init__.py @@ -4,9 +4,9 @@ from typing import Union import nonebot from bs4 import BeautifulSoup as bs -from nonebot.adapters.onebot.v11.message import MessageSegment from nonebot.log import default_format, logger from nonebot.plugin import require +from nonebot_plugin_saa import Image, MessageSegmentFactory, Text from ..plugin_config import plugin_config from .context import ProcessContext @@ -33,15 +33,15 @@ class Singleton(type): return cls._instances[cls] -async def parse_text(text: str) -> MessageSegment: +async def parse_text(text: str) -> MessageSegmentFactory: "return raw text if don't use pic, otherwise return rendered opcode" if plugin_config.bison_use_pic: require("nonebot_plugin_htmlrender") from nonebot_plugin_htmlrender import text_to_pic as _text_to_pic - return MessageSegment.image(await _text_to_pic(text)) + return Image(await _text_to_pic(text)) else: - return MessageSegment.text(text) + return Text(text) if not plugin_config.bison_skip_browser_check: diff --git a/poetry.lock b/poetry.lock index c820acd..5eb6176 100644 --- a/poetry.lock +++ b/poetry.lock @@ -276,13 +276,13 @@ beautifulsoup4 = "*" [[package]] name = "cashews" -version = "6.1.0" +version = "6.2.0" description = "cache tools with async power" optional = false python-versions = ">=3.7" files = [ - {file = "cashews-6.1.0-py3-none-any.whl", hash = "sha256:bd620e5fdb947949aca0f107f0275a48d46026c747695186d2507f89a48d1af9"}, - {file = "cashews-6.1.0.tar.gz", hash = "sha256:6ef7822500b8bc5ceadeaa284021ec7843d066e2f208612ec28a93064eef7d4a"}, + {file = "cashews-6.2.0-py3-none-any.whl", hash = "sha256:8a005fdb429efad8a99e2d8c3024a0a59bed35d49fe67da35a2cd9cb1d56cd89"}, + {file = "cashews-6.2.0.tar.gz", hash = "sha256:c197202336d1bfde732bf43c30c8fd3fdb5836700e8a81bdd25abcc5ea1df9c8"}, ] [package.extras] @@ -1467,23 +1467,20 @@ typing-extensions = ">=4.0.0" [[package]] name = "nonebot-plugin-send-anything-anywhere" -version = "0.2.4" +version = "0.2.5" description = "An adaptor for nonebot2 adaptors" optional = false -python-versions = "^3.8" -files = [] -develop = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "nonebot_plugin_send_anything_anywhere-0.2.5-py3-none-any.whl", hash = "sha256:58db714745f71693292533433e8881b00954e8ee6438ab3cd173048b5c5ce77f"}, + {file = "nonebot_plugin_send_anything_anywhere-0.2.5.tar.gz", hash = "sha256:2385e95ee22407faf59703074ad95070bedbdcf492c261ed380f26eccd202039"}, +] [package.dependencies] -nonebot2 = "^2.0.0rc1" -pydantic = "^1.10.5" -strenum = "^0.4.8" - -[package.source] -type = "git" -url = "https://github.com/felinae98/nonebot-plugin-send-anything-anywhere.git" -reference = "main" -resolved_reference = "7f8a57afc72b5b6a7f909935f1a87411bf597173" +anyio = ">=3.6.2,<4.0.0" +nonebot2 = ">=2.0.0,<3.0.0" +pydantic = ">=1.10.5,<2.0.0" +strenum = ">=0.4.8,<0.5.0" [[package]] name = "nonebot2" @@ -1537,6 +1534,22 @@ url = "https://github.com/nonebot/nonebug.git" reference = "master" resolved_reference = "bc82dbe078a1d42c515a3a783269011fb7e7bc9d" +[[package]] +name = "nonebug-saa" +version = "0.1.0" +description = "A nonebug helper for nonebot-plugin-send-anything-anything" +optional = false +python-versions = ">=3.10,<4.0" +files = [ + {file = "nonebug_saa-0.1.0-py3-none-any.whl", hash = "sha256:f2e79867f81d53ca08752382e6800ebc8c6242fbee94fa346ef48e887121b9d6"}, + {file = "nonebug_saa-0.1.0.tar.gz", hash = "sha256:bfe805d3a9d67e9e2585638b0f37185f69934d2bf433276c007850a9cb9ba52c"}, +] + +[package.dependencies] +nonebot-plugin-send-anything-anywhere = ">=0.2.2,<0.3.0" +nonebug = ">=0.3.1,<0.4.0" +pytest-mock = ">=3.10.0,<4.0.0" + [[package]] name = "noneprompt" version = "0.1.9" @@ -2873,4 +2886,4 @@ yaml = [] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0.0" -content-hash = "efba4feca911691e91af2b93cb810268f6e35a6e985811587e6b00999c2bd263" +content-hash = "109ee12685b1c143006df6969f7b1b3b0d3613a8a47fca3a7a8b005b64605902" diff --git a/pyproject.toml b/pyproject.toml index 69661f7..5a68388 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ nonebot-adapter-onebot = "^2.0.0-beta.1" nonebot-plugin-htmlrender = ">=0.2.0" nonebot-plugin-datastore = "^0.6.2" nonebot-plugin-apscheduler = "^0.2.0" -nonebot-plugin-send-anything-anywhere = {git = "https://github.com/felinae98/nonebot-plugin-send-anything-anywhere.git", rev = "main"} +nonebot-plugin-send-anything-anywhere = "^0.2.5" [tool.poetry.group.dev.dependencies] ipdb = "^0.13.4" @@ -51,6 +51,7 @@ pytest-mock = "^3.10.0" nonebug = { git = "https://github.com/nonebot/nonebug.git", rev = "master" } pytest-xdist = { extras = ["psutil"], version = "^3.1.0" } nb-cli = "^1.0.5" +nonebug-saa = "^0.1.0" [tool.poetry.extras] cli = ["anyio", "click", "typing-extensions"] diff --git a/tests/conftest.py b/tests/conftest.py index 4732b48..86dd982 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ from pathlib import Path import nonebot import pytest +from nonebot.adapters.onebot.v11 import Adapter as OnebotV11Adapter from nonebug import NONEBOT_INIT_KWARGS, App from pytest_mock.plugin import MockerFixture from sqlalchemy import delete @@ -19,6 +20,12 @@ def pytest_configure(config: pytest.Config) -> None: } +@pytest.fixture(scope="session", autouse=True) +def load_adapters(nonebug_init: None): + driver = nonebot.get_driver() + driver.register_adapter(OnebotV11Adapter) + + @pytest.fixture async def app(tmp_path: Path, request: pytest.FixtureRequest, mocker: MockerFixture): sys.path.append(str(Path(__file__).parent.parent / "src" / "plugins")) diff --git a/tests/test_config_manager_abort.py b/tests/test_config_manager_abort.py index da56639..683419a 100644 --- a/tests/test_config_manager_abort.py +++ b/tests/test_config_manager_abort.py @@ -1,6 +1,7 @@ import pytest import respx from httpx import Response +from nonebot_plugin_saa.nonebug import should_send_saa from nonebug.app import App from .platforms.utils import get_json @@ -279,7 +280,7 @@ async def test_abort_add_on_tag(app: App, init_scheduler): async def test_abort_del_sub(app: App, init_scheduler): from nonebot.adapters.onebot.v11.bot import Bot from nonebot.adapters.onebot.v11.message import Message - from nonebot_plugin_saa import TargetQQGroup + from nonebot_plugin_saa import MessageFactory, TargetQQGroup from nonebot_bison.config import config from nonebot_bison.config_manager import del_sub_matcher @@ -303,12 +304,13 @@ async def test_abort_del_sub(app: App, init_scheduler): ctx.receive_event(bot, event) ctx.should_pass_rule() ctx.should_pass_permission() - ctx.should_call_send( - event, - Message( + should_send_saa( + ctx, + MessageFactory( "订阅的帐号为:\n1 weibo 明日方舟Arknights 6279793937\n [图文] 明日方舟\n请输入要删除的订阅的序号\n输入'取消'中止" ), - True, + bot, + event=event, ) event_abort = fake_group_message_event( message=Message("取消"), sender=fake_admin_user diff --git a/tests/test_config_manager_query_del.py b/tests/test_config_manager_query_del.py index 9abb696..ba8018a 100644 --- a/tests/test_config_manager_query_del.py +++ b/tests/test_config_manager_query_del.py @@ -1,6 +1,7 @@ import pytest import respx from httpx import Response +from nonebot_plugin_saa.nonebug import should_send_saa from nonebug.app import App from .platforms.utils import get_json @@ -9,8 +10,9 @@ from .utils import fake_admin_user, fake_group_message_event @pytest.mark.asyncio async def test_query_sub(app: App, init_scheduler): - from nonebot.adapters.onebot.v11.message import Message - from nonebot_plugin_saa import TargetQQGroup + from nonebot import get_driver + from nonebot.adapters.onebot.v11 import Bot, Message + from nonebot_plugin_saa import MessageFactory, SupportedAdapters, TargetQQGroup from nonebot_bison.config import config from nonebot_bison.config_manager import query_sub_matcher @@ -26,13 +28,18 @@ async def test_query_sub(app: App, init_scheduler): ["明日方舟"], ) async with app.test_matcher(query_sub_matcher) as ctx: - bot = ctx.create_bot() + adapter = get_driver()._adapters[str(SupportedAdapters.onebot_v11)] + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event(message=Message("查询订阅"), to_me=True) ctx.receive_event(bot, event) ctx.should_pass_rule() ctx.should_pass_permission() - ctx.should_call_send( - event, Message("订阅的帐号为:\nweibo 明日方舟Arknights 6279793937 [图文] 明日方舟\n"), True + should_send_saa( + ctx, + MessageFactory("订阅的帐号为:\nweibo 明日方舟Arknights 6279793937 [图文] 明日方舟\n"), + bot, + event=event, ) @@ -40,7 +47,7 @@ async def test_query_sub(app: App, init_scheduler): async def test_del_sub(app: App, init_scheduler): from nonebot.adapters.onebot.v11.bot import Bot from nonebot.adapters.onebot.v11.message import Message - from nonebot_plugin_saa import TargetQQGroup + from nonebot_plugin_saa import MessageFactory, TargetQQGroup from nonebot_bison.config import config from nonebot_bison.config_manager import del_sub_matcher @@ -64,12 +71,13 @@ async def test_del_sub(app: App, init_scheduler): ctx.receive_event(bot, event) ctx.should_pass_rule() ctx.should_pass_permission() - ctx.should_call_send( - event, - Message( + should_send_saa( + ctx, + MessageFactory( "订阅的帐号为:\n1 weibo 明日方舟Arknights 6279793937\n [图文] 明日方舟\n请输入要删除的订阅的序号\n输入'取消'中止" ), - True, + bot, + event=event, ) event_1_err = fake_group_message_event( message=Message("2"), sender=fake_admin_user diff --git a/tests/test_custom_post.py b/tests/test_custom_post.py index c13b4f7..74390e9 100644 --- a/tests/test_custom_post.py +++ b/tests/test_custom_post.py @@ -1,28 +1,23 @@ -import base64 -import hashlib -import platform -from io import UnsupportedOperation from pathlib import Path import pytest import respx from httpx import Response -from nonebot.adapters.onebot.v11.message import MessageSegment +from nonebot_plugin_saa import Image, MessageSegmentFactory, Text from nonebug.app import App @pytest.fixture def ms_list(): - msg_segments: list[MessageSegment] = [] - msg_segments.append(MessageSegment.text("【Zc】每早合约日替攻略!")) + msg_segments: list[MessageSegmentFactory] = [] + msg_segments.append(Text("【Zc】每早合约日替攻略!")) msg_segments.append( - MessageSegment.image( - file="http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg", - cache=0, + Image( + image="http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg", ) ) - msg_segments.append(MessageSegment.text("来源: Bilibili直播 魔法Zc目录")) - msg_segments.append(MessageSegment.text("详情: https://live.bilibili.com/3044248")) + msg_segments.append(Text("来源: Bilibili直播 魔法Zc目录")) + msg_segments.append(Text("详情: https://live.bilibili.com/3044248")) return msg_segments @@ -35,7 +30,7 @@ def expected_md(): def test_gene_md(app: App, expected_md, ms_list): from nonebot_bison.post.custom_post import CustomPost - cp = CustomPost(message_segments=ms_list) + cp = CustomPost(ms_factories=ms_list) cp_md = cp._generate_md() assert cp_md == expected_md @@ -55,7 +50,7 @@ async def test_gene_pic(app: App, ms_list, expected_md): pic_router.mock(return_value=Response(200, stream=mock_pic)) - cp = CustomPost(message_segments=ms_list) + cp = CustomPost(ms_factories=ms_list) cp_pic_msg_md: str = cp._generate_md() assert cp_pic_msg_md == expected_md diff --git a/tests/test_render.py b/tests/test_render.py index 004ea9b..91b5e95 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -8,6 +8,8 @@ from nonebug.app import App @pytest.mark.asyncio @pytest.mark.render async def test_render(app: App): + from nonebot_plugin_saa import Image + from nonebot_bison.plugin_config import plugin_config from nonebot_bison.utils import parse_text @@ -23,4 +25,4 @@ VuePress 由两部分组成:第一部分是一个极简静态网站生成器 每一个由 VuePress 生成的页面都带有预渲染好的 HTML,也因此具有非常好的加载性能和搜索引擎优化(SEO)。同时,一旦页面被加载,Vue 将接管这些静态内容,并将其转换成一个完整的单页应用(SPA),其他的页面则会只在用户浏览到的时候才按需加载。 """ ) - assert res.type == "image" + assert isinstance(res, Image) diff --git a/tests/test_send.py b/tests/test_send.py index c45fb9e..3e9d422 100644 --- a/tests/test_send.py +++ b/tests/test_send.py @@ -3,36 +3,34 @@ import typing import pytest from flaky import flaky +from nonebot_plugin_saa.nonebug import should_send_saa from nonebug import App from pytest_mock.plugin import MockerFixture -if typing.TYPE_CHECKING: - from nonebot.adapters.onebot.v11.message import Message, MessageSegment - @pytest.mark.asyncio async def test_send_no_queue(app: App, mocker: MockerFixture): from nonebot.adapters.onebot.v11.bot import Bot - from nonebot.adapters.onebot.v11.message import Message + from nonebot_plugin_saa import MessageFactory, TargetQQGroup, TargetQQPrivate from nonebot_bison.plugin_config import plugin_config from nonebot_bison.send import send_msgs mocker.patch.object(plugin_config, "bison_use_queue", False) + + group_target = TargetQQGroup(group_id=1233) + private_target = TargetQQPrivate(user_id=666) + async with app.test_api() as ctx: bot = ctx.create_bot(base=Bot) assert isinstance(bot, Bot) - ctx.should_call_api( - "send_group_msg", {"group_id": "1233", "message": Message("msg1")}, True + should_send_saa(ctx, MessageFactory("msg1"), bot, target=group_target) + should_send_saa(ctx, MessageFactory("msg2"), bot, target=group_target) + should_send_saa(ctx, MessageFactory("priv"), bot, target=private_target) + await send_msgs( + bot, group_target, [MessageFactory("msg1"), MessageFactory("msg2")] ) - ctx.should_call_api( - "send_group_msg", {"group_id": "1233", "message": Message("msg2")}, True - ) - ctx.should_call_api( - "send_private_msg", {"user_id": "666", "message": Message("priv")}, True - ) - await send_msgs(bot, "1233", "group", [Message("msg1"), Message("msg2")]) - await send_msgs(bot, "666", "private", [Message("priv")]) + await send_msgs(bot, private_target, [MessageFactory("priv")]) assert ctx.wait_list.empty() @@ -40,11 +38,10 @@ async def test_send_no_queue(app: App, mocker: MockerFixture): async def test_send_queue(app: App, mocker: MockerFixture): import nonebot from nonebot.adapters.onebot.v11.bot import Bot - from nonebot.adapters.onebot.v11.message import Message, MessageSegment + from nonebot_plugin_saa import MessageFactory, TargetQQGroup - from nonebot_bison import send from nonebot_bison.plugin_config import plugin_config - from nonebot_bison.send import MESSGE_SEND_INTERVAL, do_send_msgs, send_msgs + from nonebot_bison.send import MESSGE_SEND_INTERVAL, send_msgs mocker.patch.object(plugin_config, "bison_use_queue", True) async with app.test_api() as ctx: @@ -53,39 +50,28 @@ async def test_send_queue(app: App, mocker: MockerFixture): bot = nonebot.get_bot() assert isinstance(bot, Bot) assert bot == new_bot - ctx.should_call_api( - "send_group_msg", - {"group_id": "1233", "message": [MessageSegment.text("msg")]}, - True, - ) - ctx.should_call_api( - "send_group_msg", - {"group_id": "1233", "message": [MessageSegment.text("msg2")]}, - True, - ) - await send_msgs(bot, "1233", "group", [Message("msg")]) - await send_msgs(bot, "1233", "group", [Message("msg2")]) + + target = TargetQQGroup(group_id=1233) + should_send_saa(ctx, MessageFactory("msg"), bot, target=target) + should_send_saa(ctx, MessageFactory("msg2"), bot, target=target) + + await send_msgs(bot, target, [MessageFactory("msg")]) + await send_msgs(bot, target, [MessageFactory("msg2")]) assert not ctx.wait_list.empty() await asyncio.sleep(2 * MESSGE_SEND_INTERVAL) assert ctx.wait_list.empty() -def gen_node(id, name, content: "Message"): - from nonebot.adapters.onebot.v11.message import MessageSegment - - return MessageSegment.node_custom(id, name, content) - - -def _merge_messge(nodes): - from nonebot.adapters.onebot.v11.message import Message - - return Message(nodes) - - @pytest.mark.asyncio async def test_send_merge_no_queue(app: App): from nonebot.adapters.onebot.v11.bot import Bot - from nonebot.adapters.onebot.v11.message import Message, MessageSegment + from nonebot_plugin_saa import ( + AggregatedMessageFactory, + Image, + MessageFactory, + TargetQQGroup, + Text, + ) from nonebot_bison.plugin_config import plugin_config from nonebot_bison.send import send_msgs @@ -96,100 +82,46 @@ async def test_send_merge_no_queue(app: App): async with app.test_api() as ctx: bot = ctx.create_bot(base=Bot, self_id="8888") assert isinstance(bot, Bot) - message = [ - Message(MessageSegment.text("test msg")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - ] - ctx.should_call_api( - "send_group_msg", - {"group_id": 633, "message": Message(MessageSegment.text("test msg"))}, - None, - ) - ctx.should_call_api( - "send_group_msg", - {"group_id": 633, "message": message[1]}, - None, - ) - await send_msgs(bot, 633, "group", message) + target = TargetQQGroup(group_id=633) message = [ - Message(MessageSegment.text("test msg")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - Message(MessageSegment.image("https://picsum.photos/200/300")), + MessageFactory(Text("test msg")), + MessageFactory(Image("https://picsum.photos/200/300")), ] - ctx.should_call_api( - "send_group_msg", - {"group_id": 633, "message": Message(MessageSegment.text("test msg"))}, - None, - ) - ctx.should_call_api( - "get_group_member_info", - {"group_id": 633, "user_id": 8888, "no_cache": True}, - {"user_id": 8888, "card": "admin", "nickname": "adminuser"}, - ) - merged_message = _merge_messge( - [gen_node(8888, "admin", message[1]), gen_node(8888, "admin", message[2])] - ) - ctx.should_call_api( - "send_group_forward_msg", - {"group_id": 633, "messages": merged_message}, - None, - ) - await send_msgs(bot, 633, "group", message) + should_send_saa(ctx, message[0], bot, target=target) + should_send_saa(ctx, message[1], bot, target=target) + await send_msgs(bot, target, message) message = [ - Message(MessageSegment.text("test msg")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - Message(MessageSegment.image("https://picsum.photos/200/300")), + MessageFactory(Text("test msg")), + MessageFactory(Image("https://picsum.photos/200/300")), + MessageFactory(Image("https://picsum.photos/200/300")), ] - ctx.should_call_api( - "send_group_msg", - {"group_id": 633, "message": Message(MessageSegment.text("test msg"))}, - None, - ) - ctx.should_call_api( - "get_group_member_info", - {"group_id": 633, "user_id": 8888, "no_cache": True}, - {"user_id": 8888, "card": None, "nickname": "adminuser"}, - ) - merged_message = _merge_messge( - [ - gen_node(8888, "adminuser", message[1]), - gen_node(8888, "adminuser", message[2]), - gen_node(8888, "adminuser", message[3]), - ] - ) - ctx.should_call_api( - "send_group_forward_msg", - {"group_id": 633, "messages": merged_message}, - None, - ) - await send_msgs(bot, 633, "group", message) + should_send_saa(ctx, message[0], bot, target=target) + should_send_saa(ctx, AggregatedMessageFactory(message[1:]), bot, target=target) + # import ipdb; ipdb.set_trace() + await send_msgs(bot, target, message) - # private user should not send in forward message = [ - Message(MessageSegment.text("test msg")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - Message(MessageSegment.image("https://picsum.photos/200/300")), + MessageFactory(Text("test msg")), + MessageFactory(Image("https://picsum.photos/200/300")), + MessageFactory(Image("https://picsum.photos/200/300")), + MessageFactory(Image("https://picsum.photos/200/300")), ] - ctx.should_call_api( - "send_private_msg", - {"user_id": 633, "message": Message(MessageSegment.text("test msg"))}, - None, - ) - ctx.should_call_api( - "send_private_msg", {"user_id": 633, "message": message[1]}, None - ) - ctx.should_call_api( - "send_private_msg", {"user_id": 633, "message": message[2]}, None - ) - await send_msgs(bot, 633, "private", message) + should_send_saa(ctx, message[0], bot, target=target) + should_send_saa(ctx, AggregatedMessageFactory(message[1:]), bot, target=target) + await send_msgs(bot, target, message) async def test_send_merge2_no_queue(app: App): from nonebot.adapters.onebot.v11.bot import Bot - from nonebot.adapters.onebot.v11.message import Message, MessageSegment + from nonebot_plugin_saa import ( + AggregatedMessageFactory, + Image, + MessageFactory, + TargetQQGroup, + Text, + ) from nonebot_bison.plugin_config import plugin_config from nonebot_bison.send import send_msgs @@ -200,26 +132,12 @@ async def test_send_merge2_no_queue(app: App): async with app.test_api() as ctx: bot = ctx.create_bot(base=Bot, self_id="8888") assert isinstance(bot, Bot) + target = TargetQQGroup(group_id=633) + message = [ - Message(MessageSegment.text("test msg")), - Message(MessageSegment.image("https://picsum.photos/200/300")), - Message(MessageSegment.image("https://picsum.photos/200/300")), + MessageFactory(Text("test msg")), + MessageFactory(Image("https://picsum.photos/200/300")), + MessageFactory(Image("https://picsum.photos/200/300")), ] - ctx.should_call_api( - "get_group_member_info", - {"group_id": 633, "user_id": 8888, "no_cache": True}, - {"user_id": 8888, "card": "admin", "nickname": "adminuser"}, - ) - merged_message = _merge_messge( - [ - gen_node(8888, "admin", message[0]), - gen_node(8888, "admin", message[1]), - gen_node(8888, "admin", message[2]), - ] - ) - ctx.should_call_api( - "send_group_forward_msg", - {"group_id": 633, "messages": merged_message}, - None, - ) - await send_msgs(bot, 633, "group", message) + should_send_saa(ctx, AggregatedMessageFactory(message), bot, target=target) + await send_msgs(bot, target, message)