diff --git a/astrbot/core/pipeline/process_stage/method/llm_request.py b/astrbot/core/pipeline/process_stage/method/llm_request.py index 852c2c33..2952f46d 100644 --- a/astrbot/core/pipeline/process_stage/method/llm_request.py +++ b/astrbot/core/pipeline/process_stage/method/llm_request.py @@ -64,12 +64,14 @@ class LLMRequestSubStage(Stage): if not req.prompt and not req.image_urls: return - # 执行请求 LLM 前事件。 + # 执行请求 LLM 前事件钩子。 # 装饰 system_prompt 等功能 handlers = star_handlers_registry.get_handlers_by_event_type(EventType.OnLLMRequestEvent) for handler in handlers: try: - await handler.handler(event, req) + wrapper = self._call_handler(self.ctx, event, handler.handler, req) + async for ret in wrapper: + yield ret except BaseException: logger.error(traceback.format_exc()) @@ -86,7 +88,9 @@ class LLMRequestSubStage(Stage): handlers = star_handlers_registry.get_handlers_by_event_type(EventType.OnLLMResponseEvent) for handler in handlers: try: - await handler.handler(event, llm_response) + wrapper = self._call_handler(self.ctx, event, handler.handler, llm_response) + async for ret in wrapper: + yield ret except BaseException: logger.error(traceback.format_exc()) diff --git a/astrbot/core/pipeline/process_stage/stage.py b/astrbot/core/pipeline/process_stage/stage.py index 7efe2c13..0026e9d6 100644 --- a/astrbot/core/pipeline/process_stage/stage.py +++ b/astrbot/core/pipeline/process_stage/stage.py @@ -45,12 +45,14 @@ class ProcessStage(Stage): else: yield - # 调用提供商相关请求 + # 调用 LLM 相关请求 if not self.ctx.astrbot_config['provider_settings'].get('enable', True): return if not event._has_send_oper and event.is_at_or_wake_command: + # 是否有过发送操作 and 是否是被 @ 或者通过唤醒前缀 if (event.get_result() and not event.get_result().is_stopped()) or not event.get_result(): + # 事件没有终止传播 provider = self.ctx.plugin_manager.context.get_using_provider() if not provider: diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index 95810130..9c5d5860 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -1,6 +1,7 @@ import random import asyncio import math +import traceback from typing import Union, AsyncGenerator from ..stage import register_stage, Stage from ..context import PipelineContext @@ -88,7 +89,11 @@ class RespondStage(Stage): handlers = star_handlers_registry.get_handlers_by_event_type(EventType.OnAfterMessageSentEvent) for handler in handlers: - # TODO: 如何让这里的 handler 也能使用 LLM 能力。也许需要将 LLMRequestSubStage 提取出来。 - await handler.handler(event) + try: + wrapper = self._call_handler(self.ctx, event, handler.handler) + async for ret in wrapper: + yield ret + except BaseException: + logger.error(traceback.format_exc()) event.clear_result() \ No newline at end of file diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 2f3e0040..847d3549 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -59,9 +59,16 @@ class ResultDecorateStage(Stage): async for _ in self.content_safe_check_stage.process(event, check_text=text): yield + # 发送消息前事件钩子 handlers = star_handlers_registry.get_handlers_by_event_type(EventType.OnDecoratingResultEvent) for handler in handlers: - await handler.handler(event) + try: + wrapper = self._call_handler(self.ctx, event, handler.handler) + async for ret in wrapper: + yield ret + except BaseException: + logger.error(traceback.format_exc()) + # 需要再获取一次。插件可能直接对 chain 进行了替换。 result = event.get_result() diff --git a/astrbot/core/pipeline/stage.py b/astrbot/core/pipeline/stage.py index a5851adf..e9d48a41 100644 --- a/astrbot/core/pipeline/stage.py +++ b/astrbot/core/pipeline/stage.py @@ -36,16 +36,17 @@ class Stage(abc.ABC): ctx: PipelineContext, event: AstrMessageEvent, handler: Awaitable, - **params + *args, + **kwargs, ) -> AsyncGenerator[None, None]: '''调用 Handler。''' # 判断 handler 是否是类方法(通过装饰器注册的没有 __self__ 属性) ready_to_call = None try: - ready_to_call = handler(event, **params) + ready_to_call = handler(event, *args, **kwargs) except TypeError as e: # 向下兼容 - ready_to_call = handler(event, ctx.plugin_manager.context, **params) + ready_to_call = handler(event, ctx.plugin_manager.context, *args, **kwargs) if isinstance(ready_to_call, AsyncGenerator): async for ret in ready_to_call: diff --git a/astrbot/core/pipeline/waking_check/stage.py b/astrbot/core/pipeline/waking_check/stage.py index b48637c1..5693b560 100644 --- a/astrbot/core/pipeline/waking_check/stage.py +++ b/astrbot/core/pipeline/waking_check/stage.py @@ -5,6 +5,7 @@ from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.message.message_event_result import MessageEventResult, MessageChain from astrbot.core.message.components import At, Reply from astrbot.core.star.star_handler import star_handlers_registry, EventType +from astrbot.core.star.star import star_map from astrbot.core.star.filter.command_group import CommandGroupFilter from astrbot.core.star.filter.permission import PermissionTypeFilter @@ -76,34 +77,17 @@ class WakingCheckStage(Stage): # 检查插件的 handler filter activated_handlers = [] handlers_parsed_params = {} # 注册了指令的 handler + for handler in star_handlers_registry.get_handlers_by_event_type(EventType.AdapterMessageEvent): - # filter 需要满足 AND 的逻辑关系 - passed = True - child_command_handler_md = None - + # filter 需满足 AND 逻辑关系 + passed = True permission_not_pass = False - if len(handler.event_filters) == 0: - # 不可能有这种情况, 也不允许有这种情况 - continue - - if 'sub_command' in handler.extras_configs: - # 如果是子指令 continue for filter in handler.event_filters: try: - if isinstance(filter, CommandGroupFilter): - """如果指令组过滤成功, 会返回叶子指令的 StarHandlerMetadata""" - ok, child_command_handler_md = filter.filter( - event, self.ctx.astrbot_config - ) - if not ok: - passed = False - else: - handler = child_command_handler_md # handler 覆盖 - break - elif isinstance(filter, PermissionTypeFilter): + if isinstance(filter, PermissionTypeFilter): if not filter.filter(event, self.ctx.astrbot_config): permission_not_pass = True else: @@ -111,19 +95,15 @@ class WakingCheckStage(Stage): passed = False break except Exception as e: - # event.set_result(MessageEventResult().message(f"插件 {handler.handler_full_name} 报错:{e}")) - # yield await event.send( MessageEventResult().message( - f"插件 {handler.handler_full_name} 报错:{e}" + f"插件 {star_map[handler.handler_module_path].name}: {e}" ) ) event.stop_event() passed = False break - if passed: - if permission_not_pass: if self.no_permission_reply: await event.send(MessageChain().message(f"ID {event.get_sender_id()} 权限不足。通过 /sid 获取 ID 并请管理员添加。")) @@ -138,6 +118,7 @@ class WakingCheckStage(Stage): handlers_parsed_params[handler.handler_full_name] = event.get_extra( "parsed_params" ) + event.clear_extra() event.set_extra("activated_handlers", activated_handlers) diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index 1715846c..e700878f 100644 --- a/astrbot/core/star/filter/command.py +++ b/astrbot/core/star/filter/command.py @@ -1,20 +1,20 @@ import re import inspect -from typing import List +from typing import List, Any, Type, Dict from . import HandlerFilter from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.config import AstrBotConfig -from astrbot.core.utils.param_validation_mixin import ParameterValidationMixin from .custom_filter import CustomFilter from ..star_handler import StarHandlerMetadata # 标准指令受到 wake_prefix 的制约。 -class CommandFilter(HandlerFilter, ParameterValidationMixin): +class CommandFilter(HandlerFilter): '''标准指令过滤器''' - def __init__(self, command_name: str, alias: set = None, handler_md: StarHandlerMetadata = None): + def __init__(self, command_name: str, alias: set = None, handler_md: StarHandlerMetadata = None, parent_command_names: List[str] = [""]): self.command_name = command_name self.alias = alias if alias else set() + self.parent_command_names = parent_command_names if handler_md: self.init_handler_md(handler_md) self.custom_filter_list: List[CustomFilter] = [] @@ -26,6 +26,7 @@ class CommandFilter(HandlerFilter, ParameterValidationMixin): result += f"{k}({v.__name__})," else: result += f"{k}({type(v).__name__})={v}," + result = result.rstrip(",") return result def init_handler_md(self, handle_md: StarHandlerMetadata): @@ -54,6 +55,39 @@ class CommandFilter(HandlerFilter, ParameterValidationMixin): if not custom_filter.filter(event, cfg): return False return True + + def validate_and_convert_params(self, params: List[Any], param_type: Dict[str, Type]) -> Dict[str, Any]: + '''将参数列表 params 根据 param_type 转换为参数字典。 + ''' + result = {} + for i, (param_name, param_type_or_default_val) in enumerate(param_type.items()): + if i >= len(params): + if isinstance(param_type_or_default_val, Type) or param_type_or_default_val is inspect.Parameter.empty: + # 是类型 + raise ValueError(f"必要参数缺失。该指令完整参数: {self.print_types()}") + else: + # 是默认值 + result[param_name] = param_type_or_default_val + else: + # 尝试强制转换 + try: + if param_type_or_default_val is None: + if params[i].isdigit(): + result[param_name] = int(params[i]) + else: + result[param_name] = params[i] + elif isinstance(param_type_or_default_val, str): + # 如果 param_type_or_default_val 是字符串,直接赋值 + result[param_name] = params[i] + elif isinstance(param_type_or_default_val, int): + result[param_name] = int(params[i]) + elif isinstance(param_type_or_default_val, float): + result[param_name] = float(params[i]) + else: + result[param_name] = param_type_or_default_val(params[i]) + except ValueError: + raise ValueError(f"参数 {param_name} 类型错误。完整参数: {self.print_types()}") + return result def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: if not event.is_at_or_wake_command: @@ -61,27 +95,28 @@ class CommandFilter(HandlerFilter, ParameterValidationMixin): if not self.custom_filter_ok(event, cfg): return False - - if event.get_extra("parsing_command"): - message_str = event.get_extra("parsing_command").strip() - else: - message_str = event.get_message_str().strip() - - # 分割为列表(每个参数之间可能会有多个空格) - ls = re.split(r"\s+", message_str) - if self.command_name != ls[0] and ls[0] not in self.alias: + + # 检查是否以指令开头 + message_str = re.sub(r"\s+", " ", event.get_message_str().strip()) + candidates = [self.command_name] + list(self.alias) + ok = False + for candidate in candidates: + for parent_command_name in self.parent_command_names: + _full = f"{parent_command_name} {candidate}" + if message_str.startswith(f"{_full} ") or message_str == _full.strip(): + message_str = message_str[len(_full):].strip() + ok = True + break + if not ok: return False - # if len(self.handler_params) == 0 and len(ls) > 1: - # # 一定程度避免 LLM 聊天时误判为指令 - # return False - # params_str = message_str[len(self.command_name):].strip() - ls = ls[1:] + + # 分割为列表 + ls = message_str.split(" ") # 去除空字符串 ls = [param for param in ls if param] params = {} try: params = self.validate_and_convert_params(ls, self.handler_params) - except ValueError as e: raise e diff --git a/astrbot/core/star/filter/command_group.py b/astrbot/core/star/filter/command_group.py index 228615d8..0c596fea 100644 --- a/astrbot/core/star/filter/command_group.py +++ b/astrbot/core/star/filter/command_group.py @@ -11,11 +11,12 @@ from ..star_handler import StarHandlerMetadata # 指令组受到 wake_prefix 的制约。 class CommandGroupFilter(HandlerFilter): - def __init__(self, group_name: str, alias: set = None): + def __init__(self, group_name: str, alias: set = None, parent_group: CommandGroupFilter = None): self.group_name = group_name self.alias = alias if alias else set() self.sub_command_filters: List[Union[CommandFilter, CommandGroupFilter]] = [] self.custom_filter_list: List[CustomFilter] = [] + self.parent_group = parent_group def add_sub_command_filter(self, sub_command_filter: Union[CommandFilter, CommandGroupFilter]): self.sub_command_filters.append(sub_command_filter) @@ -23,6 +24,24 @@ class CommandGroupFilter(HandlerFilter): def add_custom_filter(self, custom_filter: CustomFilter): self.custom_filter_list.append(custom_filter) + def get_complete_command_names(self) -> List[str]: + '''遍历父节点获取完整的指令名。 + + 新版本 v3.4.29 采用预编译指令,不再从指令组递归遍历子指令,因此这个方法是返回包括别名在内的整个指令名列表。''' + parent_cmd_names = self.parent_group.get_complete_command_names() if self.parent_group else [] + + if not parent_cmd_names: + # 根节点 + return [self.group_name] + list(self.alias) + + result = [] + candidates = [self.group_name] + list(self.alias) + for parent_cmd_name in parent_cmd_names: + for candidate in candidates: + result.append(parent_cmd_name + " " + candidate) + return result + + # 以树的形式打印出来 def print_cmd_tree(self, sub_command_filters: List[Union[CommandFilter, CommandGroupFilter]], @@ -43,6 +62,10 @@ class CommandGroupFilter(HandlerFilter): result += f" ({cmd_th})" else: result += " (无参数指令)" + + if sub_filter.handler_md and sub_filter.handler_md.desc: + result += f": {sub_filter.handler_md.desc}" + result += "\n" elif isinstance(sub_filter, CommandGroupFilter): custom_filter_pass = True @@ -61,46 +84,19 @@ class CommandGroupFilter(HandlerFilter): return False return True - def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> Tuple[bool, StarHandlerMetadata]: + def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: if not event.is_at_or_wake_command: - return False, None - - if event.get_extra("parsing_command"): - message_str = event.get_extra("parsing_command").strip() - else: - message_str = event.get_message_str().strip() - - ls = re.split(r"\s+", message_str) - - if ls[0] != self.group_name and ls[0] not in self.alias: - return False, None - # 改写 message_str - ls = ls[1:] - # event.message_str = " ".join(ls) - # event.message_str = event.message_str.strip() - parsing_command = " ".join(ls) - parsing_command = parsing_command.strip() - event.set_extra("parsing_command", parsing_command) + return False # 判断当前指令组的自定义过滤器 if not self.custom_filter_ok(event, cfg): - return False, None + return False - if parsing_command == "": - # 当前还是指令组 + complete_command_names = self.get_complete_command_names() + if event.message_str.strip() in complete_command_names: tree = self.group_name + "\n" + self.print_cmd_tree(self.sub_command_filters, event=event, cfg=cfg) raise ValueError(f"指令组 {self.group_name} 未填写完全。这个指令组下有如下指令:\n"+tree) - - child_command_handler_md = None - for sub_filter in self.sub_command_filters: - if isinstance(sub_filter, CommandFilter): - if sub_filter.filter(event, cfg): - child_command_handler_md = sub_filter.get_handler_md() - return True, child_command_handler_md - elif isinstance(sub_filter, CommandGroupFilter): - ok, handler = sub_filter.filter(event, cfg) - if ok: - child_command_handler_md = handler - return True, child_command_handler_md - tree = self.group_name + "\n" + self.print_cmd_tree(self.sub_command_filters, event=event, cfg=cfg) - raise ValueError(f"指令组 {self.group_name} 下没有找到对应的指令。这个指令组下有如下指令:\n"+tree) + + # complete_command_names = [name + " " for name in complete_command_names] + # return event.message_str.startswith(tuple(complete_command_names)) + return False \ No newline at end of file diff --git a/astrbot/core/star/register/star_handler.py b/astrbot/core/star/register/star_handler.py index e6bc4590..ad378ce8 100644 --- a/astrbot/core/star/register/star_handler.py +++ b/astrbot/core/star/register/star_handler.py @@ -54,14 +54,13 @@ def get_handler_or_create( def register_command(command_name: str = None, sub_command: str = None, alias: set = None, **kwargs): '''注册一个 Command. ''' - - # print("command: ", command_name, args, kwargs) - new_command = None add_to_event_filters = False if isinstance(command_name, RegisteringCommandable): # 子指令 - new_command = CommandFilter(sub_command, alias, None) + parent_command_names = command_name.parent_group.get_complete_command_names() + logger.debug(f"parent_command_names: {parent_command_names}") + new_command = CommandFilter(sub_command, alias, None, parent_command_names=parent_command_names) command_name.parent_group.add_sub_command_filter(new_command) else: # 裸指令 @@ -73,10 +72,7 @@ def register_command(command_name: str = None, sub_command: str = None, alias: s kwargs['sub_command'] = True # 打一个标记,表示这是一个子指令,再 wakingstage 阶段这个 handler 将会直接被跳过(其父指令会接管) handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent, **kwargs) new_command.init_handler_md(handler_md) - if add_to_event_filters: - # 裸指令 - handler_md.event_filters.append(new_command) - + handler_md.event_filters.append(new_command) return awaitable return decorator @@ -142,25 +138,19 @@ def register_command_group( ): '''注册一个 CommandGroup ''' - - # print("commandgroup: ", command_group_name,args, kwargs) - new_group = None - add_to_event_filters = False if isinstance(command_group_name, RegisteringCommandable): # 子指令组 - new_group = CommandGroupFilter(sub_command, alias) + new_group = CommandGroupFilter(sub_command, alias, parent_group=command_group_name.parent_group) command_group_name.parent_group.add_sub_command_filter(new_group) else: # 根指令组 new_group = CommandGroupFilter(command_group_name, alias) - add_to_event_filters = True def decorator(obj): - if add_to_event_filters: - # 根指令组 - handler_md = get_handler_or_create(obj, EventType.AdapterMessageEvent, **kwargs) - handler_md.event_filters.append(new_group) + # 根指令组 + handler_md = get_handler_or_create(obj, EventType.AdapterMessageEvent, **kwargs) + handler_md.event_filters.append(new_group) return RegisteringCommandable(new_group) @@ -168,8 +158,8 @@ def register_command_group( class RegisteringCommandable(): '''用于指令组级联注册''' - group = register_command_group - command = register_command + group: CommandGroupFilter = register_command_group + command: CommandFilter = register_command custom_filter = register_custom_filter def __init__(self, parent_group: CommandGroupFilter): diff --git a/astrbot/core/utils/param_validation_mixin.py b/astrbot/core/utils/param_validation_mixin.py deleted file mode 100644 index 5c1e864a..00000000 --- a/astrbot/core/utils/param_validation_mixin.py +++ /dev/null @@ -1,36 +0,0 @@ -import inspect -from typing import List, Dict, Any, Type - -class ParameterValidationMixin: - def validate_and_convert_params(self, params: List[Any], param_type: Dict[str, Type]) -> Dict[str, Any]: - '''将参数列表 params 根据 param_type 转换为参数字典。 - ''' - result = {} - for i, (param_name, param_type_or_default_val) in enumerate(param_type.items()): - if i >= len(params): - if isinstance(param_type_or_default_val, Type) or param_type_or_default_val is inspect.Parameter.empty: - # 是类型 - raise ValueError(f"参数 {param_name} 缺失") - else: - # 是默认值 - result[param_name] = param_type_or_default_val - else: - # 尝试强制转换 - try: - if param_type_or_default_val is None: - if params[i].isdigit(): - result[param_name] = int(params[i]) - else: - result[param_name] = params[i] - elif isinstance(param_type_or_default_val, str): - # 如果 param_type_or_default_val 是字符串,直接赋值 - result[param_name] = params[i] - elif isinstance(param_type_or_default_val, int): - result[param_name] = int(params[i]) - elif isinstance(param_type_or_default_val, float): - result[param_name] = float(params[i]) - else: - result[param_name] = param_type_or_default_val(params[i]) - except ValueError: - raise ValueError(f"参数 {param_name} 类型错误") - return result \ No newline at end of file diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index a4fc3eea..545ed3a9 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -113,11 +113,17 @@ class PluginRoute(Route): for filter in handler.event_filters: # 正常handler就只有 1~2 个 filter,因此这里时间复杂度不会太高 if isinstance(filter, CommandFilter): info["type"] = "指令" - info["cmd"] = filter.command_name + info["cmd"] = f"{filter.parent_command_names[0]} {filter.command_name}" + info["cmd"] = info["cmd"].strip() + if self.core_lifecycle.astrbot_config['wake_prefix'] and len(self.core_lifecycle.astrbot_config['wake_prefix']) > 0: + info["cmd"] = f"{self.core_lifecycle.astrbot_config['wake_prefix'][0]}{info['cmd']}" elif isinstance(filter, CommandGroupFilter): info["type"] = "指令组" - info["cmd"] = filter.group_name + info["cmd"] = filter.get_complete_command_names()[0] + info["cmd"] = info["cmd"].strip() info["sub_command"] = filter.print_cmd_tree(filter.sub_command_filters) + if self.core_lifecycle.astrbot_config['wake_prefix'] and len(self.core_lifecycle.astrbot_config['wake_prefix']) > 0: + info["cmd"] = f"{self.core_lifecycle.astrbot_config['wake_prefix'][0]}{info['cmd']}" elif isinstance(filter, RegexFilter): info["type"] = "正则匹配" info["cmd"] = filter.regex_str diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index b4ff7ddc..61a1d500 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -96,6 +96,7 @@ AstrBot 指令: @tool.command("ls") async def tool_ls(self, event: AstrMessageEvent): + '''查看函数工具列表''' tm = self.context.get_llm_tool_manager() msg = "函数工具:\n" for tool in tm.func_list: @@ -107,6 +108,7 @@ AstrBot 指令: @tool.command("on") async def tool_on(self, event: AstrMessageEvent, tool_name: str): + '''启用一个函数工具''' if self.context.activate_llm_tool(tool_name): event.set_result(MessageEventResult().message(f"激活工具 {tool_name} 成功。")) else: @@ -114,6 +116,7 @@ AstrBot 指令: @tool.command("off") async def tool_off(self, event: AstrMessageEvent, tool_name: str): + '''停用一个函数工具''' if self.context.deactivate_llm_tool(tool_name): event.set_result(MessageEventResult().message(f"停用工具 {tool_name} 成功。")) else: @@ -121,6 +124,7 @@ AstrBot 指令: @tool.command("off_all") async def tool_all_off(self, event: AstrMessageEvent): + '''停用所有函数工具''' tm = self.context.get_llm_tool_manager() for tool in tm.func_list: self.context.deactivate_llm_tool(tool.name) @@ -128,6 +132,7 @@ AstrBot 指令: @filter.command("plugin") async def plugin(self, event: AstrMessageEvent, oper1: str = None, oper2: str = None): + '''插件管理''' if oper1 is None: plugin_list_info = "已加载的插件:\n" for plugin in self.context.get_all_stars(): @@ -189,6 +194,7 @@ AstrBot 指令: @filter.command("t2i") async def t2i(self, event: AstrMessageEvent): + '''开关文本转图片''' config = self.context.get_config() if config['t2i']: config['t2i'] = False @@ -201,6 +207,7 @@ AstrBot 指令: @filter.command("tts") async def tts(self, event: AstrMessageEvent): + '''开关文本转语音''' config = self.context.get_config() if config['provider_tts_settings']['enable']: config['provider_tts_settings']['enable'] = False @@ -213,6 +220,7 @@ AstrBot 指令: @filter.command("sid") async def sid(self, event: AstrMessageEvent): + '''获取会话 ID 和 管理员 ID''' sid = event.unified_msg_origin user_id = str(event.get_sender_id()) ret = f"""SID: {sid} 此 ID 可用于设置会话白名单。/wl 添加白名单, /dwl 删除白名单。 @@ -222,6 +230,7 @@ UID: {user_id} 此 ID 可用于设置管理员。/op 授权管理员, /deo @filter.permission_type(filter.PermissionType.ADMIN) @filter.command("op") async def op(self, event: AstrMessageEvent, admin_id: str): + '''授权管理员。op ''' self.context.get_config()['admins_id'].append(admin_id) self.context.get_config().save_config() event.set_result(MessageEventResult().message("授权成功。")) @@ -229,6 +238,7 @@ UID: {user_id} 此 ID 可用于设置管理员。/op 授权管理员, /deo @filter.permission_type(filter.PermissionType.ADMIN) @filter.command("deop") async def deop(self, event: AstrMessageEvent, admin_id: str): + '''取消授权管理员。deop ''' try: self.context.get_config()['admins_id'].remove(admin_id) self.context.get_config().save_config()