from pydantic import Field, field_validator, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict import os import logging logger = logging.getLogger("onmyoji_gacha") def _read_nonebot_config(name: str) -> str: """从 NoneBot driver.config 读取配置;测试或独立加载模块时静默回退。""" try: from nonebot import get_driver driver_config = get_driver().config except Exception: return "" value = getattr(driver_config, name.lower(), "") return str(value) if value is not None else "" def _read_setting(name: str) -> str: """按单个配置名读取系统环境变量,再读取 NoneBot 配置对象。""" return os.getenv(name, "") or _read_nonebot_config(name) def _first_setting(*names: str, default: str = "") -> str: """按业务优先级读取多个兼容配置名。""" for name in names: value = _read_setting(name) if value: return value return default class Config(BaseSettings): model_config = SettingsConfigDict(extra="ignore") # 抽卡概率配置 RARITY_PROBABILITY: dict = { "R": 78.75, "SR": 20.0, "SSR": 1.0, "SP": 0.25 } # 每日抽卡限制 DAILY_LIMIT: int = 3 # 数据文件路径 DB_FILE: str = "data/onmyoji_gacha/gacha.db" # 式神图片目录 SHIKIGAMI_IMG_DIR: str = "data/chouka/" # 触发指令 GACHA_COMMANDS: list = ["抽卡","抽奖", "召唤"] STATS_COMMANDS: list = ["我的抽卡","我的抽奖", "我的图鉴"] DAILY_STATS_COMMANDS: list = ["今日抽卡", "今日统计", "抽卡统计"] TRIPLE_GACHA_COMMANDS: list = ["三连", "三连抽"] ACHIEVEMENT_COMMANDS: list = ["查询成就", "抽卡成就"] INTRO_COMMANDS: list = ["抽卡介绍", "抽卡说明", "抽卡帮助"] # 成就系统配置 ACHIEVEMENTS: dict = { "consecutive_days_30_1": { "name": "勤勤恳恳Ⅰ", "description": "连续抽卡30天", "reward": "天卡", "threshold": 30, "type": "consecutive_days", "level": 1, "repeatable": True }, "consecutive_days_30_2": { "name": "勤勤恳恳Ⅱ", "description": "连续抽卡60天", "reward": "天卡", "threshold": 60, "type": "consecutive_days", "level": 2, "repeatable": True }, "consecutive_days_30_3": { "name": "勤勤恳恳Ⅲ", "description": "连续抽卡90天", "reward": "天卡", "threshold": 90, "type": "consecutive_days", "level": 3, "repeatable": True }, "consecutive_days_30_4": { "name": "勤勤恳恳Ⅳ", "description": "连续抽卡120天", "reward": "周卡", "threshold": 120, "type": "consecutive_days", "level": 4, "repeatable": True }, "consecutive_days_30_5": { "name": "勤勤恳恳Ⅴ", "description": "连续抽卡150天", "reward": "周卡", "threshold": 150, "type": "consecutive_days", "level": 5, "repeatable": True, "repeat_reward": "天卡" }, "no_ssr_60": { "name": "非酋", "description": "连续60次未抽到SSR/SP", "reward": "天卡", "threshold": 60, "type": "no_ssr_streak" }, "no_ssr_120": { "name": "顶级非酋", "description": "连续120次未抽到SSR/SP", "reward": "周卡", "threshold": 120, "type": "no_ssr_streak" }, "no_ssr_180": { "name": "月见黑", "description": "连续180次未抽到SSR/SP", "reward": "月卡", "threshold": 180, "type": "no_ssr_streak" } } # 权限配置 ALLOWED_GROUP_ID: int = 621016172 ALLOWED_USER_ID: int = 1424473282 # 特殊概率用户配置 SPECIAL_PROBABILITY_USERS: list = [] # 100%抽到SSR或SP的用户列表 # Web后台管理配置 WEB_ADMIN_TOKEN: str = Field( default_factory=lambda: _first_setting("WEB_ADMIN_TOKEN", default="onmyoji_admin_token_2024") ) WEB_ADMIN_PORT: int = Field(default_factory=lambda: int(_first_setting("WEB_ADMIN_PORT", default="8080"))) # 蛋定服务器对接配置 DD_API_HOST: str = "https://api.danding.vip/DD/" GACHA_API_HOST: str = Field( default_factory=lambda: _first_setting( "DANDING_GACHA_API_HOST", default="https://api.danding.vip/bot/gacha", ) ) BOT_TOKEN: str = Field( default_factory=lambda: _first_setting( "DANDING_BOT_TOKEN", "ONMYOJI_BOT_TOKEN", "DANDING_API_TOKEN", "BOT_TOKEN", ) ) BOT_USER_ID: str = Field(default_factory=lambda: _first_setting("DANDING_BOT_USER", default="1424473282")) # 时区 TIMEZONE: str = "Asia/Shanghai" @field_validator("GACHA_API_HOST") @classmethod def validate_gacha_api_host(cls, value): if not value: raise ValueError("GACHA_API_HOST cannot be empty") return value.rstrip("/") @model_validator(mode="after") def _warn_default_token(self): """运行时警告:如果使用默认admin token,在生产环境可能被猜解""" if self.WEB_ADMIN_TOKEN == "onmyoji_admin_token_2024": logger.warning("⚠️ WEB_ADMIN_TOKEN 使用默认值,生产环境请务必通过环境变量覆盖!") return self