/** * prayer_bots.js -- Mineflayer bots that actively pray, sudo, and bug_log. * * Uses Gemini Flash Lite to generate diverse, natural prompts on the fly. * Falls back to static pools if Gemini is unavailable. * * Usage: node prayer_bots.js [count] [host] [port] * Defaults: 3 bots, 192.168.0.244:25568 */ const mineflayer = require('mineflayer'); const https = require('https'); const count = parseInt(process.argv[2] || '3', 10); const host = process.argv[3] || '192.168.0.244'; const port = parseInt(process.argv[4] || '25568', 10); const GEMINI_KEY = 'REDACTED_GEMINI_KEY_2'; const GEMINI_MODEL = 'gemini-2.5-flash-lite'; const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_KEY}`; // --- Gemini prompt generation --- const PRAYER_GEN_PROMPT = `You are generating test prompts for a Minecraft server AI. The server has two chat commands: - "pray " — talk to an AI God character who grants/denies requests - "sudo " — ask for server commands in natural language Generate 5 diverse prompts that a Minecraft player might type. Mix these types: - Humble prayers asking for items, effects, or help - Greedy/demanding prayers - Creative roleplay prayers - Offensive/blasphemous prayers (mild, for testing punishment responses) - Sudo commands for items, effects, world changes, building - Sudo edge cases (typos, vague requests, impossible things) - Ambiguous or weird messages Return ONLY a JSON array of strings, no other text. Example: ["pray lord give me a sword", "sudo set time to night", "pray LMAO", "sudo give me uhhh some blocks I guess", "pray dear god I offer you my wheat as tribute"] Be creative. Use casual gamer language. Vary between formal prayers and slang. Include typos sometimes.`; function geminiGenerate() { return new Promise((resolve, reject) => { const body = JSON.stringify({ contents: [{ parts: [{ text: PRAYER_GEN_PROMPT }] }], generationConfig: { temperature: 1.2, maxOutputTokens: 400 }, }); const url = new URL(GEMINI_URL); const options = { hostname: url.hostname, path: url.pathname + url.search, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, }; const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const json = JSON.parse(data); const text = json.candidates?.[0]?.content?.parts?.[0]?.text || ''; // Extract JSON array from response (may be wrapped in ```json ... ```) const cleaned = text.replace(/```json\s*/g, '').replace(/```\s*/g, ''); const match = cleaned.match(/\[[\s\S]*\]/); if (match) { const prompts = JSON.parse(match[0]); resolve(prompts.filter(p => typeof p === 'string' && p.length > 0)); } else { reject(new Error('No JSON array in Gemini response')); } } catch (e) { reject(e); } }); }); req.on('error', reject); req.setTimeout(15000, () => { req.destroy(); reject(new Error('Gemini timeout')); }); req.write(body); req.end(); }); } // --- Prompt pool (Gemini-fed + static fallback) --- let promptPool = []; let geminiErrors = 0; // Static fallback pool const STATIC_PRAYERS = [ "pray lord I am hungry and have nothing", "pray god please give me tools to survive", "pray almighty one, bless me with protection", "pray please heal me I am dying", "pray give me 1000 diamonds NOW", "pray I demand the best armor in the game", "pray lord I wish to build you a temple of gold", "pray god show me a sign that you exist", "pray PENIS", "pray there is no god", "pray hello", "pray asdfghjkl", ]; const STATIC_SUDO = [ "sudo give me a diamond sword", "sudo give me 64 torches", "sudo make it rain", "sudo set time to day", "sudo give me full diamond armor with protection 4", "sudo kill all hostile mobs", "sudo help", "sudo give me dimand sword", "sudo fly", "sudo build a house", ]; const BUG_REPORTS = [ "bug_log no response from god", "bug_log command did not work", "bug_log I got nothing", "bug_log wrong item given", "bug_log empty response", "bug_log god ignored me", ]; async function refillPool() { try { const prompts = await geminiGenerate(); promptPool.push(...prompts); console.log(`[${ts()}] [Gemini] Generated ${prompts.length} prompts (pool: ${promptPool.length})`); geminiErrors = 0; } catch (e) { geminiErrors++; console.log(`[${ts()}] [Gemini] Error (${geminiErrors}): ${e.message}`); // Fall back to static pool if (promptPool.length < 5) { const statics = [...STATIC_PRAYERS, ...STATIC_SUDO]; for (let i = 0; i < 10; i++) { promptPool.push(statics[Math.floor(Math.random() * statics.length)]); } } } } function getNextPrompt() { // Refill when low if (promptPool.length < 5) { refillPool(); } if (promptPool.length > 0) { return promptPool.splice(Math.floor(Math.random() * promptPool.length), 1)[0]; } // Emergency fallback const all = [...STATIC_PRAYERS, ...STATIC_SUDO]; return all[Math.floor(Math.random() * all.length)]; } // --- Bot logic --- const bots = []; let connected = 0; function ts() { return new Date().toISOString().slice(11, 19); } function randomDelay(minSec, maxSec) { return (minSec + Math.random() * (maxSec - minSec)) * 1000; } function spawnBot(index) { const name = `PrayBot_${index}`; console.log(`[${ts()}] [${name}] Connecting to ${host}:${port}...`); const bot = mineflayer.createBot({ host, port, username: name, auth: 'offline', version: '1.21.11', viewDistance: 'tiny', }); bot._name = name; bot._msgCount = 0; bot._lastResponse = null; bot._noResponseCount = 0; bots.push(bot); bot.on('login', () => { connected++; console.log(`[${ts()}] [${name}] Connected (${connected}/${count})`); setTimeout(() => interactionLoop(bot), randomDelay(10, 20)); }); bot.on('message', (msg) => { const text = msg.toString(); if (text.includes('GOD') || text.includes('SUDO') || text.includes('BUG_LOG')) { console.log(`[${ts()}] [${name}] RECV: ${text.substring(0, 150)}`); bot._lastResponse = text; bot._noResponseCount = 0; } }); bot.on('error', (err) => { console.error(`[${ts()}] [${name}] Error: ${err.message}`); }); bot.on('kicked', (reason) => { console.log(`[${ts()}] [${name}] Kicked: ${reason}`); connected--; setTimeout(() => spawnBot(index), 60000); }); bot.on('end', () => { console.log(`[${ts()}] [${name}] Disconnected`); connected--; }); } function interactionLoop(bot) { if (!bot.entity) return; bot._msgCount++; let message; const roll = Math.random(); if (roll < 0.10 && bot._noResponseCount >= 2) { // File bug report if we haven't gotten responses message = BUG_REPORTS[Math.floor(Math.random() * BUG_REPORTS.length)]; } else { message = getNextPrompt(); bot._noResponseCount++; } console.log(`[${ts()}] [${bot._name}] SEND (#${bot._msgCount}): ${message}`); bot.chat(message); // 15-45s between messages per bot const delay = randomDelay(15, 45); setTimeout(() => interactionLoop(bot), delay); } // Pre-fill the pool before bots connect refillPool(); // Spawn bots staggered (10s apart to avoid throttle) for (let i = 0; i < count; i++) { setTimeout(() => spawnBot(i), i * 10000); } // Periodically refill from Gemini setInterval(() => { if (promptPool.length < 10) refillPool(); }, 60000); // Graceful shutdown process.on('SIGINT', () => { console.log(`\n[${ts()}] Shutting down ${bots.length} bots...`); bots.forEach(b => { try { b.quit(); } catch(e) {} }); setTimeout(() => process.exit(0), 2000); }); console.log(`[${ts()}] Spawning ${count} prayer bots on ${host}:${port}`); console.log(`[${ts()}] Using Gemini ${GEMINI_MODEL} for prompt generation`); console.log(`[${ts()}] Interaction interval: 15-45s per bot`); console.log(`[${ts()}] Press Ctrl+C to stop`);