Add \!heal command and automatic pet recovery background system

- Implement \!heal command with 1-hour cooldown available to all users
- Add comprehensive cooldown tracking with database last_heal_time validation
- Heal command restores all active pets to full health
- Add background pet recovery system to game engine:
  - Automatic 30-minute recovery timer for fainted pets
  - Background task checks every 5 minutes for eligible pets
  - Auto-recovery restores pets to 1 HP after 30 minutes
  - Proper startup/shutdown integration with game engine
- Add pet_recovery_task to game engine with graceful shutdown
- Include detailed logging for recovery operations
- Ensure system resilience with error handling and task cancellation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
megaproxy 2025-07-16 11:32:25 +00:00
parent 72c1098a22
commit d758d6b924
2 changed files with 246 additions and 3 deletions

View file

@ -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"]
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":
@ -38,6 +38,18 @@ class Admin(BaseModule):
await self.cmd_weather(channel, nickname, args)
elif command == "setweather":
await self.cmd_setweather(channel, nickname, args)
elif command == "spawnevent":
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)
async def cmd_reload(self, channel, nickname):
"""Reload bot modules (admin only)"""
@ -317,4 +329,182 @@ class Admin(BaseModule):
except ValueError as e:
self.send_message(channel, f"{nickname}: ❌ Invalid duration: {str(e)}")
except Exception as e:
self.send_message(channel, f"{nickname}: ❌ Error setting weather: {str(e)}")
self.send_message(channel, f"{nickname}: ❌ Error setting weather: {str(e)}")
async def cmd_spawnevent(self, channel, nickname, args):
"""Force spawn an NPC event (admin only)"""
if not self.is_admin(nickname):
self.send_message(channel, f"{nickname}: Access denied. Admin command.")
return
# Default to difficulty 1 if no args provided
difficulty = 1
if args:
try:
difficulty = int(args[0])
if difficulty not in [1, 2, 3]:
self.send_message(channel, f"{nickname}: ❌ Difficulty must be 1, 2, or 3.")
return
except ValueError:
self.send_message(channel, f"{nickname}: ❌ Invalid difficulty. Use 1, 2, or 3.")
return
try:
# Get the NPC events manager from the bot
if hasattr(self.bot, 'npc_events') and self.bot.npc_events:
event_id = await self.bot.npc_events.force_spawn_event(difficulty)
if event_id:
self.send_message(channel, f"🎯 {nickname}: Spawned new NPC event! Check `!events` to see it.")
else:
self.send_message(channel, f"{nickname}: Failed to spawn NPC event.")
else:
self.send_message(channel, f"{nickname}: NPC events system not available.")
except Exception as e:
self.send_message(channel, f"{nickname}: ❌ Error spawning event: {str(e)}")
async def cmd_startevent(self, channel, nickname, args):
"""Start a specific event type (admin only)"""
if not self.is_admin(nickname):
self.send_message(channel, f"{nickname}: Access denied. Admin command.")
return
# If no args provided, show available event types
if not args:
self.send_message(channel, f"{nickname}: Available types: resource_gathering, pet_rescue, community_project, emergency_response, festival_preparation, research_expedition, crisis_response, legendary_encounter, ancient_mystery")
return
event_type = args[0].lower()
valid_types = ["resource_gathering", "pet_rescue", "community_project", "emergency_response",
"festival_preparation", "research_expedition", "crisis_response",
"legendary_encounter", "ancient_mystery"]
if event_type not in valid_types:
self.send_message(channel, f"{nickname}: ❌ Invalid type. Available: {', '.join(valid_types)}")
return
# Optional difficulty parameter
difficulty = 1
if len(args) > 1:
try:
difficulty = int(args[1])
if difficulty not in [1, 2, 3]:
self.send_message(channel, f"{nickname}: ❌ Difficulty must be 1, 2, or 3.")
return
except ValueError:
self.send_message(channel, f"{nickname}: ❌ Invalid difficulty. Use 1, 2, or 3.")
return
try:
# Get the NPC events manager from the bot
if hasattr(self.bot, 'npc_events') and self.bot.npc_events:
event_id = await self.bot.npc_events.force_spawn_specific_event(event_type, difficulty)
if event_id:
self.send_message(channel, f"🎯 {nickname}: Started {event_type} event (ID: {event_id})! Check `!events` to see it.")
else:
self.send_message(channel, f"{nickname}: Failed to start {event_type} event.")
else:
self.send_message(channel, f"{nickname}: NPC events system not available.")
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)"""
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)}")