const axios = require('axios'); class TelegramService { constructor() { this.botToken = process.env.TELEGRAM_BOT_TOKEN; this.chatId = process.env.TELEGRAM_CHAT_ID; this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; this.isEnabled = !!(this.botToken && this.chatId); this.chats = new Map(); // Store chat information this.botInfo = null; // Store bot information } // Update bot token and reinitialize updateBotToken(newToken) { this.botToken = newToken; this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; this.isEnabled = !!(this.botToken && this.chatId); this.botInfo = null; // Reset bot info return this.testConnection(); } // Update default chat ID updateChatId(newChatId) { this.chatId = newChatId; this.isEnabled = !!(this.botToken && this.chatId); } // Get bot information async getBotInfo() { if (this.botInfo) return { success: true, bot: this.botInfo }; if (!this.botToken) { return { success: false, message: 'Bot token not configured' }; } try { const response = await axios.get(`${this.baseUrl}/getMe`); this.botInfo = response.data.result; return { success: true, bot: this.botInfo }; } catch (error) { console.error('Get bot info error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Get updates (for getting chat IDs and managing chats) async getUpdates() { if (!this.botToken) { return { success: false, message: 'Bot token not configured' }; } try { const response = await axios.get(`${this.baseUrl}/getUpdates`); const updates = response.data.result; // Extract unique chats const chats = new Map(); updates.forEach(update => { if (update.message) { const chat = update.message.chat; chats.set(chat.id, { id: chat.id, type: chat.type, title: chat.title || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), username: chat.username || null, first_name: chat.first_name || null, last_name: chat.last_name || null, description: chat.description || null, invite_link: chat.invite_link || null, pinned_message: chat.pinned_message || null, permissions: chat.permissions || null, slow_mode_delay: chat.slow_mode_delay || null }); } }); // Update internal chat storage chats.forEach((chat, id) => { this.chats.set(id, chat); }); return { success: true, updates, chats: Array.from(chats.values()), totalUpdates: updates.length }; } catch (error) { console.error('Get updates error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Get chat information async getChat(chatId) { if (!this.botToken) { return { success: false, message: 'Bot token not configured' }; } try { const response = await axios.get(`${this.baseUrl}/getChat`, { params: { chat_id: chatId } }); const chat = response.data.result; // Store in local cache this.chats.set(chatId, chat); return { success: true, chat }; } catch (error) { console.error('Get chat error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Get chat administrators async getChatAdministrators(chatId) { if (!this.botToken) { return { success: false, message: 'Bot token not configured' }; } try { const response = await axios.get(`${this.baseUrl}/getChatAdministrators`, { params: { chat_id: chatId } }); return { success: true, administrators: response.data.result }; } catch (error) { console.error('Get chat administrators error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Get available chats (cached) getAvailableChats() { return Array.from(this.chats.values()).map(chat => ({ id: chat.id, title: chat.title || chat.username || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), type: chat.type, username: chat.username })); } async sendMessage(text, options = {}) { const chatId = options.chat_id || this.chatId; if (!this.botToken) { console.warn('Telegram bot token is not configured'); return { success: false, message: 'Telegram bot token not configured' }; } if (!chatId) { console.warn('Telegram chat ID is not specified'); return { success: false, message: 'Telegram chat ID not specified' }; } try { const response = await axios.post(`${this.baseUrl}/sendMessage`, { chat_id: chatId, text: text, parse_mode: 'HTML', disable_web_page_preview: false, disable_notification: false, ...options }); return { success: true, data: response.data }; } catch (error) { console.error('Telegram send message error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Send message to multiple chats async sendBroadcastMessage(text, chatIds = [], options = {}) { if (!this.botToken) { return { success: false, message: 'Telegram bot token not configured' }; } if (!chatIds || chatIds.length === 0) { return { success: false, message: 'No chat IDs specified' }; } const results = []; const errors = []; for (const chatId of chatIds) { try { const result = await this.sendMessage(text, { ...options, chat_id: chatId }); if (result.success) { results.push({ chatId, success: true, messageId: result.data.result.message_id }); } else { errors.push({ chatId, error: result.error || result.message }); } // Add delay between messages to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { errors.push({ chatId, error: error.message }); } } return { success: errors.length === 0, results, errors, totalSent: results.length, totalFailed: errors.length }; } // Send custom message with advanced options async sendCustomMessage({ text, chatIds = [], parseMode = 'HTML', disableWebPagePreview = false, disableNotification = false, replyMarkup = null }) { const targetChats = chatIds.length > 0 ? chatIds : [this.chatId]; return await this.sendBroadcastMessage(text, targetChats, { parse_mode: parseMode, disable_web_page_preview: disableWebPagePreview, disable_notification: disableNotification, reply_markup: replyMarkup }); } async sendContactNotification(contact) { const message = this.formatContactMessage(contact); return await this.sendMessage(message); } async sendNewContactAlert(contact) { const message = `🔔 Новый запрос с сайта!\n\n` + `👤 Клиент: ${contact.name}\n` + `📧 Email: ${contact.email}\n` + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + `💬 Сообщение:\n${contact.message}\n\n` + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + `🔗 Открыть в админ-панели`; return await this.sendMessage(message); } async sendPortfolioNotification(portfolio) { const message = `📁 Новый проект добавлен в портфолио\n\n` + `🏷️ Название: ${portfolio.title}\n` + `📂 Категория: ${portfolio.category}\n` + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + `🔗 Посмотреть проект`; return await this.sendMessage(message); } async sendServiceNotification(service) { const message = `⚙️ Новая услуга добавлена\n\n` + `🏷️ Название: ${service.name}\n` + `📂 Категория: ${service.category}\n` + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + `🔗 Посмотреть услуги`; return await this.sendMessage(message); } async sendCalculatorQuote(calculatorData) { const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; const message = `💰 Новый расчет стоимости\n\n` + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + `💵 Общая стоимость: $${totalCost}\n\n` + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; return await this.sendMessage(message); } formatContactMessage(contact) { return `📞 Уведомление о контакте\n\n` + `👤 Клиент: ${contact.name}\n` + `📧 Email: ${contact.email}\n` + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + `📊 Статус: ${this.getStatusText(contact.status)}\n` + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + `💬 Сообщение:\n${contact.message}\n\n` + `🔗 Открыть в админ-панели`; } formatServices(services) { if (!services || services.length === 0) return 'Не выбрано'; return services.map(service => `• ${service.name} - $${service.price || 0}` ).join('\n'); } getStatusText(status) { const statusMap = { 'new': '🆕 Новое', 'in_progress': '⏳ В работе', 'completed': '✅ Завершено' }; return statusMap[status] || status; } getPriorityText(priority) { const priorityMap = { 'low': '🟢 Низкий', 'medium': '🟡 Средний', 'high': '🔴 Высокий' }; return priorityMap[priority] || priority; } // Test connection and update bot info async testConnection() { if (!this.botToken) { return { success: false, message: 'Telegram bot token not configured' }; } try { const response = await axios.get(`${this.baseUrl}/getMe`); this.botInfo = response.data.result; // Also get updates to discover available chats await this.getUpdates(); return { success: true, bot: this.botInfo, availableChats: this.getAvailableChats() }; } catch (error) { console.error('Telegram connection test error:', error.response?.data || error.message); return { success: false, error: error.message }; } } // Webhook setup (for future use) async setWebhook(webhookUrl) { if (!this.isEnabled) { return { success: false, message: 'Telegram bot not configured' }; } try { const response = await axios.post(`${this.baseUrl}/setWebhook`, { url: webhookUrl }); return { success: true, data: response.data }; } catch (error) { console.error('Telegram webhook setup error:', error.response?.data || error.message); return { success: false, error: error.message }; } } } module.exports = new TelegramService();