from __future__ import annotations import re from yuxi.channel.protocols import SessionResolution _RE_DIGITS = re.compile(r"^\d+$") class ZaloSession: @staticmethod def build_conversation_id(chat_type: str, target_id: str) -> str: if chat_type == "GROUP": return f"zalo:group:{target_id}" return f"zalo:{target_id}" @staticmethod def build_session_key(agent_id: str, account_id: str, chat_type: str, target_id: str) -> str: chat = "direct" if chat_type in ("PRIVATE", "direct") else "group" return f"agent:{agent_id}:zalo:{chat}:{target_id}" @staticmethod def normalize_target(raw: str) -> str: target = raw.strip() for prefix in ("zalo:", "zl:"): if target.lower().startswith(prefix.lower()): target = target[len(prefix) :] break return target @staticmethod def parse_target(raw: str) -> tuple[str, str]: """Parse a target string into (chat_type, target_id). Supported formats: - zalo:{userId} → ("direct", userId) - zl:{userId} → ("direct", userId) - zalo:group:{chatId} → ("group", chatId) - zl:group:{chatId} → ("group", chatId) - {digits} → ("direct", digits) """ target = raw.strip() for prefix in ("zalo:", "zl:"): if target.lower().startswith(prefix.lower()): remainder = target[len(prefix) :] if remainder.lower().startswith("group:"): return ("group", remainder[len("group:") :]) return ("direct", remainder) if _RE_DIGITS.match(target): return ("direct", target) return ("direct", target) @staticmethod def resolve_dm_session(sender_id: str) -> SessionResolution: return SessionResolution( kind="direct", conversation_id=f"zalo:{sender_id}", ) @staticmethod def resolve_group_session(chat_id: str) -> SessionResolution: return SessionResolution( kind="group", conversation_id=f"zalo:group:{chat_id}", )