From a34e433ebf3038f08692447cf13df4a0f3e373f2 Mon Sep 17 00:00:00 2001 From: BalconyJH <73932916+BalconyJH@users.noreply.github.com> Date: Sat, 21 Dec 2024 20:46:00 +0800 Subject: [PATCH 1/5] :bug: Fix help_type in create_help_img may be none and cause AttributeError. (#1791) --- zhenxun/builtin_plugins/help/_data_source.py | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/zhenxun/builtin_plugins/help/_data_source.py b/zhenxun/builtin_plugins/help/_data_source.py index 19a3af8a..f1dd71f7 100644 --- a/zhenxun/builtin_plugins/help/_data_source.py +++ b/zhenxun/builtin_plugins/help/_data_source.py @@ -27,17 +27,18 @@ async def create_help_img(session: Uninfo, group_id: str | None): session: Uninfo group_id: 群号 """ - help_type: str = base_config.get("type") - if help_type.lower() == "html": - result = BuildImage.open(await build_html_image(group_id)) - elif help_type.lower() == "zhenxun": - result = BuildImage.open(await build_zhenxun_image(session, group_id)) - else: - result = await build_normal_image(group_id) - if group_id: - await result.save(GROUP_HELP_PATH / f"{group_id}.png") - else: - await result.save(SIMPLE_HELP_IMAGE) + help_type = base_config.get("type", "").strip().lower() + + match help_type: + case "html": + result = BuildImage.open(await build_html_image(group_id)) + case "zhenxun": + result = BuildImage.open(await build_zhenxun_image(session, group_id)) + case _: + result = await build_normal_image(group_id) + + save_path = GROUP_HELP_PATH / f"{group_id}.png" if group_id else SIMPLE_HELP_IMAGE + await result.save(save_path) async def get_user_allow_help(user_id: str) -> list[PluginType]: From 3a197c0c1d3865a6fd81d852915a33f8e905c8fa Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:52:17 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E9=81=93=E5=85=B7=E9=94=99=E8=AF=AF=20(#1790)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/builtin_plugins/shop/__init__.py | 42 +++++++++----------- zhenxun/builtin_plugins/shop/_data_source.py | 4 +- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/zhenxun/builtin_plugins/shop/__init__.py b/zhenxun/builtin_plugins/shop/__init__.py index 5c48c3f2..104ae88e 100644 --- a/zhenxun/builtin_plugins/shop/__init__.py +++ b/zhenxun/builtin_plugins/shop/__init__.py @@ -14,7 +14,6 @@ from nonebot_plugin_alconna import ( on_alconna, store_true, ) -from nonebot_plugin_session import EventSession from nonebot_plugin_uninfo import Uninfo from zhenxun.configs.utils import BaseBlock, PluginExtraData @@ -23,6 +22,7 @@ from zhenxun.utils.depends import UserName from zhenxun.utils.enum import BlockType, PluginType from zhenxun.utils.exception import GoodsNotFound from zhenxun.utils.message import MessageUtils +from zhenxun.utils.platform import PlatformUtils from ._data_source import ShopManage, gold_rank @@ -107,35 +107,29 @@ _matcher.shortcut( @_matcher.assign("$main") -async def _(session: EventSession, arparma: Arparma): +async def _(session: Uninfo, arparma: Arparma): image = await ShopManage.build_shop_image() logger.info("查看商店", arparma.header_result, session=session) await MessageUtils.build_message(image).send() @_matcher.assign("my-cost") -async def _(session: EventSession, arparma: Arparma): - if session.id1: - logger.info("查看金币", arparma.header_result, session=session) - gold = await ShopManage.my_cost(session.id1, session.platform) - await MessageUtils.build_message(f"你的当前余额: {gold}").send(reply_to=True) - else: - await MessageUtils.build_message("用户id为空...").send(reply_to=True) +async def _(session: Uninfo, arparma: Arparma): + logger.info("查看金币", arparma.header_result, session=session) + gold = await ShopManage.my_cost( + session.user.id, PlatformUtils.get_platform(session) + ) + await MessageUtils.build_message(f"你的当前余额: {gold}").send(reply_to=True) @_matcher.assign("my-props") -async def _(session: EventSession, arparma: Arparma, nickname: str = UserName()): - if session.id1: - logger.info("查看道具", arparma.header_result, session=session) - if image := await ShopManage.my_props( - session.id1, - nickname, - session.platform, - ): - await MessageUtils.build_message(image.pic2bytes()).finish(reply_to=True) - return await MessageUtils.build_message("你的道具为空捏...").send(reply_to=True) - else: - await MessageUtils.build_message("用户id为空...").send(reply_to=True) +async def _(session: Uninfo, arparma: Arparma, nickname: str = UserName()): + logger.info("查看道具", arparma.header_result, session=session) + if image := await ShopManage.my_props( + session.user.id, nickname, PlatformUtils.get_platform(session) + ): + await MessageUtils.build_message(image.pic2bytes()).finish(reply_to=True) + return await MessageUtils.build_message("你的道具为空捏...").send(reply_to=True) @_matcher.assign("buy") @@ -163,7 +157,7 @@ async def _( bot: Bot, event: Event, message: UniMsg, - session: EventSession, + session: Uninfo, arparma: Arparma, name: Match[str], num: Query[int] = AlconnaQuery("num", 1), @@ -177,7 +171,9 @@ async def _( bot, event, session, message, name.result, num.result, "" ) logger.info( - f"使用道具 {name}, 数量: {num}", arparma.header_result, session=session + f"使用道具 {name.result}, 数量: {num.result}", + arparma.header_result, + session=session, ) if isinstance(result, str): await MessageUtils.build_message(result).send(reply_to=True) diff --git a/zhenxun/builtin_plugins/shop/_data_source.py b/zhenxun/builtin_plugins/shop/_data_source.py index 45e0f754..ec8c3d97 100644 --- a/zhenxun/builtin_plugins/shop/_data_source.py +++ b/zhenxun/builtin_plugins/shop/_data_source.py @@ -332,7 +332,9 @@ class ShopManage: return f"{goods_info.goods_name} 单次使用最大数量为{param.max_num_limit}..." await cls.run_before_after(goods, param, "before", **kwargs) result = await cls.__run(goods, param, session, **kwargs) - await UserConsole.use_props(session.id1, goods_info.uuid, num, session.platform) # type: ignore + await UserConsole.use_props( + session.user.id, goods_info.uuid, num, PlatformUtils.get_platform(session) + ) await cls.run_before_after(goods, param, "after", **kwargs) if not result and param.send_success_msg: result = f"使用道具 {goods.name} {num} 次成功!" From 4291cda244e9947b628d58185b25022c9aece56f Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:09:06 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=87=AA=E6=A3=80=E6=8F=92=E4=BB=B6=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A7=84=E5=88=99=E5=88=A4=E6=96=AD=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0nonebug=E9=85=8D=E7=BD=AE=20(#1792)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :arrow_up: Expand the range of nonebug version restrictions and update nonebot-plugin-alconna. * :white_check_mark: Update pytest configuration. * :white_check_mark: Add pytest hook to tag async tests with session-scoped event loop. * ⚡️ 优化自检插件逻辑,增加规则判断 --------- Co-authored-by: BalconyJH --- poetry.lock | 22 ++++----- pyproject.toml | 3 +- requirements.txt | 2 +- tests/conftest.py | 8 +++ zhenxun/builtin_plugins/check/__init__.py | 60 +++++++++++++---------- 5 files changed, 55 insertions(+), 40 deletions(-) diff --git a/poetry.lock b/poetry.lock index e04606b2..861717b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2227,23 +2227,23 @@ reference = "aliyun" [[package]] name = "nonebot-plugin-alconna" -version = "0.53.1" +version = "0.54.1" description = "Alconna Adapter for Nonebot" optional = false python-versions = ">=3.9" files = [ - {file = "nonebot_plugin_alconna-0.53.1-py3-none-any.whl", hash = "sha256:a7734d4c7f1b2fedec586b7d235da119ffaa23d0e17a8aa0a0acc0e76a1662d0"}, - {file = "nonebot_plugin_alconna-0.53.1.tar.gz", hash = "sha256:dc9eef228bb819d05ce9816f8a5a1bead455ee85d1ea7d5e409f9fb11665805e"}, + {file = "nonebot_plugin_alconna-0.54.1-py3-none-any.whl", hash = "sha256:4edb4b081cd64ce37717c7a92d31aadd2cf287a5a0adc2ac86ed82d9bcad5048"}, + {file = "nonebot_plugin_alconna-0.54.1.tar.gz", hash = "sha256:66fae03120b8eff25bb0027d65f149e399aa6f73c7585ebdd388d1904cecdeee"}, ] [package.dependencies] -arclet-alconna = ">=1.8.31,<2.0" +arclet-alconna = ">=1.8.35,<2.0" arclet-alconna-tools = ">=0.7.10" importlib-metadata = ">=4.13.0" -nepattern = ">=0.7.4,<1.0" +nepattern = ">=0.7.7,<1.0" nonebot-plugin-waiter = ">=0.6.0" nonebot2 = ">=2.3.0" -tarina = ">=0.6.3,<0.7" +tarina = ">=0.6.8,<0.7" [package.source] type = "legacy" @@ -2420,13 +2420,13 @@ reference = "aliyun" [[package]] name = "nonebug" -version = "0.3.7" +version = "0.4.3" description = "nonebot2 test framework" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.9,<4.0" files = [ - {file = "nonebug-0.3.7-py3-none-any.whl", hash = "sha256:c39f462aafe20660602a8b789a575db6c9346ab5b6f1985eb9d98b861528299a"}, - {file = "nonebug-0.3.7.tar.gz", hash = "sha256:8a75183400681f34eafc7caa2bb6dd511c3b5660c59264f1c379a088c7ac2247"}, + {file = "nonebug-0.4.3-py3-none-any.whl", hash = "sha256:eb9b2c8ab3d45459a4f00ebdaae90729e9e9628575c0685fca4c871dd4cfd425"}, + {file = "nonebug-0.4.3.tar.gz", hash = "sha256:e9592d2c7a42b76f4a336f98726cba92e1300f6bab155c8822e865919786f10c"}, ] [package.dependencies] @@ -4932,4 +4932,4 @@ reference = "aliyun" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "d2be32cc7ba17b302a015de327b8e8d815bee185400547f4024fd93e141d3ac8" +content-hash = "37341271bbd43fcb6af878cb09491459cf21b13c24659a5fd94f9c2b5248a5d8" diff --git a/pyproject.toml b/pyproject.toml index 79545671..c45473f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ nonebot-plugin-uninfo = "^0.4.1" nonebot-plugin-alconna = "^0.54.0" [tool.poetry.dev-dependencies] -nonebug = "^0.4.2" +nonebug = "^0.4" pytest-cov = "^5.0.0" pytest-mock = "^3.6.1" pytest-asyncio = "^0.23.5" @@ -137,6 +137,7 @@ disableBytesTypePromotions = true [tool.pytest.ini_options] asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/requirements.txt b/requirements.txt index e3bbf0ab..75f1aa73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -57,7 +57,7 @@ nonebot-adapter-discord==0.1.8 ; python_version >= "3.10" and python_version < " nonebot-adapter-dodo==0.1.4 ; python_version >= "3.10" and python_version < "4.0" nonebot-adapter-kaiheila==0.3.4 ; python_version >= "3.10" and python_version < "4.0" nonebot-adapter-onebot==2.4.6 ; python_version >= "3.10" and python_version < "4.0" -nonebot-plugin-alconna==0.53.1 ; python_version >= "3.10" and python_version < "4.0" +nonebot-plugin-alconna==0.54.1 ; python_version >= "3.10" and python_version < "4.0" nonebot-plugin-apscheduler==0.3.0 ; python_version >= "3.10" and python_version < "4.0" nonebot-plugin-htmlrender==0.3.5 ; python_version >= "3.10" and python_version < "4.0" nonebot-plugin-session==0.2.3 ; python_version >= "3.10" and python_version < "4.0" diff --git a/tests/conftest.py b/tests/conftest.py index a1b7bf2f..cf29b754 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,6 +8,7 @@ from nonebug import NONEBOT_INIT_KWARGS from nonebug.app import App from nonebug.mixin.process import MatcherContext import pytest +from pytest_asyncio import is_async_test from pytest_mock import MockerFixture from respx import MockRouter @@ -22,6 +23,13 @@ def get_response_json(path: str) -> dict: ) +def pytest_collection_modifyitems(items: list[pytest.Item]): + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) + + def pytest_configure(config: pytest.Config) -> None: config.stash[NONEBOT_INIT_KWARGS] = { "driver": "~fastapi+~httpx+~websockets", diff --git a/zhenxun/builtin_plugins/check/__init__.py b/zhenxun/builtin_plugins/check/__init__.py index 047d44f6..1d4d0628 100644 --- a/zhenxun/builtin_plugins/check/__init__.py +++ b/zhenxun/builtin_plugins/check/__init__.py @@ -2,7 +2,7 @@ from nonebot import on_notice from nonebot.adapters.onebot.v11 import PokeNotifyEvent from nonebot.permission import SUPERUSER from nonebot.plugin import PluginMetadata -from nonebot.rule import to_me +from nonebot.rule import Rule, to_me from nonebot_plugin_alconna import Alconna, on_alconna from nonebot_plugin_htmlrender import template_to_pic @@ -41,9 +41,32 @@ __plugin_meta__ = PluginMetadata( ) +def commandRule() -> Rule: + return Rule(lambda: Config.get_config("check", "type") in {"message", "mix"}) + + +def noticeRule() -> Rule: + return Rule(lambda: Config.get_config("check", "type") in {"poke", "mix"}) + + +_self_check_matcher = _self_check_matcher = on_alconna( + Alconna("自检"), + rule=to_me() & commandRule(), + permission=SUPERUSER, + block=True, + priority=1, +) + +_self_check_poke_matcher = on_notice( + priority=5, + permission=SUPERUSER, + block=False, + rule=notice_rule(PokeNotifyEvent) & to_me() & noticeRule(), +) + + async def handle_self_check(): try: - logger.info("触发自检") data = await get_status_info() image = await template_to_pic( template_path=str((TEMPLATE_PATH / "check").absolute()), @@ -56,34 +79,17 @@ async def handle_self_check(): wait=2, ) await MessageUtils.build_message(image).send() - logger.info("自检成功") + logger.info("自检成功", "自检") except Exception as e: await MessageUtils.build_message(f"自检失败: {e}").send() - logger.error("自检失败", e=e) + logger.error("自检失败", "自检", e=e) -check_type = Config.get_config("check", "type") - -if check_type in {"message", "mix"}: - # message - _self_check_matcher = on_alconna( - Alconna("自检"), rule=to_me(), permission=SUPERUSER, block=True, priority=1 - ) - - @_self_check_matcher.handle() - async def handle_message_check(): - await handle_self_check() +@_self_check_matcher.handle() +async def handle_message_check(): + await handle_self_check() -if check_type in {"poke", "mix"}: - # poke - _self_check_poke_matcher = on_notice( - priority=5, - permission=SUPERUSER, - block=False, - rule=notice_rule(PokeNotifyEvent) & to_me(), - ) - - @_self_check_poke_matcher.handle() - async def handle_poke_check(event: PokeNotifyEvent): - await handle_self_check() +@_self_check_poke_matcher.handle() +async def handle_poke_check(): + await handle_self_check() From c84e99d084a7fa3c51ccd2772193692186fa76d5 Mon Sep 17 00:00:00 2001 From: AkashiCoin Date: Mon, 23 Dec 2024 10:09:38 +0800 Subject: [PATCH 4/5] =?UTF-8?q?:bug:=20=20fix(github=5Futils):=20=E9=80=82?= =?UTF-8?q?=E9=85=8D=E6=8F=92=E4=BB=B6=E4=BB=93=E5=BA=93=E6=A0=B9=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E8=AF=AD=E6=B3=95=20(#1784)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/builtin_plugins/plugin_store/data_source.py | 3 +++ zhenxun/utils/github_utils/models.py | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/zhenxun/builtin_plugins/plugin_store/data_source.py b/zhenxun/builtin_plugins/plugin_store/data_source.py index 21c977aa..4def6c39 100644 --- a/zhenxun/builtin_plugins/plugin_store/data_source.py +++ b/zhenxun/builtin_plugins/plugin_store/data_source.py @@ -229,12 +229,15 @@ class ShopManage: continue else: raise ValueError("所有API获取插件文件失败,请检查网络连接") + if module_path == ".": + module_path = "" files = repo_api.get_files( module_path=module_path.replace(".", "/") + ("" if is_dir else ".py"), is_dir=is_dir, ) download_urls = [await repo_info.get_raw_download_urls(file) for file in files] base_path = BASE_PATH / "plugins" if is_external else BASE_PATH + base_path = base_path if module_path else base_path / repo_info.repo download_paths: list[Path | str] = [base_path / file for file in files] logger.debug(f"插件下载路径: {download_paths}", "插件管理") result = await AsyncHttpx.gather_download_file(download_urls, download_paths) diff --git a/zhenxun/utils/github_utils/models.py b/zhenxun/utils/github_utils/models.py index 14f9a5ac..972889bb 100644 --- a/zhenxun/utils/github_utils/models.py +++ b/zhenxun/utils/github_utils/models.py @@ -106,8 +106,8 @@ class JsdelivrStrategy: def get_file_paths(self, module_path: str, is_dir: bool = True) -> list[str]: """获取文件路径""" paths = module_path.split("/") - filename = "" if is_dir else paths[-1] - paths = paths if is_dir else paths[:-1] + filename = "" if is_dir and module_path else paths[-1] + paths = paths if is_dir and module_path else paths[:-1] cur_file = self.body for path in paths: # 导航到正确的目录 cur_file = next( @@ -141,7 +141,8 @@ class JsdelivrStrategy: ] return [] - return collect_files(cur_file, "/".join(paths), filename) + files = collect_files(cur_file, "/".join(paths), filename) + return files if module_path else [f[1:] for f in files] @classmethod @cached(ttl=CACHED_API_TTL) @@ -208,7 +209,7 @@ class GitHubStrategy: for file in tree_info.tree if file.type == TreeType.FILE and file.path.startswith(module_path) - and (not is_dir or file.path[len(module_path)] == "/") + and (not is_dir or file.path[len(module_path)] == "/" or not module_path) ] @classmethod From 012a23008b199cb2b47da4a5a0228702c572ae93 Mon Sep 17 00:00:00 2001 From: AkashiCoin Date: Mon, 23 Dec 2024 10:09:52 +0800 Subject: [PATCH 5/5] :tada: chore(version): Update version to v0.2.4-4291cda (#1776) --- __version__ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__version__ b/__version__ index cb06b100..6bc0b553 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.2.4-e363d29 +__version__: v0.2.4-4291cda