Add comprehensive startup script validation and enhanced pet system

- Enhanced start_petbot.sh with extensive validation and error checking
- Added emoji support to pet species system with database migration
- Expanded pet species from 9 to 33 unique pets with balanced spawn rates
- Improved database integrity validation and orphaned pet detection
- Added comprehensive pre-startup testing and configuration validation
- Enhanced locations with diverse species spawning across all areas
- Added dual-type pets and rarity-based spawn distribution
- Improved startup information display with feature overview
- Added background monitoring and validation systems

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
megaproxy 2025-07-16 00:17:54 +00:00
parent add7731d80
commit fca0423c84
8 changed files with 640 additions and 81 deletions

View file

@ -10,7 +10,11 @@
"Bash(pip3 install:*)",
"Bash(apt list:*)",
"Bash(curl:*)",
"Bash(git commit:*)"
"Bash(git commit:*)",
"Bash(sed:*)",
"Bash(grep:*)",
"Bash(pkill:*)",
"Bash(git add:*)"
],
"deny": []
}

View file

@ -5,9 +5,11 @@
"level_min": 1,
"level_max": 3,
"spawns": [
{"species": "Leafy", "spawn_rate": 0.35, "min_level": 1, "max_level": 2},
{"species": "Flamey", "spawn_rate": 0.35, "min_level": 1, "max_level": 2},
{"species": "Aqua", "spawn_rate": 0.3, "min_level": 1, "max_level": 2}
{"species": "Leafy", "spawn_rate": 0.25, "min_level": 1, "max_level": 2},
{"species": "Flamey", "spawn_rate": 0.25, "min_level": 1, "max_level": 2},
{"species": "Aqua", "spawn_rate": 0.25, "min_level": 1, "max_level": 2},
{"species": "Seedling", "spawn_rate": 0.15, "min_level": 1, "max_level": 2},
{"species": "Furry", "spawn_rate": 0.1, "min_level": 1, "max_level": 3}
]
},
{
@ -16,10 +18,13 @@
"level_min": 2,
"level_max": 6,
"spawns": [
{"species": "Leafy", "spawn_rate": 0.3, "min_level": 2, "max_level": 4},
{"species": "Vinewrap", "spawn_rate": 0.35, "min_level": 3, "max_level": 5},
{"species": "Bloomtail", "spawn_rate": 0.25, "min_level": 4, "max_level": 6},
{"species": "Flamey", "spawn_rate": 0.1, "min_level": 3, "max_level": 4}
{"species": "Leafy", "spawn_rate": 0.2, "min_level": 2, "max_level": 4},
{"species": "Vinewrap", "spawn_rate": 0.25, "min_level": 3, "max_level": 5},
{"species": "Bloomtail", "spawn_rate": 0.2, "min_level": 4, "max_level": 6},
{"species": "Flamey", "spawn_rate": 0.08, "min_level": 3, "max_level": 4},
{"species": "Fernwhisk", "spawn_rate": 0.15, "min_level": 3, "max_level": 5},
{"species": "Furry", "spawn_rate": 0.08, "min_level": 2, "max_level": 4},
{"species": "Mossrock", "spawn_rate": 0.04, "min_level": 5, "max_level": 6}
]
},
{
@ -28,8 +33,11 @@
"level_min": 4,
"level_max": 9,
"spawns": [
{"species": "Sparky", "spawn_rate": 0.6, "min_level": 4, "max_level": 7},
{"species": "Rocky", "spawn_rate": 0.4, "min_level": 5, "max_level": 8}
{"species": "Sparky", "spawn_rate": 0.35, "min_level": 4, "max_level": 7},
{"species": "Rocky", "spawn_rate": 0.25, "min_level": 5, "max_level": 8},
{"species": "Zapper", "spawn_rate": 0.25, "min_level": 4, "max_level": 6},
{"species": "Ember", "spawn_rate": 0.1, "min_level": 4, "max_level": 6},
{"species": "Swiftpaw", "spawn_rate": 0.05, "min_level": 6, "max_level": 8}
]
},
{
@ -38,8 +46,11 @@
"level_min": 6,
"level_max": 12,
"spawns": [
{"species": "Rocky", "spawn_rate": 0.7, "min_level": 6, "max_level": 10},
{"species": "Sparky", "spawn_rate": 0.3, "min_level": 7, "max_level": 9}
{"species": "Rocky", "spawn_rate": 0.4, "min_level": 6, "max_level": 10},
{"species": "Sparky", "spawn_rate": 0.2, "min_level": 7, "max_level": 9},
{"species": "Pebble", "spawn_rate": 0.25, "min_level": 6, "max_level": 8},
{"species": "Crystalback", "spawn_rate": 0.1, "min_level": 9, "max_level": 12},
{"species": "Voltmane", "spawn_rate": 0.05, "min_level": 10, "max_level": 12}
]
},
{
@ -48,9 +59,13 @@
"level_min": 10,
"level_max": 16,
"spawns": [
{"species": "Hydrox", "spawn_rate": 0.4, "min_level": 10, "max_level": 14},
{"species": "Rocky", "spawn_rate": 0.3, "min_level": 11, "max_level": 15},
{"species": "Sparky", "spawn_rate": 0.3, "min_level": 12, "max_level": 14}
{"species": "Hydrox", "spawn_rate": 0.25, "min_level": 10, "max_level": 14},
{"species": "Rocky", "spawn_rate": 0.2, "min_level": 11, "max_level": 15},
{"species": "Sparky", "spawn_rate": 0.15, "min_level": 12, "max_level": 14},
{"species": "Snowball", "spawn_rate": 0.2, "min_level": 10, "max_level": 12},
{"species": "Frostbite", "spawn_rate": 0.1, "min_level": 12, "max_level": 15},
{"species": "Bubblin", "spawn_rate": 0.05, "min_level": 10, "max_level": 13},
{"species": "Frostleaf", "spawn_rate": 0.05, "min_level": 14, "max_level": 16}
]
},
{
@ -59,9 +74,19 @@
"level_min": 15,
"level_max": 25,
"spawns": [
{"species": "Blazeon", "spawn_rate": 0.5, "min_level": 15, "max_level": 20},
{"species": "Hydrox", "spawn_rate": 0.3, "min_level": 16, "max_level": 22},
{"species": "Rocky", "spawn_rate": 0.2, "min_level": 18, "max_level": 25}
{"species": "Blazeon", "spawn_rate": 0.22, "min_level": 15, "max_level": 20},
{"species": "Hydrox", "spawn_rate": 0.18, "min_level": 16, "max_level": 22},
{"species": "Rocky", "spawn_rate": 0.13, "min_level": 18, "max_level": 25},
{"species": "Scorchclaw", "spawn_rate": 0.07, "min_level": 15, "max_level": 18},
{"species": "Tidalfin", "spawn_rate": 0.07, "min_level": 16, "max_level": 19},
{"species": "Infernowyrm", "spawn_rate": 0.05, "min_level": 20, "max_level": 25},
{"species": "Abyssal", "spawn_rate": 0.05, "min_level": 20, "max_level": 25},
{"species": "Thornking", "spawn_rate": 0.05, "min_level": 20, "max_level": 25},
{"species": "Stormcaller", "spawn_rate": 0.05, "min_level": 20, "max_level": 25},
{"species": "Steamvent", "spawn_rate": 0.04, "min_level": 19, "max_level": 23},
{"species": "Mountainlord", "spawn_rate": 0.03, "min_level": 22, "max_level": 25},
{"species": "Glaciarch", "spawn_rate": 0.03, "min_level": 22, "max_level": 25},
{"species": "Harmonix", "spawn_rate": 0.03, "min_level": 18, "max_level": 22}
]
}
]

