From 0bc16c06b99551f81d06531a46d7ede3ff9c9922 Mon Sep 17 00:00:00 2001 From: KimigaiiWuyi <444835641@qq.com> Date: Fri, 7 Feb 2025 02:22:37 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=94=AF=E6=8C=81=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=83=AD=E9=87=8D=E8=BD=BD,=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E6=97=A0=E9=9C=80=E9=87=8D=E5=90=AF,=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E`core=E6=89=8B=E5=8A=A8=E9=87=8D=E8=BD=BD?= =?UTF-8?q?=E6=8F=92=E4=BB=B6`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core_command/core_restart/restart.py | 2 +- .../core_command/core_update/__init__.py | 10 ++ gsuid_core/server.py | 118 +++++++++++------- gsuid_core/utils/plugins_update/_plugins.py | 72 ++--------- .../utils/plugins_update/reload_plugin.py | 34 +++++ gsuid_core/utils/plugins_update/utils.py | 68 ++++++++++ 6 files changed, 192 insertions(+), 112 deletions(-) create mode 100644 gsuid_core/utils/plugins_update/reload_plugin.py create mode 100644 gsuid_core/utils/plugins_update/utils.py diff --git a/gsuid_core/buildin_plugins/core_command/core_restart/restart.py b/gsuid_core/buildin_plugins/core_command/core_restart/restart.py index 3385c59..88bd43e 100644 --- a/gsuid_core/buildin_plugins/core_command/core_restart/restart.py +++ b/gsuid_core/buildin_plugins/core_command/core_restart/restart.py @@ -5,7 +5,7 @@ import platform import subprocess from pathlib import Path -from gsuid_core.server import check_start_tool +from gsuid_core.utils.plugins_update.utils import check_start_tool from gsuid_core.utils.plugins_config.gs_config import core_plugins_config from ..core_status.command_global_val import save_global_val diff --git a/gsuid_core/buildin_plugins/core_command/core_update/__init__.py b/gsuid_core/buildin_plugins/core_command/core_update/__init__.py index f1449e5..2fc79f6 100644 --- a/gsuid_core/buildin_plugins/core_command/core_update/__init__.py +++ b/gsuid_core/buildin_plugins/core_command/core_update/__init__.py @@ -5,6 +5,7 @@ from gsuid_core.sv import SV from gsuid_core.bot import Bot from gsuid_core.models import Event from gsuid_core.logger import logger +from gsuid_core.utils.plugins_update.reload_plugin import reload_plugin from gsuid_core.utils.plugins_config.gs_config import core_plugins_config from gsuid_core.utils.plugins_update._plugins import ( run_install, @@ -17,6 +18,15 @@ from gsuid_core.utils.plugins_update._plugins import ( sv_core_config = SV('Core管理', pm=0) +@sv_core_config.on_prefix(('core手动重载插件')) +async def send_core_reload_msg(bot: Bot, ev: Event): + plugin_name = ev.text.strip() + logger.info(f'🔔 开始执行 [重载] {plugin_name}') + await bot.send(f'🔔 正在尝试重载插件{plugin_name}...') + retcode = reload_plugin(plugin_name) + await bot.send(retcode) + + @sv_core_config.on_fullmatch(('core更新', 'core强制更新'), block=True) async def send_core_update_msg(bot: Bot, ev: Event): logger.info('开始执行[更新] 早柚核心') diff --git a/gsuid_core/server.py b/gsuid_core/server.py index be328e3..dbf1c41 100644 --- a/gsuid_core/server.py +++ b/gsuid_core/server.py @@ -4,7 +4,8 @@ import asyncio import importlib import subprocess from pathlib import Path -from typing import Dict, List, Callable +from types import ModuleType +from typing import Dict, List, Union, Callable import toml import pkg_resources @@ -12,7 +13,7 @@ from fastapi import WebSocket from gsuid_core.bot import _Bot from gsuid_core.logger import logger -from gsuid_core.utils.plugins_update._plugins import check_start_tool +from gsuid_core.utils.plugins_update.utils import check_start_tool from gsuid_core.utils.plugins_config.gs_config import core_plugins_config auto_install_dep: bool = core_plugins_config.get_config('AutoInstallDep').data @@ -60,6 +61,64 @@ class GsServer: self.active_bot: Dict[str, _Bot] = {} self.is_initialized = True + def load_plugin(self, plugin: Union[str, Path]): + if isinstance(plugin, str): + plugin = PLUGIN_PATH / plugin + + if not plugin.exists(): + logger.warning(f'[更新] ❌ 插件{plugin.name}不存在!') + return f'❌ 插件{plugin.name}不存在!' + + plugin_parent = plugin.parent.name + if plugin.stem.startswith('_'): + return f'插件{plugin.name}包含"_", 跳过加载!' + + # 如果发现文件夹,则视为插件包 + logger.trace('===============') + logger.debug(f'🔹 导入{plugin.stem}中...') + logger.trace('===============') + try: + if plugin.is_dir(): + plugin_path = plugin / '__init__.py' + plugins_path = plugin / '__full__.py' + nest_path = plugin / '__nest__.py' + src_path = plugin / plugin.stem + # 如果文件夹内有__full_.py,则视为插件包合集 + sys.path.append(str(plugin_path.parents)) + if plugins_path.exists(): + module_list = self.load_dir_plugins(plugin, plugin_parent) + elif nest_path.exists() or src_path.exists(): + path = nest_path.parent / plugin.name + pyproject = plugin / 'pyproject.toml' + if pyproject.exists: + check_pyproject(pyproject) + if path.exists(): + module_list = self.load_dir_plugins( + path, plugin_parent, True + ) + # 如果文件夹内有__init_.py,则视为单个插件包 + elif plugin_path.exists(): + module_list = [ + importlib.import_module( + f'{plugin_parent}.{plugin.name}.__init__' + ) + ] + # 如果发现单文件,则视为单文件插件 + elif plugin.suffix == '.py': + module_list = [ + importlib.import_module( + f'{plugin_parent}.{plugin.name[:-3]}' + ) + ] + '''导入成功''' + logger.success(f'✅ 插件{plugin.stem}导入成功!') + return module_list + except Exception as e: # noqa + exception = sys.exc_info() + logger.opt(exception=exception).error(f'加载插件时发生错误: {e}') + logger.warning(f'❌ 插件{plugin.name}加载失败') + return f'❌ 插件{plugin.name}加载失败' + def load_plugins(self): logger.info('[GsCore] 开始加载插件...') get_installed_dependencies() @@ -71,58 +130,22 @@ class GsServer: # 遍历插件文件夹内所有文件 for plugin in plug_path_list: - plugin_parent = plugin.parent.name - if plugin.stem.startswith('_'): - continue - # 如果发现文件夹,则视为插件包 - logger.trace('===============') - logger.debug(f'导入{plugin.stem}中...') - logger.trace('===============') - try: - if plugin.is_dir(): - plugin_path = plugin / '__init__.py' - plugins_path = plugin / '__full__.py' - nest_path = plugin / '__nest__.py' - src_path = plugin / plugin.stem - # 如果文件夹内有__full_.py,则视为插件包合集 - sys.path.append(str(plugin_path.parents)) - if plugins_path.exists(): - self.load_dir_plugins(plugin, plugin_parent) - elif nest_path.exists() or src_path.exists(): - path = nest_path.parent / plugin.name - pyproject = plugin / 'pyproject.toml' - if pyproject.exists: - check_pyproject(pyproject) - if path.exists(): - self.load_dir_plugins(path, plugin_parent, True) - # 如果文件夹内有__init_.py,则视为单个插件包 - elif plugin_path.exists(): - importlib.import_module( - f'{plugin_parent}.{plugin.name}.__init__' - ) - # 如果发现单文件,则视为单文件插件 - elif plugin.suffix == '.py': - importlib.import_module( - f'{plugin_parent}.{plugin.name[:-3]}' - ) - '''导入成功''' - logger.success(f'插件{plugin.stem}导入成功!') - except Exception as e: # noqa - exception = sys.exc_info() - logger.opt(exception=exception).error( - f'加载插件时发生错误: {e}' - ) - logger.warning(f'插件{plugin.name}加载失败') + self.load_plugin(plugin) + logger.info('[GsCore] 插件加载完成!') def load_dir_plugins( self, plugin: Path, plugin_parent: str, nest: bool = False - ): + ) -> List[ModuleType]: + module_list = [] init_path = plugin / '__init__.py' name = plugin.name if init_path.exists(): if str(init_path.parents) not in sys.path: sys.path.append(str(init_path.parents)) - importlib.import_module(f'{plugin_parent}.{name}.{name}.__init__') + module = importlib.import_module( + f'{plugin_parent}.{name}.{name}.__init__' + ) + module_list.append(module) for sub_plugin in plugin.iterdir(): if sub_plugin.is_dir(): @@ -134,7 +157,8 @@ class GsServer: _p = f'{plugin_parent}.{name}.{name}.{sub_plugin.name}' else: _p = f'{plugin_parent}.{name}.{sub_plugin.name}' - importlib.import_module(f'{_p}') + module_list.append(importlib.import_module(f'{_p}')) + return module_list async def connect(self, websocket: WebSocket, bot_id: str) -> _Bot: await websocket.accept() diff --git a/gsuid_core/utils/plugins_update/_plugins.py b/gsuid_core/utils/plugins_update/_plugins.py index a6f87da..eced9f5 100644 --- a/gsuid_core/utils/plugins_update/_plugins.py +++ b/gsuid_core/utils/plugins_update/_plugins.py @@ -6,7 +6,6 @@ from pathlib import Path from typing import Dict, List, Union, Optional from concurrent.futures import ThreadPoolExecutor -import psutil import aiohttp from git.repo import Repo from git.exc import GitCommandError, NoSuchPathError, InvalidGitRepositoryError @@ -14,74 +13,15 @@ from git.exc import GitCommandError, NoSuchPathError, InvalidGitRepositoryError from gsuid_core.logger import logger from gsuid_core.utils.plugins_config.gs_config import core_plugins_config +from .utils import check_start_tool +from .reload_plugin import reload_plugin from .api import CORE_PATH, PLUGINS_PATH, plugins_lib plugins_list: Dict[str, Dict[str, str]] = {} -start_venv: str = core_plugins_config.get_config('StartVENV').data is_install_dep = core_plugins_config.get_config('AutoInstallDep').data -def get_command_chain() -> List[str]: - cmd_chain = [] - process = psutil.Process() - while process: - try: - cmd_chain.extend(process.cmdline()) - process = process.parent() - except Exception as e: - logger.warning(f'获取命令链失败...{e}') - break - return cmd_chain - - -def check_start_tool(is_pip: bool = False): - command_chain = get_command_chain() - command_chain = [command.lower() for command in command_chain] - command_chain_str = ' '.join(command_chain) - logger.debug(f'[检测启动工具] 命令链: {command_chain}') - - PDM = 'pdm' - POETRY = 'poetry' - UV = 'uv' - OTHER = start_venv.strip() - PYTHON = 'python' - if OTHER == 'auto': - OTHER = PYTHON - - if is_pip: - PIP = ' run python -m pip' - PDM += PIP - POETRY += ' run pip' - UV += PIP - PYTHON += ' -m pip' - - if OTHER == 'python' or OTHER == 'auto': - OTHER = 'python -m pip' - else: - OTHER += PIP - - if start_venv == 'auto': - if 'pdm' in command_chain_str: - command = PDM - elif 'poetry' in command_chain_str: - command = POETRY - elif 'uv' in command_chain or 'uv.exe' in command_chain_str: - command = UV - else: - command = PYTHON - elif start_venv == 'pdm': - command = PDM - elif start_venv == 'poetry': - command = POETRY - elif start_venv == 'uv': - command = UV - else: - command = OTHER - - return command - - async def check_plugin_exist(name: str): name = name.lower() if name in ['core_command', 'gs_test']: @@ -532,6 +472,8 @@ def update_from_git( break else: log_list.append(f'✅插件 {plugin_name} 本次无更新内容!') + if plugin_name != '早柚核心': + reload_plugin(plugin_name) return log_list @@ -562,7 +504,9 @@ async def update_plugins( break log_list = await update_from_git_in_tread( - level, plugin_name, log_key, log_limit + level, + plugin_name, + log_key, + log_limit, ) return log_list - return log_list diff --git a/gsuid_core/utils/plugins_update/reload_plugin.py b/gsuid_core/utils/plugins_update/reload_plugin.py new file mode 100644 index 0000000..5992980 --- /dev/null +++ b/gsuid_core/utils/plugins_update/reload_plugin.py @@ -0,0 +1,34 @@ +import importlib + +from gsuid_core.sv import SL +from gsuid_core.gss import gss +from gsuid_core.logger import logger + + +def reload_plugin(plugin_name: str): + logger.info(f'🔔 正在重载插件{plugin_name}...') + + del_k = [] + del_v = [] + for sv_name in SL.lst: + sv = SL.lst[sv_name] + if sv.plugins.name == plugin_name: + del_k.append(sv_name) + if sv.plugins not in del_v: + del_v.append(sv.plugins) + + for k in del_k: + del SL.lst[k] + for v in del_v: + del SL.detail_lst[v] + del SL.plugins[plugin_name] + + retcode = gss.load_plugin(plugin_name) + if isinstance(retcode, str): + logger.info(f'❌ 重载插件{plugin_name}失败...') + return retcode + else: + for module in retcode: + importlib.reload(module) + logger.info(f'✨ 已重载插件{plugin_name}') + return f'✨ 已重载插件{plugin_name}!' diff --git a/gsuid_core/utils/plugins_update/utils.py b/gsuid_core/utils/plugins_update/utils.py new file mode 100644 index 0000000..27c4ef9 --- /dev/null +++ b/gsuid_core/utils/plugins_update/utils.py @@ -0,0 +1,68 @@ +from typing import List + +import psutil + +from gsuid_core.logger import logger +from gsuid_core.utils.plugins_config.gs_config import core_plugins_config + +start_venv: str = core_plugins_config.get_config('StartVENV').data + + +def get_command_chain() -> List[str]: + cmd_chain = [] + process = psutil.Process() + while process: + try: + cmd_chain.extend(process.cmdline()) + process = process.parent() + except Exception as e: + logger.warning(f'获取命令链失败...{e}') + break + return cmd_chain + + +def check_start_tool(is_pip: bool = False): + command_chain = get_command_chain() + command_chain = [command.lower() for command in command_chain] + command_chain_str = ' '.join(command_chain) + logger.debug(f'[检测启动工具] 命令链: {command_chain}') + + PDM = 'pdm' + POETRY = 'poetry' + UV = 'uv' + OTHER = start_venv.strip() + PYTHON = 'python' + if OTHER == 'auto': + OTHER = PYTHON + + if is_pip: + PIP = ' run python -m pip' + PDM += PIP + POETRY += ' run pip' + UV += PIP + PYTHON += ' -m pip' + + if OTHER == 'python' or OTHER == 'auto': + OTHER = 'python -m pip' + else: + OTHER += PIP + + if start_venv == 'auto': + if 'pdm' in command_chain_str: + command = PDM + elif 'poetry' in command_chain_str: + command = POETRY + elif 'uv' in command_chain or 'uv.exe' in command_chain_str: + command = UV + else: + command = PYTHON + elif start_venv == 'pdm': + command = PDM + elif start_venv == 'poetry': + command = POETRY + elif start_venv == 'uv': + command = UV + else: + command = OTHER + + return command