mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-23 16:36:23 +00:00
fix(slack): handle leading-space text commands
This commit is contained in:
parent
cbc1054e23
commit
3e05b39e4b
3 changed files with 29 additions and 8 deletions
|
|
@ -1826,14 +1826,15 @@ class MessageEvent:
|
|||
|
||||
def is_command(self) -> bool:
|
||||
"""Check if this is a command message (e.g., /new, /reset)."""
|
||||
return self.text.startswith("/")
|
||||
return (self.text or "").lstrip().startswith("/")
|
||||
|
||||
def get_command(self) -> Optional[str]:
|
||||
"""Extract command name if this is a command message."""
|
||||
if not self.is_command():
|
||||
return None
|
||||
# Split on space and get first word, strip the /
|
||||
parts = self.text.split(maxsplit=1)
|
||||
command_text = (self.text or "").lstrip()
|
||||
parts = command_text.split(maxsplit=1)
|
||||
raw = parts[0][1:].lower() if parts else None
|
||||
if raw and "@" in raw:
|
||||
raw = raw.split("@", 1)[0]
|
||||
|
|
@ -1846,7 +1847,8 @@ class MessageEvent:
|
|||
"""Get the arguments after a command."""
|
||||
if not self.is_command():
|
||||
return self.text
|
||||
parts = self.text.split(maxsplit=1)
|
||||
command_text = (self.text or "").lstrip()
|
||||
parts = command_text.split(maxsplit=1)
|
||||
args = parts[1] if len(parts) > 1 else ""
|
||||
# iOS auto-corrects -- to — (em dash) and - to – (en dash)
|
||||
args = args.replace("\u2014\u2014", "--").replace("\u2014", "--").replace("\u2013", "-")
|
||||
|
|
|
|||
|
|
@ -3788,11 +3788,12 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
# gateway dispatcher) handles it like a normal slash command. Only
|
||||
# rewrite when the first token resolves to a known gateway command
|
||||
# so casual messages like "!nice work" pass through unchanged.
|
||||
if original_text.startswith("!"):
|
||||
command_probe_text = original_text.lstrip()
|
||||
if command_probe_text.startswith("!"):
|
||||
try:
|
||||
from hermes_cli.commands import is_gateway_known_command
|
||||
|
||||
first_token = original_text[1:].split(maxsplit=1)[0]
|
||||
first_token = command_probe_text[1:].split(maxsplit=1)[0]
|
||||
# Strip "@suffix" the same way get_command() does, so
|
||||
# forms like ``!stop@hermes`` still resolve.
|
||||
cmd_name = first_token.split("@", 1)[0].lower()
|
||||
|
|
@ -3801,10 +3802,12 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
and "/" not in cmd_name
|
||||
and is_gateway_known_command(cmd_name)
|
||||
):
|
||||
original_text = "/" + original_text[1:]
|
||||
original_text = "/" + command_probe_text[1:]
|
||||
command_probe_text = original_text
|
||||
except Exception: # pragma: no cover - defensive
|
||||
pass
|
||||
|
||||
is_command_text = command_probe_text.startswith("/")
|
||||
text = original_text
|
||||
|
||||
# Extract quoted/forwarded content from Slack blocks.
|
||||
|
|
@ -4163,9 +4166,15 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
|
||||
# Determine message type
|
||||
msg_type = MessageType.TEXT
|
||||
if (original_text or "").startswith("/"):
|
||||
if is_command_text:
|
||||
msg_type = MessageType.COMMAND
|
||||
|
||||
# Commands typed as Slack text messages often intentionally carry a
|
||||
# leading space (`` /stop``) so Slack itself does not intercept the
|
||||
# slash. Once classified as a command, pass only the command text into
|
||||
# the gateway dispatcher; do not prepend fetched thread context or
|
||||
# block/attachment rendering before the leading slash.
|
||||
|
||||
# Handle file attachments
|
||||
media_urls = []
|
||||
media_types = []
|
||||
|
|
@ -4514,7 +4523,7 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
)
|
||||
|
||||
msg_event = MessageEvent(
|
||||
text=text,
|
||||
text=(command_probe_text if is_command_text else text),
|
||||
message_type=msg_type,
|
||||
source=source,
|
||||
raw_message=event,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,16 @@ class TestSlashCommands:
|
|||
response_lower = response_text.lower()
|
||||
assert "no" in response_lower or "stop" in response_lower or "not running" in response_lower
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_leading_space_stop_is_still_a_command(self, adapter, platform):
|
||||
"""Slack users type `` /stop`` to avoid native Slack slash interception."""
|
||||
send = await send_and_capture(adapter, " /stop", platform)
|
||||
|
||||
send.assert_called_once()
|
||||
response_text = send.call_args[1].get("content") or send.call_args[0][1]
|
||||
response_lower = response_text.lower()
|
||||
assert "no" in response_lower or "stop" in response_lower or "not running" in response_lower
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_commands_shows_listing(self, adapter, platform):
|
||||
send = await send_and_capture(adapter, "/commands", platform)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue