修复代码格式和文件名大小写问题

This commit is contained in:
Windpicker-owo
2025-08-31 20:50:17 +08:00
parent a187130613
commit fe472dff60
213 changed files with 6897 additions and 8252 deletions

View File

@@ -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", # 数据科学类型
}

View File

@@ -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 函数仅用于兼容性,配置更改不会持久化")

View File

@@ -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
)

View File

@@ -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