Files
tg_tinder_bot/src/handlers/callbackHandlers.ts
2025-11-24 17:00:20 +09:00

2502 lines
121 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, { 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<string> {
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<void> {
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<string, any>;
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<string, any>;
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<void> {
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<void> {
this.messageHandlers.startProfileCreation(telegramId, gender);
await this.bot.sendMessage(
chatId,
'👍 Отлично!\n\n📝 Теперь напишите ваше имя:'
);
}
// Просмотр собственного профиля
async handleViewMyProfile(chatId: number, telegramId: string): Promise<void> {
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<void> {
await this.profileEditController.showProfileEditMenu(this.bot, chatId, parseInt(telegramId));
}
// Управление фотографиями
async handleManagePhotos(chatId: number, telegramId: string): Promise<void> {
await this.profileEditController.showPhotoManagementMenu(this.bot, chatId, parseInt(telegramId));
}
// Начать просмотр анкет
async handleStartBrowsing(chatId: number, telegramId: string, isNewUser: boolean = false): Promise<void> {
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<void> {
await this.showNextCandidate(chatId, telegramId);
}
// Лайк
async handleLike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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: '<27>🚫 Скрыть профиль', 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<void> {
await this.bot.sendMessage(
chatId,
'🔍 Настройки поиска будут доступны в следующем обновлении!'
);
}
// Настройки уведомлений - реализация перенесена в расширенную версию
// Как это работает
async handleHowItWorks(chatId: number): Promise<void> {
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' +
'<27><> Советы:\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<void> {
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<void> {
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<void> {
await this.profileEditController.showProfilePreview(this.bot, chatId, parseInt(telegramId));
}
// Редактирование имени
async handleEditName(chatId: number, telegramId: string): Promise<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'name');
await this.bot.sendMessage(chatId, '📝 *Введите ваше новое имя:*\n\nНапример: Анна', {
parse_mode: 'Markdown'
});
}
// Редактирование возраста
async handleEditAge(chatId: number, telegramId: string): Promise<void> {
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<void> {
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<void> {
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<void> {
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<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'job');
await this.bot.sendMessage(chatId, '💼 *Введите вашу профессию или место работы:*\n\nНапример: Дизайнер в IT-компании', {
parse_mode: 'Markdown'
});
}
// Редактирование образования
async handleEditEducation(chatId: number, telegramId: string): Promise<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'education');
await this.bot.sendMessage(chatId, '🎓 *Введите ваше образование:*\n\nНапример: МГУ, факультет журналистики', {
parse_mode: 'Markdown'
});
}
// Редактирование роста
async handleEditHeight(chatId: number, telegramId: string): Promise<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'height');
await this.bot.sendMessage(chatId, '📏 *Введите ваш рост в сантиметрах:*\n\nНапример: 175', {
parse_mode: 'Markdown'
});
}
// Редактирование религии
async handleEditReligion(chatId: number, telegramId: string): Promise<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'religion');
await this.bot.sendMessage(chatId, '🕊️ *Введите вашу религию или напишите "нет":*\n\nНапример: православие, ислам, атеизм, нет', {
parse_mode: 'Markdown'
});
}
// Редактирование цели знакомства
async handleEditDatingGoal(chatId: number, telegramId: string): Promise<void> {
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<void> {
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<void> {
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<void> {
this.messageHandlers.setWaitingForInput(parseInt(telegramId), 'photo');
await this.bot.sendMessage(chatId, '📷 *Отправьте фотографию:*\n\nМаксимум 9 фотографий в профиле', {
parse_mode: 'Markdown'
});
}
// Удаление фото
async handleDeletePhoto(chatId: number, telegramId: string): Promise<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
// Используем существующий метод 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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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, '❌ Произошла ошибка при загрузке настроек уведомлений. Попробуйте позже.');
}
}
}