forked from wmantly/mc-bot-town
		
	AI
This commit is contained in:
		
							
								
								
									
										67
									
								
								nodejs/controller/ai.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								nodejs/controller/ai.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const conf = require('../conf'); | ||||
|  | ||||
| const { | ||||
| 	GoogleGenerativeAI, | ||||
| 	HarmCategory, | ||||
| 	HarmBlockThreshold, | ||||
| } = require("@google/generative-ai"); | ||||
|  | ||||
| const genAI = new GoogleGenerativeAI(conf.ai.key); | ||||
|  | ||||
| const model = genAI.getGenerativeModel({ | ||||
| 	model: "gemini-1.5-flash", | ||||
| }); | ||||
|  | ||||
| const generationConfig = { | ||||
| 	temperature: 1, | ||||
| 	topP: 0.95, | ||||
| 	topK: 64, | ||||
| 	maxOutputTokens: 8192, | ||||
| 	responseMimeType: "application/json", | ||||
| }; | ||||
|  | ||||
| async function run(name, interval, players) { | ||||
| 	const chatSession = model.startChat({ | ||||
| 		generationConfig, | ||||
| 		safetySettings:[ | ||||
| 			// See https://ai.google.dev/gemini-api/docs/safety-settings | ||||
| 			{ | ||||
| 				category: HarmCategory.HARM_CATEGORY_HARASSMENT, | ||||
| 				threshold: HarmBlockThreshold.BLOCK_NONE, | ||||
| 			}, | ||||
| 			{ | ||||
| 				category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, | ||||
| 				threshold: HarmBlockThreshold.BLOCK_NONE, | ||||
| 			}, | ||||
| 			{ | ||||
| 				category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, | ||||
| 				threshold: HarmBlockThreshold.BLOCK_NONE, | ||||
| 			}, | ||||
| 			{ | ||||
| 				category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, | ||||
| 				threshold: HarmBlockThreshold.BLOCK_NONE, | ||||
| 			}, | ||||
| 		], | ||||
| 		history: [ | ||||
| 			{ | ||||
| 				role: "user", | ||||
| 				parts: [ | ||||
| 					{text: conf.ai.prompt(name, interval, players)}, | ||||
| 				], | ||||
| 			}, | ||||
| 			{ | ||||
| 				role: "model", | ||||
| 				parts: [ | ||||
| 					{text: "Chat stuff"}, | ||||
| 				], | ||||
| 			} | ||||
| 		], | ||||
| 	}); | ||||
|  | ||||
| 	return chatSession; | ||||
| } | ||||
|  | ||||
| module.exports = {run}; | ||||
| // run(); | ||||
| @ -7,7 +7,9 @@ const {CJbot} = require('../model/minecraft'); | ||||
| const inventoryViewer = require('mineflayer-web-inventory'); | ||||
|  | ||||
| const commands = require('./commands'); | ||||
| const {onJoin} = require('./player_list') | ||||
| const {onJoin} = require('./player_list'); | ||||
| const ai = require('./ai'); | ||||
|  | ||||
|  | ||||
| for(let name in conf.mc.bots){ | ||||
| 	if(CJbot.bots[name]) continue; | ||||
| @ -22,21 +24,72 @@ for(let name in conf.mc.bots){ | ||||
|  | ||||
| 	bot.on('onReady', async function(argument) { | ||||
| 		// inventoryViewer(bot.bot); | ||||
| 		try{ | ||||
| 			// onJoin(bot); | ||||
| 			// await sleep(1000); | ||||
| 			// bot.bot.setControlState('jump', true); | ||||
| 			// setTimeout(()=> bot.bot.setControlState('jump', false), 5000); | ||||
| 			if(bot.hasAi){ | ||||
| 				console.log(`${bot.bot.entity.username} has AI`); | ||||
| 				let messages = []; | ||||
|  | ||||
| 		onJoin(bot); | ||||
| 		await sleep(1000); | ||||
| 		bot.bot.setControlState('jump', true); | ||||
| 		setTimeout(()=> bot.bot.setControlState('jump', false), 5000) | ||||
| 				bot.bot.on('message', (message, type)=>{ | ||||
| 					if(type === 'game_info') return; | ||||
| 					if(message.toString().startsWith('<') && message.toString().split('>')[0].includes(bot.bot.entity.username)) return; | ||||
| 					console.log(`Message ${type}: ${message.toString()}`) | ||||
| 					messages.push('>', message.toString()); | ||||
| 				}); | ||||
|  | ||||
| 	}) | ||||
| 				await sleep(500); | ||||
| 				 | ||||
| 				let aiChat = await ai.run( | ||||
| 					bot.bot.entity.username, | ||||
| 					conf.ai.interval, | ||||
| 					Object.values(bot.getPlayers()).map(player=>`<[${player.lvl}] ${player.username}>`).join('\n') | ||||
| 				); | ||||
| 				 | ||||
| 				setInterval(async ()=>{ | ||||
| 					let result; | ||||
| 					if(messages.length ===0) return; | ||||
| 					 | ||||
| 					try{ | ||||
| 						result = await aiChat.sendMessage(messages); | ||||
| 					}catch(error){ | ||||
| 						console.log('error AI API', error, result); | ||||
| 						messages = []; | ||||
| 						return ; | ||||
| 					} | ||||
|  | ||||
| 	// bot.on('message', function(...args){ | ||||
| 	// 	console.log('message | ', ...args) | ||||
| 	// }) | ||||
| 					try{ | ||||
| 						messages = []; | ||||
| 						if(!result.response.text()) return; | ||||
|  | ||||
| 						for(let message of JSON.parse(result.response.text())){ | ||||
| 							console.log('toSay', message.delay, message.text); | ||||
| 							if(message.text === '___') return; | ||||
| 							setTimeout(async (message)=>{ | ||||
| 								await bot.sayAiSafe(message.text); | ||||
| 							}, message.delay*1000, message); | ||||
| 						} | ||||
| 					}catch(error){ | ||||
| 						console.log('Error in AI message loop', error, result); | ||||
| 					} | ||||
| 				}, conf.ai.interval*1000); | ||||
| 			} | ||||
|  | ||||
| 		}catch(error){ | ||||
| 			console.log('error in onReady', error); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	bot.on('error', console.log); | ||||
|  | ||||
| 	// bot.on('message', function(message, type){ | ||||
| 	// 	console.log(`${type}: ${message.toString()}`) | ||||
| 	// }); | ||||
| } | ||||
|  | ||||
| (async ()=>{ | ||||
| try{ | ||||
| (async ()=>{try{ | ||||
| 	for(let name in CJbot.bots){ | ||||
| 		let bot = CJbot.bots[name]; | ||||
| 		if(bot.autoConnect){ | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| process.env.DEBUG = 'mineflayer:*'; // Enables all debugging logs | ||||
|  | ||||
| const mineflayer = require('mineflayer'); | ||||
| const minecraftData = require('minecraft-data'); | ||||
| const { pathfinder, Movements, goals: { GoalNear } } = require('mineflayer-pathfinder'); | ||||
| @ -46,6 +48,8 @@ class CJbot{ | ||||
| 		this.auth = args.auth || 'microsoft'; | ||||
| 		this.version = args.version || '1.20.1'; | ||||
|  | ||||
| 		this.hasAi = args.hasAi; | ||||
|  | ||||
| 		// States if the bot should connect when its loaded | ||||
| 		this.autoReConnect = 'autoConnect' in args ? args.autoReConnect : true; | ||||
| 		this.autoConnect = 'autoConnect' in args  ? args.autoConnect : true; | ||||
| @ -57,44 +61,48 @@ class CJbot{ | ||||
|  | ||||
| 	connect(){ | ||||
| 		return new Promise((resolve, reject) =>{ | ||||
| 			try{ | ||||
| 				// Create the mineflayer instance | ||||
| 				this.bot = mineflayer.createBot({ | ||||
| 					host: this.host, | ||||
| 					username: this.username, | ||||
| 					password: this.password, | ||||
| 					version: this.version, | ||||
| 					auth: this.auth, | ||||
| 				}); | ||||
|  | ||||
| 			// Create the mineflayer instance | ||||
| 			this.bot = mineflayer.createBot({ | ||||
| 				host: this.host, | ||||
| 				username: this.username, | ||||
| 				password: this.password, | ||||
| 				version: this.version, | ||||
| 				auth: this.auth, | ||||
| 			}); | ||||
| 				// If an error happens before the login event, toss an error back to | ||||
| 				// the caller of the function | ||||
| 				let onError = this.bot.on('error', (m)=>{ | ||||
| 					console.log(this.name, m.toString()) | ||||
| 					reject(m) | ||||
| 				}) | ||||
|  | ||||
| 			// If an error happens before the login event, toss an error back to | ||||
| 			// the caller of the function | ||||
| 			let onError = this.bot.on('error', (m)=>{ | ||||
| 				console.log(this.bot.version, m.toString()) | ||||
| 				reject(m) | ||||
| 			}) | ||||
| 				// If the connection ends before the login event, toss an error back | ||||
| 				// to the caller of the function  | ||||
| 				this.bot.on('end', (reason, ...args)=>{ | ||||
| 					console.log(this.name, 'Connection ended:', reason, ...args); | ||||
| 					this.isReady = false; | ||||
| 					reject(reason); | ||||
| 				}); | ||||
|  | ||||
| 			// If the connection ends before the login event, toss an error back | ||||
| 			// to the caller of the function  | ||||
| 			this.bot.on('end', (m)=>{ | ||||
| 				console.log(this.name, 'Connection ended:', m); | ||||
| 				this.isReady = false; | ||||
| 				reject(m); | ||||
| 			}); | ||||
| 				// When the bot is ready, return to the caller success | ||||
| 				this.bot.on('login', ()=>{ | ||||
| 					this.__onReady() | ||||
| 					resolve() | ||||
| 				}); | ||||
|  | ||||
| 			// When the bot is ready, return to the caller success | ||||
| 			this.bot.on('login', ()=>{ | ||||
| 				this.__onReady() | ||||
| 				resolve() | ||||
| 			}); | ||||
|  | ||||
| 			// Set a timer to try to connect again in 30 seconds if the bot is | ||||
| 			// not connected | ||||
| 			setTimeout(async ()=>{try{ | ||||
| 				if(this.autoReConnect && !this.isReady) await this.connect(); | ||||
| 				// Set a timer to try to connect again in 30 seconds if the bot is | ||||
| 				// not connected | ||||
| 				setTimeout(async ()=>{try{ | ||||
| 					if(this.autoReConnect && !this.isReady) await this.connect(); | ||||
| 				}catch(error){ | ||||
| 					console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error) | ||||
| 				}}, 30000); | ||||
| 			}catch(error){ | ||||
| 				console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error) | ||||
| 			}}, 30000); | ||||
| 				reject(error); | ||||
| 			} | ||||
|  | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| @ -119,25 +127,25 @@ class CJbot{ | ||||
|  | ||||
| 		// Call the internal listeners when the bot is ready | ||||
| 		for(let callback of this.listeners.onReady || []){ | ||||
| 			console.log('calling listener', callback) | ||||
| 			await callback.call(this) | ||||
| 			console.log('calling listener', callback); | ||||
| 			await callback.call(this); | ||||
| 		} | ||||
|  | ||||
| 		this.isReady = true; | ||||
| 		this.__error() | ||||
| 		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) | ||||
| 		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) | ||||
| 				this.bot.on(event, callback); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -264,7 +272,7 @@ class CJbot{ | ||||
| 	} | ||||
|  | ||||
| 	__chatCoolDown(){ | ||||
| 		return Math.floor(Math.random() * (3000 - 1500) + 1500); | ||||
| 		return Math.floor(Math.random() * (3000 - 2000) + 2000); | ||||
| 	} | ||||
|  | ||||
| 	async say(...messages){ | ||||
| @ -278,6 +286,16 @@ class CJbot{ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async sayAiSafe(...messages){ | ||||
| 		for(let message of messages){ | ||||
| 			if(message.startsWith('/') && !(message.startsWith('/msg') || message.startsWith('/help'))){ | ||||
| 				console.log('bot tried to execute bad command', message); | ||||
| 				message = '.'+message; | ||||
| 			} | ||||
| 			await this.say(message); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async whisper(to, ...messages){ | ||||
| 		await this.say(...messages.map(message=>`/msg ${to} ${message}`)); | ||||
| 	} | ||||
| @ -314,9 +332,9 @@ class CJbot{ | ||||
| 				console.error(`Chat command error on ${cmd} from ${from}\n`, error); | ||||
| 			} | ||||
| 			this.__unLockCommand(); | ||||
| 		}else{ | ||||
| 		}/*else{ | ||||
| 			this.whisper(from, `I dont know anything about ${cmd}`); | ||||
| 		} | ||||
| 		}*/ | ||||
| 	}catch(error){ | ||||
| 		console.error('minecraft.js | __doCommand |', this.name, ' ', error) | ||||
| 	}} | ||||
|  | ||||
							
								
								
									
										9
									
								
								nodejs/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								nodejs/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -9,6 +9,7 @@ | ||||
|       "version": "1.0.0", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@google/generative-ai": "^0.17.1", | ||||
|         "axios": "^0.27.2", | ||||
|         "dotenv": "^16.0.1", | ||||
|         "extend": "^3.0.2", | ||||
| @ -45,6 +46,14 @@ | ||||
|         "node": "10 || 12 || 14 || 16 || 18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@google/generative-ai": { | ||||
|       "version": "0.17.1", | ||||
|       "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.17.1.tgz", | ||||
|       "integrity": "sha512-TgWz02c5l2XJlEDys81UVat5+Qg9xqmYah7tQt6xlsBwFvzIFPz64aZFGd1av2sxT22NsssqLATjNsusAIJICA==", | ||||
|       "engines": { | ||||
|         "node": ">=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/component-emitter": { | ||||
|       "version": "1.2.11", | ||||
|       "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
|   }, | ||||
|   "homepage": "https://github.com/wmantly/mc-cj-bot#readme", | ||||
|   "dependencies": { | ||||
|     "@google/generative-ai": "^0.17.1", | ||||
|     "axios": "^0.27.2", | ||||
|     "dotenv": "^16.0.1", | ||||
|     "extend": "^3.0.2", | ||||
|  | ||||
		Reference in New Issue
	
	Block a user