2023-12-21 15:11:38 -07:00
import { History } from './history.js' ;
import { Coder } from './coder.js' ;
2024-01-23 18:01:38 -06:00
import { initModes } from './modes.js' ;
2024-01-25 13:25:36 -08:00
import { Examples } from '../utils/examples.js' ;
import { initBot } from '../utils/mcdata.js' ;
import { sendRequest } from '../utils/gpt.js' ;
import { containsCommand , commandExists , executeCommand } from './commands/index.js' ;
2024-02-12 16:33:28 -08:00
import { ItemGoal } from './item_goal.js' ;
2023-11-07 09:44:56 -06:00
export class Agent {
2024-01-15 11:04:50 -06:00
async start ( name , profile = null , init _message = null ) {
2023-11-07 09:44:56 -06:00
this . name = name ;
2024-01-15 11:04:50 -06:00
this . examples = new Examples ( ) ;
2023-12-21 20:42:01 -07:00
this . history = new History ( this ) ;
2023-11-07 12:00:55 -06:00
this . coder = new Coder ( this ) ;
2024-02-12 16:33:28 -08:00
this . item _goal = new ItemGoal ( this ) ;
2023-11-19 15:34:53 -06:00
2024-01-30 16:43:30 -06:00
console . log ( 'Loading examples...' ) ;
2023-12-21 20:42:01 -07:00
this . history . load ( profile ) ;
2024-02-12 22:43:31 -08:00
this . item _goal . setGoals ( this . history . goals ) ;
2024-01-15 11:04:50 -06:00
await this . examples . load ( './src/examples.json' ) ;
await this . coder . load ( ) ;
2024-01-30 16:43:30 -06:00
console . log ( 'Logging in...' ) ;
2024-01-15 11:04:50 -06:00
this . bot = initBot ( name ) ;
2023-12-04 23:26:21 -08:00
2024-01-23 18:01:38 -06:00
initModes ( this ) ;
2023-12-04 23:26:21 -08:00
2023-12-26 14:42:45 -07:00
this . bot . on ( 'login' , async ( ) => {
2023-11-07 09:44:56 -06:00
console . log ( ` ${ this . name } logged in. ` ) ;
2024-01-15 11:04:50 -06:00
this . coder . clear ( ) ;
2023-12-26 14:42:45 -07:00
const ignore _messages = [
"Set own game mode to" ,
"Set the time to" ,
"Set the difficulty to" ,
"Teleported " ,
"Set the weather to" ,
"Gamerule "
] ;
2023-12-10 20:18:20 -06:00
this . bot . on ( 'chat' , ( username , message ) => {
2023-12-12 21:35:39 -08:00
if ( username === this . name ) return ;
2023-12-26 14:42:45 -07:00
if ( ignore _messages . some ( ( m ) => message . startsWith ( m ) ) ) return ;
2023-12-12 21:35:39 -08:00
console . log ( 'received message from' , username , ':' , message ) ;
2023-12-10 20:18:20 -06:00
2023-12-20 16:30:05 -08:00
this . handleMessage ( username , message ) ;
2023-12-12 21:35:39 -08:00
} ) ;
2024-01-14 14:33:25 -06:00
// set the bot to automatically eat food when hungry
this . bot . autoEat . options = {
priority : 'foodPoints' ,
startAt : 14 ,
2024-01-23 18:01:38 -06:00
bannedFood : [ "rotten_flesh" , "spider_eye" , "poisonous_potato" , "pufferfish" , "chicken" ]
2024-01-14 14:33:25 -06:00
} ;
2023-12-26 14:42:45 -07:00
2023-12-12 21:35:39 -08:00
if ( init _message ) {
2023-12-20 16:30:05 -08:00
this . handleMessage ( 'system' , init _message ) ;
2023-12-12 21:35:39 -08:00
} else {
2024-01-14 14:33:25 -06:00
this . bot . chat ( 'Hello world! I am ' + this . name ) ;
2023-12-12 21:35:39 -08:00
this . bot . emit ( 'finished_executing' ) ;
}
2024-01-23 18:01:38 -06:00
2024-01-25 13:25:36 -08:00
this . startEvents ( ) ;
2023-11-07 09:44:56 -06:00
} ) ;
2023-12-04 23:26:21 -08:00
}
2024-01-24 17:24:52 -06:00
cleanChat ( message ) {
// newlines are interpreted as separate chats, which triggers spam filters. replace them with spaces
message = message . replaceAll ( '\n' , ' ' ) ;
return this . bot . chat ( message ) ;
}
2023-12-20 16:30:05 -08:00
async handleMessage ( source , message ) {
2023-12-26 14:42:45 -07:00
if ( ! ! source && ! ! message )
await this . history . add ( source , message ) ;
2023-12-04 23:26:21 -08:00
2024-01-09 22:50:22 -06:00
const user _command _name = containsCommand ( message ) ;
if ( user _command _name ) {
2024-01-27 19:24:02 -06:00
if ( ! commandExists ( user _command _name ) ) {
this . bot . chat ( ` Command ' ${ user _command _name } ' does not exist. ` ) ;
return ;
}
2024-01-09 22:50:22 -06:00
this . bot . chat ( ` * ${ source } used ${ user _command _name . substring ( 1 ) } * ` ) ;
2024-01-11 15:59:52 -06:00
let execute _res = await executeCommand ( this , message ) ;
2024-01-13 12:10:15 -06:00
if ( user _command _name === '!newAction' ) {
// all user initiated commands are ignored by the bot except for this one
// add the preceding message to the history to give context for newAction
let truncated _msg = message . substring ( 0 , message . indexOf ( user _command _name ) ) . trim ( ) ;
this . history . add ( source , truncated _msg ) ;
}
2024-01-24 17:24:52 -06:00
if ( execute _res )
this . cleanChat ( execute _res ) ;
2024-01-09 22:50:22 -06:00
return ;
}
2023-12-20 16:30:05 -08:00
for ( let i = 0 ; i < 5 ; i ++ ) {
2024-01-15 11:04:50 -06:00
let history = await this . history . getHistory ( this . examples ) ;
let res = await sendRequest ( history , this . history . getSystemMessage ( ) ) ;
2023-12-20 16:30:05 -08:00
this . history . add ( this . name , res ) ;
2023-12-04 23:26:21 -08:00
2024-01-03 22:16:50 -08:00
let command _name = containsCommand ( res ) ;
2024-01-09 22:50:22 -06:00
if ( command _name ) { // contains query or command
2024-01-14 14:33:25 -06:00
console . log ( 'Command message:' , res ) ;
if ( ! commandExists ( command _name ) ) {
this . history . add ( 'system' , ` Command ${ command _name } does not exist. Use !newAction to perform custom actions. ` ) ;
2024-01-15 11:04:50 -06:00
console . log ( 'Agent hallucinated command:' , command _name )
2024-01-14 14:33:25 -06:00
continue ;
}
2024-01-03 22:16:50 -08:00
2024-01-11 15:59:52 -06:00
let pre _message = res . substring ( 0 , res . indexOf ( command _name ) ) . trim ( ) ;
2024-01-03 22:16:50 -08:00
2024-01-24 17:24:52 -06:00
this . cleanChat ( ` ${ pre _message } *used ${ command _name . substring ( 1 ) } * ` ) ;
2024-01-11 15:59:52 -06:00
let execute _res = await executeCommand ( this , res ) ;
2024-01-03 22:16:50 -08:00
2024-01-09 22:50:22 -06:00
console . log ( 'Agent executed:' , command _name , 'and got:' , execute _res ) ;
2024-01-03 22:16:50 -08:00
if ( execute _res )
this . history . add ( 'system' , execute _res ) ;
else
break ;
2023-12-20 16:30:05 -08:00
}
else { // conversation response
2024-01-24 17:24:52 -06:00
this . cleanChat ( res ) ;
2023-12-20 16:30:05 -08:00
console . log ( 'Purely conversational response:' , res ) ;
break ;
}
2023-12-04 23:26:21 -08:00
}
this . history . save ( ) ;
2023-12-16 12:08:47 -06:00
this . bot . emit ( 'finished_executing' ) ;
2023-11-07 09:44:56 -06:00
}
2024-01-23 18:01:38 -06:00
2024-01-25 13:25:36 -08:00
startEvents ( ) {
// Custom events
this . bot . on ( 'time' , ( ) => {
if ( this . bot . time . timeOfDay == 0 )
this . bot . emit ( 'sunrise' ) ;
else if ( this . bot . time . timeOfDay == 6000 )
this . bot . emit ( 'noon' ) ;
else if ( this . bot . time . timeOfDay == 12000 )
this . bot . emit ( 'sunset' ) ;
else if ( this . bot . time . timeOfDay == 18000 )
this . bot . emit ( 'midnight' ) ;
} ) ;
this . bot . on ( 'health' , ( ) => {
if ( this . bot . health < 20 )
this . bot . emit ( 'damaged' ) ;
} ) ;
// Logging callbacks
2024-01-24 17:24:52 -06:00
this . bot . on ( 'error' , ( err ) => {
console . error ( 'Error event!' , err ) ;
} ) ;
this . bot . on ( 'end' , ( reason ) => {
console . warn ( 'Bot disconnected! Killing agent process.' , reason )
2024-01-23 18:01:38 -06:00
process . exit ( 1 ) ;
} ) ;
this . bot . on ( 'death' , ( ) => {
2024-02-05 13:48:02 -06:00
this . coder . cancelResume ( ) ;
2024-01-23 18:01:38 -06:00
this . coder . stop ( ) ;
} ) ;
2024-01-24 17:24:52 -06:00
this . bot . on ( 'kicked' , ( reason ) => {
console . warn ( 'Bot kicked!' , reason ) ;
process . exit ( 1 ) ;
} ) ;
2024-01-23 18:01:38 -06:00
this . bot . on ( 'messagestr' , async ( message , _ , jsonMsg ) => {
if ( jsonMsg . translate && jsonMsg . translate . startsWith ( 'death' ) && message . startsWith ( this . name ) ) {
console . log ( 'Agent died: ' , message ) ;
this . handleMessage ( 'system' , ` You died with the final message: ' ${ message } '. Previous actions were stopped and you have respawned. Notify the user and perform any necessary actions. ` ) ;
}
} ) ;
2024-02-12 16:33:28 -08:00
2024-01-26 12:11:32 -08:00
this . bot . on ( 'idle' , ( ) => {
2024-02-02 15:34:17 -06:00
this . bot . modes . unPauseAll ( ) ;
2024-02-12 16:33:28 -08:00
if ( this . coder . resume _func != null )
this . coder . executeResume ( ) ;
else
this . item _goal . executeNext ( ) ;
2024-01-26 12:11:32 -08:00
} ) ;
2024-01-26 13:18:30 -06:00
// This update loop ensures that each update() is called one at a time, even if it takes longer than the interval
const INTERVAL = 300 ;
setTimeout ( async ( ) => {
while ( true ) {
let start = Date . now ( ) ;
await this . bot . modes . update ( ) ;
let remaining = INTERVAL - ( Date . now ( ) - start ) ;
if ( remaining > 0 ) {
await new Promise ( ( resolve ) => setTimeout ( resolve , remaining ) ) ;
}
}
} , INTERVAL ) ;
2024-02-12 16:33:28 -08:00
this . bot . emit ( 'idle' ) ;
2024-01-23 18:01:38 -06:00
}
2024-01-26 15:41:55 -06:00
isIdle ( ) {
return ! this . coder . executing && ! this . coder . generating ;
}
2023-11-07 09:44:56 -06:00
}