diff --git a/bot.js b/bot.js index 1e2bb3f..abd9e9e 100644 --- a/bot.js +++ b/bot.js @@ -3,6 +3,7 @@ const fs = require('fs').promises; const path = require('path'); const { OpenRouterClient } = require('openrouter-kit'); const { getPrompt } = require('./prompts.js'); +const logger = require('./logger.js'); require('dotenv').config(); const char = {name:'marina'} class TelegramHistoryBot { @@ -13,7 +14,7 @@ class TelegramHistoryBot { // Инициализация OpenRouter с проверкой ключа if (!process.env.OPENROUTER_API_KEY) { - console.warn('⚠️ OPENROUTER_API_KEY не найден, ИИ-суммаризация будет недоступна'); + logger.warn('⚠️ OPENROUTER_API_KEY не найден, ИИ-суммаризация будет недоступна'); this.openRouter = null; } else { this.openRouter = new OpenRouterClient({ @@ -37,9 +38,9 @@ class TelegramHistoryBot { try { await this.loadHistory(); this.setupHandlers(); - console.log('✅ Bot запущен и готов к работе'); + logger.info('✅ Bot запущен и готов к работе'); } catch (error) { - console.error('❌ Ошибка инициализации бота:', error); + logger.error('❌ Ошибка инициализации бота:', error); process.exit(1); } } @@ -48,9 +49,9 @@ class TelegramHistoryBot { try { const data = await fs.readFile(this.historyFile, 'utf8'); this.history = JSON.parse(data); - console.log(`📚 Загружено ${this.history.length} сообщений из истории`); + logger.info(`📚 Загружено ${this.history.length} сообщений из истории`); } catch (error) { - console.log('📝 История не найдена, создаем новую'); + logger.warn('📝 История не найдена, создаем новую'); this.history = []; await this.saveHistory(); } @@ -60,7 +61,7 @@ class TelegramHistoryBot { try { await fs.writeFile(this.historyFile, JSON.stringify(this.history, null, 2)); } catch (error) { - console.error('❌ Ошибка сохранения истории:', error); + logger.error('❌ Ошибка сохранения истории:', error); } } @@ -72,7 +73,7 @@ class TelegramHistoryBot { // await ctx.deleteMessage // return // } - console.log('📊 Получена команда summary_day'); + logger.info('📊 Получена команда summary_day'); const args = ctx.message.text.split(' '); if (args.length > 1) { char.name=args[1] @@ -82,7 +83,7 @@ class TelegramHistoryBot { }); this.bot.command('summary_hours', async (ctx) => { - console.log('📊 Получена команда summary_hours'); + logger.info('📊 Получена команда summary_hours'); const args = ctx.message.text.split(' '); if (args.length < 2 || isNaN(parseInt(args[1]))) { await ctx.reply('❗ Укажите количество часов: /summary_hours 6'); @@ -94,7 +95,7 @@ class TelegramHistoryBot { }); this.bot.command('summary_last', async (ctx) => { - console.log('📊 Получена команда summary_last'); + logger.info('📊 Получена команда summary_last'); const args = ctx.message.text.split(' '); if (args.length < 2 || isNaN(parseInt(args[1]))) { await ctx.reply('❗ Укажите количество сообщений: /summary_last 50'); @@ -120,25 +121,25 @@ class TelegramHistoryBot { // Обработка ошибок this.bot.catch(async (err, ctx) => { - console.error('❌ Ошибка бота:', err); + logger.error('❌ Ошибка бота:', err); try { await ctx.reply('❌ Произошла ошибка при обработке команды'); } catch (replyError) { - console.error('❌ Не удалось отправить сообщение об ошибке:', replyError); + logger.error('❌ Не удалось отправить сообщение об ошибке:', replyError); } }); // Запуск бота this.bot.launch(); - console.log('🚀 Бот запущен'); + logger.info('🚀 Бот запущен'); // Graceful stop process.once('SIGINT', () => { - console.log('🛑 Получен сигнал SIGINT, останавливаем бота...'); + logger.warn('🛑 Получен сигнал SIGINT, останавливаем бота...'); this.bot.stop('SIGINT'); }); process.once('SIGTERM', () => { - console.log('🛑 Получен сигнал SIGTERM, останавливаем бота...'); + logger.warn('🛑 Получен сигнал SIGTERM, останавливаем бота...'); this.bot.stop('SIGTERM'); }); } @@ -169,9 +170,9 @@ class TelegramHistoryBot { const userName = messageData.first_name || messageData.username || 'Unknown'; 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) { - console.error('❌ Ошибка при обработке сообщения:', error); + logger.error('❌ Ошибка при обработке сообщения:', error); } } @@ -220,7 +221,7 @@ class TelegramHistoryBot { } async handleSummaryCommand(ctx, type, value) { - console.log(`📊 Обработка команды суммаризации: ${type}${value ? ` (${value})` : ''}`); + logger.info(`📊 Обработка команды суммаризации: ${type}${value ? ` (${value})` : ''}`); const chatId = ctx.chat.id; let messages = []; @@ -250,7 +251,7 @@ class TelegramHistoryBot { break; } - console.log(`📈 Найдено ${messages.length} сообщений для суммаризации`); + logger.info(`📈 Найдено ${messages.length} сообщений для суммаризации`); if (messages.length === 0) { await ctx.reply('❗ Сообщения для суммаризации не найдены'); @@ -267,10 +268,10 @@ class TelegramHistoryBot { // Отправляем результат await ctx.reply(summary, { parse_mode: 'HTML' }); - console.log('✅ Суммаризация отправлена'); + logger.info('✅ Суммаризация отправлена'); } catch (error) { - console.error('❌ Ошибка при создании суммаризации:', error); + logger.error('❌ Ошибка при создании суммаризации:', error); await ctx.reply('❌ Произошла ошибка при создании суммаризации. Попробуйте позже.'); } } @@ -388,7 +389,7 @@ class TelegramHistoryBot { createSummaryPrompt(type, value) { const timeDescription = this.getTimeDescription(type, value); - console.log('Выбранный персонаж: ', char) + logger.info('Выбранный персонаж: ', char) return `Ты получишь данные чата Telegram ${timeDescription}. ` + getPrompt(char.name) @@ -443,13 +444,13 @@ ${conversationFlow}`; async callAISummarization(prompt, data) { // Если API ключ недоступен, используем fallback if (!process.env.OPENROUTER_API_KEY) { - console.log('⚠️ OpenRouter API ключ недоступен, используем базовую суммаризацию'); + logger.warn('⚠️ OpenRouter API ключ недоступен, используем базовую суммаризацию'); return this.generateFallbackSummary(data); } try { const formattedMessages = this.formatMessagesForAI(data); - console.log('🤖 Отправляем запрос к OpenRouter...'); + logger.info('🤖 Отправляем запрос к OpenRouter...'); const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', @@ -491,21 +492,21 @@ ${conversationFlow}`; throw new Error('Пустой ответ от ИИ'); } - console.log('✅ Получен ответ от OpenRouter'); + logger.info('✅ Получен ответ от OpenRouter'); return `🎬 История чата\n\n${aiSummary}`; } catch (error) { - console.error('❌ Ошибка при вызове OpenRouter API:', error); + logger.error('❌ Ошибка при вызове OpenRouter API:', error); // Детальная обработка различных типов ошибок if (error.message.includes('401')) { - console.error('❌ Неверный API ключ OpenRouter'); + logger.error('❌ Неверный API ключ OpenRouter'); } else if (error.message.includes('429')) { - console.error('❌ Превышен лимит запросов OpenRouter'); + logger.error('❌ Превышен лимит запросов OpenRouter'); } else if (error.message.includes('402')) { - console.error('❌ Недостаточно средств на счету OpenRouter'); + logger.error('❌ Недостаточно средств на счету OpenRouter'); } else if (error.code === 'ENOTFOUND') { - console.error('❌ Проблемы с сетевым подключением'); + logger.error('❌ Проблемы с сетевым подключением'); } return this.generateFallbackSummary(data); @@ -558,7 +559,7 @@ ${conversationFlow}`; const BOT_TOKEN = process.env.BOT_TOKEN; if (!BOT_TOKEN) { - console.error('❌ Укажите токен бота в переменной окружения BOT_TOKEN'); + logger.error('❌ Укажите токен бота в переменной окружения BOT_TOKEN'); process.exit(1); } diff --git a/logger.js b/logger.js new file mode 100644 index 0000000..1fd51d3 --- /dev/null +++ b/logger.js @@ -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; diff --git a/package.json b/package.json index 16df766..d7ee4f9 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "dotenv": "^16.5.0", "openrouter-kit": "^0.1.65", "process": "^0.11.10", - "telegraf": "^4.12.2" + "telegraf": "^4.12.2", + "winston": "^3.17.0" }, "devDependencies": { "nodemon": "^3.0.1"