PowerBot / app.py
OrbitMC's picture
Update app.py
4198961 verified
# 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)