""" watchdog.py — 봇 생존 감시 + 자동 재시작 5분마다 실행 (작업 스케줄러) / 장중(09:00~15:10)에만 작동 """ import asyncio, os, subprocess, sys from datetime import datetime PROJECT = r'C:\Users\whdwo\OneDrive\바탕 화면\stockbot_v3' PID_FILE = os.path.join(PROJECT, 'logs', 'bot.pid') os.chdir(PROJECT) sys.path.insert(0, '.') from app.main import load_env load_env() def is_process_alive(pid: int) -> bool: r = subprocess.run( ['tasklist', '/FI', f'PID eq {pid}', '/NH'], capture_output=True, text=True, ) return str(pid) in r.stdout def get_pid() -> int | None: try: return int(open(PID_FILE).read().strip()) except Exception: return None def restart_bot() -> int: proc = subprocess.Popen( [sys.executable, 'app/main.py'], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP, stdout=open('logs/bot_stderr.log', 'a', encoding='utf-8'), stderr=subprocess.STDOUT, close_fds=True, ) with open(PID_FILE, 'w') as f: f.write(str(proc.pid)) return proc.pid async def main(): now = datetime.now() now_str = now.strftime("%H:%M") # 장 외 시간은 체크 안 함 if not ("09:00" <= now_str <= "15:10"): print(f"[{now_str}] 장 외 시간 — 워치독 종료") return from app.monitor.notifier import send pid = get_pid() if pid is None: msg = f"[경고] 봇 PID 파일 없음 — 봇이 실행되지 않은 상태입니다 ({now_str})" print(msg) await send(msg) new_pid = restart_bot() await send(f"[복구] 봇 자동 재시작 완료 PID={new_pid}") return if is_process_alive(pid): print(f"[{now_str}] 봇 정상 실행 중 PID={pid}") return # 봇이 죽어있음 msg = f"[긴급] 봇 프로세스 종료 감지 (PID={pid}) — 자동 재시작 시도" print(msg) await send(msg) new_pid = restart_bot() await send(f"[복구] 봇 자동 재시작 완료 PID={new_pid} ({now_str})") print(f"봇 재시작 완료 PID={new_pid}") if __name__ == "__main__": asyncio.run(main())