"""验证业务配置:SDK 的 .env(AppID/AppSecret 等)之上再读验证参数。 全部项都有内置默认值,.env 里按需覆盖;取值越界或非法启动即报错。 """ from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path from dotenv import load_dotenv # 幂等:qqbotsdk 导入时也会 load_dotenv(),且不会覆盖已存在的环境变量 load_dotenv() def _int_env(name: str, default: int, lo: int, hi: int) -> int: raw = os.getenv(name, "").strip() if not raw: return default try: value = int(raw) except ValueError: raise ValueError(f"{name} 应为整数,实际: {raw!r}") from None if not lo <= value <= hi: raise ValueError(f"{name} 应在 {lo}~{hi} 之间,实际: {value}") return value def _bool_env(name: str, default: bool) -> bool: raw = os.getenv(name, "").strip().lower() if not raw: return default if raw in ("1", "true", "yes", "on"): return True if raw in ("0", "false", "no", "off"): return False raise ValueError(f"{name} 应为 true/false,实际: {raw!r}") @dataclass(frozen=True, slots=True) class AuthConfig: """验证业务参数,含义与默认值见 .env.example。""" verify_timeout_minutes: int pending_mute_hours: int timeout_kick: bool timeout_fallback_mute_hours: int captcha_options: int verified_trust_days: int foreign_click_limit: int foreign_click_mute_minutes: int data_dir: Path @classmethod def load(cls) -> "AuthConfig": return cls( verify_timeout_minutes=_int_env("VERIFY_TIMEOUT_MINUTES", 10, 1, 1440), pending_mute_hours=_int_env("PENDING_MUTE_HOURS", 24, 1, 24 * 30), timeout_kick=_bool_env("TIMEOUT_KICK", True), timeout_fallback_mute_hours=_int_env( "TIMEOUT_FALLBACK_MUTE_HOURS", 72, 1, 24 * 30 ), captcha_options=_int_env("CAPTCHA_OPTIONS", 4, 2, 5), verified_trust_days=_int_env("VERIFIED_TRUST_DAYS", 0, 0, 365), foreign_click_limit=_int_env("FOREIGN_CLICK_LIMIT", 3, 1, 100), foreign_click_mute_minutes=_int_env("FOREIGN_CLICK_MUTE_MINUTES", 5, 1, 1440), data_dir=Path(os.getenv("DATA_DIR", "data")), )