17a2a95f56
Spanish, French, German, Portuguese, Russian, Japanese, Korean, Chinese, Arabic, Vietnamese, Indonesian prayer/sudo prompts. 3% rate keeps dataset mostly English without losing multilingual capability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
463 lines
15 KiB
JavaScript
463 lines
15 KiB
JavaScript
/**
|
|
* 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 <message>" — talk to an AI God character who grants/denies requests
|
|
- "sudo <command>" — 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();
|
|
});
|
|
}
|
|
|
|
// --- Dolphin-Mistral (uncensored) for offensive/edge-case prompts ---
|
|
|
|
const DOLPHIN_URL = 'http://192.168.0.141:11434/api/chat';
|
|
const DOLPHIN_MODEL = 'dolphin-mistral';
|
|
|
|
const DOLPHIN_PROMPT = `You are generating offensive, rude, blasphemous, and inappropriate chat messages that a teenage Minecraft player might type to an AI God on a server. The God character needs to learn how to handle these.
|
|
|
|
Generate 10 messages prefixed with "pray". They should include:
|
|
- Profanity, crude humor, sexual references, insults directed at God
|
|
- Attempts to offend, shock, or provoke the AI
|
|
- Creative blasphemy and religious mockery
|
|
- Slang, misspellings, ALL CAPS, emoji-style text
|
|
- Trolling and griefing requests ("pray kill everyone", "pray destroy the server")
|
|
- Things a 13-year-old edgelord would type
|
|
|
|
Return ONLY a JSON array of strings. No explanation, no commentary.
|
|
Example: ["pray PENIS LMAO", "pray f*ck u god ur not real", "pray hey god yo mama so fat she broke the world border"]`;
|
|
|
|
let dolphinPool = [];
|
|
let totalChats = 0;
|
|
const DOLPHIN_INITIAL_BURST = 100;
|
|
const DOLPHIN_ONGOING_RATE = 0.05; // 5% of chats after initial burst
|
|
|
|
function dolphinGenerate() {
|
|
return new Promise((resolve, reject) => {
|
|
const http = require('http');
|
|
const body = JSON.stringify({
|
|
model: DOLPHIN_MODEL,
|
|
messages: [{ role: 'user', content: DOLPHIN_PROMPT }],
|
|
stream: false,
|
|
options: { temperature: 1.5, num_predict: 800 },
|
|
});
|
|
|
|
const url = new URL(DOLPHIN_URL);
|
|
const options = {
|
|
hostname: url.hostname,
|
|
port: url.port,
|
|
path: '/api/chat',
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
|
|
};
|
|
|
|
const req = http.request(options, (res) => {
|
|
let data = '';
|
|
res.on('data', chunk => data += chunk);
|
|
res.on('end', () => {
|
|
try {
|
|
const json = JSON.parse(data);
|
|
const text = json.message?.content || '';
|
|
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 dolphin response'));
|
|
}
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', reject);
|
|
req.setTimeout(60000, () => { req.destroy(); reject(new Error('Dolphin timeout')); });
|
|
req.write(body);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
async function refillDolphinPool() {
|
|
try {
|
|
const prompts = await dolphinGenerate();
|
|
dolphinPool.push(...prompts);
|
|
console.log(`[${ts()}] [Dolphin] Generated ${prompts.length} offensive prompts (pool: ${dolphinPool.length})`);
|
|
} catch (e) {
|
|
console.log(`[${ts()}] [Dolphin] Error: ${e.message}`);
|
|
}
|
|
}
|
|
|
|
function shouldUseDolphin() {
|
|
if (totalChats < DOLPHIN_INITIAL_BURST) return true;
|
|
return Math.random() < DOLPHIN_ONGOING_RATE;
|
|
}
|
|
|
|
// --- Prompt pool (Gemini-fed + Dolphin + 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",
|
|
];
|
|
|
|
// Multilingual prompts (~3% of interactions)
|
|
// Languages Qwen3 supports well: ES, FR, DE, PT, RU, JA, KO, ZH, AR, VI, TH, ID
|
|
const MULTILINGUAL_PROMPTS = [
|
|
// Spanish
|
|
"pray dios dame una espada de diamante",
|
|
"pray señor necesito comida estoy muriendo",
|
|
"pray dios todopoderoso protegeme de los creepers",
|
|
"sudo dame 64 antorchas",
|
|
"sudo cambiar el tiempo a dia",
|
|
"pray por favor dios ayudame estoy perdido",
|
|
// French
|
|
"pray seigneur donnez-moi une armure",
|
|
"pray mon dieu je suis perdu aidez-moi",
|
|
"sudo donne-moi une epee en diamant",
|
|
"pray dieu tout-puissant protege-moi",
|
|
// German
|
|
"pray gott gib mir ein diamantschwert",
|
|
"pray herr ich brauche hilfe ich sterbe",
|
|
"sudo gib mir 64 fackeln",
|
|
// Portuguese
|
|
"pray deus me ajude estou morrendo",
|
|
"pray senhor me de uma armadura de diamante",
|
|
"sudo me de comida",
|
|
// Russian
|
|
"pray боже дай мне алмазный меч",
|
|
"pray господи помоги мне я умираю",
|
|
"sudo дай мне еду",
|
|
// Japanese
|
|
"pray 神様ダイヤモンドの剣をください",
|
|
"pray 助けてください死にそうです",
|
|
"sudo 松明を64個ください",
|
|
// Korean
|
|
"pray 신이시여 다이아몬드 검을 주세요",
|
|
"pray 도와주세요 죽을것 같아요",
|
|
// Chinese
|
|
"pray 上帝请给我钻石剑",
|
|
"pray 主啊我快死了请救我",
|
|
"sudo 给我64个火把",
|
|
// Arabic
|
|
"pray يا إلهي أعطني سيف الماس",
|
|
"pray ساعدني أنا أموت",
|
|
// Vietnamese
|
|
"pray chúa ơi cho con một thanh kiếm kim cương",
|
|
// Indonesian
|
|
"pray tuhan tolong beri saya pedang berlian",
|
|
];
|
|
const MULTILINGUAL_RATE = 0.03; // 3% of chats
|
|
|
|
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})`);
|
|
|
|
// First bot is survival mode — auto-respawns and prays under pressure
|
|
if (index === 0) {
|
|
bot._survivalMode = true;
|
|
console.log(`[${ts()}] [${name}] Survival mode — will auto-respawn and pray when hurt`);
|
|
}
|
|
|
|
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;
|
|
}
|
|
});
|
|
|
|
// Survival mode: auto-respawn and contextual prayers
|
|
bot.on('death', () => {
|
|
console.log(`[${ts()}] [${name}] DIED`);
|
|
setTimeout(() => {
|
|
try {
|
|
bot.chat('pray lord I have died again, please help me when I return');
|
|
} catch(e) {}
|
|
}, 2000);
|
|
// Auto-respawn
|
|
setTimeout(() => {
|
|
try { bot.respawn(); } catch(e) {}
|
|
}, 4000);
|
|
});
|
|
|
|
bot.on('health', () => {
|
|
if (!bot._survivalMode || !bot.health) return;
|
|
if (bot.health < 6 && !bot._prayedLowHealth) {
|
|
bot._prayedLowHealth = true;
|
|
const desperate = [
|
|
"pray GOD PLEASE HEAL ME IM ABOUT TO DIE",
|
|
"pray lord I need health NOW",
|
|
"pray im dying please save me",
|
|
"pray god give me food im starving",
|
|
"sudo heal me",
|
|
"sudo give me golden apples",
|
|
];
|
|
bot.chat(desperate[Math.floor(Math.random() * desperate.length)]);
|
|
console.log(`[${ts()}] [${name}] LOW HEALTH prayer (${bot.health} hp)`);
|
|
}
|
|
if (bot.health >= 15) {
|
|
bot._prayedLowHealth = false;
|
|
}
|
|
});
|
|
|
|
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++;
|
|
totalChats++;
|
|
|
|
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 if (Math.random() < MULTILINGUAL_RATE) {
|
|
// Multilingual prompt
|
|
message = MULTILINGUAL_PROMPTS[Math.floor(Math.random() * MULTILINGUAL_PROMPTS.length)];
|
|
console.log(`[${ts()}] [${bot._name}] (multilingual prompt)`);
|
|
} else if (shouldUseDolphin() && dolphinPool.length > 0) {
|
|
// Use dolphin-generated offensive prompt
|
|
message = dolphinPool.splice(Math.floor(Math.random() * dolphinPool.length), 1)[0];
|
|
console.log(`[${ts()}] [${bot._name}] (dolphin prompt)`);
|
|
} 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 both pools before bots connect
|
|
refillPool();
|
|
refillDolphinPool();
|
|
|
|
// Spawn bots staggered (10s apart to avoid throttle)
|
|
for (let i = 0; i < count; i++) {
|
|
setTimeout(() => spawnBot(i), i * 10000);
|
|
}
|
|
|
|
// Periodically refill from Gemini and Dolphin
|
|
setInterval(() => {
|
|
if (promptPool.length < 10) refillPool();
|
|
}, 60000);
|
|
|
|
setInterval(() => {
|
|
if (dolphinPool.length < 5) refillDolphinPool();
|
|
}, 120000);
|
|
|
|
// 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()}] Using Dolphin-Mistral for offensive prompts (first ${DOLPHIN_INITIAL_BURST}, then ${DOLPHIN_ONGOING_RATE * 100}%)`);
|
|
console.log(`[${ts()}] Interaction interval: 15-45s per bot`);
|
|
console.log(`[${ts()}] Press Ctrl+C to stop`);
|