diff --git a/nonebot_bison/config/subs_io/__init__.py b/nonebot_bison/config/subs_io/__init__.py index 5e4762b..55ab017 100644 --- a/nonebot_bison/config/subs_io/__init__.py +++ b/nonebot_bison/config/subs_io/__init__.py @@ -1,3 +1,3 @@ -from .subs_io import nbesf_parser, subscribes_export, subscribes_import +from .subs_io import subscribes_export, subscribes_import -__all__ = ["subscribes_export", "subscribes_import", "nbesf_parser"] +__all__ = ["subscribes_export", "subscribes_import"] diff --git a/nonebot_bison/config/subs_io/nbesf_model.py b/nonebot_bison/config/subs_io/nbesf_model.py deleted file mode 100644 index 04002aa..0000000 --- a/nonebot_bison/config/subs_io/nbesf_model.py +++ /dev/null @@ -1,81 +0,0 @@ -""" nbexf is Nonebot Bison Enchangable Subscribes File! """ - -from pydantic import BaseModel - -from ...types import Category, Tag - -# ===== nbesf 定义格式 ====== # -NBESF_VERSION = 1 - - -class UserHead(BaseModel, orm_mode=True): - """Bison快递包收货信息""" - - type: str - uid: int - - def __hash__(self): - return hash((self.type, self.uid)) - - -class Target(BaseModel, orm_mode=True): - """Bsion快递包发货信息""" - - target_name: str - target: str - platform_name: str - default_schedule_weight: int - - -class SubPayload(BaseModel, orm_mode=True): - """Bison快递包里的单件货物""" - - categories: list[Category] - tags: list[Tag] - target: Target - - -class SubPack(BaseModel): - """Bison给指定用户派送的快递包""" - - user: UserHead - subs: list[SubPayload] - - -class SubGroup(BaseModel): - """ - Bison的全部订单(按用户分组) - - 结构参见`nbesf_model.py` - """ - - version: int = NBESF_VERSION # 表示nbesf格式版本,从1开始 - groups: list[SubPack] - - -# ======================= # - - -class SubReceipt(BaseModel): - """ - 快递包中每件货物的收据 - - 导入订阅时的Model - """ - - user: int - user_type: str - target: str - target_name: str - platform_name: str - cats: list[Category] - tags: list[Tag] - # default_schedule_weight: int - - -class NBESFVerMatchErr(Exception): - ... - - -class NBESFParseErr(Exception): - ... diff --git a/nonebot_bison/config/subs_io/nbesf_model/__init__.py b/nonebot_bison/config/subs_io/nbesf_model/__init__.py new file mode 100644 index 0000000..8f35648 --- /dev/null +++ b/nonebot_bison/config/subs_io/nbesf_model/__init__.py @@ -0,0 +1,6 @@ +""" nbesf is Nonebot Bison Enchangable Subscribes File! """ + +from . import v1, v2 +from .base import NBESFBase + +__all__ = ["v1", "v2", "NBESFBase"] diff --git a/nonebot_bison/config/subs_io/nbesf_model/base.py b/nonebot_bison/config/subs_io/nbesf_model/base.py new file mode 100644 index 0000000..11ae2bb --- /dev/null +++ b/nonebot_bison/config/subs_io/nbesf_model/base.py @@ -0,0 +1,31 @@ +from abc import ABC + +from nonebot_plugin_saa.utils import AllSupportedPlatformTarget as UserInfo +from pydantic import BaseModel + +from ....types import Category, Tag + + +class NBESFBase(BaseModel, ABC): + + version: int # 表示nbesf格式版本,有效版本从1开始 + groups: list = list() + + class Config: + orm_mode = True + + +class SubReceipt(BaseModel): + """ + 快递包中每件货物的收据 + + 导入订阅时的Model + """ + + user: UserInfo + target: str + target_name: str + platform_name: str + cats: list[Category] + tags: list[Tag] + # default_schedule_weight: int diff --git a/nonebot_bison/config/subs_io/nbesf_model/v1.py b/nonebot_bison/config/subs_io/nbesf_model/v1.py new file mode 100644 index 0000000..a9a71c7 --- /dev/null +++ b/nonebot_bison/config/subs_io/nbesf_model/v1.py @@ -0,0 +1,111 @@ +""" nbesf is Nonebot Bison Enchangable Subscribes File! ver.1""" + +from functools import partial +from typing import Any + +from nonebot.log import logger +from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate +from pydantic import BaseModel + +from ....types import Category, Tag +from ...db_config import SubscribeDupException, config +from ..utils import NBESFParseErr +from .base import NBESFBase, SubReceipt + +# ===== nbesf 定义格式 ====== # +NBESF_VERSION = 1 + + +class UserHead(BaseModel, orm_mode=True): + """Bison快递包收货信息""" + + type: str + uid: int + + +class Target(BaseModel, orm_mode=True): + """Bsion快递包发货信息""" + + target_name: str + target: str + platform_name: str + default_schedule_weight: int + + +class SubPayload(BaseModel, orm_mode=True): + """Bison快递包里的单件货物""" + + categories: list[Category] + tags: list[Tag] + target: Target + + +class SubPack(BaseModel): + """Bison给指定用户派送的快递包""" + + user: UserHead + subs: list[SubPayload] + + +class SubGroup( + NBESFBase, +): + """ + Bison的全部订单(按用户分组) + + 结构参见`nbesf_model`下的对应版本 + """ + + version = NBESF_VERSION + groups: list[SubPack] + + +# ======================= # + + +async def subs_receipt_gen(nbesf_data: SubGroup): + for item in nbesf_data.groups: + + match item.user.type: + case "group": + user = TargetQQGroup(group_id=item.user.uid) + case "private": + user = TargetQQPrivate(user_id=item.user.uid) + case _: + raise NotImplementedError(f"nbesf v1 不支持的用户类型:{item.user.type}") + + sub_receipt = partial(SubReceipt, user=user) + + for sub in item.subs: + receipt = sub_receipt( + target=sub.target.target, + target_name=sub.target.target_name, + platform_name=sub.target.platform_name, + cats=sub.categories, + tags=sub.tags, + ) + try: + await config.add_subscribe( + receipt.user, **receipt.dict(exclude={"user"}) + ) + except SubscribeDupException: + logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") + except Exception as e: + logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") + else: + logger.success(f"添加订阅条目 {repr(receipt)} 成功!") + + +def nbesf_parser(raw_data: Any) -> SubGroup: + try: + if isinstance(raw_data, str): + nbesf_data = SubGroup.parse_raw(raw_data) + else: + nbesf_data = SubGroup.parse_obj(raw_data) + + except Exception as e: + logger.error("数据解析失败,该数据格式可能不满足NBESF格式标准!") + raise NBESFParseErr("数据解析失败") from e + else: + logger.success("NBESF文件解析成功.") + return nbesf_data diff --git a/nonebot_bison/config/subs_io/nbesf_model/v2.py b/nonebot_bison/config/subs_io/nbesf_model/v2.py new file mode 100644 index 0000000..9d76bcc --- /dev/null +++ b/nonebot_bison/config/subs_io/nbesf_model/v2.py @@ -0,0 +1,94 @@ +""" nbesf is Nonebot Bison Enchangable Subscribes File! ver.2""" + +from functools import partial +from typing import Any + +from nonebot.log import logger +from nonebot_plugin_saa.utils import AllSupportedPlatformTarget +from pydantic import BaseModel + +from ....types import Category, Tag +from ...db_config import SubscribeDupException, config +from ..utils import NBESFParseErr +from .base import NBESFBase, SubReceipt + +# ===== nbesf 定义格式 ====== # +NBESF_VERSION = 2 + + +class Target(BaseModel, orm_mode=True): + """Bsion快递包发货信息""" + + target_name: str + target: str + platform_name: str + default_schedule_weight: int + + +class SubPayload(BaseModel, orm_mode=True): + """Bison快递包里的单件货物""" + + categories: list[Category] + tags: list[Tag] + target: Target + + +class SubPack(BaseModel): + """Bison给指定用户派送的快递包""" + + # user_target: Bison快递包收货信息 + user_target: AllSupportedPlatformTarget + subs: list[SubPayload] + + +class SubGroup(NBESFBase): + """ + Bison的全部订单(按用户分组) + + 结构参见`nbesf_model`下的对应版本 + """ + + version: int = NBESF_VERSION + groups: list[SubPack] + + +# ======================= # + + +async def subs_receipt_gen(nbesf_data: SubGroup): + for item in nbesf_data.groups: + + sub_receipt = partial(SubReceipt, user=item.user_target) + + for sub in item.subs: + receipt = sub_receipt( + target=sub.target.target, + target_name=sub.target.target_name, + platform_name=sub.target.platform_name, + cats=sub.categories, + tags=sub.tags, + ) + try: + await config.add_subscribe( + receipt.user, **receipt.dict(exclude={"user"}) + ) + except SubscribeDupException: + logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") + except Exception as e: + logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") + else: + logger.success(f"添加订阅条目 {repr(receipt)} 成功!") + + +def nbesf_parser(raw_data: Any) -> SubGroup: + try: + if isinstance(raw_data, str): + nbesf_data = SubGroup.parse_raw(raw_data) + else: + nbesf_data = SubGroup.parse_obj(raw_data) + + except Exception as e: + logger.error("数据解析失败,该数据格式可能不满足NBESF格式标准!") + raise NBESFParseErr("数据解析失败") from e + else: + return nbesf_data diff --git a/nonebot_bison/config/subs_io/subs_io.py b/nonebot_bison/config/subs_io/subs_io.py index c4ee765..9a16472 100644 --- a/nonebot_bison/config/subs_io/subs_io.py +++ b/nonebot_bison/config/subs_io/subs_io.py @@ -1,25 +1,20 @@ from collections import defaultdict -from typing import Any, Callable, cast +from typing import Callable, cast from nonebot.log import logger from nonebot_plugin_datastore.db import create_session +from nonebot_plugin_saa import PlatformTarget from sqlalchemy import select from sqlalchemy.orm.strategy_options import selectinload from sqlalchemy.sql.selectable import Select from ..db_model import Subscribe, User -from .nbesf_model import ( - NBESFParseErr, - NBESFVerMatchErr, - SubGroup, - SubPack, - SubPayload, - UserHead, -) -from .utils import subs_receipt_gen_ver_1 +from .nbesf_model import NBESFBase, v1, v2 +from .utils import NBESFVerMatchErr -async def subscribes_export(selector: Callable[[Select], Select]) -> SubGroup: +async def subscribes_export(selector: Callable[[Select], Select]) -> v2.SubGroup: + """ 将Bison订阅导出为 Nonebot Bison Exchangable Subscribes File 标准格式的 SubGroup 类型数据 @@ -37,25 +32,28 @@ async def subscribes_export(selector: Callable[[Select], Select]) -> SubGroup: user_stmt = cast(Select[tuple[User]], user_stmt) user_data = await sess.scalars(user_stmt) - groups: list[SubPack] = [] - user_id_sub_dict: dict[int, list[SubPayload]] = defaultdict(list) + groups: list[v2.SubPack] = [] + user_id_sub_dict: dict[int, list[v2.SubPayload]] = defaultdict(list) for sub in sub_data: - sub_paylaod = SubPayload.from_orm(sub) + sub_paylaod = v2.SubPayload.from_orm(sub) user_id_sub_dict[sub.user_id].append(sub_paylaod) for user in user_data: - user_head = UserHead.from_orm(user) - sub_pack = SubPack(user=user_head, subs=user_id_sub_dict[user.id]) + assert isinstance(user, User) + sub_pack = v2.SubPack( + user_target=PlatformTarget.deserialize(user.user_target), + subs=user_id_sub_dict[user.id], + ) groups.append(sub_pack) - sub_group = SubGroup(groups=groups) + sub_group = v2.SubGroup(groups=groups) return sub_group async def subscribes_import( - nbesf_data: SubGroup, + nbesf_data: NBESFBase, ): """ 从 Nonebot Bison Exchangable Subscribes File 标准格式的数据中导入订阅 @@ -67,21 +65,11 @@ async def subscribes_import( logger.info("开始添加订阅流程") match nbesf_data.version: case 1: - await subs_receipt_gen_ver_1(nbesf_data) + assert isinstance(nbesf_data, v1.SubGroup) + await v1.subs_receipt_gen(nbesf_data) + case 2: + assert isinstance(nbesf_data, v2.SubGroup) + await v2.subs_receipt_gen(nbesf_data) case _: raise NBESFVerMatchErr(f"不支持的NBESF版本:{nbesf_data.version}") logger.info("订阅流程结束,请检查所有订阅记录是否全部添加成功") - - -def nbesf_parser(raw_data: Any) -> SubGroup: - try: - if isinstance(raw_data, str): - nbesf_data = SubGroup.parse_raw(raw_data) - else: - nbesf_data = SubGroup.parse_obj(raw_data) - - except Exception as e: - logger.error("数据解析失败,该数据格式可能不满足NBESF格式标准!") - raise NBESFParseErr("数据解析失败") from e - else: - return nbesf_data diff --git a/nonebot_bison/config/subs_io/utils.py b/nonebot_bison/config/subs_io/utils.py index c12ff90..06f8df6 100644 --- a/nonebot_bison/config/subs_io/utils.py +++ b/nonebot_bison/config/subs_io/utils.py @@ -1,28 +1,6 @@ -from functools import partial - -from nonebot.log import logger - -from ..db_config import SubscribeDupException, config -from .nbesf_model import NBESF_VERSION, SubGroup, SubReceipt +class NBESFVerMatchErr(Exception): + ... -async def subs_receipt_gen_ver_1(nbesf_data: SubGroup): - for item in nbesf_data.groups: - sub_receipt = partial(SubReceipt, user=item.user.uid, user_type=item.user.type) - - for sub in item.subs: - receipt = sub_receipt( - target=sub.target.target, - target_name=sub.target.target_name, - platform_name=sub.target.platform_name, - cats=sub.categories, - tags=sub.tags, - ) - try: - await config.add_subscribe(**receipt.dict()) - except SubscribeDupException: - logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") - except Exception as e: - logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") - else: - logger.success(f"添加订阅条目 {repr(receipt)} 成功!") +class NBESFParseErr(Exception): + ... diff --git a/nonebot_bison/script/cli.py b/nonebot_bison/script/cli.py index 7f70808..48883c8 100644 --- a/nonebot_bison/script/cli.py +++ b/nonebot_bison/script/cli.py @@ -8,8 +8,8 @@ from typing import Any, Callable, Coroutine, TypeVar from nonebot.log import logger -from ..config.subs_io import nbesf_parser, subscribes_export, subscribes_import -from ..config.subs_io.nbesf_model import SubGroup +from ..config.subs_io import subscribes_export, subscribes_import +from ..config.subs_io.nbesf_model import v1, v2 from ..scheduler.manager import init_scheduler try: @@ -82,7 +82,7 @@ async def subs_export(path: Path, format: str): export_file = path / f"bison_subscribes_export_{int(time.time())}.{format}" logger.info("正在获取订阅信息...") - export_data: SubGroup = await subscribes_export(lambda x: x) + export_data: v2.SubGroup = await subscribes_export(lambda x: x) with export_file.open("w", encoding="utf-8") as f: match format: @@ -90,7 +90,15 @@ async def subs_export(path: Path, format: str): logger.info("正在导出为yaml...") pyyaml = import_yaml_module() - pyyaml.safe_dump(export_data.dict(), f, sort_keys=False) + # 由于 nbesf v2 中的user_target使用了AllSupportedPlatformTarget, 因此不能使用safe_dump + # 下文引自 https://pyyaml.org/wiki/PyYAMLDocumentation + # safe_dump(data, stream=None) serializes the given Python object into the stream. + # If stream is None, it returns the produced stream. + # safe_dump produces only standard YAML tags and cannot represent an arbitrary Python object. + # 进行以下曲线救国方案 + json_data = json.dumps(export_data.dict(), ensure_ascii=False) + yaml_data = pyyaml.safe_load(json_data) + pyyaml.safe_dump(yaml_data, f, sort_keys=False) case "json": logger.info("正在导出为json...") @@ -135,7 +143,17 @@ async def subs_import(path: str, format: str): case _: raise click.BadParameter(message=f"不支持的导入格式: {format}") - nbesf_data = nbesf_parser(import_items) + assert isinstance(import_items, dict) + ver = int(import_items.get("version", 0)) + logger.info(f"NBESF版本: {ver}") + match ver: + case 1: + nbesf_data = v1.nbesf_parser(import_items) + case 2: + nbesf_data = v2.nbesf_parser(import_items) + case _: + raise NotImplementedError("不支持的NBESF版本") + await subscribes_import(nbesf_data) diff --git a/tests/subs_io/static/subs_export.json b/tests/subs_io/static/v1/subs_export.json similarity index 100% rename from tests/subs_io/static/subs_export.json rename to tests/subs_io/static/v1/subs_export.json diff --git a/tests/subs_io/static/subs_export.yaml b/tests/subs_io/static/v1/subs_export.yaml similarity index 100% rename from tests/subs_io/static/subs_export.yaml rename to tests/subs_io/static/v1/subs_export.yaml diff --git a/tests/subs_io/static/subs_export_all_illegal.json b/tests/subs_io/static/v1/subs_export_all_illegal.json similarity index 100% rename from tests/subs_io/static/subs_export_all_illegal.json rename to tests/subs_io/static/v1/subs_export_all_illegal.json diff --git a/tests/subs_io/static/subs_export_has_subdup_err.json b/tests/subs_io/static/v1/subs_export_has_subdup_err.json similarity index 100% rename from tests/subs_io/static/subs_export_has_subdup_err.json rename to tests/subs_io/static/v1/subs_export_has_subdup_err.json diff --git a/tests/subs_io/static/v2/subs_export.json b/tests/subs_io/static/v2/subs_export.json new file mode 100644 index 0000000..ee338f6 --- /dev/null +++ b/tests/subs_io/static/v2/subs_export.json @@ -0,0 +1,57 @@ +{ + "version": 2, + "groups": [ + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 1232 + }, + "subs": [ + { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + ] + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 2342 + }, + "subs": [ + { + "categories": [], + "tags": [ + "kaltsit", + "amiya" + ], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + }, + { + "categories": [ + 1, + 2 + ], + "tags": [], + "target": { + "target_name": "bilibili_name", + "target": "bilibili_id", + "platform_name": "bilibili", + "default_schedule_weight": 10 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/subs_io/static/v2/subs_export.yaml b/tests/subs_io/static/v2/subs_export.yaml new file mode 100644 index 0000000..27c94ff --- /dev/null +++ b/tests/subs_io/static/v2/subs_export.yaml @@ -0,0 +1,35 @@ +version: 2 +groups: +- subs: + - categories: [] + tags: [] + target: + default_schedule_weight: 10 + platform_name: weibo + target: weibo_id + target_name: weibo_name + user_target: + platform_type: QQ Group + group_id: 123552 +- subs: + - categories: [] + tags: + - kaltsit + - amiya + target: + default_schedule_weight: 10 + platform_name: weibo + target: weibo_id + target_name: weibo_name + - categories: + - 1 + - 2 + tags: [] + target: + default_schedule_weight: 10 + platform_name: bilibili + target: bilibili_id + target_name: bilibili_name + user_target: + platform_type: QQ Group + group_id: 234662 \ No newline at end of file diff --git a/tests/subs_io/static/v2/subs_export_all_illegal.json b/tests/subs_io/static/v2/subs_export_all_illegal.json new file mode 100644 index 0000000..ef4cfac --- /dev/null +++ b/tests/subs_io/static/v2/subs_export_all_illegal.json @@ -0,0 +1,91 @@ +{ + "version": 1, + "groups": [ + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 123 + }, + "subs": [ + { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + ] + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 234 + }, + "subs": [ + { + "tags": [ + "kaltsit", + "amiya" + ], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + }, + { + "categories": [ + 1, + 2 + ], + "tags": [], + "target": [ + { + "target_name": "bilibili_name", + "target": "bilibili_id", + "platform_name": "bilibili", + "default_schedule_weight": 10 + } + ] + } + ] + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 123 + }, + "subs": { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name2", + "target": "weibo_id2", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 123 + }, + "subs": [ + { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name2", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/subs_io/static/v2/subs_export_has_subdup_err.json b/tests/subs_io/static/v2/subs_export_has_subdup_err.json new file mode 100644 index 0000000..c15a6b4 --- /dev/null +++ b/tests/subs_io/static/v2/subs_export_has_subdup_err.json @@ -0,0 +1,90 @@ +{ + "version": 2, + "groups": [ + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 1232 + }, + "subs": [ + { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + ] + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 2342 + }, + "subs": [ + { + "categories": [], + "tags": [ + "kaltsit", + "amiya" + ], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + }, + { + "categories": [ + 1, + 2 + ], + "tags": [], + "target": { + "target_name": "bilibili_name", + "target": "bilibili_id", + "platform_name": "bilibili", + "default_schedule_weight": 10 + } + } + ] + }, + { + "user_target": { + "platform_type": "QQ Group", + "group_id": 1232 + }, + "subs": [ + { + "categories": [], + "tags": [], + "target": { + "target_name": "weibo_name", + "target": "weibo_id", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + }, + { + "categories": [ + 2, + 6 + ], + "tags": [ + "poca" + ], + "target": { + "target_name": "weibo_name2", + "target": "weibo_id2", + "platform_name": "weibo", + "default_schedule_weight": 10 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/subs_io/test_cli.py b/tests/subs_io/test_cli.py index 1c3dc6d..968d3d0 100644 --- a/tests/subs_io/test_cli.py +++ b/tests/subs_io/test_cli.py @@ -35,13 +35,14 @@ def test_cli_help(app: App): async def test_subs_export(app: App, tmp_path: Path): + from nonebot_plugin_saa import TargetQQGroup + from nonebot_bison.config.db_config import config from nonebot_bison.script.cli import cli, run_sync from nonebot_bison.types import Target as TTarget await config.add_subscribe( - user=123, - user_type="group", + TargetQQGroup(group_id=123), target=TTarget("weibo_id"), target_name="weibo_name", platform_name="weibo", @@ -49,8 +50,7 @@ async def test_subs_export(app: App, tmp_path: Path): tags=[], ) await config.add_subscribe( - user=234, - user_type="group", + TargetQQGroup(group_id=234), target=TTarget("weibo_id"), target_name="weibo_name", platform_name="weibo", @@ -58,8 +58,7 @@ async def test_subs_export(app: App, tmp_path: Path): tags=["kaltsit", "amiya"], ) await config.add_subscribe( - user=234, - user_type="group", + TargetQQGroup(group_id=234), target=TTarget("bilibili_id"), target_name="bilibili_name", platform_name="bilibili", @@ -80,7 +79,8 @@ async def test_subs_export(app: App, tmp_path: Path): assert result.exit_code == 0 file_path = Path.cwd() / "bison_subscribes_export_1.json" assert file_path.exists() - assert file_path.stat().st_size + assert '"version": 2' in file_path.read_text() + assert '"group_id": 123' in file_path.read_text() # 是否导出到指定已存在文件夹 data_dir = tmp_path / "data" @@ -89,7 +89,8 @@ async def test_subs_export(app: App, tmp_path: Path): assert result.exit_code == 0 file_path2 = data_dir / "bison_subscribes_export_1.json" assert file_path2.exists() - assert file_path2.stat().st_size + assert '"version": 2' in file_path2.read_text() + assert '"group_id": 123' in file_path2.read_text() # 是否拒绝导出到不存在的文件夹 result = await run_sync(runner.invoke)( @@ -104,7 +105,9 @@ async def test_subs_export(app: App, tmp_path: Path): assert result.exit_code == 0 file_path3 = tmp_path / "bison_subscribes_export_1.yaml" assert file_path3.exists() - assert file_path3.stat().st_size + assert "version: 2" in file_path3.read_text() + assert "group_id: 123" in file_path3.read_text() + assert "platform_type: QQ Group" in file_path3.read_text() # 是否允许以未支持的格式导出 result = await run_sync(runner.invoke)( @@ -113,14 +116,14 @@ async def test_subs_export(app: App, tmp_path: Path): assert result.exit_code == 2 -async def test_subs_import(app: App, tmp_path): +async def test_subs_import_v1(app: App, tmp_path): from nonebot_bison.config.db_config import config from nonebot_bison.script.cli import cli, run_sync assert len(await config.list_subs_with_all_info()) == 0 mock_file: Path = tmp_path / "1.json" - mock_file.write_text(get_file("subs_export.json")) + mock_file.write_text(get_file("v1/subs_export.json")) runner = CliRunner() @@ -135,7 +138,38 @@ async def test_subs_import(app: App, tmp_path): assert len(await config.list_subs_with_all_info()) == 3 mock_file: Path = tmp_path / "2.yaml" - mock_file.write_text(get_file("subs_export.yaml")) + mock_file.write_text(get_file("v1/subs_export.yaml")) + + result = await run_sync(runner.invoke)( + cli, ["import", "-p", str(mock_file), "--format=yml"] + ) + assert result.exit_code == 0 + assert len(await config.list_subs_with_all_info()) == 6 + + +async def test_sub_import_v2(app: App, tmp_path): + from nonebot_bison.config.db_config import config + from nonebot_bison.script.cli import cli, run_sync + + assert len(await config.list_subs_with_all_info()) == 0 + + mock_file: Path = tmp_path / "1.json" + mock_file.write_text(get_file("v2/subs_export.json")) + + runner = CliRunner() + + result = await run_sync(runner.invoke)(cli, ["import"]) + assert result.exit_code == 2 + + result = await run_sync(runner.invoke)(cli, ["import", "-p"]) + assert result.exit_code == 2 + + result = await run_sync(runner.invoke)(cli, ["import", "-p", str(mock_file)]) + assert result.exit_code == 0 + assert len(await config.list_subs_with_all_info()) == 3 + + mock_file: Path = tmp_path / "2.yaml" + mock_file.write_text(get_file("v2/subs_export.yaml")) result = await run_sync(runner.invoke)( cli, ["import", "-p", str(mock_file), "--format=yml"] diff --git a/tests/subs_io/test_subs_io.py b/tests/subs_io/test_subs_io.py index 8ed5b39..2a0fb5b 100644 --- a/tests/subs_io/test_subs_io.py +++ b/tests/subs_io/test_subs_io.py @@ -1,22 +1,21 @@ -from pathlib import Path - import pytest from nonebug.app import App -from .utils import get_file, get_json +from .utils import get_json async def test_subs_export(app: App, init_scheduler): import time + from nonebot_plugin_saa import TargetQQGroup + from nonebot_bison.config.db_config import config from nonebot_bison.config.db_model import User from nonebot_bison.config.subs_io import subscribes_export from nonebot_bison.types import Target as TTarget await config.add_subscribe( - user=123, - user_type="group", + TargetQQGroup(group_id=1232), target=TTarget("weibo_id"), target_name="weibo_name", platform_name="weibo", @@ -24,8 +23,7 @@ async def test_subs_export(app: App, init_scheduler): tags=[], ) await config.add_subscribe( - user=234, - user_type="group", + TargetQQGroup(group_id=2342), target=TTarget("weibo_id"), target_name="weibo_name", platform_name="weibo", @@ -33,8 +31,7 @@ async def test_subs_export(app: App, init_scheduler): tags=["kaltsit", "amiya"], ) await config.add_subscribe( - user=234, - user_type="group", + TargetQQGroup(group_id=2342), target=TTarget("bilibili_id"), target_name="bilibili_name", platform_name="bilibili", @@ -46,46 +43,95 @@ async def test_subs_export(app: App, init_scheduler): assert len(data) == 3 nbesf_data = await subscribes_export(lambda x: x) - assert nbesf_data.dict() == get_json("subs_export.json") + print(nbesf_data.dict()) + assert nbesf_data.dict() == get_json("v2/subs_export.json") nbesf_data_user_234 = await subscribes_export( - lambda stmt: stmt.where(User.uid == 234, User.type == "group") + lambda stmt: stmt.where( + User.user_target == {"platform_type": "QQ Group", "group_id": 2342} + ) ) assert len(nbesf_data_user_234.groups) == 1 assert len(nbesf_data_user_234.groups[0].subs) == 2 - assert nbesf_data_user_234.groups[0].user.dict() == {"uid": 234, "type": "group"} + assert nbesf_data_user_234.groups[0].user_target == { + "group_id": 2342, + "platform_type": "QQ Group", + } async def test_subs_import(app: App, init_scheduler): from nonebot_bison.config.db_config import config - from nonebot_bison.config.subs_io import nbesf_parser, subscribes_import + from nonebot_bison.config.subs_io import subscribes_import + from nonebot_bison.config.subs_io.nbesf_model import v1, v2 - nbesf_data = nbesf_parser(get_json("subs_export.json")) + # v1 + nbesf_data_v1 = v1.nbesf_parser(get_json("v1/subs_export.json")) - await subscribes_import(nbesf_data) + await subscribes_import(nbesf_data_v1) data = await config.list_subs_with_all_info() assert len(data) == 3 + # v2 + nbesf_data_v2 = v2.nbesf_parser(get_json("v2/subs_export.json")) + + await subscribes_import(nbesf_data_v2) + data = await config.list_subs_with_all_info() + assert len(data) == 6 + async def test_subs_import_dup_err(app: App, init_scheduler): from nonebot_bison.config.db_config import config - from nonebot_bison.config.subs_io import nbesf_parser, subscribes_import + from nonebot_bison.config.subs_io import subscribes_import + from nonebot_bison.config.subs_io.nbesf_model import v1, v2 - nbesf_data = nbesf_parser(get_json("subs_export_has_subdup_err.json")) + # v1 + nbesf_data_v1 = v1.nbesf_parser(get_json("v1/subs_export_has_subdup_err.json")) - await subscribes_import(nbesf_data) + await subscribes_import(nbesf_data_v1) data = await config.list_subs_with_all_info() assert len(data) == 4 + # v2 + nbesf_data_v2 = v2.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json")) + + await subscribes_import(nbesf_data_v2) + + data = await config.list_subs_with_all_info() + + assert len(data) == 8 + + +async def test_subs_import_version_disorder(app: App, init_scheduler): + from nonebot_bison.config.db_config import config + from nonebot_bison.config.subs_io import subscribes_import + from nonebot_bison.config.subs_io.nbesf_model import v1, v2 + from nonebot_bison.config.subs_io.utils import NBESFParseErr + + # use v2 parse v1 + with pytest.raises(NBESFParseErr): + nbesf_data_v1 = v1.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json")) + + # use v1 parse v2 + with pytest.raises(NBESFParseErr): + nbesf_data_v2 = v2.nbesf_parser(get_json("v1/subs_export_has_subdup_err.json")) + + with pytest.raises(AssertionError): + nbesf_data = v2.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json")) + nbesf_data.version = 1 + await subscribes_import(nbesf_data) + async def test_subs_import_all_fail(app: App, init_scheduler): """只要文件格式有任何一个错误, 都不会进行订阅""" - from nonebot_bison.config.subs_io import nbesf_parser - from nonebot_bison.config.subs_io.nbesf_model import NBESFParseErr + from nonebot_bison.config.subs_io.nbesf_model import v1, v2 + from nonebot_bison.config.subs_io.nbesf_model.v1 import NBESFParseErr with pytest.raises(NBESFParseErr): - nbesf_data = nbesf_parser(get_json("subs_export_all_illegal.json")) + nbesf_data = v1.nbesf_parser(get_json("v1/subs_export_all_illegal.json")) + + with pytest.raises(NBESFParseErr): + nbesf_data = v2.nbesf_parser(get_json("v2/subs_export_all_illegal.json"))