"""Configuration management for the Netdata Zulip bot.""" import os from pathlib import Path import structlog from dotenv import load_dotenv from .models import ServerConfig, ZulipConfig logger = structlog.get_logger() def load_config() -> tuple[ZulipConfig, ServerConfig]: """Load configuration from environment variables and .env files.""" # Load .env file if present env_file = Path(".env") if env_file.exists(): load_dotenv(env_file) logger.info("Loaded configuration from .env file") # Load Zulip configuration (optional from env, main source should be zuliprc) zulip_config = ZulipConfig( site=os.getenv("ZULIP_SITE"), email=os.getenv("ZULIP_EMAIL"), api_key=os.getenv("ZULIP_API_KEY"), stream=os.getenv("ZULIP_STREAM", "netdata-alerts"), ) # Load server configuration server_config = ServerConfig( host=os.getenv("SERVER_HOST", "0.0.0.0"), port=int(os.getenv("SERVER_PORT", "8080")), challenge_secret=os.getenv("SERVER_CHALLENGE_SECRET"), ) logger.info( "Configuration loaded", zulip_site=zulip_config.site or "(from zuliprc)", zulip_email=zulip_config.email or "(from zuliprc)", zulip_stream=zulip_config.stream, server_host=server_config.host, server_port=server_config.port, ) return zulip_config, server_config def load_zuliprc_config(zuliprc_path: str | None = None) -> ZulipConfig: """Load Zulip configuration from a zuliprc file. Args: zuliprc_path: Path to zuliprc file. If None, looks for ~/.zuliprc Returns: ZulipConfig instance """ if zuliprc_path is None: zuliprc_path = Path.home() / ".zuliprc" else: zuliprc_path = Path(zuliprc_path) if not zuliprc_path.exists(): raise FileNotFoundError(f"Zuliprc file not found: {zuliprc_path}") config = {} with open(zuliprc_path) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: key, value = line.split("=", 1) config[key.strip()] = value.strip() # Map zuliprc keys to our config zulip_config = ZulipConfig( site=config.get("site", ""), email=config.get("email", ""), api_key=config.get("key", ""), stream=config.get("stream", "netdata-alerts"), ) # Validate required fields from zuliprc if not all([zulip_config.site, zulip_config.email, zulip_config.api_key]): missing = [] if not zulip_config.site: missing.append("site") if not zulip_config.email: missing.append("email") if not zulip_config.api_key: missing.append("key") raise ValueError( f"Missing required Zulip configuration in {zuliprc_path}: " f"{', '.join(missing)}" ) logger.info( "Loaded Zulip configuration from zuliprc", path=str(zuliprc_path), site=zulip_config.site, email=zulip_config.email, stream=zulip_config.stream, ) return zulip_config