Skip to content

Commit 08c42fa

Browse files
tony-chinchill-aipatrick-chinchillgemini-code-assist[bot]
authored
fix(slack): skip @ inside email localparts when wrapping mentions (#91)
* fix(slack): skip @ inside email localparts when wrapping mentions `SlackFormatConverter._convert_mentions_to_slack` and the AST text branch in `_node_to_mrkdwn` rewrote `alice@example.com` to `alice<@example>.com` because `(?<!<)@(\w+)` only skipped already-wrapped mentions, not the localpart/domain boundary inside emails. The same one-character omission existed in `SlackAdapter._resolve_outgoing_mentions`. Tightening the lookbehind to `(?<![\w<])` skips both. Real mentions still wrap: - `Hey @alice` -> `Hey <@alice>` - `@alice at start` -> `<@alice> at start` Emails pass through: - `Contact alice@example.com` -> `Contact alice@example.com` Two regression tests in `tests/test_slack_format.py` cover the str path and the AST path; existing mention tests untouched. * Update src/chat_sdk/adapters/slack/adapter.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: patrick-chinchill <patrick@chinchill.ai> --------- Signed-off-by: patrick-chinchill <patrick@chinchill.ai> Co-authored-by: patrick-chinchill <patrick@chinchill.ai> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent c71442a commit 08c42fa

3 files changed

Lines changed: 20 additions & 3 deletions

File tree

src/chat_sdk/adapters/slack/adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1651,7 +1651,7 @@ async def _resolve_outgoing_mentions(self, text: str, thread_id: str) -> str:
16511651
return text
16521652
state = self._chat.get_state()
16531653

1654-
mention_pattern = re.compile(r"@(\w+)")
1654+
mention_pattern = re.compile(r"(?<![\w<])@(\w+)")
16551655
mentions: dict[str, list[str]] = {}
16561656

16571657
for match in mention_pattern.finditer(text):

src/chat_sdk/adapters/slack/format_converter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def flush_text() -> None:
200200

201201
def _convert_mentions_to_slack(self, text: str) -> str:
202202
"""Convert @mentions to Slack format: @name -> <@name>."""
203-
return re.sub(r"(?<!<)@(\w+)", r"<@\1>", text)
203+
return re.sub(r"(?<![\w<])@(\w+)", r"<@\1>", text)
204204

205205
def _node_to_mrkdwn(self, node: Content) -> str:
206206
"""Convert a single AST node to Slack mrkdwn."""
@@ -215,7 +215,7 @@ def _node_to_mrkdwn(self, node: Content) -> str:
215215

216216
if node_type == "text":
217217
value = node.get("value", "")
218-
return re.sub(r"(?<!<)@(\w+)", r"<@\1>", value)
218+
return re.sub(r"(?<![\w<])@(\w+)", r"<@\1>", value)
219219

220220
if node_type == "strong":
221221
content = "".join(self._node_to_mrkdwn(c) for c in children)

tests/test_slack_format.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,23 @@ def test_from_markdown_no_double_wrap(self):
199199
result = self.converter.from_markdown("Hey <@U12345>")
200200
assert result == "Hey <@U12345>"
201201

202+
def test_does_not_wrap_at_in_email_localpart(self):
203+
"""`@` preceded by a word char is part of an email address, not a mention.
204+
205+
Without the lookbehind tightening, `alice@example.com` was rewritten to
206+
`alice<@example>.com`, surfacing a broken-looking mention in messages
207+
that quote email addresses from upstream APIs.
208+
"""
209+
result = self.converter.render_postable("Contact alice@example.com or bob@example.org")
210+
assert "<@example>" not in result
211+
assert "alice@example.com" in result
212+
assert "bob@example.org" in result
213+
214+
def test_does_not_wrap_at_in_email_localpart_from_markdown_ast(self):
215+
result = self.converter.render_postable({"markdown": "Contact alice@example.com"})
216+
assert "<@example>" not in result
217+
assert "alice@example.com" in result
218+
202219

203220
# ---------------------------------------------------------------------------
204221
# toPlainText

0 commit comments

Comments
 (0)