surfaces/adapter/matrix/handlers/chat.py
Mikhail Putilovskij 84111ca524 feat(01-02): rewrite matrix new chat handler for spaces
- create new chat rooms inside the user's space
- store space-aware room metadata with next_chat_id
- handle room creation failures with user-facing messages
2026-04-02 22:50:26 +03:00

144 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from typing import Any, Awaitable, Callable
import structlog
from nio.responses import RoomCreateError
from adapter.matrix.store import get_user_meta, next_chat_id, set_room_meta
from core.protocol import IncomingCommand, OutgoingMessage
logger = structlog.get_logger(__name__)
async def _fallback_new_chat(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
) -> list:
if not await auth_mgr.is_authenticated(event.user_id):
return [OutgoingMessage(chat_id=event.chat_id, text="Введите !start чтобы начать.")]
name = " ".join(event.args).strip() if event.args else ""
chats = await chat_mgr.list_active(event.user_id)
chat_id = f"C{len(chats) + 1}"
ctx = await chat_mgr.get_or_create(
user_id=event.user_id,
chat_id=chat_id,
platform=event.platform,
surface_ref=event.chat_id,
name=name or None,
)
return [
OutgoingMessage(
chat_id=event.chat_id, text=f"Создан чат: {ctx.display_name} ({ctx.chat_id})"
)
]
def make_handle_new_chat(
client: Any | None,
store: Any | None,
) -> Callable[..., Awaitable[list]]:
async def handle_new_chat(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
) -> list:
if client is None or store is None:
return await _fallback_new_chat(event, auth_mgr, platform, chat_mgr, settings_mgr)
if not await auth_mgr.is_authenticated(event.user_id):
return [
OutgoingMessage(
chat_id=event.chat_id,
text="Сначала примите приглашение бота.",
)
]
user_meta = await get_user_meta(store, event.user_id)
space_id = (user_meta or {}).get("space_id")
if not space_id:
return [
OutgoingMessage(
chat_id=event.chat_id,
text="Ошибка: Space не найден. Примите приглашение бота заново.",
)
]
name = " ".join(event.args).strip() if event.args else ""
chat_id = await next_chat_id(store, event.user_id)
room_name = name or f"Чат {chat_id}"
response = await client.room_create(
name=room_name,
visibility="private",
is_direct=False,
)
if isinstance(response, RoomCreateError):
logger.error(
"room_create failed",
user_id=event.user_id,
status_code=getattr(response, "status_code", None),
)
return [OutgoingMessage(chat_id=event.chat_id, text="Не удалось создать комнату.")]
room_id = getattr(response, "room_id", None)
if not room_id:
return [OutgoingMessage(chat_id=event.chat_id, text="Не удалось создать комнату.")]
homeserver = event.user_id.split(":")[-1]
await client.room_put_state(
room_id=space_id,
event_type="m.space.child",
content={"via": [homeserver]},
state_key=room_id,
)
await client.room_invite(room_id, event.user_id)
await set_room_meta(
store,
room_id,
{
"room_type": "chat",
"chat_id": chat_id,
"display_name": room_name,
"matrix_user_id": event.user_id,
"space_id": space_id,
},
)
ctx = await chat_mgr.get_or_create(
user_id=event.user_id,
chat_id=chat_id,
platform=event.platform,
surface_ref=room_id,
name=room_name,
)
return [
OutgoingMessage(
chat_id=event.chat_id,
text=f"Создан чат: {ctx.display_name} ({ctx.chat_id})",
)
]
return handle_new_chat
async def handle_list_chats(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
) -> list:
chats = await chat_mgr.list_active(event.user_id)
if not chats:
return [OutgoingMessage(chat_id=event.chat_id, text="Нет активных чатов.")]
lines = [f"{c.display_name} ({c.chat_id})" for c in chats]
return [OutgoingMessage(chat_id=event.chat_id, text="\n".join(lines))]
async def handle_rename(event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr) -> list:
if not event.args:
return [OutgoingMessage(chat_id=event.chat_id, text="Укажите название: !rename Название")]
ctx = await chat_mgr.rename(event.chat_id, " ".join(event.args), user_id=event.user_id)
return [OutgoingMessage(chat_id=event.chat_id, text=f"Переименован в: {ctx.display_name}")]
async def handle_archive(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
) -> list:
await chat_mgr.archive(event.chat_id, user_id=event.user_id)
return [OutgoingMessage(chat_id=event.chat_id, text="Чат архивирован.")]