From 2e2fd90497b2e8b0d1c7e27660cf13346e05be6d Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Thu, 28 Aug 2025 18:25:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(plugin-system):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=A7=A6=E5=8F=91=E5=92=8C=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E7=9A=84=E7=99=BD=E5=90=8D=E5=8D=95=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为事件系统添加权限控制功能,包括: - 在BaseEvent中新增allowed_subscribers和allowed_triggers白名单字段 - 事件管理器触发和订阅时进行白名单验证 - 为所有系统默认事件设置仅允许SYSTEM插件触发 - 在所有事件触发调用处显式传递plugin_name="SYSTEM"参数 确保只有授权插件可以触发特定事件和订阅处理器,增强系统安全性。 --- src/chat/chat_loop/cycle_processor.py | 2 +- src/chat/message_receive/bot.py | 2 +- src/chat/replyer/default_generator.py | 4 +-- src/main.py | 2 +- src/plugin_system/base/base_event.py | 15 ++++++++--- src/plugin_system/core/event_manager.py | 34 ++++++++++++++++++++----- 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/chat/chat_loop/cycle_processor.py b/src/chat/chat_loop/cycle_processor.py index 781d9dde4..4a946bb72 100644 --- a/src/chat/chat_loop/cycle_processor.py +++ b/src/chat/chat_loop/cycle_processor.py @@ -100,7 +100,7 @@ class CycleProcessor: from src.plugin_system.core.event_manager import event_manager from src.plugin_system.base.component_types import EventType # 触发 ON_PLAN 事件 - result = await event_manager.trigger_event(EventType.ON_PLAN, stream_id=self.context.stream_id) + result = await event_manager.trigger_event(EventType.ON_PLAN, plugin_name="SYSTEM", stream_id=self.context.stream_id) if result and not result.all_continue_process(): return diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 760d69062..9a29998d8 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -437,7 +437,7 @@ class ChatBot: logger.info(f"命令处理完成,跳过后续消息处理: {cmd_result}") return - result = await event_manager.trigger_event(EventType.ON_MESSAGE,message=message) + result = await event_manager.trigger_event(EventType.ON_MESSAGE,plugin_name="SYSTEM",message=message) if not result.all_continue_process(): raise UserWarning(f"插件{result.get_summary().get('stopped_handlers','')}于消息到达时取消了消息处理") diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index 9c9dc334f..77033472d 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -370,7 +370,7 @@ class DefaultReplyer: from src.plugin_system.core.event_manager import event_manager if not from_plugin: - result = await event_manager.trigger_event(EventType.POST_LLM,prompt=prompt,stream_id=stream_id) + result = await event_manager.trigger_event(EventType.POST_LLM,plugin_name="SYSTEM",prompt=prompt,stream_id=stream_id) if not result.all_continue_process(): raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于请求前中断了内容生成") @@ -390,7 +390,7 @@ class DefaultReplyer: } # 触发 AFTER_LLM 事件 if not from_plugin: - result = await event_manager.trigger_event(EventType.AFTER_LLM,prompt=prompt,llm_response=llm_response,stream_id=stream_id) + result = await event_manager.trigger_event(EventType.AFTER_LLM,plugin_name="SYSTEM",prompt=prompt,llm_response=llm_response,stream_id=stream_id) if not result.all_continue_process(): raise UserWarning(f"插件{result.get_summary().get('stopped_handlers','')}于请求后取消了内容生成") except UserWarning as e: diff --git a/src/main.py b/src/main.py index 7ac8cb76d..6a5f989b0 100644 --- a/src/main.py +++ b/src/main.py @@ -254,7 +254,7 @@ MoFox_Bot(第三方修改版) try: - await event_manager.trigger_event(EventType.ON_START) + await event_manager.trigger_event(EventType.ON_START,plugin_name="SYSTEM") init_time = int(1000 * (time.time() - init_start_time)) logger.info(f"初始化完成,神经元放电{init_time}次") except Exception as e: diff --git a/src/plugin_system/base/base_event.py b/src/plugin_system/base/base_event.py index 9e85dc34d..c527752d5 100644 --- a/src/plugin_system/base/base_event.py +++ b/src/plugin_system/base/base_event.py @@ -3,18 +3,18 @@ from typing import List, Dict, Any, Optional from src.common.logger import get_logger logger = get_logger("base_event") - + class HandlerResult: """事件处理器执行结果 所有事件处理器必须返回此类的实例 """ - def __init__(self, success: bool, continue_process: bool, message: str = "", handler_name: str = ""): + def __init__(self, success: bool, continue_process: bool, message: Any = {}, handler_name: str = ""): self.success = success self.continue_process = continue_process self.message = message self.handler_name = handler_name - + def __repr__(self): return f"HandlerResult(success={self.success}, continue_process={self.continue_process}, message='{self.message}', handler_name='{self.handler_name}')" @@ -67,9 +67,16 @@ class HandlerResultsCollection: } class BaseEvent: - def __init__(self, name: str): + def __init__( + self, + name: str, + allowed_subscribers: List[str]=[], + allowed_triggers: List[str]=[] + ): self.name = name self.enabled = True + self.allowed_subscribers = allowed_subscribers # 记录事件处理器名 + self.allowed_triggers = allowed_triggers # 记录插件名 from src.plugin_system.base.base_events_handler import BaseEventHandler self.subscribers: List["BaseEventHandler"] = [] # 订阅该事件的事件处理器列表 diff --git a/src/plugin_system/core/event_manager.py b/src/plugin_system/core/event_manager.py index b6845c3ef..38d5775da 100644 --- a/src/plugin_system/core/event_manager.py +++ b/src/plugin_system/core/event_manager.py @@ -40,12 +40,18 @@ class EventManager: self._initialized = True logger.info("EventManager 单例初始化完成") - def register_event(self, event_name: Union[EventType, str]) -> bool: + def register_event( + self, + event_name: Union[EventType, str], + allowed_subscribers: List[str]=[], + allowed_triggers: List[str]=[] + ) -> bool: """注册一个新的事件 Args: event_name Union[EventType, str]: 事件名称 - + allowed_subscribers: List[str]: 事件订阅者白名单, + allowed_triggers: List[str]: 事件触发插件白名单 Returns: bool: 注册成功返回True,已存在返回False """ @@ -53,7 +59,7 @@ class EventManager: logger.warning(f"事件 {event_name} 已存在,跳过注册") return False - event = BaseEvent(event_name) + event = BaseEvent(event_name,allowed_subscribers,allowed_triggers) self._events[event_name] = event logger.info(f"事件 {event_name} 注册成功") @@ -210,7 +216,12 @@ class EventManager: if handler_instance in event.subscribers: logger.warning(f"事件处理器 {handler_name} 已经订阅了事件 {event_name},跳过重复订阅") return True - + + # 白名单检查 + if event.allowed_subscribers and handler_name not in event.allowed_subscribers: + logger.warning(f"事件处理器 {handler_name} 不在事件 {event_name} 的订阅者白名单中,无法订阅") + return False + event.subscribers.append(handler_instance) # 按权重从高到低排序订阅者 @@ -264,11 +275,12 @@ class EventManager: return {handler.handler_name: handler for handler in event.subscribers} - async def trigger_event(self, event_name: Union[EventType, str], **kwargs) -> Optional[HandlerResultsCollection]: + async def trigger_event(self, event_name: Union[EventType, str], plugin_name: Optional[str]="", **kwargs) -> Optional[HandlerResultsCollection]: """触发指定事件 Args: event_name Union[EventType, str]: 事件名称 + plugin_name str: 触发事件的插件名 **kwargs: 传递给处理器的参数 Returns: @@ -280,7 +292,15 @@ class EventManager: if event is None: logger.error(f"事件 {event_name} 不存在,无法触发") return None - + + # 插件白名单检查 + if event.allowed_triggers and not plugin_name: + logger.warning(f"事件 {event_name} 存在触发者白名单,缺少plugin_name无法验证权限,已拒绝触发!") + return None + elif event.allowed_triggers and plugin_name not in event.allowed_triggers: + logger.warning(f"插件 {plugin_name} 没有权限触发事件 {event_name},已拒绝触发!") + return None + return await event.activate(params) def init_default_events(self) -> None: @@ -297,7 +317,7 @@ class EventManager: ] for event_name in default_events: - self.register_event(event_name) + self.register_event(event_name,allowed_triggers=["SYSTEM"]) logger.info("默认事件初始化完成") From 8d77d1cc3f44d9b82473a1f3dae142d85db86703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=85=E8=AF=BA=E7=8B=90?= <212194964+foxcyber907@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:35:59 +0800 Subject: [PATCH 2/3] Refactor permission checks and decorators usage Refactored permission checks in built-in plugins to use the new @require_permission decorator, improving code clarity and consistency. Enhanced permission_decorators to better extract ChatStream and support PlusCommand. Updated PermissionCommand to use permission decorators for all subcommands, improved user mention parsing, and improved feedback messages. Registered a new permission node for sending feeds in MaiZone plugin and updated command registration to use PlusCommand info. --- src/plugin_system/base/command_args.py | 6 +- .../utils/permission_decorators.py | 64 +++++--- .../commands/send_feed_command.py | 36 +---- .../built_in/maizone_refactored/plugin.py | 10 +- .../built_in/permission_management/plugin.py | 144 ++++++++++-------- 5 files changed, 140 insertions(+), 120 deletions(-) diff --git a/src/plugin_system/base/command_args.py b/src/plugin_system/base/command_args.py index 46a2b701b..5d8c1ad2c 100644 --- a/src/plugin_system/base/command_args.py +++ b/src/plugin_system/base/command_args.py @@ -50,7 +50,8 @@ class CommandArgs: self._parsed_args = self._raw_args.split() return self._parsed_args - + + @property def is_empty(self) -> bool: """检查参数是否为空 @@ -73,7 +74,8 @@ class CommandArgs: if 0 <= index < len(args): return args[index] return default - + + @property def get_first(self, default: str = "") -> str: """获取第一个参数 diff --git a/src/plugin_system/utils/permission_decorators.py b/src/plugin_system/utils/permission_decorators.py index ae5b48e0e..0f31a94f9 100644 --- a/src/plugin_system/utils/permission_decorators.py +++ b/src/plugin_system/utils/permission_decorators.py @@ -9,9 +9,9 @@ from typing import Callable, Optional from inspect import iscoroutinefunction from src.plugin_system.apis.permission_api import permission_api -from src.plugin_system.apis.send_api import send_message +from src.plugin_system.apis.send_api import text_to_stream from src.plugin_system.apis.logging_api import get_logger -from src.common.message import ChatStream +from src.chat.message_receive.chat_stream import ChatStream logger = get_logger(__name__) @@ -37,6 +37,8 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) async def async_wrapper(*args, **kwargs): # 尝试从参数中提取 ChatStream 对象 chat_stream = None + + # 首先检查位置参数中的 ChatStream for arg in args: if isinstance(arg, ChatStream): chat_stream = arg @@ -46,21 +48,31 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) if chat_stream is None: 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'): + 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.user_platform, - chat_stream.user_id, + chat_stream.platform, + chat_stream.user_info.user_id, permission_node ) if not has_permission: # 权限不足,发送拒绝消息 message = deny_message or f"❌ 你没有执行此操作的权限\n需要权限: {permission_node}" - await send_message(chat_stream, message) + await text_to_stream(message, chat_stream.stream_id) + # 对于PlusCommand的execute方法,需要返回适当的元组 + if func.__name__ == 'execute' and hasattr(args[0], 'send_text'): + return False, "权限不足", True return # 权限检查通过,执行原函数 @@ -83,13 +95,13 @@ def require_permission(permission_node: str, deny_message: Optional[str] = None) # 检查权限 has_permission = permission_api.check_permission( - chat_stream.user_platform, - chat_stream.user_id, + chat_stream.platform, + chat_stream.user_info.user_id, permission_node ) if not has_permission: - logger.warning(f"用户 {chat_stream.user_platform}:{chat_stream.user_id} 没有权限 {permission_node}") + logger.warning(f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 没有权限 {permission_node}") return # 权限检查通过,执行原函数 @@ -124,6 +136,8 @@ def require_master(deny_message: Optional[str] = None): async def async_wrapper(*args, **kwargs): # 尝试从参数中提取 ChatStream 对象 chat_stream = None + + # 首先检查位置参数中的 ChatStream for arg in args: if isinstance(arg, ChatStream): chat_stream = arg @@ -133,20 +147,28 @@ def require_master(deny_message: Optional[str] = None): if chat_stream is None: 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'): + 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.user_platform, - chat_stream.user_id + chat_stream.platform, + chat_stream.user_info.user_id ) if not is_master: - # 权限不足,发送拒绝消息 message = deny_message or "❌ 此操作仅限Master用户执行" - await send_message(chat_stream, message) + await text_to_stream(message, chat_stream.stream_id) + if func.__name__ == 'execute' and hasattr(args[0], 'send_text'): + return False, "需要Master权限", True return # 权限检查通过,执行原函数 @@ -169,12 +191,12 @@ def require_master(deny_message: Optional[str] = None): # 检查是否为Master用户 is_master = permission_api.is_master( - chat_stream.user_platform, - chat_stream.user_id + chat_stream.platform, + chat_stream.user_info.user_id ) if not is_master: - logger.warning(f"用户 {chat_stream.user_platform}:{chat_stream.user_id} 不是Master用户") + logger.warning(f"用户 {chat_stream.platform}:{chat_stream.user_info.user_id} 不是Master用户") return # 权限检查通过,执行原函数 @@ -209,8 +231,8 @@ class PermissionChecker: bool: 是否拥有权限 """ return permission_api.check_permission( - chat_stream.user_platform, - chat_stream.user_id, + chat_stream.platform, + chat_stream.user_info.user_id, permission_node ) @@ -226,8 +248,8 @@ class PermissionChecker: bool: 是否为Master用户 """ return permission_api.is_master( - chat_stream.user_platform, - chat_stream.user_id + chat_stream.platform, + chat_stream.user_info.user_id ) @staticmethod @@ -248,7 +270,7 @@ class PermissionChecker: if not has_permission: message = deny_message or f"❌ 你没有执行此操作的权限\n需要权限: {permission_node}" - await send_message(chat_stream, message) + await text_to_stream(message, chat_stream.stream_id) return has_permission @@ -269,6 +291,6 @@ class PermissionChecker: if not is_master: message = deny_message or "❌ 此操作仅限Master用户执行" - await send_message(chat_stream, message) + await text_to_stream(message, chat_stream.stream_id) return is_master diff --git a/src/plugins/built_in/maizone_refactored/commands/send_feed_command.py b/src/plugins/built_in/maizone_refactored/commands/send_feed_command.py index 0f6903b2f..3a7be39d5 100644 --- a/src/plugins/built_in/maizone_refactored/commands/send_feed_command.py +++ b/src/plugins/built_in/maizone_refactored/commands/send_feed_command.py @@ -5,52 +5,32 @@ from typing import Tuple from src.common.logger import get_logger -from src.plugin_system import BaseCommand +from src.plugin_system.base.plus_command import PlusCommand +from src.plugin_system.base.command_args import CommandArgs +from src.plugin_system.utils.permission_decorators import require_permission from ..services.manager import get_qzone_service, get_config_getter logger = get_logger("MaiZone.SendFeedCommand") -class SendFeedCommand(BaseCommand): +class SendFeedCommand(PlusCommand): """ 响应用户通过 `/send_feed` 命令发送说说的请求。 """ command_name: str = "send_feed" command_description: str = "发送一条QQ空间说说" - command_pattern: str = r"^/send_feed(?:\s+(?P.*))?$" - command_help: str = "使用 /send_feed [主题] 来发送一条说说" + command_aliases = ["发空间"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def _check_permission(self) -> bool: - """检查当前用户是否有权限执行此命令""" - user_id = self.message.message_info.user_info.user_id - if not user_id: - return False - - get_config = get_config_getter() - permission_list = get_config("send.permission", []) - permission_type = get_config("send.permission_type", "whitelist") - - if not isinstance(permission_list, list): - return False - - if permission_type == 'whitelist': - return user_id in permission_list - elif permission_type == 'blacklist': - return user_id not in permission_list - return False - - async def execute(self) -> Tuple[bool, str, bool]: + @require_permission("plugin.send.permission") + async def execute(self, args: CommandArgs) -> Tuple[bool, str, bool]: """ 执行命令的核心逻辑。 """ - if not self._check_permission(): - await self.send_text("抱歉,你没有权限使用这个命令哦。") - return False, "权限不足", True - topic = self.matched_groups.get("topic", "") + topic = args.get_remaining() stream_id = self.message.chat_stream.stream_id await self.send_text(f"收到!正在为你生成关于“{topic or '随机'}”的说说,请稍候...") diff --git a/src/plugins/built_in/maizone_refactored/plugin.py b/src/plugins/built_in/maizone_refactored/plugin.py index 6bc5ecd11..f6c6fbb29 100644 --- a/src/plugins/built_in/maizone_refactored/plugin.py +++ b/src/plugins/built_in/maizone_refactored/plugin.py @@ -13,6 +13,7 @@ from src.plugin_system import ( register_plugin ) from src.plugin_system.base.config_types import ConfigField +from src.plugin_system.apis.permission_api import permission_api from .actions.read_feed_action import ReadFeedAction from .actions.send_feed_action import SendFeedAction @@ -82,7 +83,12 @@ class MaiZoneRefactoredPlugin(BasePlugin): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - + permission_api.register_permission_node( + "plugin.send.permission", + "是否可以使用机器人发送说说", + "maiZone", + False + ) content_service = ContentService(self.get_config) image_service = ImageService(self.get_config) cookie_service = CookieService(self.get_config) @@ -102,5 +108,5 @@ class MaiZoneRefactoredPlugin(BasePlugin): return [ (SendFeedAction.get_action_info(), SendFeedAction), (ReadFeedAction.get_action_info(), ReadFeedAction), - (SendFeedCommand.get_command_info(), SendFeedCommand), + (SendFeedCommand.get_plus_command_info(), SendFeedCommand), ] \ No newline at end of file diff --git a/src/plugins/built_in/permission_management/plugin.py b/src/plugins/built_in/permission_management/plugin.py index f5b87b093..d8a39107a 100644 --- a/src/plugins/built_in/permission_management/plugin.py +++ b/src/plugins/built_in/permission_management/plugin.py @@ -16,6 +16,7 @@ from src.plugin_system.apis.permission_api import permission_api from src.plugin_system.apis.logging_api import get_logger from src.plugin_system.base.component_types import PlusCommandInfo, ChatType from src.plugin_system.base.config_types import ConfigField +from src.plugin_system.utils.permission_decorators import require_permission, require_master, PermissionChecker logger = get_logger("Permission") @@ -46,70 +47,38 @@ class PermissionCommand(PlusCommand): "permission_manager", True ) - + async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: """执行权限管理命令""" - if args.is_empty(): + if args.is_empty: await self._show_help() return True, "显示帮助信息", True - subcommand = args.get_first().lower() + subcommand = args.get_first.lower() remaining_args = args.get_args()[1:] # 获取除第一个参数外的所有参数 chat_stream = self.message.chat_stream - # 检查基本查看权限 - can_view = permission_api.check_permission( - chat_stream.platform, - chat_stream.user_info.user_id, - "plugin.permission.view" - ) or permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id) - - # 检查管理权限 - can_manage = permission_api.check_permission( - chat_stream.platform, - chat_stream.user_info.user_id, - "plugin.permission.manage" - ) or permission_api.is_master(chat_stream.platform, chat_stream.user_info.user_id) - if subcommand in ["grant", "授权", "give"]: - if not can_manage: - await self.send_text("❌ 你没有权限管理的权限") - return True, "权限不足", True await self._grant_permission(chat_stream, remaining_args) return True, "执行授权命令", True elif subcommand in ["revoke", "撤销", "remove"]: - if not can_manage: - await self.send_text("❌ 你没有权限管理的权限") - return True, "权限不足", True await self._revoke_permission(chat_stream, remaining_args) return True, "执行撤销命令", True elif subcommand in ["list", "列表", "ls"]: - if not can_view: - await self.send_text("❌ 你没有查看权限的权限") - return True, "权限不足", True await self._list_permissions(chat_stream, remaining_args) return True, "执行列表命令", True elif subcommand in ["check", "检查"]: - if not can_view: - await self.send_text("❌ 你没有查看权限的权限") - return True, "权限不足", True await self._check_permission(chat_stream, remaining_args) return True, "执行检查命令", True elif subcommand in ["nodes", "节点"]: - if not can_view: - await self.send_text("❌ 你没有查看权限的权限") - return True, "权限不足", True await self._list_nodes(chat_stream, remaining_args) return True, "执行节点命令", True elif subcommand in ["allnodes", "全部节点", "all"]: - if not can_view: - await self.send_text("❌ 你没有查看权限的权限") - return True, "权限不足", True await self._list_all_nodes_with_description(chat_stream) return True, "执行全部节点命令", True @@ -149,11 +118,18 @@ class PermissionCommand(PlusCommand): await self.send_text(help_text) def _parse_user_mention(self, mention: str) -> Optional[str]: - """解析用户提及,提取QQ号""" + """解析用户提及,提取QQ号 + + 支持的格式: + - @<用户名:QQ号> 格式 + - [CQ:at,qq=QQ号] 格式 + - 直接的QQ号 + """ # 匹配 @<用户名:QQ号> 格式,提取QQ号 at_match = re.search(r'@<[^:]+:(\d+)>', mention) if at_match: return at_match.group(1) + # 直接是数字 if mention.isdigit(): @@ -161,62 +137,94 @@ class PermissionCommand(PlusCommand): return None + @staticmethod + def parse_user_from_args(args: CommandArgs, index: int = 0) -> Optional[str]: + """从CommandArgs中解析用户ID + + Args: + args: 命令参数对象 + index: 参数索引,默认为0(第一个参数) + + Returns: + Optional[str]: 解析出的用户ID,如果解析失败返回None + """ + if index >= args.count(): + return None + + mention = args.get_arg(index) + + # 匹配 @<用户名:QQ号> 格式,提取QQ号 + at_match = re.search(r'@<[^:]+:(\d+)>', mention) + if at_match: + return at_match.group(1) + + # 匹配传统的 [CQ:at,qq=数字] 格式 + cq_match = re.search(r'\[CQ:at,qq=(\d+)\]', mention) + if cq_match: + return cq_match.group(1) + + # 直接是数字 + if mention.isdigit(): + return mention + + return None + + @require_permission("plugin.permission.manage", "❌ 你没有权限管理的权限") async def _grant_permission(self, chat_stream, args: List[str]): """授权用户权限""" if len(args) < 2: await self.send_text("❌ 用法: /permission grant <@用户|QQ号> <权限节点>") return - user_mention = args[0] - permission_node = args[1] - - # 解析用户ID - user_id = self._parse_user_mention(user_mention) + # 解析用户ID - 使用新的解析方法 + user_id = self._parse_user_mention(args[0]) if not user_id: - await self.send_text("❌ 无效的用户格式,请使用 @用户 或直接输入QQ号") + await self.send_text("❌ 无效的用户格式,请使用 @<用户名:QQ号> 或直接输入QQ号") return + permission_node = args[1] + # 执行授权 success = permission_api.grant_permission(chat_stream.platform, user_id, permission_node) if success: - await self.send_text(f"✅ 已授权用户 {user_id} 权限节点 {permission_node}") + await self.send_text(f"✅ 已授权用户 {user_id} 权限节点 `{permission_node}`") else: await self.send_text("❌ 授权失败,请检查权限节点是否存在") + @require_permission("plugin.permission.manage", "❌ 你没有权限管理的权限") async def _revoke_permission(self, chat_stream, args: List[str]): """撤销用户权限""" if len(args) < 2: await self.send_text("❌ 用法: /permission revoke <@用户|QQ号> <权限节点>") return - user_mention = args[0] - permission_node = args[1] - - # 解析用户ID - user_id = self._parse_user_mention(user_mention) + # 解析用户ID - 使用新的解析方法 + user_id = self._parse_user_mention(args[0]) if not user_id: - await self.send_text("❌ 无效的用户格式,请使用 @用户 或直接输入QQ号") + await self.send_text("❌ 无效的用户格式,请使用 @<用户名:QQ号> 或直接输入QQ号") return + permission_node = args[1] + # 执行撤销 success = permission_api.revoke_permission(chat_stream.platform, user_id, permission_node) if success: - await self.send_text(f"✅ 已撤销用户 {user_id} 权限节点 {permission_node}") + await self.send_text(f"✅ 已撤销用户 {user_id} 权限节点 `{permission_node}`") else: await self.send_text("❌ 撤销失败,请检查权限节点是否存在") + @require_permission("plugin.permission.view", "❌ 你没有查看权限的权限") async def _list_permissions(self, chat_stream, args: List[str]): """列出用户权限""" target_user_id = None if args: - # 指定了用户 - user_mention = args[0] - target_user_id = self._parse_user_mention(user_mention) + # 指定了用户 - 使用新的解析方法 + target_user_id = self._parse_user_mention(args[0]) if not target_user_id: - await self.send_text("❌ 无效的用户格式,请使用 @用户 或直接输入QQ号") + await self.send_text("❌ 无效的用户格式,请使用 @<用户名:QQ号> 或直接输入QQ号") return else: # 查看自己的权限 @@ -229,45 +237,46 @@ class PermissionCommand(PlusCommand): permissions = permission_api.get_user_permissions(chat_stream.platform, target_user_id) if is_master: - response = f"👑 用户 {target_user_id} 是Master用户,拥有所有权限" + response = f"👑 用户 `{target_user_id}` 是Master用户,拥有所有权限" else: if permissions: - perm_list = "\n".join([f"• {perm}" for perm in permissions]) - response = f"📋 用户 {target_user_id} 拥有的权限:\n{perm_list}" + perm_list = "\n".join([f"• `{perm}`" for perm in permissions]) + response = f"📋 用户 `{target_user_id}` 拥有的权限:\n{perm_list}" else: - response = f"📋 用户 {target_user_id} 没有任何权限" + response = f"📋 用户 `{target_user_id}` 没有任何权限" await self.send_text(response) + @require_permission("plugin.permission.view", "❌ 你没有查看权限的权限") async def _check_permission(self, chat_stream, args: List[str]): """检查用户权限""" if len(args) < 2: await self.send_text("❌ 用法: /permission check <@用户|QQ号> <权限节点>") return - user_mention = args[0] - permission_node = args[1] - - # 解析用户ID - user_id = self._parse_user_mention(user_mention) + # 解析用户ID - 使用新的解析方法 + user_id = self._parse_user_mention(args[0]) if not user_id: - await self.send_text("❌ 无效的用户格式,请使用 @用户 或直接输入QQ号") + await self.send_text("❌ 无效的用户格式,请使用 @<用户名:QQ号> 或直接输入QQ号") return + permission_node = args[1] + # 检查权限 has_permission = permission_api.check_permission(chat_stream.platform, user_id, permission_node) is_master = permission_api.is_master(chat_stream.platform, user_id) if has_permission: if is_master: - response = f"✅ 用户 {user_id} 拥有权限 {permission_node}(Master用户)" + response = f"✅ 用户 `{user_id}` 拥有权限 `{permission_node}`(Master用户)" else: - response = f"✅ 用户 {user_id} 拥有权限 {permission_node}" + response = f"✅ 用户 `{user_id}` 拥有权限 `{permission_node}`" else: - response = f"❌ 用户 {user_id} 没有权限 {permission_node}" + response = f"❌ 用户 `{user_id}` 没有权限 `{permission_node}`" await self.send_text(response) + @require_permission("plugin.permission.view", "❌ 你没有查看权限的权限") async def _list_nodes(self, chat_stream, args: List[str]): """列出权限节点""" plugin_name = args[0] if args else None @@ -300,6 +309,7 @@ class PermissionCommand(PlusCommand): await self.send_text(response) + @require_permission("plugin.permission.view", "❌ 你没有查看权限的权限") async def _list_all_nodes_with_description(self, chat_stream): """列出所有插件的权限节点(带详细描述)""" # 获取所有权限节点 From b28116e17bec2abf8dfd890a2ad5e3bbc0364940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=85=E8=AF=BA=E7=8B=90?= <212194964+foxcyber907@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:42:04 +0800 Subject: [PATCH 3/3] Change default start_index in get_remaining to 0 Updated the get_remaining method in CommandArgs to default start_index to 0 instead of 1, ensuring all arguments are included by default when retrieving remaining parameters. --- src/plugin_system/base/command_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin_system/base/command_args.py b/src/plugin_system/base/command_args.py index 5d8c1ad2c..b3d2611cf 100644 --- a/src/plugin_system/base/command_args.py +++ b/src/plugin_system/base/command_args.py @@ -87,7 +87,7 @@ class CommandArgs: """ return self.get_arg(0, default) - def get_remaining(self, start_index: int = 1) -> str: + def get_remaining(self, start_index: int = 0) -> str: """获取从指定索引开始的剩余参数字符串 Args: