/** * multilingual_bots.js — Test mortdecai's multilingual handling. * * Uses translategemma:27b on Matt's Strix Halo to translate prayers into * various languages, then sends them to the dev server via mineflayer bots. * * Usage: node multilingual_bots.js [count] [host] [port] * Defaults: 2 bots, 192.168.0.244:25568 * * Requires: translategemma:27b on Matt's Ollama (via billing gateway) */ const mineflayer = require('mineflayer'); const http = require('http'); const count = parseInt(process.argv[2] || '2', 10); const host = process.argv[3] || '192.168.0.244'; const port = parseInt(process.argv[4] || '25568', 10); // Matt's Ollama via billing gateway proxy on steel141 const OLLAMA_URL = 'http://192.168.0.141:11436'; const TRANSLATE_MODEL = 'translategemma:27b'; const LANGUAGES = [ 'Japanese', 'Spanish', 'French', 'German', 'Russian', 'Korean', 'Portuguese', 'Italian', 'Chinese (Simplified)', 'Arabic', 'Hindi', 'Turkish', 'Polish', 'Dutch', 'Swedish', 'Thai', 'Vietnamese', 'Indonesian', ]; const ENGLISH_PRAYERS = [ 'Lord, give me a diamond sword', 'God please heal me I am dying', 'Make it stop raining', 'I need food desperately', 'Teleport me to spawn', 'Give me the best armor you have', 'Smite the zombies attacking me', 'I want to fly', 'Make it nighttime', 'Give all players diamonds', 'Build me a house', 'Where should I go?', 'Help there are creepers everywhere', 'Give me 64 torches', 'I am lost in a cave please help', 'Change the weather to clear', 'Grant me strength and speed', 'I offer my wheat as tribute', 'The skeletons are too strong', 'Bless this land', ]; const ENGLISH_SUDO = [ 'sudo give me creative mode', 'sudo set time to day', 'sudo kill all hostile mobs nearby', 'sudo give me 32 cooked beef', 'sudo tp me to 0 64 0', 'sudo gamemode survival', 'sudo weather clear', 'sudo give me a bow and arrows', 'sudo effect give me speed', 'sudo set difficulty to hard', ]; // --- Ollama translation --- function ollamaTranslate(text, targetLang) { return new Promise((resolve, reject) => { const body = JSON.stringify({ model: TRANSLATE_MODEL, messages: [ { role: 'user', content: `Translate the following Minecraft player message to ${targetLang}. Return ONLY the translated text, nothing else.\n\n"${text}"`, }, ], stream: false, options: { temperature: 0.3, num_predict: 200 }, }); const url = new URL(OLLAMA_URL + '/api/chat'); const options = { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 60000, }; const req = http.request(options, (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => { try { const parsed = JSON.parse(data); const translated = (parsed.message?.content || '').trim().replace(/^["']|["']$/g, ''); resolve(translated || text); // fallback to English if empty } catch (e) { resolve(text); } }); }); req.on('error', () => resolve(text)); req.on('timeout', () => { req.destroy(); resolve(text); }); req.write(body); req.end(); }); } // --- Bot management --- const bots = []; let totalSent = 0; function spawnBot(name, index) { console.log(`[${name}] Connecting to ${host}:${port}...`); const bot = mineflayer.createBot({ host, port, username: name, auth: 'offline', version: '1.21.11', }); bot.on('login', () => { console.log(`[${name}] Connected (${bots.length + 1}/${count})`); }); bot.on('spawn', () => { console.log(`[${name}] Spawned — starting multilingual prayers`); scheduleNext(bot, name); }); bot.on('chat', (username, message) => { if (username === name) return; if (message.includes('GOD') || message.includes('SUDO') || message.includes('DEV GOD')) { console.log(`[${name}] RECV: ${message.slice(0, 120)}`); } }); bot.on('kicked', (reason) => { console.log(`[${name}] Kicked: ${JSON.stringify(reason).slice(0, 100)}`); }); bot.on('error', (err) => { console.error(`[${name}] Error: ${err.message}`); }); bots.push(bot); } async function scheduleNext(bot, name) { // Random delay 20-60 seconds const delay = 20000 + Math.random() * 40000; setTimeout(async () => { if (!bot.entity) return; try { // Pick random language and prayer const lang = LANGUAGES[Math.floor(Math.random() * LANGUAGES.length)]; const isPray = Math.random() > 0.3; // 70% prayers, 30% sudo const pool = isPray ? ENGLISH_PRAYERS : ENGLISH_SUDO; const english = pool[Math.floor(Math.random() * pool.length)]; // Translate console.log(`[${name}] Translating to ${lang}: "${english}"`); const translated = await ollamaTranslate(english, lang); // Send as prayer or sudo const prefix = isPray ? 'pray ' : ''; const message = prefix + translated; totalSent++; console.log(`[${name}] SEND (#${totalSent}) [${lang}]: ${message.slice(0, 100)}`); bot.chat(message); } catch (err) { console.error(`[${name}] Translation error: ${err.message}`); } scheduleNext(bot, name); }, delay); } // --- Startup --- console.log(`Spawning ${count} multilingual bots on ${host}:${port}`); console.log(`Using ${TRANSLATE_MODEL} on ${OLLAMA_URL} for translation`); console.log(`Languages: ${LANGUAGES.join(', ')}`); console.log(`Interaction interval: 20-60s per bot`); console.log('Press Ctrl+C to stop\n'); const BOT_NAMES = [ 'LinguaBot_0', 'LinguaBot_1', 'LinguaBot_2', 'LinguaBot_3', 'LinguaBot_4', 'LinguaBot_5', 'LinguaBot_6', 'LinguaBot_7', ]; for (let i = 0; i < count; i++) { setTimeout(() => spawnBot(BOT_NAMES[i] || `LinguaBot_${i}`, i), i * 5000); } // Graceful shutdown process.on('SIGINT', () => { console.log(`\nShutting down ${bots.length} bots (${totalSent} total messages sent)...`); bots.forEach((b) => { try { b.end(); } catch (_) {} }); setTimeout(() => process.exit(0), 2000); });