from pydantic import Field, SecretStr, computed_field from pydantic_settings import BaseSettings, SettingsConfigDict class BotSettings(BaseSettings): token: SecretStr class MonitorSettings(BaseSettings): check_interval: int = 30 # как часто пинговать сервисы, сек reminder_interval: int = 1800 # как часто напоминать что всё ещё лежит, сек request_timeout: int = 10 # таймаут одного запроса, сек # Сколько проверок подряд должны провалиться, прежде чем объявить падение. # Одна неудачная проверка — это обычно моргнувшая сеть, а не упавший # сервис. При 1 бот будит по любому чиху; при 2 ложные тревоги почти # исчезают, а реакция замедляется всего на один интервал. failure_threshold: int = 2 # Сколько символов ответа показывать в уведомлении. body_preview: int = 400 class KomodoSettings(BaseSettings): """Связь с Komodo. Всё опционально — без неё бот работает как раньше.""" url: str = "" # https://komod.example.com key: SecretStr = SecretStr("") secret: SecretStr = SecretStr("") timeout: int = 20 @computed_field @property def enabled(self) -> bool: return bool(self.url and self.key.get_secret_value()) class WebSettings(BaseSettings): """Приёмник входящих алертов (Komodo Alerter с endpoint type Custom).""" enabled: bool = False host: str = "0.0.0.0" # noqa: S104 port: int = 8080 # Общий секрет. Komodo произвольные заголовки слать не умеет, поэтому # принимаем и в X-Alert-Token, и в query-параметре ?token= token: SecretStr = SecretStr("") class DbSettings(BaseSettings): path: str = "data/healthbot.db" class LogSettings(BaseSettings): level: str = "INFO" level_external: str = "WARNING" show_time: bool = False console_width: int = 150 class Settings(BaseSettings): # Оставлено для совместимости: раньше админ был ровно один. admin_id: int = 0 # Несколько админов — чтобы бот годился не только владельцу-одиночке. admin_ids: list[int] = Field(default_factory=list) bot: BotSettings = Field(default_factory=BotSettings) monitor: MonitorSettings = Field(default_factory=MonitorSettings) komodo: KomodoSettings = Field(default_factory=KomodoSettings) web: WebSettings = Field(default_factory=WebSettings) db: DbSettings = Field(default_factory=DbSettings) log: LogSettings = Field(default_factory=LogSettings) model_config = SettingsConfigDict( case_sensitive=False, env_file=".env", env_nested_delimiter="__", extra="ignore" ) @computed_field @property def admins(self) -> set[int]: return {i for i in (self.admin_id, *self.admin_ids) if i} env = Settings()