diff --git a/package.json b/package.json index 504a91e..82c1ffe 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "dependencies": { "@anthropic-ai/sdk": "^0.17.1", "@google/generative-ai": "^0.2.1", + "google-translate-api-x": "^10.7.1", "minecraft-data": "^3.46.2", "mineflayer": "^4.20.0", "mineflayer-armor-manager": "^2.0.1", diff --git a/settings.js b/settings.js index 9c6bb34..6e29750 100644 --- a/settings.js +++ b/settings.js @@ -13,10 +13,13 @@ export default "load_memory": false, // load memory from previous session "init_message": "Say hello world and your name", // sends to all on spawn + "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages + "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk "code_timeout_mins": 10, // minutes code is allowed to run. -1 for no timeout "max_commands": -1, // max number of commands to use in a response. -1 for no limit "verbose_commands": true, // show full command syntax "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') -} \ No newline at end of file +} + diff --git a/src/agent/agent.js b/src/agent/agent.js index 547c06b..622fac0 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -8,7 +8,7 @@ import { NPCContoller } from './npc/controller.js'; import { MemoryBank } from './memory_bank.js'; import { SelfPrompter } from './self_prompter.js'; import settings from '../../settings.js'; - +import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js'; export class Agent { async start(profile_fp, load_mem=false, init_message=null) { @@ -48,16 +48,18 @@ export class Agent { "Gamerule " ]; const eventname = settings.profiles.length > 1 ? 'whisper' : 'chat'; - this.bot.on(eventname, (username, message) => { + this.bot.on(eventname, async (username, message) => { if (username === this.name) return; if (ignore_messages.some((m) => message.startsWith(m))) return; - console.log('received message from', username, ':', message); + let translation = await handleEnglishTranslation(message); + + console.log('received message from', username, ':', translation); this.shut_up = false; - this.handleMessage(username, message); + this.handleMessage(username, translation); }); // set the bot to automatically eat food when hungry @@ -77,7 +79,8 @@ export class Agent { this.handleMessage('system', init_message, 2); } else { - this.bot.chat('Hello world! I am ' + this.name); + const translation = await handleTranslation("Hello world! I am "+this.name); + this.bot.chat(translation); this.bot.emit('finished_executing'); } @@ -85,9 +88,17 @@ export class Agent { }); } - cleanChat(message) { + + async cleanChat(message, translate_up_to=-1) { + let to_translate = message; + let remainging = ''; + if (translate_up_to != -1) { + to_translate = to_translate.substring(0, translate_up_to); + remainging = message.substring(translate_up_to); + } + message = (await handleTranslation(to_translate)).trim() + " " + remainging; // newlines are interpreted as separate chats, which triggers spam filters. replace them with spaces - message = message.replaceAll('\n', ' '); + message = message.replaceAll('\n', ' '); return this.bot.chat(message); } @@ -168,7 +179,7 @@ export class Agent { this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name)); if (settings.verbose_commands) { - this.cleanChat(res); + this.cleanChat(res, res.indexOf(command_name)); } else { // only output command name let pre_message = res.substring(0, res.indexOf(command_name)).trim(); @@ -290,4 +301,3 @@ export class Agent { process.exit(1); } } - diff --git a/src/agent/modes.js b/src/agent/modes.js index 653598f..8d7c4ed 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -2,11 +2,14 @@ import * as skills from './library/skills.js'; import * as world from './library/world.js'; import * as mc from '../utils/mcdata.js'; import settings from '../../settings.js' +import { handleTranslation } from '../utils/translator.js'; -function say(agent, message) { + +async function say(agent, message) { agent.bot.modes.behavior_log += message + '\n'; if (agent.shut_up || !settings.narrate_behavior) return; - agent.bot.chat(message); + let translation = await handleTranslation(message); + agent.bot.chat(translation); } // a mode is a function that is called every tick to respond immediately to the world diff --git a/src/utils/translator.js b/src/utils/translator.js new file mode 100644 index 0000000..879cfe4 --- /dev/null +++ b/src/utils/translator.js @@ -0,0 +1,30 @@ +import translate from 'google-translate-api-x'; +import settings from '../../settings.js'; + +const preferred_lang = settings.language; + +export async function handleTranslation(message) { + try { + if (preferred_lang.toLowerCase() === 'en' || preferred_lang.toLowerCase() === 'english') { + return message; + } else { + const lang = String(preferred_lang); + + const translation = await translate(message, { to: lang }); + return translation.text || message; + } + } catch (error) { + console.error('Error translating message:', error); + return message; + } +} + +export async function handleEnglishTranslation(message) { + try { + const translation = await translate(message, { to: 'english' }); + return translation.text || message; + } catch (error) { + console.error('Error translating message:', error); + return message; + } +}