Files
MusicBot/musicbot/config.py
T
snowy-yang a11c0b375f 新增热重载:文件变更自动重启(开发用,默认关)
- musicbot/reload.py:轮询监控 musicbot/ 下的 .py 与 intents.toml/.env,
  变更后重启 bot 子进程;跳过 logs/、__pycache__/、.venv/
- 进程级重启而非模块重载:长连接 + 模块级状态 + handler 闭包,
  就地重载易留幽灵 handler,重启子进程状态干净
- 子进程用 _MUSICBOT_RELOAD_CHILD 标记,不会套娃自我复制
- Windows 用 taskkill /T 收整棵进程树,实测连续重载无进程泄漏
- 开启方式:`uv run musicbot --reload`(start.bat 已带)或 RELOAD=on
- 监督者不配日志,避免与子进程抢写同一日志文件
2026-09-20 22:44:20 +08:00

63 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Bot 全局配置:启动时从环境变量一次性读齐。"""
import os
from dataclasses import dataclass
from pathlib import Path
# 内嵌键盘上限 5 行 × 每行 5 个;歌曲占行 + 1 行翻页 → 每页最多 20 首
_MAX_MENU_PAGE_SIZE = 20
_LOG_LEVELS = ("TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL")
def _flag(name: str, default: bool) -> bool:
"""读布尔环境变量:on/true/1/yes 为真,off/false/0/no 为假。"""
raw = os.getenv(name, "").strip().lower()
if not raw:
return default
return raw in ("1", "true", "on", "yes")
def _send_mode() -> str:
"""SEND_MODE:下载发送方式。
file 先上传群文件、失败回退直链;link 直接发直链(跳过上传,可避开
"仅群主和管理员上传文件" 的 40093005)。非法值按默认 link 处理。
"""
raw = os.getenv("SEND_MODE", "").strip().lower()
return raw if raw in ("file", "link") else "link"
@dataclass(frozen=True, slots=True)
class BotConfig:
music_server_url: str
music_quality: str
music_source: str
menu_page_size: int = 10 # 选歌菜单每页候选数(1~20)
withdraw: bool = True # 自动撤回旧菜单/卡片是否开启
send_mode: str = "link" # 下载发送方式:"file" 先传文件 / "link" 直接发直链
log_level: str = "INFO" # 控制台与文件日志级别
log_dir: Path = Path("logs") # 日志目录;写不进去时自动退化为仅控制台
reload: bool = False # 热重载(开发用;--reload 亦可开启)
@classmethod
def load(cls) -> "BotConfig":
page_size = int(os.getenv("MENU_PAGE_SIZE", "10"))
return cls(
music_server_url=os.getenv("MUSIC_SERVER_URL", "http://127.0.0.1:16688"),
music_quality=os.getenv("MUSIC_QUALITY", "128k"),
music_source=os.getenv("MUSIC_SOURCE", "tx"),
menu_page_size=min(max(1, page_size), _MAX_MENU_PAGE_SIZE),
withdraw=_flag("WITHDRAW", True),
send_mode=_send_mode(),
log_level=_log_level(),
log_dir=Path(os.getenv("LOG_DIR", "logs")),
reload=_flag("RELOAD", False),
)
def _log_level() -> str:
"""LOG_LEVEL:日志级别,非法值按 INFO。"""
raw = os.getenv("LOG_LEVEL", "").strip().upper()
return raw if raw in _LOG_LEVELS else "INFO"