"""Configuration management for the Netdata Zulip bot.""" import os from pathlib import Path from typing import Optional import structlog from dotenv import load_dotenv from .models import ZulipConfig, ServerConfig 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")), ) 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: Optional[str] = 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, 'r') 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}: {', '.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