#!/usr/bin/env python3
"""
IRIS Bot Stop Script
Stops the bot gracefully
"""
import sys
import os
import time
import signal
import subprocess
from pathlib import Path
# Add project root to path
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
# Colors
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BOLD = '\033[1m'
NC = '\033[0m'
def print_header(text: str):
print("━" * 60)
print(f"{Colors.BOLD}{text}{Colors.NC}")
print("━" * 60)
print()
def print_success(text: str):
print(f"{Colors.GREEN}✓ {text}{Colors.NC}")
def print_warning(text: str):
print(f"{Colors.YELLOW}⚠️ {text}{Colors.NC}")
def print_error(text: str):
print(f"{Colors.RED}✗ {text}{Colors.NC}")
def get_pid_file() -> Path:
return PROJECT_ROOT / ".bot.pid"
def find_bot_processes():
"""Find bot processes using pgrep or ps"""
try:
# Try pgrep first
result = subprocess.run(
["pgrep", "-f", "src.telegram_bot.bot"],
capture_output=True,
text=True
)
if result.returncode == 0:
return [int(pid) for pid in result.stdout.strip().split('\n') if pid]
except FileNotFoundError:
pass
# Fallback to ps
try:
result = subprocess.run(
["ps", "aux"],
capture_output=True,
text=True
)
pids = []
for line in result.stdout.splitlines():
if "python" in line and "src.telegram_bot.bot" in line and "grep" not in line:
parts = line.split()
if len(parts) > 1:
try:
pids.append(int(parts[1]))
except ValueError:
continue
return pids
except:
return []
def stop_process(pid: int, timeout: int = 10) -> bool:
"""Stop a process gracefully, force kill if needed"""
try:
# Check if process exists
os.kill(pid, 0)
except ProcessLookupError:
return True
try:
# Send SIGTERM for graceful shutdown
print(f"Sending SIGTERM to process {pid}...")
os.kill(pid, signal.SIGTERM)
# Wait for graceful shutdown
for i in range(timeout):
try:
os.kill(pid, 0) # Check if still alive
time.sleep(1)
except ProcessLookupError:
return True
# Force kill if still running
print_warning(f"Process {pid} didn't stop gracefully, force killing...")
try:
os.kill(pid, signal.SIGKILL)
time.sleep(1)
return True
except ProcessLookupError:
return True
except ProcessLookupError:
return True
except Exception as e:
print_error(f"Failed to stop process {pid}: {e}")
return False
def main():
print_header("🛑 IRIS BOT - STOP SCRIPT")
pid_file = get_pid_file()
# Check PID file first
if pid_file.exists():
try:
pid = int(pid_file.read_text().strip())
print(f"Found PID file: {pid}")
try:
os.kill(pid, 0) # Check if process exists
print_warning(f"Stopping bot (PID: {pid})...")
if stop_process(pid):
print_success("Bot stopped successfully")
pid_file.unlink(missing_ok=True)
return 0
else:
print_error("Failed to stop bot")
return 1
except ProcessLookupError:
print_warning("Process not running (stale PID file)")
pid_file.unlink()
except (ValueError, OSError) as e:
print_error(f"Invalid PID file: {e}")
pid_file.unlink(missing_ok=True)
# No PID file, look for processes manually
print_warning("No PID file found")
print("Checking for running bot processes...")
pids = find_bot_processes()
if not pids:
print_success("No bot processes running")
return 0
print_warning(f"Found {len(pids)} bot process(es): {', '.join(map(str, pids))}")
response = input("\nKill these processes? (y/n): ").strip().lower()
if response != 'y':
print("Aborted")
return 0
success = True
for pid in pids:
if stop_process(pid):
print_success(f"Killed process {pid}")
else:
print_error(f"Failed to kill process {pid}")
success = False
if success:
print()
print_success("All bot processes stopped")
return 0
else:
return 1
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
print("\n\n❌ Interrupted by user")
sys.exit(1)
except Exception as e:
print(f"\n❌ Unexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)