Files
tg_tinder_bot/src/handlers/enhancedChatHandlers.ts
2025-11-06 15:09:15 +09:00

390 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void> {
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<void> {
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<void> {
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<boolean> {
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<string, { matchId: string; timestamp: number }> = 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<void> {
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, '❌ Ошибка при загрузке истории');
}
}
}