2502 lines
121 KiB
TypeScript
2502 lines
121 KiB
TypeScript
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, '❌ Произошла ошибка при загрузке настроек уведомлений. Попробуйте позже.');
|
||
}
|
||
}
|
||
}
|