新增热重载:文件变更自动重启(开发用,默认关)
- 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 - 监督者不配日志,避免与子进程抢写同一日志文件
This commit is contained in:
1 parent
9bcdad7aa1
commit
a11c0b375f
8 files changed
+266
-4
No files matched your search
+33
-2
@@ -1,12 +1,15 @@
|
||||
"""装配入口:配置日志、注册命令并运行 bot。"""
|
||||
"""装配入口:配置日志、注册命令并运行 bot(可选热重载)。"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from loguru import logger
|
||||
from qqbotsdk import EventEmitter, run_loop
|
||||
|
||||
from . import reload
|
||||
from .config import BotConfig
|
||||
from .music import register
|
||||
|
||||
@@ -52,7 +55,35 @@ async def amain() -> None:
|
||||
await run_loop(ee)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
def _parse_args(argv: list[str] | None) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(prog="musicbot", description="QQ 群点歌 bot")
|
||||
parser.add_argument(
|
||||
"--reload",
|
||||
action="store_true",
|
||||
help="开发用:监控源码与配置,变更后自动重启(等价于 RELOAD=on)",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def _project_root() -> Path:
|
||||
"""项目根:包目录的上一级(flat 布局)。找不到 pyproject.toml 时退回当前
|
||||
工作目录,避免安装成非 editable 包时监视到 site-packages。"""
|
||||
root = Path(__file__).resolve().parent.parent
|
||||
return root if (root / "pyproject.toml").is_file() else Path.cwd()
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> None:
|
||||
args = _parse_args(argv)
|
||||
config = BotConfig.load()
|
||||
# 子进程只跑 bot,绝不再套一层监督(否则 --reload 会无限自我复制)
|
||||
reload_on = (args.reload or config.reload) and not reload.is_child()
|
||||
if reload_on:
|
||||
# 监控与子进程 cwd 都用项目根:intents.toml/.env 都在那里,
|
||||
# 保证「监控的树」与「实际加载的配置」一致
|
||||
root = _project_root()
|
||||
# 监督者不配置日志:子进程自己会配,避免两边抢写同一个日志文件
|
||||
code = reload.run_supervisor([sys.executable, "-m", "musicbot"], root)
|
||||
sys.exit(code)
|
||||
try:
|
||||
asyncio.run(amain())
|
||||
except KeyboardInterrupt:
|
||||
|
||||
Reference in new issue
Block a user