Major Features Added: - Complete token bucket rate limiting for IRC commands and web interface - Per-user rate tracking with category-based limits (Basic, Gameplay, Management, Admin, Web) - Admin commands for rate limit management (\!rate_stats, \!rate_user, \!rate_unban, \!rate_reset) - Automatic violation tracking and temporary bans with cleanup - Global item spawn multiplier system with 75% spawn rate reduction - Central admin configuration system (config.py) - One-command bot startup script (start_petbot.sh) Rate Limiting: - Token bucket algorithm with burst capacity and refill rates - Category limits: Basic (20/min), Gameplay (10/min), Management (5/min), Web (60/min) - Graceful violation handling with user-friendly error messages - Admin exemption and override capabilities - Background cleanup of old violations and expired bans Item Spawn System: - Added global_spawn_multiplier to config/items.json for easy adjustment - Reduced all individual spawn rates by 75% (multiplied by 0.25) - Admins can fine-tune both global multiplier and individual item rates - Game engine integration applies multiplier to all spawn calculations Infrastructure: - Single admin user configuration in config.py - Enhanced startup script with dependency management and verification - Updated documentation and help system with rate limiting guide - Comprehensive test suite for rate limiting functionality Security: - Rate limiting protects against command spam and abuse - IP-based tracking for web interface requests - Proper error handling and status codes (429 for rate limits) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
344 lines
No EOL
10 KiB
Python
344 lines
No EOL
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
PetBot Prerequisites Installation Script
|
|
This script installs all required Python packages and dependencies for running PetBot.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
import importlib.util
|
|
from pathlib import Path
|
|
|
|
|
|
def print_header():
|
|
"""Print installation header."""
|
|
print("=" * 60)
|
|
print("🐾 PetBot Prerequisites Installation Script")
|
|
print("=" * 60)
|
|
print()
|
|
|
|
|
|
def check_python_version():
|
|
"""Check if Python version is compatible."""
|
|
print("🔍 Checking Python version...")
|
|
|
|
version = sys.version_info
|
|
required_major = 3
|
|
required_minor = 7
|
|
|
|
if version.major < required_major or (version.major == required_major and version.minor < required_minor):
|
|
print(f"❌ Python {required_major}.{required_minor}+ required. Current version: {version.major}.{version.minor}.{version.micro}")
|
|
print("Please upgrade Python and try again.")
|
|
return False
|
|
|
|
print(f"✅ Python {version.major}.{version.minor}.{version.micro} is compatible")
|
|
return True
|
|
|
|
|
|
def check_pip_available():
|
|
"""Check if pip is available."""
|
|
print("\n🔍 Checking pip availability...")
|
|
|
|
try:
|
|
import pip
|
|
print("✅ pip is available")
|
|
return True
|
|
except ImportError:
|
|
pass
|
|
|
|
# Try alternative ways to check pip
|
|
try:
|
|
result = subprocess.run([sys.executable, "-m", "pip", "--version"],
|
|
capture_output=True, text=True, check=True)
|
|
print(f"✅ pip is available: {result.stdout.strip()}")
|
|
return True
|
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
print("❌ pip is not available")
|
|
print("Please install pip first:")
|
|
print(" - On Ubuntu/Debian: sudo apt install python3-pip")
|
|
print(" - On CentOS/RHEL: sudo yum install python3-pip")
|
|
print(" - On macOS: brew install python3")
|
|
return False
|
|
|
|
|
|
def check_package_installed(package_name):
|
|
"""Check if a package is already installed."""
|
|
try:
|
|
importlib.import_module(package_name)
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
|
|
def install_package(package_spec):
|
|
"""Install a package using pip."""
|
|
print(f"📦 Installing {package_spec}...")
|
|
|
|
try:
|
|
result = subprocess.run([
|
|
sys.executable, "-m", "pip", "install", package_spec
|
|
], capture_output=True, text=True, check=True)
|
|
|
|
print(f"✅ Successfully installed {package_spec}")
|
|
return True
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"❌ Failed to install {package_spec}")
|
|
print(f"Error output: {e.stderr}")
|
|
return False
|
|
|
|
|
|
def install_requirements():
|
|
"""Install packages from requirements.txt if it exists."""
|
|
print("\n📋 Installing packages from requirements.txt...")
|
|
|
|
requirements_file = Path("requirements.txt")
|
|
if not requirements_file.exists():
|
|
print("❌ requirements.txt not found")
|
|
return False
|
|
|
|
try:
|
|
result = subprocess.run([
|
|
sys.executable, "-m", "pip", "install", "-r", "requirements.txt"
|
|
], capture_output=True, text=True, check=True)
|
|
|
|
print("✅ Successfully installed packages from requirements.txt")
|
|
return True
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
print("❌ Failed to install from requirements.txt")
|
|
print(f"Error output: {e.stderr}")
|
|
return False
|
|
|
|
|
|
def install_individual_packages():
|
|
"""Install individual packages if requirements.txt fails."""
|
|
print("\n📦 Installing individual packages...")
|
|
|
|
packages = [
|
|
("irc", "irc>=20.3.0", "IRC client library"),
|
|
("aiosqlite", "aiosqlite>=0.19.0", "Async SQLite database interface"),
|
|
("dotenv", "python-dotenv>=1.0.0", "Environment variable loading")
|
|
]
|
|
|
|
results = []
|
|
|
|
for import_name, package_spec, description in packages:
|
|
print(f"\n🔍 Checking {import_name} ({description})...")
|
|
|
|
if check_package_installed(import_name):
|
|
print(f"✅ {import_name} is already installed")
|
|
results.append(True)
|
|
else:
|
|
print(f"❌ {import_name} is not installed")
|
|
success = install_package(package_spec)
|
|
results.append(success)
|
|
|
|
return all(results)
|
|
|
|
|
|
def verify_installation():
|
|
"""Verify that all required packages are installed correctly."""
|
|
print("\n🔍 Verifying installation...")
|
|
|
|
test_imports = [
|
|
("irc", "IRC client library"),
|
|
("aiosqlite", "Async SQLite database"),
|
|
("dotenv", "Environment variable loading"),
|
|
("asyncio", "Async programming (built-in)"),
|
|
("sqlite3", "SQLite database (built-in)"),
|
|
("json", "JSON handling (built-in)"),
|
|
("socket", "Network communication (built-in)"),
|
|
("threading", "Threading (built-in)")
|
|
]
|
|
|
|
all_good = True
|
|
|
|
for module_name, description in test_imports:
|
|
try:
|
|
importlib.import_module(module_name)
|
|
print(f"✅ {module_name}: {description}")
|
|
except ImportError as e:
|
|
print(f"❌ {module_name}: {description} - {e}")
|
|
all_good = False
|
|
|
|
return all_good
|
|
|
|
|
|
def check_directory_structure():
|
|
"""Check if we're in the correct directory and required files exist."""
|
|
print("\n🔍 Checking directory structure...")
|
|
|
|
required_files = [
|
|
"requirements.txt",
|
|
"src/database.py",
|
|
"src/game_engine.py",
|
|
"src/bot.py",
|
|
"webserver.py",
|
|
"run_bot_debug.py"
|
|
]
|
|
|
|
missing_files = []
|
|
|
|
for file_path in required_files:
|
|
if not Path(file_path).exists():
|
|
missing_files.append(file_path)
|
|
|
|
if missing_files:
|
|
print("❌ Missing required files:")
|
|
for file_path in missing_files:
|
|
print(f" - {file_path}")
|
|
print("\nPlease make sure you're running this script from the PetBot project directory.")
|
|
return False
|
|
|
|
print("✅ All required files found")
|
|
return True
|
|
|
|
|
|
def create_data_directory():
|
|
"""Create data directory if it doesn't exist."""
|
|
print("\n🔍 Checking data directory...")
|
|
|
|
data_dir = Path("data")
|
|
if not data_dir.exists():
|
|
try:
|
|
data_dir.mkdir()
|
|
print("✅ Created data directory")
|
|
except Exception as e:
|
|
print(f"❌ Failed to create data directory: {e}")
|
|
return False
|
|
else:
|
|
print("✅ Data directory already exists")
|
|
|
|
return True
|
|
|
|
|
|
def create_backups_directory():
|
|
"""Create backups directory if it doesn't exist."""
|
|
print("\n🔍 Checking backups directory...")
|
|
|
|
backups_dir = Path("backups")
|
|
if not backups_dir.exists():
|
|
try:
|
|
backups_dir.mkdir()
|
|
print("✅ Created backups directory")
|
|
except Exception as e:
|
|
print(f"❌ Failed to create backups directory: {e}")
|
|
return False
|
|
else:
|
|
print("✅ Backups directory already exists")
|
|
|
|
return True
|
|
|
|
|
|
def test_basic_imports():
|
|
"""Test basic functionality by importing key modules."""
|
|
print("\n🧪 Testing basic imports...")
|
|
|
|
try:
|
|
# Test database import
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
from src.database import Database
|
|
print("✅ Database module imports successfully")
|
|
|
|
# Test connection manager import
|
|
from src.irc_connection_manager import IRCConnectionManager
|
|
print("✅ IRC connection manager imports successfully")
|
|
|
|
# Test backup manager import
|
|
from src.backup_manager import BackupManager
|
|
print("✅ Backup manager imports successfully")
|
|
|
|
return True
|
|
|
|
except ImportError as e:
|
|
print(f"❌ Import test failed: {e}")
|
|
return False
|
|
|
|
|
|
def show_next_steps():
|
|
"""Show next steps after successful installation."""
|
|
print("\n🎉 Installation completed successfully!")
|
|
print("\n📋 Next steps:")
|
|
print("1. Review and update configuration files in the config/ directory")
|
|
print("2. Test the bot with: python3 run_bot_debug.py")
|
|
print("3. Or use the new reconnection system: python3 run_bot_with_reconnect.py")
|
|
print("4. Check the web interface at: http://localhost:8080")
|
|
print("5. Review CLAUDE.md for development guidelines")
|
|
print("\n🔧 Available test commands:")
|
|
print(" python3 test_backup_simple.py - Test backup system")
|
|
print(" python3 test_reconnection.py - Test IRC reconnection")
|
|
print("\n📚 Documentation:")
|
|
print(" README.md - Project overview")
|
|
print(" CLAUDE.md - Development guide")
|
|
print(" TODO.md - Current status")
|
|
print(" issues.txt - Security audit findings")
|
|
print(" BACKUP_SYSTEM_INTEGRATION.md - Backup system guide")
|
|
|
|
|
|
def main():
|
|
"""Main installation function."""
|
|
print_header()
|
|
|
|
# Check prerequisites
|
|
if not check_python_version():
|
|
return False
|
|
|
|
if not check_pip_available():
|
|
return False
|
|
|
|
if not check_directory_structure():
|
|
return False
|
|
|
|
# Create directories
|
|
if not create_data_directory():
|
|
return False
|
|
|
|
if not create_backups_directory():
|
|
return False
|
|
|
|
# Install packages
|
|
success = install_requirements()
|
|
|
|
if not success:
|
|
print("\n⚠️ requirements.txt installation failed, trying individual packages...")
|
|
success = install_individual_packages()
|
|
|
|
if not success:
|
|
print("\n❌ Package installation failed")
|
|
return False
|
|
|
|
# Verify installation
|
|
if not verify_installation():
|
|
print("\n❌ Installation verification failed")
|
|
return False
|
|
|
|
# Test imports
|
|
if not test_basic_imports():
|
|
print("\n❌ Basic import tests failed")
|
|
return False
|
|
|
|
# Show next steps
|
|
show_next_steps()
|
|
|
|
return True
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
success = main()
|
|
if success:
|
|
print("\n✅ PetBot prerequisites installation completed successfully!")
|
|
sys.exit(0)
|
|
else:
|
|
print("\n❌ Installation failed. Please check the errors above.")
|
|
sys.exit(1)
|
|
except KeyboardInterrupt:
|
|
print("\n🛑 Installation interrupted by user")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"\n💥 Unexpected error: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1) |