#!/usr/bin/env python3 import socket import time import sys import os import asyncio import importlib sys.path.append(os.path.dirname(os.path.abspath(__file__))) from src.database import Database from src.game_engine import GameEngine from modules import CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin, Inventory class PetBot: def __init__(self): self.database = Database() self.game_engine = GameEngine(self.database) self.config = { "server": "irc.libera.chat", "port": 6667, "nickname": "PetBot", "channel": "#petz", "command_prefix": "!" } self.socket = None self.connected = False self.running = True self.active_encounters = {} # player_id -> encounter_data self.modules = {} self.command_map = {} def initialize_async_components(self): """Initialize async components""" loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) print("Initializing database...") loop.run_until_complete(self.database.init_database()) print("Loading game data...") loop.run_until_complete(self.game_engine.load_game_data()) print("Loading modules...") self.load_modules() print("✓ Bot initialized successfully!") self.loop = loop def load_modules(self): """Load all command modules""" module_classes = [ CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin, Inventory ] self.modules = {} self.command_map = {} for module_class in module_classes: module_name = module_class.__name__ module_instance = module_class(self, self.database, self.game_engine) self.modules[module_name] = module_instance # Map commands to modules for command in module_instance.get_commands(): self.command_map[command] = module_instance print(f"✓ Loaded {len(self.modules)} modules with {len(self.command_map)} commands") async def reload_modules(self): """Reload all modules (for admin use)""" try: # Reload module files import modules importlib.reload(modules.core_commands) importlib.reload(modules.exploration) importlib.reload(modules.battle_system) importlib.reload(modules.pet_management) importlib.reload(modules.achievements) importlib.reload(modules.admin) importlib.reload(modules) # Reinitialize modules self.load_modules() return True except Exception as e: print(f"Module reload failed: {e}") return False def test_connection(self): """Test if we can connect to IRC""" print("Testing IRC connection...") try: test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) test_sock.settimeout(5) test_sock.connect((self.config["server"], self.config["port"])) test_sock.close() print("✓ IRC connection test successful") return True except Exception as e: print(f"✗ IRC connection failed: {e}") return False def connect(self): self.initialize_async_components() if not self.test_connection(): print("\n=== IRC Connection Failed ===") print("The bot's core functionality is working perfectly!") print("Run these commands to test locally:") print(" python3 test/test_commands.py - Test all functionality") print(" python3 test/test_db.py - Test database operations") print("\nTo connect to IRC:") print("1. Ensure network access to irc.libera.chat:6667") print("2. Or modify config in run_bot.py to use a different server") return print(f"Connecting to {self.config['server']}:{self.config['port']}") self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.settimeout(10) try: self.socket.connect((self.config["server"], self.config["port"])) print("✓ Connected to IRC server!") except Exception as e: print(f"✗ Failed to connect: {e}") return # Send IRC handshake self.send(f"NICK {self.config['nickname']}") self.send(f"USER {self.config['nickname']} 0 * :{self.config['nickname']}") print("✓ Sent IRC handshake") # Start message processing self.socket.settimeout(1) self.main_loop() def main_loop(self): print("✓ Bot is now running! Use Ctrl+C to stop.") print(f"✓ Bot will join {self.config['channel']} when connected") print(f"✓ Loaded modules: {', '.join(self.modules.keys())}") while self.running: try: data = self.socket.recv(4096).decode('utf-8', errors='ignore') if not data: print("Connection closed by server") break lines = data.strip().split('\\n') for line in lines: if line.strip(): self.handle_line(line.strip()) except socket.timeout: continue except KeyboardInterrupt: print("\\n✓ Shutting down bot...") self.running = False break except Exception as e: print(f"Error in main loop: {e}") time.sleep(1) if self.socket: self.socket.close() self.loop.close() def send(self, message): if self.socket: full_message = f"{message}\\r\\n" print(f">> {message}") self.socket.send(full_message.encode('utf-8')) def handle_line(self, line): print(f"<< {line}") if line.startswith("PING"): pong_response = line.replace("PING", "PONG") self.send(pong_response) return # Handle connection messages if "376" in line or "422" in line: # End of MOTD if not self.connected: self.send(f"JOIN {self.config['channel']}") self.connected = True print(f"✓ Joined {self.config['channel']} - Bot is ready!") return parts = line.split() if len(parts) < 4: return if parts[1] == "PRIVMSG": channel = parts[2] message = " ".join(parts[3:])[1:] hostmask = parts[0][1:] nickname = hostmask.split('!')[0] if message.startswith(self.config["command_prefix"]): print(f"Processing command from {nickname}: {message}") self.handle_command(channel, nickname, message) def handle_command(self, channel, nickname, message): command_parts = message[1:].split() if not command_parts: return command = command_parts[0].lower() args = command_parts[1:] try: if command in self.command_map: module = self.command_map[command] # Run async command handler self.loop.run_until_complete( module.handle_command(channel, nickname, command, args) ) else: self.send_message(channel, f"{nickname}: Unknown command. Use !help for available commands.") except Exception as e: self.send_message(channel, f"{nickname}: Error processing command: {str(e)}") print(f"Command error: {e}") def send_message(self, target, message): self.send(f"PRIVMSG {target} :{message}") time.sleep(0.5) def run_async_command(self, coro): return self.loop.run_until_complete(coro) if __name__ == "__main__": print("🐾 Starting Pet Bot for IRC (Modular Version)...") bot = PetBot() try: bot.connect() except KeyboardInterrupt: print("\\n🔄 Bot stopping...") # Gracefully shutdown the game engine try: bot.loop.run_until_complete(bot.game_engine.shutdown()) except Exception as e: print(f"Error during shutdown: {e}") print("✓ Bot stopped by user") except Exception as e: print(f"✗ Bot crashed: {e}") import traceback traceback.print_exc()