修复代码格式和文件名大小写问题
This commit is contained in:
@@ -23,112 +23,105 @@
|
||||
|
||||
INSTALL_NAME_TO_IMPORT_NAME = {
|
||||
# ============== 数据科学与机器学习 (Data Science & Machine Learning) ==============
|
||||
"scikit-learn": "sklearn", # 机器学习库
|
||||
"scikit-image": "skimage", # 图像处理库
|
||||
"opencv-python": "cv2", # OpenCV 计算机视觉库
|
||||
"opencv-contrib-python": "cv2", # OpenCV 扩展模块
|
||||
"tensorflow-gpu": "tensorflow", # TensorFlow GPU版本
|
||||
"tensorboardx": "tensorboardX", # TensorBoard 的封装
|
||||
"torchvision": "torchvision", # PyTorch 视觉库 (通常与 torch 一起)
|
||||
"torchaudio": "torchaudio", # PyTorch 音频库
|
||||
"catboost": "catboost", # CatBoost 梯度提升库
|
||||
"lightgbm": "lightgbm", # LightGBM 梯度提升库
|
||||
"xgboost": "xgboost", # XGBoost 梯度提升库
|
||||
"imbalanced-learn": "imblearn", # 处理不平衡数据集
|
||||
"seqeval": "seqeval", # 序列标注评估
|
||||
"gensim": "gensim", # 主题建模和NLP
|
||||
"nltk": "nltk", # 自然语言工具包
|
||||
"spacy": "spacy", # 工业级自然语言处理
|
||||
"fuzzywuzzy": "fuzzywuzzy", # 模糊字符串匹配
|
||||
"python-levenshtein": "Levenshtein", # Levenshtein 距离计算
|
||||
|
||||
"scikit-learn": "sklearn", # 机器学习库
|
||||
"scikit-image": "skimage", # 图像处理库
|
||||
"opencv-python": "cv2", # OpenCV 计算机视觉库
|
||||
"opencv-contrib-python": "cv2", # OpenCV 扩展模块
|
||||
"tensorflow-gpu": "tensorflow", # TensorFlow GPU版本
|
||||
"tensorboardx": "tensorboardX", # TensorBoard 的封装
|
||||
"torchvision": "torchvision", # PyTorch 视觉库 (通常与 torch 一起)
|
||||
"torchaudio": "torchaudio", # PyTorch 音频库
|
||||
"catboost": "catboost", # CatBoost 梯度提升库
|
||||
"lightgbm": "lightgbm", # LightGBM 梯度提升库
|
||||
"xgboost": "xgboost", # XGBoost 梯度提升库
|
||||
"imbalanced-learn": "imblearn", # 处理不平衡数据集
|
||||
"seqeval": "seqeval", # 序列标注评估
|
||||
"gensim": "gensim", # 主题建模和NLP
|
||||
"nltk": "nltk", # 自然语言工具包
|
||||
"spacy": "spacy", # 工业级自然语言处理
|
||||
"fuzzywuzzy": "fuzzywuzzy", # 模糊字符串匹配
|
||||
"python-levenshtein": "Levenshtein", # Levenshtein 距离计算
|
||||
# ============== Web开发与API (Web Development & API) ==============
|
||||
"python-socketio": "socketio", # Socket.IO 服务器和客户端
|
||||
"python-engineio": "engineio", # Engine.IO 底层库
|
||||
"aiohttp": "aiohttp", # 异步HTTP客户端/服务器
|
||||
"python-multipart": "multipart", # 解析 multipart/form-data
|
||||
"uvloop": "uvloop", # 高性能asyncio事件循环
|
||||
"httptools": "httptools", # 高性能HTTP解析器
|
||||
"websockets": "websockets", # WebSocket实现
|
||||
"fastapi": "fastapi", # 高性能Web框架
|
||||
"starlette": "starlette", # ASGI框架
|
||||
"uvicorn": "uvicorn", # ASGI服务器
|
||||
"gunicorn": "gunicorn", # WSGI服务器
|
||||
"django-rest-framework": "rest_framework", # Django REST框架
|
||||
"django-cors-headers": "corsheaders", # Django CORS处理
|
||||
"flask-jwt-extended": "flask_jwt_extended", # Flask JWT扩展
|
||||
"flask-sqlalchemy": "flask_sqlalchemy", # Flask SQLAlchemy扩展
|
||||
"flask-migrate": "flask_migrate", # Flask Alembic迁移扩展
|
||||
"python-jose": "jose", # JOSE (JWT, JWS, JWE) 实现
|
||||
"passlib": "passlib", # 密码哈希库
|
||||
"bcrypt": "bcrypt", # Bcrypt密码哈希
|
||||
|
||||
"python-socketio": "socketio", # Socket.IO 服务器和客户端
|
||||
"python-engineio": "engineio", # Engine.IO 底层库
|
||||
"aiohttp": "aiohttp", # 异步HTTP客户端/服务器
|
||||
"python-multipart": "multipart", # 解析 multipart/form-data
|
||||
"uvloop": "uvloop", # 高性能asyncio事件循环
|
||||
"httptools": "httptools", # 高性能HTTP解析器
|
||||
"websockets": "websockets", # WebSocket实现
|
||||
"fastapi": "fastapi", # 高性能Web框架
|
||||
"starlette": "starlette", # ASGI框架
|
||||
"uvicorn": "uvicorn", # ASGI服务器
|
||||
"gunicorn": "gunicorn", # WSGI服务器
|
||||
"django-rest-framework": "rest_framework", # Django REST框架
|
||||
"django-cors-headers": "corsheaders", # Django CORS处理
|
||||
"flask-jwt-extended": "flask_jwt_extended", # Flask JWT扩展
|
||||
"flask-sqlalchemy": "flask_sqlalchemy", # Flask SQLAlchemy扩展
|
||||
"flask-migrate": "flask_migrate", # Flask Alembic迁移扩展
|
||||
"python-jose": "jose", # JOSE (JWT, JWS, JWE) 实现
|
||||
"passlib": "passlib", # 密码哈希库
|
||||
"bcrypt": "bcrypt", # Bcrypt密码哈希
|
||||
# ============== 数据库 (Database) ==============
|
||||
"mysql-connector-python": "mysql.connector", # MySQL官方驱动
|
||||
"psycopg2-binary": "psycopg2", # PostgreSQL驱动 (二进制)
|
||||
"pymongo": "pymongo", # MongoDB驱动
|
||||
"redis": "redis", # Redis客户端
|
||||
"aioredis": "aioredis", # 异步Redis客户端
|
||||
"sqlalchemy": "sqlalchemy", # SQL工具包和ORM
|
||||
"alembic": "alembic", # SQLAlchemy数据库迁移工具
|
||||
"tortoise-orm": "tortoise", # 异步ORM
|
||||
|
||||
"mysql-connector-python": "mysql.connector", # MySQL官方驱动
|
||||
"psycopg2-binary": "psycopg2", # PostgreSQL驱动 (二进制)
|
||||
"pymongo": "pymongo", # MongoDB驱动
|
||||
"redis": "redis", # Redis客户端
|
||||
"aioredis": "aioredis", # 异步Redis客户端
|
||||
"sqlalchemy": "sqlalchemy", # SQL工具包和ORM
|
||||
"alembic": "alembic", # SQLAlchemy数据库迁移工具
|
||||
"tortoise-orm": "tortoise", # 异步ORM
|
||||
# ============== 图像与多媒体 (Image & Multimedia) ==============
|
||||
"Pillow": "PIL", # Python图像处理库 (PIL Fork)
|
||||
"moviepy": "moviepy", # 视频编辑库
|
||||
"pydub": "pydub", # 音频处理库
|
||||
"pycairo": "cairo", # Cairo 2D图形库的Python绑定
|
||||
"wand": "wand", # ImageMagick的Python绑定
|
||||
|
||||
"Pillow": "PIL", # Python图像处理库 (PIL Fork)
|
||||
"moviepy": "moviepy", # 视频编辑库
|
||||
"pydub": "pydub", # 音频处理库
|
||||
"pycairo": "cairo", # Cairo 2D图形库的Python绑定
|
||||
"wand": "wand", # ImageMagick的Python绑定
|
||||
# ============== 解析与序列化 (Parsing & Serialization) ==============
|
||||
"beautifulsoup4": "bs4", # HTML/XML解析库
|
||||
"lxml": "lxml", # 高性能HTML/XML解析库
|
||||
"PyYAML": "yaml", # YAML解析库
|
||||
"python-dotenv": "dotenv", # .env文件解析
|
||||
"python-dateutil": "dateutil", # 强大的日期时间解析
|
||||
"protobuf": "google.protobuf", # Protocol Buffers
|
||||
"msgpack": "msgpack", # MessagePack序列化
|
||||
"orjson": "orjson", # 高性能JSON库
|
||||
"pydantic": "pydantic", # 数据验证和设置管理
|
||||
|
||||
"beautifulsoup4": "bs4", # HTML/XML解析库
|
||||
"lxml": "lxml", # 高性能HTML/XML解析库
|
||||
"PyYAML": "yaml", # YAML解析库
|
||||
"python-dotenv": "dotenv", # .env文件解析
|
||||
"python-dateutil": "dateutil", # 强大的日期时间解析
|
||||
"protobuf": "google.protobuf", # Protocol Buffers
|
||||
"msgpack": "msgpack", # MessagePack序列化
|
||||
"orjson": "orjson", # 高性能JSON库
|
||||
"pydantic": "pydantic", # 数据验证和设置管理
|
||||
# ============== 系统与硬件 (System & Hardware) ==============
|
||||
"pyserial": "serial", # 串口通信
|
||||
"pyusb": "usb", # USB访问
|
||||
"pybluez": "bluetooth", # 蓝牙通信 (可能因平台而异)
|
||||
"psutil": "psutil", # 系统信息和进程管理
|
||||
"watchdog": "watchdog", # 文件系统事件监控
|
||||
"python-gnupg": "gnupg", # GnuPG的Python接口
|
||||
|
||||
"pyserial": "serial", # 串口通信
|
||||
"pyusb": "usb", # USB访问
|
||||
"pybluez": "bluetooth", # 蓝牙通信 (可能因平台而异)
|
||||
"psutil": "psutil", # 系统信息和进程管理
|
||||
"watchdog": "watchdog", # 文件系统事件监控
|
||||
"python-gnupg": "gnupg", # GnuPG的Python接口
|
||||
# ============== 加密与安全 (Cryptography & Security) ==============
|
||||
"pycrypto": "Crypto", # 加密库 (较旧)
|
||||
"pycryptodome": "Crypto", # PyCrypto的现代分支
|
||||
"cryptography": "cryptography", # 现代加密库
|
||||
"pyopenssl": "OpenSSL", # OpenSSL的Python接口
|
||||
"service-identity": "service_identity", # 服务身份验证
|
||||
|
||||
"pycrypto": "Crypto", # 加密库 (较旧)
|
||||
"pycryptodome": "Crypto", # PyCrypto的现代分支
|
||||
"cryptography": "cryptography", # 现代加密库
|
||||
"pyopenssl": "OpenSSL", # OpenSSL的Python接口
|
||||
"service-identity": "service_identity", # 服务身份验证
|
||||
# ============== 工具与杂项 (Utilities & Miscellaneous) ==============
|
||||
"setuptools": "setuptools", # 打包工具
|
||||
"pip": "pip", # 包安装器
|
||||
"tqdm": "tqdm", # 进度条
|
||||
"regex": "regex", # 替代的正则表达式引擎
|
||||
"colorama": "colorama", # 跨平台彩色终端文本
|
||||
"termcolor": "termcolor", # 终端颜色格式化
|
||||
"requests-oauthlib": "requests_oauthlib", # OAuth for Requests
|
||||
"oauthlib": "oauthlib", # 通用OAuth库
|
||||
"authlib": "authlib", # OAuth和OpenID Connect客户端/服务器
|
||||
"pyjwt": "jwt", # JSON Web Token实现
|
||||
"python-editor": "editor", # 程序化地调用编辑器
|
||||
"prompt-toolkit": "prompt_toolkit", # 构建交互式命令行
|
||||
"pygments": "pygments", # 语法高亮
|
||||
"tabulate": "tabulate", # 生成漂亮的表格
|
||||
"nats-client": "nats", # NATS客户端
|
||||
"gitpython": "git", # Git的Python接口
|
||||
"pygithub": "github", # GitHub API v3的Python接口
|
||||
"python-gitlab": "gitlab", # GitLab API的Python接口
|
||||
"jira": "jira", # JIRA API的Python接口
|
||||
"python-jenkins": "jenkins", # Jenkins API的Python接口
|
||||
"huggingface-hub": "huggingface_hub", # Hugging Face Hub API
|
||||
"apache-airflow": "airflow", # Airflow工作流管理
|
||||
"pandas-stubs": "pandas-stubs", # Pandas的类型存根
|
||||
"data-science-types": "data_science_types", # 数据科学类型
|
||||
}
|
||||
"setuptools": "setuptools", # 打包工具
|
||||
"pip": "pip", # 包安装器
|
||||
"tqdm": "tqdm", # 进度条
|
||||
"regex": "regex", # 替代的正则表达式引擎
|
||||
"colorama": "colorama", # 跨平台彩色终端文本
|
||||
"termcolor": "termcolor", # 终端颜色格式化
|
||||
"requests-oauthlib": "requests_oauthlib", # OAuth for Requests
|
||||
"oauthlib": "oauthlib", # 通用OAuth库
|
||||
"authlib": "authlib", # OAuth和OpenID Connect客户端/服务器
|
||||
"pyjwt": "jwt", # JSON Web Token实现
|
||||
"python-editor": "editor", # 程序化地调用编辑器
|
||||
"prompt-toolkit": "prompt_toolkit", # 构建交互式命令行
|
||||
"pygments": "pygments", # 语法高亮
|
||||
"tabulate": "tabulate", # 生成漂亮的表格
|
||||
"nats-client": "nats", # NATS客户端
|
||||
"gitpython": "git", # Git的Python接口
|
||||
"pygithub": "github", # GitHub API v3的Python接口
|
||||
"python-gitlab": "gitlab", # GitLab API的Python接口
|
||||
"jira": "jira", # JIRA API的Python接口
|
||||
"python-jenkins": "jenkins", # Jenkins API的Python接口
|
||||
"huggingface-hub": "huggingface_hub", # Hugging Face Hub API
|
||||
"apache-airflow": "airflow", # Airflow工作流管理
|
||||
"pandas-stubs": "pandas-stubs", # Pandas的类型存根
|
||||
"data-science-types": "data_science_types", # 数据科学类型
|
||||
}
|
||||
|
||||
@@ -6,62 +6,61 @@ logger = get_logger("dependency_config")
|
||||
|
||||
class DependencyConfig:
|
||||
"""依赖管理配置类 - 现在使用全局配置"""
|
||||
|
||||
|
||||
def __init__(self, global_config=None):
|
||||
self._global_config = global_config
|
||||
|
||||
|
||||
def _get_config(self):
|
||||
"""获取全局配置对象"""
|
||||
if self._global_config is not None:
|
||||
return self._global_config
|
||||
|
||||
|
||||
# 延迟导入以避免循环依赖
|
||||
try:
|
||||
from src.config.config import global_config
|
||||
|
||||
return global_config
|
||||
except ImportError:
|
||||
logger.warning("无法导入全局配置,使用默认设置")
|
||||
return None
|
||||
|
||||
|
||||
@property
|
||||
def auto_install(self) -> bool:
|
||||
"""是否启用自动安装"""
|
||||
config = self._get_config()
|
||||
if config and hasattr(config, 'dependency_management'):
|
||||
if config and hasattr(config, "dependency_management"):
|
||||
return config.dependency_management.auto_install
|
||||
return True
|
||||
|
||||
|
||||
@property
|
||||
def use_mirror(self) -> bool:
|
||||
"""是否使用PyPI镜像源"""
|
||||
config = self._get_config()
|
||||
if config and hasattr(config, 'dependency_management'):
|
||||
if config and hasattr(config, "dependency_management"):
|
||||
return config.dependency_management.use_mirror
|
||||
return False
|
||||
|
||||
|
||||
@property
|
||||
def mirror_url(self) -> str:
|
||||
"""PyPI镜像源URL"""
|
||||
config = self._get_config()
|
||||
if config and hasattr(config, 'dependency_management'):
|
||||
if config and hasattr(config, "dependency_management"):
|
||||
return config.dependency_management.mirror_url
|
||||
return ""
|
||||
|
||||
|
||||
@property
|
||||
def install_timeout(self) -> int:
|
||||
"""安装超时时间(秒)"""
|
||||
config = self._get_config()
|
||||
if config and hasattr(config, 'dependency_management'):
|
||||
if config and hasattr(config, "dependency_management"):
|
||||
return config.dependency_management.auto_install_timeout
|
||||
return 300
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def prompt_before_install(self) -> bool:
|
||||
"""安装前是否提示用户"""
|
||||
config = self._get_config()
|
||||
if config and hasattr(config, 'dependency_management'):
|
||||
if config and hasattr(config, "dependency_management"):
|
||||
return config.dependency_management.prompt_before_install
|
||||
return False
|
||||
|
||||
@@ -82,4 +81,4 @@ def configure_dependency_settings(**kwargs) -> None:
|
||||
"""配置依赖管理设置 - 注意:这个函数现在仅用于兼容性,实际配置需要修改bot_config.toml"""
|
||||
logger.info("依赖管理设置现在通过 bot_config.toml 的 [dependency_management] 节进行配置")
|
||||
logger.info(f"请求的配置更改: {kwargs}")
|
||||
logger.warning("configure_dependency_settings 函数仅用于兼容性,配置更改不会持久化")
|
||||
logger.warning("configure_dependency_settings 函数仅用于兼容性,配置更改不会持久化")
|
||||
|
||||
@@ -15,13 +15,13 @@ logger = get_logger("dependency_manager")
|
||||
|
||||
class DependencyManager:
|
||||
"""Python包依赖管理器
|
||||
|
||||
|
||||
负责检查和自动安装插件的Python包依赖
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, auto_install: bool = True, use_mirror: bool = False, mirror_url: Optional[str] = None):
|
||||
"""初始化依赖管理器
|
||||
|
||||
|
||||
Args:
|
||||
auto_install: 是否自动安装缺失的依赖
|
||||
use_mirror: 是否使用PyPI镜像源
|
||||
@@ -30,38 +30,39 @@ class DependencyManager:
|
||||
# 延迟导入配置以避免循环依赖
|
||||
try:
|
||||
from src.plugin_system.utils.dependency_config import get_dependency_config
|
||||
|
||||
config = get_dependency_config()
|
||||
|
||||
|
||||
# 优先使用配置文件中的设置,参数作为覆盖
|
||||
self.auto_install = config.auto_install if auto_install is True else auto_install
|
||||
self.use_mirror = config.use_mirror if use_mirror is False else use_mirror
|
||||
self.mirror_url = config.mirror_url if mirror_url is None else mirror_url
|
||||
self.install_timeout = config.install_timeout
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"无法加载依赖配置,使用默认设置: {e}")
|
||||
self.auto_install = auto_install
|
||||
self.use_mirror = use_mirror or False
|
||||
self.mirror_url = mirror_url or ""
|
||||
self.install_timeout = 300
|
||||
|
||||
|
||||
def check_dependencies(self, dependencies: Any, plugin_name: str = "") -> Tuple[bool, List[str], List[str]]:
|
||||
"""检查依赖包是否满足要求
|
||||
|
||||
|
||||
Args:
|
||||
dependencies: 依赖列表,支持字符串或PythonDependency对象
|
||||
plugin_name: 插件名称,用于日志记录
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple[bool, List[str], List[str]]: (是否全部满足, 缺失的包, 错误信息)
|
||||
"""
|
||||
missing_packages = []
|
||||
error_messages = []
|
||||
log_prefix = f"[Plugin:{plugin_name}] " if plugin_name else ""
|
||||
|
||||
|
||||
# 标准化依赖格式
|
||||
normalized_deps = self._normalize_dependencies(dependencies)
|
||||
|
||||
|
||||
for dep in normalized_deps:
|
||||
try:
|
||||
if not self._check_single_dependency(dep):
|
||||
@@ -71,38 +72,40 @@ class DependencyManager:
|
||||
error_msg = f"检查依赖 {dep.package_name} 时发生错误: {str(e)}"
|
||||
error_messages.append(error_msg)
|
||||
logger.error(f"{log_prefix}{error_msg}")
|
||||
|
||||
|
||||
all_satisfied = len(missing_packages) == 0 and len(error_messages) == 0
|
||||
|
||||
|
||||
if all_satisfied:
|
||||
logger.debug(f"{log_prefix}所有Python依赖检查通过")
|
||||
else:
|
||||
logger.warning(f"{log_prefix}Python依赖检查失败: 缺失{len(missing_packages)}个包, {len(error_messages)}个错误")
|
||||
|
||||
logger.warning(
|
||||
f"{log_prefix}Python依赖检查失败: 缺失{len(missing_packages)}个包, {len(error_messages)}个错误"
|
||||
)
|
||||
|
||||
return all_satisfied, missing_packages, error_messages
|
||||
|
||||
|
||||
def install_dependencies(self, packages: List[str], plugin_name: str = "") -> Tuple[bool, List[str]]:
|
||||
"""自动安装缺失的依赖包
|
||||
|
||||
|
||||
Args:
|
||||
packages: 要安装的包列表
|
||||
plugin_name: 插件名称,用于日志记录
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple[bool, List[str]]: (是否全部安装成功, 失败的包列表)
|
||||
"""
|
||||
if not packages:
|
||||
return True, []
|
||||
|
||||
|
||||
if not self.auto_install:
|
||||
logger.info(f"[Plugin:{plugin_name}] 自动安装已禁用,跳过安装: {packages}")
|
||||
return False, packages
|
||||
|
||||
|
||||
log_prefix = f"[Plugin:{plugin_name}] " if plugin_name else ""
|
||||
logger.info(f"{log_prefix}开始自动安装Python依赖: {packages}")
|
||||
|
||||
|
||||
failed_packages = []
|
||||
|
||||
|
||||
for package in packages:
|
||||
try:
|
||||
if self._install_single_package(package, plugin_name):
|
||||
@@ -113,37 +116,37 @@ class DependencyManager:
|
||||
except Exception as e:
|
||||
failed_packages.append(package)
|
||||
logger.error(f"{log_prefix}❌ 安装 {package} 时发生异常: {str(e)}")
|
||||
|
||||
|
||||
success = len(failed_packages) == 0
|
||||
if success:
|
||||
logger.info(f"{log_prefix}🎉 所有依赖安装完成")
|
||||
else:
|
||||
logger.error(f"{log_prefix}⚠️ 部分依赖安装失败: {failed_packages}")
|
||||
|
||||
|
||||
return success, failed_packages
|
||||
|
||||
|
||||
def check_and_install_dependencies(self, dependencies: Any, plugin_name: str = "") -> Tuple[bool, List[str]]:
|
||||
"""检查并自动安装依赖(组合操作)
|
||||
|
||||
|
||||
Args:
|
||||
dependencies: 依赖列表
|
||||
plugin_name: 插件名称
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple[bool, List[str]]: (是否全部满足, 错误信息列表)
|
||||
"""
|
||||
# 第一步:检查依赖
|
||||
all_satisfied, missing_packages, check_errors = self.check_dependencies(dependencies, plugin_name)
|
||||
|
||||
|
||||
if all_satisfied:
|
||||
return True, []
|
||||
|
||||
|
||||
all_errors = check_errors.copy()
|
||||
|
||||
|
||||
# 第二步:尝试安装缺失的包
|
||||
if missing_packages and self.auto_install:
|
||||
install_success, failed_packages = self.install_dependencies(missing_packages, plugin_name)
|
||||
|
||||
|
||||
if not install_success:
|
||||
all_errors.extend([f"安装失败: {pkg}" for pkg in failed_packages])
|
||||
else:
|
||||
@@ -156,13 +159,13 @@ class DependencyManager:
|
||||
return True, []
|
||||
else:
|
||||
all_errors.extend([f"缺失依赖: {pkg}" for pkg in missing_packages])
|
||||
|
||||
|
||||
return False, all_errors
|
||||
|
||||
|
||||
def _normalize_dependencies(self, dependencies: Any) -> List[PythonDependency]:
|
||||
"""将依赖列表标准化为PythonDependency对象"""
|
||||
normalized = []
|
||||
|
||||
|
||||
for dep in dependencies:
|
||||
if isinstance(dep, str):
|
||||
# 解析字符串格式的依赖
|
||||
@@ -170,28 +173,27 @@ class DependencyManager:
|
||||
# 尝试解析为requirement格式 (如 "package>=1.0.0")
|
||||
req = Requirement(dep)
|
||||
version_spec = str(req.specifier) if req.specifier else ""
|
||||
|
||||
normalized.append(PythonDependency(
|
||||
package_name=req.name,
|
||||
version=version_spec,
|
||||
install_name=dep # 保持原始的安装名称
|
||||
))
|
||||
|
||||
normalized.append(
|
||||
PythonDependency(
|
||||
package_name=req.name,
|
||||
version=version_spec,
|
||||
install_name=dep, # 保持原始的安装名称
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
# 如果解析失败,作为简单包名处理
|
||||
normalized.append(PythonDependency(
|
||||
package_name=dep,
|
||||
install_name=dep
|
||||
))
|
||||
normalized.append(PythonDependency(package_name=dep, install_name=dep))
|
||||
elif isinstance(dep, PythonDependency):
|
||||
normalized.append(dep)
|
||||
else:
|
||||
logger.warning(f"未知的依赖格式: {dep}")
|
||||
|
||||
|
||||
return normalized
|
||||
|
||||
|
||||
def _check_single_dependency(self, dep: PythonDependency) -> bool:
|
||||
"""检查单个依赖是否满足要求"""
|
||||
|
||||
|
||||
def _try_check(import_name: str) -> bool:
|
||||
"""尝试使用给定的导入名进行检查"""
|
||||
try:
|
||||
@@ -206,11 +208,11 @@ class DependencyManager:
|
||||
# 检查版本要求
|
||||
try:
|
||||
module = importlib.import_module(import_name)
|
||||
installed_version = getattr(module, '__version__', None)
|
||||
installed_version = getattr(module, "__version__", None)
|
||||
|
||||
if installed_version is None:
|
||||
# 尝试其他常见的版本属性
|
||||
installed_version = getattr(module, 'VERSION', None)
|
||||
installed_version = getattr(module, "VERSION", None)
|
||||
if installed_version is None:
|
||||
logger.debug(f"无法获取包 {import_name} 的版本信息,假设满足要求")
|
||||
return True
|
||||
@@ -243,33 +245,27 @@ class DependencyManager:
|
||||
|
||||
# 3. 如果别名也失败了,或者没有别名,最终确认失败
|
||||
return False
|
||||
|
||||
|
||||
def _install_single_package(self, package: str, plugin_name: str = "") -> bool:
|
||||
"""安装单个包"""
|
||||
try:
|
||||
cmd = [sys.executable, "-m", "pip", "install", package]
|
||||
|
||||
|
||||
# 添加镜像源设置
|
||||
if self.use_mirror and self.mirror_url:
|
||||
cmd.extend(["-i", self.mirror_url])
|
||||
logger.debug(f"[Plugin:{plugin_name}] 使用PyPI镜像源: {self.mirror_url}")
|
||||
|
||||
|
||||
logger.debug(f"[Plugin:{plugin_name}] 执行安装命令: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=self.install_timeout,
|
||||
check=False
|
||||
)
|
||||
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=self.install_timeout, check=False)
|
||||
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
logger.error(f"[Plugin:{plugin_name}] pip安装失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error(f"[Plugin:{plugin_name}] 安装 {package} 超时")
|
||||
return False
|
||||
@@ -294,7 +290,5 @@ def configure_dependency_manager(auto_install: bool = True, use_mirror: bool = F
|
||||
"""配置全局依赖管理器"""
|
||||
global _global_dependency_manager
|
||||
_global_dependency_manager = DependencyManager(
|
||||
auto_install=auto_install,
|
||||
use_mirror=use_mirror,
|
||||
mirror_url=mirror_url
|
||||
)
|
||||
auto_install=auto_install, use_mirror=use_mirror, mirror_url=mirror_url
|
||||
)
|
||||
|
||||
@@ -19,65 +19,64 @@ logger = get_logger(__name__)
|
||||
def require_permission(permission_node: str, deny_message: Optional[str] = None):
|
||||
"""
|
||||
权限检查装饰器
|
||||
|
||||
|
||||
用于装饰需要特定权限才能执行的函数。如果用户没有权限,会发送拒绝消息并阻止函数执行。
|
||||
|
||||
|
||||
Args:
|
||||
permission_node: 所需的权限节点名称
|
||||
deny_message: 权限不足时的提示消息,如果为None则使用默认消息
|
||||
|
||||
|
||||
Example:
|
||||
@require_permission("plugin.example.admin")
|
||||
async def admin_command(message: Message, chat_stream: ChatStream):
|
||||
# 只有拥有 plugin.example.admin 权限的用户才能执行
|
||||
pass
|
||||
"""
|
||||
|
||||
def decorator(func: Callable):
|
||||
@wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
# 尝试从参数中提取 ChatStream 对象
|
||||
chat_stream = None
|
||||
|
||||
|
||||
# 首先检查位置参数中的 ChatStream
|
||||
for arg in args:
|
||||
if isinstance(arg, ChatStream):
|
||||
chat_stream = arg
|
||||
break
|
||||
|
||||
|
||||
# 如果在位置参数中没找到,尝试从关键字参数中查找
|
||||
if chat_stream is None:
|
||||
chat_stream = kwargs.get('chat_stream')
|
||||
|
||||
chat_stream = kwargs.get("chat_stream")
|
||||
|
||||
# 如果还没找到,检查是否是 PlusCommand 方法调用
|
||||
if chat_stream is None and args:
|
||||
# 检查第一个参数是否有 message.chat_stream 属性(PlusCommand 实例)
|
||||
instance = args[0]
|
||||
if hasattr(instance, 'message') and hasattr(instance.message, 'chat_stream'):
|
||||
if hasattr(instance, "message") and hasattr(instance.message, "chat_stream"):
|
||||
chat_stream = instance.message.chat_stream
|
||||
|
||||
|
||||
if chat_stream is None:
|
||||
logger.error(f"权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}")
|
||||
return
|
||||
|
||||
|
||||
# 检查权限
|
||||
has_permission = permission_api.check_permission(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id,
|
||||
permission_node
|
||||
chat_stream.platform, chat_stream.user_info.user_id, permission_node
|
||||
)
|
||||
|
||||
|
||||
if not has_permission:
|
||||
# 权限不足,发送拒绝消息
|
||||
message = deny_message or f"❌ 你没有执行此操作的权限\n需要权限: {permission_node}"
|
||||
await text_to_stream(message, chat_stream.stream_id)
|
||||
# 对于PlusCommand的execute方法,需要返回适当的元组
|
||||
if func.__name__ == 'execute' and hasattr(args[0], 'send_text'):
|
||||
if func.__name__ == "execute" and hasattr(args[0], "send_text"):
|
||||
return False, "权限不足", True
|
||||
return
|
||||
|
||||
|
||||
# 权限检查通过,执行原函数
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
# 对于同步函数,我们不能发送异步消息,只能记录日志
|
||||
chat_stream = None
|
||||
@@ -85,95 +84,93 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None)
|
||||
if isinstance(arg, ChatStream):
|
||||
chat_stream = arg
|
||||
break
|
||||
|
||||
|
||||
if chat_stream is None:
|
||||
chat_stream = kwargs.get('chat_stream')
|
||||
|
||||
chat_stream = kwargs.get("chat_stream")
|
||||
|
||||
if chat_stream is None:
|
||||
logger.error(f"权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}")
|
||||
return
|
||||
|
||||
|
||||
# 检查权限
|
||||
has_permission = permission_api.check_permission(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id,
|
||||
permission_node
|
||||
chat_stream.platform, chat_stream.user_info.user_id, permission_node
|
||||
)
|
||||
|
||||
|
||||
if not has_permission:
|
||||
logger.warning(f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 没有权限 {permission_node}")
|
||||
logger.warning(
|
||||
f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 没有权限 {permission_node}"
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
# 权限检查通过,执行原函数
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
||||
# 根据函数类型选择包装器
|
||||
if iscoroutinefunction(func):
|
||||
return async_wrapper
|
||||
else:
|
||||
return sync_wrapper
|
||||
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def require_master(deny_message: Optional[str] = None):
|
||||
"""
|
||||
Master权限检查装饰器
|
||||
|
||||
|
||||
用于装饰只有Master用户才能执行的函数。
|
||||
|
||||
|
||||
Args:
|
||||
deny_message: 权限不足时的提示消息,如果为None则使用默认消息
|
||||
|
||||
|
||||
Example:
|
||||
@require_master()
|
||||
async def master_only_command(message: Message, chat_stream: ChatStream):
|
||||
# 只有Master用户才能执行
|
||||
pass
|
||||
"""
|
||||
|
||||
def decorator(func: Callable):
|
||||
@wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
# 尝试从参数中提取 ChatStream 对象
|
||||
chat_stream = None
|
||||
|
||||
|
||||
# 首先检查位置参数中的 ChatStream
|
||||
for arg in args:
|
||||
if isinstance(arg, ChatStream):
|
||||
chat_stream = arg
|
||||
break
|
||||
|
||||
|
||||
# 如果在位置参数中没找到,尝试从关键字参数中查找
|
||||
if chat_stream is None:
|
||||
chat_stream = kwargs.get('chat_stream')
|
||||
|
||||
chat_stream = kwargs.get("chat_stream")
|
||||
|
||||
# 如果还没找到,检查是否是 PlusCommand 方法调用
|
||||
if chat_stream is None and args:
|
||||
# 检查第一个参数是否有 message.chat_stream 属性(PlusCommand 实例)
|
||||
instance = args[0]
|
||||
if hasattr(instance, 'message') and hasattr(instance.message, 'chat_stream'):
|
||||
if hasattr(instance, "message") and hasattr(instance.message, "chat_stream"):
|
||||
chat_stream = instance.message.chat_stream
|
||||
|
||||
|
||||
if chat_stream is None:
|
||||
logger.error(f"Master权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}")
|
||||
return
|
||||
|
||||
|
||||
# 检查是否为Master用户
|
||||
is_master = permission_api.is_master(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id
|
||||
)
|
||||
|
||||
is_master = permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id)
|
||||
|
||||
if not is_master:
|
||||
message = deny_message or "❌ 此操作仅限Master用户执行"
|
||||
await text_to_stream(message, chat_stream.stream_id)
|
||||
if func.__name__ == 'execute' and hasattr(args[0], 'send_text'):
|
||||
if func.__name__ == "execute" and hasattr(args[0], "send_text"):
|
||||
return False, "需要Master权限", True
|
||||
return
|
||||
|
||||
|
||||
# 权限检查通过,执行原函数
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
# 对于同步函数,我们不能发送异步消息,只能记录日志
|
||||
chat_stream = None
|
||||
@@ -181,116 +178,106 @@ def require_master(deny_message: Optional[str] = None):
|
||||
if isinstance(arg, ChatStream):
|
||||
chat_stream = arg
|
||||
break
|
||||
|
||||
|
||||
if chat_stream is None:
|
||||
chat_stream = kwargs.get('chat_stream')
|
||||
|
||||
chat_stream = kwargs.get("chat_stream")
|
||||
|
||||
if chat_stream is None:
|
||||
logger.error(f"Master权限装饰器无法找到 ChatStream 对象,函数: {func.__name__}")
|
||||
return
|
||||
|
||||
|
||||
# 检查是否为Master用户
|
||||
is_master = permission_api.is_master(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id
|
||||
)
|
||||
|
||||
is_master = permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id)
|
||||
|
||||
if not is_master:
|
||||
logger.warning(f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 不是Master用户")
|
||||
return
|
||||
|
||||
|
||||
# 权限检查通过,执行原函数
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
||||
# 根据函数类型选择包装器
|
||||
if iscoroutinefunction(func):
|
||||
return async_wrapper
|
||||
else:
|
||||
return sync_wrapper
|
||||
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class PermissionChecker:
|
||||
"""
|
||||
权限检查工具类
|
||||
|
||||
|
||||
提供一些便捷的权限检查方法,用于在代码中进行权限验证。
|
||||
"""
|
||||
|
||||
|
||||
@staticmethod
|
||||
def check_permission(chat_stream: ChatStream, permission_node: str) -> bool:
|
||||
"""
|
||||
检查用户是否拥有指定权限
|
||||
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流对象
|
||||
permission_node: 权限节点名称
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否拥有权限
|
||||
"""
|
||||
return permission_api.check_permission(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id,
|
||||
permission_node
|
||||
)
|
||||
|
||||
return permission_api.check_permission(chat_stream.platform, chat_stream.user_info.user_id, permission_node)
|
||||
|
||||
@staticmethod
|
||||
def is_master(chat_stream: ChatStream) -> bool:
|
||||
"""
|
||||
检查用户是否为Master用户
|
||||
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流对象
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否为Master用户
|
||||
"""
|
||||
return permission_api.is_master(
|
||||
chat_stream.platform,
|
||||
chat_stream.user_info.user_id
|
||||
)
|
||||
|
||||
return permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id)
|
||||
|
||||
@staticmethod
|
||||
async def ensure_permission(chat_stream: ChatStream, permission_node: str,
|
||||
deny_message: Optional[str] = None) -> bool:
|
||||
async def ensure_permission(
|
||||
chat_stream: ChatStream, permission_node: str, deny_message: Optional[str] = None
|
||||
) -> bool:
|
||||
"""
|
||||
确保用户拥有指定权限,如果没有权限会发送消息并返回False
|
||||
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流对象
|
||||
permission_node: 权限节点名称
|
||||
deny_message: 权限不足时的提示消息
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否拥有权限
|
||||
"""
|
||||
has_permission = PermissionChecker.check_permission(chat_stream, permission_node)
|
||||
|
||||
|
||||
if not has_permission:
|
||||
message = deny_message or f"❌ 你没有执行此操作的权限\n需要权限: {permission_node}"
|
||||
await text_to_stream(message, chat_stream.stream_id)
|
||||
|
||||
|
||||
return has_permission
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def ensure_master(chat_stream: ChatStream,
|
||||
deny_message: Optional[str] = None) -> bool:
|
||||
async def ensure_master(chat_stream: ChatStream, deny_message: Optional[str] = None) -> bool:
|
||||
"""
|
||||
确保用户为Master用户,如果不是会发送消息并返回False
|
||||
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流对象
|
||||
deny_message: 权限不足时的提示消息
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否为Master用户
|
||||
"""
|
||||
is_master = PermissionChecker.is_master(chat_stream)
|
||||
|
||||
|
||||
if not is_master:
|
||||
message = deny_message or "❌ 此操作仅限Master用户执行"
|
||||
await text_to_stream(message, chat_stream.stream_id)
|
||||
|
||||
|
||||
return is_master
|
||||
|
||||
Reference in New Issue
Block a user