update regex matching - now bot responds only for base commands (like /ping) and with mention (like /ping@panfilovi4_bot) and ignores command if it mentions other bot;
Add ability to see, change and list gemini, mistral and ollama models
This commit is contained in:
@@ -1,15 +1,58 @@
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {Requirements} from "./requirements";
|
||||
|
||||
export type ArgsMode = "none" | "optional" | "required";
|
||||
|
||||
export abstract class ChatCommand {
|
||||
|
||||
abstract regexp: RegExp;
|
||||
regexp?: RegExp | null;
|
||||
command?: string | string[];
|
||||
argsMode: ArgsMode = "none";
|
||||
|
||||
requirements?: Requirements = null;
|
||||
title?: string;
|
||||
description?: string;
|
||||
|
||||
get finalRegexp(): RegExp {
|
||||
if (!this.regexp) {
|
||||
const inferred = name(this.constructor.name);
|
||||
const names = this.command ?? inferred;
|
||||
this.regexp = createCommandRegExp(names, this.argsMode);
|
||||
}
|
||||
return this.regexp;
|
||||
}
|
||||
|
||||
abstract execute(
|
||||
msg: Message,
|
||||
match?: RegExpExecArray
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export function name(s: string) {
|
||||
return s
|
||||
.replace(/Command$/, "")
|
||||
.replace(/([a-z0-9])([A-Z])/g, "$1$2")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function escapeRe(s: string) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
export function createCommandRegExp(
|
||||
names: string | string[],
|
||||
argsMode: ArgsMode = "optional",
|
||||
) {
|
||||
const list = Array.isArray(names) ? names : [names];
|
||||
const group = list.map(escapeRe).join("|");
|
||||
|
||||
const base = `^\\/(${group})(?:@([\\w_]+))?`; // (1)=cmd, (2)=bot
|
||||
const tail =
|
||||
argsMode === "none"
|
||||
? "\\s*$"
|
||||
: argsMode === "required"
|
||||
? "\\s+([\\s\\S]+)\\s*$" // (3)=args обязателен
|
||||
: "(?:\\s+([\\s\\S]+))?\\s*$"; // (3)=args опционален
|
||||
|
||||
return new RegExp(base + tail, "i");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {Environment} from "../common/environment";
|
||||
import {botUser} from "../index";
|
||||
|
||||
export class AdminsAdd extends ChatCommand {
|
||||
regexp = /^\/addadmin/i;
|
||||
command = "addAdmin";
|
||||
title = "/addAdmin";
|
||||
description = "Add user to admins";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {Environment} from "../common/environment";
|
||||
import {botUser} from "../index";
|
||||
|
||||
export class AdminsRemove extends ChatCommand {
|
||||
regexp = /^\/removeadmin/i;
|
||||
command = "removeAdmin";
|
||||
title = "/removeAdmin";
|
||||
description = "Remove user from admins";
|
||||
|
||||
|
||||
+5
-4
@@ -4,15 +4,16 @@ import {errorPlaceholder, logError, oldSendMessage} from "../util/utils";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
|
||||
export class Ae implements ChatCommand {
|
||||
regexp = /^\/ae\s([^]+)/i;
|
||||
export class Ae extends ChatCommand {
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/ae";
|
||||
description = "evaluation";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message, params: string[]) {
|
||||
const match = params[1];
|
||||
async execute(msg: Message, params?: RegExpExecArray) {
|
||||
const match = params?.[3];
|
||||
|
||||
try {
|
||||
let e = eval(match);
|
||||
|
||||
+4
-5
@@ -3,11 +3,10 @@ import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {bot, botUser} from "../index";
|
||||
import {fullName, logError, oldSendMessage, replyToMessage} from "../util/utils";
|
||||
import {fullName, logError, oldSendMessage, oldReplyToMessage} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class Ban extends ChatCommand {
|
||||
regexp = /^\/ban/i;
|
||||
title = "/ban [reply]";
|
||||
description = "ban user from chat";
|
||||
|
||||
@@ -25,17 +24,17 @@ export class Ban extends ChatCommand {
|
||||
const userId = user.id;
|
||||
|
||||
if (userId === botUser.id) {
|
||||
await replyToMessage(msg, "Используй /leave").catch(logError);
|
||||
await oldReplyToMessage(msg, "Используй /leave").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId === Environment.CREATOR_ID) {
|
||||
await replyToMessage(msg, "Бот не будет банить своего создателя.").catch(logError);
|
||||
await oldReplyToMessage(msg, "Бот не будет банить своего создателя.").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.from.id !== Environment.CREATOR_ID && Environment.ADMIN_IDS.has(userId)) {
|
||||
await replyToMessage(msg, "Бот не будет банить своих администраторов.").catch(logError);
|
||||
await oldReplyToMessage(msg, "Бот не будет банить своих администраторов.").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, randomValue, replyToMessage} from "../util/utils";
|
||||
import {logError, oldReplyToMessage, randomValue} from "../util/utils";
|
||||
|
||||
export class Choice extends ChatCommand {
|
||||
regexp = /^\/choice\b\s*(.*)$/i;
|
||||
command = "choice";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/choice a, b, ..., c";
|
||||
description = "Выбор случайного значения";
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
|
||||
console.log("match", match);
|
||||
|
||||
const payload = match[1];
|
||||
const payload = match[3];
|
||||
|
||||
const re =
|
||||
/\s*(?:"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|([^,]+?))\s*(?:,|$)/g;
|
||||
@@ -32,6 +34,6 @@ export class Choice extends ChatCommand {
|
||||
|
||||
const random = randomValue(out);
|
||||
|
||||
await replyToMessage(msg, `Выбрал *${random}*`, "Markdown").catch(logError);
|
||||
await oldReplyToMessage(msg, `Выбрал *${random}*`, "Markdown").catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {getRangedRandomInt, logError, replyToMessage} from "../util/utils";
|
||||
import {getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils";
|
||||
|
||||
export class Coin extends ChatCommand {
|
||||
regexp = /^\/coin$/i;
|
||||
title = "/coin";
|
||||
description = "Heads or tails";
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
const random = getRangedRandomInt(0, 2);
|
||||
const headsOrTails = random === 1 ? "Выпал *Орёл* 🪙" : "Выпала *Решка* 🪙";
|
||||
await replyToMessage(msg, headsOrTails, "Markdown").catch(logError); }
|
||||
await oldReplyToMessage(msg, headsOrTails, "Markdown").catch(logError); }
|
||||
}
|
||||
@@ -7,8 +7,7 @@ type DiceEmoji = "🎲" | "🎯" | "🏀" | "⚽" | "🎳" | "🎰";
|
||||
const emojis = ["🎲", "🎯", "🏀", "⚽", "🎳", "🎰"];
|
||||
|
||||
export class Dice extends ChatCommand {
|
||||
regexp = /^\/dice/i;
|
||||
title = "/dice [emoji]";
|
||||
title = "/dice";
|
||||
description = "Sends random or specific dice";
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
|
||||
+11
-7
@@ -1,10 +1,12 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {downloadTelegramFile, extractImageFileId, logError, replyToMessage, waveDistortSharp} from "../util/utils";
|
||||
import {downloadTelegramFile, extractImageFileId, logError, oldReplyToMessage, waveDistortSharp} from "../util/utils";
|
||||
import {bot} from "../index";
|
||||
|
||||
export class Distort extends ChatCommand {
|
||||
regexp = /^\/distort(?:@[\w_]+)?(?:\s+(\d+))?(?:\s+(\d+))?\s*$/i;
|
||||
command = "distort";
|
||||
argsMode = "optional" as const;
|
||||
|
||||
title = "/distort [amp] [wavelength]";
|
||||
description = "Distortion of picture";
|
||||
|
||||
@@ -13,7 +15,7 @@ export class Distort extends ChatCommand {
|
||||
|
||||
const reply = msg.reply_to_message;
|
||||
if (!reply) {
|
||||
await replyToMessage(
|
||||
await oldReplyToMessage(
|
||||
msg,
|
||||
"Ответь командой /distort на сообщение с картинкой (фото, документ или стикер).\n" + "Пример: /distort 16 80"
|
||||
);
|
||||
@@ -22,15 +24,17 @@ export class Distort extends ChatCommand {
|
||||
|
||||
const fileId = extractImageFileId(reply);
|
||||
if (!fileId) {
|
||||
await replyToMessage(
|
||||
await oldReplyToMessage(
|
||||
msg,
|
||||
"В реплае не вижу картинку. Пришли фото или файл-изображение."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const amp = match?.[1] ? parseInt(match[1], 10) : 14;
|
||||
const wavelength = match?.[2] ? parseInt(match[2], 10) : 72;
|
||||
const args = (match?.[3] ?? "").trim();
|
||||
const [a, b] = args ? args.split(/\s+/) : [];
|
||||
const amp = a ? Number(a) : 14;
|
||||
const wavelength = b ? Number(b) : 72;
|
||||
|
||||
try {
|
||||
await bot.sendChatAction({chat_id: chatId, action: "upload_photo"});
|
||||
@@ -48,7 +52,7 @@ export class Distort extends ChatCommand {
|
||||
caption: `Искажение готово ✅ (amp=${amp}, wavelength=${wavelength})`,
|
||||
});
|
||||
} catch (e) {
|
||||
await replyToMessage(
|
||||
await oldReplyToMessage(
|
||||
msg, `Не получилось исказить изображение: ${e?.message ?? String(e)}`
|
||||
).catch(logError);
|
||||
}
|
||||
|
||||
+16
-14
@@ -3,30 +3,31 @@ import {Message} from "typescript-telegram-bot-api";
|
||||
import {
|
||||
collectReplyChainText,
|
||||
editMessageText,
|
||||
escapeMarkdownV2Text, extractText,
|
||||
escapeMarkdownV2Text,
|
||||
extractText,
|
||||
logError,
|
||||
replyToMessage,
|
||||
oldReplyToMessage,
|
||||
startIntervalEditor
|
||||
} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
import {bot} from "../index";
|
||||
import {bot, googleAi} from "../index";
|
||||
import {MessageStore} from "../common/message-store";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {ApiError, GoogleGenAI} from "@google/genai";
|
||||
import {ApiError} from "@google/genai";
|
||||
|
||||
export class GeminiChat extends ChatCommand {
|
||||
regexp = /^\/gemini\s([^]+)/i;
|
||||
command = "gemini";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/gemini";
|
||||
description = "Chat with AI (Gemini)";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
private googleAi = new GoogleGenAI({apiKey: Environment.GEMINI_API_KEY});
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
|
||||
console.log("match", match);
|
||||
return this.executeGemini(msg, match?.[1]);
|
||||
return this.executeGemini(msg, match?.[3]);
|
||||
}
|
||||
|
||||
async executeGemini(msg: Message, text: string): Promise<void> {
|
||||
@@ -67,8 +68,8 @@ export class GeminiChat extends ChatCommand {
|
||||
}
|
||||
});
|
||||
|
||||
const stream = await this.googleAi.models.generateContentStream({
|
||||
model: Environment.GEMINI_MODEL || "gemini-2.5-flash",
|
||||
const stream = await googleAi.models.generateContentStream({
|
||||
model: Environment.GEMINI_MODEL,
|
||||
contents: chatContent,
|
||||
});
|
||||
|
||||
@@ -85,6 +86,7 @@ export class GeminiChat extends ChatCommand {
|
||||
onStop: async () => {
|
||||
}
|
||||
});
|
||||
await editor.tick();
|
||||
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
@@ -123,21 +125,21 @@ export class GeminiChat extends ChatCommand {
|
||||
|
||||
waitMessage.reply_to_message = msg;
|
||||
waitMessage.text = messageText;
|
||||
MessageStore.put(waitMessage);
|
||||
await MessageStore.put(waitMessage);
|
||||
|
||||
await replyToMessage(waitMessage, `⏱️ ${diff}s`);
|
||||
await oldReplyToMessage(waitMessage, `⏱️ ${diff}s`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof ApiError) {
|
||||
if (error.status === 429) {
|
||||
await replyToMessage(waitMessage, "На сегодня всё, лимиты закончились.").catch(logError);
|
||||
await oldReplyToMessage(waitMessage, "На сегодня всё, лимиты закончились.").catch(logError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await replyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
await oldReplyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class GeminiGetModel extends ChatCommand {
|
||||
title = "/geminiGetModel";
|
||||
description = "Get current Gemini model";
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
await replyToMessage({message: msg, text: `Текущая модель: "${Environment.GEMINI_MODEL}"`}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {googleAi} from "../index";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
|
||||
export class GeminiListModels extends ChatCommand {
|
||||
title = "/geminiListModels";
|
||||
description = "List all Gemini models";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
try {
|
||||
const listResponse = await googleAi.models.list();
|
||||
console.log(listResponse);
|
||||
|
||||
const modelsString = listResponse.page
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(e => `\`${e.name}\``)
|
||||
.join("\n");
|
||||
|
||||
const text = "Доступные модели:\n\n" + modelsString;
|
||||
|
||||
await replyToMessage({
|
||||
message: msg,
|
||||
text: text,
|
||||
parse_mode: "Markdown"
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await replyToMessage({message: msg, text: "Не получилось загрузить список моделей"}).catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {Environment} from "../common/environment";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
|
||||
export class GeminiSetModel extends ChatCommand {
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/geminiSetModel";
|
||||
description = "Set Gemini model";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray | null): Promise<void> {
|
||||
const newModel = match?.[1];
|
||||
Environment.setGeminiModel(newModel || Environment.GEMINI_MODEL);
|
||||
|
||||
const text = newModel ? `Выбрана модель "${newModel}"`
|
||||
: `Модель не задана. Будет использоваться стандартная модель "${Environment.GEMINI_MODEL}".`;
|
||||
|
||||
await replyToMessage({message: msg, text: text}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,9 @@ import {ChatCommand} from "../base/chat-command";
|
||||
import {chatCommands} from "../index";
|
||||
import {TelegramError} from "typescript-telegram-bot-api/dist/errors";
|
||||
|
||||
export class Help implements ChatCommand {
|
||||
regexp = /^\/(h|help)/i;
|
||||
export class Help extends ChatCommand {
|
||||
command = ["h", "help"];
|
||||
|
||||
title = "/help";
|
||||
description = "Show list of commands";
|
||||
|
||||
@@ -16,7 +17,7 @@ export class Help implements ChatCommand {
|
||||
text += `${chatCommandToString(c)}\n`;
|
||||
});
|
||||
|
||||
await sendMessage({chatId: msg.from.id, text: text})
|
||||
await sendMessage({chat_id: msg.from.id, text: text})
|
||||
.then(async () => {
|
||||
if (msg.chat.type !== "private") {
|
||||
await sendMessage({message: msg, text: "Отправил команды в ЛС 😎"}).catch(logError);
|
||||
|
||||
+2
-3
@@ -1,9 +1,8 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {logError, oldReplyToMessage} from "../util/utils";
|
||||
|
||||
export class Id extends ChatCommand {
|
||||
regexp = /^\/id/i;
|
||||
title = "/id";
|
||||
description = "ID of chat, user and reply (if replied to any message)";
|
||||
|
||||
@@ -13,6 +12,6 @@ export class Id extends ChatCommand {
|
||||
text += ` \nreply id: \n\`\`\`${msg.reply_to_message.from.id}\`\`\``;
|
||||
}
|
||||
|
||||
await replyToMessage(msg, text, "MarkdownV2").catch(logError);
|
||||
await oldReplyToMessage(msg, text, "MarkdownV2").catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import {Requirement} from "../base/requirement";
|
||||
import {bot} from "../index";
|
||||
|
||||
export class Leave extends ChatCommand {
|
||||
regexp = /^\/leave/i;
|
||||
title = "/leave";
|
||||
description = "Bot will leave current chat";
|
||||
|
||||
|
||||
@@ -7,31 +7,27 @@ import {
|
||||
editMessageText,
|
||||
escapeMarkdownV2Text,
|
||||
extractText,
|
||||
getPhotoMaxSize,
|
||||
logError,
|
||||
replyToMessage,
|
||||
oldReplyToMessage,
|
||||
startIntervalEditor
|
||||
} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
import {bot} from "../index";
|
||||
import {bot, mistralAi} from "../index";
|
||||
import {MessageStore} from "../common/message-store";
|
||||
import {Mistral} from "@mistralai/mistralai";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import axios from "axios";
|
||||
|
||||
export class MistralChat extends ChatCommand {
|
||||
regexp = /^\/mistral\s([^]+)/i;
|
||||
command = "mistral";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/mistral";
|
||||
description = "Chat with AI (Mistral)";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
private mistralAi = new Mistral({apiKey: Environment.MISTRAL_API_KEY});
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
|
||||
console.log("match", match);
|
||||
return this.executeMistral(msg, match?.[1]);
|
||||
return this.executeMistral(msg, match?.[3]);
|
||||
}
|
||||
|
||||
async executeMistral(msg: Message, text: string): Promise<void> {
|
||||
@@ -39,41 +35,18 @@ export class MistralChat extends ChatCommand {
|
||||
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
let imageFilePath: string | null = null;
|
||||
|
||||
const maxSize = await getPhotoMaxSize(msg.photo);
|
||||
if (maxSize) {
|
||||
const imagePath = path.join(Environment.DATA_PATH, "temp");
|
||||
if (!fs.existsSync(imagePath)) {
|
||||
fs.mkdirSync(imagePath);
|
||||
}
|
||||
|
||||
imageFilePath = path.join(imagePath, maxSize.unique_file_id + ".jpg");
|
||||
if (!fs.existsSync(imageFilePath)) {
|
||||
const res = await axios.get<ArrayBuffer>(maxSize.url, {responseType: "arraybuffer"});
|
||||
const src = Buffer.from(res.data);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(imageFilePath, src);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
imageFilePath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const messageParts = await collectReplyChainText(msg, "/mistral");
|
||||
console.log("MESSAGE PARTS", messageParts);
|
||||
|
||||
const chatMessages = messageParts.map((part, i) => {
|
||||
const chatMessages = messageParts.map(part => {
|
||||
const content = [];
|
||||
content.push({
|
||||
type: "text",
|
||||
text: (Environment.USE_NAMES_IN_PROMPT && !part.bot ? `MESSAGE FROM USER "${part.name}":\n` : "") + extractText(part.content, Environment.BOT_PREFIX),
|
||||
});
|
||||
|
||||
if (imageFilePath && i === 0) {
|
||||
const base64Image = Buffer.from(fs.readFileSync(imageFilePath)).toString("base64");
|
||||
if (part.images && part.images.length > 0) {
|
||||
const base64Image = Buffer.from(fs.readFileSync(part.images[0])).toString("base64");
|
||||
content.push({
|
||||
type: "image_url",
|
||||
imageUrl: "data:image/jpeg;base64," + base64Image
|
||||
@@ -102,8 +75,8 @@ export class MistralChat extends ChatCommand {
|
||||
}
|
||||
});
|
||||
|
||||
const stream = await this.mistralAi.chat.stream({
|
||||
model: Environment.MISTRAL_MODEL || "mistral-small-latest",
|
||||
const stream = await mistralAi.chat.stream({
|
||||
model: Environment.MISTRAL_MODEL,
|
||||
messages: chatMessages as any
|
||||
});
|
||||
|
||||
@@ -120,14 +93,13 @@ export class MistralChat extends ChatCommand {
|
||||
onStop: async () => {
|
||||
}
|
||||
});
|
||||
await editor.tick();
|
||||
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
// const text = chunk.text;
|
||||
const text = chunk.data.choices[0].delta.content;
|
||||
console.log("chunk", chunk);
|
||||
|
||||
// const text = "";
|
||||
const length = (messageText + text).length;
|
||||
if (length > 4096) {
|
||||
messageText = messageText.slice(0, 4093) + "...";
|
||||
@@ -163,7 +135,7 @@ export class MistralChat extends ChatCommand {
|
||||
waitMessage.text = messageText;
|
||||
MessageStore.put(waitMessage);
|
||||
|
||||
await replyToMessage(waitMessage, `⏱️ ${diff}s`);
|
||||
await oldReplyToMessage(waitMessage, `⏱️ ${diff}s`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -175,7 +147,7 @@ export class MistralChat extends ChatCommand {
|
||||
// }
|
||||
// }
|
||||
|
||||
await replyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
await oldReplyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
|
||||
export class MistralGetModel extends ChatCommand {
|
||||
title = "/mistralGetModel";
|
||||
description = "Get current Mistral model";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
await replyToMessage({message: msg, text: `Текущая модель: "${Environment.MISTRAL_MODEL}"`}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {mistralAi} from "../index";
|
||||
import {logError, oldReplyToMessage, replyToMessage} from "../util/utils";
|
||||
|
||||
export class MistralListModels extends ChatCommand {
|
||||
title = "/mistralListModels";
|
||||
description = "List all Mistral models";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
try {
|
||||
const listResponse = await mistralAi.models.list();
|
||||
console.log(listResponse);
|
||||
|
||||
const modelsString = listResponse.data
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(e => `\`${e.name}\``)
|
||||
.join("\n");
|
||||
|
||||
const text = "Доступные модели:\n\n" + modelsString;
|
||||
|
||||
await replyToMessage({
|
||||
chat_id: msg.chat.id,
|
||||
text: text,
|
||||
parse_mode: "Markdown",
|
||||
message: msg
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await oldReplyToMessage(msg, "Не получилось загрузить список моделей").catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {Environment} from "../common/environment";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
|
||||
export class MistralSetModel extends ChatCommand {
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/mistralSetModel";
|
||||
description = "Set Mistral model";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray | null): Promise<void> {
|
||||
const newModel = match?.[1];
|
||||
Environment.setMistralModel(newModel || Environment.MISTRAL_MODEL);
|
||||
|
||||
const text = newModel ? `Выбрана модель "${newModel}"`
|
||||
: `Модель не задана. Будет использоваться стандартная модель "${Environment.MISTRAL_MODEL}".`;
|
||||
|
||||
await replyToMessage({message: msg, text: text}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,7 @@ import {fullName, logError, oldSendMessage} from "../util/utils";
|
||||
import {botUser} from "../index";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class Mute implements ChatCommand {
|
||||
regexp = /^\/mute/i;
|
||||
export class Mute extends ChatCommand {
|
||||
title = "/mute";
|
||||
description = "Bot will ignore user";
|
||||
|
||||
|
||||
+16
-39
@@ -6,27 +6,25 @@ import {
|
||||
editMessageText,
|
||||
escapeMarkdownV2Text,
|
||||
extractText,
|
||||
getPhotoMaxSize,
|
||||
logError,
|
||||
replyToMessage,
|
||||
oldReplyToMessage,
|
||||
startIntervalEditor
|
||||
} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
import {MessageStore} from "../common/message-store";
|
||||
import axios from "axios";
|
||||
import * as fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import {Cancel} from "../callback_commands/cancel";
|
||||
import {OllamaCancel} from "../callback_commands/ollama-cancel";
|
||||
|
||||
export class OllamaChat extends ChatCommand {
|
||||
regexp = /^\/ollama\s([^]+)/;
|
||||
command = "ollama";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/ollama";
|
||||
description = "talk to AI (Ollama)";
|
||||
description = "Chat with AI (Ollama)";
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray | null): Promise<void> {
|
||||
console.log("match", match);
|
||||
return this.executeOllama(msg, match?.[1]);
|
||||
return this.executeOllama(msg, match?.[3]);
|
||||
}
|
||||
|
||||
async executeOllama(msg: Message, text: string): Promise<void> {
|
||||
@@ -34,41 +32,18 @@ export class OllamaChat extends ChatCommand {
|
||||
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
let imageFilePath: string | null = null;
|
||||
|
||||
const maxSize = await getPhotoMaxSize(msg.photo);
|
||||
if (maxSize) {
|
||||
const imagePath = path.join(Environment.DATA_PATH, "temp");
|
||||
if (!fs.existsSync(imagePath)) {
|
||||
fs.mkdirSync(imagePath);
|
||||
}
|
||||
|
||||
imageFilePath = path.join(imagePath, maxSize.unique_file_id + ".jpg");
|
||||
if (!fs.existsSync(imageFilePath)) {
|
||||
const res = await axios.get<ArrayBuffer>(maxSize.url, {responseType: "arraybuffer"});
|
||||
const src = Buffer.from(res.data);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(imageFilePath, src);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
imageFilePath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const messageParts = await collectReplyChainText(msg);
|
||||
console.log("MESSAGE PARTS", messageParts);
|
||||
|
||||
const chatMessages = messageParts.map((part, i) => {
|
||||
const chatMessages = messageParts.map(part => {
|
||||
return {
|
||||
role: part.bot ? "assistant" : "user",
|
||||
content: (Environment.USE_NAMES_IN_PROMPT && !part.bot ? `MESSAGE FROM USER "${part.name}":\n` : "") + extractText(part.content, Environment.BOT_PREFIX),
|
||||
images: imageFilePath && i === 0 ? [imageFilePath] : null
|
||||
images: part.images
|
||||
};
|
||||
});
|
||||
chatMessages.reverse();
|
||||
chatMessages.unshift({role: "system", content: Environment.SYSTEM_PROMPT, images: null});
|
||||
chatMessages.unshift({role: "system", content: Environment.SYSTEM_PROMPT, images: []});
|
||||
|
||||
let waitMessage: Message;
|
||||
|
||||
@@ -81,7 +56,8 @@ export class OllamaChat extends ChatCommand {
|
||||
|
||||
waitMessage = await bot.sendMessage({
|
||||
chat_id: chatId,
|
||||
text: maxSize !== null ? `🔍 Внимательно изучаю изображение...\n🤓 ${maxSize.width}x${maxSize.height}px` : Environment.waitText,
|
||||
text: Environment.waitText,
|
||||
// text: maxSize !== null ? `🔍 Внимательно изучаю изображение...\n🤓 ${maxSize.width}x${maxSize.height}px` : Environment.waitText,
|
||||
reply_parameters: {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id
|
||||
@@ -117,17 +93,18 @@ export class OllamaChat extends ChatCommand {
|
||||
onStop: async () => {
|
||||
}
|
||||
});
|
||||
await editor.tick();
|
||||
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
if (!getOllamaRequest(uuid).done) {
|
||||
currentText += chunk.message.content;
|
||||
|
||||
if (currentText.length > 4096) {
|
||||
currentText = currentText.slice(0, 4093) + "...";
|
||||
shouldBreak = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (getOllamaRequest(uuid).done) {
|
||||
shouldBreak = true;
|
||||
}
|
||||
|
||||
@@ -154,7 +131,7 @@ export class OllamaChat extends ChatCommand {
|
||||
waitMessage.text = currentText;
|
||||
await MessageStore.put(waitMessage);
|
||||
|
||||
await replyToMessage(waitMessage, `⏱️ ${diff}s` + (maxSize !== null ? `\n🤓 ${maxSize.width}x${maxSize.height}px` : ""));
|
||||
await oldReplyToMessage(waitMessage, `⏱️ ${diff}s` /*+ (maxSize !== null ? `\n🤓 ${maxSize.width}x${maxSize.height}px` : "")*/);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -173,7 +150,7 @@ export class OllamaChat extends ChatCommand {
|
||||
}).catch(logError);
|
||||
|
||||
console.error(error);
|
||||
await replyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
await oldReplyToMessage(waitMessage, `Произошла ошибка!\n${error.toString()}`).catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {boolToEmoji, logError, replyToMessage} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
import {ollama} from "../index";
|
||||
|
||||
export class OllamaGetModel extends ChatCommand {
|
||||
title = "/ollamaGetModel";
|
||||
description = "Ollama model info";
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
|
||||
const showResponse = await ollama.show({model: Environment.OLLAMA_MODEL});
|
||||
console.log(showResponse);
|
||||
|
||||
const caps = showResponse.capabilities;
|
||||
|
||||
const text = "```ollama\n" +
|
||||
`model: ${Environment.OLLAMA_MODEL}\n\n` +
|
||||
`vision: ${boolToEmoji(caps.includes("vision"))}\n` +
|
||||
`thinking: ${boolToEmoji(caps.includes("thinking"))}\n` +
|
||||
`tools: ${boolToEmoji(caps.includes("tools"))}`
|
||||
+ "```";
|
||||
|
||||
await replyToMessage({message: msg, text: text, parse_mode: "Markdown"}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {ollama} from "../index";
|
||||
import {logError, oldReplyToMessage, sendMessage} from "../util/utils";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
|
||||
export class OllamaListModels extends ChatCommand {
|
||||
title = "/ollamaListModels";
|
||||
description = "List all Ollama models";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
try {
|
||||
const listResponse = await ollama.list();
|
||||
|
||||
const modelsString = listResponse.models
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(e => `\`${e.model}\``)
|
||||
.join("\n");
|
||||
|
||||
const message = "Доступные модели:\n\n" + modelsString;
|
||||
|
||||
await sendMessage({
|
||||
chat_id: msg.chat.id,
|
||||
text: message,
|
||||
parse_mode: "Markdown",
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await oldReplyToMessage(msg, "Не получилось загрузить список моделей").catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {bot, ollama} from "../index";
|
||||
import {editMessageText, ignore, replyToMessage} from "../util/utils";
|
||||
import {editMessageText, ignore, oldReplyToMessage} from "../util/utils";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class OllamaPrompt extends ChatCommand {
|
||||
regexp = /^\/ollamaprompt\s([^]+)/i;
|
||||
command = "ollamaPrompt";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/ollamaPrompt";
|
||||
description = "Custom prompt for AI (Ollama)";
|
||||
|
||||
@@ -86,7 +88,7 @@ export class OllamaPrompt extends ChatCommand {
|
||||
messageText += `\n\nДумал ${diff}s`;
|
||||
|
||||
await editMessageText(chatId, waitMessage.message_id, messageText);
|
||||
await replyToMessage(waitMessage, "Закончил лишь часть 😉");
|
||||
await oldReplyToMessage(waitMessage, "Закончил лишь часть 😉");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -101,7 +103,7 @@ export class OllamaPrompt extends ChatCommand {
|
||||
messageText += `\n\nДумал ${diff}s`;
|
||||
|
||||
await editMessageText(chatId, waitMessage.message_id, messageText);
|
||||
await replyToMessage(waitMessage, "Закончил 😉");
|
||||
await oldReplyToMessage(waitMessage, "Закончил 😉");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,7 +8,9 @@ import {editMessageText} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class OllamaSearch extends ChatCommand {
|
||||
regexp = /^\/(s|search)\s([^]+)/;
|
||||
command = ["s", "search"];
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/search";
|
||||
description = "Web search via Ollama";
|
||||
|
||||
@@ -29,7 +31,7 @@ export class OllamaSearch extends ChatCommand {
|
||||
parse_mode: "Markdown"
|
||||
});
|
||||
|
||||
const results = await ollama.webSearch({query: match?.[2]});
|
||||
const results = await ollama.webSearch({query: match?.[3]});
|
||||
console.log("results", results);
|
||||
|
||||
let message = "Результаты:\n\n";
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Environment} from "../common/environment";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
|
||||
export class OllamaSetModel extends ChatCommand {
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/ollamaSetModel";
|
||||
description = "Set Ollama model";
|
||||
|
||||
requirements = Requirements.Build(Requirement.BOT_CREATOR);
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray | null): Promise<void> {
|
||||
const newModel = match?.[1];
|
||||
Environment.setOllamaModel(newModel || Environment.OLLAMA_MODEL);
|
||||
|
||||
const text = newModel ? `Выбрана модель "${newModel}"`
|
||||
: `Модель не задана. Будет использоваться стандартная модель "${Environment.OLLAMA_MODEL}".`;
|
||||
|
||||
await replyToMessage({message: msg, text: text}).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@ import {logError, oldSendMessage} from "../util/utils";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
|
||||
export class Ping implements ChatCommand {
|
||||
regexp = /^\/ping/i;
|
||||
export class Ping extends ChatCommand {
|
||||
title = "/ping";
|
||||
description = "Ping between received and sent message";
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, randomValue, replyToMessage} from "../util/utils";
|
||||
import {logError, randomValue, oldReplyToMessage} from "../util/utils";
|
||||
import {prefixAnswers} from "../db/database";
|
||||
|
||||
export class PrefixResponse extends ChatCommand {
|
||||
regexp: RegExp;
|
||||
|
||||
async execute(msg: Message): Promise<void> {
|
||||
await replyToMessage(msg, randomValue(prefixAnswers)).catch(logError);
|
||||
await oldReplyToMessage(msg, randomValue(prefixAnswers)).catch(logError);
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -5,7 +5,6 @@ import {bot} from "../index";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
export class Qr extends ChatCommand {
|
||||
regexp = /^\/qr/i;
|
||||
title = "/qr";
|
||||
description = "Generates QR-code from text you sent or replied to.";
|
||||
|
||||
@@ -18,7 +17,7 @@ export class Qr extends ChatCommand {
|
||||
const payload = extractMessagePayload(msg, matchText);
|
||||
if (!payload) {
|
||||
await sendMessage({
|
||||
chatId: chatId,
|
||||
chat_id: chatId,
|
||||
text: "Отправь: /qr <текст или ссылка>\n" + "или ответь командой /qr на сообщение, из которого взять текст."
|
||||
});
|
||||
return;
|
||||
@@ -26,7 +25,7 @@ export class Qr extends ChatCommand {
|
||||
|
||||
if (payload.length > 1500) {
|
||||
await sendMessage({
|
||||
chatId: chatId,
|
||||
chat_id: chatId,
|
||||
text: `Слишком длинный текст для QR (${payload.length} символов). Максимум 1500 символов.`
|
||||
});
|
||||
return;
|
||||
@@ -51,7 +50,7 @@ export class Qr extends ChatCommand {
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
await sendMessage({chatId: chatId, text: `Не получилось сгенерировать QR: ${e?.message ?? String(e)}`}).catch(logError);
|
||||
await sendMessage({chat_id: chatId, text: `Не получилось сгенерировать QR: ${e?.message ?? String(e)}`}).catch(logError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
getUserAvatar,
|
||||
logError,
|
||||
makeDarkGradientBgFancy,
|
||||
oldSendMessage,
|
||||
replyToMessage
|
||||
oldReplyToMessage,
|
||||
oldSendMessage
|
||||
} from "../util/utils";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
@@ -34,7 +34,9 @@ try {
|
||||
}
|
||||
|
||||
export class Quote extends ChatCommand {
|
||||
regexp = /^\/(cit|q|quote)$/i;
|
||||
command = ["cit", "citation", "q", "quote"];
|
||||
argsMode = "none" as const;
|
||||
|
||||
title = "/quote";
|
||||
description = "Make quote from text (or quote)";
|
||||
|
||||
@@ -45,14 +47,14 @@ export class Quote extends ChatCommand {
|
||||
const reply = msg.reply_to_message;
|
||||
|
||||
if (!reply) {
|
||||
await replyToMessage(msg, "Сделай /quote реплаем на сообщение 🙂").catch(logError);
|
||||
await oldReplyToMessage(msg, "Сделай /quote реплаем на сообщение 🙂").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const quoteRaw = (msg.quote?.text ?? reply.text ?? reply.caption ?? "").trim();
|
||||
if (quoteRaw.length === 0) {
|
||||
await replyToMessage(msg, "Не нашёл в сообщении текста 😢").catch(logError);
|
||||
await oldReplyToMessage(msg, "Не нашёл в сообщении текста 😢").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {getRandomInt, getRangedRandomInt, logError, oldSendMessage} from "../uti
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
|
||||
export class RandomInt extends ChatCommand {
|
||||
regexp = /^\/randomInt/i;
|
||||
title = "/randomInt [min] [max]";
|
||||
description = "Ranged random integer from parameters";
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import {ChatCommand} from "../base/chat-command";
|
||||
import {getRandomInt, logError, oldSendMessage} from "../util/utils";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
|
||||
export class RandomString implements ChatCommand {
|
||||
regexp = /^\/randomString/i;
|
||||
export class RandomString extends ChatCommand {
|
||||
title = "/randomString [length]";
|
||||
description = "literally random string (up to 4096 symbols)";
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ const timings = [1500, 2500];
|
||||
const timer = [3, 2, 1];
|
||||
|
||||
export class Shutdown extends ChatCommand {
|
||||
regexp = /^\/shutdown/i;
|
||||
title = "/shutdown";
|
||||
description = "Self-destruction sequence for bot (shutdown)";
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import {chatCommands} from "../index";
|
||||
import {Help} from "./help";
|
||||
|
||||
export class Start extends ChatCommand {
|
||||
regexp = /^\/start/i;
|
||||
title = "/start";
|
||||
description = "Start the bot";
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import {logError, oldSendMessage} from "../util/utils";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {systemInfoText} from "../index";
|
||||
|
||||
export class SystemSpecs implements ChatCommand {
|
||||
regexp = /^\/systeminfo/i;
|
||||
export class SystemSpecs extends ChatCommand {
|
||||
title = "/systemInfo";
|
||||
description = "System information";
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, randomValue, replyToMessage} from "../util/utils";
|
||||
import {logError, randomValue, oldReplyToMessage} from "../util/utils";
|
||||
import {testAnswers} from "../db/database";
|
||||
|
||||
export class Test implements ChatCommand {
|
||||
export class Test extends ChatCommand {
|
||||
regexp = /^(test|тест|еуые|ntcn|инноке(нтий|ш|нтич))/i;
|
||||
title = "тест";
|
||||
description = "System functionality check";
|
||||
|
||||
async execute(msg: Message) {
|
||||
await replyToMessage(msg, randomValue(testAnswers) || "а").catch(logError);
|
||||
await oldReplyToMessage(msg, randomValue(testAnswers) || "а").catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,14 @@ import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {logError, oldReplyToMessage} from "../util/utils";
|
||||
import {bot} from "../index";
|
||||
|
||||
export class Title extends ChatCommand {
|
||||
regexp = /^\/title\s([^]+)/;
|
||||
title = "/title [title]";
|
||||
command = "title";
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/title";
|
||||
description = "Change group title";
|
||||
|
||||
requirements = Requirements.Build(
|
||||
@@ -19,7 +21,7 @@ export class Title extends ChatCommand {
|
||||
async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
|
||||
const title = (match?.[1] ?? "").trim();
|
||||
if (title.length === 0) {
|
||||
await replyToMessage(msg, "Не нашёл название...").catch(logError);
|
||||
await oldReplyToMessage(msg, "Не нашёл название...").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {logError, replyToMessage} from "../util/utils";
|
||||
import {logError, oldReplyToMessage} from "../util/utils";
|
||||
|
||||
const EN =
|
||||
"`qwertyuiop[]asdfghjkl;'zxcvbnm,./" +
|
||||
@@ -98,7 +98,6 @@ export function fixLayoutAuto(
|
||||
}
|
||||
|
||||
export class Transliteration extends ChatCommand {
|
||||
regexp = /^\/tr/i;
|
||||
title = "/tr [text or reply]";
|
||||
description = "Transliteration EN <--> RU";
|
||||
|
||||
@@ -120,6 +119,6 @@ export class Transliteration extends ChatCommand {
|
||||
|
||||
const newText = fixLayoutAuto(text, toRuLayout, toEnLayout);
|
||||
|
||||
await replyToMessage(msg, newText).catch(logError);
|
||||
await oldReplyToMessage(msg, newText).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,10 @@ import {Requirements} from "../base/requirements";
|
||||
import {Requirement} from "../base/requirement";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {bot, botUser} from "../index";
|
||||
import {fullName, logError, oldSendMessage, replyToMessage} from "../util/utils";
|
||||
import {fullName, logError, oldSendMessage, oldReplyToMessage} from "../util/utils";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class Unban extends ChatCommand {
|
||||
regexp = /^\/unban/i;
|
||||
title = "/unban [reply]";
|
||||
description = "unban user from chat";
|
||||
|
||||
@@ -25,17 +24,17 @@ export class Unban extends ChatCommand {
|
||||
const userId = user.id;
|
||||
|
||||
if (userId === botUser.id) {
|
||||
await replyToMessage(msg, "Бот и так не в бане сам у себя.").catch(logError);
|
||||
await oldReplyToMessage(msg, "Бот и так не в бане сам у себя.").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId === Environment.CREATOR_ID) {
|
||||
await replyToMessage(msg, "Создатель бота и так не в бане и никогда не будет.").catch(logError);
|
||||
await oldReplyToMessage(msg, "Создатель бота и так не в бане и никогда не будет.").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.from.id !== Environment.CREATOR_ID && Environment.ADMIN_IDS.has(userId)) {
|
||||
await replyToMessage(msg, "Админимтраторы бота и так не в бане.").catch(logError);
|
||||
await oldReplyToMessage(msg, "Админимтраторы бота и так не в бане.").catch(logError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import {Message} from "typescript-telegram-bot-api";
|
||||
import {botUser} from "../index";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class Unmute implements ChatCommand {
|
||||
regexp = /^\/unmute/i;
|
||||
export class Unmute extends ChatCommand {
|
||||
title = "/unmute";
|
||||
description = "Bot will start responding to the user";
|
||||
requirements = Requirements.Build(Requirement.BOT_ADMIN, Requirement.CHAT, Requirement.REPLY);
|
||||
|
||||
@@ -3,7 +3,6 @@ import {Message} from "typescript-telegram-bot-api";
|
||||
import {getUptime, logError, oldSendMessage} from "../util/utils";
|
||||
|
||||
export class Uptime extends ChatCommand {
|
||||
regexp = /^\/uptime/i;
|
||||
title = "/uptime";
|
||||
description = "Bot's uptime";
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {logError, randomValue, oldSendMessage} from "../util/utils";
|
||||
import {logError, oldSendMessage, randomValue} from "../util/utils";
|
||||
import {betterAnswers} from "../db/database";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
|
||||
export class WhatBetter extends ChatCommand {
|
||||
regexp = /^\/(what|что)\s(better|лучше)\s([^]+)\s(or|или)\s([^]+)/i;
|
||||
command = ["what", "что"];
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/what better [a] or [b]";
|
||||
description = "either a or b randomly (50% chance)";
|
||||
|
||||
private argsRe = /^(better|лучше)\s+([\s\S]+?)\s+(or|или)\s+([\s\S]+)$/i;
|
||||
|
||||
async execute(msg: Message, match?: RegExpExecArray) {
|
||||
const a = match[3];
|
||||
const b = match[5].trimStart();
|
||||
const args = (match?.[3] ?? "").trim();
|
||||
const m = this.argsRe.exec(args);
|
||||
if (!m) return;
|
||||
const a = m[2].trim();
|
||||
const b = m[4].trim();
|
||||
|
||||
const text = `${randomValue(betterAnswers)} ${randomValue([a, b])}`;
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {ChatCommand} from "../base/chat-command";
|
||||
import {getRandomInt, getRangedRandomInt, logError, replyToMessage} from "../util/utils";
|
||||
import {getRandomInt, getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
|
||||
export class When extends ChatCommand {
|
||||
regexp = /^\/(when|когда)\s([^]+)/i;
|
||||
command = ["when", "когда"];
|
||||
argsMode = "required" as const;
|
||||
|
||||
title = "/when [value]";
|
||||
description = "random date";
|
||||
|
||||
@@ -85,6 +87,6 @@ export class When extends ChatCommand {
|
||||
}
|
||||
}
|
||||
|
||||
await replyToMessage(msg, text).catch(logError);
|
||||
await oldReplyToMessage(msg, text).catch(logError);
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,10 @@ export class Environment {
|
||||
static OLLAMA_API_KEY?: string;
|
||||
|
||||
static GEMINI_API_KEY?: string;
|
||||
static GEMINI_MODEL?: string;
|
||||
static GEMINI_MODEL: string;
|
||||
|
||||
static MISTRAL_API_KEY?: string;
|
||||
static MISTRAL_MODEL?: string;
|
||||
static MISTRAL_MODEL: string;
|
||||
|
||||
static waitText = "⏳ Дайте-ка подумать...";
|
||||
|
||||
@@ -64,10 +64,10 @@ export class Environment {
|
||||
Environment.OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;
|
||||
|
||||
Environment.GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
||||
Environment.GEMINI_MODEL = process.env.GEMINI_MODEL;
|
||||
Environment.GEMINI_MODEL = process.env.GEMINI_MODEL || "gemini-2.5-flash";
|
||||
|
||||
Environment.MISTRAL_API_KEY = process.env.MISTRAL_API_KEY;
|
||||
Environment.MISTRAL_MODEL = process.env.MISTRAL_MODEL;
|
||||
Environment.MISTRAL_MODEL = process.env.MISTRAL_MODEL || "mistral-small-latest";
|
||||
}
|
||||
|
||||
static setAdmins(admins: Set<number>) {
|
||||
@@ -93,4 +93,16 @@ export class Environment {
|
||||
|
||||
return has;
|
||||
}
|
||||
|
||||
static setOllamaModel(newModel: string) {
|
||||
Environment.OLLAMA_MODEL = newModel;
|
||||
}
|
||||
|
||||
static setGeminiModel(newModel: string) {
|
||||
Environment.GEMINI_MODEL = newModel;
|
||||
}
|
||||
|
||||
static setMistralModel(newModel: string) {
|
||||
Environment.MISTRAL_MODEL = newModel;
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,5 @@ export type MessagePart = {
|
||||
bot: boolean;
|
||||
name?: string;
|
||||
content: string;
|
||||
images: string[];
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export class MessageStore {
|
||||
static async put(m: Message, prefix: string = Environment.BOT_PREFIX) {
|
||||
const msg: StoredMessage = {
|
||||
chatId: m.chat.id,
|
||||
messageId: m.message_id,
|
||||
id: m.message_id,
|
||||
replyToMessageId: m.reply_to_message?.message_id ?? null,
|
||||
fromId: m.from.id,
|
||||
text: extractTextMessage(m, prefix),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {messagesTable} from "./schema";
|
||||
import {MessageInsert, messagesTable} from "./schema";
|
||||
import {DatabaseManager} from "./database-manager";
|
||||
import {StoredMessage} from "../model/stored-message";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
@@ -66,7 +66,7 @@ export class MessageDao extends Dao<StoredMessage> {
|
||||
return this.mapFrom(messages);
|
||||
}
|
||||
|
||||
async insert(values: typeof messagesTable.$inferInsert[]): Promise<true> {
|
||||
async insert(values: MessageInsert[]): Promise<true> {
|
||||
const then = Date.now();
|
||||
const r = await DatabaseManager.db
|
||||
.insert(messagesTable)
|
||||
@@ -82,7 +82,7 @@ export class MessageDao extends Dao<StoredMessage> {
|
||||
return true;
|
||||
}
|
||||
|
||||
mapTo(messages: Message[]): typeof messagesTable.$inferInsert[] {
|
||||
mapTo(messages: Message[]): MessageInsert[] {
|
||||
return messages.map(msg => {
|
||||
return {
|
||||
chatId: msg.chat.id,
|
||||
@@ -95,15 +95,16 @@ export class MessageDao extends Dao<StoredMessage> {
|
||||
});
|
||||
}
|
||||
|
||||
mapFrom(messages: typeof messagesTable.$inferInsert[]): StoredMessage[] {
|
||||
mapFrom(messages: MessageInsert[]): StoredMessage[] {
|
||||
return messages.map(m => {
|
||||
return {
|
||||
chatId: m.chatId,
|
||||
messageId: m.id,
|
||||
id: m.id,
|
||||
replyToMessageId: m.replyToMessageId,
|
||||
fromId: m.fromId,
|
||||
text: m.text,
|
||||
date: m.date
|
||||
date: m.date,
|
||||
photoMaxSizeFilePath: m.photoMaxSizeFilePath
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export const messagesTable = sqliteTable("messages", {
|
||||
fromId: int().notNull(),
|
||||
text: text(),
|
||||
date: int().notNull(),
|
||||
photoMaxSizeFilePath: text(),
|
||||
});
|
||||
|
||||
export type MessageInsert = typeof messagesTable.$inferInsert;
|
||||
|
||||
+44
-5
@@ -7,6 +7,7 @@ import {
|
||||
executeChatCommand,
|
||||
extractTextMessage,
|
||||
findAndExecuteCallbackCommand,
|
||||
ignore,
|
||||
initSystemSpecs,
|
||||
logError,
|
||||
randomValue,
|
||||
@@ -57,6 +58,17 @@ 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";
|
||||
|
||||
process.setUncaughtExceptionCaptureCallback(console.error);
|
||||
|
||||
@@ -69,6 +81,9 @@ 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}`}
|
||||
@@ -148,7 +163,13 @@ export const callbackCommands: CallbackCommand[] = [
|
||||
];
|
||||
|
||||
if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) {
|
||||
chatCommands.push(new OllamaChat(), new OllamaPrompt());
|
||||
chatCommands.push(
|
||||
new OllamaChat(),
|
||||
new OllamaPrompt(),
|
||||
new OllamaListModels(),
|
||||
new OllamaGetModel(),
|
||||
new OllamaSetModel()
|
||||
);
|
||||
}
|
||||
|
||||
if (Environment.OLLAMA_API_KEY) {
|
||||
@@ -156,18 +177,29 @@ if (Environment.OLLAMA_API_KEY) {
|
||||
}
|
||||
|
||||
if (Environment.GEMINI_API_KEY) {
|
||||
chatCommands.push(new GeminiChat());
|
||||
chatCommands.push(
|
||||
new GeminiChat(),
|
||||
new GeminiListModels(),
|
||||
new GeminiGetModel(),
|
||||
new GeminiSetModel()
|
||||
);
|
||||
}
|
||||
|
||||
if (Environment.MISTRAL_API_KEY) {
|
||||
chatCommands.push(new MistralChat());
|
||||
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}`
|
||||
`MAX_PHOTO_SIZE: ${Environment.MAX_PHOTO_SIZE}\n` +
|
||||
`ONLY_FOR_CREATOR: ${Environment.ONLY_FOR_CREATOR_MODE}`
|
||||
);
|
||||
|
||||
const commands = chatCommands.filter(cmd => {
|
||||
@@ -214,7 +246,7 @@ bot.on("edited_message", async (msg) => {
|
||||
bot.on("message", async (msg) => {
|
||||
console.log("message", msg);
|
||||
|
||||
await Promise.all([MessageStore.put(msg), UserStore.put(msg.from)]);
|
||||
Promise.all([MessageStore.put(msg), UserStore.put(msg.from)]).then(ignore);
|
||||
|
||||
if ((msg.new_chat_members?.length || 0 > 0)) {
|
||||
await bot.sendMessage({chat_id: msg.chat.id, text: randomValue(inviteAnswers)}).catch(logError);
|
||||
@@ -232,8 +264,15 @@ bot.on("message", async (msg) => {
|
||||
|
||||
const cmdText = msg.text || msg.caption || "";
|
||||
|
||||
const then = Date.now();
|
||||
|
||||
const cmd = searchChatCommand(chatCommands, cmdText);
|
||||
const executed = await executeChatCommand(cmd, msg, cmdText);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log("diff", diff);
|
||||
|
||||
if (executed || !cmdText) return;
|
||||
|
||||
const startsWithPrefix = cmdText.toLowerCase().startsWith(Environment.BOT_PREFIX.toLowerCase());
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export type StoredMessage = {
|
||||
chatId: number;
|
||||
messageId: number;
|
||||
id: number;
|
||||
replyToMessageId?: number | null;
|
||||
fromId: number;
|
||||
text?: string;
|
||||
date: number;
|
||||
photoMaxSizeFilePath?: string | null;
|
||||
};
|
||||
+99
-20
@@ -14,6 +14,7 @@ import {UserStore} from "../common/user-store";
|
||||
import * as orm from "drizzle-orm";
|
||||
import {sql, type SQL} from "drizzle-orm";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const ignore = () => {
|
||||
};
|
||||
@@ -38,12 +39,21 @@ export const errorPlaceholder = async (msg: Message) => {
|
||||
await sendErrorPlaceholder(msg).catch(logError);
|
||||
};
|
||||
|
||||
export function searchChatCommand(commands: ChatCommand[], text: string): ChatCommand | null {
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const command = commands[i];
|
||||
if (command?.regexp.test(text)) {
|
||||
return command;
|
||||
export function searchChatCommand(
|
||||
commands: ChatCommand[],
|
||||
text: string,
|
||||
botUsername: string = botUser.username
|
||||
): ChatCommand | null {
|
||||
for (const command of commands) {
|
||||
const match = command.finalRegexp.exec(text);
|
||||
if (!match) continue;
|
||||
|
||||
const mentioned = match[2]?.toLowerCase();
|
||||
if (botUsername && mentioned && mentioned !== botUsername.toLowerCase()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -81,13 +91,13 @@ export async function checkRequirements(cmd: ChatCommand | null, msg: Message):
|
||||
|
||||
if (reqs.isRequiresBotCreator() && fromId !== Environment.CREATOR_ID) {
|
||||
console.log(`${cmd.title}: creatorId is bad`);
|
||||
await replyToMessage(msg, "Вы не являетесь создателем бота.");
|
||||
await oldReplyToMessage(msg, "Вы не являетесь создателем бота.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reqs.isRequiresBotAdmin() && !Environment.ADMIN_IDS.has(fromId)) {
|
||||
console.log(`${cmd.title}: adminId is bad`);
|
||||
await replyToMessage(msg, "Вы не являетесь администратором бота.");
|
||||
await oldReplyToMessage(msg, "Вы не являетесь администратором бота.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,20 +107,20 @@ export async function checkRequirements(cmd: ChatCommand | null, msg: Message):
|
||||
|
||||
if (!isAdmin) {
|
||||
console.log(`${cmd.title}: chatAdminId is bad`);
|
||||
await replyToMessage(msg, "Бот не является администратором чата.");
|
||||
await oldReplyToMessage(msg, "Бот не является администратором чата.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (reqs.isRequiresChat() && msg.chat.type === "private") {
|
||||
console.log(`${cmd.title}: chatId is bad`);
|
||||
await replyToMessage(msg, "Тут Вам не чат.");
|
||||
await oldReplyToMessage(msg, "Тут Вам не чат.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reqs.isRequiresReply() && !msg.reply_to_message) {
|
||||
console.log(`${cmd.title}: replyMessage is bad`);
|
||||
await replyToMessage(msg, "Отсутствует ответ на сообщение.");
|
||||
await oldReplyToMessage(msg, "Отсутствует ответ на сообщение.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -179,10 +189,11 @@ export async function editMessageText(chatId: number, messageId: number, message
|
||||
}
|
||||
|
||||
export type SendOptions = {
|
||||
chatId?: number;
|
||||
chat_id?: number;
|
||||
message?: Message,
|
||||
message_id?: number;
|
||||
text: string,
|
||||
parseMode?: ParseMode,
|
||||
parse_mode?: ParseMode,
|
||||
};
|
||||
|
||||
export async function oldSendMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> {
|
||||
@@ -197,15 +208,28 @@ export async function oldSendMessage(message: Message, text: string, parseMode?:
|
||||
|
||||
export async function sendMessage(options: SendOptions): Promise<Message> {
|
||||
const response = await bot.sendMessage({
|
||||
chat_id: options.chatId ?? options.message?.chat?.id,
|
||||
chat_id: options.chat_id ?? options.message?.chat?.id,
|
||||
text: options.text,
|
||||
parse_mode: options.parseMode
|
||||
parse_mode: options.parse_mode
|
||||
});
|
||||
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
|
||||
export async function replyToMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> {
|
||||
export async function replyToMessage(options: SendOptions): Promise<Message> {
|
||||
const response = await bot.sendMessage({
|
||||
chat_id: options.chat_id ?? options.message?.chat?.id,
|
||||
text: options.text,
|
||||
parse_mode: options.parse_mode,
|
||||
reply_parameters: {
|
||||
message_id: options.message_id || options.message?.message_id
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
|
||||
export async function oldReplyToMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> {
|
||||
const response = await bot.sendMessage({
|
||||
chat_id: message.chat.id,
|
||||
text: text,
|
||||
@@ -382,6 +406,7 @@ export function extractTextStored(msg: StoredMessage, prefix: string): string {
|
||||
}
|
||||
|
||||
export function extractText(text: string, prefix: string): string {
|
||||
if (!text) return "";
|
||||
if (text.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||
text = text.substring(prefix.length).trim();
|
||||
}
|
||||
@@ -389,18 +414,58 @@ export function extractText(text: string, prefix: string): string {
|
||||
return text;
|
||||
}
|
||||
|
||||
export function isStoredMessage(msg: Message | StoredMessage): msg is StoredMessage {
|
||||
return "id" in msg;
|
||||
}
|
||||
|
||||
export async function loadImageIfExists(msg: Message | StoredMessage): Promise<string | null> {
|
||||
if (isStoredMessage(msg)) {
|
||||
return msg.photoMaxSizeFilePath;
|
||||
}
|
||||
|
||||
let imageFilePath: string | null = null;
|
||||
|
||||
const maxSize = await getPhotoMaxSize(msg.photo);
|
||||
if (maxSize) {
|
||||
const imagePath = path.join(Environment.DATA_PATH, "temp");
|
||||
if (!fs.existsSync(imagePath)) {
|
||||
fs.mkdirSync(imagePath);
|
||||
}
|
||||
|
||||
imageFilePath = path.join(imagePath, maxSize.unique_file_id + ".jpg");
|
||||
if (!fs.existsSync(imageFilePath)) {
|
||||
const res = await axios.get<ArrayBuffer>(maxSize.url, {responseType: "arraybuffer"});
|
||||
const src = Buffer.from(res.data);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(imageFilePath, src);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
imageFilePath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageFilePath;
|
||||
}
|
||||
|
||||
export async function collectReplyChainText(triggerMsg: Message, prefix: string = Environment.BOT_PREFIX, limit: number = 40, includeTrigger = true): Promise<MessagePart[]> {
|
||||
const chatId = triggerMsg.chat.id as number;
|
||||
|
||||
const parts: MessagePart[] = [];
|
||||
if (includeTrigger) {
|
||||
const t = extractTextMessage(triggerMsg, prefix);
|
||||
if (t) parts.push({
|
||||
const img = (await loadImageIfExists(triggerMsg)) /*|| triggerMsg.reply_to_message ?
|
||||
(await loadImageIfExists(triggerMsg.reply_to_message)) : null*/;
|
||||
if (t) {
|
||||
parts.push({
|
||||
bot: triggerMsg.from.id === botUser.id,
|
||||
content: t,
|
||||
name: triggerMsg.from.first_name
|
||||
name: triggerMsg.from.first_name,
|
||||
images: img ? [img] : []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const first = triggerMsg.reply_to_message;
|
||||
if (!first) {
|
||||
@@ -408,7 +473,15 @@ export async function collectReplyChainText(triggerMsg: Message, prefix: string
|
||||
}
|
||||
|
||||
const firstText = extractTextMessage(first, prefix);
|
||||
if (firstText) parts.push({bot: first.from.id === botUser.id, content: firstText, name: first.from.first_name});
|
||||
if (firstText || first.photo) {
|
||||
const img = await loadImageIfExists(first);
|
||||
parts.push({
|
||||
bot: first.from.id === botUser.id,
|
||||
content: firstText,
|
||||
name: first.from.first_name,
|
||||
images: img ? [img] : []
|
||||
});
|
||||
}
|
||||
|
||||
let curId = first.message_id;
|
||||
|
||||
@@ -418,14 +491,16 @@ export async function collectReplyChainText(triggerMsg: Message, prefix: string
|
||||
if (!parentId) break;
|
||||
|
||||
const parent = await messageDao.getById({chatId: chatId, id: parentId});
|
||||
if (!parent?.text) break;
|
||||
if (!parent?.text && !parent?.photoMaxSizeFilePath) break;
|
||||
|
||||
const user = await UserStore.get(parent.fromId);
|
||||
const img = await loadImageIfExists(parent);
|
||||
|
||||
parts.push({
|
||||
bot: parent.fromId === botUser.id,
|
||||
content: extractTextStored(parent, prefix),
|
||||
name: user?.firstName
|
||||
name: user?.firstName,
|
||||
images: img ? [img] : []
|
||||
});
|
||||
curId = parentId;
|
||||
}
|
||||
@@ -807,3 +882,7 @@ export function ifTrue(exp?: never): boolean {
|
||||
|
||||
return ["true", "t", "y", 1, "1"].includes(exp);
|
||||
}
|
||||
|
||||
export function boolToEmoji(bool: boolean): string {
|
||||
return bool ? "✅" : "❌";
|
||||
}
|
||||
Reference in New Issue
Block a user