View file

@ -8,7 +8,8 @@
"base_defense": 43,
"base_speed": 65,
"evolution_level": null,
"rarity": 1
"rarity": 1,
"emoji": "🔥"
},
{
"name": "Aqua",
@ -19,7 +20,8 @@
"base_defense": 65,
"base_speed": 43,
"evolution_level": null,
"rarity": 1
"rarity": 1,
"emoji": "💧"
},
{
"name": "Leafy",
@ -30,7 +32,8 @@
"base_defense": 49,
"base_speed": 45,
"evolution_level": null,
"rarity": 1
"rarity": 1,
"emoji": "🍃"
},
{
"name": "Sparky",
@ -41,7 +44,8 @@
"base_defense": 40,
"base_speed": 90,
"evolution_level": null,
"rarity": 2
"rarity": 2,
"emoji": "⚡"
},
{
"name": "Rocky",
@ -52,7 +56,8 @@
"base_defense": 100,
"base_speed": 25,
"evolution_level": null,
"rarity": 2
"rarity": 2,
"emoji": "🗿"
},
{
"name": "Blazeon",
@ -63,7 +68,8 @@
"base_defense": 60,
"base_speed": 95,
"evolution_level": null,
"rarity": 3
"rarity": 3,
"emoji": "🌋"
},
{
"name": "Hydrox",
@ -74,7 +80,8 @@
"base_defense": 90,
"base_speed": 60,
"evolution_level": null,
"rarity": 3
"rarity": 3,
"emoji": "🌊"
},
{
"name": "Vinewrap",
@ -85,7 +92,8 @@
"base_defense": 70,
"base_speed": 40,
"evolution_level": null,
"rarity": 2
"rarity": 2,
"emoji": "🌿"
},
{
"name": "Bloomtail",
@ -96,6 +104,295 @@
"base_defense": 50,
"base_speed": 80,
"evolution_level": null,
"rarity": 2
"rarity": 2,
"emoji": "🌺"
},
{
"name": "Ember",
"type1": "Fire",
"type2": null,
"base_hp": 42,
"base_attack": 50,
"base_defense": 40,
"base_speed": 68,
"evolution_level": null,
"rarity": 1,
"emoji": "✨"
},
{
"name": "Scorchclaw",
"type1": "Fire",
"type2": null,
"base_hp": 55,
"base_attack": 75,
"base_defense": 55,
"base_speed": 70,
"evolution_level": null,
"rarity": 2,
"emoji": "🐱"
},
{
"name": "Infernowyrm",
"type1": "Fire",
"type2": null,
"base_hp": 90,
"base_attack": 120,
"base_defense": 75,
"base_speed": 85,
"evolution_level": null,
"rarity": 4,
"emoji": "🐉"
},
{
"name": "Bubblin",
"type1": "Water",
"type2": null,
"base_hp": 48,
"base_attack": 40,
"base_defense": 60,
"base_speed": 52,
"evolution_level": null,
"rarity": 1,
"emoji": "🫧"
},
{
"name": "Tidalfin",
"type1": "Water",
"type2": null,
"base_hp": 65,
"base_attack": 60,
"base_defense": 70,
"base_speed": 80,
"evolution_level": null,
"rarity": 2,
"emoji": "🐬"
},
{
"name": "Abyssal",
"type1": "Water",
"type2": null,
"base_hp": 100,
"base_attack": 85,
"base_defense": 110,
"base_speed": 55,
"evolution_level": null,
"rarity": 4,
"emoji": "🐙"
},
{
"name": "Seedling",
"type1": "Grass",
"type2": null,
"base_hp": 40,
"base_attack": 35,
"base_defense": 50,
"base_speed": 40,
"evolution_level": null,
"rarity": 1,
"emoji": "🌱"
},
{
"name": "Fernwhisk",
"type1": "Grass",
"type2": null,
"base_hp": 50,
"base_attack": 55,
"base_defense": 65,
"base_speed": 75,
"evolution_level": null,
"rarity": 2,
"emoji": "🌾"
},
{
"name": "Thornking",
"type1": "Grass",
"type2": null,
"base_hp": 85,
"base_attack": 95,
"base_defense": 120,
"base_speed": 50,
"evolution_level": null,
"rarity": 4,
"emoji": "👑"
},
{
"name": "Zapper",
"type1": "Electric",
"type2": null,
"base_hp": 30,
"base_attack": 45,
"base_defense": 35,
"base_speed": 95,
"evolution_level": null,
"rarity": 1,
"emoji": "🐭"
},
{
"name": "Voltmane",
"type1": "Electric",
"type2": null,
"base_hp": 60,
"base_attack": 85,
"base_defense": 50,
"base_speed": 110,
"evolution_level": null,
"rarity": 3,
"emoji": "🐎"
},
{
"name": "Stormcaller",
"type1": "Electric",
"type2": null,
"base_hp": 75,
"base_attack": 130,
"base_defense": 60,
"base_speed": 125,
"evolution_level": null,
"rarity": 4,
"emoji": "🦅"
},
{
"name": "Pebble",
"type1": "Rock",
"type2": null,
"base_hp": 45,
"base_attack": 60,
"base_defense": 80,
"base_speed": 20,
"evolution_level": null,
"rarity": 1,
"emoji": "🪨"
},
{
"name": "Crystalback",
"type1": "Rock",
"type2": null,
"base_hp": 70,
"base_attack": 90,
"base_defense": 130,
"base_speed": 35,
"evolution_level": null,
"rarity": 3,
"emoji": "🐢"
},
{
"name": "Mountainlord",
"type1": "Rock",
"type2": null,
"base_hp": 120,
"base_attack": 110,
"base_defense": 150,
"base_speed": 20,
"evolution_level": null,
"rarity": 4,
"emoji": "⛰️"
},
{
"name": "Snowball",
"type1": "Ice",
"type2": null,
"base_hp": 40,
"base_attack": 35,
"base_defense": 55,
"base_speed": 45,
"evolution_level": null,
"rarity": 1,
"emoji": "☃️"
},
{
"name": "Frostbite",
"type1": "Ice",
"type2": null,
"base_hp": 55,
"base_attack": 65,
"base_defense": 70,
"base_speed": 85,
"evolution_level": null,
"rarity": 2,
"emoji": "🦨"
},
{
"name": "Glaciarch",
"type1": "Ice",
"type2": null,
"base_hp": 95,
"base_attack": 80,
"base_defense": 130,
"base_speed": 45,
"evolution_level": null,
"rarity": 4,
"emoji": "❄️"
},
{
"name": "Furry",
"type1": "Normal",
"type2": null,
"base_hp": 50,
"base_attack": 45,
"base_defense": 45,
"base_speed": 60,
"evolution_level": null,
"rarity": 1,
"emoji": "🐹"
},
{
"name": "Swiftpaw",
"type1": "Normal",
"type2": null,
"base_hp": 55,
"base_attack": 70,
"base_defense": 50,
"base_speed": 100,
"evolution_level": null,
"rarity": 2,
"emoji": "🐺"
},
{
"name": "Harmonix",
"type1": "Normal",
"type2": null,
"base_hp": 80,
"base_attack": 75,
"base_defense": 75,
"base_speed": 80,
"evolution_level": null,
"rarity": 3,
"emoji": "🎵"
},
{
"name": "Steamvent",
"type1": "Water",
"type2": "Fire",
"base_hp": 65,
"base_attack": 80,
"base_defense": 70,
"base_speed": 75,
"evolution_level": null,
"rarity": 3,
"emoji": "💨"
},
{
"name": "Mossrock",
"type1": "Grass",
"type2": "Rock",
"base_hp": 70,
"base_attack": 65,
"base_defense": 100,
"base_speed": 40,
"evolution_level": null,
"rarity": 3,
"emoji": "🍄"
},
{
"name": "Frostleaf",
"type1": "Ice",
"type2": "Grass",
"base_hp": 60,
"base_attack": 55,
"base_defense": 85,
"base_speed": 65,
"evolution_level": null,
"rarity": 3,
"emoji": "🧊"
}
]

