♻️ use saa

This commit is contained in:
felinae98 2023-03-24 21:17:52 +08:00
parent 4118329bb0
commit da8e988ee9
15 changed files with 223 additions and 308 deletions

View File

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

View File

@ -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 = []

View File

@ -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 += "{}<br>".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 += "{}<br>".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`全局配置

View File

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

View File

@ -115,8 +115,7 @@ class Scheduler:
else:
await send_msgs(
bot,
user.user,
user.user_type,
user,
await send_post.generate_messages(),
)

View File

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

View File

@ -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:

47
poetry.lock generated
View File

@ -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"

View File

@ -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"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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