from __future__ import annotations from dataclasses import dataclass, field @dataclass class DirectoryPeer: user_id: str username: str nickname: str = "" email: str = "" first_name: str = "" last_name: str = "" @property def display_name(self) -> str: return self.nickname or self.username @property def opaque_id(self) -> str: return f"user:{self.user_id}" @dataclass class DirectoryGroup: channel_id: str name: str display_name: str = "" group_type: str = "O" # O=public, P=private, D=direct member_count: int = 0 purpose: str = "" header: str = "" @property def opaque_id(self) -> str: return f"channel:{self.channel_id}" @dataclass class DirectorySnapshot: peers: list[DirectoryPeer] = field(default_factory=list) groups: list[DirectoryGroup] = field(default_factory=list) @property def peer_count(self) -> int: return len(self.peers) @property def group_count(self) -> int: return len(self.groups) def find_peer(self, user_id: str) -> DirectoryPeer | None: for p in self.peers: if p.user_id == user_id: return p return None def find_group(self, channel_id: str) -> DirectoryGroup | None: for g in self.groups: if g.channel_id == channel_id: return g return None def list_peers_by_name(self, query: str) -> list[DirectoryPeer]: q = query.casefold() return [p for p in self.peers if q in p.username.casefold() or q in p.display_name.casefold()] def list_groups_by_name(self, query: str) -> list[DirectoryGroup]: q = query.casefold() return [g for g in self.groups if q in g.name.casefold() or q in g.display_name.casefold()] def build_peer_from_user_data(user: dict) -> DirectoryPeer: return DirectoryPeer( user_id=user.get("id", ""), username=user.get("username", ""), nickname=user.get("nickname", ""), email=user.get("email", ""), first_name=user.get("first_name", ""), last_name=user.get("last_name", ""), ) def build_group_from_channel_data(channel: dict) -> DirectoryGroup: return DirectoryGroup( channel_id=channel.get("id", ""), name=channel.get("name", ""), display_name=channel.get("display_name", ""), group_type=channel.get("type", "O"), member_count=channel.get("total_msg_count", 0), purpose=channel.get("purpose", ""), header=channel.get("header", ""), )