surfaces/adapter/matrix/handlers/chat.py
Mikhail Putilovskij b7a04b6cf1 feat(01-02): convert matrix archive and rename handlers to factories
- register archive and rename as client-aware closure handlers
- rename matrix rooms via stored surface_ref when a client is available
- keep archive scoped to core chat state for phase 1
2026-04-02 22:51:01 +03:00

165 lines
5.6 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))]
def make_handle_rename(
client: Any | None,
store: Any | None,
) -> Callable[..., Awaitable[list]]:
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 Название")
]
new_name = " ".join(event.args)
ctx = await chat_mgr.rename(event.chat_id, new_name, user_id=event.user_id)
if client is not None and ctx.surface_ref:
await client.room_set_name(ctx.surface_ref, new_name)
return [OutgoingMessage(chat_id=event.chat_id, text=f"Переименован в: {ctx.display_name}")]
return handle_rename
def make_handle_archive(
client: Any | None,
store: Any | None,
) -> Callable[..., Awaitable[list]]:
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="Чат архивирован.")]
return handle_archive