'use strict'; const {sleep} = require('../utils'); const mineflayer = require('mineflayer'); class CJbot{ isReady = false; listeners = {}; nextChatTime = 1500; commandLock = false; commandCollDownTime = 1500; commands = {}; combatTag = false; doLogOut = false; static bots = {}; static __addLock = false; static __toConnect = []; constructor(args){ this.name = args.name; this.username = args.username; this.password = args.password; this.host = args.host; this.auth = args.auth || 'microsoft'; this.version = args.version || '1.19.2'; this.autoReConnect = 'autoConnect' in args ? args.autoReConnect : true; this.autoConnect = 'autoConnect' in args ? args.autoConnect : true; if(this.autoReConnect) this.__autoReConnect() } connect(){ return new Promise((resolve, reject) =>{ // let cancle = setTimeout(()=>{ // reject('TimeOut') // }, 10000) // resolve = (...args)=>{ // clearTimeout(cancle); // return resolve(...args); // } // reject = (...args)=>{ // // clearTimeout(cancle); // return reject(...args); // } this.bot = mineflayer.createBot({ host: this.host, username: this.username, password: this.password, version: this.version, auth: this.auth, }); // this.bot.on('message', (m)=> console.log(m.toString())) this.bot.on('error', (m)=>{ console.log(this.bot.version, m.toString()) reject(m) }) this.bot.on('end', (m)=>{ console.log(this.name, 'Connection ended:', m); this.isReady = false; reject(m); }); this.bot.on('login', ()=>{ this.__onReady() resolve() }); setTimeout(async ()=>{try{ if(this.autoReConnect && !this.isReady) await this.connect(); }catch(error){ console.error('minecraft.js/connect/setTimeout/', this.name, ' ', error) }}, 30000); }); } static once(event){ return new Promise((resolve, reject)=> this.bot.once(event, resolve)); } async __onReady(){try{ this.__startListeners(); // console.log('listeners', this.listeners.onReady) for(let callback of this.listeners.onReady || []){ console.log('calling listener', callback) await callback(this) } this.isReady = true; this.__error() console.log('Bot is ready', this.bot.entity.username, this.username); // Start chat listeners this.__listen(); }catch(error){ console.error('minecraft.js/__onReady/', this.name, ' ', error) }} __startListeners(){ for(let event in this.listeners){ for(let callback of this.listeners[event]){ this.bot.on(event, callback) } } } on(event, callback){ if(!this.listeners[event]) this.listeners[event] = []; this.listeners[event].push(callback); if(this.isReady){ if(event === 'onReady') callback(this); else this.bot.on(event, callback); } return event === 'onReady' ? true : ()=> this.bot.off(listener, callback); } __autoReConnect(){ try{ console.log('auto connect function') this.on('end', async (reason)=>{ console.error('_autorestart MC on end', reason) await sleep('30000') this.connect() }); this.on('kick', console.error) this.on('error', (error)=>{ console.error('MC on error', error); this.connect(); }); }catch(error){ console.error('error in __autoReConnect', error); } } __error(message){ this.bot.on('error', (error)=>{ console.error(`ERROR!!! MC bot ${this.username} on ${this.host} had an error:\n`, error) }); } /* Chat and messaging*/ __listen(){ this.bot.on('chat', (from, message)=>{ // Ignore messages from this bot if(from === this.bot.entity.username) return; // Filter messages to this bot if(message.startsWith(this.bot.entity.username)){ this.__doCommand( from, message.replace(`${this.bot.entity.username} ` , ' ').trim() ) } }); this.bot.on('whisper', (from, message)=>{ this.__doCommand( from, message.replace(`${this.bot.entity.username} ` , ' ').trim() ) }); this.bot.on('message', (message, type)=>{ if(message.toString().includes(' invited you to teleport to him.')){ // teleport invite // console.log('ChatBot on message', message.toString()) this.__doCommand(message.toString().split(' ')[0], '.invite'); } if(message.toString().includes('You are combat tagged by')){ try{ this.combatTag = true; console.log('was attacked by') let attacker = message.toString().split('. ')[0].replace('You are combat tagged by ', '') console.log('was attacked by', attacker) // teleport invite this.whisper(attacker, 'Please do not attack me, I am a bot.') }catch(error){ console.log('error!!!!!!', error) } } if(message.toString().includes('You are no longer in combat. You may now logout.')){ this.combatTag = false if(this.doLogOut){ this.quit() } } }); } quit(force){ if(!this.combatTag || force){ this.bot.quit(); }else{ console.log('Logout prevented due to combatTag'); this.doLogOut = true; } } __chatCoolDown(){ return Math.floor(Math.random() * (3000 - 1500) + 1500); } async say(...messages){ for(let message of messages){ if(this.nextChatTime > Date.now()){ await sleep(this.nextChatTime-Date.now()+1) } this.bot.chat(message); this.nextChatTime = Date.now() + this.__chatCoolDown(); } } async whisper(to, ...messages){ await this.say(...messages.map(message=>`/msg ${to} ${message}`)); } /* Commands */ async __unLockCommand(time){ await sleep(this.commandCollDownTime); this.commandLock = false; } __reduceCommands(from){ return Object.keys(this.commands).filter(command =>{ if (this.commands[command].allowed && !this.commands[command].allowed.includes(from)) return false; return true; }); } async __doCommand(from, command){try{ if(this.commandLock){ this.whisper(from, `cool down, try again in ${this.commandCollDownTime/1000} seconds...`); return ; } let [cmd, ...parts] = command.split(/\s+/); if(this.__reduceCommands(from).includes(cmd)){ this.commandLock = true; try{ await this.commands[cmd].function.call(this, from, ...parts); }catch(error){ this.whisper(from, `The command encountered an error.`); console.error(`Chat command error on ${cmd} from ${from}\n`, error); } this.__unLockCommand(); }else{ this.whisper(from, `I dont know anything about ${cmd}`); } }catch(error){ console.error('minecraft.js/__doCommand/', this.name, ' ', error) }} addCommand(name, obj){ if(this.commands[name]) return false; this.commands[name] = obj; } getPlayers(){ for (let [username, value] of Object.entries(this.bot.players)){ value.lvl = Number(value.displayName.extra[0].text) } return this.bot.players; } } module.exports = {CJbot};