From 35695e043fc825254c17029b745c422c9fd13686 Mon Sep 17 00:00:00 2001 From: Mikhail Putilovskij Date: Fri, 3 Apr 2026 12:26:32 +0300 Subject: [PATCH] fix(01-05): align matrix confirmation scope with user and room - carry Matrix room_id through command callbacks - persist pending confirmations by user_id and room_id --- adapter/matrix/bot.py | 24 ++++++++++++++---------- adapter/matrix/converter.py | 10 +++++++--- adapter/matrix/handlers/confirm.py | 24 ++++++++++++++++++++---- adapter/matrix/store.py | 29 +++++++++++++++++++++-------- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/adapter/matrix/bot.py b/adapter/matrix/bot.py index a7e6ac6..ef0a2a7 100644 --- a/adapter/matrix/bot.py +++ b/adapter/matrix/bot.py @@ -20,7 +20,7 @@ from adapter.matrix.converter import from_room_event from adapter.matrix.handlers import register_matrix_handlers from adapter.matrix.handlers.auth import handle_invite from adapter.matrix.room_router import resolve_chat_id -from adapter.matrix.store import set_pending_confirm +from adapter.matrix.store import get_room_meta, set_pending_confirm from core.auth import AuthManager from core.chat import ChatManager from core.handler import EventDispatcher @@ -150,15 +150,19 @@ async def send_outgoing( if event.buttons and store is not None: action_id = event.buttons[0].action payload = event.buttons[0].payload - await set_pending_confirm( - store, - room_id, - { - "action_id": action_id, - "description": event.text, - "payload": payload, - }, - ) + room_meta = await get_room_meta(store, room_id) + matrix_user_id = room_meta.get("matrix_user_id") if room_meta else None + if matrix_user_id: + await set_pending_confirm( + store, + matrix_user_id, + room_id, + { + "action_id": action_id, + "description": event.text, + "payload": payload, + }, + ) return diff --git a/adapter/matrix/converter.py b/adapter/matrix/converter.py index 96a9f4e..1005512 100644 --- a/adapter/matrix/converter.py +++ b/adapter/matrix/converter.py @@ -56,7 +56,7 @@ def extract_attachments(event: Any) -> list[Attachment]: return [] -def from_command(body: str, sender: str, chat_id: str) -> IncomingEvent: +def from_command(body: str, sender: str, chat_id: str, room_id: str | None = None) -> IncomingEvent: raw = body.lstrip("!").strip() parts = raw.split() command = parts[0].lower() if parts else "" @@ -69,7 +69,11 @@ def from_command(body: str, sender: str, chat_id: str) -> IncomingEvent: platform=PLATFORM, chat_id=chat_id, action=action, - payload={"source": "command", "command": command}, + payload={ + "source": "command", + "command": command, + **({"room_id": room_id} if room_id is not None else {}), + }, ) aliases = { @@ -132,7 +136,7 @@ def from_room_event(event: Any, room_id: str, chat_id: str) -> IncomingEvent | N body = (getattr(event, "body", None) or "").strip() sender = getattr(event, "sender", "") if body.startswith("!"): - return from_command(body, sender=sender, chat_id=chat_id) + return from_command(body, sender=sender, chat_id=chat_id, room_id=room_id) return IncomingMessage( user_id=sender, platform=PLATFORM, diff --git a/adapter/matrix/handlers/confirm.py b/adapter/matrix/handlers/confirm.py index 4106cbc..e988dac 100644 --- a/adapter/matrix/handlers/confirm.py +++ b/adapter/matrix/handlers/confirm.py @@ -11,12 +11,20 @@ def make_handle_confirm(store=None): if store is None: return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")] - pending = await get_pending_confirm(store, event.chat_id) + room_id = event.payload.get("room_id") + pending = None + if room_id: + pending = await get_pending_confirm(store, event.user_id, room_id) + if not pending: + pending = await get_pending_confirm(store, event.chat_id) if not pending: return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")] description = pending.get("description", "действие") - await clear_pending_confirm(store, event.chat_id) + if room_id: + await clear_pending_confirm(store, event.user_id, room_id) + else: + await clear_pending_confirm(store, event.chat_id) return [OutgoingMessage(chat_id=event.chat_id, text=f"Подтверждено: {description}")] @@ -30,11 +38,19 @@ def make_handle_cancel(store=None): if store is None: return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")] - pending = await get_pending_confirm(store, event.chat_id) + room_id = event.payload.get("room_id") + pending = None + if room_id: + pending = await get_pending_confirm(store, event.user_id, room_id) + if not pending: + pending = await get_pending_confirm(store, event.chat_id) if not pending: return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")] - await clear_pending_confirm(store, event.chat_id) + if room_id: + await clear_pending_confirm(store, event.user_id, room_id) + else: + await clear_pending_confirm(store, event.chat_id) return [OutgoingMessage(chat_id=event.chat_id, text="Действие отменено.")] return handle_cancel diff --git a/adapter/matrix/store.py b/adapter/matrix/store.py index 59801d6..30ee076 100644 --- a/adapter/matrix/store.py +++ b/adapter/matrix/store.py @@ -51,13 +51,26 @@ async def next_chat_id(store: StateStore, matrix_user_id: str) -> str: return f"C{index}" -async def get_pending_confirm(store: StateStore, room_id: str) -> dict | None: - return await store.get(f"{PENDING_CONFIRM_PREFIX}{room_id}") +def _pending_confirm_key(user_id: str, room_id: str | None = None) -> str: + if room_id is None: + return f"{PENDING_CONFIRM_PREFIX}{user_id}" + return f"{PENDING_CONFIRM_PREFIX}{user_id}:{room_id}" + +async def get_pending_confirm( + store: StateStore, user_id: str, room_id: str | None = None +) -> dict | None: + return await store.get(_pending_confirm_key(user_id, room_id)) + +async def set_pending_confirm( + store: StateStore, user_id: str, room_id: str | dict, meta: dict | None = None +) -> None: + if meta is None: + await store.set(_pending_confirm_key(user_id), room_id) + return + await store.set(_pending_confirm_key(user_id, str(room_id)), meta) -async def set_pending_confirm(store: StateStore, room_id: str, meta: dict) -> None: - await store.set(f"{PENDING_CONFIRM_PREFIX}{room_id}", meta) - - -async def clear_pending_confirm(store: StateStore, room_id: str) -> None: - await store.delete(f"{PENDING_CONFIRM_PREFIX}{room_id}") +async def clear_pending_confirm( + store: StateStore, user_id: str, room_id: str | None = None +) -> None: + await store.delete(_pending_confirm_key(user_id, room_id))