#!/usr/bin/env python3 """Pet management commands module for PetBot""" from .base_module import BaseModule class PetManagement(BaseModule): """Handles team, pets, and future pet management commands""" def get_commands(self): return ["team", "pets", "activate", "deactivate", "nickname", "heal", "teamname", "teamswap", "teamlist", "activeteam", "verifyteamswap"] async def handle_command(self, channel, nickname, command, args): if command == "team": await self.cmd_team(channel, nickname) elif command == "pets": await self.cmd_pets(channel, nickname) elif command == "activate": await self.cmd_activate(channel, nickname, args) elif command == "deactivate": await self.cmd_deactivate(channel, nickname, args) elif command == "nickname": await self.cmd_nickname(channel, nickname, args) elif command == "heal": await self.cmd_heal(channel, nickname) elif command == "teamname": await self.cmd_teamname(channel, nickname, args) elif command == "teamswap": await self.cmd_teamswap(channel, nickname, args) elif command == "teamlist": await self.cmd_teamlist(channel, nickname) elif command == "activeteam": await self.cmd_activeteam(channel, nickname) elif command == "verifyteamswap": await self.cmd_verifyteamswap(channel, nickname, args) async def cmd_team(self, channel, nickname): """Redirect player to their team builder page""" player = await self.require_player(channel, nickname) if not player: return # Redirect to web interface for team management self.send_message(channel, f"โš”๏ธ {nickname}: Manage your team at: http://petz.rdx4.com/teambuilder/{nickname}") async def cmd_pets(self, channel, nickname): """Show link to pet collection web page""" player = await self.require_player(channel, nickname) if not player: return # Send URL to player's profile page instead of PM spam self.send_message(channel, f"{nickname}: View your complete pet collection at: http://petz.rdx4.com/player/{nickname}#pets") async def cmd_activate(self, channel, nickname, args): """Activate a pet for battle (PM only)""" # Redirect to PM for privacy if not args: self.send_pm(nickname, "Usage: !activate ") self.send_message(channel, f"{nickname}: Pet activation instructions sent via PM!") return player = await self.require_player(channel, nickname) if not player: return pet_name = " ".join(self.normalize_input(args)) result = await self.database.activate_pet(player["id"], pet_name) if result["success"]: pet = result["pet"] display_name = pet["nickname"] or pet["species_name"] position = result.get("team_position", "?") self.send_pm(nickname, f"โœ… {display_name} is now active for battle! Team position: {position}") self.send_message(channel, f"{nickname}: Pet activated successfully!") else: self.send_pm(nickname, f"โŒ {result['error']}") self.send_message(channel, f"{nickname}: Pet activation failed - check PM for details!") async def cmd_deactivate(self, channel, nickname, args): """Deactivate a pet to storage (PM only)""" # Redirect to PM for privacy if not args: self.send_pm(nickname, "Usage: !deactivate ") self.send_message(channel, f"{nickname}: Pet deactivation instructions sent via PM!") return player = await self.require_player(channel, nickname) if not player: return pet_name = " ".join(self.normalize_input(args)) result = await self.database.deactivate_pet(player["id"], pet_name) if result["success"]: pet = result["pet"] display_name = pet["nickname"] or pet["species_name"] self.send_pm(nickname, f"๐Ÿ“ฆ {display_name} moved to storage!") self.send_message(channel, f"{nickname}: Pet deactivated successfully!") else: self.send_pm(nickname, f"โŒ {result['error']}") self.send_message(channel, f"{nickname}: Pet deactivation failed - check PM for details!") async def cmd_nickname(self, channel, nickname, args): """Set a nickname for a pet""" if len(args) < 2: self.send_message(channel, f"{nickname}: Usage: !nickname ") self.send_message(channel, f"Example: !nickname Charmander Flamey") return player = await self.require_player(channel, nickname) if not player: return # Split args into pet identifier and new nickname pet_identifier = self.normalize_input(args[0]) new_nickname = " ".join(args[1:]) result = await self.database.set_pet_nickname(player["id"], pet_identifier, new_nickname) if result["success"]: old_name = result["old_name"] new_name = result["new_nickname"] self.send_message(channel, f"โœจ {nickname}: {old_name} is now nicknamed '{new_name}'!") else: self.send_message(channel, f"โŒ {nickname}: {result['error']}") async def cmd_heal(self, channel, nickname): """Heal active pets (available to all users with 1-hour cooldown)""" try: player = await self.require_player(channel, nickname) if not player: return # Check cooldown from datetime import datetime, timedelta last_heal = await self.database.get_last_heal_time(player["id"]) if last_heal: time_since_heal = datetime.now() - last_heal if time_since_heal < timedelta(hours=1): remaining = timedelta(hours=1) - time_since_heal minutes_remaining = int(remaining.total_seconds() / 60) self.send_message(channel, f"โฐ {nickname}: Heal command is on cooldown! {minutes_remaining} minutes remaining.") return # Get active pets active_pets = await self.database.get_active_pets(player["id"]) if not active_pets: self.send_message(channel, f"โŒ {nickname}: You don't have any active pets to heal!") return # Count how many pets need healing pets_healed = 0 for pet in active_pets: if pet["hp"] < pet["max_hp"]: # Heal pet to full HP await self.database.update_pet_hp(pet["id"], pet["max_hp"]) pets_healed += 1 if pets_healed == 0: self.send_message(channel, f"โœ… {nickname}: All your active pets are already at full health!") return # Update cooldown await self.database.update_last_heal_time(player["id"]) self.send_message(channel, f"๐Ÿ’Š {nickname}: Healed {pets_healed} pet{'s' if pets_healed != 1 else ''} to full health! Next heal available in 1 hour.") except Exception as e: self.send_message(channel, f"{nickname}: โŒ Error with heal command: {str(e)}") async def cmd_teamname(self, channel, nickname, args): """Redirect player to team builder for team naming""" player = await self.require_player(channel, nickname) if not player: return # Redirect to web interface for team management self.send_message(channel, f"๐Ÿท๏ธ {nickname}: Rename your teams at: http://petz.rdx4.com/teambuilder/{nickname}") self.send_message(channel, f"๐Ÿ’ก Click on team names to rename them with secure PIN verification!") async def cmd_teamswap(self, channel, nickname, args): """Switch active team to specified slot with PIN verification""" if not args: self.send_message(channel, f"{nickname}: Usage: !teamswap ") self.send_message(channel, f"Example: !teamswap 2") self.send_message(channel, f"Slots: 1-3") return player = await self.require_player(channel, nickname) if not player: return try: slot = int(args[0]) if slot < 1 or slot > 3: self.send_message(channel, f"โŒ {nickname}: Invalid slot! Must be 1, 2, or 3") return except ValueError: self.send_message(channel, f"โŒ {nickname}: Slot must be a number (1-3)") return try: # Check if team slot exists and has pets config = await self.database.load_team_configuration(player["id"], slot) if not config: self.send_message(channel, f"โŒ {nickname}: Team slot {slot} is empty! Create a team first via the web interface") return # Parse team data to check if it has pets import json team_data = json.loads(config["team_data"]) if config["team_data"] else [] if not team_data: self.send_message(channel, f"โŒ {nickname}: Team slot {slot} '{config['config_name']}' has no pets!") return # Check if this team is already active current_active_slot = await self.database.get_active_team_slot(player["id"]) if current_active_slot == slot: self.send_message(channel, f"โœ… {nickname}: Team slot {slot} '{config['config_name']}' is already active!") return # Get team management service for PIN-based swap from src.team_management import TeamManagementService from src.pin_authentication import PinAuthenticationService pin_service = PinAuthenticationService(self.database, self.bot) team_service = TeamManagementService(self.database, pin_service) # Request team swap with PIN verification swap_result = await team_service.request_team_swap(player["id"], nickname, slot) if swap_result["success"]: pet_count = len(team_data) self.send_message(channel, f"๐Ÿ” {nickname}: PIN sent for swapping to '{config['config_name']}' ({pet_count} pets). Check your PM!") else: self.send_message(channel, f"โŒ {nickname}: {swap_result.get('error', 'Failed to request team swap')}") except Exception as e: self.logger.error(f"Error in teamswap command: {e}") self.send_message(channel, f"โŒ {nickname}: Error processing team swap request") async def cmd_teamlist(self, channel, nickname): """Show all team slots with names and pet names""" player = await self.require_player(channel, nickname) if not player: return try: import json # Get team configurations directly from database team_configs = await self.database.get_player_team_configurations(player["id"]) current_active_slot = await self.database.get_active_team_slot(player["id"]) # Build team list display team_lines = [f"๐Ÿ“‹ {nickname}'s Teams:"] for slot in range(1, 4): # Find config for this slot config = next((c for c in team_configs if c.get("slot") == slot), None) if config and config.get("team_data"): team_name = config["name"] team_data = config["team_data"] # Get pet names from team data pet_names = [] if isinstance(team_data, list): # New format: list of pet objects (already fetched by get_player_team_configurations) for pet in team_data: if pet and isinstance(pet, dict): display_name = pet.get("nickname") or pet.get("species_name", "Unknown") pet_names.append(display_name) elif isinstance(team_data, dict): # Old format: dict with positions containing pet IDs for pos in sorted(team_data.keys()): pet_id = team_data[pos] if pet_id: pet = await self.database.get_pet_by_id(pet_id) if pet: display_name = pet.get("nickname") or pet.get("species_name", "Unknown") pet_names.append(display_name) # Mark active team active_marker = " ๐ŸŸข" if current_active_slot == slot else "" if pet_names: pets_text = " - ".join(pet_names) team_lines.append(f" {slot}. {team_name} - {pets_text}{active_marker}") else: team_lines.append(f" {slot}. {team_name} - empty{active_marker}") else: team_lines.append(f" {slot}. Team {slot} - empty") team_lines.append("") team_lines.append("Commands: !teamswap | Web: http://petz.rdx4.com/teambuilder/" + nickname) # Send each line separately to avoid IRC length limits for line in team_lines: self.send_message(channel, line) except Exception as e: self.logger.error(f"Error in teamlist command: {e}") self.send_message(channel, f"โŒ {nickname}: Error loading team list") async def cmd_activeteam(self, channel, nickname): """Show current active team details""" player = await self.require_player(channel, nickname) if not player: return try: # Get active team active_pets = await self.database.get_active_team(player["id"]) current_slot = await self.database.get_active_team_slot(player["id"]) if not active_pets: self.send_message(channel, f"โŒ {nickname}: You don't have an active team! Use !teamswap or the web interface to set one") return # Get team name if it's from a saved configuration team_name = "Active Team" if current_slot: config = await self.database.load_team_configuration(player["id"], current_slot) if config: team_name = config["config_name"] # Build active team display team_lines = [f"โš”๏ธ {nickname}'s {team_name}:"] for i, pet in enumerate(active_pets, 1): display_name = pet.get("nickname") or pet.get("species_name", "Unknown") level = pet.get("level", 1) hp = pet.get("hp", 0) max_hp = pet.get("max_hp", 100) # Health status indicator health_pct = (hp / max_hp * 100) if max_hp > 0 else 0 if health_pct >= 75: health_icon = "๐Ÿ’š" elif health_pct >= 50: health_icon = "๐Ÿ’›" elif health_pct >= 25: health_icon = "๐Ÿงก" else: health_icon = "โค๏ธ" team_lines.append(f" {i}. {display_name} (Lv.{level}) {health_icon} {hp}/{max_hp}") team_lines.append("") team_lines.append(f"Use !heal to restore health (1hr cooldown)") team_lines.append(f"Manage teams: http://petz.rdx4.com/teambuilder/{nickname}") # Send team info for line in team_lines: self.send_message(channel, line) except Exception as e: self.logger.error(f"Error in activeteam command: {e}") self.send_message(channel, f"โŒ {nickname}: Error loading active team") async def cmd_verifyteamswap(self, channel, nickname, args): """Verify PIN and execute team swap""" if not args: self.send_message(channel, f"{nickname}: Usage: !verifyteamswap ") return player = await self.require_player(channel, nickname) if not player: return pin_code = args[0].strip() try: # Get team management service from src.team_management import TeamManagementService from src.pin_authentication import PinAuthenticationService pin_service = PinAuthenticationService(self.database, self.bot) team_service = TeamManagementService(self.database, pin_service) # Verify PIN and execute team swap result = await team_service.verify_team_swap(player["id"], pin_code) if result["success"]: self.send_message(channel, f"โœ… {nickname}: {result['message']}") if 'pets_applied' in result: self.send_message(channel, f"๐Ÿ”„ {result['pets_applied']} pets are now active for battle!") else: self.send_message(channel, f"โŒ {nickname}: {result.get('error', 'Team swap failed')}") except Exception as e: self.logger.error(f"Error in verifyteamswap command: {e}") self.send_message(channel, f"โŒ {nickname}: Error processing team swap verification")