import importlib
import json
import shutil
from typing import Any, Dict, List, Optional
from loguru import logger
import conf
from basic_dirs import CONFIG_HOME
class PluginLoader:
def __init__(self, p_mgr: Optional[Any] = None) -> None:
self.plugins_settings: Dict[str, Any] = {}
self.plugins_name: List[str] = []
self.plugins_dict: Dict[str, Any] = {}
self.manager = p_mgr
def set_manager(self, p_mgr: Any) -> None:
self.manager = p_mgr
def load_plugins(self) -> List[str]:
plugin_config = conf.load_plugin_config()
safe_plugin_enabled = plugin_config.get('safe_plugin', False)
if 'temp_disabled_plugins' in plugin_config:
temp_disabled = plugin_config['temp_disabled_plugins']
if temp_disabled:
for plugin_name in temp_disabled:
if plugin_name not in plugin_config.get('enabled_plugins', []):
plugin_config['enabled_plugins'].append(plugin_name)
plugin_config['temp_disabled_plugins'] = []
conf.save_plugin_config(plugin_config)
for folder in conf.PLUGIN_HOME.iterdir():
if folder.is_dir() and (folder / 'plugin.json').exists():
self.plugins_name.append(folder.name)
if folder.name not in conf.load_plugin_config()['enabled_plugins']:
continue
relative_path = conf.PLUGIN_HOME.name
module_name = f"{relative_path}.{folder.name}"
try:
module = importlib.import_module(module_name)
if hasattr(module, 'Settings'):
plugin_class = module.Settings
self.plugins_settings[folder.name] = plugin_class(
str(conf.PLUGIN_HOME / folder.name)
)
if self.manager and hasattr(module, 'Plugin'):
plugin_class = module.Plugin
self.plugins_dict[folder.name] = plugin_class(
self.manager.get_app_contexts(folder.name), self.manager.method
)
logger.success(f"加载插件成功:{module_name}")
except (ImportError, FileNotFoundError) as e:
logger.warning(f"加载插件 {folder.name} 失败: {e}. 将禁用此插件")
plugin_config = conf.load_plugin_config()
if folder.name in plugin_config['enabled_plugins']:
plugin_config['enabled_plugins'].remove(folder.name)
conf.save_plugin_config(plugin_config)
if folder.name in self.plugins_name:
self.plugins_name.remove(folder.name)
if safe_plugin_enabled:
self._disable_plugin_safely(folder.name)
logger.warning(f"已临时禁用插件 {folder.name}")
continue
except Exception as e:
logger.error(f"加载插件 {folder.name} 时发生未知错误: {e}")
if safe_plugin_enabled:
self._disable_plugin_safely(folder.name)
logger.warning(f"已临时禁用插件 {folder.name}")
continue
return self.plugins_name
def _disable_plugin_safely(self, plugin_name: str) -> None:
"""安全禁用插件"""
plugin_config = conf.load_plugin_config()
if plugin_name in plugin_config.get('enabled_plugins', []):
plugin_config['enabled_plugins'].remove(plugin_name)
if 'temp_disabled_plugins' not in plugin_config:
plugin_config['temp_disabled_plugins'] = []
if plugin_name not in plugin_config['temp_disabled_plugins']:
plugin_config['temp_disabled_plugins'].append(plugin_name)
conf.save_plugin_config(plugin_config)
logger.info(f"插件 {plugin_name} 已被临时禁用")
def run_plugins(self) -> None:
for plugin in self.plugins_dict.values():
plugin.execute()
def update_plugins(self) -> None:
for plugin in self.plugins_dict.values():
if hasattr(plugin, 'update') and self.manager:
plugin.update(self.manager.get_app_contexts())
def delete_plugin(self, plugin_name: str) -> bool:
plugin_dir = conf.PLUGIN_HOME / plugin_name
if not plugin_dir.is_dir():
logger.warning(f"插件目录 {plugin_dir} 不存在,无法删除。")
return False
widgets_to_remove = []
if widgets_to_remove:
try:
widget_config_path = CONFIG_HOME / 'widget.json'
if widget_config_path.exists():
with open(widget_config_path, encoding='utf-8') as f:
widget_config = json.load(f)
original_widgets = widget_config.get('widgets', [])
widget_config['widgets'] = [
w for w in original_widgets if w not in widgets_to_remove
]
with open(widget_config_path, 'w', encoding='utf-8') as f:
json.dump(widget_config, f, ensure_ascii=False, indent=4)
logger.info(
f"已从 config/widget.json 中移除插件 {plugin_name} 的关联组件: {widgets_to_remove}"
)
else:
logger.warning("主配置文件 config/widget.json 不存在,无法移除插件组件。")
except Exception as e:
logger.error(f"更新 config/widget.json 失败: {e}")
if plugin_name in self.plugins_dict:
del self.plugins_dict[plugin_name]
logger.info(f"已移除正在运行的插件实例: {plugin_name}")
if plugin_name in self.plugins_settings:
del self.plugins_settings[plugin_name]
logger.info(f"已移除插件设置实例: {plugin_name}")
plugin_config = conf.load_plugin_config()
if plugin_name in plugin_config.get('enabled_plugins', []):
plugin_config['enabled_plugins'].remove(plugin_name)
conf.save_plugin_config(plugin_config)
logger.info(f"已从启用插件列表中移除: {plugin_name}")
if plugin_name in self.plugins_name:
self.plugins_name.remove(plugin_name)
try:
shutil.rmtree(plugin_dir)
logger.success(f"插件 {plugin_name} 已成功删除。")
return True
except Exception as e:
logger.error(f"删除插件目录 {plugin_dir} 失败: {e}")
return False
p_loader = PluginLoader()
if __name__ == '__main__':
p_loader.load_plugins()
p_loader.run_plugins()