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
This commit is contained in:
Mikhail Putilovskij 2026-04-03 12:26:32 +03:00
parent 97a3dc35ea
commit 35695e043f
4 changed files with 62 additions and 25 deletions

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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))