"""Environment and API key management for last30days skill.""" import os from pathlib import Path from typing import Optional, Dict, Any CONFIG_DIR = Path.home() / ".config" / "last30days" CONFIG_FILE = CONFIG_DIR / ".env" def load_env_file(path: Path) -> Dict[str, str]: """Load environment variables from a file.""" env = {} if not path.exists(): return env with open(path, 'r') as f: for line in f: line = line.strip() if not line or line.startswith('#'): continue if '=' in line: key, _, value = line.partition('=') key = key.strip() value = value.strip() # Remove quotes if present if value and value[0] in ('"', "'") and value[-1] == value[0]: value = value[1:-1] if key and value: env[key] = value return env def get_config() -> Dict[str, Any]: """Load configuration from ~/.config/last30days/.env and environment.""" # Load from config file first file_env = load_env_file(CONFIG_FILE) # Environment variables override file config = { 'OPENAI_API_KEY': os.environ.get('OPENAI_API_KEY') or file_env.get('OPENAI_API_KEY'), 'XAI_API_KEY': os.environ.get('XAI_API_KEY') or file_env.get('XAI_API_KEY'), 'OPENAI_MODEL_POLICY': os.environ.get('OPENAI_MODEL_POLICY') or file_env.get('OPENAI_MODEL_POLICY', 'auto'), 'OPENAI_MODEL_PIN': os.environ.get('OPENAI_MODEL_PIN') or file_env.get('OPENAI_MODEL_PIN'), 'XAI_MODEL_POLICY': os.environ.get('XAI_MODEL_POLICY') or file_env.get('XAI_MODEL_POLICY', 'latest'), 'XAI_MODEL_PIN': os.environ.get('XAI_MODEL_PIN') or file_env.get('XAI_MODEL_PIN'), } return config def config_exists() -> bool: """Check if configuration file exists.""" return CONFIG_FILE.exists() def get_available_sources(config: Dict[str, Any]) -> str: """Determine which sources are available based on API keys. Returns: 'both', 'reddit', 'x', or 'none' """ has_openai = bool(config.get('OPENAI_API_KEY')) has_xai = bool(config.get('XAI_API_KEY')) if has_openai and has_xai: return 'both' elif has_openai: return 'reddit' elif has_xai: return 'x' else: return 'none' def validate_sources(requested: str, available: str) -> tuple[str, Optional[str]]: """Validate requested sources against available keys. Args: requested: 'auto', 'reddit', 'x', or 'both' available: Result from get_available_sources() Returns: Tuple of (effective_sources, error_message) """ if available == 'none': return 'none', "No API keys configured. Please add at least one key to ~/.config/last30days/.env" if requested == 'auto': return available, None if requested == 'both': if available != 'both': missing = 'xAI' if available == 'reddit' else 'OpenAI' return 'none', f"Requested both sources but {missing} key is missing. Use --sources=auto to use available keys." if requested == 'reddit' and available == 'x': return 'none', "Requested Reddit but only xAI key is available." if requested == 'x' and available == 'reddit': return 'none', "Requested X but only OpenAI key is available." return requested, None