import os import configparser from pathlib import Path from dotenv import load_dotenv REQUIRED_VARS = [ 'DISCORD_TOKEN', 'ALLOWED_GUILD_ID', # Legacy 'ALLOWED_GUILD_IDS', # New 'PANEL_URL', 'CLIENT_API_KEY', 'APPLICATION_API_KEY' ] def validate_env_vars(): """Validate that required environment variables are set and non-empty.""" # Check for either legacy or new guild ID variable has_guild_id = bool(os.getenv('ALLOWED_GUILD_ID')) has_guild_ids = bool(os.getenv('ALLOWED_GUILD_IDS')) if not has_guild_id and not has_guild_ids: raise ValueError( "Missing required environment variable: ALLOWED_GUILD_IDS (or legacy ALLOWED_GUILD_ID)" ) # Check other required vars missing_vars = [ var for var in ['DISCORD_TOKEN', 'PANEL_URL', 'CLIENT_API_KEY', 'APPLICATION_API_KEY'] if not os.getenv(var) ] if missing_vars: raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}") def get_env_config(): """Extract configuration from environment variables.""" # Support both new ALLOWED_GUILD_IDS and legacy ALLOWED_GUILD_ID allowed_guild_ids = os.getenv('ALLOWED_GUILD_IDS', '') if not allowed_guild_ids: allowed_guild_ids = os.getenv('ALLOWED_GUILD_ID', '') return { 'Discord': { 'Token': os.getenv('DISCORD_TOKEN'), 'AllowedGuildIDs': allowed_guild_ids }, 'Pterodactyl': { 'PanelURL': os.getenv('PANEL_URL'), 'ClientAPIKey': os.getenv('CLIENT_API_KEY'), 'ApplicationAPIKey': os.getenv('APPLICATION_API_KEY') } } def configs_match(existing_config, env_config): """Compare existing config with environment variables.""" for section, values in env_config.items(): if not existing_config.has_section(section): return False for key, env_value in values.items(): if env_value is None: # Environment variable not set, don't require match continue # For AllowedGuildIDs, also check legacy AllowedGuildID key check_keys = [key] if key == 'AllowedGuildIDs': check_keys.append('AllowedGuildID') matched = False for check_key in check_keys: existing_value = existing_config.get(section, check_key, fallback=None) if existing_value == env_value: matched = True break if not matched: return False return True def generate_config(): """ Generate config.ini from environment variables if: 1. No config file exists, OR 2. Config file exists but values don't match environment variables Environment variables always take priority. """ config_path = Path(os.getenv('CONFIG_PATH', 'config.ini')) # Check if config file exists if not config_path.exists(): print(f"No config file found at {config_path}, generating from environment variables...") validate_env_vars() _write_config(config_path, get_env_config()) return # Config file exists, read it and compare with environment existing_config = configparser.ConfigParser() existing_config.read(config_path) env_config = get_env_config() if not configs_match(existing_config, env_config): print(f"Config file at {config_path} differs from environment variables, regenerating...") # Validate that required env vars are present for regeneration validate_env_vars() _write_config(config_path, env_config) return print(f"Config file at {config_path} matches environment variables, using existing config.") def _write_config(config_path, config_dict): """Write config dictionary to file.""" config = configparser.ConfigParser() for section, values in config_dict.items(): config[section] = {k: str(v) for k, v in values.items() if v is not None} config_path.parent.mkdir(exist_ok=True, parents=True) with open(config_path, 'w') as configfile: config.write(configfile) print(f"Config file written to {config_path}") if __name__ == "__main__": load_dotenv() generate_config()