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();