From 4d08d9be9790d93ef44504d3ddb397321658db85 Mon Sep 17 00:00:00 2001 From: Mer3y <1520637986@qq.com> Date: Sat, 18 Jul 2026 02:05:30 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=BF=9D=E6=8A=A4=20@=E6=98=B5?= =?UTF-8?q?=E7=A7=B0(ID)=20=E6=A0=BC=E5=BC=8F=E5=9C=A8=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E8=A7=A3=E6=9E=90=E6=97=B6=E4=B8=8D=E8=A2=AB?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC=E6=8B=86=E5=9D=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当 @mention 的昵称包含空格时(如 @Heaven Whisper(488267082)), 原有的 message_str.split(' ') 会将其拆为多个参数, 导致后续参数类型转换失败(如 value 参数收到 'Whisper(488267082)' 无法转 int)。 使用正则 re.findall(r'@[^@]+?\(\d+\)|[^\s]+', ...) 替代简单 split, 将 @昵称(ID) 作为整体 token 保留,不受昵称中空格影响。 影响范围:仅 CommandFilter.filter() 中参数分割逻辑, 不影响无 @mention 的普通命令解析。 --- astrbot/core/star/filter/command.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index 31949b674c..96ea16df52 100755 --- a/astrbot/core/star/filter/command.py +++ b/astrbot/core/star/filter/command.py @@ -205,8 +205,10 @@ def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: if not ok: return False - # 分割为列表 - ls = message_str.split(" ") + # 分割为列表,同时保护 @昵称(ID) 格式不被空格拆坏 + # 例如 "@Heaven Whisper(488267082) 64" 应拆为 ["@Heaven Whisper(488267082)", "64"] + # 而非 ["@Heaven", "Whisper(488267082)", "64"] + ls = re.findall(r"@[^@]+?\(\d+\)|[^\s]+", message_str) # 去除空字符串 ls = [param for param in ls if param] params = {} From 83ecbcdb06dd2d7d3d4bbfea7e014b2c2950a7b0 Mon Sep 17 00:00:00 2001 From: Mer3y <1520637986@qq.com> Date: Sat, 18 Jul 2026 03:01:28 +0000 Subject: [PATCH 2/2] fix(command): support non-numeric mention IDs --- astrbot/core/star/filter/command.py | 10 +++-- tests/unit/test_command_filter.py | 59 +++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 tests/unit/test_command_filter.py diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index 96ea16df52..da17335204 100755 --- a/astrbot/core/star/filter/command.py +++ b/astrbot/core/star/filter/command.py @@ -11,6 +11,10 @@ from . import HandlerFilter from .custom_filter import CustomFilter +# Preserve @nickname(ID) as one argument even when the nickname contains spaces. +# IDs are platform-specific and may be strings (for example Slack/Lark IDs). +COMMAND_ARGUMENT_PATTERN = re.compile(r"@[^@]*\([^()\s]+\)(?=\s|$)|[^\s]+") + class GreedyStr(str): """标记指令完成其他参数接收后的所有剩余文本。""" @@ -205,10 +209,8 @@ def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: if not ok: return False - # 分割为列表,同时保护 @昵称(ID) 格式不被空格拆坏 - # 例如 "@Heaven Whisper(488267082) 64" 应拆为 ["@Heaven Whisper(488267082)", "64"] - # 而非 ["@Heaven", "Whisper(488267082)", "64"] - ls = re.findall(r"@[^@]+?\(\d+\)|[^\s]+", message_str) + # Split into arguments while preserving @nickname(ID) mentions as one token. + ls = COMMAND_ARGUMENT_PATTERN.findall(message_str) # 去除空字符串 ls = [param for param in ls if param] params = {} diff --git a/tests/unit/test_command_filter.py b/tests/unit/test_command_filter.py new file mode 100644 index 0000000000..ca21d212a7 --- /dev/null +++ b/tests/unit/test_command_filter.py @@ -0,0 +1,59 @@ +import pytest + +from astrbot.core.star.filter.command import CommandFilter + + +class FakeCommandEvent: + def __init__(self, message_str: str): + self.message_str = message_str + self.is_at_or_wake_command = True + self.extras = {} + + def get_message_str(self) -> str: + return self.message_str + + def set_extra(self, key: str, value) -> None: + self.extras[key] = value + + +@pytest.mark.parametrize( + ("command_name", "message_str", "handler_params", "expected_params"), + [ + ( + "修改好感度", + "修改好感度 @Heaven Whisper(488267082) 64", + {"target": str, "value": int}, + {"target": "@Heaven Whisper(488267082)", "value": 64}, + ), + ( + "修改好感度", + "修改好感度 @Jane Doe(U01ABC-DEF) 64", + {"target": str, "value": int}, + {"target": "@Jane Doe(U01ABC-DEF)", "value": 64}, + ), + ( + "修改好感度", + "修改好感度 488267082 64", + {"target": str, "value": int}, + {"target": "488267082", "value": 64}, + ), + ( + "修改关系", + "修改关系 @Alice One(U1) @Bob Two(U2)", + {"source": str, "target": str}, + {"source": "@Alice One(U1)", "target": "@Bob Two(U2)"}, + ), + ], +) +def test_command_filter_keeps_at_mentions_with_spaces_as_single_params( + command_name, + message_str, + handler_params, + expected_params, +): + command_filter = CommandFilter(command_name) + command_filter.handler_params = handler_params + event = FakeCommandEvent(message_str) + + assert command_filter.filter(event, cfg=None) + assert event.extras["parsed_params"] == expected_params