diff --git a/src/commands/gemini-chat.ts b/src/commands/gemini-chat.ts index 1eb87cb..262556f 100644 --- a/src/commands/gemini-chat.ts +++ b/src/commands/gemini-chat.ts @@ -18,11 +18,11 @@ export class GeminiChat extends ChatCommand { command = "gemini"; argsMode = "required" as const; + requirements = Requirements.Build(Requirement.BOT_CREATOR); + title = "/gemini"; description = "Chat with AI (Gemini)"; - requirements = Requirements.Build(Requirement.BOT_CREATOR); - async execute(msg: Message, match?: RegExpExecArray): Promise { console.log("match", match); return this.executeGemini(msg, match?.[3]); diff --git a/src/commands/mistral-chat.ts b/src/commands/mistral-chat.ts index 2aa3ea1..87035b4 100644 --- a/src/commands/mistral-chat.ts +++ b/src/commands/mistral-chat.ts @@ -17,11 +17,11 @@ export class MistralChat extends ChatCommand { command = "mistral"; argsMode = "required" as const; + requirements = Requirements.Build(Requirement.BOT_CREATOR); + title = "/mistral"; description = "Chat with AI (Mistral)"; - requirements = Requirements.Build(Requirement.BOT_CREATOR); - async execute(msg: Message, match?: RegExpExecArray): Promise { console.log("match", match); return this.executeMistral(msg, match?.[3]); diff --git a/src/commands/openai-chat.ts b/src/commands/openai-chat.ts index 57c94c0..0912a96 100644 --- a/src/commands/openai-chat.ts +++ b/src/commands/openai-chat.ts @@ -133,8 +133,6 @@ export class OpenAIChat extends ChatCommand { break; } } - - } } finally { await editor.tick(); diff --git a/src/common/environment.ts b/src/common/environment.ts index 3bdaf44..e75217f 100644 --- a/src/common/environment.ts +++ b/src/common/environment.ts @@ -3,6 +3,13 @@ import {saveData} from "../db/database"; import {Answers} from "../model/answers"; import {ifTrue} from "../util/utils"; +export enum AiProvider { + OLLAMA = "OLLAMA", + GEMINI = "GEMINI", + MISTRAL = "MISTRAL", + OPENAI = "OPENAI", +} + export class Environment { static BOT_TOKEN: string; static TEST_ENVIRONMENT: boolean; @@ -24,6 +31,8 @@ export class Environment { static MAX_PHOTO_SIZE: number; + static DEFAULT_AI_PROVIDER: AiProvider; + static SYSTEM_PROMPT?: string; static OLLAMA_ADDRESS?: string; @@ -65,6 +74,13 @@ export class Environment { Environment.MAX_PHOTO_SIZE = Number(process.env.MAX_PHOTO_SIZE || "1280"); + const aiProvider = process.env.DEFAULT_AI_PROVIDER || "OLLAMA"; + if (Object.values(AiProvider).includes(aiProvider as AiProvider)) { + Environment.DEFAULT_AI_PROVIDER = aiProvider as AiProvider; + } else { + Environment.DEFAULT_AI_PROVIDER = AiProvider.OLLAMA; + } + Environment.SYSTEM_PROMPT = process.env.SYSTEM_PROMPT?.trim(); Environment.OLLAMA_ADDRESS = process.env.OLLAMA_ADDRESS; diff --git a/src/index.ts b/src/index.ts index 740e9b1..1ec89cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,16 @@ import "dotenv/config"; import {Environment} from "./common/environment"; -import {InlineQueryResult, TelegramBot, User} from "typescript-telegram-bot-api"; +import {TelegramBot, User} from "typescript-telegram-bot-api"; import {Command} from "./base/command"; import { delay, - extractTextMessage, - findAndExecuteCallbackCommand, ignore, initSystemSpecs, logError, + processCallbackQuery, + processEditedMessage, + processInlineQuery, + processMyChatMember, processNewMessage } from "./util/utils"; import {Ae} from "./commands/ae"; @@ -27,7 +29,6 @@ import {RandomInt} from "./commands/random-int"; import {Ban} from "./commands/ban"; import {Quote} from "./commands/quote"; import {Ollama} from "ollama"; -import {WebSearchResponse} from "./model/web-search-response"; import {OllamaSearch} from "./commands/ollama-search"; import {Id} from "./commands/id"; import {OllamaPrompt} from "./commands/ollama-prompt"; @@ -37,7 +38,6 @@ import {Shutdown} from "./commands/shutdown"; import {Leave} from "./commands/leave"; import {OllamaChat} from "./commands/ollama-chat"; import {Start} from "./commands/start"; -import {MessageStore} from "./common/message-store"; import {GeminiChat} from "./commands/gemini-chat"; import {Choice} from "./commands/choice"; import {Coin} from "./commands/coin"; @@ -221,7 +221,8 @@ async function main() { `TEST_ENVIRONMENT: ${Environment.TEST_ENVIRONMENT}\n` + `DATA_PATH: ${Environment.DATA_PATH}\n` + `MAX_PHOTO_SIZE: ${Environment.MAX_PHOTO_SIZE}\n` + - `ONLY_FOR_CREATOR: ${Environment.ONLY_FOR_CREATOR_MODE}` + `ONLY_FOR_CREATOR: ${Environment.ONLY_FOR_CREATOR_MODE}\n` + + `DEFAULT_AI_PROVIDER: ${Environment.DEFAULT_AI_PROVIDER}` ); fs.mkdir(photoDir, ignore); @@ -272,74 +273,10 @@ async function main() { } } -bot.on("my_chat_member", async (u) => { - console.log("my_chat_member", u); -}); - -bot.on("edited_message", async (msg) => { - console.log("edited_message", msg); - - await UserStore.put(msg.from); - - if (!extractTextMessage(msg) || msg.from.id === botUser.id) return; - - await MessageStore.put(msg); -}); - +bot.on("my_chat_member", processMyChatMember); +bot.on("edited_message", processEditedMessage); bot.on("message", processNewMessage); - -bot.on("inline_query", async (query) => { - console.log("query", query); - - if (Environment.CREATOR_ID !== query.from.id) { - await bot.answerInlineQuery({ - inline_query_id: query.id, - results: [], - button: { - text: "No access", - start_parameter: "nope" - } - }).catch(logError); - return; - } - - if (query.query.trim().length !== 0) { - try { - const queryResults: InlineQueryResult[] = []; - const results = await ollama.webSearch({query: query.query}); - - console.log("results", results); - - results.results.forEach((result, i) => { - const r = result as WebSearchResponse; - queryResults.push({ - type: "article", - id: `${i}`, - title: `${r.title}`, - input_message_content: { - message_text: `${r.title}\n\n${r.url}` - } - }); - }); - - await bot.answerInlineQuery({ - inline_query_id: query.id, - results: queryResults, - }); - } catch (e) { - logError(e); - } - } else { - await bot.answerInlineQuery({ - inline_query_id: query.id, - results: [], - }).catch(logError); - } -}); - -bot.on("callback_query", async (query) => { - console.log(query); - await findAndExecuteCallbackCommand(callbackCommands, query); -}); +bot.on("inline_query", processInlineQuery); +bot.on("callback_query", processCallbackQuery); main().catch(logError); \ No newline at end of file diff --git a/src/util/utils.ts b/src/util/utils.ts index d7e67dd..6b295d1 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -4,15 +4,18 @@ import {CallbackCommand} from "../base/callback-command"; import { CallbackQuery, ChatMember, + ChatMemberUpdated, InlineKeyboardMarkup, + InlineQuery, + InlineQueryResult, Message, ParseMode, PhotoSize, User } from "typescript-telegram-bot-api"; -import {Environment} from "../common/environment"; +import {AiProvider, Environment} from "../common/environment"; import {TelegramError} from "typescript-telegram-bot-api/dist/errors"; -import {bot, botUser, commands, messageDao} from "../index"; +import {bot, botUser, callbackCommands, commands, messageDao, ollama} from "../index"; import os from "os"; import axios from "axios"; import {MessagePart} from "../common/message-part"; @@ -30,6 +33,10 @@ import {OllamaChat} from "../commands/ollama-chat"; import {getYouTubeVideoId} from "./ytdl"; import {YouTubeDownload} from "../commands/youtube-download"; import {ChatCommand} from "../base/chat-command"; +import {WebSearchResponse} from "../model/web-search-response"; +import {GeminiChat} from "../commands/gemini-chat"; +import {MistralChat} from "../commands/mistral-chat"; +import {OpenAIChat} from "../commands/openai-chat"; export const ignore = () => { }; @@ -1028,8 +1035,34 @@ export function boolToEmoji(bool: boolean): string { export const albumCache = new Map(); -export async function processNewMessage(msg: Message) { - console.log("message", msg); +async function processAlbum(groupId: string): Promise { + const entry = albumCache.get(groupId); + if (!entry) return; + + const allPhotos = entry.messages + .filter(m => m.photo) + .map(m => m.photo); + + const allPhotoMaxSizes = await Promise.all(allPhotos.map(photo => getPhotoMaxSize(photo))); + const ids = await loadImagesFromFileIds(allPhotoMaxSizes); + + console.log(`Received album ${groupId} with ${ids.length} photos.`); + console.log("File IDs:", ids); + + albumCache.delete(groupId); + return ids; +} + +export function photoPathByUniqueId(uniqueId: string): string { + return path.join(Environment.DATA_PATH, "photo", uniqueId + ".jpg"); +} + +export async function processMyChatMember(u: ChatMemberUpdated): Promise { + console.log("my_chat_member", u); +} + +export async function processNewMessage(msg: Message): Promise { + console.log("New Message", msg); let storedMsg: StoredMessage | null = null; @@ -1133,30 +1166,86 @@ export async function processNewMessage(msg: Message) { if (!startsWithPrefix && msg.chat.type !== "private") return; if (msg.chat.type === "private" && !Environment.ADMIN_IDS.has(msg.chat.id)) return; - const chat = commands.find(e => e instanceof OllamaChat); - if (await checkRequirements(chat, msg)) { - await chat.executeOllama(msg, textToCheck); + switch (Environment.DEFAULT_AI_PROVIDER) { + case AiProvider.OLLAMA: { + await commands.find(e => e instanceof OllamaChat).executeOllama(msg, textToCheck); + break; + } + case AiProvider.GEMINI: { + await commands.find(e => e instanceof GeminiChat).executeGemini(msg, textToCheck); + break; + } + case AiProvider.MISTRAL: { + await commands.find(e => e instanceof MistralChat).executeMistral(msg, textToCheck); + break; + } + case AiProvider.OPENAI: { + await commands.find(e => e instanceof OpenAIChat).executeOpenAI(msg, textToCheck); + break; + } } } -async function processAlbum(groupId: string): Promise { - const entry = albumCache.get(groupId); - if (!entry) return; +export async function processEditedMessage(msg: Message): Promise { + console.log("Edited Message", msg); - const allPhotos = entry.messages - .filter(m => m.photo) - .map(m => m.photo); + await UserStore.put(msg.from); - const allPhotoMaxSizes = await Promise.all(allPhotos.map(photo => getPhotoMaxSize(photo))); - const ids = await loadImagesFromFileIds(allPhotoMaxSizes); + if (!extractTextMessage(msg) || msg.from.id === botUser.id) return; - console.log(`Received album ${groupId} with ${ids.length} photos.`); - console.log("File IDs:", ids); - - albumCache.delete(groupId); - return ids; + await MessageStore.put(msg); } -export function photoPathByUniqueId(uniqueId: string): string { - return path.join(Environment.DATA_PATH, "photo", uniqueId + ".jpg"); +export async function processInlineQuery(query: InlineQuery): Promise { + console.log("InlineQuery", query); + + if (Environment.CREATOR_ID !== query.from.id) { + await bot.answerInlineQuery({ + inline_query_id: query.id, + results: [], + button: { + text: "No access", + start_parameter: "nope" + } + }).catch(logError); + return; + } + + if (query.query.trim().length !== 0) { + try { + const queryResults: InlineQueryResult[] = []; + const results = await ollama.webSearch({query: query.query}); + + console.log("results", results); + + results.results.forEach((result, i) => { + const r = result as WebSearchResponse; + queryResults.push({ + type: "article", + id: `${i}`, + title: `${r.title}`, + input_message_content: { + message_text: `${r.title}\n\n${r.url}` + } + }); + }); + + await bot.answerInlineQuery({ + inline_query_id: query.id, + results: queryResults, + }); + } catch (e) { + logError(e); + } + } else { + await bot.answerInlineQuery({ + inline_query_id: query.id, + results: [], + }).catch(logError); + } +} + +export async function processCallbackQuery(query: CallbackQuery): Promise { + console.log("CallbackQuery", query); + await findAndExecuteCallbackCommand(callbackCommands, query); } \ No newline at end of file