Major cleanup of the modular command system: **Legacy Code Removal:** - Removed all legacy command handlers from src/bot.py (74 lines) - Eliminated outdated command implementations superseded by modular system - Maintained backward compatibility while cleaning up codebase **Duplicate Command Consolidation:** - Removed duplicate status/uptime/ping commands from admin.py - Kept comprehensive implementations in connection_monitor.py - Eliminated 3 redundant commands and ~70 lines of duplicate code **Admin System Standardization:** - Updated backup_commands.py to use central ADMIN_USER from config.py - Updated connection_monitor.py to use central ADMIN_USER from config.py - Removed hardcoded admin user lists across modules - Ensured consistent admin privilege checking system-wide **Benefits:** - Cleaner, more maintainable command structure - No duplicate functionality across modules - Consistent admin configuration management - Reduced codebase size while maintaining all functionality - Better separation of concerns in modular architecture This cleanup addresses technical debt identified in the command audit and prepares the codebase for future development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
240 lines
No EOL
9.5 KiB
Python
240 lines
No EOL
9.5 KiB
Python
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
|
|
} |