feat(plugin_store): 添加搜索插件功能及更新配置URL

添加了一个新的搜索命令,允许用户根据插件名称或作者搜索插件。此外,更新了插件配置的URL列表,以使用新的API端点。还修复了requirements.txt处理中的日志记录问题,并对插件安装和删除流程进行了微调。
This commit is contained in:
molanp 2024-08-24 14:49:55 +08:00
parent 28b7415581
commit e0f323fdeb
3 changed files with 110 additions and 37 deletions

View File

@ -1,12 +1,13 @@
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER # type: ignore
from nonebot.plugin import PluginMetadata from nonebot.plugin import PluginMetadata # type: ignore
from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna from nonebot_plugin_alconna import Alconna, Args, Subcommand, on_alconna # type: ignore
from nonebot_plugin_session import EventSession from nonebot_plugin_session import EventSession # type: ignore
from nonebot.exception import FinishedException # type: ignore
from zhenxun.configs.utils import PluginExtraData from zhenxun.configs.utils import PluginExtraData # type: ignore
from zhenxun.services.log import logger from zhenxun.services.log import logger # type: ignore
from zhenxun.utils.enum import PluginType from zhenxun.utils.enum import PluginType # type: ignore
from zhenxun.utils.message import MessageUtils from zhenxun.utils.message import MessageUtils # type: ignore
from .data_source import ShopManage from .data_source import ShopManage
@ -17,6 +18,7 @@ __plugin_meta__ = PluginMetadata(
插件商店 : 查看当前的插件商店 插件商店 : 查看当前的插件商店
添加插件 id : 添加插件 添加插件 id : 添加插件
移除插件 id : 移除插件 移除插件 id : 移除插件
搜索插件 name or author : 搜索插件
""".strip(), """.strip(),
extra=PluginExtraData( extra=PluginExtraData(
author="HibiKier", author="HibiKier",
@ -30,13 +32,13 @@ _matcher = on_alconna(
"插件商店", "插件商店",
Subcommand("add", Args["plugin_id", int]), Subcommand("add", Args["plugin_id", int]),
Subcommand("remove", Args["plugin_id", int]), Subcommand("remove", Args["plugin_id", int]),
Subcommand("search", Args["plugin_name_or_author", str]),
), ),
permission=SUPERUSER, permission=SUPERUSER,
priority=1, priority=1,
block=True, block=True,
) )
_matcher.shortcut( _matcher.shortcut(
r"添加插件", r"添加插件",
command="插件商店", command="插件商店",
@ -51,6 +53,13 @@ _matcher.shortcut(
prefix=True, prefix=True,
) )
_matcher.shortcut(
r"搜索插件",
command="插件商店",
arguments=["search", "{%0}"],
prefix=True,
)
@_matcher.assign("$main") @_matcher.assign("$main")
async def _(session: EventSession): async def _(session: EventSession):
@ -58,6 +67,8 @@ async def _(session: EventSession):
result = await ShopManage.get_plugins_info() result = await ShopManage.get_plugins_info()
logger.info("查看插件列表", "插件商店", session=session) logger.info("查看插件列表", "插件商店", session=session)
await MessageUtils.build_message(result).finish() await MessageUtils.build_message(result).finish()
except FinishedException:
pass
except Exception as e: except Exception as e:
logger.error(f"查看插件列表失败 e: {e}", "插件商店", session=session, e=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): async def _(session: EventSession, plugin_id: int):
try: try:
result = await ShopManage.add_plugin(plugin_id) result = await ShopManage.add_plugin(plugin_id)
except FinishedException:
pass
except Exception as e: except Exception as e:
logger.error(f"添加插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) logger.error(f"添加插件 Id: {plugin_id}失败", "插件商店", session=session, e=e)
await MessageUtils.build_message( await MessageUtils.build_message(
@ -79,6 +92,8 @@ async def _(session: EventSession, plugin_id: int):
async def _(session: EventSession, plugin_id: int): async def _(session: EventSession, plugin_id: int):
try: try:
result = await ShopManage.remove_plugin(plugin_id) result = await ShopManage.remove_plugin(plugin_id)
except FinishedException:
pass
except Exception as e: except Exception as e:
logger.error(f"移除插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) logger.error(f"移除插件 Id: {plugin_id}失败", "插件商店", session=session, e=e)
await MessageUtils.build_message( await MessageUtils.build_message(
@ -86,3 +101,17 @@ async def _(session: EventSession, plugin_id: int):
).finish() ).finish()
logger.info(f"移除插件 Id: {plugin_id}", "插件商店", session=session) logger.info(f"移除插件 Id: {plugin_id}", "插件商店", session=session)
await MessageUtils.build_message(result).finish() 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()

View File

@ -4,12 +4,12 @@ BASE_PATH = Path() / "zhenxun"
BASE_PATH.mkdir(parents=True, exist_ok=True) BASE_PATH.mkdir(parents=True, exist_ok=True)
CONFIG_URL = ( CONFIG_URL_LIST = [
"https://cdn.jsdelivr.net/gh/HibiKier/zhenxun_bot_plugins/plugins.json" ("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 = ( CONFIG_URL = CONFIG_URL_LIST[0][0]
"https://api.github.com/repos/HibiKier/zhenxun_bot_plugins/contents/{}?ref=main"
) DOWNLOAD_URL = CONFIG_URL_LIST[0][1]
"""插件下载地址"""

View File

@ -1,14 +1,13 @@
import shutil import shutil
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import os
import nonebot import nonebot
import ujson as json import ujson as json
from zhenxun.services.log import logger from zhenxun.services.log import logger # type: ignore
from zhenxun.utils.http_utils import AsyncHttpx from zhenxun.utils.http_utils import AsyncHttpx # type: ignore
from zhenxun.utils.image_utils import BuildImage, ImageTemplate, RowStyle from zhenxun.utils.image_utils import BuildImage, ImageTemplate, RowStyle # type: ignore
from .config import BASE_PATH, CONFIG_URL, DOWNLOAD_URL from .config import BASE_PATH, CONFIG_URL, DOWNLOAD_URL
@ -89,14 +88,18 @@ def install_requirement(plugin_path: Path):
requirement_path = plugin_path / "requirement.txt" requirement_path = plugin_path / "requirement.txt"
if not requirement_path.exists(): 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 return
try: try:
result = subprocess.run(["pip", "install", "-r", str(requirement_path)], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) result = subprocess.run(["pip", "install", "-r", str(requirement_path)],
logger.debug(f"Successfully installed dependencies for plugin: {plugin_path.name}. Output:\n{result.stdout}", "插件管理") 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: 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: class ShopManage:
@ -137,18 +140,19 @@ class ShopManage:
for k in data.copy(): for k in data.copy():
if data[k]["plugin_type"]: if data[k]["plugin_type"]:
data[k]["plugin_type"] = cls.type2name[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 = [ data_list = [
[ [
"已安装" if v[1]["module"] in suc_plugin else "", "已安装" if plugin_info[1]["module"] in suc_plugin else "",
i, id,
v[0], plugin_info[0],
v[1]["description"], plugin_info[1]["description"],
v[1]["author"], plugin_info[1]["author"],
v[1]["version"], plugin_info[1]["version"],
v[1]["plugin_type"], 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( return await ImageTemplate.table_page(
"插件列表", "插件列表",
@ -214,3 +218,43 @@ class ShopManage:
else: else:
path.unlink() path.unlink()
return f"插件 {plugin_key} 移除成功!" 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,
)