import TelegramBot, { CallbackQuery, InlineKeyboardMarkup } from 'node-telegram-bot-api'; import { ProfileService } from '../services/profileService'; import { MatchingService } from '../services/matchingService'; import { ChatService } from '../services/chatService'; import { Profile } from '../models/Profile'; import { MessageHandlers } from './messageHandlers'; import { NotificationHandlers } from './notificationHandlers'; import { ProfileEditController } from '../controllers/profileEditController'; import { EnhancedChatHandlers } from './enhancedChatHandlers'; import { VipController } from '../controllers/vipController'; import { VipService } from '../services/vipService'; import { TranslationController } from '../controllers/translationController'; import { t } from '../services/localizationService'; import { LikeBackHandler } from './likeBackHandler'; export class CallbackHandlers { private bot: TelegramBot; private profileService: ProfileService; private matchingService: MatchingService; private chatService: ChatService; private messageHandlers: MessageHandlers; private profileEditController: ProfileEditController; private enhancedChatHandlers: EnhancedChatHandlers; private vipController: VipController; private vipService: VipService; private translationController: TranslationController; private notificationHandlers?: NotificationHandlers; private likeBackHandler: LikeBackHandler; constructor(bot: TelegramBot, messageHandlers: MessageHandlers) { this.bot = bot; this.profileService = new ProfileService(); this.matchingService = new MatchingService(); // Получаем notificationService из messageHandlers (если есть) const notificationService = (messageHandlers as any).notificationService; this.chatService = new ChatService(notificationService); this.messageHandlers = messageHandlers; this.profileEditController = new ProfileEditController(this.profileService); this.enhancedChatHandlers = new EnhancedChatHandlers(bot); this.vipController = new VipController(bot); this.vipService = new VipService(); this.translationController = new TranslationController(); // Создаем экземпляр NotificationHandlers try { this.notificationHandlers = new NotificationHandlers(bot); } catch (error) { console.error('Failed to initialize NotificationHandlers:', error); } this.likeBackHandler = new LikeBackHandler(bot); } // Вспомогательный метод для получения перевода с учетом языка пользователя private async getTranslation(userId: string, key: string, options?: any): Promise { try { const lang = await this.profileService.getUserLanguage(userId); const LocalizationService = require('../services/localizationService').default; const locService = LocalizationService.getInstance(); locService.setLanguage(lang); return locService.t(key, options); } catch (error) { console.error('Translation error:', error); // Возвращаем ключ как fallback return key; } } register(): void { this.bot.on('callback_query', (query) => this.handleCallback(query)); } async handleCallback(query: CallbackQuery): Promise { if (!query.data || !query.from || !query.message) return; const telegramId = query.from.id.toString(); const chatId = query.message.chat.id; const data = query.data; try { // Обработка выбора языка if (data.startsWith('set_lang_')) { const LanguageHandlers = require('./languageHandlers').LanguageHandlers; const languageHandlers = new LanguageHandlers(this.bot); await languageHandlers.handleSetLanguage(query); return; } // Основные действия профиля if (data === 'create_profile') { await this.handleCreateProfile(chatId, telegramId); } else if (data.startsWith('gender_')) { const gender = data.replace('gender_', ''); await this.handleGenderSelection(chatId, telegramId, gender); } else if (data === 'view_my_profile') { await this.handleViewMyProfile(chatId, telegramId); } else if (data === 'edit_profile') { await this.handleEditProfile(chatId, telegramId); } else if (data === 'manage_photos') { await this.handleManagePhotos(chatId, telegramId); } else if (data === 'preview_profile') { await this.handlePreviewProfile(chatId, telegramId); } // Редактирование полей профиля else if (data === 'edit_name') { await this.handleEditName(chatId, telegramId); } else if (data === 'edit_age') { await this.handleEditAge(chatId, telegramId); } else if (data === 'edit_bio') { await this.handleEditBio(chatId, telegramId); } else if (data === 'edit_hobbies') { await this.handleEditHobbies(chatId, telegramId); } else if (data === 'edit_city') { await this.handleEditCity(chatId, telegramId); } else if (data === 'confirm_city') { try { const states = (this.messageHandlers as any).userStates as Map; const userState = states ? states.get(telegramId) : null; if (userState && userState.step === 'confirm_city') { // Подтверждаем город и переводим пользователя к вводу био userState.step = 'waiting_bio'; console.log(`User ${telegramId} confirmed city: ${userState.data.city}`); // Убираем inline-кнопки из сообщения с подтверждением try { // clear inline keyboard await this.bot.editMessageReplyMarkup({ inline_keyboard: [] } as any, { chat_id: chatId, message_id: query.message?.message_id }); } catch (e) { // ignore } await this.bot.sendMessage(chatId, `✅ Город подтверждён: *${userState.data.city}*\n\n📝 Теперь расскажите немного о себе (био):`, { parse_mode: 'Markdown' }); } else { const errorText = await this.getTranslation(telegramId, 'errors.contextNotFound'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } catch (error) { console.error('Error confirming city via callback:', error); const errorText = await this.getTranslation(telegramId, 'errors.cityConfirmError'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } else if (data === 'edit_city_manual') { try { const states = (this.messageHandlers as any).userStates as Map; const userState = states ? states.get(telegramId) : null; if (userState) { userState.step = 'waiting_city'; console.log(`User ${telegramId} chose to enter city manually`); try { // clear inline keyboard await this.bot.editMessageReplyMarkup({ inline_keyboard: [] } as any, { chat_id: chatId, message_id: query.message?.message_id }); } catch (e) { } await this.bot.sendMessage(chatId, '✏️ Введите название вашего города вручную:', { reply_markup: { remove_keyboard: true } }); } else { const errorText = await this.getTranslation(telegramId, 'errors.contextNotFound'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } catch (error) { console.error('Error switching to manual city input via callback:', error); const errorText = await this.getTranslation(telegramId, 'errors.generalError'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } else if (data === 'confirm_city_edit') { try { const editState = this.messageHandlers.profileEditStates.get(telegramId); if (editState && editState.field === 'city' && editState.tempCity) { console.log(`User ${telegramId} confirmed city edit: ${editState.tempCity}`); // Обновляем город в профиле await this.messageHandlers.updateProfileField(telegramId, 'city', editState.tempCity); // Обновляем координаты, если они есть if (editState.tempLocation) { console.log(`User ${telegramId} updating location: lat=${editState.tempLocation.latitude}, lon=${editState.tempLocation.longitude}`); await this.messageHandlers.updateProfileField(telegramId, 'location', editState.tempLocation); } // Очищаем состояние this.messageHandlers.clearProfileEditState(telegramId); // Убираем inline-кнопки try { await this.bot.editMessageReplyMarkup({ inline_keyboard: [] } as any, { chat_id: chatId, message_id: query.message?.message_id }); } catch (e) { } await this.bot.sendMessage(chatId, '✅ Город успешно обновлён!'); setTimeout(async () => { const keyboard = { inline_keyboard: [ [ { text: '✏️ Продолжить редактирование', callback_data: 'edit_profile' }, { text: '👀 Предпросмотр', callback_data: 'preview_profile' } ], [{ text: '🏠 Главное меню', callback_data: 'main_menu' }] ] }; const selectActionText = await this.getTranslation(telegramId, 'buttons.selectAction'); await this.bot.sendMessage(chatId, selectActionText, { reply_markup: keyboard }); }, 500); } else { const errorText = await this.getTranslation(telegramId, 'errors.contextNotFound'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } catch (error) { console.error('Error confirming city edit via callback:', error); const errorText = await this.getTranslation(telegramId, 'errors.cityConfirmError'); await this.bot.answerCallbackQuery(query.id, { text: errorText }); } } else if (data === 'edit_city_manual_edit') { try { const editState = this.messageHandlers.profileEditStates.get(telegramId); if (editState && editState.field === 'city') { console.log(`User ${telegramId} chose to re-enter city during edit`); // Очищаем временный город, но оставляем состояние редактирования delete editState.tempCity; try { await this.bot.editMessageReplyMarkup({ inline_keyboard: [] } as any, { chat_id: chatId, message_id: query.message?.message_id }); } catch (e) { } await this.bot.sendMessage(chatId, '✏️ Введите название вашего города вручную или отправьте геолокацию:', { reply_markup: { keyboard: [ [{ text: '📍 Отправить геолокацию', request_location: true }] ], resize_keyboard: true, one_time_keyboard: true } }); } else { await this.bot.answerCallbackQuery(query.id, { text: 'Контекст не найден. Повторите, пожалуйста.' }); } } catch (error) { console.error('Error switching to re-enter city during edit via callback:', error); await this.bot.answerCallbackQuery(query.id, { text: 'Ошибка' }); } } else if (data === 'edit_job') { await this.handleEditJob(chatId, telegramId); } else if (data === 'edit_education') { await this.handleEditEducation(chatId, telegramId); } else if (data === 'edit_height') { await this.handleEditHeight(chatId, telegramId); } else if (data === 'edit_religion') { await this.handleEditReligion(chatId, telegramId); } else if (data === 'edit_dating_goal') { await this.handleEditDatingGoal(chatId, telegramId); } else if (data === 'edit_lifestyle') { await this.handleEditLifestyle(chatId, telegramId); } else if (data === 'edit_search_preferences') { await this.handleEditSearchPreferences(chatId, telegramId); } // Управление фотографиями else if (data === 'add_photo') { await this.handleAddPhoto(chatId, telegramId); } else if (data === 'delete_photo') { await this.handleDeletePhoto(chatId, telegramId); } else if (data === 'set_main_photo') { await this.handleSetMainPhoto(chatId, telegramId); } else if (data.startsWith('delete_photo_')) { const photoIndex = parseInt(data.replace('delete_photo_', '')); await this.handleDeletePhotoByIndex(chatId, telegramId, photoIndex); } else if (data.startsWith('set_main_photo_')) { const photoIndex = parseInt(data.replace('set_main_photo_', '')); await this.handleSetMainPhotoByIndex(chatId, telegramId, photoIndex); } // Цели знакомства else if (data.startsWith('set_dating_goal_')) { const goal = data.replace('set_dating_goal_', ''); await this.handleSetDatingGoal(chatId, telegramId, goal); } // Образ жизни else if (data === 'edit_smoking') { await this.handleEditSmoking(chatId, telegramId); } else if (data === 'edit_drinking') { await this.handleEditDrinking(chatId, telegramId); } else if (data === 'edit_kids') { await this.handleEditKids(chatId, telegramId); } else if (data.startsWith('set_smoking_')) { const value = data.replace('set_smoking_', ''); await this.handleSetLifestyle(chatId, telegramId, 'smoking', value); } else if (data.startsWith('set_drinking_')) { const value = data.replace('set_drinking_', ''); await this.handleSetLifestyle(chatId, telegramId, 'drinking', value); } else if (data.startsWith('set_kids_')) { const value = data.replace('set_kids_', ''); await this.handleSetLifestyle(chatId, telegramId, 'kids', value); } // Настройки поиска else if (data === 'edit_age_range') { await this.handleEditAgeRange(chatId, telegramId); } else if (data === 'edit_distance') { await this.handleEditDistance(chatId, telegramId); } // Просмотр анкет и свайпы else if (data === 'start_browsing') { await this.handleStartBrowsing(chatId, telegramId, false); } else if (data === 'start_browsing_first') { // Показываем всех пользователей для нового пользователя await this.handleStartBrowsing(chatId, telegramId, true); } else if (data === 'vip_search') { await this.handleVipSearch(chatId, telegramId); } else if (data.startsWith('search_by_goal_')) { const goal = data.replace('search_by_goal_', ''); await this.handleSearchByGoal(chatId, telegramId, goal); } else if (data === 'next_candidate') { await this.handleNextCandidate(chatId, telegramId); } else if (data.startsWith('like_')) { const targetUserId = data.replace('like_', ''); await this.handleLike(chatId, telegramId, targetUserId); } else if (data.startsWith('dislike_')) { const targetUserId = data.replace('dislike_', ''); await this.handleDislike(chatId, telegramId, targetUserId); } else if (data.startsWith('superlike_')) { const targetUserId = data.replace('superlike_', ''); await this.handleSuperlike(chatId, telegramId, targetUserId); } else if (data.startsWith('view_profile_')) { const targetUserId = data.replace('view_profile_', ''); await this.handleViewProfile(chatId, telegramId, targetUserId); } else if (data.startsWith('more_photos_')) { const targetUserId = data.replace('more_photos_', ''); await this.handleMorePhotos(chatId, telegramId, targetUserId); } // Обработка лайков и ответных лайков из уведомлений else if (data.startsWith('like_back:')) { const targetUserId = data.replace('like_back:', ''); await this.likeBackHandler.handleLikeBack(chatId, telegramId, targetUserId); } // Быстрый переход в чат из уведомлений else if (data.startsWith('open_chat:')) { const matchId = data.replace('open_chat:', ''); await this.enhancedChatHandlers.openNativeChat(chatId, telegramId, matchId); } // Матчи и чаты else if (data === 'view_matches') { await this.handleViewMatches(chatId, telegramId); } else if (data === 'open_chats') { await this.handleOpenChats(chatId, telegramId); } else if (data === 'native_chats') { await this.enhancedChatHandlers.showChatsNative(chatId, telegramId); } else if (data.startsWith('open_native_chat_')) { const matchId = data.replace('open_native_chat_', ''); await this.enhancedChatHandlers.openNativeChat(chatId, telegramId, matchId); } else if (data.startsWith('chat_history_')) { const matchId = data.replace('chat_history_', ''); await this.enhancedChatHandlers.showChatHistory(chatId, telegramId, matchId); } else if (data.startsWith('chat_')) { const matchId = data.replace('chat_', ''); await this.handleOpenChat(chatId, telegramId, matchId); } else if (data.startsWith('send_message_')) { const matchId = data.replace('send_message_', ''); await this.handleSendMessage(chatId, telegramId, matchId); } else if (data.startsWith('view_chat_profile_')) { const matchId = data.replace('view_chat_profile_', ''); await this.handleViewChatProfile(chatId, telegramId, matchId); } else if (data.startsWith('unmatch_')) { const matchId = data.replace('unmatch_', ''); await this.handleUnmatch(chatId, telegramId, matchId); } else if (data.startsWith('confirm_unmatch_')) { const matchId = data.replace('confirm_unmatch_', ''); await this.handleConfirmUnmatch(chatId, telegramId, matchId); } // Настройки else if (data === 'settings') { await this.handleSettings(chatId, telegramId); } else if (data === 'search_settings') { await this.handleSearchSettings(chatId, telegramId); } else if (data === 'notification_settings') { await this.handleNotificationSettings(chatId, telegramId); } else if (data === 'view_stats') { await this.handleViewStats(chatId, telegramId); } else if (data === 'view_profile_viewers') { await this.handleViewProfileViewers(chatId, telegramId); } else if (data === 'hide_profile') { await this.handleHideProfile(chatId, telegramId); } else if (data === 'delete_profile') { await this.handleDeleteProfile(chatId, telegramId); } else if (data === 'main_menu') { await this.handleMainMenu(chatId, telegramId); } else if (data === 'confirm_delete_profile') { await this.handleConfirmDeleteProfile(chatId, telegramId); } // Информация else if (data === 'how_it_works') { await this.handleHowItWorks(chatId); } else if (data === 'back_to_browsing') { await this.handleStartBrowsing(chatId, telegramId); } else if (data === 'get_vip') { await this.vipController.showVipSearch(chatId, telegramId); } // VIP функции else if (data === 'vip_search') { await this.vipController.showVipSearch(chatId, telegramId); } else if (data === 'vip_quick_search') { await this.vipController.performQuickVipSearch(chatId, telegramId); } else if (data === 'vip_advanced_search') { await this.vipController.startAdvancedSearch(chatId, telegramId); } else if (data === 'vip_dating_goal_search') { await this.vipController.showDatingGoalSearch(chatId, telegramId); } else if (data.startsWith('vip_goal_')) { const goal = data.replace('vip_goal_', ''); await this.vipController.performDatingGoalSearch(chatId, telegramId, goal); } else if (data.startsWith('vip_like_')) { const targetTelegramId = data.replace('vip_like_', ''); await this.handleVipLike(chatId, telegramId, targetTelegramId); } else if (data.startsWith('vip_superlike_')) { const targetTelegramId = data.replace('vip_superlike_', ''); await this.handleVipSuperlike(chatId, telegramId, targetTelegramId); } else if (data.startsWith('vip_dislike_')) { const targetTelegramId = data.replace('vip_dislike_', ''); await this.handleVipDislike(chatId, telegramId, targetTelegramId); } // Настройки языка и переводы else if (data === 'language_settings') { await this.handleLanguageSettings(chatId, telegramId); } else if (data.startsWith('set_language_')) { const languageCode = data.replace('set_language_', ''); await this.handleSetLanguage(chatId, telegramId, languageCode); } else if (data.startsWith('translate_profile_')) { const profileUserId = parseInt(data.replace('translate_profile_', '')); await this.handleTranslateProfile(chatId, telegramId, profileUserId); } else if (data === 'back_to_settings') { await this.handleSettings(chatId, telegramId); } // Настройки уведомлений else if (data === 'notifications') { if (this.notificationHandlers) { const userId = await this.profileService.getUserIdByTelegramId(telegramId); if (!userId) { await this.bot.answerCallbackQuery(query.id, { text: '❌ Вы не зарегистрированы.' }); return; } const settings = await this.notificationHandlers.getNotificationService().getNotificationSettings(userId); await this.notificationHandlers.sendNotificationSettings(chatId, settings); } else { await this.handleNotificationSettings(chatId, telegramId); } } // Обработка переключения настроек уведомлений else if (data.startsWith('notif_toggle:') || data === 'notif_time' || data.startsWith('notif_time_set:') || data === 'notif_dnd' || data.startsWith('notif_dnd_set:') || data === 'notif_dnd_time' || data.startsWith('notif_dnd_time_set:') || data === 'notif_dnd_time_custom') { // Делегируем обработку в NotificationHandlers, если он доступен if (this.notificationHandlers) { // Эти коллбэки уже обрабатываются в NotificationHandlers, поэтому здесь ничего не делаем // NotificationHandlers уже зарегистрировал свои обработчики в register() } else { const errorText = await this.getTranslation(telegramId, 'notifications.unavailable'); await this.bot.answerCallbackQuery(query.id, { text: errorText, show_alert: true }); } } else { const devText = await this.getTranslation(telegramId, 'notifications.inDevelopment'); await this.bot.answerCallbackQuery(query.id, { text: devText, show_alert: false }); return; } await this.bot.answerCallbackQuery(query.id); } catch (error) { console.error('Callback handler error:', error); const errorText = await this.getTranslation(telegramId, 'errors.tryAgain'); await this.bot.answerCallbackQuery(query.id, { text: errorText, show_alert: true }); } } // Создание профиля async handleCreateProfile(chatId: number, telegramId: string): Promise { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '👨 Мужской', callback_data: 'gender_male' }], [{ text: '👩 Женский', callback_data: 'gender_female' }], [{ text: '🔀 Другой', callback_data: 'gender_other' }] ] }; await this.bot.sendMessage( chatId, '👋 Давайте создадим ваш профиль!\n\n' + '🚹🚺 Сначала выберите ваш пол:', { reply_markup: keyboard } ); } // Выбор пола async handleGenderSelection(chatId: number, telegramId: string, gender: string): Promise { this.messageHandlers.startProfileCreation(telegramId, gender); await this.bot.sendMessage( chatId, '👍 Отлично!\n\n📝 Теперь напишите ваше имя:' ); } // Просмотр собственного профиля async handleViewMyProfile(chatId: number, telegramId: string): Promise { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } await this.showProfile(chatId, profile, true); } // Редактирование профиля async handleEditProfile(chatId: number, telegramId: string): Promise { await this.profileEditController.showProfileEditMenu(this.bot, chatId, parseInt(telegramId)); } // Управление фотографиями async handleManagePhotos(chatId: number, telegramId: string): Promise { await this.profileEditController.showPhotoManagementMenu(this.bot, chatId, parseInt(telegramId)); } // Начать просмотр анкет async handleStartBrowsing(chatId: number, telegramId: string, isNewUser: boolean = false): Promise { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Сначала создайте профиль!'); return; } await this.showNextCandidate(chatId, telegramId, isNewUser); } // Следующий кандидат async handleNextCandidate(chatId: number, telegramId: string): Promise { await this.showNextCandidate(chatId, telegramId); } // Лайк async handleLike(chatId: number, telegramId: string, targetUserId: string): Promise { try { // Получаем telegram_id целевого пользователя const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetUserId); if (!targetTelegramId) { throw new Error('Target user not found'); } const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'like'); if (result.isMatch) { // Это матч! const targetProfile = await this.profileService.getProfileByUserId(targetUserId); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '💬 Написать сообщение', callback_data: 'chat_' + targetUserId }, { text: '👤 Посмотреть профиль', callback_data: 'view_profile_' + targetUserId } ], [{ text: '🔍 Продолжить поиск', callback_data: 'next_candidate' }] ] }; const matchText = await this.getTranslation(telegramId, 'matches.mutualLike', { name: targetProfile?.name || await this.getTranslation(telegramId, 'common.thisUser') }); await this.bot.sendMessage( chatId, '🎉 ЭТО МАТЧ! 💕\n\n' + matchText, { reply_markup: keyboard } ); } else { await this.bot.sendMessage(chatId, '👍 Лайк отправлен!'); await this.showNextCandidate(chatId, telegramId); } } catch (error: any) { // Проверяем, связана ли ошибка с уже существующим свайпом if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') { await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее'); await this.showNextCandidate(chatId, telegramId); } else { await this.bot.sendMessage(chatId, '❌ Ошибка при отправке лайка'); console.error('Like error:', error); } } } // Дизлайк async handleDislike(chatId: number, telegramId: string, targetUserId: string): Promise { try { // Получаем telegram_id целевого пользователя const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetUserId); if (!targetTelegramId) { throw new Error('Target user not found'); } await this.matchingService.performSwipe(telegramId, targetTelegramId, 'pass'); await this.showNextCandidate(chatId, telegramId); } catch (error: any) { // Проверяем, связана ли ошибка с уже существующим свайпом if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') { await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее'); await this.showNextCandidate(chatId, telegramId); } else { await this.bot.sendMessage(chatId, '❌ Ошибка при отправке дизлайка'); console.error('Dislike error:', error); } } } // Супер лайк async handleSuperlike(chatId: number, telegramId: string, targetUserId: string): Promise { try { // Получаем telegram_id целевого пользователя const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetUserId); if (!targetTelegramId) { throw new Error('Target user not found'); } const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'superlike'); if (result.isMatch) { const targetProfile = await this.profileService.getProfileByUserId(targetUserId); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '💬 Написать сообщение', callback_data: 'chat_' + targetUserId }, { text: '👤 Посмотреть профиль', callback_data: 'view_profile_' + targetUserId } ], [{ text: '🔍 Продолжить поиск', callback_data: 'next_candidate' }] ] }; const superMatchText = await this.getTranslation(telegramId, 'matches.superLikeMatch', { name: targetProfile?.name || await this.getTranslation(telegramId, 'common.thisUser') }); await this.bot.sendMessage( chatId, '💖 СУПЕР МАТЧ! ⭐\n\n' + superMatchText, { reply_markup: keyboard } ); } else { await this.bot.sendMessage(chatId, '💖 Супер лайк отправлен!'); await this.showNextCandidate(chatId, telegramId); } } catch (error: any) { // Проверяем, связана ли ошибка с уже существующим свайпом if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') { await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее'); await this.showNextCandidate(chatId, telegramId); } else { await this.bot.sendMessage(chatId, '❌ Ошибка при отправке супер лайка'); console.error('Superlike error:', error); } } } // Обработка обратного лайка из уведомления async handleLikeBack(chatId: number, telegramId: string, targetUserId: string): Promise { try { // Получаем информацию о пользователях const targetProfile = await this.profileService.getProfileByUserId(targetUserId); if (!targetProfile) { await this.bot.sendMessage(chatId, '❌ Не удалось найти профиль'); return; } // Получаем telegram ID целевого пользователя для свайпа const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetUserId); if (!targetTelegramId) { await this.bot.sendMessage(chatId, '❌ Не удалось найти пользователя'); return; } // Выполняем свайп const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'like'); if (result.isMatch) { // Это матч! await this.bot.sendMessage( chatId, '🎉 *Поздравляем! Это взаимно!*\n\n' + `Вы и *${targetProfile.name}* понравились друг другу!\n` + 'Теперь вы можете начать общение.', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [{ text: '💬 Начать общение', callback_data: `start_chat:${targetUserId}` }], [{ text: '👀 Посмотреть профиль', callback_data: `view_profile:${targetUserId}` }] ] } } ); } else { await this.bot.sendMessage( chatId, '❤️ Вам понравился профиль ' + targetProfile.name + '!\n\n' + 'Если вы также понравитесь этому пользователю, будет создан матч.', { reply_markup: { inline_keyboard: [ [{ text: '🔍 Продолжить поиск', callback_data: 'start_browsing' }] ] } } ); } } catch (error) { console.error('Error in handleLikeBack:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при обработке лайка'); } } // Просмотр профиля кандидата async handleViewProfile(chatId: number, telegramId: string, targetUserId: string): Promise { const targetProfile = await this.profileService.getProfileByUserId(targetUserId); if (!targetProfile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } // Записываем просмотр профиля const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetProfile.userId); if (targetTelegramId) { await this.profileService.recordProfileView(telegramId, targetTelegramId, 'profile_view'); } await this.showProfile(chatId, targetProfile, false, telegramId); } // Показать больше фотографий async handleMorePhotos(chatId: number, telegramId: string, targetUserId: string): Promise { const targetProfile = await this.profileService.getProfileByUserId(targetUserId); if (!targetProfile || targetProfile.photos.length <= 1) { await this.bot.sendMessage(chatId, '📷 У пользователя нет дополнительных фотографий'); return; } // Отправляем фотографии в виде медиа-группы (коллажа) // Создаем массив объектов медиа для группового отправления const mediaGroup = targetProfile.photos.slice(1).map((photoFileId, index) => ({ type: 'photo' as const, media: photoFileId, caption: index === 0 ? `📸 Дополнительные фото ${targetProfile.name}` : undefined })); try { // Отправляем все фото одним сообщением (медиа-группой) await this.bot.sendMediaGroup(chatId, mediaGroup); } catch (error) { console.error('Error sending media group:', error); // Если не получилось отправить медиа-группой, отправляем по одной for (let i = 1; i < targetProfile.photos.length; i++) { try { await this.bot.sendPhoto(chatId, targetProfile.photos[i]); } catch (photoError) { console.error(`Error sending photo ${i}:`, photoError); } } } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '👎 Не нравится', callback_data: 'dislike_' + targetUserId }, { text: '💖 Супер лайк', callback_data: 'superlike_' + targetUserId }, { text: '👍 Нравится', callback_data: 'like_' + targetUserId } ] ] }; await this.bot.sendMessage( chatId, '📸 Все фотографии просмотрены!\n\nВаше решение?', { reply_markup: keyboard } ); } // Просмотр матчей async handleViewMatches(chatId: number, telegramId: string): Promise { const matches = await this.matchingService.getUserMatches(telegramId); if (matches.length === 0) { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '🔍 Начать поиск', callback_data: 'start_browsing' }] ] }; await this.bot.sendMessage( chatId, '💔 У вас пока нет матчей\n\n' + 'Попробуйте просмотреть больше анкет!', { reply_markup: keyboard } ); return; } let matchText = 'Ваши матчи (' + matches.length + '):\n\n'; for (const match of matches) { const otherUserId = match.userId1 === telegramId ? match.userId2 : match.userId1; const otherProfile = await this.profileService.getProfileByUserId(otherUserId); if (otherProfile) { matchText += '💖 ' + otherProfile.name + ', ' + otherProfile.age + '\n'; matchText += '📍 ' + (otherProfile.city || 'Не указан') + '\n\n'; } } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '💬 Открыть чаты', callback_data: 'open_chats' }], [{ text: '🔍 Найти еще', callback_data: 'start_browsing' }] ] }; await this.bot.sendMessage(chatId, matchText, { reply_markup: keyboard }); } // Открыть чаты // Открыть список чатов async handleOpenChats(chatId: number, telegramId: string): Promise { const chats = await this.chatService.getUserChats(telegramId); if (chats.length === 0) { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '🔍 Найти матчи', callback_data: 'start_browsing' }], [{ text: '💕 Мои матчи', callback_data: 'view_matches' }] ] }; await this.bot.sendMessage( chatId, '💬 У вас пока нет активных чатов\n\n' + 'Начните просматривать анкеты и получите первые матчи!', { reply_markup: keyboard } ); return; } let messageText = '💬 Ваши чаты:\n\n'; const keyboard: InlineKeyboardMarkup = { inline_keyboard: [] }; for (const chat of chats.slice(0, 10)) { // Показываем только первые 10 чатов const unreadBadge = chat.unreadCount > 0 ? ` (${chat.unreadCount})` : ''; const lastMessagePreview = chat.lastMessage ? (chat.lastMessage.length > 30 ? chat.lastMessage.substring(0, 30) + '...' : chat.lastMessage) : 'Новый матч'; messageText += `💕 ${chat.otherUserName}${unreadBadge}\n`; messageText += `💬 ${lastMessagePreview}\n\n`; keyboard.inline_keyboard.push([ { text: `💬 ${chat.otherUserName}${unreadBadge}`, callback_data: `chat_${chat.matchId}` } ]); } if (chats.length > 10) { messageText += `...и еще ${chats.length - 10} чатов`; } keyboard.inline_keyboard.push([ { text: '🔍 Найти еще', callback_data: 'start_browsing' }, { text: '💕 Матчи', callback_data: 'view_matches' } ]); await this.bot.sendMessage(chatId, messageText, { reply_markup: keyboard }); } // Открыть конкретный чат async handleOpenChat(chatId: number, telegramId: string, matchId: string): Promise { 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, 10); let chatText = `💬 Чат с ${matchInfo.otherUserProfile?.name}\n\n`; if (messages.length === 0) { chatText += '📝 Начните общение! Напишите первое сообщение.\n\n'; } else { chatText += '📝 Последние сообщения:\n\n'; for (const message of messages.slice(-5)) { // Показываем последние 5 сообщений const currentUserId = await this.profileService.getUserIdByTelegramId(telegramId); const isFromMe = message.senderId === currentUserId; const sender = isFromMe ? 'Вы' : matchInfo.otherUserProfile?.name; const time = message.createdAt.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }); chatText += `${sender} (${time}):\n${message.content}\n\n`; } } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '✍️ Написать сообщение', callback_data: `send_message_${matchId}` } ], [ { text: '👤 Профиль', callback_data: `view_chat_profile_${matchId}` }, { text: '💔 Удалить матч', callback_data: `unmatch_${matchId}` } ], [ { text: '← Назад к чатам', callback_data: 'open_chats' } ] ] }; await this.bot.sendMessage(chatId, chatText, { reply_markup: keyboard }); } // Отправить сообщение async handleSendMessage(chatId: number, telegramId: string, matchId: string): Promise { const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo) { await this.bot.sendMessage(chatId, '❌ Чат не найден или недоступен'); return; } // Устанавливаем состояние ожидания сообщения this.messageHandlers.setWaitingForMessage(telegramId, matchId); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '❌ Отмена', callback_data: `chat_${matchId}` }] ] }; await this.bot.sendMessage( chatId, `✍️ Напишите сообщение для ${matchInfo.otherUserProfile?.name}:\n\n` + '💡 Просто отправьте текст в этот чат', { reply_markup: keyboard } ); } // Просмотр профиля в чате async handleViewChatProfile(chatId: number, telegramId: string, matchId: string): Promise { const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo || !matchInfo.otherUserProfile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } await this.showProfile(chatId, matchInfo.otherUserProfile, false, telegramId); } // Удалить матч (размэтчиться) async handleUnmatch(chatId: number, telegramId: string, matchId: string): Promise { const matchInfo = await this.chatService.getMatchInfo(matchId, telegramId); if (!matchInfo) { await this.bot.sendMessage(chatId, '❌ Матч не найден'); return; } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '✅ Да, удалить', callback_data: `confirm_unmatch_${matchId}` }, { text: '❌ Отмена', callback_data: `chat_${matchId}` } ] ] }; await this.bot.sendMessage( chatId, `💔 Вы уверены, что хотите удалить матч с ${matchInfo.otherUserProfile?.name}?\n\n` + '⚠️ Это действие нельзя отменить. Вся переписка будет удалена.', { reply_markup: keyboard } ); } // Подтвердить удаление матча async handleConfirmUnmatch(chatId: number, telegramId: string, matchId: string): Promise { const success = await this.chatService.unmatch(matchId, telegramId); if (success) { await this.bot.sendMessage( chatId, '💔 Матч удален\n\n' + 'Вы больше не увидите этого пользователя в своих матчах.' ); // Возвращаемся к списку чатов setTimeout(() => { this.handleOpenChats(chatId, telegramId); }, 2000); } else { await this.bot.sendMessage(chatId, '❌ Не удалось удалить матч. Попробуйте еще раз.'); } } // Настройки async handleSettings(chatId: number, telegramId: string): Promise { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '🔍 Настройки поиска', callback_data: 'search_settings' }, { text: '🔔 Уведомления', callback_data: 'notification_settings' } ], [ { text: '🌐 Язык интерфейса', callback_data: 'language_settings' }, { text: '📊 Статистика', callback_data: 'view_stats' } ], [ { text: '�🚫 Скрыть профиль', callback_data: 'hide_profile' }, { text: '🗑 Удалить профиль', callback_data: 'delete_profile' } ] ] }; await this.bot.sendMessage( chatId, '⚙️ Настройки профиля\n\nВыберите что хотите изменить:', { reply_markup: keyboard } ); } // Настройки поиска async handleSearchSettings(chatId: number, telegramId: string): Promise { await this.bot.sendMessage( chatId, '🔍 Настройки поиска будут доступны в следующем обновлении!' ); } // Настройки уведомлений - реализация перенесена в расширенную версию // Как это работает async handleHowItWorks(chatId: number): Promise { const helpText = '🎯 Как работает Telegram Tinder Bot?\n\n' + '1️⃣ Создайте профиль\n' + ' • Добавьте фото и описание\n' + ' • Укажите ваши предпочтения\n\n' + '2️⃣ Просматривайте анкеты\n' + ' • Ставьте лайки понравившимся\n' + ' • Используйте супер лайки для особых случаев\n\n' + '3️⃣ Получайте матчи\n' + ' • Когда ваш лайк взаимен - это матч!\n' + ' • Начинайте общение\n\n' + '4️⃣ Общайтесь и знакомьтесь\n' + ' • Находите общие интересы\n' + ' • Договаривайтесь о встрече\n\n' + '�� Советы:\n' + '• Используйте качественные фото\n' + '• Напишите интересное описание\n' + '• Будьте вежливы в общении\n\n' + '❤️ Удачи в поиске любви!'; const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '🚀 Создать профиль', callback_data: 'create_profile' }] ] }; await this.bot.sendMessage(chatId, helpText, { reply_markup: keyboard }); } // Вспомогательные методы async showProfile(chatId: number, profile: Profile, isOwner: boolean = false, viewerId?: string): Promise { const hasMultiplePhotos = profile.photos.length > 1; const mainPhotoFileId = profile.photos[0]; // Первое фото - главное let profileText = '👤 ' + profile.name + ', ' + profile.age + '\n'; profileText += '📍 ' + (profile.city || 'Не указан'); // Добавляем расстояние, если это не владелец профиля и есть viewerId if (!isOwner && viewerId) { const viewerProfile = await this.profileService.getProfileByTelegramId(viewerId); if (viewerProfile && viewerProfile.location && profile.location) { const distance = viewerProfile.getDistanceTo(profile); if (distance !== null) { profileText += ` (${Math.round(distance)} км)`; } } } profileText += '\n'; if (profile.job) profileText += '💼 ' + profile.job + '\n'; if (profile.education) profileText += '🎓 ' + profile.education + '\n'; if (profile.height) profileText += '📏 ' + profile.height + ' см\n'; if (profile.religion) profileText += '🕊️ ' + profile.religion + '\n'; // Цель знакомства if (profile.datingGoal) { const goalText = this.getDatingGoalText(profile.datingGoal); profileText += '💕 ' + goalText + '\n'; } // Образ жизни if (profile.lifestyle) { const lifestyleText = this.getLifestyleText(profile.lifestyle); if (lifestyleText) { profileText += lifestyleText + '\n'; } } profileText += '\n📝 ' + (profile.bio || 'Описание не указано') + '\n'; // Хобби с хэштегами if (profile.hobbies) { let hobbiesArray: string[] = []; if (typeof profile.hobbies === 'string') { hobbiesArray = profile.hobbies.split(',').map(hobby => hobby.trim()).filter(hobby => hobby); } else if (Array.isArray(profile.hobbies)) { hobbiesArray = (profile.hobbies as string[]).filter((hobby: string) => hobby && hobby.trim()); } if (hobbiesArray.length > 0) { const formattedHobbies = hobbiesArray.map(hobby => '#' + hobby).join(' '); profileText += '\n🎯 ' + formattedHobbies + '\n'; } } if (profile.interests.length > 0) { profileText += '\n💡 Интересы: ' + profile.interests.join(', '); } let keyboard: InlineKeyboardMarkup; if (isOwner) { keyboard = { inline_keyboard: [ [ { text: '✏️ Редактировать', callback_data: 'edit_profile' }, { text: '📸 Фото', callback_data: 'manage_photos' } ], [{ text: '🔍 Начать поиск', callback_data: 'start_browsing' }] ] }; } else { keyboard = { inline_keyboard: [ [ { text: '👎 Не нравится', callback_data: 'dislike_' + profile.userId }, { text: '💖 Супер лайк', callback_data: 'superlike_' + profile.userId }, { text: '👍 Нравится', callback_data: 'like_' + profile.userId } ], [{ text: '🔍 Продолжить поиск', callback_data: 'next_candidate' }] ] }; } // Проверяем, есть ли валидное фото (file_id или URL) const hasValidPhoto = mainPhotoFileId && (mainPhotoFileId.startsWith('http') || mainPhotoFileId.startsWith('AgAC') || mainPhotoFileId.length > 20); // file_id обычно длинные if (hasValidPhoto) { try { if (hasMultiplePhotos) { // Если есть несколько фото, отправляем их как медиа-группу (коллаж) const mediaGroup = profile.photos.map((photoFileId, index) => ({ type: 'photo' as const, media: photoFileId, caption: index === 0 ? profileText : undefined, parse_mode: index === 0 ? 'Markdown' as const : undefined })); // Сначала отправляем медиа-группу await this.bot.sendMediaGroup(chatId, mediaGroup); // Затем отправляем отдельное сообщение с кнопками await this.bot.sendMessage(chatId, '📸 Выберите действие:', { reply_markup: keyboard }); } else { // Если только одно фото, отправляем его с текстом await this.bot.sendPhoto(chatId, mainPhotoFileId, { caption: profileText, reply_markup: keyboard, parse_mode: 'Markdown' }); } } catch (error) { console.error('Error sending profile photos:', error); // Если не удалось отправить фото, отправляем текст await this.bot.sendMessage(chatId, '🖼 Фото недоступно\n\n' + profileText, { reply_markup: keyboard, parse_mode: 'Markdown' }); } } else { // Отправляем как текстовое сообщение await this.bot.sendMessage(chatId, profileText, { reply_markup: keyboard, parse_mode: 'Markdown' }); } } async showNextCandidate(chatId: number, telegramId: string, isNewUser: boolean = false): Promise { const candidate = await this.matchingService.getNextCandidate(telegramId, isNewUser); if (!candidate) { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '🔄 Попробовать еще раз', callback_data: 'start_browsing' }], [{ text: '💕 Мои матчи', callback_data: 'view_matches' }] ] }; await this.bot.sendMessage( chatId, '🎉 Вы просмотрели всех доступных кандидатов!\n\n' + '⏰ Попробуйте позже - возможно появятся новые анкеты!', { reply_markup: keyboard } ); return; } // Записываем просмотр кандидата const candidateTelegramId = await this.profileService.getTelegramIdByUserId(candidate.userId); if (candidateTelegramId) { await this.profileService.recordProfileView(telegramId, candidateTelegramId, 'browse'); } const candidatePhotoFileId = candidate.photos[0]; // Первое фото - главное // Получаем профиль текущего пользователя для вычисления расстояния const userProfile = await this.profileService.getProfileByTelegramId(telegramId); let candidateText = candidate.name + ', ' + candidate.age + '\n'; candidateText += '📍 ' + (candidate.city || 'Не указан'); // Добавляем расстояние, если есть координаты у обоих пользователей if (userProfile && userProfile.location && candidate.location) { const distance = userProfile.getDistanceTo(candidate); if (distance !== null) { candidateText += ` (${Math.round(distance)} км)`; } } candidateText += '\n'; if (candidate.job) candidateText += '💼 ' + candidate.job + '\n'; if (candidate.education) candidateText += '🎓 ' + candidate.education + '\n'; if (candidate.height) candidateText += '📏 ' + candidate.height + ' см\n'; if (candidate.religion) candidateText += '🕊️ ' + candidate.religion + '\n'; candidateText += '\n📝 ' + (candidate.bio || 'Описание отсутствует') + '\n'; if (candidate.interests.length > 0) { candidateText += '\n🎯 Интересы: ' + candidate.interests.join(', '); } if (candidate.hobbies) { candidateText += '\n🎮 Хобби: ' + candidate.hobbies; } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '👎 Не нравится', callback_data: 'dislike_' + candidate.userId }, { text: '💖 Супер лайк', callback_data: 'superlike_' + candidate.userId }, { text: '👍 Нравится', callback_data: 'like_' + candidate.userId } ], [ { text: '👤 Профиль', callback_data: 'view_profile_' + candidate.userId }, { text: '📸 Еще фото', callback_data: 'more_photos_' + candidate.userId } ], [{ text: '⏭ Следующий', callback_data: 'next_candidate' }] ] }; // Проверяем, есть ли валидное фото (file_id или URL) const hasValidPhoto = candidatePhotoFileId && (candidatePhotoFileId.startsWith('http') || candidatePhotoFileId.startsWith('AgAC') || candidatePhotoFileId.length > 20); // file_id обычно длинные if (hasValidPhoto) { try { await this.bot.sendPhoto(chatId, candidatePhotoFileId, { caption: candidateText, reply_markup: keyboard }); } catch (error) { // Если не удалось отправить фото, отправляем текст await this.bot.sendMessage(chatId, '🖼 Фото недоступно\n\n' + candidateText, { reply_markup: keyboard }); } } else { // Отправляем как текстовое сообщение await this.bot.sendMessage(chatId, '📝 ' + candidateText, { reply_markup: keyboard }); } } // ===== НОВЫЕ МЕТОДЫ ДЛЯ РЕДАКТИРОВАНИЯ ПРОФИЛЯ ===== // Предпросмотр профиля async handlePreviewProfile(chatId: number, telegramId: string): Promise { await this.profileEditController.showProfilePreview(this.bot, chatId, parseInt(telegramId)); } // Редактирование имени async handleEditName(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'name'); await this.bot.sendMessage(chatId, '📝 *Введите ваше новое имя:*\n\nНапример: Анна', { parse_mode: 'Markdown' }); } // Редактирование возраста async handleEditAge(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'age'); await this.bot.sendMessage(chatId, '🎂 *Введите ваш возраст:*\n\nВозраст должен быть от 18 до 100 лет', { parse_mode: 'Markdown' }); } // Редактирование описания "О себе" async handleEditBio(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'bio'); await this.bot.sendMessage(chatId, '📖 *Расскажите о себе:*\n\n' + 'Напишите несколько предложений, которые помогут людям лучше вас узнать.\n\n' + '_Максимум 500 символов_', { parse_mode: 'Markdown' }); } // Редактирование хобби async handleEditHobbies(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'hobbies'); await this.bot.sendMessage(chatId, '🎯 *Введите ваши хобби через запятую:*\n\n' + 'Например: футбол, чтение, путешествия, кулинария\n\n' + '_В анкете они будут отображаться как хэштеги: #футбол #чтение #путешествия_', { parse_mode: 'Markdown' }); } // Редактирование города async handleEditCity(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'city'); await this.bot.sendMessage(chatId, '🏙️ *Укажите ваш город:*\n\nВыберите один из вариантов:', { parse_mode: 'Markdown', reply_markup: { keyboard: [ [{ text: '📍 Отправить геолокацию', request_location: true }] ], resize_keyboard: true, one_time_keyboard: true } }); } // Редактирование работы async handleEditJob(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'job'); await this.bot.sendMessage(chatId, '💼 *Введите вашу профессию или место работы:*\n\nНапример: Дизайнер в IT-компании', { parse_mode: 'Markdown' }); } // Редактирование образования async handleEditEducation(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'education'); await this.bot.sendMessage(chatId, '🎓 *Введите ваше образование:*\n\nНапример: МГУ, факультет журналистики', { parse_mode: 'Markdown' }); } // Редактирование роста async handleEditHeight(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'height'); await this.bot.sendMessage(chatId, '📏 *Введите ваш рост в сантиметрах:*\n\nНапример: 175', { parse_mode: 'Markdown' }); } // Редактирование религии async handleEditReligion(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'religion'); await this.bot.sendMessage(chatId, '🕊️ *Введите вашу религию или напишите "нет":*\n\nНапример: православие, ислам, атеизм, нет', { parse_mode: 'Markdown' }); } // Редактирование цели знакомства async handleEditDatingGoal(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '💕 Серьёзные отношения', callback_data: 'set_dating_goal_serious' }, { text: '🎉 Лёгкие отношения', callback_data: 'set_dating_goal_casual' } ], [ { text: '👥 Дружба', callback_data: 'set_dating_goal_friends' }, { text: '🔥 Одна ночь', callback_data: 'set_dating_goal_one_night' } ], [ { text: '😏 FWB', callback_data: 'set_dating_goal_fwb' }, { text: '💎 Спонсорство', callback_data: 'set_dating_goal_sugar' } ], [ { text: '💍 Брак с переездом', callback_data: 'set_dating_goal_marriage_abroad' }, { text: '💫 Полиамория', callback_data: 'set_dating_goal_polyamory' } ], [ { text: '🤷‍♀️ Пока не определился', callback_data: 'set_dating_goal_unsure' } ], [ { text: '⬅️ Назад', callback_data: 'edit_profile' } ] ] }; await this.bot.sendMessage(chatId, '💕 *Выберите цель знакомства:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Редактирование образа жизни async handleEditLifestyle(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '🚬 Курение', callback_data: 'edit_smoking' }, { text: '🍷 Алкоголь', callback_data: 'edit_drinking' } ], [ { text: '👶 Отношение к детям', callback_data: 'edit_kids' } ], [ { text: '⬅️ Назад', callback_data: 'edit_profile' } ] ] }; await this.bot.sendMessage(chatId, '🚬 *Выберите что хотите изменить в образе жизни:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Редактирование предпочтений поиска async handleEditSearchPreferences(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '🔢 Возрастной диапазон', callback_data: 'edit_age_range' }, { text: '📍 Максимальное расстояние', callback_data: 'edit_distance' } ], [ { text: '⬅️ Назад', callback_data: 'edit_profile' } ] ] }; await this.bot.sendMessage(chatId, '⚙️ *Настройки поиска:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Добавление фото async handleAddPhoto(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'photo'); await this.bot.sendMessage(chatId, '📷 *Отправьте фотографию:*\n\nМаксимум 9 фотографий в профиле', { parse_mode: 'Markdown' }); } // Удаление фото async handleDeletePhoto(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile || profile.photos.length === 0) { await this.bot.sendMessage(chatId, '❌ У вас нет фотографий для удаления'); return; } const keyboard = { inline_keyboard: [ ...profile.photos.map((photo, index) => [ { text: `🗑️ Удалить фото ${index + 1}`, callback_data: `delete_photo_${index}` } ]), [{ text: '⬅️ Назад', callback_data: 'manage_photos' }] ] }; await this.bot.sendMessage(chatId, '🗑️ *Выберите фото для удаления:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } catch (error) { console.error('Error in handleDeletePhoto:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Установка главного фото async handleSetMainPhoto(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile || profile.photos.length <= 1) { await this.bot.sendMessage(chatId, '❌ У вас недостаточно фотографий'); return; } const keyboard = { inline_keyboard: [ ...profile.photos.map((photo, index) => [ { text: `⭐ Сделать главным фото ${index + 1}`, callback_data: `set_main_photo_${index}` } ]), [{ text: '⬅️ Назад', callback_data: 'manage_photos' }] ] }; await this.bot.sendMessage(chatId, '⭐ *Выберите главное фото:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } catch (error) { console.error('Error in handleSetMainPhoto:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // ===== НОВЫЕ МЕТОДЫ ДЛЯ РАСШИРЕННОГО РЕДАКТИРОВАНИЯ ===== // Удаление фото по индексу async handleDeletePhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile || photoIndex >= profile.photos.length) { await this.bot.sendMessage(chatId, '❌ Фото не найдено'); return; } profile.removePhoto(profile.photos[photoIndex]); await this.profileService.updateProfile(profile.userId, { photos: profile.photos }); await this.bot.sendMessage(chatId, '✅ Фото удалено!'); setTimeout(() => { this.handleManagePhotos(chatId, telegramId); }, 1000); } catch (error) { console.error('Error deleting photo:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Установка главного фото по индексу async handleSetMainPhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile || photoIndex >= profile.photos.length) { await this.bot.sendMessage(chatId, '❌ Фото не найдено'); return; } profile.setMainPhoto(profile.photos[photoIndex]); await this.profileService.updateProfile(profile.userId, { photos: profile.photos }); await this.bot.sendMessage(chatId, '✅ Главное фото установлено!'); setTimeout(() => { this.handleManagePhotos(chatId, telegramId); }, 1000); } catch (error) { console.error('Error setting main photo:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Установка цели знакомства async handleSetDatingGoal(chatId: number, telegramId: string, goal: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } await this.profileService.updateProfile(profile.userId, { datingGoal: goal as any }); const goalTexts: { [key: string]: string } = { 'serious': 'Серьёзные отношения', 'casual': 'Лёгкие отношения', 'friends': 'Дружба', 'unsure': 'Пока не определился', 'one_night': 'Отношения на одну ночь', 'fwb': 'Друзья с привилегиями', 'marriage_abroad': 'Брак с переездом', 'sugar': 'Спонсорство', 'polyamory': 'Полиамория' }; await this.bot.sendMessage(chatId, `✅ Цель знакомства установлена: ${goalTexts[goal]}`); setTimeout(() => { this.profileEditController.showProfileEditMenu(this.bot, chatId, parseInt(telegramId)); }, 1500); } catch (error) { console.error('Error setting dating goal:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Редактирование курения async handleEditSmoking(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '🚭 Не курю', callback_data: 'set_smoking_never' }, { text: '🚬 Иногда', callback_data: 'set_smoking_sometimes' } ], [ { text: '🚬 Регулярно', callback_data: 'set_smoking_regularly' } ], [ { text: '⬅️ Назад', callback_data: 'edit_lifestyle' } ] ] }; await this.bot.sendMessage(chatId, '🚬 *Выберите ваше отношение к курению:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Редактирование алкоголя async handleEditDrinking(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '🚫 Не пью', callback_data: 'set_drinking_never' }, { text: '🍷 Иногда', callback_data: 'set_drinking_sometimes' } ], [ { text: '🍺 Регулярно', callback_data: 'set_drinking_regularly' } ], [ { text: '⬅️ Назад', callback_data: 'edit_lifestyle' } ] ] }; await this.bot.sendMessage(chatId, '🍷 *Выберите ваше отношение к алкоголю:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Редактирование отношения к детям async handleEditKids(chatId: number, telegramId: string): Promise { const keyboard = { inline_keyboard: [ [ { text: '👶 Есть дети', callback_data: 'set_kids_have' }, { text: '💕 Хочу детей', callback_data: 'set_kids_want' } ], [ { text: '🚫 Не хочу детей', callback_data: 'set_kids_dont_want' }, { text: '🤷‍♀️ Пока не знаю', callback_data: 'set_kids_unsure' } ], [ { text: '⬅️ Назад', callback_data: 'edit_lifestyle' } ] ] }; await this.bot.sendMessage(chatId, '👶 *Выберите ваше отношение к детям:*', { parse_mode: 'Markdown', reply_markup: keyboard }); } // Установка параметра образа жизни async handleSetLifestyle(chatId: number, telegramId: string, type: string, value: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } // Обновляем отдельные колонки напрямую, а не через объект lifestyle const updates: any = {}; switch (type) { case 'smoking': updates.smoking = value; break; case 'drinking': updates.drinking = value; break; case 'kids': // Для поля has_kids, которое имеет тип boolean, преобразуем строковые значения if (value === 'have') { updates.has_kids = true; } else { // Для 'want', 'dont_want', 'unsure' ставим false updates.has_kids = false; } break; } await this.profileService.updateProfile(profile.userId, updates); const typeTexts: { [key: string]: string } = { 'smoking': 'курение', 'drinking': 'алкоголь', 'kids': 'отношение к детям' }; const valueTexts: { [key: string]: { [key: string]: string } } = { smoking: { 'never': 'не курю', 'sometimes': 'иногда', 'regularly': 'регулярно' }, drinking: { 'never': 'не пью', 'sometimes': 'иногда', 'regularly': 'регулярно' }, kids: { 'have': 'есть дети', 'want': 'хочу детей', 'dont_want': 'не хочу детей', 'unsure': 'пока не знаю' } }; const typeText = typeTexts[type] || type; const valueText = valueTexts[type]?.[value] || value; await this.bot.sendMessage(chatId, `✅ ${typeText}: ${valueText}`); setTimeout(() => { this.profileEditController.showProfileEditMenu(this.bot, chatId, parseInt(telegramId)); }, 1500); } catch (error) { console.error('Error setting lifestyle:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Редактирование возрастного диапазона async handleEditAgeRange(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'age_range'); await this.bot.sendMessage(chatId, '🔢 *Введите возрастной диапазон:*\n\n' + 'Формат: минимальный-максимальный возраст\n' + 'Например: 18-35', { parse_mode: 'Markdown' }); } // Редактирование максимального расстояния async handleEditDistance(chatId: number, telegramId: string): Promise { this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'distance'); await this.bot.sendMessage(chatId, '📍 *Введите максимальное расстояние для поиска:*\n\n' + 'В километрах (например: 50)', { parse_mode: 'Markdown' }); } // ===== VIP ФУНКЦИИ ===== // VIP поиск по целям знакомства async handleVipSearch(chatId: number, telegramId: string): Promise { try { // Проверяем VIP статус пользователя const user = await this.profileService.getUserByTelegramId(telegramId); if (!user || !user.premium) { // Изменено с isPremium на premium, чтобы соответствовать названию колонки в базе данных const keyboard = { inline_keyboard: [ [ { text: '💎 Получить VIP', callback_data: 'get_vip' }, { text: '⬅️ Назад', callback_data: 'main_menu' } ] ] }; await this.bot.sendMessage(chatId, '🔒 *VIP Поиск*\n\n' + 'Эта функция доступна только для VIP пользователей!\n\n' + '✨ *VIP возможности:*\n' + '• Поиск по целям знакомства\n' + '• Расширенные фильтры\n' + '• Приоритет в показе анкет\n' + '• Безлимитные суперлайки', { parse_mode: 'Markdown', reply_markup: keyboard }); return; } const keyboard = { inline_keyboard: [ [ { text: '💕 Серьёзные отношения', callback_data: 'search_by_goal_serious' }, { text: '🎉 Лёгкие отношения', callback_data: 'search_by_goal_casual' } ], [ { text: '👥 Дружба', callback_data: 'search_by_goal_friends' }, { text: '🔥 Одна ночь', callback_data: 'search_by_goal_one_night' } ], [ { text: '😏 FWB', callback_data: 'search_by_goal_fwb' }, { text: '💎 Спонсорство', callback_data: 'search_by_goal_sugar' } ], [ { text: '💍 Брак с переездом', callback_data: 'search_by_goal_marriage_abroad' }, { text: '💫 Полиамория', callback_data: 'search_by_goal_polyamory' } ], [ { text: '🎲 Все цели', callback_data: 'start_browsing' } ], [ { text: '⬅️ Главное меню', callback_data: 'main_menu' } ] ] }; await this.bot.sendMessage(chatId, '🔍 *VIP Поиск по целям знакомства*\n\n' + 'Выберите интересующую вас цель:', { parse_mode: 'Markdown', reply_markup: keyboard }); } catch (error) { console.error('Error in VIP search:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка'); } } // Поиск по конкретной цели async handleSearchByGoal(chatId: number, telegramId: string, goal: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Сначала создайте профиль!'); return; } // Получаем кандидатов с определенной целью знакомства const candidates = await this.matchingService.getCandidatesWithGoal(profile, goal); if (candidates.length === 0) { const goalTexts: { [key: string]: string } = { 'serious': 'серьёзные отношения', 'casual': 'лёгкие отношения', 'friends': 'дружбу', 'one_night': 'отношения на одну ночь', 'fwb': 'друзей с привилегиями', 'marriage_abroad': 'брак с переездом', 'sugar': 'спонсорство', 'polyamory': 'полиаморию' }; const keyboard = { inline_keyboard: [ [ { text: '🔍 Другие цели', callback_data: 'vip_search' }, { text: '🎲 Обычный поиск', callback_data: 'start_browsing' } ], [ { text: '⬅️ Главное меню', callback_data: 'main_menu' } ] ] }; await this.bot.sendMessage(chatId, `😔 *Пока нет анкет*\n\n` + `К сожалению, сейчас нет пользователей, которые ищут ${goalTexts[goal] || goal}.\n\n` + 'Попробуйте позже или выберите другую цель!', { parse_mode: 'Markdown', reply_markup: keyboard }); return; } // Показываем первого кандидата const candidate = candidates[0]; await this.displayCandidate(chatId, candidate); } catch (error) { console.error('Error searching by goal:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при поиске'); } } // Показ конкретного кандидата (для VIP поиска) async displayCandidate(chatId: number, candidate: Profile): Promise { const candidatePhotoFileId = candidate.photos[0]; // Первое фото - главное let candidateText = candidate.name + ', ' + candidate.age + '\n'; candidateText += '📍 ' + (candidate.city || 'Не указан') + '\n'; if (candidate.job) candidateText += '💼 ' + candidate.job + '\n'; if (candidate.education) candidateText += '🎓 ' + candidate.education + '\n'; if (candidate.height) candidateText += '📏 ' + candidate.height + ' см\n'; if (candidate.religion) candidateText += '🕊️ ' + candidate.religion + '\n'; // Цель знакомства if (candidate.datingGoal) { const goalText = this.getDatingGoalText(candidate.datingGoal); candidateText += '💕 ' + goalText + '\n'; } // Образ жизни if (candidate.lifestyle) { const lifestyleText = this.getLifestyleText(candidate.lifestyle); if (lifestyleText) { candidateText += lifestyleText + '\n'; } } candidateText += '\n📝 ' + (candidate.bio || 'Описание отсутствует') + '\n'; // Хобби с хэштегами if (candidate.hobbies) { let hobbiesArray: string[] = []; if (typeof candidate.hobbies === 'string') { hobbiesArray = candidate.hobbies.split(',').map(hobby => hobby.trim()).filter(hobby => hobby); } else if (Array.isArray(candidate.hobbies)) { hobbiesArray = (candidate.hobbies as string[]).filter((hobby: string) => hobby && hobby.trim()); } if (hobbiesArray.length > 0) { const formattedHobbies = hobbiesArray.map(hobby => '#' + hobby).join(' '); candidateText += '\n🎯 ' + formattedHobbies + '\n'; } } if (candidate.interests.length > 0) { candidateText += '\n� Интересы: ' + candidate.interests.join(', '); } const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '👎 Не нравится', callback_data: 'dislike_' + candidate.userId }, { text: '💖 Супер лайк', callback_data: 'superlike_' + candidate.userId }, { text: '👍 Нравится', callback_data: 'like_' + candidate.userId } ], [ { text: '👤 Профиль', callback_data: 'view_profile_' + candidate.userId }, { text: '📸 Еще фото', callback_data: 'more_photos_' + candidate.userId } ], [ { text: '⏭ Следующий', callback_data: 'next_candidate' }, { text: '🔍 VIP поиск', callback_data: 'vip_search' } ] ] }; // Проверяем, есть ли валидное фото (file_id или URL) const hasValidPhoto = candidatePhotoFileId && (candidatePhotoFileId.startsWith('http') || candidatePhotoFileId.startsWith('AgAC') || candidatePhotoFileId.length > 20); // file_id обычно длинные if (hasValidPhoto) { try { await this.bot.sendPhoto(chatId, candidatePhotoFileId, { caption: candidateText, reply_markup: keyboard }); } catch (error) { // Если не удалось отправить фото, отправляем текст await this.bot.sendMessage(chatId, '🖼 Фото недоступно\n\n' + candidateText, { reply_markup: keyboard }); } } else { // Отправляем как текстовое сообщение await this.bot.sendMessage(chatId, '📝 ' + candidateText, { reply_markup: keyboard }); } } // Получить текст цели знакомства private getDatingGoalText(goal: string): string { const goals: { [key: string]: string } = { 'serious': 'Серьёзные отношения', 'casual': 'Лёгкие отношения', 'friends': 'Дружба', 'unsure': 'Пока не определился', 'one_night': 'Отношения на одну ночь', 'fwb': 'Друзья с привилегиями', 'marriage_abroad': 'Брак с переездом', 'sugar': 'Спонсорство', 'polyamory': 'Полиамория' }; return goals[goal] || goal; } // Получить текст образа жизни private getLifestyleText(lifestyle: any): string { const parts: string[] = []; if (lifestyle?.smoking) { const smokingTexts: { [key: string]: string } = { 'never': 'Не курю', 'sometimes': 'Иногда курю', 'regularly': 'Курю' }; parts.push('🚬 ' + (smokingTexts[lifestyle.smoking] || lifestyle.smoking)); } if (lifestyle?.drinking) { const drinkingTexts: { [key: string]: string } = { 'never': 'Не пью', 'sometimes': 'Иногда пью', 'regularly': 'Пью' }; parts.push('🍷 ' + (drinkingTexts[lifestyle.drinking] || lifestyle.drinking)); } if (lifestyle?.kids) { const kidsTexts: { [key: string]: string } = { 'have': 'Есть дети', 'want': 'Хочу детей', 'dont_want': 'Не хочу детей', 'unsure': 'Пока не знаю' }; parts.push('👶 ' + (kidsTexts[lifestyle.kids] || lifestyle.kids)); } return parts.join(', '); } // Просмотр статистики профиля async handleViewStats(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } const stats = await this.profileService.getProfileStats(profile.userId); const statsText = `📊 Статистика вашего профиля\n\n` + `💖 Ваши лайки: ${stats.totalLikes}\n` + `💕 Матчи: ${stats.totalMatches}\n` + `👀 Просмотры профиля: ${stats.profileViews}\n` + `❤️ Лайки получено: ${stats.likesReceived}\n\n` + `💡 Совет: Заполните все поля профиля и добавьте качественные фото для большего успеха!`; const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '👀 Кто смотрел профиль', callback_data: 'view_profile_viewers' }], [{ text: '🔙 Назад', callback_data: 'settings' }] ] }; await this.bot.sendMessage(chatId, statsText, { reply_markup: keyboard }); } catch (error) { console.error('Error viewing stats:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при получении статистики'); } } // Просмотр кто смотрел профиль async handleViewProfileViewers(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } const viewers = await this.profileService.getProfileViewers(profile.userId, 10); if (viewers.length === 0) { await this.bot.sendMessage( chatId, '👁️ Пока никто не просматривал ваш профиль\n\n' + 'Начните искать анкеты, чтобы другие пользователи тоже могли вас найти!', { reply_markup: { inline_keyboard: [ [{ text: '🔍 Начать поиск', callback_data: 'start_browsing' }], [{ text: '🔙 Назад', callback_data: 'settings' }] ] } } ); return; } let viewersText = '👀 Последние просмотры вашего профиля:\n\n'; viewers.forEach((viewer, index) => { viewersText += `${index + 1}. ${viewer.name}, ${viewer.age}\n`; if (viewer.city) viewersText += ` 📍 ${viewer.city}\n`; viewersText += '\n'; }); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '📊 Статистика', callback_data: 'view_stats' }], [{ text: '🔙 Назад', callback_data: 'settings' }] ] }; await this.bot.sendMessage(chatId, viewersText, { reply_markup: keyboard }); } catch (error) { console.error('Error viewing profile viewers:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при получении информации о просмотрах'); } } // Возврат в главное меню async handleMainMenu(chatId: number, telegramId: string): Promise { // Используем существующий метод handleStart из CommandHandlers const profile = await this.profileService.getProfileByTelegramId(telegramId); if (profile) { // Проверяем премиум статус const premiumInfo = await this.vipService.checkPremiumStatus(telegramId); let keyboardRows = [ [ { text: '👤 Мой профиль', callback_data: 'view_my_profile' }, { text: '🔍 Просмотр анкет', callback_data: 'start_browsing' } ], [ { text: '💕 Мои матчи', callback_data: 'view_matches' } ] ]; // Добавляем кнопку VIP поиска если есть премиум, или кнопку "Получить VIP" если нет if (premiumInfo && premiumInfo.isPremium) { keyboardRows[1].push({ text: '⭐ VIP поиск', callback_data: 'vip_search' }); } else { keyboardRows[1].push({ text: '💎 Получить VIP', callback_data: 'get_vip' }); } keyboardRows.push([{ text: '⚙️ Настройки', callback_data: 'settings' }]); const keyboard: InlineKeyboardMarkup = { inline_keyboard: keyboardRows }; await this.bot.sendMessage( chatId, `🎉 Добро пожаловать, ${profile.name}!\n\n` + `💖 Telegram Tinder Bot готов к работе!\n\n` + `Что хотите сделать?`, { reply_markup: keyboard } ); } else { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [{ text: '✨ Создать профиль', callback_data: 'create_profile' }], [{ text: 'ℹ️ Как это работает?', callback_data: 'how_it_works' }] ] }; await this.bot.sendMessage( chatId, `🎉 Добро пожаловать в Telegram Tinder Bot!\n\n` + `💕 Здесь вы можете найти свою вторую половинку!\n\n` + `Для начала создайте свой профиль:`, { reply_markup: keyboard } ); } } // Скрыть/показать профиль async handleHideProfile(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } const newVisibility = !profile.isVisible; await this.profileService.toggleVisibility(profile.userId); const statusText = newVisibility ? 'видимым' : 'скрытым'; const emoji = newVisibility ? '👁️' : '🙈'; await this.bot.sendMessage( chatId, `${emoji} Ваш профиль теперь ${statusText}!\n\n` + (newVisibility ? '✅ Другие пользователи смогут найти вас в поиске' : '🔒 Ваш профиль скрыт и не отображается в поиске'), { reply_markup: { inline_keyboard: [ [{ text: '🔙 Назад к настройкам', callback_data: 'settings' }] ] } } ); } catch (error) { console.error('Error toggling profile visibility:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при изменении видимости профиля'); } } // Удаление профиля async handleDeleteProfile(chatId: number, telegramId: string): Promise { const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '✅ Да, удалить', callback_data: 'confirm_delete_profile' }, { text: '❌ Отмена', callback_data: 'settings' } ] ] }; await this.bot.sendMessage( chatId, '⚠️ ВНИМАНИЕ!\n\n' + '🗑️ Вы действительно хотите удалить свой профиль?\n\n' + '❗ Это действие нельзя отменить:\n' + '• Все ваши фото будут удалены\n' + '• Все матчи и переписки исчезнут\n' + '• Придется создавать профиль заново\n\n' + '🤔 Подумайте еще раз!', { reply_markup: keyboard } ); } // Подтверждение удаления профиля async handleConfirmDeleteProfile(chatId: number, telegramId: string): Promise { try { const profile = await this.profileService.getProfileByTelegramId(telegramId); if (!profile) { await this.bot.sendMessage(chatId, '❌ Профиль не найден'); return; } // Удаляем профиль (это также удалит связанные данные через CASCADE) await this.profileService.deleteProfile(profile.userId); await this.bot.sendMessage( chatId, '💔 Ваш профиль успешно удален\n\n' + '😢 Мы будем скучать! Но вы всегда можете вернуться и создать новый профиль.\n\n' + '👋 До свидания!', { reply_markup: { inline_keyboard: [ [{ text: '✨ Создать новый профиль', callback_data: 'create_profile' }] ] } } ); } catch (error) { console.error('Error deleting profile:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при удалении профиля'); } } // Получение VIP статуса async handleGetVip(chatId: number, telegramId: string): Promise { await this.bot.sendMessage( chatId, '💎 *VIP Статус*\n\n' + '✨ VIP дает вам дополнительные возможности:\n\n' + '🔍 *Расширенный поиск* - найти людей по интересам\n' + '📊 *Подробная статистика* - кто смотрел ваш профиль\n' + '💕 *Больше лайков* - отправляйте до 100 лайков в день\n' + '⭐ *Приоритет в поиске* - ваш профиль показывается первым\n' + '🎯 *Суперлайки* - выделите себя среди других\n\n' + '💰 *Стоимость:*\n' + '• 1 месяц - 299₽\n' + '• 3 месяца - 699₽ (экономия 200₽)\n' + '• 6 месяцев - 1199₽ (экономия 600₽)\n\n' + '📞 Для получения VIP свяжитесь с администратором: @admin', { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: '📞 Связаться с админом', url: 'https://t.me/admin' } ], [ { text: '🔙 Назад', callback_data: 'vip_search' } ] ] } } ); } // VIP лайк async handleVipLike(chatId: number, telegramId: string, targetTelegramId: string): Promise { try { // Получаем user_id по telegram_id для совместимости с существующей логикой const targetUserId = await this.profileService.getUserIdByTelegramId(targetTelegramId); if (!targetUserId) { throw new Error('Target user not found'); } const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'like'); if (result.isMatch) { // Это матч! const targetProfile = await this.profileService.getProfileByUserId(targetUserId); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '💬 Написать сообщение', callback_data: 'chat_' + targetUserId }, { text: '📱 Нативный чат', callback_data: 'open_native_chat_' + result.match?.id } ], [{ text: '🔍 Продолжить VIP поиск', callback_data: 'vip_search' }] ] }; await this.bot.sendMessage( chatId, '🎉 ЭТО МАТЧ! 💕\n\n' + 'Вы понравились друг другу с ' + (targetProfile?.name || 'этим пользователем') + '!\n\n' + 'Теперь вы можете начать общение!', { reply_markup: keyboard } ); } else { await this.bot.sendMessage(chatId, '👍 Лайк отправлен! Продолжайте VIP поиск.'); } } catch (error) { await this.bot.sendMessage(chatId, '❌ Ошибка при отправке лайка'); console.error('VIP Like error:', error); } } // VIP супер-лайк async handleVipSuperlike(chatId: number, telegramId: string, targetTelegramId: string): Promise { try { const targetUserId = await this.profileService.getUserIdByTelegramId(targetTelegramId); if (!targetUserId) { throw new Error('Target user not found'); } const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'superlike'); if (result.isMatch) { const targetProfile = await this.profileService.getProfileByUserId(targetUserId); const keyboard: InlineKeyboardMarkup = { inline_keyboard: [ [ { text: '💬 Написать сообщение', callback_data: 'chat_' + targetUserId }, { text: '📱 Нативный чат', callback_data: 'open_native_chat_' + result.match?.id } ], [{ text: '🔍 Продолжить VIP поиск', callback_data: 'vip_search' }] ] }; await this.bot.sendMessage( chatId, '⭐ СУПЕР МАТЧ! ⭐\n\n' + 'Ваш супер-лайк привел к матчу с ' + (targetProfile?.name || 'этим пользователем') + '!\n\n' + 'Начните общение прямо сейчас!', { reply_markup: keyboard } ); } else { await this.bot.sendMessage(chatId, '⭐ Супер-лайк отправлен! Это повышает ваши шансы.'); } } catch (error) { await this.bot.sendMessage(chatId, '❌ Ошибка при отправке супер-лайка'); console.error('VIP Superlike error:', error); } } // VIP дизлайк async handleVipDislike(chatId: number, telegramId: string, targetTelegramId: string): Promise { try { await this.matchingService.performSwipe(telegramId, targetTelegramId, 'pass'); await this.bot.sendMessage(chatId, '👎 Профиль пропущен. Продолжайте VIP поиск.'); } catch (error) { await this.bot.sendMessage(chatId, '❌ Ошибка при выполнении действия'); console.error('VIP Dislike error:', error); } } // Обработчики языковых настроек async handleLanguageSettings(chatId: number, telegramId: string): Promise { try { const keyboard = this.translationController.getLanguageSelectionKeyboard(); await this.bot.sendMessage( chatId, `🌐 ${t('commands.settings')} - Выбор языка\n\nВыберите язык интерфейса:`, { reply_markup: keyboard } ); } catch (error) { console.error('Language settings error:', error); await this.bot.sendMessage(chatId, t('errors.serverError')); } } async handleSetLanguage(chatId: number, telegramId: string, languageCode: string): Promise { try { const result = await this.translationController.handleLanguageSelection(parseInt(telegramId), languageCode); await this.bot.sendMessage(chatId, result); // Показать обновленное меню настроек setTimeout(() => { this.handleSettings(chatId, telegramId); }, 1000); } catch (error) { console.error('Set language error:', error); await this.bot.sendMessage(chatId, t('errors.serverError')); } } async handleTranslateProfile(chatId: number, telegramId: string, profileUserId: number): Promise { try { // Показать индикатор загрузки await this.bot.sendMessage(chatId, t('translation.translating')); // Получить текущий язык пользователя const userLanguage = 'ru'; // TODO: получить из базы данных const result = await this.translationController.handleProfileTranslation( parseInt(telegramId), profileUserId, userLanguage ); if (result.success && result.translatedProfile) { const formattedProfile = this.translationController.formatTranslatedProfile( result.translatedProfile, 'auto', userLanguage ); await this.bot.sendMessage(chatId, formattedProfile); } else { await this.bot.sendMessage(chatId, result.message); } } catch (error) { console.error('Translate profile error:', error); await this.bot.sendMessage(chatId, t('translation.error')); } } async handleNotificationSettings(chatId: number, telegramId: string): Promise { try { if (this.notificationHandlers) { const userId = await this.profileService.getUserIdByTelegramId(telegramId); if (!userId) { await this.bot.sendMessage(chatId, '❌ Вы не зарегистрированы. Используйте команду /start для регистрации.'); return; } // Вызываем метод из notificationHandlers для получения настроек и отображения меню const settings = await this.notificationHandlers.getNotificationService().getNotificationSettings(userId); await this.notificationHandlers.sendNotificationSettings(chatId, settings); } else { // Если NotificationHandlers недоступен, показываем сообщение об ошибке await this.bot.sendMessage(chatId, '⚙️ Настройки уведомлений временно недоступны. Попробуйте позже.'); await this.handleSettings(chatId, telegramId); } } catch (error) { console.error('Notification settings error:', error); await this.bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке настроек уведомлений. Попробуйте позже.'); } } }