"""Настройки из переменных окружения (.env).""" from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path from dotenv import load_dotenv load_dotenv() ROOT = Path(__file__).resolve().parent.parent def _list(name: str) -> list[str]: return [x.strip() for x in os.getenv(name, "").split(",") if x.strip()] def _bool(name: str, default: bool) -> bool: v = os.getenv(name) if v is None or v == "": return default return v.strip().lower() in ("1", "true", "yes", "on") def _quiet(v: str | None) -> tuple[int, int] | None: # "23-8" -> (23, 8) if not v: return None try: a, b = v.split("-") return int(a), int(b) except ValueError: return None @dataclass(frozen=True) class Settings: tg_token: str owner_id: int openrouter_key: str model: str uni_email: str uni_password: str | None my_name: str mail_url: str teams_url: str data_dir: Path headless: bool chromium_path: str | None notify_min: int quiet_hours: tuple[int, int] | None digest_time: str | None tz: str reload_hours: float scan_interval: int batch_window: float vip: list[str] mute: list[str] rules_path: Path selectors_path: Path @property def profile_dir(self) -> Path: return self.data_dir / "browser-profile" @property def db_path(self) -> Path: return self.data_dir / "uni.db" def load() -> Settings: missing = [n for n in ("TG_TOKEN", "OWNER_ID", "OPENROUTER_API_KEY", "UNI_EMAIL") if not os.getenv(n)] if missing: raise SystemExit(f"Не заданы переменные окружения: {', '.join(missing)} (см. .env.example)") data_dir = Path(os.getenv("DATA_DIR", ROOT / "data")) data_dir.mkdir(parents=True, exist_ok=True) return Settings( tg_token=os.environ["TG_TOKEN"], owner_id=int(os.environ["OWNER_ID"]), openrouter_key=os.environ["OPENROUTER_API_KEY"], model=os.getenv("LLM_MODEL", "~anthropic/claude-haiku-latest"), uni_email=os.environ["UNI_EMAIL"], uni_password=os.getenv("UNI_PASSWORD") or None, my_name=os.getenv("MY_NAME", ""), mail_url=os.getenv("MAIL_URL", "https://outlook.office.com/mail/"), teams_url=os.getenv("TEAMS_URL", "https://teams.microsoft.com/v2/"), data_dir=data_dir, headless=_bool("HEADLESS", False), chromium_path=os.getenv("CHROMIUM_PATH") or None, notify_min=int(os.getenv("NOTIFY_MIN_LEVEL", "2")), quiet_hours=_quiet(os.getenv("QUIET_HOURS")), digest_time=os.getenv("DIGEST_TIME") or None, tz=os.getenv("TZ", "Europe/Warsaw"), reload_hours=float(os.getenv("RELOAD_HOURS", "6")), scan_interval=int(os.getenv("SCAN_INTERVAL", "120")), batch_window=float(os.getenv("BATCH_WINDOW", "4")), vip=_list("VIP"), mute=_list("MUTE"), rules_path=Path(os.getenv("RULES_PATH", ROOT / "config" / "rules.md")), selectors_path=Path(os.getenv("SELECTORS_PATH", ROOT / "config" / "selectors.json")), )