from pathlib import Path import json import os from dotenv import load_dotenv # Load environment variables from .env file (if it exists) load_dotenv() class DynamicConfig: """Dynamic configuration that reloads from JSON on each access""" def __init__(self): self._json_cache = None self._cache_time = 0 def _load_from_json(self): """Load config from JSON file if it exists""" # Use path relative to this file, not current working directory config_dir = Path(__file__).parent path = config_dir / "data" / "config.json" print(f"[CONFIG] Looking for config at: {path.absolute()}") if not path.exists(): print(f"[CONFIG] Config file not found at {path.absolute()}") return {} try: with open(path, "r", encoding="utf-8") as f: data = json.load(f) return data.get("config", {}) except Exception as e: print(f"Error loading config from JSON: {e}") return {} def _get_value(self, key: str, default, is_int: bool = False): """Get config value with priority: env vars > json > defaults""" # 1. Try environment variable first (highest priority) env_val = os.getenv(key) if env_val is not None: return int(env_val) if is_int else env_val # 2. Try JSON config (reload from file each time) json_config = self._load_from_json() json_val = json_config.get(key) if json_val is not None: return json_val # 3. Use default return default def reload(self): """Force reload of JSON config (called after settings update)""" # Just clear cache - next access will reload self._json_cache = None print("[CONFIG] Configuration reloaded from file") # Torn API @property def TORN_API_KEY(self): return self._get_value("TORN_API_KEY", "YOUR_TORN_API_KEY_HERE") # FFScouter API @property def FFSCOUTER_KEY(self): return self._get_value("FFSCOUTER_KEY", "YOUR_FFSCOUTER_KEY_HERE") # Discord Bot @property def DISCORD_TOKEN(self): return self._get_value("DISCORD_TOKEN", "YOUR_DISCORD_BOT_TOKEN_HERE") @property def ALLOWED_CHANNEL_ID(self): return self._get_value("ALLOWED_CHANNEL_ID", 0, is_int=True) # Intervals @property def HIT_CHECK_INTERVAL(self): return self._get_value("HIT_CHECK_INTERVAL", 60, is_int=True) @property def REASSIGN_DELAY(self): return self._get_value("REASSIGN_DELAY", 120, is_int=True) # Bot Assignment Settings @property def ASSIGNMENT_TIMEOUT(self): return self._get_value("ASSIGNMENT_TIMEOUT", 60, is_int=True) @property def ASSIGNMENT_REMINDER(self): return self._get_value("ASSIGNMENT_REMINDER", 45, is_int=True) # Chain Timer Settings @property def CHAIN_TIMER_THRESHOLD(self): return self._get_value("CHAIN_TIMER_THRESHOLD", 5, is_int=True) # Authentication @property def AUTH_PASSWORD(self): return self._get_value("AUTH_PASSWORD", "YOUR_AUTH_PASSWORD_HERE") @property def JWT_SECRET(self): return self._get_value("JWT_SECRET", "your-secret-key-change-this") # Create global config instance config = DynamicConfig() # Export reload function def reload_config(): """Reload configuration from file""" config.reload() # For backward compatibility, access config properties directly # Code using "from config import TORN_API_KEY" needs to be changed to use config.TORN_API_KEY # But we can't do that without breaking existing code, so we need a different approach # Instead, we'll make these read from the config instance each time they're accessed # by using __getattr__ at the module level def __getattr__(name): """Dynamically fetch config values when accessed as module attributes""" if hasattr(config, name): return getattr(config, name) raise AttributeError(f"module '{__name__}' has no attribute '{name}'")