重构并增强Napcat适配器的功能

- 更新了`BaseAdapter`以简化子进程处理。
- 对`AdapterManager`进行了重构,以便根据适配器的`run_in_subprocess`属性来管理适配器。
- 增强了`NapcatAdapter`,以利用新的`CoreSinkManager`实现更优的进程管理。
- 在`utils.py`中实现了针对群组和成员信息的缓存机制。
- 改进了`message_handler.py`中的消息处理,以支持各种消息类型和格式。
- 已将插件配置版本更新至7.8.3。
This commit is contained in:
Windpicker-owo
2025-11-25 19:55:36 +08:00
parent 1ebdc37b22
commit 6b3b2a8245
38 changed files with 2082 additions and 3277 deletions

View File

@@ -62,8 +62,8 @@ class BaseAdapter(MoFoxAdapterBase, ABC):
self._config: Dict[str, Any] = {}
self._health_check_task: Optional[asyncio.Task] = None
self._running = False
# 标记是否在子进程中运行(由核心管理器传入 ProcessCoreSink 时自动生效)
self._is_subprocess = isinstance(core_sink, ProcessCoreSink)
# 标记是否在子进程中运行
self._is_subprocess = False
@classmethod
def from_process_queues(

View File

@@ -2,6 +2,11 @@
Adapter 管理器
负责管理所有注册的适配器,支持子进程自动启动和生命周期管理。
重构说明2025-11
- 使用 CoreSinkManager 统一管理 InProcessCoreSink 和 ProcessCoreSink
- 根据适配器的 run_in_subprocess 属性自动选择 CoreSink 类型
- 子进程适配器通过 CoreSinkManager 的通信队列与主进程交互
"""
from __future__ import annotations
@@ -15,7 +20,6 @@ if TYPE_CHECKING:
from src.plugin_system.base.base_adapter import BaseAdapter
from mofox_bus import ProcessCoreSinkServer
from src.common.core_sink import get_core_sink
from src.common.logger import get_logger
logger = get_logger("adapter_manager")
@@ -34,6 +38,11 @@ def _adapter_process_entry(
incoming_queue: mp.Queue,
outgoing_queue: mp.Queue,
):
"""
子进程适配器入口函数
在子进程中运行,创建 ProcessCoreSink 与主进程通信
"""
import asyncio
import contextlib
from mofox_bus import ProcessCoreSink
@@ -44,9 +53,14 @@ def _adapter_process_entry(
if plugin_info:
plugin_cls = _load_class(plugin_info["module"], plugin_info["class"])
plugin_instance = plugin_cls(plugin_info["plugin_dir"], plugin_info["metadata"])
# 创建 ProcessCoreSink 用于与主进程通信
core_sink = ProcessCoreSink(to_core_queue=incoming_queue, from_core_queue=outgoing_queue)
# 创建并启动适配器
adapter = adapter_cls(core_sink, plugin=plugin_instance)
await adapter.start()
try:
while not getattr(core_sink, "_closed", False):
await asyncio.sleep(0.2)
@@ -61,21 +75,23 @@ def _adapter_process_entry(
class AdapterProcess:
"""适配器子进程封装:管理子进程的生命周期与通信桥接"""
"""
适配器子进程封装:管理子进程的生命周期与通信桥接
使用 CoreSinkManager 创建通信队列,自动维护与子进程的消息通道
"""
def __init__(self, adapter_cls: "type[BaseAdapter]", plugin, core_sink) -> None:
def __init__(self, adapter_cls: "type[BaseAdapter]", plugin) -> None:
self.adapter_cls = adapter_cls
self.adapter_name = adapter_cls.adapter_name
self.plugin = plugin
self.process: mp.Process | None = None
self._ctx = mp.get_context("spawn")
self._incoming_queue: mp.Queue = self._ctx.Queue()
self._outgoing_queue: mp.Queue = self._ctx.Queue()
self._incoming_queue: mp.Queue | None = None
self._outgoing_queue: mp.Queue | None = None
self._bridge: ProcessCoreSinkServer | None = None
self._core_sink = core_sink
self._adapter_path: tuple[str, str] = (adapter_cls.__module__, adapter_cls.__name__)
self._plugin_info = self._extract_plugin_info(plugin)
self._outgoing_handler = None
@staticmethod
def _extract_plugin_info(plugin) -> dict | None:
@@ -88,37 +104,28 @@ class AdapterProcess:
"metadata": getattr(plugin, "plugin_meta", None),
}
def _make_outgoing_handler(self):
async def _handler(envelope):
if self._bridge:
await self._bridge.push_outgoing(envelope)
return _handler
async def start(self) -> bool:
"""启动适配器子进程"""
try:
logger.info(f"启动适配器子进程: {self.adapter_name}")
self._bridge = ProcessCoreSinkServer(
incoming_queue=self._incoming_queue,
outgoing_queue=self._outgoing_queue,
core_handler=self._core_sink.send,
name=self.adapter_name,
)
self._bridge.start()
if hasattr(self._core_sink, "set_outgoing_handler"):
self._outgoing_handler = self._make_outgoing_handler()
try:
self._core_sink.set_outgoing_handler(self._outgoing_handler)
except Exception:
logger.exception("Failed to register outgoing bridge for %s", self.adapter_name)
# 从 CoreSinkManager 获取通信队列
from src.common.core_sink_manager import get_core_sink_manager
manager = get_core_sink_manager()
self._incoming_queue, self._outgoing_queue = manager.create_process_sink_queues(self.adapter_name)
# 启动子进程
self.process = self._ctx.Process(
target=_adapter_process_entry,
args=(self._adapter_path, self._plugin_info, self._incoming_queue, self._outgoing_queue),
name=f"{self.adapter_name}-proc",
)
self.process.start()
logger.info(f"启动适配器子进程 {self.adapter_name} (PID: {self.process.pid})")
return True
except Exception as e:
logger.error(f"启动适配器子进程 {self.adapter_name} 失败: {e}", exc_info=True)
return False
@@ -127,26 +134,31 @@ class AdapterProcess:
"""停止适配器子进程"""
if not self.process:
return
logger.info(f"停止适配器子进程: {self.adapter_name} (PID: {self.process.pid})")
try:
remover = getattr(self._core_sink, "remove_outgoing_handler", None)
if callable(remover) and self._outgoing_handler:
try:
remover(self._outgoing_handler)
except Exception:
logger.exception(f"移除 {self.adapter_name} 的 outgoing bridge 失败")
if self._bridge:
await self._bridge.close()
# 从 CoreSinkManager 移除通信队列
from src.common.core_sink_manager import get_core_sink_manager
manager = get_core_sink_manager()
manager.remove_process_sink(self.adapter_name)
# 等待子进程结束
if self.process.is_alive():
self.process.join(timeout=5.0)
if self.process.is_alive():
logger.warning(f"适配器 {self.adapter_name} 未能及时停止,强制终止中")
self.process.terminate()
self.process.join()
except Exception as e:
logger.error(f"停止适配器子进程 {self.adapter_name} 时发生错误: {e}", exc_info=True)
finally:
self.process = None
self._incoming_queue = None
self._outgoing_queue = None
def is_running(self) -> bool:
"""适配器是否正在运行"""
@@ -155,7 +167,13 @@ class AdapterProcess:
return self.process.is_alive()
class AdapterManager:
"""适配器管理器"""
"""
适配器管理器
负责管理所有注册的适配器,根据 run_in_subprocess 属性自动选择:
- run_in_subprocess=True: 在子进程中运行,使用 ProcessCoreSink
- run_in_subprocess=False: 在主进程中运行,使用 InProcessCoreSink
"""
def __init__(self):
# 注册信息name -> (adapter class, plugin instance | None)
@@ -178,14 +196,26 @@ class AdapterManager:
self._adapter_defs[adapter_name] = (adapter_cls, plugin)
adapter_version = getattr(adapter_cls, 'adapter_version', 'unknown')
logger.info(f"注册适配器: {adapter_name} v{adapter_version}")
run_in_subprocess = getattr(adapter_cls, 'run_in_subprocess', False)
logger.info(
f"注册适配器: {adapter_name} v{adapter_version} "
f"(子进程: {'' if run_in_subprocess else ''})"
)
async def start_adapter(self, adapter_name: str) -> bool:
"""启动指定适配器"""
"""
启动指定适配器
根据适配器的 run_in_subprocess 属性自动选择:
- True: 创建子进程,使用 ProcessCoreSink
- False: 在当前进程,使用 InProcessCoreSink
"""
definition = self._adapter_defs.get(adapter_name)
if not definition:
logger.error(f"适配器 {adapter_name} 未注册")
return False
adapter_cls, plugin = definition
run_in_subprocess = getattr(adapter_cls, "run_in_subprocess", False)
@@ -193,15 +223,14 @@ class AdapterManager:
return await self._start_adapter_subprocess(adapter_name, adapter_cls, plugin)
return await self._start_adapter_in_process(adapter_name, adapter_cls, plugin)
async def _start_adapter_subprocess(self, adapter_name: str, adapter_cls: type[BaseAdapter], plugin) -> bool:
"""在子进程中启动适配器"""
try:
core_sink = get_core_sink()
except Exception as e:
logger.error(f"无法获取 core_sink启动子进程 {adapter_name} 失败: {e}", exc_info=True)
return False
adapter_process = AdapterProcess(adapter_cls, plugin, core_sink)
async def _start_adapter_subprocess(
self,
adapter_name: str,
adapter_cls: type[BaseAdapter],
plugin
) -> bool:
"""在子进程中启动适配器(使用 ProcessCoreSink"""
adapter_process = AdapterProcess(adapter_cls, plugin)
success = await adapter_process.start()
if success:
@@ -209,15 +238,25 @@ class AdapterManager:
return success
async def _start_adapter_in_process(self, adapter_name: str, adapter_cls: type[BaseAdapter], plugin) -> bool:
"""在当前进程中启动适配器"""
async def _start_adapter_in_process(
self,
adapter_name: str,
adapter_cls: type[BaseAdapter],
plugin
) -> bool:
"""在当前进程中启动适配器(使用 InProcessCoreSink"""
try:
core_sink = get_core_sink()
# 从 CoreSinkManager 获取 InProcessCoreSink
from src.common.core_sink_manager import get_core_sink_manager
core_sink = get_core_sink_manager().get_in_process_sink()
adapter = adapter_cls(core_sink, plugin=plugin) # type: ignore[call-arg]
await adapter.start()
self._in_process_adapters[adapter_name] = adapter
logger.info(f"适配器 {adapter_name} 已在当前进程启动")
return True
except Exception as e:
logger.error(f"启动适配器 {adapter_name} 失败: {e}", exc_info=True)
return False