diff --git a/plugins/platforms/simplex/adapter.py b/plugins/platforms/simplex/adapter.py index 6745854d3baa0..4046ce4910ad9 100644 --- a/plugins/platforms/simplex/adapter.py +++ b/plugins/platforms/simplex/adapter.py @@ -830,15 +830,17 @@ class SimplexAdapter(BasePlatformAdapter): if content: corr_id = self._make_corr_id() + # Structured form: addresses by ID, and json.dumps escapes + # newlines + special chars correctly. The bare @id text + # syntax is unreliable for DMs — the daemon silently drops + # messages when it cannot resolve the display name. + composed = json.dumps( + [{"msgContent": {"type": "text", "text": content}}] + ) if chat_id.startswith("group:"): - # Structured form: addresses by numeric ID, and json.dumps - # escapes newlines + special chars correctly. - composed = json.dumps( - [{"msgContent": {"type": "text", "text": content}}] - ) cmd_str = f"/_send #{chat_id[6:]} json {composed}" else: - cmd_str = f"@{chat_id} {content}" + cmd_str = f"/_send @{chat_id} json {composed}" await self._send_ws({"corrId": corr_id, "cmd": cmd_str}) diff --git a/tests/gateway/test_simplex_plugin.py b/tests/gateway/test_simplex_plugin.py index 5a9cbc34320b6..87808f81f1bd1 100644 --- a/tests/gateway/test_simplex_plugin.py +++ b/tests/gateway/test_simplex_plugin.py @@ -133,6 +133,35 @@ def test_corr_id_pending_set_self_trims(): # 7. Outbound send (mocked WS) # --------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_send_dm(): + """DMs use the structured ``/_send @ json [...]`` form. + + The bare ``@ text`` chat-command form is unreliable — the + daemon silently drops messages when it cannot resolve the display + name. The structured ``/_send`` form addresses by ID and + survives newlines/quoting through ``json.dumps``, matching what + ``send_image`` and ``send_document`` already do. + """ + from gateway.config import PlatformConfig + cfg = PlatformConfig(enabled=True, extra={"ws_url": "ws://localhost:5225"}) + adapter = SimplexAdapter(cfg) + + mock_ws = AsyncMock() + adapter._ws = mock_ws + + result = await adapter.send("contact-42", "Hello, SimpleX!") + mock_ws.send.assert_called_once() + payload = json.loads(mock_ws.send.call_args[0][0]) + assert payload["cmd"].startswith("/_send @contact-42 json ") + msg_content = json.loads(payload["cmd"].split(" json ", 1)[1])[0][ + "msgContent" + ] + assert msg_content == {"type": "text", "text": "Hello, SimpleX!"} + assert payload["corrId"].startswith(_CORR_PREFIX) + assert result.success is True + + @pytest.mark.asyncio async def test_send_group():