import TelegramBot, { InlineKeyboardMarkup } from 'node-telegram-bot-api'; import { ChatService } from '../services/chatService'; import { ProfileService } from '../services/profileService'; import { NotificationService } from '../services/notificationService'; export class EnhancedChatHandlers { private bot: TelegramBot; private chatService: ChatService; private profileService: ProfileService; private notificationService: NotificationService; constructor(bot: TelegramBot) { this.bot = bot; this.profileService = new ProfileService(); this.notificationService = new NotificationService(bot); this.chatService = new ChatService(this.notificationService); } // ===== НАТИВНЫЙ ИНТЕРФЕЙС ЧАТОВ ===== // Показать список чатов в более простом формате async showChatsNative(chatId: number, telegramId: string): Promise { try { const chats = await this.chatService.getUserChats(telegramId); if (chats.length === 0) { await this.bot.sendMessage( chatId, '💬 *Чаты*\n\n' + '😔 У вас пока нет активных чатов\n\n' + '💡 Ставьте лайки, чтобы найти матчи и начать общение!', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [{ text: '🔍 Найти людей', callback_data: 'start_browsing' }], [{ text: '🏠 Главное меню', callback_data: 'main_menu' }] ] } } ); return; } // Создаем простые кнопки для каждого чата const chatButtons: any[][] = []; for (const chat of chats.slice(0, 10)) { // Показываем максимум 10 чатов const unreadBadge = chat.unreadCount > 0 ? ` 🔴${chat.unreadCount}` : ''; const lastMessagePreview = this.getMessagePreview(chat.lastMessage); chatButtons.push([{ text: `💬 ${chat.otherUserName}${unreadBadge}`, callback_data: `open_native_chat_${chat.matchId}` }]); } // Добавляем кнопки управления chatButtons.push([ { text: '🔍 Найти людей', callback_data: 'start_browsing' }, { text: '🏠 Главное меню', callback_data: 'main_menu' } ]); await this.bot.sendMessage( chatId, `💬 *Ваши чаты* (${chats.length})\n\n` + '👆 Выберите чат для общения:', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: chatButtons } } ); } catch (error) { console.error('Error showing native chats:', error); await this.bot.sendMessage(chatId, '❌ Ошибка при загрузке чатов'); } } // Открыть чат в нативном формате async openNativeChat(chatId: number, telegramId: string, matchId: string): Promise { try { const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo) { await this.bot.sendMessage(chatId, '❌ Чат не найден'); return; } // Отмечаем сообщения как прочитанные await this.chatService.markMessagesAsRead(matchId, telegramId); // Получаем последние сообщения const messages = await this.chatService.getChatMessages(matchId, 20); const otherUserName = matchInfo.otherUserProfile?.name || 'Пользователь'; if (messages.length === 0) { // Первое сообщение в чате await this.bot.sendMessage( chatId, `💕 *Новый матч с ${otherUserName}!*\n\n` + '🎉 Поздравляем! Вы понравились друг другу!\n\n' + '💬 *Как начать общение?*\n' + '• Просто напишите сообщение в этот чат\n' + '• Ваше сообщение будет доставлено собеседнику\n' + '• Он получит уведомление о новом сообщении\n\n' + '💡 Напишите что-нибудь интересное!', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [{ text: '👤 Профиль собеседника', callback_data: `view_chat_profile_${matchId}` }], [{ text: '← Назад к чатам', callback_data: 'native_chats' }] ] } } ); } else { // Показываем историю сообщений let chatHistory = `💬 *Чат с ${otherUserName}*\n\n`; const currentUserId = await this.profileService.getUserIdByTelegramId(telegramId); // Показываем последние 10 сообщений for (const message of messages.slice(-10)) { const isFromMe = message.senderId === currentUserId; const senderIcon = isFromMe ? '✅' : '💌'; const time = message.createdAt.toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); chatHistory += `${senderIcon} *${time}*\n`; chatHistory += `${this.escapeMarkdown(message.content)}\n\n`; } chatHistory += '💡 *Напишите сообщение в этот чат для ответа*'; await this.bot.sendMessage( chatId, chatHistory, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: '👤 Профиль', callback_data: `view_chat_profile_${matchId}` }, { text: '📜 История', callback_data: `chat_history_${matchId}` } ], [{ text: '💔 Удалить матч', callback_data: `confirm_unmatch_${matchId}` }], [{ text: '← Назад к чатам', callback_data: 'native_chats' }] ] } } ); } // Устанавливаем контекст чата для пользователя this.setUserChatContext(telegramId, matchId); } catch (error) { console.error('Error opening native chat:', error); await this.bot.sendMessage(chatId, '❌ Ошибка при открытии чата'); } } // ===== СИСТЕМА УВЕДОМЛЕНИЙ ===== // Отправить уведомление о новом сообщении - теперь используем NotificationService async sendMessageNotification(receiverTelegramId: string, senderName: string, messagePreview: string, matchId: string): Promise { try { // Получаем идентификаторы пользователей для использования в NotificationService const receiverUserId = await this.profileService.getUserIdByTelegramId(receiverTelegramId); const sender = await this.chatService.getMatchInfo(matchId, receiverTelegramId); if (!receiverUserId || !sender?.otherUserId) { console.error('Failed to get user IDs for notification'); return; } // Используем сервис уведомлений для отправки более красивого уведомления await this.notificationService.sendMessageNotification( receiverUserId, sender.otherUserId, messagePreview, matchId ); } catch (error) { console.error('Error sending message notification:', error); } } // ===== ОБРАБОТКА СООБЩЕНИЙ ===== // Обработать входящее сообщение для чата async handleIncomingChatMessage(msg: any, telegramId: string): Promise { try { const currentChatContext = this.getUserChatContext(telegramId); if (!currentChatContext) { return false; // Пользователь не в контексте чата } const { matchId } = currentChatContext; // Проверяем, что матч еще активен const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo) { await this.bot.sendMessage(msg.chat.id, '❌ Этот чат больше недоступен'); this.clearUserChatContext(telegramId); return true; } // Сохраняем сообщение const messageId = await this.chatService.sendMessage( matchId, telegramId, msg.photo ? (msg.caption || '[Фото]') + ' [file_id: ' + msg.photo[msg.photo.length - 1].file_id + ']' : (msg.text || '[Медиа]'), msg.photo ? 'photo' : 'text' ); if (messageId) { // Подтверждение отправки await this.bot.sendMessage( msg.chat.id, '✅ Сообщение отправлено!', { reply_markup: { inline_keyboard: [ [{ text: '💬 Продолжить чат', callback_data: `open_native_chat_${matchId}` }], [{ text: '📱 Все чаты', callback_data: 'native_chats' }] ] } } ); // Отправляем уведомление получателю const senderProfile = await this.profileService.getProfileByTelegramId(telegramId); const receiverTelegramId = await this.profileService.getTelegramIdByUserId( matchInfo.otherUserProfile?.userId || '' ); if (senderProfile && receiverTelegramId) { const messagePreview = this.getMessagePreview(msg.text || '[Медиа]'); await this.sendMessageNotification( receiverTelegramId, senderProfile.name, messagePreview, matchId ); } } else { await this.bot.sendMessage(msg.chat.id, '❌ Ошибка при отправке сообщения'); } return true; } catch (error) { console.error('Error handling incoming chat message:', error); await this.bot.sendMessage(msg.chat.id, '❌ Ошибка при обработке сообщения'); return true; } } // ===== КОНТЕКСТ ЧАТОВ ===== private userChatContexts: Map = new Map(); private setUserChatContext(telegramId: string, matchId: string): void { this.userChatContexts.set(telegramId, { matchId, timestamp: Date.now() }); // Автоматически очищаем контекст через 30 минут setTimeout(() => { const current = this.userChatContexts.get(telegramId); if (current && current.matchId === matchId) { this.userChatContexts.delete(telegramId); } }, 30 * 60 * 1000); } private getUserChatContext(telegramId: string): { matchId: string } | null { const context = this.userChatContexts.get(telegramId); if (!context) return null; // Проверяем, что контекст не старше 30 минут if (Date.now() - context.timestamp > 30 * 60 * 1000) { this.userChatContexts.delete(telegramId); return null; } return { matchId: context.matchId }; } private clearUserChatContext(telegramId: string): void { this.userChatContexts.delete(telegramId); } // ===== ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ===== private getMessagePreview(message: string | null): string { if (!message) return '[Пустое сообщение]'; return message.length > 50 ? message.substring(0, 50) + '...' : message; } private escapeMarkdown(text: string): string { return text.replace(/[_*[\]()~`>#+=|{}.!-]/g, '\\$&'); } // Показать расширенную историю чата async showChatHistory(chatId: number, telegramId: string, matchId: string): Promise { try { const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo) { await this.bot.sendMessage(chatId, '❌ Чат не найден'); return; } const messages = await this.chatService.getChatMessages(matchId, 50); const otherUserName = matchInfo.otherUserProfile?.name || 'Пользователь'; const currentUserId = await this.profileService.getUserIdByTelegramId(telegramId); if (messages.length === 0) { await this.bot.sendMessage( chatId, `📜 *История с ${otherUserName}*\n\n` + 'История сообщений пуста', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [{ text: '← Назад к чату', callback_data: `open_native_chat_${matchId}` }] ] } } ); return; } // Разбиваем сообщения на части, если их много const messagesPerPage = 20; const totalPages = Math.ceil(messages.length / messagesPerPage); const currentPage = 1; // Пока показываем только первую страницу const startIndex = (currentPage - 1) * messagesPerPage; const endIndex = startIndex + messagesPerPage; const pageMessages = messages.slice(startIndex, endIndex); let historyText = `📜 *История с ${otherUserName}*\n`; historyText += `📄 Страница ${currentPage} из ${totalPages}\n\n`; for (const message of pageMessages) { const isFromMe = message.senderId === currentUserId; const senderIcon = isFromMe ? '✅' : '💌'; const senderName = isFromMe ? 'Вы' : otherUserName; const time = message.createdAt.toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); historyText += `${senderIcon} **${senderName}** _(${time})_\n`; historyText += `${this.escapeMarkdown(message.content)}\n\n`; } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '← Назад к чату', callback_data: `open_native_chat_${matchId}` }] ] }; await this.bot.sendMessage(chatId, historyText, { parse_mode: 'Markdown', reply_markup: keyboard }); } catch (error) { console.error('Error showing chat history:', error); await this.bot.sendMessage(chatId, '❌ Ошибка при загрузке истории'); } } }