b74e0a0f3e
- make MessageStore.put() return StoredMessage and allow collectReplyChainText() to work with StoredMessage - move muted users + answers loading into Environment (add Answers model + GEMINI_IMAGE_MODEL) - extract message handling into processNewMessage() and add media-group (album) caching/downloading by unique_file_id - for Ollama: check model capabilities before sending images; use replyToMessage helper consistently - add /geminiGenImage command stub for Gemini image generation
295 lines
8.7 KiB
TypeScript
295 lines
8.7 KiB
TypeScript
import "dotenv/config";
|
|
import {Environment} from "./common/environment";
|
|
import {InlineQueryResult, TelegramBot, User} from "typescript-telegram-bot-api";
|
|
import {ChatCommand} from "./base/chat-command";
|
|
import {
|
|
extractTextMessage,
|
|
findAndExecuteCallbackCommand,
|
|
initSystemSpecs,
|
|
logError,
|
|
processNewMessage
|
|
} from "./util/utils";
|
|
import {Ae} from "./commands/ae";
|
|
import {Help} from "./commands/help";
|
|
import {Ignore} from "./commands/ignore";
|
|
import {Unignore} from "./commands/unignore";
|
|
import {Ping} from "./commands/ping";
|
|
import {RandomString} from "./commands/random-string";
|
|
import {SystemInfo} from "./commands/system-info";
|
|
import {Test} from "./commands/test";
|
|
import {readData, retrieveAnswers} from "./db/database";
|
|
import {Uptime} from "./commands/uptime";
|
|
import {WhatBetter} from "./commands/what-better";
|
|
import {When} from "./commands/when";
|
|
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";
|
|
import {AdminsAdd} from "./commands/admins-add";
|
|
import {AdminsRemove} from "./commands/admins-remove";
|
|
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";
|
|
import {Qr} from "./commands/qr";
|
|
import {Distort} from "./commands/distort";
|
|
import {Dice} from "./commands/dice";
|
|
import {Unban} from "./commands/unban";
|
|
import {Title} from "./commands/title";
|
|
import {MessageDao} from "./db/message-dao";
|
|
import {DatabaseManager} from "./db/database-manager";
|
|
import {UserDao} from "./db/user-dao";
|
|
import {UserStore} from "./common/user-store";
|
|
import {OllamaRequest} from "./model/ollama-request";
|
|
import {CallbackCommand} from "./base/callback-command";
|
|
import {OllamaCancel} from "./callback_commands/ollama-cancel";
|
|
import {MistralChat} from "./commands/mistral-chat";
|
|
import {Transliteration} from "./commands/transliteration";
|
|
import {OllamaListModels} from "./commands/ollama-list-models";
|
|
import {OllamaGetModel} from "./commands/ollama-get-model";
|
|
import {OllamaSetModel} from "./commands/ollama-set-model";
|
|
import {Mistral} from "@mistralai/mistralai";
|
|
import {GoogleGenAI} from "@google/genai";
|
|
import {MistralGetModel} from "./commands/mistral-get-model";
|
|
import {MistralSetModel} from "./commands/mistral-set-model";
|
|
import {MistralListModels} from "./commands/mistral-list-models";
|
|
import {GeminiListModels} from "./commands/gemini-list-models";
|
|
import {GeminiGetModel} from "./commands/gemini-get-model";
|
|
import {GeminiSetModel} from "./commands/gemini-set-model";
|
|
import {Debug} from "./commands/debug";
|
|
import {GeminiGenerateImage} from "./commands/gemini-generate-image";
|
|
|
|
process.setUncaughtExceptionCaptureCallback(logError);
|
|
|
|
Environment.load();
|
|
DatabaseManager.init();
|
|
|
|
export const messageDao = new MessageDao();
|
|
export const userDao = new UserDao();
|
|
|
|
export const bot = new TelegramBot({botToken: Environment.BOT_TOKEN, testEnvironment: Environment.TEST_ENVIRONMENT});
|
|
export let botUser: User;
|
|
|
|
export const googleAi = new GoogleGenAI({apiKey: Environment.GEMINI_API_KEY});
|
|
export const mistralAi = new Mistral({apiKey: Environment.MISTRAL_API_KEY});
|
|
|
|
export const ollama = new Ollama({
|
|
host: Environment.OLLAMA_ADDRESS,
|
|
headers: {"Authorization": `Bearer ${Environment.OLLAMA_API_KEY}`}
|
|
});
|
|
|
|
export const ollamaRequests: OllamaRequest[] = [];
|
|
|
|
export function getOllamaRequest(uuid: string): OllamaRequest | null {
|
|
return ollamaRequests.find(r => r.uuid === uuid);
|
|
}
|
|
|
|
export function updateOllamaRequest(uuid: string, request: OllamaRequest) {
|
|
const index = ollamaRequests.findIndex(r => r.uuid === uuid);
|
|
if (index >= 0) {
|
|
ollamaRequests[index] = request;
|
|
}
|
|
}
|
|
|
|
export function abortOllamaRequest(uuid: string): boolean {
|
|
const request = getOllamaRequest(uuid);
|
|
if (!request || request.done) return false;
|
|
|
|
try {
|
|
request.stream.abort();
|
|
updateOllamaRequest(uuid, {...request, done: true});
|
|
return true;
|
|
} catch (e) {
|
|
logError(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export const chatCommands: ChatCommand[] = [
|
|
new Start(),
|
|
new Help(),
|
|
new Test(),
|
|
new Ae(),
|
|
new Ignore(),
|
|
new Unignore(),
|
|
new Ping(),
|
|
new RandomInt(),
|
|
new RandomString(),
|
|
new SystemInfo(),
|
|
new Uptime(),
|
|
|
|
new WhatBetter(),
|
|
new When(),
|
|
|
|
new Ban(),
|
|
new Unban(),
|
|
|
|
new Quote(),
|
|
new Id(),
|
|
new Choice(),
|
|
new Coin(),
|
|
new Qr(),
|
|
new Distort(),
|
|
new Dice(),
|
|
new Title(),
|
|
new Transliteration(),
|
|
new Debug(),
|
|
|
|
new AdminsAdd(),
|
|
new AdminsRemove(),
|
|
|
|
new Shutdown(),
|
|
new Leave(),
|
|
];
|
|
|
|
export const callbackCommands: CallbackCommand[] = [
|
|
new OllamaCancel()
|
|
];
|
|
|
|
if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) {
|
|
chatCommands.push(
|
|
new OllamaChat(),
|
|
new OllamaPrompt(),
|
|
new OllamaListModels(),
|
|
new OllamaGetModel(),
|
|
new OllamaSetModel()
|
|
);
|
|
}
|
|
|
|
if (Environment.OLLAMA_API_KEY) {
|
|
chatCommands.push(new OllamaSearch());
|
|
}
|
|
|
|
if (Environment.GEMINI_API_KEY) {
|
|
chatCommands.push(
|
|
new GeminiChat(),
|
|
new GeminiListModels(),
|
|
new GeminiGetModel(),
|
|
new GeminiSetModel(),
|
|
new GeminiGenerateImage()
|
|
);
|
|
}
|
|
|
|
if (Environment.MISTRAL_API_KEY) {
|
|
chatCommands.push(
|
|
new MistralChat(),
|
|
new MistralListModels(),
|
|
new MistralGetModel(),
|
|
new MistralSetModel()
|
|
);
|
|
}
|
|
|
|
async function main() {
|
|
console.log(
|
|
`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}`
|
|
);
|
|
|
|
const commands = chatCommands.filter(cmd => {
|
|
return cmd.title && cmd.title.startsWith("/") && cmd.title.split(" ").length === 1 && cmd.description;
|
|
}).map(cmd => {
|
|
return {
|
|
command: cmd.title.toLowerCase(),
|
|
description: cmd.description,
|
|
};
|
|
});
|
|
|
|
try {
|
|
const results = await Promise.all(
|
|
[
|
|
initSystemSpecs(), readData(), retrieveAnswers(),
|
|
bot.getMe(),
|
|
bot.setMyCommands({commands: commands, scope: {type: "default"}})
|
|
]
|
|
);
|
|
botUser = results[3];
|
|
await UserStore.put(botUser);
|
|
await bot.startPolling();
|
|
|
|
console.log("Bot started!");
|
|
} catch (error) {
|
|
logError(error);
|
|
}
|
|
}
|
|
|
|
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("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);
|
|
});
|
|
|
|
main().catch(logError); |