diff --git a/astrbot/core/pipeline/waking_check/stage.py b/astrbot/core/pipeline/waking_check/stage.py index 6fb3378ca..7c91a2fcf 100644 --- a/astrbot/core/pipeline/waking_check/stage.py +++ b/astrbot/core/pipeline/waking_check/stage.py @@ -8,6 +8,7 @@ from astrbot.core.message.components import At, AtAll from astrbot.core.star.star_handler import star_handlers_registry, EventType from astrbot.core.star.star import star_map from astrbot.core.star.filter.permission import PermissionTypeFilter +from astrbot.core.star.session_plugin_manager import SessionPluginManager @register_stage @@ -160,6 +161,9 @@ class WakingCheckStage(Stage): event.clear_extra() + # 根据会话配置过滤插件处理器 + activated_handlers = SessionPluginManager.filter_handlers_by_session(event, activated_handlers) + event.set_extra("activated_handlers", activated_handlers) event.set_extra("handlers_parsed_params", handlers_parsed_params) diff --git a/astrbot/core/star/session_plugin_manager.py b/astrbot/core/star/session_plugin_manager.py new file mode 100644 index 000000000..8f68247b3 --- /dev/null +++ b/astrbot/core/star/session_plugin_manager.py @@ -0,0 +1,135 @@ +""" +会话插件管理器 - 负责管理每个会话的插件启停状态 +""" + +from astrbot.core import sp, logger +from typing import Dict, List, Optional +from astrbot.core.platform.astr_message_event import AstrMessageEvent + + +class SessionPluginManager: + """管理会话级别的插件启停状态""" + + @staticmethod + def is_plugin_enabled_for_session(session_id: str, plugin_name: str) -> bool: + """检查插件是否在指定会话中启用 + + Args: + session_id: 会话ID (unified_msg_origin) + plugin_name: 插件名称 + + Returns: + bool: True表示启用,False表示禁用 + """ + # 获取会话插件配置 + session_plugin_config = sp.get("session_plugin_config", {}) + session_config = session_plugin_config.get(session_id, {}) + + enabled_plugins = session_config.get("enabled_plugins", []) + disabled_plugins = session_config.get("disabled_plugins", []) + + # 如果插件在禁用列表中,返回False + if plugin_name in disabled_plugins: + return False + + # 如果插件在启用列表中,返回True + if plugin_name in enabled_plugins: + return True + + # 如果都没有配置,默认为启用(兼容性考虑) + return True + + @staticmethod + def set_plugin_status_for_session(session_id: str, plugin_name: str, enabled: bool) -> None: + """设置插件在指定会话中的启停状态 + + Args: + session_id: 会话ID (unified_msg_origin) + plugin_name: 插件名称 + enabled: True表示启用,False表示禁用 + """ + # 获取当前配置 + session_plugin_config = sp.get("session_plugin_config", {}) + if session_id not in session_plugin_config: + session_plugin_config[session_id] = { + "enabled_plugins": [], + "disabled_plugins": [] + } + + session_config = session_plugin_config[session_id] + enabled_plugins = session_config.get("enabled_plugins", []) + disabled_plugins = session_config.get("disabled_plugins", []) + + if enabled: + # 启用插件 + if plugin_name in disabled_plugins: + disabled_plugins.remove(plugin_name) + if plugin_name not in enabled_plugins: + enabled_plugins.append(plugin_name) + else: + # 禁用插件 + if plugin_name in enabled_plugins: + enabled_plugins.remove(plugin_name) + if plugin_name not in disabled_plugins: + disabled_plugins.append(plugin_name) + + # 保存配置 + session_config["enabled_plugins"] = enabled_plugins + session_config["disabled_plugins"] = disabled_plugins + session_plugin_config[session_id] = session_config + sp.put("session_plugin_config", session_plugin_config) + + logger.info(f"会话 {session_id} 的插件 {plugin_name} 状态已更新为: {'启用' if enabled else '禁用'}") + + @staticmethod + def get_session_plugin_config(session_id: str) -> Dict[str, List[str]]: + """获取指定会话的插件配置 + + Args: + session_id: 会话ID (unified_msg_origin) + + Returns: + Dict[str, List[str]]: 包含enabled_plugins和disabled_plugins的字典 + """ + session_plugin_config = sp.get("session_plugin_config", {}) + return session_plugin_config.get(session_id, { + "enabled_plugins": [], + "disabled_plugins": [] + }) + + @staticmethod + def filter_handlers_by_session(event: AstrMessageEvent, handlers: List) -> List: + """根据会话配置过滤处理器列表 + + Args: + event: 消息事件 + handlers: 原始处理器列表 + + Returns: + List: 过滤后的处理器列表 + """ + from astrbot.core.star.star import star_map + + session_id = event.unified_msg_origin + filtered_handlers = [] + + for handler in handlers: + # 获取处理器对应的插件 + plugin = star_map.get(handler.handler_module_path) + if not plugin: + # 如果找不到插件元数据,允许执行(可能是系统插件) + filtered_handlers.append(handler) + continue + + # 跳过保留插件(系统插件) + if plugin.reserved: + filtered_handlers.append(handler) + continue + + # 检查插件是否在当前会话中启用 + if SessionPluginManager.is_plugin_enabled_for_session(session_id, plugin.name): + filtered_handlers.append(handler) + else: + logger.debug(f"插件 {plugin.name} 在会话 {session_id} 中被禁用,跳过处理器 {handler.handler_name}") + + return filtered_handlers diff --git a/astrbot/dashboard/routes/session_management.py b/astrbot/dashboard/routes/session_management.py index dd7da8add..ed3af645f 100644 --- a/astrbot/dashboard/routes/session_management.py +++ b/astrbot/dashboard/routes/session_management.py @@ -5,6 +5,7 @@ from quart import request from astrbot.core.db import BaseDatabase from astrbot.core.core_lifecycle import AstrBotCoreLifecycle from astrbot.core.provider.entities import ProviderType +from astrbot.core.star.session_plugin_manager import SessionPluginManager class SessionManagementRoute(Route): @@ -20,6 +21,8 @@ class SessionManagementRoute(Route): "/session/update_persona": ("POST", self.update_session_persona), "/session/update_provider": ("POST", self.update_session_provider), "/session/get_session_info": ("POST", self.get_session_info), + "/session/plugins": ("GET", self.get_session_plugins), + "/session/update_plugin": ("POST", self.update_session_plugin), } self.db_helper = db_helper self.core_lifecycle = core_lifecycle @@ -359,3 +362,82 @@ class SessionManagementRoute(Route): error_msg = f"获取会话信息失败: {str(e)}\n{traceback.format_exc()}" logger.error(error_msg) return Response().error(f"获取会话信息失败: {str(e)}").__dict__ + + async def get_session_plugins(self): + """获取指定会话的插件配置信息""" + try: + session_id = request.args.get("session_id") + + if not session_id: + return Response().error("缺少必要参数: session_id").__dict__ + + # 获取所有已激活的插件 + all_plugins = [] + plugin_manager = self.core_lifecycle.star_context._star_manager + + for plugin in plugin_manager.context.get_all_stars(): + # 只显示已激活的插件,不包括保留插件 + if plugin.activated and not plugin.reserved: + plugin_enabled = SessionPluginManager.is_plugin_enabled_for_session(session_id, plugin.name) + + all_plugins.append({ + "name": plugin.name, + "author": plugin.author, + "desc": plugin.desc, + "enabled": plugin_enabled, + }) + + return Response().ok({ + "session_id": session_id, + "plugins": all_plugins, + }).__dict__ + + except Exception as e: + error_msg = f"获取会话插件配置失败: {str(e)}\n{traceback.format_exc()}" + logger.error(error_msg) + return Response().error(f"获取会话插件配置失败: {str(e)}").__dict__ + + async def update_session_plugin(self): + """更新指定会话的插件启停状态""" + try: + data = await request.get_json() + session_id = data.get("session_id") + plugin_name = data.get("plugin_name") + enabled = data.get("enabled") + + if not session_id: + return Response().error("缺少必要参数: session_id").__dict__ + + if not plugin_name: + return Response().error("缺少必要参数: plugin_name").__dict__ + + if enabled is None: + return Response().error("缺少必要参数: enabled").__dict__ + + # 验证插件是否存在且已激活 + plugin_manager = self.core_lifecycle.star_context._star_manager + plugin = plugin_manager.context.get_registered_star(plugin_name) + + if not plugin: + return Response().error(f"插件 {plugin_name} 不存在").__dict__ + + if not plugin.activated: + return Response().error(f"插件 {plugin_name} 未激活").__dict__ + + if plugin.reserved: + return Response().error(f"插件 {plugin_name} 是系统保留插件,无法管理").__dict__ + + # 使用 SessionPluginManager 更新插件状态 + SessionPluginManager.set_plugin_status_for_session(session_id, plugin_name, enabled) + + return Response().ok({ + "message": f"插件 {plugin_name} 已{'启用' if enabled else '禁用'}", + "session_id": session_id, + "plugin_name": plugin_name, + "enabled": enabled, + }).__dict__ + + except Exception as e: + error_msg = f"更新会话插件状态失败: {str(e)}\n{traceback.format_exc()}" + logger.error(error_msg) + return Response().error(f"更新会话插件状态失败: {str(e)}").__dict__ diff --git a/dashboard/src/views/SessionManagementPage.vue b/dashboard/src/views/SessionManagementPage.vue index b3f8d95ad..7b54dc14c 100644 --- a/dashboard/src/views/SessionManagementPage.vue +++ b/dashboard/src/views/SessionManagementPage.vue @@ -157,6 +157,19 @@ + + +