diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index 31949b674c..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,8 +209,8 @@ def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: if not ok: return False - # 分割为列表 - ls = message_str.split(" ") + # 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