From 286ec6afb3224482b141ab664a0794f6442757f7 Mon Sep 17 00:00:00 2001 From: Ben Phillips Date: Thu, 16 Jul 2026 09:27:50 +0100 Subject: [PATCH] fix(slack): count pipe-form bot mentions as mentioned in ignore gate The ignore_other_user_mentions gate relied solely on is_mentioned, which only recognises exact <@UID> markup, so a message mentioning the bot in pipe form (<@UID|name>) alongside a leading other-user mention was wrongly suppressed. The gate now also scans for the bot's own mention in either markup before ignoring a message. Claude-Session: https://claude.ai/code/session_01TKsNdptNdo9CqT2u7JMdkH --- plugins/platforms/slack/adapter.py | 16 +++++++ .../test_slack_ignore_other_user_mentions.py | 47 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/plugins/platforms/slack/adapter.py b/plugins/platforms/slack/adapter.py index 5946ff180f3..318ba85f1e3 100644 --- a/plugins/platforms/slack/adapter.py +++ b/plugins/platforms/slack/adapter.py @@ -4453,6 +4453,7 @@ class SlackAdapter(BasePlatformAdapter): if ( self._slack_ignore_other_user_mentions() and not is_mentioned + and not self._slack_message_mentions_self(routing_text, self_uids) and self._slack_message_addressed_to_other_user(routing_text, self_uids) ): logger.debug( @@ -6766,6 +6767,21 @@ class SlackAdapter(BasePlatformAdapter): return False return match.group(1) not in self_uids + def _slack_message_mentions_self(self, text: str, self_uids: set) -> bool: + """Return True when ``text`` @-mentions this bot anywhere in the message. + + Matches both mention markups — ``<@U123>`` and the pipe form + ``<@U123|name>`` — so the ignore_other_user_mentions gate treats a + pipe-form bot mention as "also mentioned" even though the exact-markup + ``is_mentioned`` check only recognises ``<@U123>``. + """ + if not text: + return False + return any( + re.search(rf"<@{re.escape(uid)}(?:\|[^>]*)?>", text) + for uid in self_uids + ) + def _slack_free_response_channels(self) -> set: """Return channel IDs where no @mention is required.""" raw = self.config.extra.get("free_response_channels") diff --git a/tests/gateway/test_slack_ignore_other_user_mentions.py b/tests/gateway/test_slack_ignore_other_user_mentions.py index d103c0bc395..dcfa4fb877b 100644 --- a/tests/gateway/test_slack_ignore_other_user_mentions.py +++ b/tests/gateway/test_slack_ignore_other_user_mentions.py @@ -159,6 +159,35 @@ def test_addressed_channel_and_broadcast_tokens_are_not_users(): assert _addressed("<#C0000000000|general> see here") is False +# --------------------------------------------------------------------------- +# _slack_message_mentions_self() +# --------------------------------------------------------------------------- + +def _mentions_self(text): + return _make_adapter()._slack_message_mentions_self(text, SELF_UIDS) + + +def test_mentions_self_plain_form(): + assert _mentions_self(f"hello <@{BOT_USER_ID}>") is True + + +def test_mentions_self_pipe_form(): + assert _mentions_self(f"hello <@{BOT_USER_ID}|hermes>") is True + + +def test_mentions_self_other_user_only(): + assert _mentions_self(f"hello <@{OTHER_USER_ID}|rasha>") is False + + +def test_mentions_self_id_prefix_is_not_a_match(): + # <@U_BOT_123X> is a different user whose ID merely starts with ours. + assert _mentions_self(f"hello <@{BOT_USER_ID}X>") is False + + +def test_mentions_self_empty(): + assert _mentions_self("") is False + + # --------------------------------------------------------------------------- # Integration: real _handle_slack_message # --------------------------------------------------------------------------- @@ -245,6 +274,24 @@ async def test_free_response_replies_when_bot_also_mentioned(adapter): adapter.handle_message.assert_awaited_once() +@pytest.mark.asyncio +async def test_free_response_replies_when_bot_mentioned_in_pipe_form(adapter): + """A pipe-form bot mention (``<@U123|name>``) counts as "also mentioned" + even though the exact-markup ``is_mentioned`` check misses it.""" + adapter.config.extra["free_response_channels"] = CHANNEL_ID + adapter.config.extra["ignore_other_user_mentions"] = True + + await _run( + adapter, + _event( + f"<@{OTHER_USER_ID}|rasha> and <@{BOT_USER_ID}|hermes> please compare", + ts="1700000000.000004", + ), + ) + + adapter.handle_message.assert_awaited_once() + + @pytest.mark.asyncio async def test_mentioned_thread_ignores_followup_addressed_to_other_user(adapter): """Ben's case: once the bot has been mentioned in a thread it auto-follows,