diff --git a/bot.js b/bot.js
index 5894e2b..cada945 100644
--- a/bot.js
+++ b/bot.js
@@ -4,6 +4,9 @@ const path = require('path');
const { OpenRouterClient } = require('openrouter-kit');
const { getPrompt } = require('./prompts.js');
const logger = require('./logger.js');
+const callAI = require("./requestAI");
+const {searchInCache, searchInPrompts, saveInPrompts} = require("./promptResponser");
+const INSTRUCTIONS = require("./promptGen");
require('dotenv').config();
const char = {name:'marina'}
class TelegramHistoryBot {
@@ -66,7 +69,40 @@ class TelegramHistoryBot {
}
setupHandlers() {
+ this.bot.command('summy', async (ctx) => {
+ const message = ctx.message.text || '';
+ const args = message.replace(/^\/summy(@\w+)?\s*/, ''); // удаляет /summy и возможный @botname
+ const trimmed = args.trim(); // удаляет лишние пробелы по краям, если нужно
+ logger.info('📊 Получена команда /summy с запросом: ' + trimmed);
+ let request = await callAI('', trimmed, 'request');
+console.log('получили отвкт: ', typeof request, request)
+ if (typeof request === 'string') {
+ try {
+ request = JSON.parse(request);
+ } catch {
+ request = {persona: "Карл Маркс", messages: 22, hours: 0};
+ }
+ }
+console.log('ищем персону')
+ let prompt = await searchInPrompts(request["persona"])
+ if (!prompt) {
+ logger.info('⚠️ Не найден подходящий prompt для запроса, генерируем...')
+ prompt=await callAI(INSTRUCTIONS, request["persona"], 'prompt')
+ await saveInPrompts(request["persona"], prompt)
+ } else {
+ logger.info('✅ Найден подходящий prompt для запроса')
+ }
+ //handleSummaryCommand
+ const options = {persona:request["persona"], promptToUse:prompt}
+ if (request["messages"]>0) {
+ await this.handleSummaryCommand(ctx, 'last', request["messages"], options);
+ } else {
+ await this.handleSummaryCommand(ctx, 'hours', request["hours"], options);
+ }
+ //await ctx.reply(prompt)
+
+ });
// Команды суммаризации - должны быть ДО обработки обычных сообщений
this.bot.command('summary_day', async (ctx) => {
// if (!this.isAdmin(ctx)) {
@@ -220,10 +256,11 @@ class TelegramHistoryBot {
return Object.keys(info).length > 0 ? info : null;
}
- async handleSummaryCommand(ctx, type, value) {
+ async handleSummaryCommand(ctx, type, value, options = {}) {
logger.info(`📊 Обработка команды суммаризации: ${type}${value ? ` (${value})` : ''}`);
const chatId = ctx.chat.id;
+
let messages = [];
try {
@@ -264,7 +301,7 @@ class TelegramHistoryBot {
}
// Генерируем суммаризацию
- const summary = await this.generateSummary(messages, type, value);
+ const summary = await this.generateSummary(messages, type, value, options);
// Отправляем результат
await ctx.reply(summary, { parse_mode: 'HTML' });
@@ -298,11 +335,15 @@ class TelegramHistoryBot {
.slice(-count);
}
- async generateSummary(messages, type, value) {
+ async generateSummary(messages, type, value, options = {}) {
const preparedData = this.prepareDataForAI(messages);
- const prompt = this.createSummaryPrompt(type, value);
+ if (options) {
- return await this.callAISummarization(prompt, preparedData);
+ return await this.callAISummarization('Ты получишь данные чата. ' + options.promptToUse, preparedData);
+ } else {
+ const prompt = this.createSummaryPrompt(type, value);
+ return await this.callAISummarization(prompt, preparedData);
+ }
}
prepareDataForAI(messages) {
@@ -440,79 +481,10 @@ ${participantsList}
ХОД РАЗГОВОРА:
${conversationFlow}`;
}
-
async callAISummarization(prompt, data) {
- // Если API ключ недоступен, используем fallback
- if (!process.env.OPENROUTER_API_KEY) {
- logger.warn('⚠️ OpenRouter API ключ недоступен, используем базовую суммаризацию');
- return this.generateFallbackSummary(data);
- }
-
- try {
- const formattedMessages = this.formatMessagesForAI(data);
- logger.info('🤖 Отправляем запрос к OpenRouter...');
-
- const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
- 'Content-Type': 'application/json',
- 'HTTP-Referer': 'https://github.com/your-repo', // Замените на ваш репозиторий
- 'X-Title': 'Telegram History Bot'
- },
- body: JSON.stringify({
- model: process.env.OPENROUTER_MODEL,
- // model: 'google/gemini-2.5-flash-preview-05-20',
- // model: 'google/gemini-2.0-flash-exp:free',
- // model: 'deepseek/deepseek-chat-v3-0324:free',
- messages: [
- {
- role: 'system',
- content: prompt
- },
- {
- role: 'user',
- content: formattedMessages
- }
- ],
- max_tokens: 2000,
- temperature: 0.8,
- top_p: 0.9
- })
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`);
- }
-
- const completion = await response.json();
- const aiSummary = completion.choices?.[0]?.message?.content;
-
- if (!aiSummary) {
- throw new Error('Пустой ответ от ИИ');
- }
-
- logger.info('✅ Получен ответ от OpenRouter');
- return `🎬 История чата\n\n${aiSummary}`;
-
- } catch (error) {
- logger.error('❌ Ошибка при вызове OpenRouter API:', error);
-
- // Детальная обработка различных типов ошибок
- if (error.message.includes('401')) {
- logger.error('❌ Неверный API ключ OpenRouter');
- } else if (error.message.includes('429')) {
- logger.error('❌ Превышен лимит запросов OpenRouter');
- } else if (error.message.includes('402')) {
- logger.error('❌ Недостаточно средств на счету OpenRouter');
- } else if (error.code === 'ENOTFOUND') {
- logger.error('❌ Проблемы с сетевым подключением');
- }
-
- return this.generateFallbackSummary(data);
- }
+ return await callAI(prompt, this.formatMessagesForAI(data), 'history')
}
+
generateFallbackSummary(data) {
const { metadata } = data;
const mostActive = metadata.most_active_user;
diff --git a/commandResponser.js b/commandResponser.js
new file mode 100644
index 0000000..7650659
--- /dev/null
+++ b/commandResponser.js
@@ -0,0 +1,75 @@
+const fs = require('fs');
+const logger = require('./logger');
+const cacheFile = 'commandCache.json';
+
+let aiCache = {};
+
+// Загружаем кэш из файла при старте
+function loadAiCache() {
+ logger.info('Загрузка командного кэша...');
+ try {
+ if (fs.existsSync(cacheFile)) {
+ const data = fs.readFileSync(cacheFile);
+ aiCache = JSON.parse(data);
+ logger.info('✅ Кэш команд загружен.');
+ } else {
+ fs.writeFileSync(cacheFile, JSON.stringify({}, null, 2));
+ logger.info('✅ Кэш команд создан.');
+ }
+ } catch (error) {
+ logger.error('❌ Ошибка загрузки командного кэша:', error);
+ }
+}
+
+// Сохраняем кэш в файл
+function saveAiCache() {
+ try {
+ fs.writeFileSync(cacheFile, JSON.stringify(aiCache, null, 2));
+ } catch (error) {
+ logger.error('❌ Ошибка сохранения командного кэша:', error);
+ }
+}
+loadAiCache();
+
+// Поиск запроса в кэше
+function searchInCache(request) {
+ logger.info(`🔎 Поиск в кэше: ${request}`);
+ if (!request || typeof request !== 'string') return null;
+ const result = aiCache[request];
+ if (result) {
+ logger.info(`🔁 Найдено в кэше: ${request}`);
+ return result;
+ }
+ return null;
+}
+
+// Сохранение запроса и ответа в кэш
+// async function saveInCache(request, result) {
+// if (!request || !result) return;
+// aiCache[request] = result;
+// saveAiCache();
+// logger.info(`💾 Запрос сохранён в кэш: ${request}`);
+// }
+async function saveInCache(request, result) {
+ if (!request || !result) return;
+
+ let finalResult = result;
+
+ // Если это результат парсинга команды и он пришёл как строка JSON
+ if (typeof result === 'string' && result.includes('"persona"') && result.includes('"messages"')) {
+ try {
+ finalResult = JSON.parse(result);
+ } catch (error) {
+ logger.warn(`⚠️ Не удалось распарсить JSON команды: ${error.message}`);
+ }
+ }
+
+ aiCache[request] = finalResult;
+ saveAiCache();
+ logger.info(`💾 Запрос сохранён в кэш: ${request}`);
+}
+//
+// // Загрузим кэш при старте
+// loadAiCache();
+
+module.exports = { searchInCache, saveInCache };
diff --git a/promptGen.js b/promptGen.js
new file mode 100644
index 0000000..fa4553c
--- /dev/null
+++ b/promptGen.js
@@ -0,0 +1,73 @@
+const INSTRUCTIONS = `Ты генератор промптов для создания суммаризаторов чата от лица различных персонажей.
+
+ВХОДНЫЕ ДАННЫЕ:
+Пользователь укажет персонажа одним из способов:
+1. Известная личность: "Владимир Ленин", "Шерлок Холмс", "Дональд Трамп", "группа Металлика"
+2. Описанный персонаж: "участник чата @username, его особенности - любит игуан, ебет медведей, пьет спирт, всех ненавидит, кроме тех, кто кидает мемасики"
+
+ЗАДАЧА:
+На основе указанного персонажа создай полный промпт для суммаризатора чата.
+
+СТРУКТУРА ПРОМПТА:
+
+ТВОЙ ПЕРСОНАЖ - [Имя и краткое описание персонажа, его возраст/статус если релевантно]
+
+ЗАДАЧА: Создай ЖИВУЮ ИСТОРИЮ того, что происходило в чате [с точки зрения этого персонажа].
+
+ВВОДНЫЕ:
+- чат не начался, он продолжается много лет
+- [специфичные для персонажа вводные]
+
+НЕ пиши сухие факты! Покажи [СТИЛЬ ПОВЕСТВОВАНИЯ ПЕРСОНАЖА]:
+- Кто с кем общался и о чем
+- Какие были споры, шутки, обсуждения
+- [специфичные пункты для персонажа]
+
+СТИЛЬ:
+- [Характерный стиль речи персонажа]
+- [Специфичная лексика и обороты]
+- [Особенности мировоззрения]
+- [Отношение к людям и событиям]
+
+ВАЖНО:
+- Мемы/медиа - [как персонаж это воспринимает]
+- Споры - [как персонаж видит конфликты]
+- НЕ используй теги пользователей, НЕ используй форматирование
+- Длина: 2-4 абзаца максимум
+
+ОЧЕНЬ ВАЖНО:
+- Игнорируй словесные игры
+- Игнорируй рекламу
+- [специфичные ограничения для персонажа]
+
+Заголовок НЕ НУЖЕН.
+
+[Финальная фраза в стиле персонажа, призывающая к анализу]
+
+ПРАВИЛА ГЕНЕРАЦИИ:
+
+1. Для ИЗВЕСТНЫХ ЛИЧНОСТЕЙ:
+- Используй их реальные черты характера, манеру речи, мировоззрение
+- Адаптируй их под современные реалии чата
+- Сохраняй узнаваемость персонажа
+
+2. Для ИЗВЕСТНЫХ ПИСАТЕЛЕЙ:
+- Используй их реальные черты характера, манеру речи, мировоззрение
+- Текст должен быть похож на часть книги, написанной автором.
+- Сохраняй узнаваемость стилистики автора
+
+
+3. Для ОПИСАННЫХ ПЕРСОНАЖЕЙ:
+- Строго следуй данному описанию
+- Развивай характер на основе указанных особенностей
+- Не придумывай лишних черт, не упомянутых в описании
+
+4. ОБЩИЕ ТРЕБОВАНИЯ:
+- Персонаж должен быть ярким и узнаваемым
+- Стиль речи должен отличаться от других персонажей
+- Избегай повторов конструкций из предыдущих промптов
+- Делай персонажа живым, а не картонным
+
+ВАЖНО: Не используй в промпте форматирование и теги пользователей!`
+
+module.exports = INSTRUCTIONS
\ No newline at end of file
diff --git a/promptResponser.js b/promptResponser.js
new file mode 100644
index 0000000..3cfdab9
--- /dev/null
+++ b/promptResponser.js
@@ -0,0 +1,105 @@
+const fs = require('fs');
+const logger = require('./logger');
+const cacheFile = 'prompts.json';
+
+let prompts = {};
+
+// Загружаем кэш из файла при старте
+function loadPrompts() {
+ logger.info('Загрузка промптов...');
+ try {
+ if (fs.existsSync(cacheFile)) {
+ const data = fs.readFileSync(cacheFile);
+ prompts = JSON.parse(data);
+ logger.info('✅ Промпты загружены.');
+ } else {
+ fs.writeFileSync(cacheFile, JSON.stringify({}, null, 2));
+ logger.info('✅ Файл промптов создан.');
+ }
+ } catch (error) {
+ logger.error('❌ Ошибка загрузки промптов:', error);
+ }
+}
+
+// Сохраняем кэш в файл
+function savePrompt() {
+ try {
+ fs.writeFileSync(cacheFile, JSON.stringify(prompts, null, 2));
+ } catch (error) {
+ logger.error('❌ Ошибка сохранения промптов:', error);
+ }
+}
+loadPrompts();
+
+// Поиск запроса в кэше
+
+/**
+ * Выполняет поиск промпта в кэше по ключу запроса
+ *
+ * @description Функция ищет сохранённый промпт в глобальном кэше prompts.
+ * Логирует процесс поиска и результат. Используется для избежания повторных
+ * обращений к AI при одинаковых запросах.
+ *
+ * @param {string} request - Ключ запроса для поиска в кэше промптов
+ * @returns {string|null} Найденный промпт или null, если не найден
+ *
+ * @example
+ * // Поиск существующего промпта
+ * const prompt = searchInPrompts("Карл Маркс");
+ * if (prompt) {
+ * console.log("Промпт найден:", prompt);
+ * }
+ *
+ * @example
+ * // Обработка отсутствующего промпта
+ * const result = searchInPrompts("Журавль летающий");
+ * // result будет null
+ *
+ * @since 1.0.0
+ * @author Vufer
+ *
+ * @see {@link saveInPrompts} для сохранения в кэш
+ *
+ * @throws {Error} Не выбрасывает исключений, возвращает null при ошибках
+ *
+ * @todo Добавить поиск по частичному совпадению ключей
+ */
+function searchInPrompts(request) {
+ logger.info(`🔎 Поиск в промптах: ${request}`);
+ if (!request || typeof request !== 'string') return null;
+ const result = prompts[request] || null;
+ if (result) {
+ logger.info(`🔁 Найдено в промптах: ${request}`);
+ return result;
+ }
+ return null;
+}
+// Сохранение запроса и ответа в кэш
+// async function saveInCache(request, result) {
+// if (!request || !result) return;
+// aiCache[request] = result;
+// saveAiCache();
+// logger.info(`💾 Запрос сохранён в кэш: ${request}`);
+// }
+async function saveInPrompts(request, result) {
+ if (!request || !result) return;
+
+ let finalResult = result;
+
+ if (typeof result === 'string' && result.includes('persona') && result.includes('messages')) {
+ try {
+ finalResult = JSON.parse(result);
+ } catch (error) {
+ logger.warn(`⚠️ Не удалось распарсить JSON команды: ${error.message}`);
+ }
+ }
+
+ prompts[request] = finalResult;
+ savePrompt();
+ logger.info(`💾 Промпт сохранён в кэш: ${request}`);
+}
+//
+// // Загрузим кэш при старте
+// loadAiCache();
+
+module.exports = { searchInPrompts, saveInPrompts };
diff --git a/requestAI.js b/requestAI.js
new file mode 100644
index 0000000..185daba
--- /dev/null
+++ b/requestAI.js
@@ -0,0 +1,218 @@
+const logger = require("./logger");
+const {searchInCache, saveInCache} = require("./commandResponser");
+require('dotenv').config();
+// request_parse_prompt= 'Получена строка, это команда ТГ бота. ' +
+// 'Команда может содержать период или количество сообщений, а так же имя персонажа (вероятнее всего известная личность, возможно - название музыкальной группы).' +
+// 'Необходимо разобрать.' +
+// 'Если не удалось разобрать, то вернуть со значениями persona:"Карл Маркс",messages:22.' +
+// 'Одновременно не может быть и количество сообщений, и количество часов, что-то одно.' +
+// 'Если интервал не в часах - пересчитать в часы!' +
+// 'Если имя принадлежит публичной или известной персоне - указать имя полностью! (например - кипелов - Валерий Кипелов, кюри - Мария Склодовская-Кюри' +
+// 'Если это название музыкальной группы - указать, что это музыкальная группа (группа ХХХХ)' +
+// 'Комментарии не оставлять' +
+// 'Вывод - строго в формате json' +
+// 'command={\n' +
+// ' persona:\'Карл Маркс\', // имя персонажа\n' +
+// ' messages:100, // сколько сообщений обработать или 0\n' +
+// ' hours: 24 // за сколько часов, или 0\n' +
+// '}' ;
+//
+//
+request_parse_prompt= 'Ты - эксперт по анализу текстовых команд. Твоя задача - разобрать команду Telegram бота и извлечь из неё информацию о персоне и временном периоде.\n' +
+ 'ВХОДНЫЕ ДАННЫЕ\n' +
+ 'Получена строка команды, которая может содержать:\n' +
+ '\n' +
+ 'Имя персонажа (известная личность, историческая фигура, или название музыкальной группы)\n' +
+ 'Временной период (количество часов, дней, недель, месяцев) ИЛИ количество сообщений\n' +
+ 'Дополнительные слова и предлоги\n' +
+ '\n' +
+ 'ПРАВИЛА ОБРАБОТКИ\n' +
+ '1. ПЕРСОНА\n' +
+ '\n' +
+ 'Если упомянута известная личность - указать полное имя (например: "кипелов" → "Валерий Кипелов", "кюри" → "Мария Склодовская-Кюри", "летов" → "Егор Николаевич Летов")\n' +
+ 'Если это музыкальная группа - указать с пометкой "группа" (например: "битлз" → "группа The Beatles", "гражданская оборона" → "группа Гражданская оборона")\n' +
+ 'Если персона не определена или неясна - использовать "Карл Маркс"\n' +
+ 'Учитывать различные формы написания (сокращения, транслитерацию, опечатки)\n' +
+ '\n' +
+ '2. ВРЕМЕННОЙ ПЕРИОД\n' +
+ '\n' +
+ 'Может быть указан в различных форматах: "2 часа", "3ч", "день", "неделю", "месяц", "за вчера"\n' +
+ 'ВСЕ периоды пересчитывать в ЧАСЫ:\n' +
+ '\n' +
+ '1 день = 24 часа\n' +
+ '1 неделя = 168 часов\n' +
+ '1 месяц = 720 часов (30 дней)\n' +
+ '"вчера" = 24 часа\n' +
+ '"позавчера" = 48 часов\n' +
+ '\n' +
+ '\n' +
+ 'Если период не указан - использовать 0\n' +
+ '\n' +
+ '3. КОЛИЧЕСТВО СООБЩЕНИЙ\n' +
+ '\n' +
+ 'Может быть указано как число + "сообщений", "постов", "записей"\n' +
+ 'Если не указано - использовать 0\n' +
+ 'ВАЖНО: одновременно НЕ МОЖЕТ быть и количество сообщений, и временной период - только что-то одно!\n' +
+ '\n' +
+ '4. ПРИОРИТЕТЫ\n' +
+ '\n' +
+ 'Если указаны и период, и количество сообщений - приоритет у количества сообщений (hours = 0)\n' +
+ 'Если ничего не указано - messages = 22, hours = 0\n' +
+ '\n' +
+ 'ПРИМЕРЫ ВХОДНЫХ КОМАНД И ОЖИДАЕМЫЙ РЕЗУЛЬТАТ\n' +
+ 'Команда: "покажи последние 50 сообщений в стиле Высоцкого"\n' +
+ 'Результат: {"persona": "Владимир Семёнович Высоцкий", "messages": 50, "hours": 0}\n' +
+ 'Команда: "сгенерируй как Metallica за последний день"\n' +
+ 'Результат: {"persona": "группа Metallica", "messages": 0, "hours": 24}\n' +
+ 'Команда: "в стиле Маска за 3 часа"\n' +
+ 'Результат: {"persona": "Илон Маск", "messages": 0, "hours": 3}\n' +
+ 'Команда: "как Цой последние 100 постов"\n' +
+ 'Результат: {"persona": "Виктор Робертович Цой", "messages": 100, "hours": 0}\n' +
+ 'Команда: "покажи Бетховена"\n' +
+ 'Результат: {"persona": "Людвиг ван Бетховен", "messages": 22, "hours": 0}\n' +
+ 'ИНСТРУКЦИИ ПО ВЫВОДУ\n' +
+ '\n' +
+ 'Комментарии НЕ оставлять\n' +
+ 'Вывод СТРОГО в формате JSON без markdown блоков\n' +
+ 'НЕ ИСПОЛЬЗОВАТЬ json или блоки\n' +
+ 'Возвращать ТОЛЬКО чистый JSON объект\n' +
+ 'Не добавлять дополнительные поля\n' +
+ 'Использовать двойные кавычки для JSON\n' +
+ 'Никаких объяснений до или после JSON\n' +
+ 'ОБЯЗАТЕЛЬНО ВАЛИДНЫЙ JSON!!! ОЧЕНЬ ВАЖНО!!\n' +
+ '\n' +
+ 'ФОРМАТ ОТВЕТА (возвращать БЕЗ markdown блоков):\n' +
+ '{"persona": "Полное имя персоны или группа Название (краткое описание в трех-пяти словах)", "messages": число_или_0, "hours": число_или_0}\n' +
+ 'КРИТИЧЕСКИ ВАЖНО\n' +
+ '\n' +
+ 'Твой ответ должен начинаться с { и заканчиваться }\n' +
+ 'Никаких дополнительных символов, пробелов или переносов строк до и после JSON\n' +
+ 'НЕ оборачивать в ```json блоки\n' +
+ 'Возвращать ТОЛЬКО валидный JSON объект\n' +
+ '\n' +
+ 'ОБРАБАТЫВАЙ КОМАНДУ СОГЛАСНО ВСЕМ ПРАВИЛАМ ВЫШЕ. АНАЛИЗИРУЙ КАЖДОЕ СЛОВО И КОНТЕКСТ. ВЕРНИ ТОЛЬКО JSON!'
+
+
+// async function handleRequest(request) {
+// // Попытка найти результат в кэше
+// const cachedResult = await searchInCache(request);
+//
+// if (cachedResult) {
+// return cachedResult; // Возвращаем найденное в кэше
+// }
+//
+// // Если в кэше ничего нет — вызываем основную функцию
+// const result = await someFunction(request);
+//
+// // Сохраняем результат в кэш
+// await saveInCache(request, result);
+//
+// return result;
+// }
+
+async function callAI(prompt, data, type = 'history') {
+ // Если API ключ недоступен, используем fallback
+ if (!process.env.OPENROUTER_API_KEY ) {
+ logger.error('⚠️ OpenRouter API ключ недоступен');
+ return;
+ }
+ let workingPrompt = '';
+ switch (type) {
+ case 'history':workingPrompt=prompt;
+ break;
+ case 'request':{
+ workingPrompt=request_parse_prompt;
+ const cachedResult = await searchInCache(data);
+ if (cachedResult) {
+ return cachedResult; // Возвращаем найденное в кэше
+ }
+ }
+
+ break;
+ default:workingPrompt=prompt;
+ break;
+ }
+ try {
+ const formattedMessages = data;
+ // const formattedMessages = this.formatMessagesForAI(data);
+ let useModel
+ switch (type) {
+ case 'history':useModel=process.env.OPENROUTER_MODEL;
+ break;
+ case 'prompt':useModel=process.env.OPENROUTER_MODEL;
+ break;
+ case 'request':useModel=process.env.OPENROUTER_CHEAP_MODEL;
+ break;
+ default:useModel=process.env.OPENROUTER_MODEL;
+ break;
+ }
+ // = (type!==='history' ? process.env.OPENROUTER_MODEL: process.env.OPENROUTER_CHEAP_MODEL);
+ logger.info(`🤖 Отправляем запрос к OpenRouter (модель ${useModel})...`);
+ const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
+ 'Content-Type': 'application/json',
+ 'HTTP-Referer': 'https://git.rockzo.ru/Rockzo_Develop/summy',
+ 'X-Title': 'Telegram History Bot'
+ },
+ body: JSON.stringify({
+ model: useModel,
+ messages: [
+ {
+ role: 'system',
+ content: workingPrompt
+ },
+ {
+ role: 'user',
+ content: formattedMessages
+ }
+ ],
+ max_tokens: 2000,
+ temperature: 0.8,
+ top_p: 0.9
+ })
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`);
+ }
+
+ const completion = await response.json();
+ const aiSummary = completion.choices?.[0]?.message?.content;
+
+ if (!aiSummary) {
+ logger.error(JSON.stringify(completion));
+ throw new Error('Пустой ответ от ИИ');
+ }
+
+ logger.info('✅ Получен ответ от OpenRouter');
+ if (type==='request') {
+ await saveInCache(data, aiSummary);
+ }
+ // return `🎬 История чата\n\n${aiSummary}`;
+if (type==='request') {
+ console.log(typeof aiSummary, aiSummary)
+ return JSON.parse(aiSummary);
+}
+ return aiSummary;
+
+ } catch (error) {
+ logger.error('❌ Ошибка при вызове OpenRouter API:', error);
+
+ // Детальная обработка различных типов ошибок
+ if (error.message.includes('401')) {
+ logger.error('❌ Неверный API ключ OpenRouter');
+ } else if (error.message.includes('429')) {
+ logger.error('❌ Превышен лимит запросов OpenRouter');
+ } else if (error.message.includes('402')) {
+ logger.error('❌ Недостаточно средств на счету OpenRouter');
+ } else if (error.code === 'ENOTFOUND') {
+ logger.error('❌ Проблемы с сетевым подключением');
+ }
+
+ return // this.generateFallbackSummary(data);
+ }
+}
+module.exports = callAI;
\ No newline at end of file