const { Webhook } = require('discord-webhook-node'); const readline = require("readline"); const { Ollama } = require("ollama"); const clc = require("cli-color"); const fs = require('fs'); const path = require('path'); class AIBot { constructor() { this.ollama = new Ollama({ host: "http://127.0.0.1:11434" }); this.discordHook = new Webhook("https://discord.com/api/webhooks/1328616300544786444/oCwaJ1HQW_Hk0_BQB6lWfuLSikhGOzEJqaOr37MNb6lkICk0Bllk7izVPdmXHKm1JhAN"); this.discordHook.setUsername("@Nyamma"); this.modelName = "qwen2.5:7b"; this.sessionName = this.modelName.replace(/[.:]/g, ""); this.messageHistory = []; const sessionsDir = path.join(__dirname, 'sessions'); if (!fs.existsSync(sessionsDir)) { fs.mkdirSync(sessionsDir); } this.initializeTools(); } async saveSessionData() { const sessionPath = path.join(__dirname, 'sessions', `${this.sessionName}.json`); const jsonData = JSON.stringify({ messageList: this.messageHistory }, null, 2); fs.writeFile(sessionPath, jsonData, "utf8", (err) => { if (err) { console.error(clc.red("Session save error:"), err); } }); } initializeTools() { this.availableFunctions = { sendDiscordMessage: this.discordMessage.bind(this), getRandomCharacter: this.getRandomCharacter.bind(this), getRandomQuote: this.getRandomQuote.bind(this), doGoogleSearch: this.googleSearch.bind(this) }; this.availableTools = [ { type: 'function', function: { name: 'sendDiscordMessage', description: 'Send a message via discord to @shironeko.', parameters: { type: 'object', required: ['message'], properties: { message: { type: 'string', description: 'Message contents.' } } } } }, { type: 'function', function: { name: 'getRandomCharacter', description: 'Gets random anime characters from randomCharacter.org, max 1.', parameters: { type: 'object', required: ['amount'], properties: { amount: { type: 'string', description: 'Amount of characters to generate.' } } } } }, { type: 'function', function: { name: 'getRandomQuote', description: 'Gets random quotes from randomQuotes.org, max 1.', parameters: { type: 'object', required: ['amount'], properties: { amount: { type: 'string', description: 'Amount of quotes to get.' } } } } }, { type: 'function', function: { name: 'doGoogleSearch', description: 'Search in google for a query.', parameters: { type: 'object', required: ['query'], properties: { query: { type: 'string', description: 'Query to search for' } } } } } ]; } async discordMessage(args) { try { await this.discordHook.send(args.message.toString()); return "Message sent successfully!"; } catch (error) { return "Failed to send Discord message. Error:", error } } getRandomCharacter(args) { return "Miku"; } getRandomQuote(args) { return '"Cerber is cute pass it on." -twitch chat'; } async googleSearch(args) { try { setTimeout(() => { this.processSearchResult(args.query, "Hatsune Miku, the virtual Diva"); }, 5000); return `Searching for "${args.query}", Please wait.`; } catch (error) { console.error(clc.red("Search error:"), error); throw new Error("Search failed"); } } async processSearchResult(query, searchResult) { // Add search result to message history this.writeMessage({ role: "tool", content: `doGoogleSearch: Result for ${query}: ${searchResult}` }); // Get LLM's interpretation of the search result const followUp = await this.ollama.chat({ model: this.modelName, messages: this.messageHistory, tools: this.availableTools }); this.writeMessage(followUp.message); // Handle any tool calls from the follow-up response if (followUp.message.tool_calls) { await this.handleToolCall(followUp.message.tool_calls); } } async writeMessage(newMessage) { this.messageHistory.push(newMessage); this.saveSessionData(); } async getUserInput() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(clc.yellow("User: "), (input) => { rl.close(); resolve(input); }); }); } async handleToolCall(toolCalls) { for (const tool of toolCalls) { const { name, arguments: args } = tool.function; const functionToCall = this.availableFunctions[name]; if (!functionToCall) { console.error(clc.red(`Tool "${name}" not found.`)); this.writeMessage({ role: "tool", content: `Tool "${name}" not recognized.` }); continue; } try { console.log(clc.cyan(`Executing: ${name}`), args); const output = await functionToCall(args); console.log(clc.green(`Result: ${output}`)); this.writeMessage({ role: "tool", content: `${name}: ${output}` }); } catch (error) { console.error(clc.red(`Tool execution error (${name}):`, error)); this.writeMessage({ role: "tool", content: `Error executing tool "${name}": ${error.message}` }); } } const followUp = await this.ollama.chat({ model: this.modelName, messages: this.messageHistory, tools: this.availableTools }); this.writeMessage(followUp.message); if (followUp.message.tool_calls) { await this.handleToolCall(followUp.message.tool_calls); } else { console.log(clc.blueBright("Nyamma:"), followUp.message.content); } } async processMessage(userMessage) { try { this.writeMessage({ role: "user", content: userMessage }); const response = await this.ollama.chat({ model: this.modelName, messages: this.messageHistory, tools: this.availableTools }); this.writeMessage(response.message); if (response.message.tool_calls) { await this.handleToolCall(response.message.tool_calls); } else { console.log(clc.blueBright("Nyamma:"), response.message.content); } } catch (error) { console.error(clc.red("Message processing error:"), error); } } async run() { //while (true) { try { //const userMessage = await this.getUserInput(); const userMessage = "please search in google who is the cutest character and send me only the result via discord"; await this.processMessage(userMessage); } catch (error) { console.error(clc.red("Runtime error:"), error); } //} } } const bot = new AIBot(); bot.run().catch(error => { console.error(clc.red("Critical error:"), error); process.exit(1); });