f38ad8c819
Workaround being unable to look at what player is looking at. Currently looks at what player is standing on.
601 lines
24 KiB
JavaScript
601 lines
24 KiB
JavaScript
const v = require('vec3')
|
|
let mcData
|
|
|
|
let cfg = {}
|
|
let bot = {}
|
|
// bot.chatAddPattern(new RegExp(`^hi|hello ${bot.username}`, "i"), 'greet', "General greeting")
|
|
|
|
function todo() {
|
|
cfg.quiet && console.warn("not implemented") || bot.chat("not implemented yet")
|
|
}
|
|
|
|
function checkBlockExists(name) {
|
|
if (mcData.blocksByName[name] === undefined) {
|
|
bot.chat(`${name} is not a block name`)
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
function checkItemExists(name) {
|
|
if (mcData.itemsByName[name] === undefined) {
|
|
bot.chat(`${name} is not an item name`)
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
const events = {
|
|
whisper: function command_whisper(username, message) {
|
|
if ([bot.username, "me"].includes(username)) return
|
|
if (/^gossip/.test(message)) return
|
|
// if (/^!/.test(message) && username === cfg.admin){
|
|
if (username === cfg.admin) {
|
|
message = message.replace("\\", "@")
|
|
console.info("whispered command", message)
|
|
if (/^!/.test(message)) {
|
|
command(username, message)
|
|
} else {
|
|
bot.chat(message)
|
|
// bot.chat("/" + message)
|
|
}
|
|
} else {
|
|
bot.whisper(cfg.admin, `gossip ${username}: ${message}`)
|
|
console.info(username, "whispered", message)
|
|
}
|
|
}
|
|
, chat: command
|
|
, kicked: function rejoin(reason, loggedIn) {
|
|
console.warn(reason, loggedIn && "logged_in")
|
|
if (reason.extra && reason.extra[0].text === "Server closed") {
|
|
bot.quit()
|
|
bot.end()
|
|
// TODO implement all startup features (maybe refactor all into a single function / module?)
|
|
setTimeout((bot, cfg) => {
|
|
bot = mineflayer.createBot(cfg.botOptions)
|
|
}, 15 * 60 * 1000, bot, cfg);
|
|
}
|
|
}
|
|
}
|
|
|
|
const events_registered = []
|
|
|
|
function command(username, message) {
|
|
function fuzzyRespond(responses, probability = 1, timeout = 1) {
|
|
if (Math.random() < probability) {
|
|
const response = responses[Math.floor(Math.random() * responses.length)]
|
|
return setTimeout(() => bot.chat(response), timeout * Math.random() * 1000)
|
|
}
|
|
}
|
|
|
|
function swingArm(swung = 3) {
|
|
if (swung > 0) {
|
|
setTimeout(swingArm, 500 * Math.random(), --swung)
|
|
bot.swingArm()
|
|
}
|
|
}
|
|
|
|
if (username === bot.username && !message.startsWith("!")) return
|
|
|
|
const player = bot.players[username] ? bot.players[username].entity : null
|
|
|
|
if (message.startsWith("zzz")) {
|
|
bot.chat("/afk")
|
|
cfg.plugins.sleeper.sleep()
|
|
// if(cfg)
|
|
return
|
|
}
|
|
|
|
|
|
if (message.startsWith("!") || cfg.botAddress.test(message)) {
|
|
message = cfg.botAddress.test(message) ? cfg.botAddress.exec(message)[1] : message
|
|
|
|
console.log(message)
|
|
message = message.slice(1) // remove `!`
|
|
// TODO command dispatchEvent, for aliases
|
|
function subcommand(message) {
|
|
const message_parts = message.split(/\s+/)
|
|
|
|
switch (message_parts[0]) {
|
|
case "stop":
|
|
bot.pathfinder && bot.pathfinder.setGoal(null)
|
|
bot.stopDigging()
|
|
bot.chat("ok")
|
|
break;
|
|
|
|
case "mute":
|
|
case "quiet":
|
|
case "silent":
|
|
cfg.quiet = !cfg.quiet
|
|
cfg.quiet || bot.chat(`ok, ${cfg.quiet ? "" : "not "}being ${message_parts[0]}`)
|
|
break;
|
|
// case "follow":
|
|
// // if(username === cfg.admin)
|
|
// cfg.stateMachines.follow.transitions[4].trigger()
|
|
// // cfg.stateMachines.follow.transitions[1].trigger()
|
|
// break;
|
|
// case "stay":
|
|
// // if(username === cfg.admin)
|
|
// cfg.stateMachines.follow.transitions[3].trigger()
|
|
// break;
|
|
case "echo":
|
|
// bot.chat(message_parts[1])
|
|
// bot.chat(message.slice(1))
|
|
cfg.quiet && bot.whisper(username, message) || bot.chat(message)
|
|
break;
|
|
case "autosleep":
|
|
case "sleep":
|
|
message_parts[1] = message_parts[0] == "autosleep" ? "auto" : message_parts[1]
|
|
switch (message_parts[1]) {
|
|
case "auto":
|
|
cfg.sleep.auto = !cfg.sleep.auto
|
|
bot.chat(`ok, ${cfg.sleep.auto ? "" : "not "}auto sleeping`)
|
|
bot.chat("/afk")
|
|
break;
|
|
case "silent":
|
|
case "quiet":
|
|
cfg.sleep.quiet = !cfg.sleep.quiet
|
|
bot.chat(`ok, ${cfg.sleep.quiet ? "" : "not "}sleeping quietly`)
|
|
break;
|
|
case "timeout":
|
|
if (!message_parts[2]) {
|
|
bot.chat(`sleeping every ${Math.round(cfg.sleep.timeout / 60 / 1000)}mins`)
|
|
return
|
|
}
|
|
const timeout = parseFloat(message_parts[2], 10)
|
|
if (timeout) {
|
|
cfg.sleep.timeout = timeout * 60 * 1000
|
|
cfg.sleep.timeoutFn = null
|
|
cfg.plugins.sleeper.sleep(true) //reset timer
|
|
bot.chat(`ok, next sleep attempt in ${timeout}mins`)
|
|
break;
|
|
}
|
|
default:
|
|
bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`)
|
|
break;
|
|
}
|
|
break; //todo; needed?
|
|
case "zzz":
|
|
cfg.plugins.sleeper.sleep()
|
|
case "afk":
|
|
bot.chat("/afk")
|
|
break;
|
|
case "awaken":
|
|
case "wake":
|
|
case "wakeup":
|
|
cfg.plugins.sleeper.wake()
|
|
break;
|
|
case "autoeat":
|
|
case "eat":
|
|
switch (message_parts[1]) {
|
|
case "auto":
|
|
cfg.eat.auto = !cfg.eat.auto
|
|
bot.chat(`ok, ${cfg.eat.auto ? "" : "not "}auto eating `)
|
|
break;
|
|
case "at":
|
|
case "set":
|
|
const amount = parseInt(message_parts[2], 10)
|
|
if (amount < 20 && amount > 0) {
|
|
cfg.eat.startAt = amount
|
|
cfg.eat.quiet || bot.chat(`ok, eating when hunger at ${amount}`)
|
|
}
|
|
break
|
|
case "silent":
|
|
case "quiet":
|
|
cfg.eat.quiet = !!!cfg.eat.quiet
|
|
break;
|
|
default:
|
|
cfg.plugins.eater.eat((err) => { if (err) { bot.chat(err.message) } })
|
|
// bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`)
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case "follow":
|
|
subcommand("go follow " + message_parts.slice(2).join(" "))
|
|
break;
|
|
case "come":
|
|
switch (message_parts[1]) {
|
|
case "close":
|
|
case "closer":
|
|
subcommand("go follow close")
|
|
break
|
|
default:
|
|
subcommand("go follow once")
|
|
}
|
|
break;
|
|
case "move":
|
|
case "go":
|
|
// TODO move most of the subcommands into mover.js?
|
|
const message_parts2 = message_parts.slice(2)
|
|
switch (message_parts[1]) {
|
|
case "init":
|
|
cfg.plugins.mover.initMoves()
|
|
break
|
|
case "near":
|
|
// message_parts2 = message_parts.slice(2)
|
|
switch (message_parts2.length) {
|
|
case 0:
|
|
cfg.plugins.mover.moveNear(bot.nearestEntity().position)
|
|
break
|
|
case 1:
|
|
switch (message_parts2[0]) {
|
|
case "me":
|
|
if (player) {
|
|
cfg.plugins.mover.moveNear(player.position)
|
|
} else {
|
|
cfg.quiet || bot.chat("can't see you")
|
|
}
|
|
break;
|
|
|
|
default:
|
|
const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
|
|
if (aPlayer) {
|
|
cfg.plugins.mover.moveNear(aPlayer.position)
|
|
} else {
|
|
cfg.quiet || bot.chat(`can't see ${message_parts[2]}`)
|
|
}
|
|
break;
|
|
}
|
|
break
|
|
case 2:
|
|
todo()
|
|
// bot.lookAt({}) goalxz?
|
|
break
|
|
case 3:
|
|
//TODO more checks
|
|
cfg.plugins.mover.moveNear(message_parts2)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
break
|
|
|
|
case "follow":
|
|
// message_parts2 = message_parts.slice(2)
|
|
switch (message_parts2.length) {
|
|
case 0:
|
|
cfg.plugins.mover.follow(bot.nearestEntity())
|
|
break
|
|
case 1:
|
|
let dist = 3
|
|
switch (message_parts2[0]) {
|
|
case "close":
|
|
dist = 1
|
|
case "me":
|
|
case "once":
|
|
if (player) {
|
|
cfg.plugins.mover.follow(player, message_parts2[0] === "me", dist)
|
|
} else {
|
|
cfg.quiet || bot.chat("can't see you")
|
|
}
|
|
break;
|
|
|
|
default:
|
|
const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
|
|
if (aPlayer) {
|
|
cfg.plugins.mover.follow(aPlayer)
|
|
} else {
|
|
cfg.quiet || bot.chat(`can't see ${message_parts[2]}`)
|
|
}
|
|
break;
|
|
}
|
|
break
|
|
// case 2:
|
|
// bot.lookAt({}) goalxz?
|
|
// break
|
|
// case 3:
|
|
//TODO more checks
|
|
// cfg.plugins.mover.moveNear(message_parts2)
|
|
// break
|
|
default:
|
|
todo()
|
|
break
|
|
}
|
|
break
|
|
case "stop":
|
|
cfg.plugins.mover.stop()
|
|
break
|
|
default:
|
|
return todo()
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case "attack":
|
|
case "rage":
|
|
case "ragemode":
|
|
case "guard":
|
|
switch (message_parts.length) {
|
|
case 1:
|
|
if (!player) {
|
|
bot.chat("can't see you.")
|
|
return
|
|
}
|
|
bot.chat('for glory!')
|
|
cfg.plugins.guard.guardArea(player.position)
|
|
break
|
|
case 2:
|
|
switch (message_parts[1]) {
|
|
case "self":
|
|
cfg.guard.self = !cfg.guard.self
|
|
bot.chat(`ok, ${cfg.guard.self ? "no longer being altruistic" : "self sacrifice!"}`)
|
|
return;
|
|
case "stop":
|
|
cfg.plugins.guard.stopGuarding()
|
|
bot.chat('no longer guarding this area.')
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
bot.chat("don't know wym")
|
|
}
|
|
// entity = new BehaviorGetClosestEntity(bot, bot.nearestEntity(), a => EntityFilters().MobsOnly(a))
|
|
// if (entity) {
|
|
// bot.attack(entity, true)
|
|
// } else {
|
|
// bot.chat('no nearby entities')
|
|
// }
|
|
// // bot.attack(new BehaviorGetClosestEntity(bot, {}, EntityFilters().MobsOnly()))
|
|
break;
|
|
case "find":
|
|
switch (message_parts.length) {
|
|
case 2:
|
|
cfg.plugins.finder.findBlocks(message_parts[1])
|
|
break
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break
|
|
// TODO move look (and maybe find) to informer plugin?
|
|
case "look":
|
|
case "lookat":
|
|
// const coords = v(message_parts.splice(1))
|
|
switch (message_parts.length) {
|
|
case 1:
|
|
bot.lookAt(bot.nearestEntity().position)
|
|
break
|
|
case 2:
|
|
switch (message_parts[1]) {
|
|
case "me":
|
|
if (player) {
|
|
bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
|
|
} else {
|
|
cfg.quiet || bot.chat("can't see you")
|
|
}
|
|
break;
|
|
|
|
case "this":
|
|
// TODO lookat the block the user is looking at
|
|
// Currently looks player position
|
|
if (player) {
|
|
bot.lookAt(player.position)
|
|
} else {
|
|
cfg.quiet || bot.chat("can't see you")
|
|
}
|
|
break
|
|
default:
|
|
const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
|
|
if (aPlayer) bot.lookAt((new v.Vec3(0, 1, 0)).add(aPlayer.position))
|
|
break;
|
|
}
|
|
break
|
|
case 3:
|
|
todo()
|
|
// bot.lookAt({})
|
|
break
|
|
case 4:
|
|
//TODO more checks
|
|
bot.lookAt(v(message_parts.splice(1)))
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
break
|
|
case "ride":
|
|
case "mount":
|
|
bot.mount(bot.nearestEntity())
|
|
break
|
|
case "getoff":
|
|
case "unmount":
|
|
case "dismount":
|
|
bot.dismount()
|
|
break
|
|
case "go":
|
|
bot.moveVehicle(0, 10)
|
|
break
|
|
// case "use":
|
|
// bot.useOn(bot.nearestEntity())
|
|
// break;
|
|
|
|
// // TODO move all inventory related tasks into inventory.js
|
|
case "craft":
|
|
cfg.plugins.inventory.craftItem(message_parts[1])
|
|
break
|
|
// case "give":
|
|
// // switch (message_parts[1]) {
|
|
// // case "hand":
|
|
// // case "right":
|
|
// // break;
|
|
// // case "left":
|
|
// // break;
|
|
// // // case "slot":
|
|
// // default:
|
|
// // let slot = parseInt(message_parts[1])
|
|
// // if (!isNaN(slot)) {
|
|
|
|
// // } else {
|
|
|
|
// // }
|
|
// // break;
|
|
// // }
|
|
// // break;
|
|
// case "take":
|
|
// // TODO take only what's requested, then throw all the rest
|
|
// // TODO take all
|
|
|
|
// TODO move subcommands to cfg.plugins.inventory.itemByName
|
|
case "toss":
|
|
case "drop":
|
|
if (!message_parts[1]) { return false } // FIXME, works but ugly
|
|
if (!mcData.findItemOrBlockByName(message_parts[1])) {
|
|
console.log("doesn't exist:", message_parts[1])
|
|
cfg.quiet || bot.chat(`item doesn't exist: ${message_parts[1]}`)
|
|
return false
|
|
}
|
|
const item = cfg.plugins.inventory.itemByName(message_parts[1])
|
|
if (!item) {
|
|
console.log("don't have:", message_parts[1])
|
|
cfg.quiet || bot.chat(`don't have item: ${message_parts[1]}`)
|
|
return false
|
|
}
|
|
switch (message_parts.length) {
|
|
case 2:
|
|
bot.tossStack(
|
|
item,
|
|
(err) => {
|
|
if (err) {
|
|
console.error(err)
|
|
cfg.quiet || bot.chat(err.message)
|
|
}
|
|
}
|
|
)
|
|
break
|
|
case 3:
|
|
const amount = parseInt(message_parts[2])
|
|
bot.toss(
|
|
item.type,
|
|
null, //metadata
|
|
amount,
|
|
(err) => {
|
|
if (err) {
|
|
console.error(err)
|
|
cfg.quiet || bot.chat(err.message)
|
|
}
|
|
}
|
|
)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
break;
|
|
case "location":
|
|
// TODO put in /lib/location
|
|
switch (message_parts[1]) {
|
|
case "add":
|
|
case "record":
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
case "where":
|
|
case "where?":
|
|
// TODO put in /lib/location
|
|
console.log(bot.entity.position)
|
|
if (cfg.mods.includes(username)) //anarchy
|
|
bot.chat(
|
|
bot.game.dimension.split(":", 2)[1].replace("_", " ")
|
|
+ " " + bot.entity.position.floored().toString()
|
|
)
|
|
break;
|
|
case "warp":
|
|
// if (message_parts[1] == "spawn")
|
|
// if (cfg.mods.includes(username)) //anarchy
|
|
bot.chat("/" + message)
|
|
break;
|
|
|
|
default:
|
|
if (cfg.mods.includes(username))
|
|
bot.chat("/" + message)
|
|
break;
|
|
|
|
}
|
|
}
|
|
subcommand(message)
|
|
} else {
|
|
// TODO, maybe extract to a new function `fuzzychat`?, so can address direct messages
|
|
switch (message) {
|
|
case "wassup":
|
|
case "what's cooking":
|
|
case "what's new":
|
|
case "what's up":
|
|
case "whats cooking":
|
|
case "whats new":
|
|
case "whats up":
|
|
case "sup":
|
|
case "suh":
|
|
fuzzyRespond([
|
|
"jus chilin", "nothin", "random stuff"
|
|
], 0.3, 3)
|
|
case "hi":
|
|
case "hey":
|
|
case "hello":
|
|
case "ola":
|
|
case "howdy":
|
|
case "heyo":
|
|
case "yo":
|
|
if (player) bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
|
|
|
|
// TODO sneak
|
|
// function swingArm() {
|
|
// if (swung > 0) {
|
|
// setTimeout(swing, 500 * Math.random())
|
|
// bot.p()
|
|
// swung--
|
|
// }
|
|
// }
|
|
|
|
setTimeout(swingArm, 1000, 4) // or sneak greating
|
|
return
|
|
case "F":
|
|
return fuzzyRespond(["F"], 0.9, 1)
|
|
case "RIP":
|
|
return fuzzyRespond(["F", "oh no"], 0.3, 2)
|
|
case "69":
|
|
case "cool":
|
|
Math.random() < 0.5 && bot.chat("nice!")
|
|
return
|
|
case "awesome":
|
|
case "superb":
|
|
case "nice":
|
|
case "nice!":
|
|
return fuzzyRespond(["cool", "very nice!"], 0.3, 5)
|
|
case "good bot":
|
|
return fuzzyRespond(["thanks", "why, thank you!", ":)", "(:", "you're too kind!"], 1, 5)
|
|
case "bad bot":
|
|
case "not nice":
|
|
return fuzzyRespond([":(", "):", "sorry"], 0.1, 10)
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const load = (config) => {
|
|
cfg = config
|
|
bot = cfg.bot
|
|
|
|
mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
|
|
for (const [key, fn] of Object.entries(events)) {
|
|
events_registered.push(
|
|
bot.on(key, fn)
|
|
)
|
|
}
|
|
}
|
|
|
|
const unload = () => {
|
|
console.log("events_registered:", events_registered.length, "of", bot._eventsCount)
|
|
for (const [key, fn] of Object.entries(events)) {
|
|
bot.off(key, fn)
|
|
events_registered.shift()
|
|
}
|
|
console.log("events_registered:", bot._eventsCount)
|
|
}
|
|
|
|
module.exports = { load, unload, events_registered, command } |