import os import secrets from dataclasses import dataclass from typing import Optional @dataclass class BotConfig: token: str skip_pending: bool timeout: int polling_timeout: int num_threads: int parse_mode: str @classmethod def from_env(cls): return cls(os.getenv("BOT_TOKEN"), bool(int(os.getenv("BOT_SKIP_PENDING", True))), int(os.getenv("BOT_TIMEOUT", 20)), int(os.getenv("BOT_POLLING_TIMEOUT", 20)), int(os.getenv("BOT_NUM_THREADS", 2)), os.getenv("BOT_PARSE_MODE", "html")) @dataclass class WebhookConfig: domain: Optional[str] url_path: str max_connections: int drop_pending_updates: bool # secret token use_secret_token: bool secret_token: Optional[str] # self-signed certificate cert_path: Optional[str] def __post_init__(self): if self.use_secret_token and not self.secret_token: self.secret_token = secrets.token_hex() @property def url(self): return f"https://{self.domain}{self.url_path}" @classmethod def from_env(cls): return cls(os.getenv("WEBHOOK_DOMAIN"), os.getenv("WEBHOOK_URL_PATH", "/"), int(os.getenv("WEBHOOK_MAX_CONNECTIONS", 40)), bool(int(os.getenv("WEBHOOK_DROP_PENDING", True))), bool(int(os.getenv("WEBHOOK_USE_SECRET_TOKEN", True))), os.getenv("WEBHOOK_SECRET_TOKEN"), os.getenv("WEBHOOK_CERT_PATH")) @dataclass class I18NConfig: path: str lang: str fallback_lang: str @classmethod def from_env(cls): return cls(os.getenv("I18N_PATH", "i18n.yaml"), os.getenv("I18N_LANG", "en"), os.getenv("I18N_FALLBACK_LANG", "en")) @dataclass class StateStorageConfig: type: str redis_host: Optional[str] redis_port: int redis_db: int redis_pass: Optional[str] @classmethod def from_env(cls): return cls(os.getenv("SS_TYPE", "memory"), os.getenv("SS_REDIS_HOST"), int(os.getenv("SS_REDIS_PORT", 6379)), int(os.getenv("SS_REDIS_DB", 0)), os.getenv("SS_REDIS_PASS")) @dataclass class DatabaseConfig: url: str pool_recycle: int pool_pre_ping: bool @classmethod def from_env(cls): return cls(os.getenv("DB_URL", "sqlite:///bot.db"), int(os.getenv("DB_POOL_RECYCLE", 3600)), bool(int(os.getenv("DB_POOL_PRE_PING", True)))) @dataclass class Config: bot: BotConfig i18n: I18NConfig states: StateStorageConfig database: DatabaseConfig webhook: WebhookConfig use_webhook: bool log_level: str owner_id: int @classmethod def from_env(cls): return cls( bot=BotConfig.from_env(), i18n=I18NConfig.from_env(), states=StateStorageConfig.from_env(), database=DatabaseConfig.from_env(), webhook=WebhookConfig.from_env(), use_webhook=bool(int(os.getenv("USE_WEBHOOK", False))), log_level=os.getenv("LOG_LEVEL", "INFO"), owner_id=int(os.getenv("OWNER_ID", 1)), ) def load_config() -> Config: return Config.from_env()