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" :
switch ( message _parts . length ) {
case 1 :
subcommand ( "go follow me" )
break ;
default :
subcommand ( "go " + message )
break ;
}
break ;
case "come" :
switch ( message _parts [ 1 ] ) {
case "close" :
case "closer" :
subcommand ( "go follow close" )
break
case "up" :
case "down" :
cfg . plugins . mover . moveY ( player . position )
break
default :
subcommand ( "go follow once" )
}
break ;
case "ride" :
case "mount" :
cfg . plugins . mover . command ( message _parts )
break
case "unride" :
case "getoff" :
case "unmount" :
case "dismount" :
bot . dismount ( )
bot . vehicle = void 0
break
case "move" :
case "go" :
cfg . plugins . mover . command ( message _parts . slice ( 1 ) , player )
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 "info" :
cfg . plugins . informer . command ( message _parts . splice ( 1 ) )
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 "sm" :
case "step" :
cfg . plugins . statemachine ? . command ? . (
message _parts [ 0 ] == "sm" ? message _parts . slice ( 1 ) : message _parts , player
)
// TODO refactor into plugin detection and command exec function
// safecommand(plugin_name, message_parts, player)
// message_parts includes command?
|| bot . chat ( "statemachine plugin not loaded" )
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 }