""" config.py — Centralised configuration for RepoRadar. Loads environment variables and defines git-server endpoints, the colour palette, cache TTLs and other tunables. Import `settings` everywhere instead of reading os.environ directly so configuration stays in one place. """ from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path from dotenv import load_dotenv # Load .env from the project root (the directory this file lives in). PROJECT_ROOT = Path(__file__).resolve().parent load_dotenv(PROJECT_ROOT / ".env") def _get_int(name: str) -> int | None: """Parse an optional integer env var, returning None when unset/blank.""" raw = os.getenv(name, "").strip() if not raw: return None try: return int(raw) except ValueError: return None @dataclass(frozen=True) class Settings: """Immutable runtime settings sourced from environment + defaults.""" # ── Discord ────────────────────────────────────────────── discord_token: str = os.getenv("DISCORD_TOKEN", "").strip() # Restrict slash-command sync to one guild for instant updates (recommended). guild_id: int | None = _get_int("GUILD_ID") # ── Git server ─────────────────────────────────────────── # Provider: "auto" (probe), "gitea" (also Forgejo), "gitlab", or "github". git_provider: str = os.getenv("GIT_PROVIDER", "auto").strip().lower() # Base URL of the git server, e.g. https://git.example.com / https://gitlab.com # For GitHub leave blank (defaults to api.github.com) and set GIT_PROVIDER=github. git_base_url: str = os.getenv("GIT_BASE_URL", "").strip().rstrip("/") # Personal access token (read-only is enough). Optional for public servers. git_token: str = os.getenv("GIT_TOKEN", "").strip() # Default channel for new-project drops (also settable per-guild via modal). git_drop_channel_id: int | None = _get_int("GIT_DROP_CHANNEL_ID") # Channel for highlighted/featured drops (keyword/star matches). git_highlight_channel_id: int | None = _get_int("GIT_HIGHLIGHT_CHANNEL_ID") # Role to ping on a highlighted drop. git_highlight_role_id: int | None = _get_int("GIT_HIGHLIGHT_ROLE_ID") # How often (seconds) the monitor polls the git server for new projects. git_poll_interval: int = _get_int("GIT_POLL_INTERVAL") or 300 # Max new projects to drop per scan cycle (avoids flooding on bursts). git_max_drops_per_cycle: int = _get_int("GIT_MAX_DROPS_PER_CYCLE") or 10 # ── Paths ──────────────────────────────────────────────── data_dir: Path = PROJECT_ROOT / "data" db_path: Path = PROJECT_ROOT / "data" / "reporadar.db" # ── Behaviour ──────────────────────────────────────────── log_level: str = os.getenv("LOG_LEVEL", "INFO").strip().upper() http_timeout: int = 30 # seconds per request request_retries: int = 3 # transient retry attempts user_agent: str = "RepoRadar/1.0 (+https://github.com/your-org/reporadar)" # ── Cyberpunk / hacker colour palette (hex → int) ───────── color_drop: int = 0x8A63FF # neon violet (new project drops) color_highlight: int = 0xE0115F # ruby (featured / highlighted) color_success: int = 0x00FF9C # matrix green color_error: int = 0xFF003C # neon red color_medium: int = 0xFFB300 # amber (unconfigured / neutral) color_info: int = 0x0A84FF # electric blue def ensure_dirs(self) -> None: """Create the data directory if it does not yet exist.""" self.data_dir.mkdir(parents=True, exist_ok=True) settings = Settings()