Add comprehensive web interface enhancements and encounter tracking
Major Features Added: - Complete petdex page showing all available pets with stats, types, evolution info - Encounter tracking system recording pet discoveries and catch statistics - Gym badges display on player profiles with victory counts and dates - Enhanced player profiles with discovery progress and completion percentages Technical Implementation: - New /petdex route with rarity-organized pet encyclopedia - Database encounter tracking with automatic integration into exploration/catch - Updated webserver.py with encounter data fetching and display - Fixed battle_system.py syntax error in gym battle completion logic - Organized project by moving unused bot files to backup_bots/ folder Database Changes: - Added player_encounters table for tracking discoveries - Added methods: record_encounter, get_player_encounters, get_encounter_stats - Enhanced player profile queries to include gym badges and encounters Web Interface Updates: - Petdex page with search stats, rarity grouping, and spawn location info - Player profiles now show species seen, completion %, gym badges earned - Encounter section displaying discovered pets with catch statistics - Updated navigation to include petdex link on main game hub 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bd455f1be5
commit
1ce7158200
7 changed files with 684 additions and 63 deletions
256
backup_bots/run_bot.py
Normal file
256
backup_bots/run_bot.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#!/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, GymBattles
|
||||
|
||||
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,
|
||||
GymBattles
|
||||
]
|
||||
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue