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 "