From 8b3171ca2b59001499a7744d15caf9fd740f86ca Mon Sep 17 00:00:00 2001 From: Alexander Minges Date: Tue, 10 Feb 2026 17:21:52 +0100 Subject: [PATCH] fix(matrix): include empty m.mentions in outgoing messages --- nanobot/channels/matrix.py | 11 +++++++++-- tests/test_matrix_channel.py | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/nanobot/channels/matrix.py b/nanobot/channels/matrix.py index 5893bb2..504e11f 100644 --- a/nanobot/channels/matrix.py +++ b/nanobot/channels/matrix.py @@ -173,9 +173,16 @@ def _render_markdown_html(text: str) -> str | None: return formatted -def _build_matrix_text_content(text: str) -> dict[str, str]: +def _build_matrix_text_content(text: str) -> dict[str, Any]: """Build Matrix m.text payload with plaintext fallback and optional HTML.""" - content: dict[str, str] = {"msgtype": "m.text", "body": text} + content: dict[str, Any] = { + "msgtype": "m.text", + "body": text, + # Matrix spec recommends always including m.mentions for message + # semantics/interoperability, even when no mentions are present. + # https://spec.matrix.org/v1.17/client-server-api/#mmentions + "m.mentions": {}, + } formatted_html = _render_markdown_html(text) if not formatted_html: return content diff --git a/tests/test_matrix_channel.py b/tests/test_matrix_channel.py index 6a33a5e..f55fd0f 100644 --- a/tests/test_matrix_channel.py +++ b/tests/test_matrix_channel.py @@ -627,7 +627,11 @@ async def test_send_clears_typing_after_send() -> None: ) assert len(client.room_send_calls) == 1 - assert client.room_send_calls[0]["content"] == {"msgtype": "m.text", "body": "Hi"} + assert client.room_send_calls[0]["content"] == { + "msgtype": "m.text", + "body": "Hi", + "m.mentions": {}, + } assert client.typing_calls[-1] == ("!room:matrix.org", False, TYPING_NOTICE_TIMEOUT_MS) @@ -678,6 +682,7 @@ async def test_send_adds_formatted_body_for_markdown() -> None: content = client.room_send_calls[0]["content"] assert content["msgtype"] == "m.text" assert content["body"] == markdown_text + assert content["m.mentions"] == {} assert content["format"] == MATRIX_HTML_FORMAT assert "

Headline

" in str(content["formatted_body"]) assert "" in str(content["formatted_body"]) @@ -698,6 +703,7 @@ async def test_send_adds_formatted_body_for_inline_url_superscript_subscript() - content = client.room_send_calls[0]["content"] assert content["msgtype"] == "m.text" assert content["body"] == markdown_text + assert content["m.mentions"] == {} assert content["format"] == MATRIX_HTML_FORMAT assert '' in str( content["formatted_body"] @@ -764,7 +770,7 @@ async def test_send_falls_back_to_plaintext_when_markdown_render_fails(monkeypat ) content = client.room_send_calls[0]["content"] - assert content == {"msgtype": "m.text", "body": markdown_text} + assert content == {"msgtype": "m.text", "body": markdown_text, "m.mentions": {}} @pytest.mark.asyncio @@ -778,4 +784,8 @@ async def test_send_keeps_plaintext_only_for_plain_text() -> None: OutboundMessage(channel="matrix", chat_id="!room:matrix.org", content=text) ) - assert client.room_send_calls[0]["content"] == {"msgtype": "m.text", "body": text} + assert client.room_send_calls[0]["content"] == { + "msgtype": "m.text", + "body": text, + "m.mentions": {}, + }