from __future__ import annotations import os from copy import deepcopy from typing import Any def resolve_merged_account_config( base_config: dict[str, Any], account_config: dict[str, Any], ) -> dict[str, Any]: merged = deepcopy(base_config) _deep_merge(merged, account_config) return merged def _deep_merge(base: dict, override: dict) -> None: for key, value in override.items(): if key in base and isinstance(base[key], dict) and isinstance(value, dict): _deep_merge(base[key], value) else: base[key] = value def list_irc_account_ids(config: dict[str, Any]) -> list[str]: accounts = config.get("accounts", {}) if not isinstance(accounts, dict): return [] return list(accounts.keys()) def list_enabled_irc_accounts(config: dict[str, Any]) -> list[dict[str, Any]]: accounts = config.get("accounts", {}) if not isinstance(accounts, dict): return [] result: list[dict[str, Any]] = [] base_config = _extract_base_config(config) for account_id, account_config in accounts.items(): if not isinstance(account_config, dict): continue if account_config.get("enabled") is False: continue merged = resolve_merged_account_config(base_config, account_config) merged["_account_id"] = account_id result.append(merged) return result def _extract_base_config(config: dict[str, Any]) -> dict[str, Any]: top_level_keys = { "server", "port", "use_tls", "nick", "username", "realname", "password", "sasl_username", "sasl_password", "use_sasl", "nickserv_service", "nickserv_password", "nickserv_password_file", "nickserv_register", "nickserv_register_email", "nickserv_enabled", "auto_join_channels", "nick_recovery", "dm_policy", "group_policy", "allowFrom", "groupAllowFrom", "groups", "dangerouslyAllowNameMatching", "groupAllowFromFallbackToAllowFrom", "connect_timeout", "probe_timeout", "chunker_mode", "commands", "reply_enabled", "markdown", "server_password", "server_password_file", } return {k: v for k, v in config.items() if k in top_level_keys and v} def get_account_config( config: dict[str, Any], account_id: str, ) -> dict[str, Any] | None: accounts = config.get("accounts", {}) if not isinstance(accounts, dict): return None account_config = accounts.get(account_id) if not isinstance(account_config, dict): return None if account_config.get("enabled") is False: return None base_config = _extract_base_config(config) merged = resolve_merged_account_config(base_config, account_config) merged["_account_id"] = account_id return merged def is_configured(config: dict[str, Any]) -> bool: host = config.get("server", "") or os.environ.get("IRC_HOST", "") nick = config.get("nick", "") or os.environ.get("IRC_NICK", "") return bool(host and nick) def has_configured_state() -> bool: return bool(os.environ.get("IRC_HOST", "") and os.environ.get("IRC_NICK", "")) def resolve_default_irc_account_id( config: dict[str, Any], default: str = "default", ) -> str: accounts = config.get("accounts", {}) if isinstance(accounts, dict) and default in accounts: return default account_ids = list_irc_account_ids(config) if account_ids: return account_ids[0] return default