feat: bot internal event & health & message & death

This commit is contained in:
Neko Ayaka
2025-01-07 01:31:35 +08:00
parent d4d1e8135a
commit 4fc1c4e44a
2 changed files with 183 additions and 1 deletions
+174 -1
View File
@@ -1,3 +1,4 @@
import type { BotInternalEventHandlers, BotInternalEvents } from './events'
import { useLogg } from '@guiiai/logg'
import mineflayer, { type Bot, type BotOptions } from 'mineflayer'
@@ -26,6 +27,9 @@ export interface BotContext {
lastDamageTime: number
lastDamageTaken: number
}
emit: (event: BotInternalEvents) => any
eventListeners: Record<BotInternalEvents, Array<BotInternalEventHandlers[BotInternalEvents]>>
}
export interface Component {
@@ -59,8 +63,36 @@ export function createBot(options: BotOptions): Bot {
lastDamageTime: 0,
lastDamageTaken: 0,
},
emit: (event: BotInternalEvents) => {
if (!ctx)
return
const listeners = ctx.eventListeners[event]
if (listeners) {
listeners.forEach(listener => listener())
}
},
eventListeners: {
'time:sunrise': [],
'time:noon': [],
'time:sunset': [],
'time:midnight': [],
},
}
ctx.bot.on('time', () => {
if (!ctx)
return
if (ctx.bot.time.timeOfDay === 0)
ctx.emit('time:sunrise')
else if (ctx.bot.time.timeOfDay === 6000)
ctx.emit('time:noon')
else if (ctx.bot.time.timeOfDay === 12000)
ctx.emit('time:sunset')
else if (ctx.bot.time.timeOfDay === 18000)
ctx.emit('time:midnight')
})
ctx.bot.on('health', () => {
if (!ctx)
return
@@ -84,8 +116,27 @@ export function createBot(options: BotOptions): Bot {
logger.error('Bot died')
})
ctx.bot.on('messagestr', () => {
ctx.bot.on('messagestr', async (message, _, jsonMsg) => {
if (!ctx)
return
// jsonMsg.translate:
// - death.attack.player
// message:
// - <bot username> was slain by <player / entity>
// - <bot username> drowned
if (jsonMsg.translate && jsonMsg.translate.startsWith('death') && message.startsWith(ctx.botName)) {
const deathPos = ctx.bot.entity.position
// this.memory_bank.rememberPlace('last_death_position', deathPos.x, deathPos.y, deathPos.z)
let deathPosStr: string | undefined
if (deathPos) {
deathPosStr = `x: ${deathPos.x.toFixed(2)}, y: ${deathPos.y.toFixed(2)}, z: ${deathPos.x.toFixed(2)}`
}
const dimension = ctx.bot.game.dimension
await handleMessage(ctx, 'system', `You died at position ${deathPosStr || 'unknown'} in the ${dimension} dimension with the final message: '${message}'. Your place of death has been saved as 'last_death_position' if you want to return. Previous actions were stopped and you have re-spawned.`)
}
})
ctx.bot.on('end', (reason) => {
@@ -104,6 +155,128 @@ export function createBot(options: BotOptions): Bot {
return ctx.bot
}
async function handleMessage(ctx: BotContext, source: string, message: string, maxResponses: number = Infinity) {
// if (!source || !message) {
// console.warn('Received empty message from', source);
// return false;
// }
// let used_command = false;
// if (maxResponses === null) {
// maxResponses = settings.max_commands === -1 ? Infinity : settings.max_commands;
// }
// if (maxResponses === -1) {
// maxResponses = Infinity;
// }
// const self_prompt = source === 'system' || source === ctx.botName;
// const from_other_bot = convoManager.isOtherAgent(source);
// if (!self_prompt && !from_other_bot) { // from user, check for forced commands
// const user_command_name = containsCommand(message);
// if (user_command_name) {
// if (!commandExists(user_command_name)) {
// this.routeResponse(source, `Command '${user_command_name}' does not exist.`);
// return false;
// }
// this.routeResponse(source, `*${source} used ${user_command_name.substring(1)}*`);
// 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
// this.history.add(source, message);
// }
// let execute_res = await executeCommand(this, message);
// if (execute_res)
// this.routeResponse(source, execute_res);
// return true;
// }
// }
// if (from_other_bot)
// this.last_sender = source;
// // Now translate the message
// message = await handleEnglishTranslation(message);
// console.log('received message from', source, ':', message);
// const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up || convoManager.responseScheduledFor(source);
// let behavior_log = this.bot.modes.flushBehaviorLog();
// if (behavior_log.trim().length > 0) {
// const MAX_LOG = 500;
// if (behavior_log.length > MAX_LOG) {
// behavior_log = '...' + behavior_log.substring(behavior_log.length - MAX_LOG);
// }
// behavior_log = 'Recent behaviors log: \n' + behavior_log.substring(behavior_log.indexOf('\n'));
// await this.history.add('system', behavior_log);
// }
// // Handle other user messages
// await this.history.add(source, message);
// this.history.save();
// if (!self_prompt && this.self_prompter.on) // message is from user during self-prompting
// maxResponses = 1; // force only respond to this message, then let self-prompting take over
// for (let i=0; i<maxResponses; i++) {
// if (checkInterrupt()) break;
// let history = this.history.getHistory();
// let res = await this.prompter.promptConvo(history);
// console.log(`${this.name} full response to ${source}: ""${res}""`);
// if (res.trim().length === 0) {
// console.warn('no response')
// break; // empty response ends loop
// }
// let command_name = containsCommand(res);
// if (command_name) { // contains query or command
// res = truncCommandMessage(res); // everything after the command is ignored
// this.history.add(this.name, res);
// if (!commandExists(command_name)) {
// this.history.add('system', `Command ${command_name} does not exist.`);
// console.warn('Agent hallucinated command:', command_name)
// continue;
// }
// if (checkInterrupt()) break;
// this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name));
// if (settings.verbose_commands) {
// this.routeResponse(source, res);
// }
// else { // only output command name
// let pre_message = res.substring(0, res.indexOf(command_name)).trim();
// let chat_message = `*used ${command_name.substring(1)}*`;
// if (pre_message.length > 0)
// chat_message = `${pre_message} ${chat_message}`;
// this.routeResponse(source, chat_message);
// }
// let execute_res = await executeCommand(this, res);
// console.log('Agent executed:', command_name, 'and got:', execute_res);
// used_command = true;
// if (execute_res)
// this.history.add('system', execute_res);
// else
// break;
// }
// else { // conversation response
// this.history.add(this.name, res);
// this.routeResponse(source, res);
// break;
// }
// this.history.save();
// }
// return used_command;
}
export function useBot() {
if (ctx == null || ctx.bot == null) {
throw new Error('Bot instance not found')
@@ -0,0 +1,9 @@
export interface BotInternalEventHandlers {
'time:sunrise': () => void
'time:noon': () => void
'time:sunset': () => void
'time:midnight': () => void
}
export type BotInternalEvents = keyof BotInternalEventHandlers
export type BotInternalEventsHandler<K extends BotInternalEvents> = BotInternalEventHandlers[K]