From 9cb1657d2172f9e9944cd465e5835f77fdfc8d1f Mon Sep 17 00:00:00 2001 From: Mikhail Putilovskij Date: Sun, 19 Apr 2026 17:25:25 +0300 Subject: [PATCH] Add lazy platform chat IDs for Matrix rooms --- adapter/matrix/bot.py | 12 ++++++ tests/adapter/matrix/test_dispatcher.py | 49 ++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/adapter/matrix/bot.py b/adapter/matrix/bot.py index 5b84b60..974882d 100644 --- a/adapter/matrix/bot.py +++ b/adapter/matrix/bot.py @@ -28,6 +28,7 @@ from adapter.matrix.store import ( clear_load_pending, get_load_pending, get_room_meta, + set_platform_chat_id, set_room_meta, set_pending_confirm, ) @@ -138,6 +139,15 @@ class MatrixBot: self.client = client self.runtime = runtime + async def _ensure_platform_chat_id(self, room_id: str, room_meta: dict | None) -> None: + if not room_meta: + return + if room_meta.get("redirect_room_id"): + return + if room_meta.get("platform_chat_id"): + return + await set_platform_chat_id(self.runtime.store, room_id, f"matrix:{room_id}") + async def on_room_message(self, room: MatrixRoom, event: RoomMessageText) -> None: if getattr(event, "sender", None) == self.client.user_id: return @@ -177,6 +187,8 @@ class MatrixBot: user=sender, ) return + else: + await self._ensure_platform_chat_id(room.room_id, room_meta) chat_id = await resolve_chat_id(self.runtime.store, room.room_id, sender) incoming = from_room_event(event, room_id=room.room_id, chat_id=chat_id) diff --git a/tests/adapter/matrix/test_dispatcher.py b/tests/adapter/matrix/test_dispatcher.py index 97308b6..68f27a8 100644 --- a/tests/adapter/matrix/test_dispatcher.py +++ b/tests/adapter/matrix/test_dispatcher.py @@ -9,7 +9,7 @@ from nio.responses import SyncResponse from adapter.matrix.bot import MatrixBot, build_runtime, prepare_live_sync from adapter.matrix.handlers.auth import handle_invite -from adapter.matrix.store import get_room_meta, get_user_meta, set_user_meta +from adapter.matrix.store import get_platform_chat_id, get_room_meta, get_user_meta, set_room_meta, set_user_meta from core.protocol import IncomingCallback, IncomingCommand, OutgoingMessage from sdk.interface import PlatformError from sdk.mock import MockPlatformClient @@ -226,6 +226,50 @@ async def test_bot_degrades_platform_errors_to_user_reply(): ) +async def test_bot_assigns_platform_chat_id_for_existing_managed_room(): + runtime = build_runtime(platform=MockPlatformClient()) + await set_room_meta( + runtime.store, + "!chat1:example.org", + {"chat_id": "C1", "matrix_user_id": "@alice:example.org"}, + ) + client = SimpleNamespace(user_id="@bot:example.org") + bot = MatrixBot(client, runtime) + bot._send_all = AsyncMock() + runtime.dispatcher.dispatch = AsyncMock(return_value=[]) + room = SimpleNamespace(room_id="!chat1:example.org") + event = SimpleNamespace(sender="@alice:example.org", body="hello") + + await bot.on_room_message(room, event) + + assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "matrix:!chat1:example.org" + runtime.dispatcher.dispatch.assert_awaited_once() + + +async def test_bot_leaves_existing_platform_chat_id_unchanged(): + runtime = build_runtime(platform=MockPlatformClient()) + await set_room_meta( + runtime.store, + "!chat1:example.org", + { + "chat_id": "C1", + "matrix_user_id": "@alice:example.org", + "platform_chat_id": "matrix:existing", + }, + ) + client = SimpleNamespace(user_id="@bot:example.org") + bot = MatrixBot(client, runtime) + bot._send_all = AsyncMock() + runtime.dispatcher.dispatch = AsyncMock(return_value=[]) + room = SimpleNamespace(room_id="!chat1:example.org") + event = SimpleNamespace(sender="@alice:example.org", body="hello") + + await bot.on_room_message(room, event) + + assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "matrix:existing" + runtime.dispatcher.dispatch.assert_awaited_once() + + async def test_unregistered_room_bootstraps_space_and_chat_on_first_message(): runtime = build_runtime(platform=MockPlatformClient()) await set_user_meta(runtime.store, "@alice:example.org", {"next_chat_index": 1}) @@ -293,6 +337,9 @@ async def test_unregistered_room_second_message_reuses_existing_bootstrap(): and "Рабочий чат уже создан: C1" in call.args[2]["body"] for call in room_send_calls ) + entry_meta = await get_room_meta(runtime.store, "!entry:example.org") + assert entry_meta is not None + assert "platform_chat_id" not in entry_meta async def test_unregistered_room_creates_new_chat_in_existing_space():