diff --git a/.gitignore b/.gitignore index 856f35a..cd009ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules package-lock.json -.env \ No newline at end of file +.env +*.log \ No newline at end of file diff --git a/afk.js b/afk.js new file mode 100644 index 0000000..788f303 --- /dev/null +++ b/afk.js @@ -0,0 +1,146 @@ +const mineflayer = require("mineflayer"); +const fs = require("fs"); +const path = require("path"); + +// Configuration +const BOT_CONFIG = { + host: "java.donutsmp.net", + username: "AFKBot", + auth: "microsoft", + version: "1.21.1", +}; + +const LOG_FILE = path.join(__dirname, "afk.log"); +const TELEPORT_DETECT_REGEX = /you teleported to\b/i; + +// Logging +function log(message) { + const timestamp = new Date().toISOString(); + const logMessage = `[${timestamp}] ${message}`; + console.log(logMessage); + fs.appendFileSync(LOG_FILE, logMessage + "\n"); +} + +function toPlainText(value) { + if (value == null) return ""; + if (typeof value === "string") return value; + if ( + typeof value.toString === "function" && + value.toString !== Object.prototype.toString + ) { + const asString = value.toString(); + if (asString && asString !== "[object Object]") return asString; + } + + if (Array.isArray(value)) { + return value.map(toPlainText).join(""); + } + + if (typeof value === "object") { + let text = ""; + if (typeof value.text === "string") text += value.text; + if (typeof value.translate === "string") text += value.translate; + if (Array.isArray(value.with)) text += value.with.map(toPlainText).join(""); + if (Array.isArray(value.extra)) + text += value.extra.map(toPlainText).join(""); + return text; + } + + return String(value); +} + +function logIfTeleport(source, text) { + const normalized = String(text || "").trim(); + if (!normalized) return; + if (!TELEPORT_DETECT_REGEX.test(normalized)) return; + log(`Teleport detected from ${source}: ${normalized}`); +} + +// Create bot +const bot = mineflayer.createBot({ + host: BOT_CONFIG.host, + username: BOT_CONFIG.username, + auth: BOT_CONFIG.auth, + version: BOT_CONFIG.version, +}); + +// Event handlers +bot.on("login", () => { + log("Bot logged in"); + bot.setControlState("forward", false); +}); + +bot.on("spawn", () => { + log("Bot spawned"); +}); + +bot.on("error", (err) => { + log(`Error: ${err.message}`); +}); + +bot.on("kicked", (reason) => { + log(`Kicked: ${reason}`); +}); + +bot.on("end", () => { + log("Bot disconnected"); + process.exit(0); +}); + +bot.on("chat", (username, message) => { + if (username === bot.username) return; + log(`Chat [${username}]: ${message}`); + logIfTeleport("chat", message); +}); + +bot.on("messagestr", (message, position) => { + if (!message) return; + + if (position === "game_info") { + log(`Hotbar: ${message}`); + } + + logIfTeleport(`messagestr${position ? `:${position}` : ""}`, message); +}); + +bot.on("message", (jsonMsg, position) => { + const text = toPlainText(jsonMsg); + if (!text) return; + + if (position === "game_info") { + log(`Hotbar(json): ${text}`); + } + + logIfTeleport(`message${position ? `:${position}` : ""}`, text); +}); + +// Fallback packet hooks for title/actionbar channels on servers that do not +// emit teleport text through regular chat events. +bot._client.on("set_action_bar_text", (packet) => { + const text = toPlainText(packet?.text); + if (!text) return; + log(`ActionBar: ${text}`); + logIfTeleport("actionbar", text); +}); + +bot._client.on("set_title_text", (packet) => { + const text = toPlainText(packet?.text); + if (!text) return; + log(`Title: ${text}`); + logIfTeleport("title", text); +}); + +bot._client.on("set_subtitle_text", (packet) => { + const text = toPlainText(packet?.text); + if (!text) return; + log(`Subtitle: ${text}`); + logIfTeleport("subtitle", text); +}); + +// Graceful shutdown +process.on("SIGINT", () => { + log("Shutdown signal received"); + bot.quit(); +}); + +log("Starting AFK bot...");