app: wire new commands and update docs

This commit is contained in:
2026-05-10 22:53:43 +03:00
parent 94d695e008
commit d2464b9b21
2 changed files with 43 additions and 70 deletions
+9 -1
View File
@@ -9,6 +9,13 @@ cp .env.example .env
# Edit .env: add BOT_TOKEN, CREATOR_ID and configure optional AI models (GEMINI_API_KEY, MISTRAL_API_KEY, OLLAMA_ADDRESS) # Edit .env: add BOT_TOKEN, CREATOR_ID and configure optional AI models (GEMINI_API_KEY, MISTRAL_API_KEY, OLLAMA_ADDRESS)
``` ```
For local Ollama document RAG, install an embedding model locally and set it in `.env`:
```bash
ollama pull nomic-embed-text
OLLAMA_EMBEDDING_MODEL=nomic-embed-text
```
**With Bun (Recommended):** **With Bun (Recommended):**
```bash ```bash
bun install bun install
@@ -42,13 +49,14 @@ docker run -d --env-file .env -v $(pwd)/data:/config/data tg-bot-bun
## Requirements ## Requirements
- Node.js >= 18 OR Bun >= 1.0 - Node.js >= 20 OR Bun >= 1.0
- Docker (optional) - Docker (optional)
## Features ## Features
- AI chat (Gemini, Mistral, Ollama) - AI chat (Gemini, Mistral, Ollama)
- Local document RAG for Ollama without third-party providers
- Custom answers and commands - Custom answers and commands
- Admin management - Admin management
- User blocking (mute/unmute) - User blocking (mute/unmute)
+34 -69
View File
@@ -3,11 +3,11 @@ import {Environment} from "./common/environment";
import {BotCommand, TelegramBot, User} from "typescript-telegram-bot-api"; import {BotCommand, TelegramBot, User} from "typescript-telegram-bot-api";
import {Command} from "./base/command"; import {Command} from "./base/command";
import { import {
delay,
initSystemSpecs, initSystemSpecs,
logError, logError,
processCallbackQuery, processCallbackQuery,
processEditedMessage, processEditedMessage,
processGuestMessage,
processInlineQuery, processInlineQuery,
processMyChatMember, processMyChatMember,
processNewMessage processNewMessage
@@ -27,10 +27,8 @@ import {When} from "./commands/when";
import {RandomInt} from "./commands/random-int"; import {RandomInt} from "./commands/random-int";
import {Ban} from "./commands/ban"; import {Ban} from "./commands/ban";
import {Quote} from "./commands/quote"; import {Quote} from "./commands/quote";
import {Ollama} from "ollama";
import {OllamaSearch} from "./commands/ollama-search"; import {OllamaSearch} from "./commands/ollama-search";
import {Id} from "./commands/id"; import {Id} from "./commands/id";
import {OllamaPrompt} from "./commands/ollama-prompt";
import {AdminsAdd} from "./commands/admins-add"; import {AdminsAdd} from "./commands/admins-add";
import {AdminsRemove} from "./commands/admins-remove"; import {AdminsRemove} from "./commands/admins-remove";
import {Shutdown} from "./commands/shutdown"; import {Shutdown} from "./commands/shutdown";
@@ -49,16 +47,14 @@ import {MessageDao} from "./db/message-dao";
import {DatabaseManager} from "./db/database-manager"; import {DatabaseManager} from "./db/database-manager";
import {UserDao} from "./db/user-dao"; import {UserDao} from "./db/user-dao";
import {UserStore} from "./common/user-store"; import {UserStore} from "./common/user-store";
import {OllamaRequest} from "./model/ollama-request";
import {CallbackCommand} from "./base/callback-command"; import {CallbackCommand} from "./base/callback-command";
import {OllamaCancel} from "./callback_commands/ollama-cancel"; import {AiCancel} from "./callback_commands/ai-cancel";
import {AiRegenerate} from "./callback_commands/ai-regenerate";
import {MistralChat} from "./commands/mistral-chat"; import {MistralChat} from "./commands/mistral-chat";
import {Transliteration} from "./commands/transliteration"; import {Transliteration} from "./commands/transliteration";
import {OllamaListModels} from "./commands/ollama-list-models"; import {OllamaListModels} from "./commands/ollama-list-models";
import {OllamaGetModel} from "./commands/ollama-get-model"; import {OllamaGetModel} from "./commands/ollama-get-model";
import {OllamaSetModel} from "./commands/ollama-set-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 {MistralGetModel} from "./commands/mistral-get-model";
import {MistralSetModel} from "./commands/mistral-set-model"; import {MistralSetModel} from "./commands/mistral-set-model";
import {MistralListModels} from "./commands/mistral-list-models"; import {MistralListModels} from "./commands/mistral-list-models";
@@ -66,20 +62,19 @@ import {GeminiListModels} from "./commands/gemini-list-models";
import {GeminiGetModel} from "./commands/gemini-get-model"; import {GeminiGetModel} from "./commands/gemini-get-model";
import {GeminiSetModel} from "./commands/gemini-set-model"; import {GeminiSetModel} from "./commands/gemini-set-model";
import {Debug} from "./commands/debug"; import {Debug} from "./commands/debug";
import {GeminiGenerateImage} from "./commands/gemini-generate-image";
import fs from "node:fs"; import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import {setInterval} from "node:timers";
import {OpenAI} from "openai";
import {OpenAIChat} from "./commands/openai-chat"; import {OpenAIChat} from "./commands/openai-chat";
import {OpenAIListModels} from "./commands/openai-list-models"; import {OpenAIListModels} from "./commands/openai-list-models";
import {OpenAIGetModel} from "./commands/openai-get-model"; import {OpenAIGetModel} from "./commands/openai-get-model";
import {OpenAISetModel} from "./commands/openai-set-model"; import {OpenAISetModel} from "./commands/openai-set-model";
import {Info} from "./commands/info"; import {Info} from "./commands/info";
import {OpenAIGenImage} from "./commands/openai-gen-image";
import {clearUpFolderFromOldFiles} from "./util/files";
import {AdminsList} from "./commands/admins-list"; import {AdminsList} from "./commands/admins-list";
import {ExportDb} from "./commands/export-db"; import {ExportDb} from "./commands/export-db";
import {Settings} from "./commands/settings";
import {UserSettingsCallback} from "./callback_commands/user-settings";
import {TextToSpeech} from "./commands/text-to-speech";
import {SpeechToText} from "./commands/speech-to-text";
process.setUncaughtExceptionCaptureCallback(logError); process.setUncaughtExceptionCaptureCallback(logError);
@@ -92,42 +87,6 @@ export const userDao = new UserDao();
export const bot = new TelegramBot({botToken: Environment.BOT_TOKEN, testEnvironment: Environment.TEST_ENVIRONMENT}); export const bot = new TelegramBot({botToken: Environment.BOT_TOKEN, testEnvironment: Environment.TEST_ENVIRONMENT});
export let botUser: User; 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 openAi = new OpenAI({apiKey: Environment.OPENAI_API_KEY, baseURL: Environment.OPENAI_BASE_URL, dangerouslyAllowBrowser: true});
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 | undefined {
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 commands: Command[] = [ export const commands: Command[] = [
new Start(), new Start(),
new Help(), new Help(),
@@ -157,6 +116,9 @@ export const commands: Command[] = [
new Transliteration(), new Transliteration(),
new Debug(), new Debug(),
new Info(), new Info(),
new Settings(),
new TextToSpeech(),
new SpeechToText(),
new AdminsAdd(), new AdminsAdd(),
new AdminsRemove(), new AdminsRemove(),
@@ -173,13 +135,14 @@ if (Environment.ENABLE_UNSAFE_EVAL) {
} }
export const callbackCommands: CallbackCommand[] = [ export const callbackCommands: CallbackCommand[] = [
new OllamaCancel(), new AiCancel(),
new AiRegenerate(),
new UserSettingsCallback(),
]; ];
if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL) { if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_CHAT_MODEL) {
commands.push( commands.push(
new OllamaChat(), new OllamaChat(),
new OllamaPrompt(),
new OllamaListModels(), new OllamaListModels(),
new OllamaGetModel(), new OllamaGetModel(),
new OllamaSetModel() new OllamaSetModel()
@@ -196,7 +159,6 @@ if (Environment.GEMINI_API_KEY) {
new GeminiListModels(), new GeminiListModels(),
new GeminiGetModel(), new GeminiGetModel(),
new GeminiSetModel(), new GeminiSetModel(),
new GeminiGenerateImage()
); );
} }
@@ -215,14 +177,16 @@ if (Environment.OPENAI_API_KEY) {
new OpenAIListModels(), new OpenAIListModels(),
new OpenAIGetModel(), new OpenAIGetModel(),
new OpenAISetModel(), new OpenAISetModel(),
new OpenAIGenImage()
); );
} }
export const cacheDir = path.join(Environment.DATA_PATH, "cache"); export const cacheDir = path.join(Environment.DATA_PATH, "cache");
export const photoDir = path.join(cacheDir, "photo"); export const photoDir = path.join(cacheDir, "photo");
export const photoGenDir = path.join(photoDir, "gen"); export const photoGenDir = path.join(photoDir, "gen");
export const documentDir = path.join(cacheDir, "document");
export const audioDir = path.join(cacheDir, "audio");
export const videoDir = path.join(cacheDir, "video"); export const videoDir = path.join(cacheDir, "video");
export const videoNotesDir = path.join(cacheDir, "video-note");
export const videoTempDir = path.join(videoDir, "temp"); export const videoTempDir = path.join(videoDir, "temp");
let isShuttingDown = false; let isShuttingDown = false;
@@ -253,29 +217,29 @@ async function main() {
`DEFAULT_AI_PROVIDER: ${Environment.DEFAULT_AI_PROVIDER}` `DEFAULT_AI_PROVIDER: ${Environment.DEFAULT_AI_PROVIDER}`
); );
const dirsToCheck = [cacheDir, photoDir, photoGenDir, videoDir, videoTempDir]; const dirsToCheck = [cacheDir, photoDir, photoGenDir, documentDir, audioDir, videoDir, videoNotesDir, videoTempDir];
dirsToCheck.forEach(dir => { dirsToCheck.forEach(dir => {
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir); fs.mkdirSync(dir, {recursive: true});
} }
}); });
const now = new Date(); // const now = new Date();
const midnight = new Date(); // const midnight = new Date();
midnight.setHours(0, 0, 0, 0); // midnight.setHours(0, 0, 0, 0);
midnight.setDate(now.getDate() + 1); // midnight.setDate(now.getDate() + 1);
const diff = midnight.getTime() - now.getTime(); // const diff = midnight.getTime() - now.getTime();
console.log("Clearing up cache will be started in " + diff + "ms"); // console.log("Clearing up cache will be started in " + diff + "ms");
clearUpFolderFromOldFiles(cacheDir); // clearUpFolderFromOldFiles(cacheDir);
delay(diff).then(() => { // delay(diff).then(() => {
setInterval(() => { // setInterval(() => {
console.log("Started clearing up cache"); // console.log("Started clearing up cache");
clearUpFolderFromOldFiles(cacheDir); // clearUpFolderFromOldFiles(cacheDir);
}, 1000 * 60 * 60 * 24); // }, 1000 * 60 * 60 * 24);
}); // });
const cmds = commands.filter(cmd => { const cmds = commands.filter(cmd => {
return cmd.title && cmd.title.startsWith("/") && cmd.title.split(" ").length === 1 && cmd.description; return cmd.title && cmd.title.startsWith("/") && cmd.title.split(" ").length === 1 && cmd.description;
@@ -311,6 +275,7 @@ bot.on("edited_message", processEditedMessage);
bot.on("message", processNewMessage); bot.on("message", processNewMessage);
bot.on("inline_query", processInlineQuery); bot.on("inline_query", processInlineQuery);
bot.on("callback_query", processCallbackQuery); bot.on("callback_query", processCallbackQuery);
bot.on("guest_message", processGuestMessage);
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
shutdown("SIGTERM").catch(logError); shutdown("SIGTERM").catch(logError);
@@ -320,4 +285,4 @@ process.on("SIGINT", () => {
shutdown("SIGINT").catch(logError); shutdown("SIGINT").catch(logError);
}); });
main().catch(logError); main().catch(logError);