From e0f323fdeb08e0bb117e0bbaaa379b145f8c8a92 Mon Sep 17 00:00:00 2001 From: molanp <104612722+molanp@users.noreply.github.com> Date: Sat, 24 Aug 2024 14:49:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(plugin=5Fstore):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=8F=92=E4=BB=B6=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AEURL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了一个新的搜索命令,允许用户根据插件名称或作者搜索插件。此外,更新了插件配置的URL列表,以使用新的API端点。还修复了requirements.txt处理中的日志记录问题,并对插件安装和删除流程进行了微调。 --- .../builtin_plugins/plugin_store/__init__.py | 47 ++++++++-- .../builtin_plugins/plugin_store/config.py | 14 +-- .../plugin_store/data_source.py | 86 ++++++++++++++----- 3 files changed, 110 insertions(+), 37 deletions(-) diff --git a/zhenxun/builtin_plugins/plugin_store/__init__.py b/zhenxun/builtin_plugins/plugin_store/__init__.py index abf3049a..afa9dd5f 100644 --- a/zhenxun/builtin_plugins/plugin_store/__init__.py +++ b/zhenxun/builtin_plugins/plugin_store/__init__.py @@ -1,12 +1,13 @@ -from nonebot.permission import SUPERUSER -from nonebot.plugin import PluginMetadata -from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna -from nonebot_plugin_session import EventSession +from nonebot.permission import SUPERUSER # type: ignore +from nonebot.plugin import PluginMetadata # type: ignore +from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna # type: ignore +from nonebot_plugin_session import EventSession # type: ignore +from nonebot.exception import FinishedException # type: ignore -from zhenxun.configs.utils import PluginExtraData -from zhenxun.services.log import logger -from zhenxun.utils.enum import PluginType -from zhenxun.utils.message import MessageUtils +from zhenxun.configs.utils import PluginExtraData # type: ignore +from zhenxun.services.log import logger # type: ignore +from zhenxun.utils.enum import PluginType # type: ignore +from zhenxun.utils.message import MessageUtils # type: ignore from .data_source import ShopManage @@ -17,6 +18,7 @@ __plugin_meta__ = PluginMetadata( 插件商店 : 查看当前的插件商店 添加插件 id : 添加插件 移除插件 id : 移除插件 + 搜索插件 name or author : 搜索插件 """.strip(), extra=PluginExtraData( author="HibiKier", @@ -30,13 +32,13 @@ _matcher = on_alconna( "插件商店", Subcommand("add", Args["plugin_id", int]), Subcommand("remove", Args["plugin_id", int]), + Subcommand("search", Args["plugin_name_or_author", str]), ), permission=SUPERUSER, priority=1, block=True, ) - _matcher.shortcut( r"添加插件", command="插件商店", @@ -51,6 +53,13 @@ _matcher.shortcut( prefix=True, ) +_matcher.shortcut( + r"搜索插件", + command="插件商店", + arguments=["search", "{%0}"], + prefix=True, +) + @_matcher.assign("$main") async def _(session: EventSession): @@ -58,6 +67,8 @@ async def _(session: EventSession): result = await ShopManage.get_plugins_info() logger.info("查看插件列表", "插件商店", session=session) await MessageUtils.build_message(result).finish() + except FinishedException: + pass except Exception as e: logger.error(f"查看插件列表失败 e: {e}", "插件商店", session=session, e=e) @@ -66,6 +77,8 @@ async def _(session: EventSession): async def _(session: EventSession, plugin_id: int): try: result = await ShopManage.add_plugin(plugin_id) + except FinishedException: + pass except Exception as e: logger.error(f"添加插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) await MessageUtils.build_message( @@ -79,6 +92,8 @@ async def _(session: EventSession, plugin_id: int): async def _(session: EventSession, plugin_id: int): try: result = await ShopManage.remove_plugin(plugin_id) + except FinishedException: + pass except Exception as e: logger.error(f"移除插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) await MessageUtils.build_message( @@ -86,3 +101,17 @@ async def _(session: EventSession, plugin_id: int): ).finish() logger.info(f"移除插件 Id: {plugin_id}", "插件商店", session=session) await MessageUtils.build_message(result).finish() + +@_matcher.assign("search") +async def _(session: EventSession, plugin_name_or_author: str): + try: + result = await ShopManage.search_plugin(plugin_name_or_author) + except FinishedException: + pass + except Exception as e: + logger.error(f"搜索插件 name: {plugin_name_or_author}失败", "插件商店", session=session, e=e) + await MessageUtils.build_message( + f"搜索插件 name: {plugin_name_or_author} 失败 e: {e}" + ).finish() + logger.info(f"搜索插件 name: {plugin_name_or_author}", "插件商店", session=session) + await MessageUtils.build_message(result).finish() diff --git a/zhenxun/builtin_plugins/plugin_store/config.py b/zhenxun/builtin_plugins/plugin_store/config.py index 056c1e83..c2866da5 100644 --- a/zhenxun/builtin_plugins/plugin_store/config.py +++ b/zhenxun/builtin_plugins/plugin_store/config.py @@ -4,12 +4,12 @@ BASE_PATH = Path() / "zhenxun" BASE_PATH.mkdir(parents=True, exist_ok=True) -CONFIG_URL = ( - "https://cdn.jsdelivr.net/gh/HibiKier/zhenxun_bot_plugins/plugins.json" -) +CONFIG_URL_LIST = [ + ("https://cdn.jsdelivr.net/gh/HibiKier/zhenxun_bot_plugins/plugins.json", + "https://api.github.com/repos/HibiKier/zhenxun_bot_plugins/contents/{}?ref=main") +] """插件信息文件""" -DOWNLOAD_URL = ( - "https://api.github.com/repos/HibiKier/zhenxun_bot_plugins/contents/{}?ref=main" -) -"""插件下载地址""" +CONFIG_URL = CONFIG_URL_LIST[0][0] + +DOWNLOAD_URL = CONFIG_URL_LIST[0][1] diff --git a/zhenxun/builtin_plugins/plugin_store/data_source.py b/zhenxun/builtin_plugins/plugin_store/data_source.py index c868998f..b39ade7d 100644 --- a/zhenxun/builtin_plugins/plugin_store/data_source.py +++ b/zhenxun/builtin_plugins/plugin_store/data_source.py @@ -1,14 +1,13 @@ import shutil from pathlib import Path import subprocess -import os import nonebot import ujson as json -from zhenxun.services.log import logger -from zhenxun.utils.http_utils import AsyncHttpx -from zhenxun.utils.image_utils import BuildImage, ImageTemplate, RowStyle +from zhenxun.services.log import logger # type: ignore +from zhenxun.utils.http_utils import AsyncHttpx # type: ignore +from zhenxun.utils.image_utils import BuildImage, ImageTemplate, RowStyle # type: ignore from .config import BASE_PATH, CONFIG_URL, DOWNLOAD_URL @@ -87,16 +86,20 @@ async def download_file(url: str): def install_requirement(plugin_path: Path): requirement_path = plugin_path / "requirement.txt" - + if not requirement_path.exists(): - logger.debug(f"No requirement.txt found for plugin: {plugin_path.name}", "插件管理") + logger.debug( + f"No requirement.txt found for plugin: {plugin_path.name}", "插件管理") return - + try: - result = subprocess.run(["pip", "install", "-r", str(requirement_path)], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - logger.debug(f"Successfully installed dependencies for plugin: {plugin_path.name}. Output:\n{result.stdout}", "插件管理") + result = subprocess.run(["pip", "install", "-r", str(requirement_path)], + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + logger.debug( + f"Successfully installed dependencies for plugin: {plugin_path.name}. Output:\n{result.stdout}", "插件管理") except subprocess.CalledProcessError as e: - logger.error(f"Failed to install dependencies for plugin: {plugin_path.name}. Error:\n{e.stderr}") + logger.error( + f"Failed to install dependencies for plugin: {plugin_path.name}. Error:\n{e.stderr}") class ShopManage: @@ -137,18 +140,19 @@ class ShopManage: for k in data.copy(): if data[k]["plugin_type"]: data[k]["plugin_type"] = cls.type2name[data[k]["plugin_type"]] - suc_plugin = [p.name for p in nonebot.get_loaded_plugins()] + suc_plugin = [p.name for p in nonebot.get_loaded_plugins() # type: ignore + ] data_list = [ [ - "已安装" if v[1]["module"] in suc_plugin else "", - i, - v[0], - v[1]["description"], - v[1]["author"], - v[1]["version"], - v[1]["plugin_type"], + "已安装" if plugin_info[1]["module"] in suc_plugin else "", + id, + plugin_info[0], + plugin_info[1]["description"], + plugin_info[1]["author"], + plugin_info[1]["version"], + plugin_info[1]["plugin_type"], ] - for i, v in enumerate(data.items()) + for id, plugin_info in enumerate(data.items()) ] return await ImageTemplate.table_page( "插件列表", @@ -187,11 +191,11 @@ class ShopManage: return "插件下载地址构建失败..." logger.debug(f"尝试下载插件 URL: {url_path}", "插件管理") await download_file(DOWNLOAD_URL.format(url_path)) - + # 安装依赖 plugin_path = BASE_PATH / "/".join(module_path_split) install_requirement(plugin_path) - + return f"插件 {plugin_key} 安装成功!" @classmethod @@ -214,3 +218,43 @@ class ShopManage: else: path.unlink() return f"插件 {plugin_key} 移除成功!" + + @classmethod + async def search_plugin(cls, plugin_name_or_author: str) -> BuildImage | str: + data: dict = await cls.__get_data() + column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"] + for k in data.copy(): + if data[k]["plugin_type"]: + data[k]["plugin_type"] = cls.type2name[data[k]["plugin_type"]] + + suc_plugin = [p.name for p in nonebot.get_loaded_plugins()] # type: ignore + filtered_data = [ + (id, plugin_info) + for id, plugin_info in enumerate(data.items()) + if plugin_name_or_author.lower() in plugin_info[0].lower() or + plugin_name_or_author.lower() in plugin_info[1]["author"].lower() + ] + + data_list = [ + [ + "已安装" if plugin_info[1]["module"] in suc_plugin else "", + id, + plugin_info[0], + plugin_info[1]["description"], + plugin_info[1]["author"], + plugin_info[1]["version"], + plugin_info[1]["plugin_type"], + ] + for id, plugin_info in filtered_data + ] + + if len(data_list) == 0: + return "未找到插件..." + + return await ImageTemplate.table_page( + "插件列表", + f"通过安装/卸载插件 ID 来管理插件", + column_name, + data_list, + text_style=row_style, + ) \ No newline at end of file