PowerBot / server.js
Tokipo's picture
Create server.js
8bb61b2 verified
raw
history blame
8.08 kB
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const mineflayer = require('mineflayer');
const fetch = require('node-fetch');
const { parse } = require('csv-parse/sync');
const path = require('path');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
// Google Sheets configuration
const SHEET_ID = '109roJQr-Y4YCLTkCqaK6iwShC-Dr2Jb-hB0qE2phNqQ';
const SHEET_URL = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/export?format=csv`;
// Bot management
const bots = new Map();
const botLastReconnect = new Map();
const serverBotMap = new Map(); // Track one bot per server
class BotManager {
constructor(botName, ip, port, version) {
this.botName = botName;
this.ip = ip;
this.port = port;
this.version = version || '1.20.1';
this.bot = null;
this.status = 'Disconnected';
this.deathCount = 0;
this.disconnectTime = null;
this.isManualDisconnect = false;
this.startTime = Date.now();
}
async connect() {
try {
// Check if server already has a bot
const serverKey = `${this.ip}:${this.port}`;
const existingBot = serverBotMap.get(serverKey);
if (existingBot && existingBot !== this.botName) {
this.status = 'Server already has a bot';
return false;
}
this.status = 'Connecting...';
this.bot = mineflayer.createBot({
host: this.ip,
port: parseInt(this.port),
username: this.botName,
auth: 'offline',
version: this.version,
hideErrors: true,
checkTimeoutInterval: 30000
});
// Register this bot for the server
serverBotMap.set(serverKey, this.botName);
this.bot.once('spawn', () => {
this.status = 'Connected';
this.disconnectTime = null;
console.log(`Bot ${this.botName} spawned on ${this.ip}:${this.port}`);
// Start AFK behavior
this.startAFK();
});
this.bot.on('death', () => {
this.deathCount++;
console.log(`Bot ${this.botName} died. Total deaths: ${this.deathCount}`);
});
this.bot.on('kicked', (reason) => {
console.log(`Bot ${this.botName} was kicked: ${reason}`);
this.handleDisconnect();
});
this.bot.on('error', (err) => {
console.error(`Bot ${this.botName} error:`, err.message);
});
this.bot.on('end', () => {
this.handleDisconnect();
});
return true;
} catch (error) {
console.error(`Failed to connect bot ${this.botName}:`, error);
this.status = 'Connection Failed';
return false;
}
}
handleDisconnect() {
const serverKey = `${this.ip}:${this.port}`;
if (serverBotMap.get(serverKey) === this.botName) {
serverBotMap.delete(serverKey);
}
this.status = 'Disconnected';
this.disconnectTime = Date.now();
this.bot = null;
if (!this.isManualDisconnect) {
console.log(`Bot ${this.botName} disconnected from ${this.ip}:${this.port}`);
}
}
disconnect() {
this.isManualDisconnect = true;
if (this.bot) {
this.bot.quit();
}
this.handleDisconnect();
}
startAFK() {
if (!this.bot) return;
// Simple AFK movement
let direction = 1;
const afkInterval = setInterval(() => {
if (!this.bot || this.status !== 'Connected') {
clearInterval(afkInterval);
return;
}
// Walk forward and backward
this.bot.setControlState('forward', direction > 0);
this.bot.setControlState('back', direction < 0);
setTimeout(() => {
if (this.bot) {
this.bot.clearControlStates();
}
}, 1000);
direction *= -1;
}, 5000);
}
canReconnect() {
if (this.status === 'Connected') return false;
const lastReconnect = botLastReconnect.get(this.botName) || 0;
const hourAgo = Date.now() - (60 * 60 * 1000);
return lastReconnect < hourAgo;
}
async reconnect() {
if (!this.canReconnect()) {
return false;
}
this.isManualDisconnect = false;
botLastReconnect.set(this.botName, Date.now());
return await this.connect();
}
getInfo() {
const uptime = this.status === 'Connected' ?
Math.floor((Date.now() - this.startTime) / 1000) : 0;
return {
botName: this.botName,
status: this.status,
deathCount: this.deathCount,
uptime: uptime,
canReconnect: this.canReconnect(),
disconnectTime: this.disconnectTime
};
}
}
// Fetch and parse Google Sheets data
async function fetchSheetData() {
try {
const response = await fetch(SHEET_URL);
const csvText = await response.text();
const records = parse(csvText, {
columns: true,
skip_empty_lines: true
});
return records;
} catch (error) {
console.error('Error fetching sheet data:', error);
return [];
}
}
// Update bots based on sheet data
async function updateBots() {
const sheetData = await fetchSheetData();
const activeBots = new Set();
for (const row of sheetData) {
const botName = row['BOT NAME']?.trim();
const ip = row['IP']?.trim();
const port = row['PORT']?.trim();
const version = row['Version']?.trim() || '1.20.1';
if (!botName || !ip || !port) continue;
activeBots.add(botName);
// Add new bot if it doesn't exist
if (!bots.has(botName)) {
const botManager = new BotManager(botName, ip, port, version);
bots.set(botName, botManager);
await botManager.connect();
}
}
// Remove bots that are no longer in the sheet
for (const [botName, botManager] of bots.entries()) {
if (!activeBots.has(botName)) {
botManager.disconnect();
bots.delete(botName);
botLastReconnect.delete(botName);
}
}
}
// Socket.IO events
io.on('connection', (socket) => {
console.log('Client connected');
// Send initial bot data
const sendBotData = () => {
const botData = Array.from(bots.values()).map(bot => bot.getInfo());
socket.emit('botUpdate', botData);
};
sendBotData();
const updateInterval = setInterval(sendBotData, 2000);
socket.on('reconnectBot', async (botName) => {
const botManager = bots.get(botName);
if (botManager) {
const success = await botManager.reconnect();
socket.emit('reconnectResult', { botName, success });
}
});
socket.on('refreshSheet', async () => {
await updateBots();
sendBotData();
});
socket.on('disconnect', () => {
clearInterval(updateInterval);
console.log('Client disconnected');
});
});
// Serve static files
app.use(express.static(__dirname));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
// Start server
const PORT = process.env.PORT || 7860;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Initial load and periodic updates
updateBots();
setInterval(updateBots, 30000); // Check sheet every 30 seconds