from __future__ import annotations import os from typing import Any _ENV_VAR_MAP: dict[str, str] = { "MATRIX_HOMESERVER": "homeserver", "MATRIX_USER_ID": "user_id", "MATRIX_ACCESS_TOKEN": "access_token", "MATRIX_PASSWORD": "password", "MATRIX_DEVICE_ID": "device_id", "MATRIX_DEVICE_NAME": "deviceName", "MATRIX_CRYPTO_STORE_DIR": "crypto_store_dir", "MATRIX_DM_POLICY": "dm.policy", "MATRIX_GROUP_POLICY": "groupPolicy", "MATRIX_PROXY": "proxy", "MATRIX_AUTO_JOIN": "autoJoin", "MATRIX_SYNC_TIMEOUT_MS": "sync_timeout_ms", "MATRIX_DISPLAY_NAME": "display_name", "MATRIX_AVATAR_URL": "avatarUrl", "MATRIX_RESPONSE_PREFIX": "responsePrefix", "MATRIX_MEDIA_MAX_MB": "mediaMaxMb", } _NESTED_KEY_MAP: dict[str, tuple[str, str]] = { "dm.policy": ("dm", "policy"), "groupPolicy": (None, "groupPolicy"), } def resolve_env_vars(config: dict[str, Any], account_id: str = "") -> dict[str, Any]: result = dict(config) for env_var, config_key in _ENV_VAR_MAP.items(): env_value = os.environ.get(env_var) if env_value is not None: if "." in config_key: _set_nested(result, config_key, env_value) else: result[config_key] = _coerce_type(env_value, config.get(config_key)) if account_id: prefix = f"MATRIX_{account_id.upper()}_" for env_name, env_value in os.environ.items(): if env_name.startswith(prefix): config_key = env_name[len(prefix) :].lower() result[config_key] = _coerce_type(env_value, config.get(config_key)) return result def _set_nested(config: dict[str, Any], key: str, value: Any) -> None: parts = key.split(".") if len(parts) == 2: parent, child = parts if parent not in config or not isinstance(config[parent], dict): config[parent] = {} config[parent][child] = value def _coerce_type(value: str, existing: Any) -> Any: if existing is None: return value if isinstance(existing, bool): return value.lower() in ("true", "1", "yes") if isinstance(existing, int): try: return int(value) except ValueError: return value if isinstance(existing, float): try: return float(value) except ValueError: return value return value def get_env_var_descriptions() -> dict[str, str]: return { "MATRIX_HOMESERVER": "Matrix homeserver URL (e.g. https://matrix.org)", "MATRIX_USER_ID": "Matrix user ID (e.g. @bot:matrix.org)", "MATRIX_ACCESS_TOKEN": "Matrix access token (alternative to password)", "MATRIX_PASSWORD": "Matrix account password", "MATRIX_DEVICE_ID": "Device ID for this session", "MATRIX_DEVICE_NAME": "Display name for this device", "MATRIX_CRYPTO_STORE_DIR": "Directory for E2EE key storage", "MATRIX_DM_POLICY": "DM access policy: open/disabled/allowlist/pairing", "MATRIX_GROUP_POLICY": "Group access policy: open/disabled/allowlist", "MATRIX_PROXY": "HTTP(S) proxy URL", "MATRIX_AUTO_JOIN": "Auto-join policy: off/always/allowlist/invites", "MATRIX_SYNC_TIMEOUT_MS": "Sync timeout in milliseconds", "MATRIX_DISPLAY_NAME": "Bot display name", "MATRIX_AVATAR_URL": "Bot avatar URL (mxc:// or https://)", "MATRIX_RESPONSE_PREFIX": "Prefix added to all bot responses", "MATRIX_MEDIA_MAX_MB": "Maximum media file size in MB", }