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 inventoryViewer = require('mineflayer-web-inventory'); | ||||||
|  |  | ||||||
| const commands = require('./commands'); | 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){ | for(let name in conf.mc.bots){ | ||||||
| 	if(CJbot.bots[name]) continue; | 	if(CJbot.bots[name]) continue; | ||||||
| @ -22,21 +24,72 @@ for(let name in conf.mc.bots){ | |||||||
|  |  | ||||||
| 	bot.on('onReady', async function(argument) { | 	bot.on('onReady', async function(argument) { | ||||||
| 		// inventoryViewer(bot.bot); | 		// 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); | 				bot.bot.on('message', (message, type)=>{ | ||||||
| 		await sleep(1000); | 					if(type === 'game_info') return; | ||||||
| 		bot.bot.setControlState('jump', true); | 					if(message.toString().startsWith('<') && message.toString().split('>')[0].includes(bot.bot.entity.username)) return; | ||||||
| 		setTimeout(()=> bot.bot.setControlState('jump', false), 5000) | 					console.log(`Message ${type}: ${message.toString()}`) | ||||||
|  | 					messages.push('>', message.toString()); | ||||||
|  | 				}); | ||||||
|  |  | ||||||
| 	}) | 				await sleep(500); | ||||||
| 				 | 				 | ||||||
| 	// bot.on('message', function(...args){ | 				let aiChat = await ai.run( | ||||||
| 	// 	console.log('message | ', ...args) | 					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 ; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					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 ()=>{ | (async ()=>{try{ | ||||||
| try{ |  | ||||||
| 	for(let name in CJbot.bots){ | 	for(let name in CJbot.bots){ | ||||||
| 		let bot = CJbot.bots[name]; | 		let bot = CJbot.bots[name]; | ||||||
| 		if(bot.autoConnect){ | 		if(bot.autoConnect){ | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
|  | process.env.DEBUG = 'mineflayer:*'; // Enables all debugging logs | ||||||
|  |  | ||||||
| const mineflayer = require('mineflayer'); | const mineflayer = require('mineflayer'); | ||||||
| const minecraftData = require('minecraft-data'); | const minecraftData = require('minecraft-data'); | ||||||
| const { pathfinder, Movements, goals: { GoalNear } } = require('mineflayer-pathfinder'); | const { pathfinder, Movements, goals: { GoalNear } } = require('mineflayer-pathfinder'); | ||||||
| @ -46,6 +48,8 @@ class CJbot{ | |||||||
| 		this.auth = args.auth || 'microsoft'; | 		this.auth = args.auth || 'microsoft'; | ||||||
| 		this.version = args.version || '1.20.1'; | 		this.version = args.version || '1.20.1'; | ||||||
|  |  | ||||||
|  | 		this.hasAi = args.hasAi; | ||||||
|  |  | ||||||
| 		// States if the bot should connect when its loaded | 		// States if the bot should connect when its loaded | ||||||
| 		this.autoReConnect = 'autoConnect' in args ? args.autoReConnect : true; | 		this.autoReConnect = 'autoConnect' in args ? args.autoReConnect : true; | ||||||
| 		this.autoConnect = 'autoConnect' in args  ? args.autoConnect : true; | 		this.autoConnect = 'autoConnect' in args  ? args.autoConnect : true; | ||||||
| @ -57,7 +61,7 @@ class CJbot{ | |||||||
|  |  | ||||||
| 	connect(){ | 	connect(){ | ||||||
| 		return new Promise((resolve, reject) =>{ | 		return new Promise((resolve, reject) =>{ | ||||||
|  | 			try{ | ||||||
| 				// Create the mineflayer instance | 				// Create the mineflayer instance | ||||||
| 				this.bot = mineflayer.createBot({ | 				this.bot = mineflayer.createBot({ | ||||||
| 					host: this.host, | 					host: this.host, | ||||||
| @ -70,16 +74,16 @@ class CJbot{ | |||||||
| 				// If an error happens before the login event, toss an error back to | 				// If an error happens before the login event, toss an error back to | ||||||
| 				// the caller of the function | 				// the caller of the function | ||||||
| 				let onError = this.bot.on('error', (m)=>{ | 				let onError = this.bot.on('error', (m)=>{ | ||||||
| 				console.log(this.bot.version, m.toString()) | 					console.log(this.name, m.toString()) | ||||||
| 					reject(m) | 					reject(m) | ||||||
| 				}) | 				}) | ||||||
|  |  | ||||||
| 				// If the connection ends before the login event, toss an error back | 				// If the connection ends before the login event, toss an error back | ||||||
| 				// to the caller of the function  | 				// to the caller of the function  | ||||||
| 			this.bot.on('end', (m)=>{ | 				this.bot.on('end', (reason, ...args)=>{ | ||||||
| 				console.log(this.name, 'Connection ended:', m); | 					console.log(this.name, 'Connection ended:', reason, ...args); | ||||||
| 					this.isReady = false; | 					this.isReady = false; | ||||||
| 				reject(m); | 					reject(reason); | ||||||
| 				}); | 				}); | ||||||
|  |  | ||||||
| 				// When the bot is ready, return to the caller success | 				// When the bot is ready, return to the caller success | ||||||
| @ -95,6 +99,10 @@ class CJbot{ | |||||||
| 				}catch(error){ | 				}catch(error){ | ||||||
| 					console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error) | 					console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error) | ||||||
| 				}}, 30000); | 				}}, 30000); | ||||||
|  | 			}catch(error){ | ||||||
|  | 				reject(error); | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -119,25 +127,25 @@ class CJbot{ | |||||||
|  |  | ||||||
| 		// Call the internal listeners when the bot is ready | 		// Call the internal listeners when the bot is ready | ||||||
| 		for(let callback of this.listeners.onReady || []){ | 		for(let callback of this.listeners.onReady || []){ | ||||||
| 			console.log('calling listener', callback) | 			console.log('calling listener', callback); | ||||||
| 			await callback.call(this) | 			await callback.call(this); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		this.isReady = true; | 		this.isReady = true; | ||||||
| 		this.__error() | 		this.__error(); | ||||||
| 		console.log('Bot is ready', this.bot.entity.username, this.username); | 		console.log('Bot is ready', this.bot.entity.username, this.username); | ||||||
| 		 | 		 | ||||||
| 		// Start chat listeners | 		// Start chat listeners | ||||||
| 		this.__listen(); | 		this.__listen(); | ||||||
| 	 | 	 | ||||||
| 	}catch(error){ | 	}catch(error){ | ||||||
| 		console.error('minecraft.js | __onReady | ', this.name, ' ', error) | 		console.error('minecraft.js | __onReady | ', this.name, ' ', error); | ||||||
| 	}} | 	}} | ||||||
|  |  | ||||||
| 	__startListeners(){ | 	__startListeners(){ | ||||||
| 		for(let event in this.listeners){ | 		for(let event in this.listeners){ | ||||||
| 			for(let callback of this.listeners[event]){ | 			for(let callback of this.listeners[event]){ | ||||||
| 				this.bot.on(event, callback) | 				this.bot.on(event, callback); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -264,7 +272,7 @@ class CJbot{ | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	__chatCoolDown(){ | 	__chatCoolDown(){ | ||||||
| 		return Math.floor(Math.random() * (3000 - 1500) + 1500); | 		return Math.floor(Math.random() * (3000 - 2000) + 2000); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	async say(...messages){ | 	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){ | 	async whisper(to, ...messages){ | ||||||
| 		await this.say(...messages.map(message=>`/msg ${to} ${message}`)); | 		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); | 				console.error(`Chat command error on ${cmd} from ${from}\n`, error); | ||||||
| 			} | 			} | ||||||
| 			this.__unLockCommand(); | 			this.__unLockCommand(); | ||||||
| 		}else{ | 		}/*else{ | ||||||
| 			this.whisper(from, `I dont know anything about ${cmd}`); | 			this.whisper(from, `I dont know anything about ${cmd}`); | ||||||
| 		} | 		}*/ | ||||||
| 	}catch(error){ | 	}catch(error){ | ||||||
| 		console.error('minecraft.js | __doCommand |', this.name, ' ', 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", |       "version": "1.0.0", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|  |         "@google/generative-ai": "^0.17.1", | ||||||
|         "axios": "^0.27.2", |         "axios": "^0.27.2", | ||||||
|         "dotenv": "^16.0.1", |         "dotenv": "^16.0.1", | ||||||
|         "extend": "^3.0.2", |         "extend": "^3.0.2", | ||||||
| @ -45,6 +46,14 @@ | |||||||
|         "node": "10 || 12 || 14 || 16 || 18" |         "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": { |     "node_modules/@types/component-emitter": { | ||||||
|       "version": "1.2.11", |       "version": "1.2.11", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", |       "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", |   "homepage": "https://github.com/wmantly/mc-cj-bot#readme", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@google/generative-ai": "^0.17.1", | ||||||
|     "axios": "^0.27.2", |     "axios": "^0.27.2", | ||||||
|     "dotenv": "^16.0.1", |     "dotenv": "^16.0.1", | ||||||
|     "extend": "^3.0.2", |     "extend": "^3.0.2", | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user