Compare commits
2 Commits
1fb3805794
...
e6a9743de3
| Author | SHA1 | Date | |
|---|---|---|---|
| e6a9743de3 | |||
| 31b7e14f75 |
64
bot.js
64
bot.js
@ -3,6 +3,7 @@ const fs = require('fs').promises;
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { OpenRouterClient } = require('openrouter-kit');
|
const { OpenRouterClient } = require('openrouter-kit');
|
||||||
const { getPrompt } = require('./prompts.js');
|
const { getPrompt } = require('./prompts.js');
|
||||||
|
const logger = require('./logger.js');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const char = {name:'marina'}
|
const char = {name:'marina'}
|
||||||
class TelegramHistoryBot {
|
class TelegramHistoryBot {
|
||||||
@ -13,7 +14,7 @@ class TelegramHistoryBot {
|
|||||||
|
|
||||||
// Инициализация OpenRouter с проверкой ключа
|
// Инициализация OpenRouter с проверкой ключа
|
||||||
if (!process.env.OPENROUTER_API_KEY) {
|
if (!process.env.OPENROUTER_API_KEY) {
|
||||||
console.warn('⚠️ OPENROUTER_API_KEY не найден, ИИ-суммаризация будет недоступна');
|
logger.warn('⚠️ OPENROUTER_API_KEY не найден, ИИ-суммаризация будет недоступна');
|
||||||
this.openRouter = null;
|
this.openRouter = null;
|
||||||
} else {
|
} else {
|
||||||
this.openRouter = new OpenRouterClient({
|
this.openRouter = new OpenRouterClient({
|
||||||
@ -37,9 +38,9 @@ class TelegramHistoryBot {
|
|||||||
try {
|
try {
|
||||||
await this.loadHistory();
|
await this.loadHistory();
|
||||||
this.setupHandlers();
|
this.setupHandlers();
|
||||||
console.log('✅ Bot запущен и готов к работе');
|
logger.info('✅ Bot запущен и готов к работе');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка инициализации бота:', error);
|
logger.error('❌ Ошибка инициализации бота:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,9 +49,9 @@ class TelegramHistoryBot {
|
|||||||
try {
|
try {
|
||||||
const data = await fs.readFile(this.historyFile, 'utf8');
|
const data = await fs.readFile(this.historyFile, 'utf8');
|
||||||
this.history = JSON.parse(data);
|
this.history = JSON.parse(data);
|
||||||
console.log(`📚 Загружено ${this.history.length} сообщений из истории`);
|
logger.info(`📚 Загружено ${this.history.length} сообщений из истории`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('📝 История не найдена, создаем новую');
|
logger.warn('📝 История не найдена, создаем новую');
|
||||||
this.history = [];
|
this.history = [];
|
||||||
await this.saveHistory();
|
await this.saveHistory();
|
||||||
}
|
}
|
||||||
@ -60,7 +61,7 @@ class TelegramHistoryBot {
|
|||||||
try {
|
try {
|
||||||
await fs.writeFile(this.historyFile, JSON.stringify(this.history, null, 2));
|
await fs.writeFile(this.historyFile, JSON.stringify(this.history, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка сохранения истории:', error);
|
logger.error('❌ Ошибка сохранения истории:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ class TelegramHistoryBot {
|
|||||||
// await ctx.deleteMessage
|
// await ctx.deleteMessage
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
console.log('📊 Получена команда summary_day');
|
logger.info('📊 Получена команда summary_day');
|
||||||
const args = ctx.message.text.split(' ');
|
const args = ctx.message.text.split(' ');
|
||||||
if (args.length > 1) {
|
if (args.length > 1) {
|
||||||
char.name=args[1]
|
char.name=args[1]
|
||||||
@ -82,7 +83,7 @@ class TelegramHistoryBot {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.bot.command('summary_hours', async (ctx) => {
|
this.bot.command('summary_hours', async (ctx) => {
|
||||||
console.log('📊 Получена команда summary_hours');
|
logger.info('📊 Получена команда summary_hours');
|
||||||
const args = ctx.message.text.split(' ');
|
const args = ctx.message.text.split(' ');
|
||||||
if (args.length < 2 || isNaN(parseInt(args[1]))) {
|
if (args.length < 2 || isNaN(parseInt(args[1]))) {
|
||||||
await ctx.reply('❗ Укажите количество часов: /summary_hours 6');
|
await ctx.reply('❗ Укажите количество часов: /summary_hours 6');
|
||||||
@ -94,7 +95,7 @@ class TelegramHistoryBot {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.bot.command('summary_last', async (ctx) => {
|
this.bot.command('summary_last', async (ctx) => {
|
||||||
console.log('📊 Получена команда summary_last');
|
logger.info('📊 Получена команда summary_last');
|
||||||
const args = ctx.message.text.split(' ');
|
const args = ctx.message.text.split(' ');
|
||||||
if (args.length < 2 || isNaN(parseInt(args[1]))) {
|
if (args.length < 2 || isNaN(parseInt(args[1]))) {
|
||||||
await ctx.reply('❗ Укажите количество сообщений: /summary_last 50');
|
await ctx.reply('❗ Укажите количество сообщений: /summary_last 50');
|
||||||
@ -120,25 +121,25 @@ class TelegramHistoryBot {
|
|||||||
|
|
||||||
// Обработка ошибок
|
// Обработка ошибок
|
||||||
this.bot.catch(async (err, ctx) => {
|
this.bot.catch(async (err, ctx) => {
|
||||||
console.error('❌ Ошибка бота:', err);
|
logger.error('❌ Ошибка бота:', err);
|
||||||
try {
|
try {
|
||||||
await ctx.reply('❌ Произошла ошибка при обработке команды');
|
await ctx.reply('❌ Произошла ошибка при обработке команды');
|
||||||
} catch (replyError) {
|
} catch (replyError) {
|
||||||
console.error('❌ Не удалось отправить сообщение об ошибке:', replyError);
|
logger.error('❌ Не удалось отправить сообщение об ошибке:', replyError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Запуск бота
|
// Запуск бота
|
||||||
this.bot.launch();
|
this.bot.launch();
|
||||||
console.log('🚀 Бот запущен');
|
logger.info('🚀 Бот запущен');
|
||||||
|
|
||||||
// Graceful stop
|
// Graceful stop
|
||||||
process.once('SIGINT', () => {
|
process.once('SIGINT', () => {
|
||||||
console.log('🛑 Получен сигнал SIGINT, останавливаем бота...');
|
logger.warn('🛑 Получен сигнал SIGINT, останавливаем бота...');
|
||||||
this.bot.stop('SIGINT');
|
this.bot.stop('SIGINT');
|
||||||
});
|
});
|
||||||
process.once('SIGTERM', () => {
|
process.once('SIGTERM', () => {
|
||||||
console.log('🛑 Получен сигнал SIGTERM, останавливаем бота...');
|
logger.warn('🛑 Получен сигнал SIGTERM, останавливаем бота...');
|
||||||
this.bot.stop('SIGTERM');
|
this.bot.stop('SIGTERM');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -169,9 +170,9 @@ class TelegramHistoryBot {
|
|||||||
|
|
||||||
const userName = messageData.first_name || messageData.username || 'Unknown';
|
const userName = messageData.first_name || messageData.username || 'Unknown';
|
||||||
const content = messageData.text || `[${messageData.message_type}]`;
|
const content = messageData.text || `[${messageData.message_type}]`;
|
||||||
console.log(`💾 Сохранено сообщение от ${userName}: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`);
|
logger.info(`💾 Сохранено сообщение от ${userName} в ${msg.chat.id} : ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка при обработке сообщения:', error);
|
logger.error('❌ Ошибка при обработке сообщения:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +221,7 @@ class TelegramHistoryBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleSummaryCommand(ctx, type, value) {
|
async handleSummaryCommand(ctx, type, value) {
|
||||||
console.log(`📊 Обработка команды суммаризации: ${type}${value ? ` (${value})` : ''}`);
|
logger.info(`📊 Обработка команды суммаризации: ${type}${value ? ` (${value})` : ''}`);
|
||||||
|
|
||||||
const chatId = ctx.chat.id;
|
const chatId = ctx.chat.id;
|
||||||
let messages = [];
|
let messages = [];
|
||||||
@ -250,7 +251,7 @@ class TelegramHistoryBot {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`📈 Найдено ${messages.length} сообщений для суммаризации`);
|
logger.info(`📈 Найдено ${messages.length} сообщений для суммаризации`);
|
||||||
|
|
||||||
if (messages.length === 0) {
|
if (messages.length === 0) {
|
||||||
await ctx.reply('❗ Сообщения для суммаризации не найдены');
|
await ctx.reply('❗ Сообщения для суммаризации не найдены');
|
||||||
@ -267,10 +268,10 @@ class TelegramHistoryBot {
|
|||||||
|
|
||||||
// Отправляем результат
|
// Отправляем результат
|
||||||
await ctx.reply(summary, { parse_mode: 'HTML' });
|
await ctx.reply(summary, { parse_mode: 'HTML' });
|
||||||
console.log('✅ Суммаризация отправлена');
|
logger.info('✅ Суммаризация отправлена');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка при создании суммаризации:', error);
|
logger.error('❌ Ошибка при создании суммаризации:', error);
|
||||||
await ctx.reply('❌ Произошла ошибка при создании суммаризации. Попробуйте позже.');
|
await ctx.reply('❌ Произошла ошибка при создании суммаризации. Попробуйте позже.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +389,7 @@ class TelegramHistoryBot {
|
|||||||
|
|
||||||
createSummaryPrompt(type, value) {
|
createSummaryPrompt(type, value) {
|
||||||
const timeDescription = this.getTimeDescription(type, value);
|
const timeDescription = this.getTimeDescription(type, value);
|
||||||
console.log('Выбранный персонаж: ', char)
|
logger.info('Выбранный персонаж: ', char)
|
||||||
return `Ты получишь данные чата Telegram ${timeDescription}. ` +
|
return `Ты получишь данные чата Telegram ${timeDescription}. ` +
|
||||||
getPrompt(char.name)
|
getPrompt(char.name)
|
||||||
|
|
||||||
@ -443,13 +444,13 @@ ${conversationFlow}`;
|
|||||||
async callAISummarization(prompt, data) {
|
async callAISummarization(prompt, data) {
|
||||||
// Если API ключ недоступен, используем fallback
|
// Если API ключ недоступен, используем fallback
|
||||||
if (!process.env.OPENROUTER_API_KEY) {
|
if (!process.env.OPENROUTER_API_KEY) {
|
||||||
console.log('⚠️ OpenRouter API ключ недоступен, используем базовую суммаризацию');
|
logger.warn('⚠️ OpenRouter API ключ недоступен, используем базовую суммаризацию');
|
||||||
return this.generateFallbackSummary(data);
|
return this.generateFallbackSummary(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formattedMessages = this.formatMessagesForAI(data);
|
const formattedMessages = this.formatMessagesForAI(data);
|
||||||
console.log('🤖 Отправляем запрос к OpenRouter...');
|
logger.info('🤖 Отправляем запрос к OpenRouter...');
|
||||||
|
|
||||||
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -460,7 +461,8 @@ ${conversationFlow}`;
|
|||||||
'X-Title': 'Telegram History Bot'
|
'X-Title': 'Telegram History Bot'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: 'google/gemini-2.5-flash-preview-05-20',
|
model: process.env.OPENROUTER_MODEL,
|
||||||
|
// model: 'google/gemini-2.5-flash-preview-05-20',
|
||||||
// model: 'google/gemini-2.0-flash-exp:free',
|
// model: 'google/gemini-2.0-flash-exp:free',
|
||||||
// model: 'deepseek/deepseek-chat-v3-0324:free',
|
// model: 'deepseek/deepseek-chat-v3-0324:free',
|
||||||
messages: [
|
messages: [
|
||||||
@ -491,21 +493,21 @@ ${conversationFlow}`;
|
|||||||
throw new Error('Пустой ответ от ИИ');
|
throw new Error('Пустой ответ от ИИ');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Получен ответ от OpenRouter');
|
logger.info('✅ Получен ответ от OpenRouter');
|
||||||
return `🎬 <b>История чата</b>\n\n${aiSummary}`;
|
return `🎬 <b>История чата</b>\n\n${aiSummary}`;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка при вызове OpenRouter API:', error);
|
logger.error('❌ Ошибка при вызове OpenRouter API:', error);
|
||||||
|
|
||||||
// Детальная обработка различных типов ошибок
|
// Детальная обработка различных типов ошибок
|
||||||
if (error.message.includes('401')) {
|
if (error.message.includes('401')) {
|
||||||
console.error('❌ Неверный API ключ OpenRouter');
|
logger.error('❌ Неверный API ключ OpenRouter');
|
||||||
} else if (error.message.includes('429')) {
|
} else if (error.message.includes('429')) {
|
||||||
console.error('❌ Превышен лимит запросов OpenRouter');
|
logger.error('❌ Превышен лимит запросов OpenRouter');
|
||||||
} else if (error.message.includes('402')) {
|
} else if (error.message.includes('402')) {
|
||||||
console.error('❌ Недостаточно средств на счету OpenRouter');
|
logger.error('❌ Недостаточно средств на счету OpenRouter');
|
||||||
} else if (error.code === 'ENOTFOUND') {
|
} else if (error.code === 'ENOTFOUND') {
|
||||||
console.error('❌ Проблемы с сетевым подключением');
|
logger.error('❌ Проблемы с сетевым подключением');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.generateFallbackSummary(data);
|
return this.generateFallbackSummary(data);
|
||||||
@ -558,7 +560,7 @@ ${conversationFlow}`;
|
|||||||
const BOT_TOKEN = process.env.BOT_TOKEN;
|
const BOT_TOKEN = process.env.BOT_TOKEN;
|
||||||
|
|
||||||
if (!BOT_TOKEN) {
|
if (!BOT_TOKEN) {
|
||||||
console.error('❌ Укажите токен бота в переменной окружения BOT_TOKEN');
|
logger.error('❌ Укажите токен бота в переменной окружения BOT_TOKEN');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
logger.js
Normal file
42
logger.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const winston = require('winston');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// __dirname уже доступен в CommonJS, нет нужды использовать fileURLToPath
|
||||||
|
|
||||||
|
// Формат для файла (подробный JSON)
|
||||||
|
const fileFormat = winston.format.combine(
|
||||||
|
winston.format.timestamp(),
|
||||||
|
winston.format.json()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Формат для консоли (краткий, с цветами)
|
||||||
|
const consoleFormat = winston.format.combine(
|
||||||
|
winston.format.colorize(),
|
||||||
|
winston.format.timestamp({ format: 'HH:mm:ss' }),
|
||||||
|
winston.format.printf(({ level, message, timestamp }) => {
|
||||||
|
return `${timestamp} ${level}: ${message}`;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const logger = winston.createLogger({
|
||||||
|
level: 'info',
|
||||||
|
transports: [
|
||||||
|
// Логирование ошибок в отдельный файл
|
||||||
|
new winston.transports.File({
|
||||||
|
filename: path.join(__dirname, '../logs/error.log'),
|
||||||
|
level: 'error',
|
||||||
|
format: fileFormat
|
||||||
|
}),
|
||||||
|
// Общий лог файл
|
||||||
|
new winston.transports.File({
|
||||||
|
filename: path.join(__dirname, '../logs/combined.log'),
|
||||||
|
format: fileFormat
|
||||||
|
}),
|
||||||
|
// Консольный вывод
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: consoleFormat
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = logger;
|
||||||
@ -11,7 +11,8 @@
|
|||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"openrouter-kit": "^0.1.65",
|
"openrouter-kit": "^0.1.65",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"telegraf": "^4.12.2"
|
"telegraf": "^4.12.2",
|
||||||
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.0.1"
|
"nodemon": "^3.0.1"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user