"""Run both the Telegram bot and FastAPI server together.""" import sys, os, threading, logging, subprocess, time sys.path.insert(0, os.path.dirname(__file__)) logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s") API_PORT = 8000 def _free_port(port: int): """Kill any stale process holding the given port (Windows only). Prevents [Errno 10048] when restarting after Ctrl+C.""" if sys.platform != "win32": return try: result = subprocess.run( ["netstat", "-ano"], capture_output=True, text=True, timeout=5 ) for line in result.stdout.splitlines(): cols = line.split() # netstat columns: Proto Local Foreign State PID if len(cols) >= 5 and f":{port}" in cols[1] and cols[3] == "LISTENING": pid = cols[4] if pid not in ("0", str(os.getpid())): subprocess.run( ["taskkill", "/F", "/PID", pid], capture_output=True, timeout=5 ) print(f" Freed port {port} (killed stale PID {pid})", flush=True) except Exception: pass def run_api_server(): """Run FastAPI in a background thread.""" import uvicorn from backend.api.routes import app uvicorn.run(app, host="0.0.0.0", port=API_PORT, log_level="warning") def run_telegram_bot(): """Run Telegram bot (blocks the main thread).""" from backend.bot.bot import run_bot run_bot() if __name__ == "__main__": print("Starting ThirdEye...", flush=True) print(f" API: http://localhost:{API_PORT}", flush=True) print(" Bot: Running on Telegram", flush=True) print(" Dashboard: http://localhost:3000\n", flush=True) # Release port if a previous run's daemon thread didn't clean up _free_port(API_PORT) api_thread = threading.Thread(target=run_api_server, daemon=True) api_thread.start() time.sleep(2) print("API server starting...\n", flush=True) run_telegram_bot()