AI #2
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);
|
||||||
|
|
||||||
|
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){
|
try{
|
||||||
// console.log('message | ', ...args)
|
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,44 +61,48 @@ class CJbot{
|
|||||||
|
|
||||||
connect(){
|
connect(){
|
||||||
return new Promise((resolve, reject) =>{
|
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
|
// If an error happens before the login event, toss an error back to
|
||||||
this.bot = mineflayer.createBot({
|
// the caller of the function
|
||||||
host: this.host,
|
let onError = this.bot.on('error', (m)=>{
|
||||||
username: this.username,
|
console.log(this.name, m.toString())
|
||||||
password: this.password,
|
reject(m)
|
||||||
version: this.version,
|
})
|
||||||
auth: this.auth,
|
|
||||||
});
|
|
||||||
|
|
||||||
// If an error happens before the login event, toss an error back to
|
// If the connection ends before the login event, toss an error back
|
||||||
// the caller of the function
|
// to the caller of the function
|
||||||
let onError = this.bot.on('error', (m)=>{
|
this.bot.on('end', (reason, ...args)=>{
|
||||||
console.log(this.bot.version, m.toString())
|
console.log(this.name, 'Connection ended:', reason, ...args);
|
||||||
reject(m)
|
this.isReady = false;
|
||||||
})
|
reject(reason);
|
||||||
|
});
|
||||||
|
|
||||||
// If the connection ends before the login event, toss an error back
|
// When the bot is ready, return to the caller success
|
||||||
// to the caller of the function
|
this.bot.on('login', ()=>{
|
||||||
this.bot.on('end', (m)=>{
|
this.__onReady()
|
||||||
console.log(this.name, 'Connection ended:', m);
|
resolve()
|
||||||
this.isReady = false;
|
});
|
||||||
reject(m);
|
|
||||||
});
|
|
||||||
|
|
||||||
// When the bot is ready, return to the caller success
|
// Set a timer to try to connect again in 30 seconds if the bot is
|
||||||
this.bot.on('login', ()=>{
|
// not connected
|
||||||
this.__onReady()
|
setTimeout(async ()=>{try{
|
||||||
resolve()
|
if(this.autoReConnect && !this.isReady) await this.connect();
|
||||||
});
|
}catch(error){
|
||||||
|
console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error)
|
||||||
// Set a timer to try to connect again in 30 seconds if the bot is
|
}}, 30000);
|
||||||
// not connected
|
|
||||||
setTimeout(async ()=>{try{
|
|
||||||
if(this.autoReConnect && !this.isReady) await this.connect();
|
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.error('minecraft.js | connect | setTimeout |', this.name, ' ', error)
|
reject(error);
|
||||||
}}, 30000);
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user