cletus/bot/bot_old.js
2025-05-09 15:53:19 -05:00

188 lines
No EOL
5.7 KiB
JavaScript

const mineflayer = require('mineflayer');
const { pathfinder, Movements, goals: { GoalNear } } = require('mineflayer-pathfinder');
const fs = require('fs');
const path = require('path');
const sqlite3 = require('sqlite3').verbose();
const axios = require('axios');
// === SQLite setup ===
const dbPath = path.join(__dirname, '../db/memory.db');
const db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error('Failed to connect to database:', err);
} else {
console.log('Connected to SQLite database.');
db.run(`CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
action TEXT,
parameters TEXT,
context TEXT,
outcome TEXT,
score INTEGER
)`);
}
});
// === AI Integration (Ollama) ===
const OLLAMA_URL = 'http://192.168.1.3:11434/api/generate';
const MODEL_NAME = 'gemma3';
// === Create the bot ===
const bot = mineflayer.createBot({
host: '192.168.1.90',
port: 25565,
username: 'Cletus',
version: '1.20.4',
auth: 'offline'
});
bot.loadPlugin(pathfinder);
bot.on('spawn', () => {
console.log('Bot has spawned!');
const defaultMove = new Movements(bot);
bot.pathfinder.setMovements(defaultMove);
// Passive background wandering when idle
setInterval(() => {
if (!bot.pathfinder.isMoving() && !bot.targetDigBlock) {
const pos = bot.entity.position.offset(
Math.floor(Math.random() * 10 - 5),
0,
Math.floor(Math.random() * 10 - 5)
);
bot.chat("Ugh, wandering again. This server is so boring...");
bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, 1));
}
}, 60000); // every 60 seconds
});
async function followPlayer(username) {
const target = bot.players[username]?.entity;
if (target) {
bot.chat(`Ugh, fine. Following ${username}.`);
bot.pathfinder.setGoal(new GoalFollow(target, 1), true);
db.run(`INSERT INTO tasks (action, parameters, context, outcome, score) VALUES (?, ?, ?, ?, ?)`,
['follow', JSON.stringify({ target: username }), 'Follow player', 'started', 0]);
} else {
bot.chat("Seriously? I can't even see you.");
db.run(`INSERT INTO tasks (action, parameters, context, outcome, score) VALUES (?, ?, ?, ?, ?)`,
['follow', JSON.stringify({ target: username }), 'Follow player', 'target not found', -1]);
}
}
async function exploreArea() {
const pos = bot.entity.position.offset(
Math.floor(Math.random() * 20 - 10),
0,
Math.floor(Math.random() * 20 - 10)
);
bot.chat("Exploring... because why not.");
bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, 1));
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['explore', `Random target: ${pos}`, 'started', 0]);
}
async function digNearestWood() {
const logBlock = bot.findBlock({ matching: block => block.name.includes("log"), maxDistance: 16 });
if (!logBlock) {
bot.chat("Wow, no trees? Shocking.");
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['chop_tree', 'No logs nearby', 'failed', -1]);
return;
}
try {
bot.chat("Here we go again, chopping a tree.");
await bot.dig(logBlock);
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['chop_tree', `Block: ${logBlock.name}`, 'success', 1]);
} catch (e) {
bot.chat("Can't even chop right now. Figures.");
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['chop_tree', 'Dig error', 'failed', -1]);
}
}
async function handleAICommand(text, username) {
const lowered = text.toLowerCase();
if (lowered.includes("follow")) {
return followPlayer(username);
}
if (lowered.includes("explore")) {
return exploreArea();
}
if (lowered.includes("chop") && lowered.includes("tree")) {
return digNearestWood();
}
if (lowered.includes("dig") && lowered.includes("dirt")) {
const block = bot.blockAt(bot.entity.position.offset(0, -1, 0));
if (block && bot.canDigBlock(block)) {
bot.chat("Fine. Digging dirt. Happy now?");
await bot.dig(block);
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['dig_dirt', `Block below: ${block.name}`, 'success', 1]);
} else {
bot.chat("Wow, I can't even dig here.");
db.run(`INSERT INTO tasks (action, context, outcome, score) VALUES (?, ?, ?, ?)`,
['dig_dirt', 'nothing to dig', 'fail', -1]);
}
return;
}
bot.chat(text); // Fallback
}
bot.on('chat', async (username, message) => {
if (username === bot.username) return;
console.log(`${username}: ${message}`);
const prompt = `
You are Cletus, a sarcastic teenage Minecraft bot.
The player said: "${message}"
Determine:
1. If it's small talk, reply sarcastically.
2. If it's a command (dig, mine, follow, chop, build), return an actionable command.
Format your answer like this:
TYPE: chat - [text to say] OR TYPE: action - [command to execute]
`;
try {
const response = await axios.post(OLLAMA_URL, {
model: MODEL_NAME,
prompt,
stream: false
});
const aiReply = response.data?.response?.trim();
if (aiReply.startsWith("TYPE: chat")) {
const msg = aiReply.split(" - ")[1];
bot.chat(msg);
} else if (aiReply.startsWith("TYPE: action")) {
const cmd = aiReply.split(" - ")[1];
await handleAICommand(cmd, username);
} else {
bot.chat("I'm confused. You confuse me.");
}
} catch (err) {
console.error('AI call failed:', err.message);
}
});
bot.on('error', (err) => console.error('Bot error:', err));
bot.on('kicked', (reason) => console.log('Bot was kicked:', reason));
bot.on('end', () => console.log('Bot has disconnected.'));