Implement comprehensive team management and fix critical bugs
Team Management Features: - Added 6 new IRC commands: \!teamlist, \!activeteam, \!teamname, \!teamswap, \!heal, \!verifyteamswap - \!teamlist shows teams with pet names in format "Team name - pet1 - pet2 - pet3" - \!teamname redirects to web interface for secure PIN-based renaming - \!teamswap enables team switching with PIN verification via IRC - \!activeteam displays current team with health status indicators - \!heal command with 1-hour cooldown for pet health restoration Critical Bug Fixes: - Fixed \!teamlist SQL binding error - handled new team data format correctly - Fixed \!wild command duplicates - now shows unique species types only - Removed all debug print statements and implemented proper logging - Fixed data format inconsistencies in team management system Production Improvements: - Added logging infrastructure to BaseModule and core components - Converted 45+ print statements to professional logging calls - Database query optimization with DISTINCT for spawn deduplication - Enhanced error handling and user feedback messages Cross-platform Integration: - Seamless sync between IRC commands and web interface - PIN authentication leverages existing secure infrastructure - Team operations maintain consistency across all interfaces 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e920503dbd
commit
e17705dc63
10 changed files with 2012 additions and 384 deletions
|
|
@ -7,7 +7,7 @@ class PetManagement(BaseModule):
|
|||
"""Handles team, pets, and future pet management commands"""
|
||||
|
||||
def get_commands(self):
|
||||
return ["team", "pets", "activate", "deactivate", "nickname"]
|
||||
return ["team", "pets", "activate", "deactivate", "nickname", "heal", "teamname", "teamswap", "teamlist", "activeteam", "verifyteamswap"]
|
||||
|
||||
async def handle_command(self, channel, nickname, command, args):
|
||||
if command == "team":
|
||||
|
|
@ -20,6 +20,18 @@ class PetManagement(BaseModule):
|
|||
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"""
|
||||
|
|
@ -110,4 +122,273 @@ class PetManagement(BaseModule):
|
|||
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']}")
|
||||
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 <slot>")
|
||||
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 <slot> | 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 <pin>")
|
||||
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")
|
||||
Loading…
Add table
Add a link
Reference in a new issue