View file

@ -57,6 +57,10 @@ class PetBotDebug:
loop.run_until_complete(self.validate_all_player_data())
print("✅ Player data validation complete")
print("🔄 Validating database integrity...")
loop.run_until_complete(self.validate_database_integrity())
print("✅ Database integrity validation complete")
print("🔄 Starting background validation task...")
self.start_background_validation(loop)
print("✅ Background validation started")
@ -142,6 +146,79 @@ class PetBotDebug:
print(f"❌ Error during player data validation: {e}")
# Don't fail startup if validation fails
async def validate_database_integrity(self):
"""Validate database integrity and fix common issues"""
try:
import aiosqlite
async with aiosqlite.connect(self.database.db_path) as db:
# Check for orphaned pets
cursor = await db.execute("""
SELECT COUNT(*) FROM pets
WHERE species_id NOT IN (SELECT id FROM pet_species)
""")
orphaned_pets = (await cursor.fetchone())[0]
if orphaned_pets > 0:
print(f"⚠️ Found {orphaned_pets} orphaned pets - fixing references...")
# This should not happen with the new startup logic, but just in case
await self.fix_orphaned_pets(db)
# Check player data accessibility
cursor = await db.execute("SELECT id, nickname FROM players")
players = await cursor.fetchall()
total_accessible_pets = 0
for player_id, nickname in players:
cursor = await db.execute("""
SELECT COUNT(*) FROM pets p
JOIN pet_species ps ON p.species_id = ps.id
WHERE p.player_id = ?
""", (player_id,))
accessible_pets = (await cursor.fetchone())[0]
total_accessible_pets += accessible_pets
if accessible_pets > 0:
print(f"{nickname}: {accessible_pets} pets accessible")
else:
# Get total pets for this player
cursor = await db.execute("SELECT COUNT(*) FROM pets WHERE player_id = ?", (player_id,))
total_pets = (await cursor.fetchone())[0]
if total_pets > 0:
print(f" ⚠️ {nickname}: {total_pets} pets but 0 accessible (orphaned)")
print(f"✅ Database integrity check: {total_accessible_pets} total accessible pets")
except Exception as e:
print(f"❌ Database integrity validation failed: {e}")
async def fix_orphaned_pets(self, db):
"""Fix orphaned pet references (emergency fallback)"""
try:
# This is a simplified fix - map common species names to current IDs
common_species = ['Flamey', 'Aqua', 'Leafy', 'Vinewrap', 'Bloomtail', 'Furry']
for species_name in common_species:
cursor = await db.execute("SELECT id FROM pet_species WHERE name = ?", (species_name,))
species_row = await cursor.fetchone()
if species_row:
current_id = species_row[0]
# Update any pets that might be referencing old IDs for this species
await db.execute("""
UPDATE pets SET species_id = ?
WHERE species_id NOT IN (SELECT id FROM pet_species)
AND species_id IN (
SELECT DISTINCT p.species_id FROM pets p
WHERE p.species_id NOT IN (SELECT id FROM pet_species)
LIMIT 1
)
""", (current_id,))
await db.commit()
print(" ✅ Orphaned pets fixed")
except Exception as e:
print(f" ❌ Failed to fix orphaned pets: {e}")
def start_background_validation(self, loop):
"""Start background task to periodically validate player data"""
import asyncio

