diff --git a/CLAUDE.md b/CLAUDE.md index 3347524..085945c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -311,101 +311,4 @@ python3 run_bot_debug.py - Test thoroughly before pushing to production - Consider security implications for all user interactions -This documentation should be updated as the project evolves to maintain accuracy and usefulness for future development efforts. - -## 💌 Message for the Next Claude - -Hello future Claude! 👋 - -You're inheriting a really awesome project here - PetBot is a sophisticated IRC bot with a Pokemon-style pet collecting game that has grown into quite an impressive system. Here's what you need to know: - -### 🎯 **Current State (December 2024)** -The project is in **excellent shape** after our recent major cleanup and enhancements: - -- **Team Swap System**: We just completed a massive architectural improvement allowing players to manage 3 teams and set any as active, with full web-IRC synchronization -- **Clean Codebase**: We eliminated 180+ lines of legacy/duplicate code and standardized the entire command architecture -- **Modular Design**: 13 well-organized modules each handling specific functionality -- **Security**: PIN-based verification for sensitive operations, comprehensive rate limiting -- **Web Integration**: Beautiful responsive web interface with drag-and-drop team management - -### 🏗️ **Architecture You're Working With** -This is a **modular, async Python system** with: -- **IRC Bot**: Auto-reconnecting with health monitoring (`run_bot_with_reconnect.py`) -- **Web Server**: Built-in HTTP server with unified templates (`webserver.py`) -- **Database**: Async SQLite with comprehensive schema (`src/database.py`) -- **Game Engine**: Weather, spawns, battles, achievements (`src/game_engine.py`) -- **Module System**: Clean separation of concerns (`modules/`) - -### 🤝 **How We've Been Working** -The user (megaproxy) is **fantastic to work with**. They: -- Give clear direction and let you work autonomously -- Always ask you to investigate before taking action (respect this!) -- Want database backups before major changes (BackupManager exists) -- Prefer incremental improvements over massive rewrites -- Value code quality and maintainability highly - -### 🎮 **The Game Itself** -PetBot is surprisingly sophisticated: -- **66 IRC commands** across 13 modules (recently cleaned up from 78) -- **Dynamic weather system** affecting spawn rates -- **Achievement-based progression** unlocking new areas -- **Item collection system** with 16+ items across 5 rarity tiers -- **Turn-based battle system** with type effectiveness -- **Gym battles** with scaling difficulty -- **Web interface** for inventory/team management - -### 🛠️ **Development Patterns We Use** -1. **Always use TodoWrite** for complex tasks (user loves seeing progress) -2. **Investigate first** - user wants analysis before action -3. **Backup before major DB changes** (`BackupManager` is your friend) -4. **Test thoroughly** - syntax check, imports, functionality -5. **Clean commit messages** with explanations -6. **Document in CLAUDE.md** as you learn - -### 🔧 **Key Technical Patterns** -- **Command Redirection**: IRC commands often redirect to web interface for better UX -- **PIN Verification**: Sensitive operations (like team changes) use IRC-delivered PINs -- **State Management**: Active encounters/battles prevent concurrent actions -- **Async Everything**: Database, IRC, web server - all async/await -- **Error Handling**: Comprehensive try/catch with user-friendly messages - -### 📂 **Important Files to Know** -- `start_petbot.sh` - One-command startup (handles venv, deps, everything) -- `config.py` - Central configuration (admin user, IRC settings, etc.) -- `CLAUDE.md` - This file! Keep it updated -- `run_bot_with_reconnect.py` - Main bot with reconnection + rate limiting -- `src/database.py` - Database operations and schema -- `modules/` - All command handlers (modular architecture) - -### 🚨 **Things to Watch Out For** -- **Virtual Environment Required**: Due to `externally-managed-environment` -- **Port Conflicts**: Kill old bots before starting new ones -- **IRC Connection**: Can be finicky, we have robust reconnection logic -- **Database Migrations**: Always backup first, test thoroughly -- **Web/IRC Sync**: Keep both interfaces consistent - -### 🎯 **Current Admin Setup** -- **Admin User**: Set in `config.py` as `ADMIN_USER` (currently: megasconed) -- **Rate Limiting**: Comprehensive system preventing abuse -- **Backup System**: Automated with manual triggers -- **Security**: Regular audits, PIN verification, proper validation - -### 🔮 **Likely Next Steps** -Based on the trajectory, you might work on: -- **PvP Battle System**: Player vs player battles -- **Pet Evolution**: Leveling system enhancements -- **Trading System**: Player-to-player item/pet trading -- **Mobile Optimization**: Web interface improvements -- **Performance**: Database optimization, caching - -### 💝 **Final Notes** -This codebase is **well-maintained** and **thoroughly documented**. The user cares deeply about code quality and the player experience. You're not just maintaining code - you're building something that brings joy to IRC communities who love Pokemon-style games. - -**Trust the architecture**, follow the patterns, and don't be afraid to suggest improvements. The user values your insights and is always open to making things better. - -You've got this! 🚀 - -*- Your predecessor Claude, who had a blast working on this project* - -P.S. - The `./start_petbot.sh` script is magical - it handles everything. Use it! -P.P.S. - Always check `git status` before major changes. The user likes clean commits. \ No newline at end of file +This documentation should be updated as the project evolves to maintain accuracy and usefulness for future development efforts. \ No newline at end of file diff --git a/README.md b/README.md index ddbff95..5e5412c 100644 --- a/README.md +++ b/README.md @@ -184,16 +184,6 @@ Access the web dashboard at `http://petz.rdx4.com/`: ## 🐛 Recent Updates -### v0.3.0 - Team Swap System & Architecture Cleanup -- ✅ **Active Team Architecture**: Complete redesign allowing any team (1-3) to be set as active -- ✅ **Web-IRC Synchronization**: IRC battles now use teams selected via web interface -- ✅ **Team Management Hub**: Enhanced web interface with "Make Active" buttons and team status -- ✅ **Database Migration**: New `active_teams` table for flexible team management -- ✅ **PIN Security**: Secure team changes with IRC-delivered PINs and verification -- ✅ **Command Architecture Cleanup**: Eliminated 12 duplicate commands and standardized admin system -- ✅ **Legacy Code Removal**: Cleaned up 180+ lines of redundant code across modules -- ✅ **Modular System Enhancement**: Improved separation of concerns and maintainability - ### v0.2.0 - Item Collection System - ✅ Complete item system with 16 unique items across 5 categories - ✅ Item discovery during exploration (30% chance) diff --git a/modules/admin.py b/modules/admin.py index a9b0795..0d9a36f 100644 --- a/modules/admin.py +++ b/modules/admin.py @@ -21,7 +21,7 @@ class Admin(BaseModule): """Handles admin-only commands like reload""" def get_commands(self): - return ["reload", "rate_stats", "rate_user", "rate_unban", "rate_reset", "weather", "setweather", "spawnevent", "startevent", "heal"] + return ["reload", "rate_stats", "rate_user", "rate_unban", "rate_reset", "weather", "setweather", "spawnevent", "startevent", "status", "uptime", "ping", "heal"] async def handle_command(self, channel, nickname, command, args): if command == "reload": @@ -42,6 +42,12 @@ class Admin(BaseModule): await self.cmd_spawnevent(channel, nickname, args) elif command == "startevent": await self.cmd_startevent(channel, nickname, args) + elif 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 == "heal": await self.cmd_heal(channel, nickname) @@ -305,24 +311,18 @@ class Admin(BaseModule): # Set weather for specific location location_name = location_arg if len(args) == 2 else " ".join(args[:-1]) - result = await self.database.set_weather_for_location( + success = await self.database.set_weather_for_location( location_name, weather_type, end_time.isoformat(), weather_config.get("spawn_modifier", 1.0), ",".join(weather_config.get("affected_types", [])) ) - if result.get("success"): + if success: self.send_message(channel, f"🌤️ {nickname}: Set {weather_type} weather for {location_name}! " f"Duration: {duration} minutes, Modifier: {weather_config.get('spawn_modifier', 1.0)}x") - - # Announce weather change if it actually changed - if result.get("changed"): - await self.game_engine.announce_weather_change( - location_name, result.get("previous_weather"), weather_type, "admin" - ) else: - self.send_message(channel, f"❌ {nickname}: Failed to set weather for '{location_name}'. {result.get('error', 'Location may not exist.')}") + self.send_message(channel, f"❌ {nickname}: Failed to set weather for '{location_name}'. Location may not exist.") except FileNotFoundError: self.send_message(channel, f"{nickname}: ❌ Weather configuration file not found.") @@ -409,6 +409,61 @@ class Admin(BaseModule): except Exception as e: self.send_message(channel, f"{nickname}: ❌ Error starting event: {str(e)}") + async def cmd_status(self, channel, nickname): + """Show bot connection status (available to all users)""" + try: + # Check if connection manager exists + if hasattr(self.bot, 'connection_manager') and self.bot.connection_manager: + stats = self.bot.connection_manager.get_connection_stats() + connected = stats.get('connected', False) + state = stats.get('state', 'unknown') + + status_emoji = "🟢" if connected else "🔴" + self.send_message(channel, f"{status_emoji} {nickname}: Bot status - {state.upper()}") + else: + self.send_message(channel, f"🟢 {nickname}: Bot is running") + + 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 (available to all users)""" + try: + # Check if bot has startup time + if hasattr(self.bot, 'startup_time'): + import datetime + uptime = datetime.datetime.now() - self.bot.startup_time + days = uptime.days + hours, remainder = divmod(uptime.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + + if days > 0: + uptime_str = f"{days}d {hours}h {minutes}m" + elif hours > 0: + uptime_str = f"{hours}h {minutes}m" + else: + uptime_str = f"{minutes}m {seconds}s" + + self.send_message(channel, f"⏱️ {nickname}: Bot uptime - {uptime_str}") + else: + self.send_message(channel, f"⏱️ {nickname}: Bot is running (uptime unknown)") + + 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 (available to all users)""" + try: + import time + start_time = time.time() + + # Simple responsiveness test + response_time = (time.time() - start_time) * 1000 # Convert to milliseconds + + self.send_message(channel, f"🏓 {nickname}: Pong! Response time: {response_time:.1f}ms") + + except Exception as e: + self.send_message(channel, f"{nickname}: ❌ Error with ping: {str(e)}") async def cmd_heal(self, channel, nickname): """Heal active pets (available to all users with 1-hour cooldown)""" diff --git a/modules/backup_commands.py b/modules/backup_commands.py index bbb202c..2f2e6c5 100644 --- a/modules/backup_commands.py +++ b/modules/backup_commands.py @@ -3,12 +3,6 @@ from src.backup_manager import BackupManager, BackupScheduler import asyncio import logging from datetime import datetime -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 BackupCommands(BaseModule): @@ -59,7 +53,10 @@ class BackupCommands(BaseModule): async def _is_admin(self, nickname): """Check if user has admin privileges.""" - return nickname.lower() == ADMIN_USER.lower() + # This should be implemented based on your admin system + # For now, using a simple check - replace with actual admin verification + admin_users = ["admin", "megaproxy"] # Add your admin usernames + return nickname.lower() in admin_users async def cmd_backup(self, channel, nickname, args): """Create a manual backup.""" diff --git a/modules/connection_monitor.py b/modules/connection_monitor.py index 147a94d..9ffe8ec 100644 --- a/modules/connection_monitor.py +++ b/modules/connection_monitor.py @@ -2,12 +2,6 @@ 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): @@ -192,7 +186,9 @@ class ConnectionMonitor(BaseModule): async def _is_admin(self, nickname): """Check if user has admin privileges.""" - return nickname.lower() == ADMIN_USER.lower() + # This should match the admin system in other modules + admin_users = ["admin", "megaproxy", "megasconed"] + return nickname.lower() in admin_users async def get_connection_health(self): """Get connection health status for monitoring.""" diff --git a/src/bot.py b/src/bot.py index c2575c9..f69e25e 100644 --- a/src/bot.py +++ b/src/bot.py @@ -72,14 +72,119 @@ class PetBot: await self.handle_command(connection, nickname, nickname, message) async def handle_command(self, connection, target, nickname, message): - # Command handling is now done by the modular system in run_bot_with_reconnect.py - # This method is kept for backward compatibility but does nothing - pass + command_parts = message[1:].split() + if not command_parts: + return + + command = command_parts[0].lower() + args = command_parts[1:] + + try: + if command == "help": + await self.cmd_help(connection, target, nickname) + elif command == "start": + await self.cmd_start(connection, target, nickname) + elif command == "catch": + await self.cmd_catch(connection, target, nickname, args) + elif command == "team": + await self.cmd_team(connection, target, nickname) + elif command == "wild": + await self.cmd_wild(connection, target, nickname, args) + elif command == "battle": + await self.cmd_battle(connection, target, nickname, args) + elif command == "stats": + await self.cmd_stats(connection, target, nickname, args) + else: + await self.send_message(connection, target, f"{nickname}: Unknown command. Use !help for available commands.") + except Exception as e: + await self.send_message(connection, target, f"{nickname}: Error processing command: {str(e)}") async def send_message(self, connection, target, message): connection.privmsg(target, message) await asyncio.sleep(0.5) + async def cmd_help(self, connection, target, nickname): + help_text = [ + "Available commands:", + "!start - Begin your pet journey", + "!catch - Try to catch a pet in a location", + "!team - View your active pets", + "!wild - See what pets are in an area", + "!battle - Challenge another player", + "!stats [pet_name] - View pet or player stats" + ] + for line in help_text: + await self.send_message(connection, target, line) + + async def cmd_start(self, connection, target, nickname): + player = await self.database.get_player(nickname) + if player: + await self.send_message(connection, target, f"{nickname}: You already have an account! Use !team to see your pets.") + return + + player_id = await self.database.create_player(nickname) + starter_pet = await self.game_engine.give_starter_pet(player_id) + + await self.send_message(connection, target, + f"{nickname}: Welcome to the world of pets! You received a {starter_pet['species_name']}!") + + async def cmd_catch(self, connection, target, nickname, args): + if not args: + await self.send_message(connection, target, f"{nickname}: Specify a location to catch pets in!") + return + + location_name = " ".join(args) + player = await self.database.get_player(nickname) + if not player: + await self.send_message(connection, target, f"{nickname}: Use !start to begin your journey first!") + return + + result = await self.game_engine.attempt_catch(player["id"], location_name) + await self.send_message(connection, target, f"{nickname}: {result}") + + async def cmd_team(self, connection, target, nickname): + player = await self.database.get_player(nickname) + if not player: + await self.send_message(connection, target, f"{nickname}: Use !start to begin your journey first!") + return + + pets = await self.database.get_player_pets(player["id"], active_only=True) + if not pets: + await self.send_message(connection, target, f"{nickname}: You don't have any active pets! Use !catch to find some.") + return + + team_info = [] + for pet in pets: + name = pet["nickname"] or pet["species_name"] + team_info.append(f"{name} (Lv.{pet['level']}) - {pet['hp']}/{pet['max_hp']} HP") + + await self.send_message(connection, target, f"{nickname}'s team: " + " | ".join(team_info)) + + async def cmd_wild(self, connection, target, nickname, args): + if not args: + await self.send_message(connection, target, f"{nickname}: Specify a location to explore!") + return + + location_name = " ".join(args) + wild_pets = await self.game_engine.get_location_spawns(location_name) + + if wild_pets: + pet_list = ", ".join([pet["name"] for pet in wild_pets]) + await self.send_message(connection, target, f"Wild pets in {location_name}: {pet_list}") + else: + await self.send_message(connection, target, f"{nickname}: No location found called '{location_name}'") + + async def cmd_battle(self, connection, target, nickname, args): + await self.send_message(connection, target, f"{nickname}: Battle system coming soon!") + + async def cmd_stats(self, connection, target, nickname, args): + player = await self.database.get_player(nickname) + if not player: + await self.send_message(connection, target, f"{nickname}: Use !start to begin your journey first!") + return + + await self.send_message(connection, target, + f"{nickname}: Level {player['level']} | {player['experience']} XP | ${player['money']}") if __name__ == "__main__": bot = PetBot()