from __future__ import annotations import re from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from yuxi.channels.adapters.matrix.adapter import MatrixAdapter _DEFAULT_AGENT_CONFIG_ID = 1 _TARGET_PREFIXES = { "matrix:": "matrix_uri", "room:": "room_id", "channel:": "room_alias", "user:": "user_id", } _MXID_PATTERN = re.compile(r"^@[\w._=\-/]+:[\w.\-]+(?:\.[\w.\-]+)*$") _ROOM_ID_PATTERN = re.compile(r"^![\w._=\-/]+:[\w.\-]+(?:\.[\w.\-]+)*$") _ROOM_ALIAS_PATTERN = re.compile(r"^#[\w._=\-/]+:[\w.\-]+(?:\.[\w.\-]+)*$") class MatrixSessionHelper: def __init__(self, adapter: MatrixAdapter): self._adapter = adapter self._alias_cache: dict[str, str] = {} def resolve_chat_id(self, room_id: str, is_dm: bool) -> str: if is_dm: return f"dm_{room_id}" return room_id def resolve_thread_id( self, room_id: str, is_dm: bool, is_thread: bool, root_event_id: str | None, agent_config_id: int | None = None, ) -> str: cid = agent_config_id if agent_config_id is not None else _DEFAULT_AGENT_CONFIG_ID if is_thread and root_event_id: return f"agent:{cid}:matrix:thread:{room_id}:{root_event_id}" if is_dm: return f"agent:main:matrix:dm:{room_id}" return f"agent:{cid}:matrix:room:{room_id}" def resolve_chat_type(self, room_id: str) -> str: if self._adapter._is_dm_room(room_id): return "direct" return "group" def parse_target_id(self, raw: str) -> dict[str, str]: for prefix, target_type in _TARGET_PREFIXES.items(): if raw.startswith(prefix): value = raw[len(prefix) :] return {"type": target_type, "value": value, "raw": raw} if _MXID_PATTERN.match(raw): return {"type": "user_id", "value": raw, "raw": raw} if _ROOM_ID_PATTERN.match(raw): return {"type": "room_id", "value": raw, "raw": raw} if _ROOM_ALIAS_PATTERN.match(raw): return {"type": "room_alias", "value": raw, "raw": raw} if raw.startswith("#"): return {"type": "room_alias_localpart", "value": raw, "raw": raw} return {"type": "unknown", "value": raw, "raw": raw} def resolve_target_to_room(self, target: str) -> str | None: parsed = self.parse_target_id(target) t = parsed["type"] value = parsed["value"] if t in ("room_id",): return value if t in ("matrix_uri",): inner = self.parse_target_id(value) if inner["type"] in ("room_id",): return inner["value"] if inner["type"] in ("room_alias",): return self._resolve_alias(inner["value"]) if t in ("room_alias", "room_alias_localpart"): return self._resolve_alias(value) if t in ("user_id",): dm_rooms = self._adapter._direct_rooms for room_id in dm_rooms: return room_id return None def _resolve_alias(self, alias: str) -> str | None: if alias in self._alias_cache: return self._alias_cache[alias] client = self._adapter._client if not client: return None try: import asyncio resp = asyncio.get_event_loop().run_until_complete(client.room_resolve_alias(alias)) if hasattr(resp, "room_id") and resp.room_id: self._alias_cache[alias] = resp.room_id return resp.room_id except Exception: pass return None def resolve_session_metadata( self, room_id: str, event_source: dict[str, Any] | None = None, ) -> dict[str, Any]: metadata: dict[str, Any] = { "room_id": room_id, "chat_type": self.resolve_chat_type(room_id), } if event_source: content = event_source.get("content", {}) relates_to = content.get("m.relates_to", {}) if relates_to.get("rel_type") == "m.thread": metadata["thread_root"] = relates_to.get("event_id") metadata["is_thread"] = True if relates_to.get("m.in_reply_to"): metadata["reply_to"] = relates_to["m.in_reply_to"].get("event_id") info = content.get("info", {}) if info.get("mimetype"): metadata["mimetype"] = info["mimetype"] if info.get("size"): metadata["size"] = info["size"] config = self._adapter.config rooms_config = config.get("rooms", {}) room_cfg = rooms_config.get(room_id, {}) if room_cfg: metadata["room_config"] = room_cfg return metadata def resolve_participant_metadata( self, user_id: str, display_name: str = "", avatar_url: str = "", ) -> dict[str, Any]: return { "user_id": user_id, "display_name": display_name, "avatar_url": avatar_url, "is_bot_self": user_id == self._adapter.user_id, }