from modules.base_module import BaseModule from datetime import datetime, timedelta import asyncio import json import sys import os # Add parent directory to path for config import sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from config import ADMIN_USER class ConnectionMonitor(BaseModule): """Module for monitoring IRC connection status and providing connection commands.""" def __init__(self, bot, database, game_engine=None): super().__init__(bot, database) self.game_engine = game_engine self.start_time = datetime.now() def get_commands(self): """Return list of available connection monitoring commands.""" return [ "status", "uptime", "ping", "reconnect", "connection_stats" ] async def handle_command(self, channel, nickname, command, args): """Handle connection monitoring commands.""" if command == "status": await self.cmd_status(channel, nickname) elif command == "uptime": await self.cmd_uptime(channel, nickname) elif command == "ping": await self.cmd_ping(channel, nickname) elif command == "reconnect": await self.cmd_reconnect(channel, nickname) elif command == "connection_stats": await self.cmd_connection_stats(channel, nickname) async def cmd_status(self, channel, nickname): """Show bot connection status.""" try: # Get connection manager if available connection_manager = getattr(self.bot, 'connection_manager', None) if not connection_manager: self.send_message(channel, f"{nickname}: Connection manager not available") return stats = connection_manager.get_connection_stats() state = stats.get("state", "unknown") connected = stats.get("connected", False) # Status emoji status_emoji = "🟢" if connected else "🔴" # Build status message status_msg = f"{status_emoji} {nickname}: Bot Status - {state.upper()}" if connected: uptime = stats.get("uptime", "unknown") message_count = stats.get("message_count", 0) status_msg += f" | Uptime: {uptime} | Messages: {message_count}" else: reconnect_attempts = stats.get("reconnect_attempts", 0) status_msg += f" | Reconnect attempts: {reconnect_attempts}" self.send_message(channel, status_msg) except Exception as e: self.send_message(channel, f"{nickname}: Error getting status: {str(e)}") async def cmd_uptime(self, channel, nickname): """Show bot uptime.""" try: uptime = datetime.now() - self.start_time # Format uptime days = uptime.days hours, remainder = divmod(uptime.seconds, 3600) minutes, seconds = divmod(remainder, 60) uptime_str = f"{days}d {hours}h {minutes}m {seconds}s" # Get additional stats if available connection_manager = getattr(self.bot, 'connection_manager', None) if connection_manager: stats = connection_manager.get_connection_stats() reconnections = stats.get("total_reconnections", 0) failures = stats.get("connection_failures", 0) uptime_str += f" | Reconnections: {reconnections} | Failures: {failures}" self.send_message(channel, f"⏰ {nickname}: Bot uptime: {uptime_str}") except Exception as e: self.send_message(channel, f"{nickname}: Error getting uptime: {str(e)}") async def cmd_ping(self, channel, nickname): """Test bot responsiveness.""" try: start_time = datetime.now() # Get connection status connection_manager = getattr(self.bot, 'connection_manager', None) connected = False if connection_manager: connected = connection_manager.is_connected() end_time = datetime.now() response_time = (end_time - start_time).total_seconds() * 1000 # Status indicators connection_status = "🟢 Connected" if connected else "🔴 Disconnected" ping_emoji = "🏓" if response_time < 100 else "🐌" self.send_message(channel, f"{ping_emoji} {nickname}: Pong! Response time: {response_time:.1f}ms | {connection_status}" ) except Exception as e: self.send_message(channel, f"{nickname}: Error during ping: {str(e)}") async def cmd_reconnect(self, channel, nickname): """Force reconnection (admin only).""" try: # Check if user is admin if not await self._is_admin(nickname): self.send_message(channel, f"{nickname}: This command requires admin privileges.") return connection_manager = getattr(self.bot, 'connection_manager', None) if not connection_manager: self.send_message(channel, f"{nickname}: Connection manager not available.") return self.send_message(channel, f"{nickname}: Initiating manual reconnection...") # Force reconnection by stopping and starting await connection_manager.stop() await asyncio.sleep(2) # Start connection manager in background asyncio.create_task(connection_manager.start()) self.send_message(channel, f"{nickname}: Reconnection initiated.") except Exception as e: self.send_message(channel, f"{nickname}: Error during reconnection: {str(e)}") async def cmd_connection_stats(self, channel, nickname): """Show detailed connection statistics.""" try: connection_manager = getattr(self.bot, 'connection_manager', None) if not connection_manager: self.send_message(channel, f"{nickname}: Connection manager not available") return stats = connection_manager.get_connection_stats() # Build detailed stats message lines = [ f"📊 {nickname}: Connection Statistics", f"State: {stats.get('state', 'unknown').upper()}", f"Connected: {'Yes' if stats.get('connected') else 'No'}", f"Uptime: {stats.get('uptime', 'unknown')}", f"Messages: {stats.get('message_count', 0)}", f"Reconnections: {stats.get('total_reconnections', 0)}", f"Failures: {stats.get('connection_failures', 0)}", f"Reconnect attempts: {stats.get('reconnect_attempts', 0)}" ] # Add timing information last_message = stats.get('last_message_time') if last_message: lines.append(f"Last message: {last_message}") last_ping = stats.get('last_ping_time') if last_ping: lines.append(f"Last ping: {last_ping}") # Send each line for line in lines: self.send_message(channel, line) await asyncio.sleep(0.3) # Small delay to prevent flooding except Exception as e: self.send_message(channel, f"{nickname}: Error getting connection stats: {str(e)}") async def _is_admin(self, nickname): """Check if user has admin privileges.""" return nickname.lower() == ADMIN_USER.lower() async def get_connection_health(self): """Get connection health status for monitoring.""" try: connection_manager = getattr(self.bot, 'connection_manager', None) if not connection_manager: return { "healthy": False, "error": "Connection manager not available" } stats = connection_manager.get_connection_stats() connected = stats.get("connected", False) # Check if connection is healthy healthy = connected and stats.get("reconnect_attempts", 0) < 5 return { "healthy": healthy, "connected": connected, "state": stats.get("state", "unknown"), "uptime": stats.get("uptime"), "message_count": stats.get("message_count", 0), "reconnect_attempts": stats.get("reconnect_attempts", 0), "total_reconnections": stats.get("total_reconnections", 0), "connection_failures": stats.get("connection_failures", 0) } except Exception as e: return { "healthy": False, "error": str(e) } def get_module_stats(self): """Get module-specific statistics.""" uptime = datetime.now() - self.start_time return { "module_name": "ConnectionMonitor", "module_uptime": str(uptime), "commands_available": len(self.get_commands()), "start_time": self.start_time }