Merge branch 'r-dev' of github.com:Mai-with-u/MaiBot into r-dev

This commit is contained in:
UnCLAS-Prommer
2026-03-15 23:53:59 +08:00
3 changed files with 115 additions and 27 deletions

View File

@@ -9,6 +9,7 @@
from collections import deque
from typing import Any, Dict, List, Optional, Set, Tuple
import contextlib
import importlib
import importlib.util
import json
@@ -239,36 +240,60 @@ class PluginLoader:
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
# 优先使用新版 create_plugin 工厂函数
create_plugin = getattr(module, "create_plugin", None)
if create_plugin is not None:
instance = create_plugin()
logger.info(f"插件 {plugin_id} v{manifest.get('version', '?')} 加载成功")
return PluginMeta(
plugin_id=plugin_id,
plugin_dir=plugin_dir,
plugin_instance=instance,
manifest=manifest,
)
plugin_parent_dir = os.path.normpath(os.path.dirname(plugin_dir))
with self._temporary_sys_path_entry(plugin_parent_dir):
spec.loader.exec_module(module)
# 回退:检测旧@register_plugin 标记的 BasePlugin 子类
instance = self._try_load_legacy_plugin(module, plugin_id)
if instance is not None:
logger.info(
f"插件 {plugin_id} v{manifest.get('version', '?')} 通过旧版兼容层加载成功(请尽快迁移到 maibot_sdk"
)
return PluginMeta(
plugin_id=plugin_id,
plugin_dir=plugin_dir,
plugin_instance=instance,
manifest=manifest,
)
# 优先使用新create_plugin 工厂函数
create_plugin = getattr(module, "create_plugin", None)
if create_plugin is not None:
instance = create_plugin()
logger.info(f"插件 {plugin_id} v{manifest.get('version', '?')} 加载成功")
return PluginMeta(
plugin_id=plugin_id,
plugin_dir=plugin_dir,
plugin_instance=instance,
manifest=manifest,
)
# 回退:检测旧版 @register_plugin 标记的 BasePlugin 子类
instance = self._try_load_legacy_plugin(module, plugin_id)
if instance is not None:
logger.info(
f"插件 {plugin_id} v{manifest.get('version', '?')} 通过旧版兼容层加载成功(请尽快迁移到 maibot_sdk"
)
return PluginMeta(
plugin_id=plugin_id,
plugin_dir=plugin_dir,
plugin_instance=instance,
manifest=manifest,
)
logger.error(f"插件 {plugin_id} 缺少 create_plugin 工厂函数且未检测到旧版 BasePlugin")
return None
@staticmethod
@contextlib.contextmanager
def _temporary_sys_path_entry(path: str):
"""临时将路径放入 sys.path 头部,并在离开作用域后恢复。"""
if not path:
yield
return
normalized_path = os.path.normpath(path)
existing_paths = {os.path.normpath(entry) for entry in sys.path}
inserted = normalized_path not in existing_paths
if inserted:
sys.path.insert(0, normalized_path)
try:
yield
finally:
if inserted:
with contextlib.suppress(ValueError):
sys.path.remove(normalized_path)
# ──── 旧版插件兼容 ────────────────────────────────────────
def _ensure_compat_hook(self) -> None:

View File

@@ -627,14 +627,19 @@ def _isolate_sys_path(plugin_dirs: List[str]) -> None:
allowed.add(p)
# 添加插件目录
for d in plugin_dirs:
allowed.add(os.path.normpath(d))
plugin_dir_paths = [os.path.normpath(d) for d in plugin_dirs]
for d in plugin_dir_paths:
allowed.add(d)
# 添加项目根目录(使得 src.plugin_runtime / src.common 可导入)
runtime_root = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
allowed.add(runtime_root)
sys.path[:] = [p for p in sys.path if p in allowed]
preserved_paths = [p for p in sys.path if p in allowed]
for extra_path in [*plugin_dir_paths, runtime_root]:
if extra_path not in preserved_paths:
preserved_paths.append(extra_path)
sys.path[:] = preserved_paths
# 安装 import 钩子,阻止插件导入主程序核心模块
# 仅允许 src.plugin_runtime 和 src.common拒绝其他 src.* 子包