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)
```
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):**
```bash
bun install
@@ -42,13 +49,14 @@ docker run -d --env-file .env -v $(pwd)/data:/config/data tg-bot-bun
## Requirements
- Node.js >= 18 OR Bun >= 1.0
- Node.js >= 20 OR Bun >= 1.0
- Docker (optional)
## Features
- AI chat (Gemini, Mistral, Ollama)
- Local document RAG for Ollama without third-party providers
- Custom answers and commands
- Admin management
- User blocking (mute/unmute)
+33 -68
View File
@@ -3,11 +3,11 @@ import {Environment} from "./common/environment";
import {BotCommand, TelegramBot, User} from "typescript-telegram-bot-api";
import {Command} from "./base/command";
import {
delay,
initSystemSpecs,
logError,
processCallbackQuery,
processEditedMessage,
processGuestMessage,
processInlineQuery,
processMyChatMember,
processNewMessage
@@ -27,10 +27,8 @@ 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 {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";
@@ -49,16 +47,14 @@ 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 {AiCancel} from "./callback_commands/ai-cancel";
import {AiRegenerate} from "./callback_commands/ai-regenerate";
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";
@@ -66,20 +62,19 @@ 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";
import fs from "node:fs";
import path from "node:path";
import {setInterval} from "node:timers";
import {OpenAI} from "openai";
import {OpenAIChat} from "./commands/openai-chat";
import {OpenAIListModels} from "./commands/openai-list-models";
import {OpenAIGetModel} from "./commands/openai-get-model";
import {OpenAISetModel} from "./commands/openai-set-model";
import {Info} from "./commands/info";
import {OpenAIGenImage} from "./commands/openai-gen-image";
import {clearUpFolderFromOldFiles} from "./util/files";
import {AdminsList} from "./commands/admins-list";
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);
@@ -92,42 +87,6 @@ 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 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[] = [
new Start(),
new Help(),
@@ -157,6 +116,9 @@ export const commands: Command[] = [
new Transliteration(),
new Debug(),
new Info(),
new Settings(),
new TextToSpeech(),
new SpeechToText(),
new AdminsAdd(),
new AdminsRemove(),
@@ -173,13 +135,14 @@ if (Environment.ENABLE_UNSAFE_EVAL) {
}
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(
new OllamaChat(),
new OllamaPrompt(),
new OllamaListModels(),
new OllamaGetModel(),
new OllamaSetModel()
@@ -196,7 +159,6 @@ if (Environment.GEMINI_API_KEY) {
new GeminiListModels(),
new GeminiGetModel(),
new GeminiSetModel(),
new GeminiGenerateImage()
);
}
@@ -215,14 +177,16 @@ if (Environment.OPENAI_API_KEY) {
new OpenAIListModels(),
new OpenAIGetModel(),
new OpenAISetModel(),
new OpenAIGenImage()
);
}
export const cacheDir = path.join(Environment.DATA_PATH, "cache");
export const photoDir = path.join(cacheDir, "photo");
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 videoNotesDir = path.join(cacheDir, "video-note");
export const videoTempDir = path.join(videoDir, "temp");
let isShuttingDown = false;
@@ -253,29 +217,29 @@ async function main() {
`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 => {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
fs.mkdirSync(dir, {recursive: true});
}
});
const now = new Date();
// const now = new Date();
const midnight = new Date();
midnight.setHours(0, 0, 0, 0);
midnight.setDate(now.getDate() + 1);
// const midnight = new Date();
// midnight.setHours(0, 0, 0, 0);
// midnight.setDate(now.getDate() + 1);
const diff = midnight.getTime() - now.getTime();
console.log("Clearing up cache will be started in " + diff + "ms");
// const diff = midnight.getTime() - now.getTime();
// console.log("Clearing up cache will be started in " + diff + "ms");
clearUpFolderFromOldFiles(cacheDir);
delay(diff).then(() => {
setInterval(() => {
console.log("Started clearing up cache");
clearUpFolderFromOldFiles(cacheDir);
}, 1000 * 60 * 60 * 24);
});
// clearUpFolderFromOldFiles(cacheDir);
// delay(diff).then(() => {
// setInterval(() => {
// console.log("Started clearing up cache");
// clearUpFolderFromOldFiles(cacheDir);
// }, 1000 * 60 * 60 * 24);
// });
const cmds = commands.filter(cmd => {
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("inline_query", processInlineQuery);
bot.on("callback_query", processCallbackQuery);
bot.on("guest_message", processGuestMessage);
process.on("SIGTERM", () => {
shutdown("SIGTERM").catch(logError);