From 9eca6a97ca13cec97031e1a78f1a357134464494 Mon Sep 17 00:00:00 2001 From: AkashiCoin Date: Mon, 2 Sep 2024 12:00:27 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=95=86=E5=BA=97=E6=A3=80=E6=9F=A5=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=97=AE=E9=A2=98=20(#1597)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 修复插件商店检查插件更新问题 * 🐛 恶意命令检测问题 --- .../plugin_store/test_plugin_store.py | 232 +++++++++++++++++- tests/config.py | 2 + tests/content/plugin_store/github_sub.py | 24 ++ tests/content/plugin_store/jitang.py | 17 ++ tests/content/plugin_store/search_image.py | 18 ++ zhenxun/builtin_plugins/hooks/chkdsk_hook.py | 19 +- .../builtin_plugins/plugin_store/config.py | 1 + .../plugin_store/data_source.py | 25 +- 8 files changed, 311 insertions(+), 27 deletions(-) create mode 100644 tests/content/plugin_store/github_sub.py create mode 100644 tests/content/plugin_store/jitang.py create mode 100644 tests/content/plugin_store/search_image.py diff --git a/tests/builtin_plugins/plugin_store/test_plugin_store.py b/tests/builtin_plugins/plugin_store/test_plugin_store.py index 69eaaf3d..aaf548fd 100644 --- a/tests/builtin_plugins/plugin_store/test_plugin_store.py +++ b/tests/builtin_plugins/plugin_store/test_plugin_store.py @@ -13,6 +13,7 @@ from nonebot.adapters.onebot.v11.event import GroupMessageEvent from tests.utils import _v11_group_message_event from tests.config import BotId, UserId, GroupId, MessageId +from tests.utils import get_content_bytes as _get_content_bytes from tests.utils import get_response_json as _get_response_json @@ -20,6 +21,10 @@ def get_response_json(file: str) -> dict: return _get_response_json(Path() / "plugin_store", file=file) +def get_content_bytes(file: str) -> bytes: + return _get_content_bytes(Path() / "plugin_store", file) + + def init_mocked_api(mocked_api: MockRouter) -> None: mocked_api.get( "https://data.jsdelivr.com/v1/packages/gh/xuanerwa/zhenxun_github_sub@main", @@ -40,15 +45,15 @@ def init_mocked_api(mocked_api: MockRouter) -> None: mocked_api.get( "https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/search_image/__init__.py", name="search_image_plugin_file_init", - ).respond(content=b"") + ).respond(content=get_content_bytes("search_image.py")) mocked_api.get( "https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/alapi/jitang.py", name="jitang_plugin_file", - ).respond(content=b"") + ).respond(content=get_content_bytes("jitang.py")) mocked_api.get( "https://raw.githubusercontent.com/xuanerwa/zhenxun_github_sub/main/github_sub/__init__.py", name="github_sub_plugin_file_init", - ).respond(content=b"") + ).respond(content=get_content_bytes("github_sub.py")) async def test_add_plugin_basic( @@ -207,7 +212,7 @@ async def test_add_plugin_extra( assert (mock_base_path / "plugins" / "github_sub" / "__init__.py").is_file() -async def test_update_plugin_basic( +async def test_update_plugin_basic_need_update( app: App, mocker: MockerFixture, mocked_api: MockRouter, @@ -215,7 +220,7 @@ async def test_update_plugin_basic( tmp_path: Path, ) -> None: """ - 测试更新基础插件 + 测试更新基础插件,插件需要更新 """ from zhenxun.builtin_plugins.plugin_store import _matcher @@ -224,6 +229,10 @@ async def test_update_plugin_basic( "zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH", new=tmp_path / "zhenxun", ) + mock_base_path = mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins", + return_value=[("search_image", "0.0")], + ) plugin_id = 1 @@ -259,6 +268,59 @@ async def test_update_plugin_basic( assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file() +async def test_update_plugin_basic_is_new( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试更新基础插件,插件是最新版 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH", + new=tmp_path / "zhenxun", + ) + mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins", + return_value=[("search_image", "0.1")], + ) + + plugin_id = 1 + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = f"更新插件 {plugin_id}" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message=f"正在更新插件 Id: {plugin_id}"), + result=None, + bot=bot, + ) + ctx.should_call_send( + event=event, + message=Message(message="插件 识图 已是最新版本"), + result=None, + bot=bot, + ) + assert mocked_api["basic_plugins"].called + assert mocked_api["extra_plugins"].called + + async def test_remove_plugin( app: App, mocker: MockerFixture, @@ -280,8 +342,8 @@ async def test_remove_plugin( plugin_path = mock_base_path / "plugins" / "search_image" plugin_path.mkdir(parents=True, exist_ok=True) - with open(plugin_path / "__init__.py", "w") as f: - f.write("") + with open(plugin_path / "__init__.py", "wb") as f: + f.write(get_content_bytes("search_image.py")) plugin_id = 1 @@ -307,3 +369,159 @@ async def test_remove_plugin( assert mocked_api["basic_plugins"].called assert mocked_api["extra_plugins"].called assert not (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file() + + +async def test_plugin_not_exist_add( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试插件不存在,添加插件 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + plugin_id = -1 + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = f"添加插件 {plugin_id}" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message=f"正在添加插件 Id: {plugin_id}"), + result=None, + bot=bot, + ) + ctx.should_call_send( + event=event, + message=Message(message="插件ID不存在..."), + result=None, + bot=bot, + ) + + +async def test_plugin_not_exist_update( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试插件不存在,更新插件 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + plugin_id = -1 + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = f"更新插件 {plugin_id}" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID_2, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message=f"正在更新插件 Id: {plugin_id}"), + result=None, + bot=bot, + ) + ctx.should_call_send( + event=event, + message=Message(message="插件ID不存在..."), + result=None, + bot=bot, + ) + + +async def test_plugin_not_exist_remove( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试插件不存在,移除插件 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + plugin_id = -1 + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = f"移除插件 {plugin_id}" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID_2, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message="插件ID不存在..."), + result=None, + bot=bot, + ) + + +async def test_plugin_not_exist_search( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试插件不存在,搜索插件 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + plugin_id = -1 + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = f"搜索插件 {plugin_id}" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID_3, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message="未找到相关插件..."), + result=None, + bot=bot, + ) diff --git a/tests/config.py b/tests/config.py index 7a54cd45..d472c0a8 100644 --- a/tests/config.py +++ b/tests/config.py @@ -18,3 +18,5 @@ class GroupId: class MessageId: MESSAGE_ID = 30001 MESSAGE_ID_2 = 30002 + MESSAGE_ID_3 = 30003 + MESSAGE_ID_4 = 30004 diff --git a/tests/content/plugin_store/github_sub.py b/tests/content/plugin_store/github_sub.py new file mode 100644 index 00000000..17b349d5 --- /dev/null +++ b/tests/content/plugin_store/github_sub.py @@ -0,0 +1,24 @@ +from nonebot.plugin import PluginMetadata + +from zhenxun.configs.utils import PluginExtraData + +__plugin_meta__ = PluginMetadata( + name="github订阅", + description="订阅github用户或仓库", + usage=""" + usage: + github新Comment,PR,Issue等提醒 + 指令: + 添加github ['用户'/'仓库'] [用户名/{owner/repo}] + 删除github [用户名/{owner/repo}] + 查看github + 示例:添加github订阅 用户 HibiKier + 示例:添加gb订阅 仓库 HibiKier/zhenxun_bot + 示例:添加github 用户 HibiKier + 示例:删除gb订阅 HibiKier + """.strip(), + extra=PluginExtraData( + author="xuanerwa", + version="0.7", + ).dict(), +) diff --git a/tests/content/plugin_store/jitang.py b/tests/content/plugin_store/jitang.py new file mode 100644 index 00000000..ae1207e7 --- /dev/null +++ b/tests/content/plugin_store/jitang.py @@ -0,0 +1,17 @@ +from nonebot.plugin import PluginMetadata + +from zhenxun.configs.utils import PluginExtraData + +__plugin_meta__ = PluginMetadata( + name="鸡汤", + description="喏,亲手为你煮的鸡汤", + usage=""" + 不喝点什么感觉有点不舒服 + 指令: + 鸡汤 + """.strip(), + extra=PluginExtraData( + author="HibiKier", + version="0.1", + ).dict(), +) diff --git a/tests/content/plugin_store/search_image.py b/tests/content/plugin_store/search_image.py new file mode 100644 index 00000000..9b1c14b0 --- /dev/null +++ b/tests/content/plugin_store/search_image.py @@ -0,0 +1,18 @@ +from nonebot.plugin import PluginMetadata + +from zhenxun.configs.utils import PluginExtraData + +__plugin_meta__ = PluginMetadata( + name="识图", + description="以图搜图,看破本源", + usage=""" + 识别图片 [二次元图片] + 指令: + 识图 [图片] + """.strip(), + extra=PluginExtraData( + author="HibiKier", + version="0.1", + menu_type="一些工具", + ).dict(), +) diff --git a/zhenxun/builtin_plugins/hooks/chkdsk_hook.py b/zhenxun/builtin_plugins/hooks/chkdsk_hook.py index 5fb66a09..915a1969 100644 --- a/zhenxun/builtin_plugins/hooks/chkdsk_hook.py +++ b/zhenxun/builtin_plugins/hooks/chkdsk_hook.py @@ -2,19 +2,19 @@ import time from collections import defaultdict from nonebot.adapters import Event -from nonebot.adapters.onebot.v11 import Bot -from nonebot.exception import IgnoredException -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor from nonebot.typing import T_State +from nonebot.matcher import Matcher from nonebot_plugin_alconna import At +from nonebot.adapters.onebot.v11 import Bot +from nonebot.message import run_preprocessor +from nonebot.exception import IgnoredException from nonebot_plugin_session import EventSession -from zhenxun.configs.config import Config -from zhenxun.models.ban_console import BanConsole from zhenxun.services.log import logger +from zhenxun.configs.config import Config from zhenxun.utils.enum import PluginType from zhenxun.utils.message import MessageUtils +from zhenxun.models.ban_console import BanConsole malicious_check_time = Config.get_config("hook", "MALICIOUS_CHECK_TIME") malicious_ban_count = Config.get_config("hook", "MALICIOUS_BAN_COUNT") @@ -36,12 +36,12 @@ class BanCheckLimiter: self.default_check_time = default_check_time self.default_count = default_count - def add(self, key: str | int | float): + def add(self, key: str | float): if self.mint[key] == 1: self.mtime[key] = time.time() self.mint[key] += 1 - def check(self, key: str | int | float) -> bool: + def check(self, key: str | float) -> bool: if time.time() - self.mtime[key] > self.default_check_time: self.mtime[key] = time.time() self.mint[key] = 0 @@ -76,6 +76,7 @@ async def _( PluginType.HIDDEN, PluginType.DEPENDANT, PluginType.ADMIN, + PluginType.SUPERUSER, ]: return else: @@ -101,7 +102,7 @@ async def _( await MessageUtils.build_message( [ At(flag="user", target=user_id), - f"检测到恶意触发命令,您将被封禁 30 分钟", + "检测到恶意触发命令,您将被封禁 30 分钟", ] ).send() logger.debug( diff --git a/zhenxun/builtin_plugins/plugin_store/config.py b/zhenxun/builtin_plugins/plugin_store/config.py index 8c904698..af55c145 100644 --- a/zhenxun/builtin_plugins/plugin_store/config.py +++ b/zhenxun/builtin_plugins/plugin_store/config.py @@ -15,6 +15,7 @@ CONFIG_INDEX_CDN_URL = "https://cdn.jsdelivr.net/gh/zhenxun-org/zhenxun_bot_plug """插件索引库信息文件cdn""" DEFAULT_GITHUB_URL = "https://github.com/zhenxun-org/zhenxun_bot_plugins/tree/main" +"""默认github仓库地址""" GITHUB_REPO_URL_PATTERN = re.compile( r"^https://github.com/(?P[^/]+)/(?P[^/]+)(/tree/(?P[^/]+))?$" diff --git a/zhenxun/builtin_plugins/plugin_store/data_source.py b/zhenxun/builtin_plugins/plugin_store/data_source.py index 0d883d4a..bec20cfa 100644 --- a/zhenxun/builtin_plugins/plugin_store/data_source.py +++ b/zhenxun/builtin_plugins/plugin_store/data_source.py @@ -180,7 +180,7 @@ class ShopManage: str: 版本号 """ module = plugin_info.module - if cls.check_version_is_new(plugin_info, suc_plugin): + if not cls.check_version_is_new(plugin_info, suc_plugin): return f"{suc_plugin[module]} (有更新->{plugin_info.version})" return plugin_info.version @@ -198,7 +198,16 @@ class ShopManage: bool: 是否有更新 """ module = plugin_info.module - return module in suc_plugin and plugin_info.version != suc_plugin[module] + return suc_plugin.get(module) and plugin_info.version == suc_plugin[module] + + @classmethod + async def get_loaded_plugins(cls, *args) -> list[tuple[str, str]]: + """获取已加载的插件 + + 返回: + list[str]: 已加载的插件 + """ + return await PluginInfo.filter(load_status=True).values_list(*args) @classmethod async def get_plugins_info(cls) -> BuildImage | str: @@ -209,9 +218,7 @@ class ShopManage: """ data: dict[str, StorePluginInfo] = await cls.__get_data() column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"] - plugin_list = await PluginInfo.filter(load_status=True).values_list( - "module", "version" - ) + plugin_list = await cls.get_loaded_plugins("module", "version") suc_plugin = {p[0]: (p[1] or "0.1") for p in plugin_list} data_list = [ [ @@ -380,9 +387,7 @@ class ShopManage: BuildImage | str: 返回消息 """ data = await cls.__get_data() - plugin_list = await PluginInfo.filter(load_status=True).values_list( - "module", "version" - ) + plugin_list = await cls.get_loaded_plugins("module", "version") suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]} filtered_data = [ (id, plugin_info) @@ -430,9 +435,7 @@ class ShopManage: plugin_key = list(data.keys())[plugin_id] logger.info(f"尝试更新插件 {plugin_key}", "插件管理") plugin_info = data[plugin_key] - plugin_list = await PluginInfo.filter(load_status=True).values_list( - "module", "version" - ) + plugin_list = await cls.get_loaded_plugins("module", "version") suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]} logger.debug(f"当前插件列表: {suc_plugin}", "插件管理") if cls.check_version_is_new(plugin_info, suc_plugin):