# app.py import os import sys import time import json import threading import subprocess import signal import socket import struct from datetime import datetime from flask import Flask, jsonify, request from flask_cors import CORS import logging # Disable Flask logging log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) app = Flask(__name__) CORS(app) # ==================== CONFIGURATION ==================== SERVER_HOST = "orbitmc.progamer.me" SERVER_PORT = 40675 SERVER_VERSION = "1.21.8" BOT_NAMES = ["Alfha_lag", "Bigboybloom", "Kitcat"] ROTATION_DURATION = 3600 # 1 hour # ==================== STORAGE ==================== bots = {} bot_processes = {} current_bot_index = 0 rotation_start_time = None server_status = { "online": False, "players": "0/0", "latency": 0, "last_check": None, "motd": "" } # ==================== BOT SCRIPT ==================== BOT_SCRIPT = """ const mineflayer = require('mineflayer'); const { Vec3 } = require('vec3'); const botName = process.argv[2]; const host = process.argv[3]; const port = parseInt(process.argv[4]) || 25565; const version = process.argv[5] || false; let isConnected = false; let circleInterval = null; let chatInterval = null; let angle = 0; let centerPos = null; let reconnectTimeout = null; console.log(JSON.stringify({event:'starting', name:botName, time:Date.now()})); function createBot() { const bot = mineflayer.createBot({ host: host, port: port, username: botName, auth: 'offline', version: version === 'false' ? false : version, hideErrors: false, checkTimeoutInterval: 30000, keepAlive: true, skipValidation: true }); function startCircularMovement() { if (circleInterval) clearInterval(circleInterval); setTimeout(() => { if (!bot.entity || !isConnected) return; centerPos = bot.entity.position.clone(); angle = 0; console.log(JSON.stringify({ event:'movement_started', name:botName, center: { x: Math.floor(centerPos.x), y: Math.floor(centerPos.y), z: Math.floor(centerPos.z) } })); circleInterval = setInterval(() => { if (!bot.entity || !isConnected || !centerPos) return; try { const radius = 4; angle += 0.03; const targetX = centerPos.x + Math.cos(angle) * radius; const targetZ = centerPos.z + Math.sin(angle) * radius; const dx = targetX - bot.entity.position.x; const dz = targetZ - bot.entity.position.z; const yaw = Math.atan2(-dx, -dz); bot.look(yaw, 0, true); bot.setControlState('forward', true); // Send position every 2 seconds if (Math.floor(angle * 100) % 200 === 0) { const pos = bot.entity.position; console.log(JSON.stringify({ event:'position', name:botName, x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z), health: bot.health, food: bot.food })); } } catch(err) { console.log(JSON.stringify({event:'movement_error', name:botName, error:err.message})); } }, 100); }, 3000); } function stopMovement() { if (circleInterval) { clearInterval(circleInterval); circleInterval = null; } if (chatInterval) { clearInterval(chatInterval); chatInterval = null; } try { bot.setControlState('forward', false); } catch(err) {} } bot.once('spawn', () => { console.log(JSON.stringify({ event:'connected', name:botName, time:Date.now(), gamemode: bot.game.gameMode, dimension: bot.game.dimension })); isConnected = true; startCircularMovement(); // Random chat chatInterval = setInterval(() => { if (isConnected && Math.random() > 0.8) { try { const messages = ['Hello!', 'Im monitoring', 'All good here', ':)', 'Server looking good']; bot.chat(messages[Math.floor(Math.random() * messages.length)]); } catch(err) {} } }, 180000); // Every 3 minutes }); bot.on('respawn', () => { console.log(JSON.stringify({event:'respawned', name:botName, time:Date.now()})); stopMovement(); centerPos = null; startCircularMovement(); }); bot.on('death', () => { console.log(JSON.stringify({event:'death', name:botName, time:Date.now()})); stopMovement(); centerPos = null; }); bot.on('health', () => { if (bot.health <= 0) { console.log(JSON.stringify({event:'died', name:botName})); } }); bot.on('kicked', (reason) => { console.log(JSON.stringify({ event:'kicked', name:botName, reason: JSON.stringify(reason), time:Date.now() })); isConnected = false; stopMovement(); }); bot.on('error', (err) => { console.log(JSON.stringify({ event:'error', name:botName, error:err.message, time:Date.now() })); }); bot.on('end', (reason) => { console.log(JSON.stringify({ event:'disconnected', name:botName, reason: reason || 'unknown', time:Date.now() })); isConnected = false; stopMovement(); // Auto reconnect after 5 seconds if (reconnectTimeout) clearTimeout(reconnectTimeout); reconnectTimeout = setTimeout(() => { console.log(JSON.stringify({event:'reconnecting', name:botName})); createBot(); }, 5000); }); bot.on('message', (message) => { const msg = message.toString(); if (msg.includes(botName)) { console.log(JSON.stringify({event:'mentioned', name:botName, message:msg})); } }); // Heartbeat setInterval(() => { if (isConnected && bot.entity) { console.log(JSON.stringify({ event:'heartbeat', name:botName, health: bot.health || 0, food: bot.food || 0, time:Date.now() })); } }, 30000); process.on('SIGTERM', () => { stopMovement(); bot.quit(); process.exit(0); }); process.on('SIGINT', () => { stopMovement(); bot.quit(); process.exit(0); }); } createBot(); """ def write_bot_script(): """Write bot.js to disk""" try: with open('/tmp/bot.js', 'w') as f: f.write(BOT_SCRIPT) print("āœ… Bot script written to /tmp/bot.js") return True except Exception as e: print(f"āŒ Failed to write bot script: {e}") return False def ping_minecraft_server(): """Ping Minecraft server and get real status""" global server_status try: start_time = time.time() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) # Connect sock.connect((SERVER_HOST, SERVER_PORT)) # Handshake packet handshake = b'\x00\x00' + len(SERVER_HOST).to_bytes(1, 'big') + SERVER_HOST.encode('utf-8') handshake += SERVER_PORT.to_bytes(2, 'big') + b'\x01' handshake = len(handshake).to_bytes(1, 'big') + handshake # Status request status_request = b'\x01\x00' sock.send(handshake) sock.send(status_request) # Read response response_length = sock.recv(1024) response_data = sock.recv(4096) latency = int((time.time() - start_time) * 1000) sock.close() # Parse JSON response try: # Skip packet ID json_start = response_data.find(b'{') if json_start != -1: json_data = response_data[json_start:].decode('utf-8', errors='ignore') server_info = json.loads(json_data) players = server_info.get('players', {}) online = players.get('online', 0) max_players = players.get('max', 0) server_status.update({ "online": True, "players": f"{online}/{max_players}", "latency": latency, "motd": server_info.get('description', {}).get('text', 'Minecraft Server'), "last_check": datetime.now().isoformat() }) else: server_status.update({ "online": True, "players": "?/?", "latency": latency, "last_check": datetime.now().isoformat() }) except: server_status.update({ "online": True, "players": "?/?", "latency": latency, "last_check": datetime.now().isoformat() }) except Exception as e: server_status.update({ "online": False, "players": "0/0", "latency": 0, "motd": str(e), "last_check": datetime.now().isoformat() }) def start_bot(name): """Start a bot process""" if name in bot_processes: try: proc = bot_processes[name] proc.terminate() proc.wait(timeout=3) except: pass try: proc = subprocess.Popen( ['node', '/tmp/bot.js', name, SERVER_HOST, str(SERVER_PORT), SERVER_VERSION], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1 ) bot_processes[name] = proc if name not in bots: bots[name] = { 'status': 'connecting', 'start_time': time.time(), 'deaths': 0, 'reconnects': 0, 'position': {'x': 0, 'y': 0, 'z': 0}, 'health': 20, 'food': 20 } else: bots[name]['status'] = 'connecting' bots[name]['start_time'] = time.time() threading.Thread(target=monitor_bot, args=(name,), daemon=True).start() print(f"šŸš€ Started bot: {name}") return True except Exception as e: print(f"āŒ Error starting bot {name}: {e}") return False def stop_bot(name): """Stop a bot""" if name in bot_processes: try: proc = bot_processes[name] proc.terminate() proc.wait(timeout=3) del bot_processes[name] if name in bots: bots[name]['status'] = 'offline' print(f"ā¹ļø Stopped bot: {name}") except Exception as e: print(f"āš ļø Error stopping bot {name}: {e}") def monitor_bot(name): """Monitor bot output""" if name not in bot_processes: return proc = bot_processes[name] try: while proc.poll() is None: line = proc.stdout.readline() if not line: continue try: data = json.loads(line.strip()) event = data.get('event') if name in bots: if event in ['connected', 'heartbeat']: bots[name]['status'] = 'online' bots[name]['health'] = data.get('health', 20) bots[name]['food'] = data.get('food', 20) elif event == 'position': bots[name]['position'] = { 'x': data.get('x', 0), 'y': data.get('y', 0), 'z': data.get('z', 0) } bots[name]['health'] = data.get('health', 20) bots[name]['food'] = data.get('food', 20) elif event in ['death', 'died']: bots[name]['deaths'] += 1 elif event == 'reconnecting': bots[name]['reconnects'] += 1 bots[name]['status'] = 'connecting' elif event in ['disconnected', 'error', 'kicked']: bots[name]['status'] = 'offline' if event in ['connected', 'disconnected', 'kicked', 'error', 'death']: print(f"[{name}] {event}") except json.JSONDecodeError: pass except: pass if name in bots: bots[name]['status'] = 'offline' def rotation_manager(): """Manage bot rotation""" global current_bot_index, rotation_start_time while True: try: current_bot = BOT_NAMES[current_bot_index] if rotation_start_time is None or time.time() - rotation_start_time >= ROTATION_DURATION: # Stop all bots for name in BOT_NAMES: stop_bot(name) time.sleep(3) # Start new bot start_bot(current_bot) rotation_start_time = time.time() print(f"šŸ”„ Rotation: {current_bot} is now active") # Next bot current_bot_index = (current_bot_index + 1) % len(BOT_NAMES) time.sleep(5) except Exception as e: print(f"āš ļø Rotation error: {e}") time.sleep(10) def server_ping_loop(): """Continuously ping server""" while True: try: ping_minecraft_server() time.sleep(1) except Exception as e: print(f"āš ļø Server ping error: {e}") time.sleep(2) def initialize(): """Initialize bots""" for name in BOT_NAMES: bots[name] = { 'status': 'offline', 'start_time': 0, 'deaths': 0, 'reconnects': 0, 'position': {'x': 0, 'y': 0, 'z': 0}, 'health': 20, 'food': 20 } # ==================== API ROUTES ==================== @app.route('/') def index(): """Serve HTML""" return open('index.html').read() @app.route('/api/status') def api_status(): """Get full status""" current_bot = BOT_NAMES[(current_bot_index - 1) % len(BOT_NAMES)] if rotation_start_time else BOT_NAMES[current_bot_index] next_bot = BOT_NAMES[current_bot_index] elapsed = int(time.time() - rotation_start_time) if rotation_start_time else 0 remaining = max(0, ROTATION_DURATION - elapsed) bot_list = [] for name in BOT_NAMES: bot = bots.get(name, {}) uptime = int(time.time() - bot.get('start_time', time.time())) if bot.get('status') == 'online' else 0 bot_list.append({ 'name': name, 'status': bot.get('status', 'offline'), 'is_active': name == current_bot, 'uptime': uptime, 'deaths': bot.get('deaths', 0), 'reconnects': bot.get('reconnects', 0), 'position': bot.get('position', {'x': 0, 'y': 0, 'z': 0}), 'health': bot.get('health', 20), 'food': bot.get('food', 20) }) return jsonify({ 'server_info': { 'host': SERVER_HOST, 'port': SERVER_PORT, 'version': SERVER_VERSION }, 'server_status': server_status, 'rotation': { 'current_bot': current_bot, 'next_bot': next_bot, 'elapsed': elapsed, 'remaining': remaining, 'queue': BOT_NAMES }, 'bots': bot_list }) @app.route('/api/next_rotation', methods=['POST']) def api_next_rotation(): """Force next rotation""" global rotation_start_time rotation_start_time = 0 return jsonify({'success': True}) def cleanup(sig=None, frame=None): """Clean shutdown""" print("\nšŸ›‘ Shutting down...") for name in BOT_NAMES: stop_bot(name) sys.exit(0) if __name__ == '__main__': signal.signal(signal.SIGINT, cleanup) signal.signal(signal.SIGTERM, cleanup) print("=" * 60) print("šŸŽ® MINECRAFT BOT MANAGER") print("=" * 60) if not write_bot_script(): print("āŒ Failed to write bot script!") sys.exit(1) print(f"šŸ“” Server: {SERVER_HOST}:{SERVER_PORT}") print(f"šŸ¤– Bots: {', '.join(BOT_NAMES)}") print(f"ā±ļø Rotation: {ROTATION_DURATION//60} minutes per bot") print("=" * 60) initialize() # Start threads threading.Thread(target=rotation_manager, daemon=True).start() threading.Thread(target=server_ping_loop, daemon=True).start() print("🌐 Dashboard: http://localhost:7860") print("=" * 60) app.run(host='0.0.0.0', port=7860, debug=False, use_reloader=False)