View file

@ -36,10 +36,19 @@ class Database:
evolution_level INTEGER,
evolution_species_id INTEGER,
rarity INTEGER DEFAULT 1,
emoji TEXT,
FOREIGN KEY (evolution_species_id) REFERENCES pet_species (id)
)
""")
# Add emoji column if it doesn't exist (migration)
try:
await db.execute("ALTER TABLE pet_species ADD COLUMN emoji TEXT")
await db.commit()
except Exception:
# Column already exists or other error, which is fine
pass
await db.execute("""
CREATE TABLE IF NOT EXISTS pets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -452,7 +461,7 @@ class Database:
async with aiosqlite.connect(self.db_path) as db:
db.row_factory = aiosqlite.Row
query = """
SELECT p.*, ps.name as species_name, ps.type1, ps.type2
SELECT p.*, ps.name as species_name, ps.type1, ps.type2, ps.emoji
FROM pets p
JOIN pet_species ps ON p.species_id = ps.id
WHERE p.player_id = ?

View file

@ -35,19 +35,28 @@ class GameEngine:
species_data = json.load(f)
async with aiosqlite.connect(self.database.db_path) as db:
for species in species_data:
await db.execute("""
INSERT OR IGNORE INTO pet_species
(name, type1, type2, base_hp, base_attack, base_defense,
base_speed, evolution_level, rarity)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
species["name"], species["type1"], species.get("type2"),
species["base_hp"], species["base_attack"], species["base_defense"],
species["base_speed"], species.get("evolution_level"),
species.get("rarity", 1)
))
await db.commit()
# Check if species already exist to avoid re-inserting and changing IDs
cursor = await db.execute("SELECT COUNT(*) FROM pet_species")
existing_count = (await cursor.fetchone())[0]
if existing_count == 0:
# Only insert if no species exist to avoid ID conflicts
for species in species_data:
await db.execute("""
INSERT OR IGNORE INTO pet_species
(name, type1, type2, base_hp, base_attack, base_defense,
base_speed, evolution_level, rarity, emoji)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
species["name"], species["type1"], species.get("type2"),
species["base_hp"], species["base_attack"], species["base_defense"],
species["base_speed"], species.get("evolution_level"),
species.get("rarity", 1), species.get("emoji", "🐾")
))
await db.commit()
print(f"✅ Loaded {len(species_data)} pet species into database")
else:
print(f"✅ Found {existing_count} existing pet species - skipping reload to preserve IDs")
except FileNotFoundError:
await self.create_default_species()
@ -664,6 +673,38 @@ class GameEngine:
except Exception as e:
print(f"Error checking expired weather: {e}")
async def get_pet_emoji(self, species_name: str) -> str:
"""Get emoji for a pet species"""
try:
async with aiosqlite.connect(self.database.db_path) as db:
cursor = await db.execute(
"SELECT emoji FROM pet_species WHERE name = ?",
(species_name,)
)
row = await cursor.fetchone()
return row[0] if row and row[0] else "🐾"
except Exception:
return "🐾" # Default emoji if something goes wrong
def format_pet_name_with_emoji(self, pet_name: str, emoji: str = None, species_name: str = None) -> str:
"""Format pet name with emoji for display in IRC or web
Args:
pet_name: The pet's nickname or species name
emoji: Optional emoji to use (if None, will look up by species_name)
species_name: Species name for emoji lookup if emoji not provided
Returns:
Formatted string like "🔥 Flamey" or "🐉 Infernowyrm"
"""
if emoji:
return f"{emoji} {pet_name}"
elif species_name:
# This would need to be async, but for IRC we can use sync fallback
return f"🐾 {pet_name}" # Use default for now, can be enhanced later
else:
return f"🐾 {pet_name}"
async def shutdown(self):
"""Gracefully shutdown the game engine"""
print("🔄 Shutting down game engine...")

View file

@ -1,7 +1,7 @@
#!/bin/bash
#
# PetBot Startup Script
# Complete one-command startup for PetBot with all dependencies
# Complete one-command startup for PetBot with all dependencies and validation
#
# Usage: ./start_petbot.sh
#
@ -10,6 +10,8 @@ set -e # Exit on any error
echo "🐾 Starting PetBot..."
echo "===================="
echo "Version: $(date '+%Y-%m-%d %H:%M:%S') - Enhanced with startup validation"
echo ""
# Get script directory (works even if called from elsewhere)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -29,23 +31,22 @@ fi
echo "🔄 Activating virtual environment..."
source venv/bin/activate
# Upgrade pip to latest version
echo "🔄 Ensuring pip is up to date..."
pip install --upgrade pip -q
# Check if requirements are installed
echo "🔄 Checking dependencies..."
if ! python -c "import aiosqlite, irc, dotenv" 2>/dev/null; then
echo "📦 Installing/updating dependencies..."
if ! python -c "import aiosqlite, irc" 2>/dev/null; then
echo "📦 Installing/updating dependencies from requirements.txt..."
# Create requirements.txt if it doesn't exist
# Verify requirements.txt exists
if [ ! -f "requirements.txt" ]; then
echo "📝 Creating requirements.txt..."
cat > requirements.txt << EOF
aiosqlite>=0.17.0
irc>=20.3.0
python-dotenv>=0.19.0
aiohttp>=3.8.0
EOF
echo "❌ requirements.txt not found!"
echo "🔧 Please run install_prerequisites.sh first"
exit 1
fi
pip install --upgrade pip
pip install -r requirements.txt
echo "✅ Dependencies installed"
else
@ -60,44 +61,143 @@ sys.path.append('.')
try:
from src.database import Database
from src.game_engine import GameEngine
from src.game_engine import GameEngine
from src.rate_limiter import RateLimiter
from src.irc_connection_manager import IRCConnectionManager
from config import ADMIN_USER, IRC_CONFIG, RATE_LIMIT_CONFIG
print('✅ Core modules verified')
print(f' Admin user: {ADMIN_USER}')
print(f' IRC Channel: {IRC_CONFIG[\"channel\"]}')
print(f' Rate limiting: {\"Enabled\" if RATE_LIMIT_CONFIG[\"enabled\"] else \"Disabled\"}')
except ImportError as e:
print(f'❌ Module import error: {e}')
print('💡 Try running: ./install_prerequisites.sh')
sys.exit(1)
"
# Create data directory if it doesn't exist
if [ ! -d "data" ]; then
echo "📁 Creating data directory..."
mkdir -p data
# Create required directories
echo "🔄 Creating required directories..."
mkdir -p data backups logs
# Check configuration files
echo "🔄 Verifying configuration files..."
config_files=("config/pets.json" "config/locations.json" "config/items.json" "config/achievements.json" "config/gyms.json")
missing_configs=()
for config_file in "${config_files[@]}"; do
if [ ! -f "$config_file" ]; then
missing_configs+=("$config_file")
fi
done
if [ ${#missing_configs[@]} -gt 0 ]; then
echo "❌ Missing configuration files:"
for missing in "${missing_configs[@]}"; do
echo " - $missing"
done
echo "💡 These files are required for proper bot operation"
exit 1
fi
# Create backups directory if it doesn't exist
if [ ! -d "backups" ]; then
echo "📁 Creating backups directory..."
mkdir -p backups
fi
echo "✅ All configuration files present"
# Check if database exists, if not mention first-time setup
if [ ! -f "data/petbot.db" ]; then
# Database pre-flight check
if [ -f "data/petbot.db" ]; then
echo "🔄 Validating existing database..."
# Quick database validation
python3 -c "
import sqlite3
import sys
try:
conn = sqlite3.connect('data/petbot.db')
cursor = conn.cursor()
# Check essential tables exist
tables = ['players', 'pets', 'pet_species', 'locations']
for table in tables:
cursor.execute(f'SELECT COUNT(*) FROM {table}')
count = cursor.fetchone()[0]
print(f' ✅ {table}: {count} records')
conn.close()
print('✅ Database validation passed')
except Exception as e:
print(f'❌ Database validation failed: {e}')
print('💡 Database may be corrupted - backup and recreate if needed')
sys.exit(1)
"
else
echo " First-time setup detected - database will be created automatically"
fi
# Display startup information
# Pre-startup system test
echo "🔄 Running pre-startup system test..."
python3 -c "
import sys
sys.path.append('.')
try:
# Test basic imports and initialization
from run_bot_debug import PetBotDebug
# Create a test bot instance to verify everything loads
print(' 🔧 Testing bot initialization...')
bot = PetBotDebug()
print(' ✅ Bot instance created successfully')
print('✅ Pre-startup test passed')
except Exception as e:
print(f'❌ Pre-startup test failed: {e}')
import traceback
traceback.print_exc()
sys.exit(1)
"
# Display comprehensive startup information
echo ""
echo "🚀 Launching PetBot with Auto-Reconnect..."
echo "🌐 Web interface will be available at: http://localhost:8080"
echo "💬 IRC: Connecting to irc.libera.chat #petz"
echo "📊 Features: Rate limiting, auto-reconnect, web interface, team builder"
echo "🚀 Launching PetBot with Enhanced Features..."
echo "============================================="
echo "🌐 Web interface: http://localhost:8080"
echo "📱 Public access: http://petz.rdx4.com/"
echo "💬 IRC Server: irc.libera.chat"
echo "📢 IRC Channel: #petz"
echo "👤 Admin User: $(python3 -c 'from config import ADMIN_USER; print(ADMIN_USER)')"
echo ""
echo "🎮 Features Available:"
echo " ✅ 33 Pet Species with Emojis"
echo " ✅ 6 Exploration Locations"
echo " ✅ Team Builder with PIN Verification"
echo " ✅ Achievement System"
echo " ✅ Gym Battles"
echo " ✅ Weather System"
echo " ✅ Rate Limiting & Anti-Abuse"
echo " ✅ Auto-Reconnection"
echo " ✅ Startup Data Validation"
echo " ✅ Background Monitoring"
echo ""
echo "🔧 Technical Details:"
echo " 📊 Database: SQLite with validation"
echo " 🌐 Webserver: Integrated with bot instance"
echo " 🛡️ Security: Rate limiting enabled"
echo " 🔄 Reliability: Auto-reconnect on failure"
echo " 📈 Monitoring: Background validation every 30min"
echo ""
echo "Press Ctrl+C to stop the bot"
echo "===================="
echo "============================================="
echo ""
# Launch the bot
exec python run_bot_with_reconnect.py
# Launch the appropriate bot based on what's available
if [ -f "run_bot_with_reconnect.py" ]; then
echo "🚀 Starting with auto-reconnect support..."
exec python run_bot_with_reconnect.py
elif [ -f "run_bot_debug.py" ]; then
echo "🚀 Starting in debug mode..."
exec python run_bot_debug.py
else
echo "❌ No bot startup file found!"
echo "💡 Expected: run_bot_with_reconnect.py or run_bot_debug.py"
exit 1
fi

View file

@ -2222,7 +2222,9 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
async with aiosqlite.connect(database.db_path) as db:
# Get all pet species with evolution information (no duplicates)
cursor = await db.execute("""
SELECT DISTINCT ps.*,
SELECT DISTINCT ps.id, ps.name, ps.type1, ps.type2, ps.base_hp, ps.base_attack,
ps.base_defense, ps.base_speed, ps.evolution_level, ps.evolution_species_id,
ps.rarity, ps.emoji,
evolve_to.name as evolves_to_name,
(SELECT COUNT(*) FROM location_spawns ls WHERE ls.species_id = ps.id) as location_count
FROM pet_species ps
@ -2237,8 +2239,8 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
'id': row[0], 'name': row[1], 'type1': row[2], 'type2': row[3],
'base_hp': row[4], 'base_attack': row[5], 'base_defense': row[6],
'base_speed': row[7], 'evolution_level': row[8],
'evolution_species_id': row[9], 'rarity': row[10],
'evolves_to_name': row[11], 'location_count': row[12]
'evolution_species_id': row[9], 'rarity': row[10], 'emoji': row[11],
'evolves_to_name': row[12], 'location_count': row[13]
}
pets.append(pet_dict)
@ -2359,7 +2361,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
petdex_html += f"""
<div class="pet-card" style="border-left: 4px solid {rarity_color};">
<div class="pet-header">
<h3 style="color: {rarity_color};">{pet['name']}</h3>
<h3 style="color: {rarity_color};">{pet.get('emoji', '🐾')} {pet['name']}</h3>
<span class="type-badge">{type_str}</span>
</div>
<div class="pet-stats">
@ -2478,7 +2480,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
# Get player pets
cursor = await db.execute("""
SELECT p.*, ps.name as species_name, ps.type1, ps.type2
SELECT p.*, ps.name as species_name, ps.type1, ps.type2, ps.emoji
FROM pets p
JOIN pet_species ps ON p.species_id = ps.id
WHERE p.player_id = ?
@ -2493,7 +2495,8 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
'hp': row[6], 'max_hp': row[7], 'attack': row[8],
'defense': row[9], 'speed': row[10], 'happiness': row[11],
'caught_at': row[12], 'is_active': bool(row[13]), # Convert to proper boolean
'team_order': row[14], 'species_name': row[15], 'type1': row[16], 'type2': row[17]
'team_order': row[14], 'species_name': row[15], 'type1': row[16], 'type2': row[17],
'emoji': row[18] if row[18] else '🐾' # Add emoji support
}
pets.append(pet_dict)
@ -2717,7 +2720,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
pets_html += f"""
<tr>
<td class="{status_class}">{status}</td>
<td><strong>{name}</strong></td>
<td><strong>{pet.get('emoji', '🐾')} {name}</strong></td>
<td>{pet['species_name']}</td>
<td><span class="type-badge">{type_str}</span></td>
<td>{pet['level']}</td>
@ -3456,6 +3459,9 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
if pet['type2']:
type_str += f"/{pet['type2']}"
# Get emoji for the pet species
emoji = pet.get('emoji', '🐾') # Default to paw emoji if none specified
# Debug logging
print(f"Making pet card for {name} (ID: {pet['id']}): is_active={pet['is_active']}, passed_is_active={is_active}, status_class={status_class}, team_order={pet.get('team_order', 'None')}")
@ -3466,7 +3472,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
return f"""
<div class="pet-card {status_class}" draggable="true" data-pet-id="{pet['id']}" data-active="{str(is_active).lower()}" data-team-order="{pet.get('team_order', '')}">
<div class="pet-header">
<h4 class="pet-name">{name}</h4>
<h4 class="pet-name">{emoji} {name}</h4>
<div class="status-badge">{status}</div>
</div>
<div class="pet-species">Level {pet['level']} {pet['species_name']}</div>