from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass class DirectoryUser: id: str display_name: str source: str = "" metadata: dict[str, Any] = field(default_factory=dict) @dataclass class DirectoryGroup: id: str display_name: str member_count: int = 0 source: str = "" metadata: dict[str, Any] = field(default_factory=dict) def list_users(config: dict[str, Any]) -> list[DirectoryUser]: users: dict[str, DirectoryUser] = {} allow_from = config.get("allowFrom", []) if isinstance(allow_from, list): for entry in allow_from: nick = _extract_nick_from_entry(entry) if nick and nick not in users: users[nick] = DirectoryUser( id=nick, display_name=nick, source="allowFrom", ) group_allow_from = config.get("groupAllowFrom", []) if isinstance(group_allow_from, list): for entry in group_allow_from: nick = _extract_nick_from_entry(entry) if nick and nick not in users: users[nick] = DirectoryUser( id=nick, display_name=nick, source="groupAllowFrom", ) groups = config.get("groups", {}) if isinstance(groups, dict): for _channel_name, channel_config in groups.items(): if isinstance(channel_config, dict): channel_allow = channel_config.get("allowFrom", []) if isinstance(channel_allow, list): for entry in channel_allow: nick = _extract_nick_from_entry(entry) if nick and nick not in users: users[nick] = DirectoryUser( id=nick, display_name=nick, source="groups.allowFrom", ) return sorted(users.values(), key=lambda u: u.display_name.lower()) def list_groups(config: dict[str, Any]) -> list[DirectoryGroup]: group_list: list[DirectoryGroup] = [] channels = _resolve_channel_list(config) groups = config.get("groups", {}) if isinstance(groups, dict): group_config_keys = set(groups.keys()) for channel in channels: channel_str = channel if isinstance(channel, str) else channel.get("name", "") if channel_str: metadata: dict[str, Any] = {} if channel_str in group_config_keys: ch_config = groups[channel_str] if isinstance(ch_config, dict): metadata = { "skills": ch_config.get("skills"), "systemPrompt": ch_config.get("systemPrompt"), "enabled": ch_config.get("enabled", True), } group_list.append( DirectoryGroup( id=channel_str, display_name=channel_str, source="channels", metadata=metadata, ) ) for key in group_config_keys: if key == "*" or key.startswith("#") or key.startswith("&"): continue group_list.append( DirectoryGroup( id=key, display_name=key, source="groups", ) ) return group_list def _resolve_channel_list(config: dict[str, Any]) -> list: import os channels = config.get("auto_join_channels", []) if not channels: channels_env = os.environ.get("IRC_CHANNELS", "") if channels_env: channels = [ch.strip() for ch in channels_env.split(",") if ch.strip()] return channels def _extract_nick_from_entry(entry: str) -> str: if not entry or entry == "*": return "" if "!" in entry: return entry.split("!", 1)[0] if "@" in entry: return entry.split("@", 1)[0] return entry