feat: add VIP search option and profile editing functionality
- Added a new button for '⭐ VIP поиск' in command handlers.
- Implemented profile editing states and methods in message handlers.
- Enhanced profile model to include hobbies, religion, dating goals, and lifestyle preferences.
- Updated profile service to handle new fields and ensure proper database interactions.
- Introduced a VIP function in matching service to find candidates based on dating goals.
This commit is contained in:
294
src/controllers/profileEditController.ts
Normal file
294
src/controllers/profileEditController.ts
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
import { ProfileService } from '../services/profileService';
|
||||||
|
import { Profile } from '../models/Profile';
|
||||||
|
import TelegramBot from 'node-telegram-bot-api';
|
||||||
|
|
||||||
|
export class ProfileEditController {
|
||||||
|
private profileService: ProfileService;
|
||||||
|
|
||||||
|
constructor(profileService: ProfileService) {
|
||||||
|
this.profileService = profileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Показать главное меню редактирования профиля
|
||||||
|
async showProfileEditMenu(bot: TelegramBot, chatId: number, telegramId: number): Promise<void> {
|
||||||
|
try {
|
||||||
|
const profile = await this.profileService.getProfileByTelegramId(telegramId.toString());
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
await bot.sendMessage(chatId, '❌ Профиль не найден. Сначала создайте профиль.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
[
|
||||||
|
{ text: '📝 Изменить имя', callback_data: 'edit_name' },
|
||||||
|
{ text: '🎂 Изменить возраст', callback_data: 'edit_age' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '📖 Изменить "О себе"', callback_data: 'edit_bio' },
|
||||||
|
{ text: '🎯 Хобби', callback_data: 'edit_hobbies' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '📷 Управление фото', callback_data: 'manage_photos' },
|
||||||
|
{ text: '🏙️ Город', callback_data: 'edit_city' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '💼 Работа', callback_data: 'edit_job' },
|
||||||
|
{ text: '🎓 Образование', callback_data: 'edit_education' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '📏 Рост', callback_data: 'edit_height' },
|
||||||
|
{ text: '🕊️ Религия', callback_data: 'edit_religion' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '💕 Цель знакомства', callback_data: 'edit_dating_goal' },
|
||||||
|
{ text: '🚬 Образ жизни', callback_data: 'edit_lifestyle' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '⚙️ Настройки поиска', callback_data: 'edit_search_preferences' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '👀 Предпросмотр профиля', callback_data: 'preview_profile' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '⬅️ Назад в главное меню', callback_data: 'main_menu' }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const profileText = this.getProfileSummary(profile);
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId,
|
||||||
|
`🛠️ *Редактирование профиля*\n\n${profileText}\n\n*Выберите что хотите изменить:*`,
|
||||||
|
{
|
||||||
|
parse_mode: 'Markdown',
|
||||||
|
reply_markup: keyboard
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error showing profile edit menu:', error);
|
||||||
|
await bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке профиля.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Показать меню управления фотографиями
|
||||||
|
async showPhotoManagementMenu(bot: TelegramBot, chatId: number, telegramId: number): Promise<void> {
|
||||||
|
try {
|
||||||
|
const profile = await this.profileService.getProfileByTelegramId(telegramId.toString());
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
await bot.sendMessage(chatId, '❌ Профиль не найден.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
[
|
||||||
|
{ text: '➕ Добавить фото', callback_data: 'add_photo' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '🗑️ Удалить фото', callback_data: 'delete_photo' },
|
||||||
|
{ text: '⭐ Главное фото', callback_data: 'set_main_photo' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '⬅️ Назад', callback_data: 'edit_profile' }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let photoText = '📷 *Управление фотографиями*\n\n';
|
||||||
|
|
||||||
|
if (profile.photos.length === 0) {
|
||||||
|
photoText += 'У вас пока нет фотографий.\n';
|
||||||
|
} else {
|
||||||
|
photoText += `Количество фото: ${profile.photos.length}/9\n`;
|
||||||
|
if (profile.getMainPhoto()) {
|
||||||
|
photoText += '⭐ Главное фото установлено\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, photoText, {
|
||||||
|
parse_mode: 'Markdown',
|
||||||
|
reply_markup: keyboard
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если есть фото, покажем их
|
||||||
|
if (profile.photos.length > 0) {
|
||||||
|
for (let i = 0; i < Math.min(profile.photos.length, 3); i++) {
|
||||||
|
const caption = i === 0 ? '⭐ Главное фото' : `Фото ${i + 1}`;
|
||||||
|
await bot.sendPhoto(chatId, profile.photos[i], { caption });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error showing photo management menu:', error);
|
||||||
|
await bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке фотографий.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Предпросмотр профиля
|
||||||
|
async showProfilePreview(bot: TelegramBot, chatId: number, telegramId: number): Promise<void> {
|
||||||
|
try {
|
||||||
|
const profile = await this.profileService.getProfileByTelegramId(telegramId.toString());
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
await bot.sendMessage(chatId, '❌ Профиль не найден.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayProfile = profile.getDisplayProfile();
|
||||||
|
let previewText = `👤 *${displayProfile.name}, ${displayProfile.age}*\n\n`;
|
||||||
|
|
||||||
|
if (displayProfile.bio) {
|
||||||
|
previewText += `📖 *О себе:*\n${displayProfile.bio}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.hobbies) {
|
||||||
|
previewText += `🎯 *Хобби:* ${displayProfile.hobbies}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.city) {
|
||||||
|
previewText += `🏙️ *Город:* ${displayProfile.city}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.job) {
|
||||||
|
previewText += `💼 *Работа:* ${displayProfile.job}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.education) {
|
||||||
|
previewText += `🎓 *Образование:* ${displayProfile.education}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.height) {
|
||||||
|
previewText += `📏 *Рост:* ${displayProfile.height} см\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.religion) {
|
||||||
|
previewText += `🕊️ *Религия:* ${displayProfile.religion}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.datingGoal) {
|
||||||
|
const goalText = this.getDatingGoalText(displayProfile.datingGoal);
|
||||||
|
previewText += `💕 *Цель знакомства:* ${goalText}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayProfile.lifestyle) {
|
||||||
|
previewText += `\n🚬 *Образ жизни:*\n`;
|
||||||
|
if (displayProfile.lifestyle.smoking) {
|
||||||
|
previewText += `Курение: ${this.getLifestyleText('smoking', displayProfile.lifestyle.smoking)}\n`;
|
||||||
|
}
|
||||||
|
if (displayProfile.lifestyle.drinking) {
|
||||||
|
previewText += `Алкоголь: ${this.getLifestyleText('drinking', displayProfile.lifestyle.drinking)}\n`;
|
||||||
|
}
|
||||||
|
if (displayProfile.lifestyle.kids) {
|
||||||
|
previewText += `Дети: ${this.getLifestyleText('kids', displayProfile.lifestyle.kids)}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
[
|
||||||
|
{ text: '✏️ Редактировать', callback_data: 'edit_profile' },
|
||||||
|
{ text: '⬅️ Главное меню', callback_data: 'main_menu' }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Отправляем главное фото, если есть
|
||||||
|
if (displayProfile.photos.length > 0) {
|
||||||
|
await bot.sendPhoto(chatId, displayProfile.photos[0], {
|
||||||
|
caption: previewText,
|
||||||
|
parse_mode: 'Markdown',
|
||||||
|
reply_markup: keyboard
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await bot.sendMessage(chatId, previewText + '\n\n⚠️ *Добавьте фотографии для лучшего результата!*', {
|
||||||
|
parse_mode: 'Markdown',
|
||||||
|
reply_markup: keyboard
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error showing profile preview:', error);
|
||||||
|
await bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке предпросмотра.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получить краткую сводку профиля
|
||||||
|
private getProfileSummary(profile: Profile): string {
|
||||||
|
let summary = `👤 *${profile.name}, ${profile.age}*\n`;
|
||||||
|
|
||||||
|
const completeness = this.calculateProfileCompleteness(profile);
|
||||||
|
summary += `📊 Заполненность профиля: ${completeness}%\n\n`;
|
||||||
|
|
||||||
|
summary += `📷 Фото: ${profile.photos.length}/9\n`;
|
||||||
|
summary += `📖 О себе: ${profile.bio ? '✅' : '❌'}\n`;
|
||||||
|
summary += `🎯 Хобби: ${profile.hobbies ? '✅' : '❌'}\n`;
|
||||||
|
summary += `🏙️ Город: ${profile.city ? '✅' : '❌'}\n`;
|
||||||
|
summary += `💼 Работа: ${profile.job ? '✅' : '❌'}\n`;
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рассчитать процент заполненности профиля
|
||||||
|
private calculateProfileCompleteness(profile: Profile): number {
|
||||||
|
const fields = [
|
||||||
|
profile.name,
|
||||||
|
profile.age,
|
||||||
|
profile.photos.length > 0,
|
||||||
|
profile.bio,
|
||||||
|
profile.hobbies,
|
||||||
|
profile.city,
|
||||||
|
profile.job,
|
||||||
|
profile.education,
|
||||||
|
profile.height,
|
||||||
|
profile.religion,
|
||||||
|
profile.datingGoal
|
||||||
|
];
|
||||||
|
|
||||||
|
const filledFields = fields.filter(field =>
|
||||||
|
field !== null && field !== undefined && field !== ''
|
||||||
|
).length;
|
||||||
|
|
||||||
|
return Math.round((filledFields / fields.length) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получить текст цели знакомства
|
||||||
|
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(type: string, value: string): string {
|
||||||
|
const lifestyleTexts: { [key: string]: { [key: string]: string } } = {
|
||||||
|
smoking: {
|
||||||
|
'never': 'Не курю',
|
||||||
|
'sometimes': 'Иногда',
|
||||||
|
'regularly': 'Регулярно'
|
||||||
|
},
|
||||||
|
drinking: {
|
||||||
|
'never': 'Не пью',
|
||||||
|
'sometimes': 'Иногда',
|
||||||
|
'regularly': 'Регулярно'
|
||||||
|
},
|
||||||
|
kids: {
|
||||||
|
'have': 'Есть дети',
|
||||||
|
'want': 'Хочу детей',
|
||||||
|
'dont_want': 'Не хочу детей',
|
||||||
|
'unsure': 'Пока не знаю'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return lifestyleTexts[type]?.[value] || value;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/database/migrations/add_profile_fields.sql
Normal file
40
src/database/migrations/add_profile_fields.sql
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
-- Add new profile fields for enhanced profile management
|
||||||
|
|
||||||
|
-- Add hobbies field
|
||||||
|
ALTER TABLE profiles
|
||||||
|
ADD COLUMN IF NOT EXISTS hobbies TEXT;
|
||||||
|
|
||||||
|
-- Add religion field
|
||||||
|
ALTER TABLE profiles
|
||||||
|
ADD COLUMN IF NOT EXISTS religion VARCHAR(100);
|
||||||
|
|
||||||
|
-- Add dating goal field
|
||||||
|
ALTER TABLE profiles
|
||||||
|
ADD COLUMN IF NOT EXISTS dating_goal VARCHAR(20)
|
||||||
|
CHECK (dating_goal IN ('serious', 'casual', 'friends', 'unsure'));
|
||||||
|
|
||||||
|
-- Add lifestyle preferences
|
||||||
|
ALTER TABLE profiles
|
||||||
|
ADD COLUMN IF NOT EXISTS has_kids VARCHAR(20)
|
||||||
|
CHECK (has_kids IN ('have', 'want', 'dont_want', 'unsure'));
|
||||||
|
|
||||||
|
-- Create indexes for better performance
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_profiles_dating_goal ON profiles(dating_goal);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_profiles_religion ON profiles(religion);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_profiles_has_kids ON profiles(has_kids);
|
||||||
|
|
||||||
|
-- Update updated_at timestamp function
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
-- Apply trigger to profiles table if not exists
|
||||||
|
DROP TRIGGER IF EXISTS update_profiles_updated_at ON profiles;
|
||||||
|
CREATE TRIGGER update_profiles_updated_at
|
||||||
|
BEFORE UPDATE ON profiles
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
46
src/database/migrations/add_scheduled_notifications.sql
Normal file
46
src/database/migrations/add_scheduled_notifications.sql
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
-- Миграция для добавления таблицы запланированных уведомлений
|
||||||
|
-- Версия: 2025-09-12-002
|
||||||
|
|
||||||
|
-- Создание таблицы запланированных уведомлений
|
||||||
|
CREATE TABLE IF NOT EXISTS scheduled_notifications (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
type VARCHAR(50) NOT NULL, -- 'inactivity_reminder', 'likes_summary', 'match_reminder', etc.
|
||||||
|
scheduled_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
sent BOOLEAN DEFAULT FALSE,
|
||||||
|
data JSONB, -- Дополнительные данные для уведомления
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для оптимизации запросов
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_user_id ON scheduled_notifications(user_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_scheduled_at ON scheduled_notifications(scheduled_at) WHERE sent = FALSE;
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_type ON scheduled_notifications(type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_sent ON scheduled_notifications(sent);
|
||||||
|
|
||||||
|
-- Триггер для обновления updated_at
|
||||||
|
CREATE TRIGGER scheduled_notifications_updated_at BEFORE UPDATE ON scheduled_notifications
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||||
|
|
||||||
|
-- Функция для очистки старых уведомлений (старше 30 дней)
|
||||||
|
CREATE OR REPLACE FUNCTION cleanup_old_notifications()
|
||||||
|
RETURNS INTEGER AS $$
|
||||||
|
DECLARE
|
||||||
|
deleted_count INTEGER;
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM scheduled_notifications
|
||||||
|
WHERE sent = TRUE
|
||||||
|
AND created_at < CURRENT_TIMESTAMP - INTERVAL '30 days';
|
||||||
|
|
||||||
|
GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
||||||
|
RETURN deleted_count;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Комментарии к таблице и колонкам
|
||||||
|
COMMENT ON TABLE scheduled_notifications IS 'Таблица для хранения запланированных уведомлений';
|
||||||
|
COMMENT ON COLUMN scheduled_notifications.type IS 'Тип уведомления: inactivity_reminder, likes_summary, match_reminder и т.д.';
|
||||||
|
COMMENT ON COLUMN scheduled_notifications.scheduled_at IS 'Время, когда должно быть отправлено уведомление';
|
||||||
|
COMMENT ON COLUMN scheduled_notifications.sent IS 'Флаг, указывающий было ли отправлено уведомление';
|
||||||
|
COMMENT ON COLUMN scheduled_notifications.data IS 'JSON данные для кастомизации уведомления';
|
||||||
@@ -4,6 +4,7 @@ import { MatchingService } from '../services/matchingService';
|
|||||||
import { ChatService } from '../services/chatService';
|
import { ChatService } from '../services/chatService';
|
||||||
import { Profile } from '../models/Profile';
|
import { Profile } from '../models/Profile';
|
||||||
import { MessageHandlers } from './messageHandlers';
|
import { MessageHandlers } from './messageHandlers';
|
||||||
|
import { ProfileEditController } from '../controllers/profileEditController';
|
||||||
|
|
||||||
export class CallbackHandlers {
|
export class CallbackHandlers {
|
||||||
private bot: TelegramBot;
|
private bot: TelegramBot;
|
||||||
@@ -11,6 +12,7 @@ export class CallbackHandlers {
|
|||||||
private matchingService: MatchingService;
|
private matchingService: MatchingService;
|
||||||
private chatService: ChatService;
|
private chatService: ChatService;
|
||||||
private messageHandlers: MessageHandlers;
|
private messageHandlers: MessageHandlers;
|
||||||
|
private profileEditController: ProfileEditController;
|
||||||
|
|
||||||
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
|
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
|
||||||
this.bot = bot;
|
this.bot = bot;
|
||||||
@@ -18,6 +20,7 @@ export class CallbackHandlers {
|
|||||||
this.matchingService = new MatchingService();
|
this.matchingService = new MatchingService();
|
||||||
this.chatService = new ChatService();
|
this.chatService = new ChatService();
|
||||||
this.messageHandlers = messageHandlers;
|
this.messageHandlers = messageHandlers;
|
||||||
|
this.profileEditController = new ProfileEditController(this.profileService);
|
||||||
}
|
}
|
||||||
|
|
||||||
register(): void {
|
register(): void {
|
||||||
@@ -44,11 +47,91 @@ export class CallbackHandlers {
|
|||||||
await this.handleEditProfile(chatId, telegramId);
|
await this.handleEditProfile(chatId, telegramId);
|
||||||
} else if (data === 'manage_photos') {
|
} else if (data === 'manage_photos') {
|
||||||
await this.handleManagePhotos(chatId, telegramId);
|
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 === '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') {
|
else if (data === 'start_browsing') {
|
||||||
await this.handleStartBrowsing(chatId, telegramId);
|
await this.handleStartBrowsing(chatId, telegramId);
|
||||||
|
} 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') {
|
} else if (data === 'next_candidate') {
|
||||||
await this.handleNextCandidate(chatId, telegramId);
|
await this.handleNextCandidate(chatId, telegramId);
|
||||||
} else if (data.startsWith('like_')) {
|
} else if (data.startsWith('like_')) {
|
||||||
@@ -167,52 +250,12 @@ export class CallbackHandlers {
|
|||||||
|
|
||||||
// Редактирование профиля
|
// Редактирование профиля
|
||||||
async handleEditProfile(chatId: number, telegramId: string): Promise<void> {
|
async handleEditProfile(chatId: number, telegramId: string): Promise<void> {
|
||||||
const keyboard: InlineKeyboardMarkup = {
|
await this.profileEditController.showProfileEditMenu(this.bot, chatId, parseInt(telegramId));
|
||||||
inline_keyboard: [
|
|
||||||
[
|
|
||||||
{ text: '📝 Имя', callback_data: 'edit_name' },
|
|
||||||
{ text: '📅 Возраст', callback_data: 'edit_age' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '📍 Город', callback_data: 'edit_city' },
|
|
||||||
{ text: '💼 Работа', callback_data: 'edit_job' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '📖 О себе', callback_data: 'edit_bio' },
|
|
||||||
{ text: '🎯 Интересы', callback_data: 'edit_interests' }
|
|
||||||
],
|
|
||||||
[{ text: '👈 Назад к профилю', callback_data: 'view_my_profile' }]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.bot.sendMessage(
|
|
||||||
chatId,
|
|
||||||
'✏️ Что хотите изменить в профиле?',
|
|
||||||
{ reply_markup: keyboard }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Управление фотографиями
|
// Управление фотографиями
|
||||||
async handleManagePhotos(chatId: number, telegramId: string): Promise<void> {
|
async handleManagePhotos(chatId: number, telegramId: string): Promise<void> {
|
||||||
const keyboard: InlineKeyboardMarkup = {
|
await this.profileEditController.showPhotoManagementMenu(this.bot, chatId, parseInt(telegramId));
|
||||||
inline_keyboard: [
|
|
||||||
[
|
|
||||||
{ text: '📷 Добавить фото', callback_data: 'add_photo' },
|
|
||||||
{ text: '🗑 Удалить фото', callback_data: 'delete_photo' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '⭐ Сделать главным', callback_data: 'set_main_photo' },
|
|
||||||
{ text: '🔄 Изменить порядок', callback_data: 'reorder_photos' }
|
|
||||||
],
|
|
||||||
[{ text: '👈 Назад к профилю', callback_data: 'view_my_profile' }]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.bot.sendMessage(
|
|
||||||
chatId,
|
|
||||||
'📸 Управление фотографиями\n\nВыберите действие:',
|
|
||||||
{ reply_markup: keyboard }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Начать просмотр анкет
|
// Начать просмотр анкет
|
||||||
@@ -669,10 +712,33 @@ export class CallbackHandlers {
|
|||||||
if (profile.job) profileText += '💼 ' + profile.job + '\n';
|
if (profile.job) profileText += '💼 ' + profile.job + '\n';
|
||||||
if (profile.education) profileText += '🎓 ' + profile.education + '\n';
|
if (profile.education) profileText += '🎓 ' + profile.education + '\n';
|
||||||
if (profile.height) profileText += '📏 ' + profile.height + ' см\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';
|
profileText += '\n📝 ' + (profile.bio || 'Описание не указано') + '\n';
|
||||||
|
|
||||||
|
// Хобби с хэштегами
|
||||||
|
if (profile.hobbies && profile.hobbies.trim()) {
|
||||||
|
const hobbiesArray = profile.hobbies.split(',').map(hobby => hobby.trim()).filter(hobby => hobby);
|
||||||
|
const formattedHobbies = hobbiesArray.map(hobby => '#' + hobby).join(' ');
|
||||||
|
profileText += '\n🎯 ' + formattedHobbies + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
if (profile.interests.length > 0) {
|
if (profile.interests.length > 0) {
|
||||||
profileText += '\n🎯 Интересы: ' + profile.interests.join(', ');
|
profileText += '\n<EFBFBD> Интересы: ' + profile.interests.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyboard: InlineKeyboardMarkup;
|
let keyboard: InlineKeyboardMarkup;
|
||||||
@@ -752,12 +818,17 @@ export class CallbackHandlers {
|
|||||||
candidateText += '📍 ' + (candidate.city || 'Не указан') + '\n';
|
candidateText += '📍 ' + (candidate.city || 'Не указан') + '\n';
|
||||||
if (candidate.job) candidateText += '💼 ' + candidate.job + '\n';
|
if (candidate.job) candidateText += '💼 ' + candidate.job + '\n';
|
||||||
if (candidate.education) candidateText += '🎓 ' + candidate.education + '\n';
|
if (candidate.education) candidateText += '🎓 ' + candidate.education + '\n';
|
||||||
if (candidate.height) candidateText += '<EFBFBD><EFBFBD> ' + candidate.height + ' см\n';
|
if (candidate.height) candidateText += '📏 ' + candidate.height + ' см\n';
|
||||||
|
if (candidate.religion) candidateText += '🕊️ ' + candidate.religion + '\n';
|
||||||
candidateText += '\n📝 ' + (candidate.bio || 'Описание отсутствует') + '\n';
|
candidateText += '\n📝 ' + (candidate.bio || 'Описание отсутствует') + '\n';
|
||||||
|
|
||||||
if (candidate.interests.length > 0) {
|
if (candidate.interests.length > 0) {
|
||||||
candidateText += '\n🎯 Интересы: ' + candidate.interests.join(', ');
|
candidateText += '\n🎯 Интересы: ' + candidate.interests.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidate.hobbies) {
|
||||||
|
candidateText += '\n🎮 Хобби: ' + candidate.hobbies;
|
||||||
|
}
|
||||||
|
|
||||||
const keyboard: InlineKeyboardMarkup = {
|
const keyboard: InlineKeyboardMarkup = {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
@@ -799,4 +870,711 @@ export class CallbackHandlers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== НОВЫЕ МЕТОДЫ ДЛЯ РЕДАКТИРОВАНИЯ ПРОФИЛЯ =====
|
||||||
|
|
||||||
|
// Предпросмотр профиля
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Редактирование работы
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lifestyle = profile.lifestyle || {};
|
||||||
|
lifestyle[type as keyof typeof lifestyle] = value as any;
|
||||||
|
|
||||||
|
await this.profileService.updateProfile(profile.userId, {
|
||||||
|
lifestyle: lifestyle
|
||||||
|
});
|
||||||
|
|
||||||
|
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.isPremium) {
|
||||||
|
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 && candidate.hobbies.trim()) {
|
||||||
|
const hobbiesArray = candidate.hobbies.split(',').map(hobby => hobby.trim()).filter(hobby => hobby);
|
||||||
|
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(', ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ export class CommandHandlers {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ text: '💕 Мои матчи', callback_data: 'view_matches' },
|
{ text: '💕 Мои матчи', callback_data: 'view_matches' },
|
||||||
|
{ text: '⭐ VIP поиск', callback_data: 'vip_search' }
|
||||||
|
],
|
||||||
|
[
|
||||||
{ text: '⚙️ Настройки', callback_data: 'settings' }
|
{ text: '⚙️ Настройки', callback_data: 'settings' }
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,12 +14,19 @@ interface ChatState {
|
|||||||
matchId: string;
|
matchId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Состояния пользователей для редактирования профиля
|
||||||
|
interface ProfileEditState {
|
||||||
|
waitingForInput: boolean;
|
||||||
|
field: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class MessageHandlers {
|
export class MessageHandlers {
|
||||||
private bot: TelegramBot;
|
private bot: TelegramBot;
|
||||||
private profileService: ProfileService;
|
private profileService: ProfileService;
|
||||||
private chatService: ChatService;
|
private chatService: ChatService;
|
||||||
private userStates: Map<string, UserState> = new Map();
|
private userStates: Map<string, UserState> = new Map();
|
||||||
private chatStates: Map<string, ChatState> = new Map();
|
private chatStates: Map<string, ChatState> = new Map();
|
||||||
|
private profileEditStates: Map<string, ProfileEditState> = new Map();
|
||||||
|
|
||||||
constructor(bot: TelegramBot) {
|
constructor(bot: TelegramBot) {
|
||||||
this.bot = bot;
|
this.bot = bot;
|
||||||
@@ -42,6 +49,7 @@ export class MessageHandlers {
|
|||||||
|
|
||||||
const userState = this.userStates.get(userId);
|
const userState = this.userStates.get(userId);
|
||||||
const chatState = this.chatStates.get(userId);
|
const chatState = this.chatStates.get(userId);
|
||||||
|
const profileEditState = this.profileEditStates.get(userId);
|
||||||
|
|
||||||
// Если пользователь в процессе отправки сообщения в чат
|
// Если пользователь в процессе отправки сообщения в чат
|
||||||
if (chatState?.waitingForMessage && msg.text) {
|
if (chatState?.waitingForMessage && msg.text) {
|
||||||
@@ -49,6 +57,12 @@ export class MessageHandlers {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если пользователь редактирует профиль
|
||||||
|
if (profileEditState?.waitingForInput) {
|
||||||
|
await this.handleProfileEdit(msg, userId, profileEditState.field);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Если пользователь в процессе создания профиля
|
// Если пользователь в процессе создания профиля
|
||||||
if (userState) {
|
if (userState) {
|
||||||
await this.handleProfileCreation(msg, userId, userState);
|
await this.handleProfileCreation(msg, userId, userState);
|
||||||
@@ -312,4 +326,206 @@ export class MessageHandlers {
|
|||||||
await this.bot.sendMessage(msg.chat.id, '❌ Не удалось отправить сообщение. Попробуйте еще раз.');
|
await this.bot.sendMessage(msg.chat.id, '❌ Не удалось отправить сообщение. Попробуйте еще раз.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== МЕТОДЫ ДЛЯ РЕДАКТИРОВАНИЯ ПРОФИЛЯ =====
|
||||||
|
|
||||||
|
// Установить состояние ожидания ввода для редактирования профиля
|
||||||
|
setWaitingForInput(telegramId: number, field: string): void {
|
||||||
|
this.profileEditStates.set(telegramId.toString(), {
|
||||||
|
waitingForInput: true,
|
||||||
|
field: field
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очистить состояние редактирования профиля
|
||||||
|
clearProfileEditState(userId: string): void {
|
||||||
|
this.profileEditStates.delete(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка редактирования профиля
|
||||||
|
async handleProfileEdit(msg: Message, userId: string, field: string): Promise<void> {
|
||||||
|
const chatId = msg.chat.id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let value: any = msg.text;
|
||||||
|
let isValid = true;
|
||||||
|
let errorMessage = '';
|
||||||
|
|
||||||
|
// Валидация в зависимости от поля
|
||||||
|
switch (field) {
|
||||||
|
case 'name':
|
||||||
|
if (!value || value.length < 2 || value.length > 50) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Имя должно быть от 2 до 50 символов';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'age':
|
||||||
|
const age = parseInt(value);
|
||||||
|
if (isNaN(age) || age < 18 || age > 100) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Возраст должен быть числом от 18 до 100';
|
||||||
|
}
|
||||||
|
value = age;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bio':
|
||||||
|
if (value && value.length > 500) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Описание не должно превышать 500 символов';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'height':
|
||||||
|
const height = parseInt(value);
|
||||||
|
if (isNaN(height) || height < 100 || height > 250) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Рост должен быть числом от 100 до 250 см';
|
||||||
|
}
|
||||||
|
value = height;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'photo':
|
||||||
|
if (!msg.photo || !msg.photo.length) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Отправьте фотографию';
|
||||||
|
} else {
|
||||||
|
// Берём фото наибольшего размера
|
||||||
|
value = msg.photo[msg.photo.length - 1].file_id;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'age_range':
|
||||||
|
const ageRangeParts = value.split('-');
|
||||||
|
if (ageRangeParts.length !== 2) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Неверный формат. Используйте: минимальный-максимальный возраст (например: 18-35)';
|
||||||
|
} else {
|
||||||
|
const minAge = parseInt(ageRangeParts[0]);
|
||||||
|
const maxAge = parseInt(ageRangeParts[1]);
|
||||||
|
if (isNaN(minAge) || isNaN(maxAge) || minAge < 18 || maxAge > 100 || minAge >= maxAge) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Возраст должен быть от 18 до 100, минимальный меньше максимального';
|
||||||
|
}
|
||||||
|
value = { minAge, maxAge };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'distance':
|
||||||
|
const distance = parseInt(value);
|
||||||
|
if (isNaN(distance) || distance < 1 || distance > 1000) {
|
||||||
|
isValid = false;
|
||||||
|
errorMessage = 'Расстояние должно быть числом от 1 до 1000 км';
|
||||||
|
}
|
||||||
|
value = distance;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
await this.bot.sendMessage(chatId, `❌ ${errorMessage}\n\nПопробуйте еще раз:`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем профиль
|
||||||
|
await this.updateProfileField(userId, field, value);
|
||||||
|
|
||||||
|
// Очищаем состояние
|
||||||
|
this.clearProfileEditState(userId);
|
||||||
|
|
||||||
|
// Отправляем подтверждение и возвращаем к меню редактирования
|
||||||
|
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' }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.bot.sendMessage(
|
||||||
|
chatId,
|
||||||
|
'🛠️ Что дальше?',
|
||||||
|
{ reply_markup: keyboard }
|
||||||
|
);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error handling profile edit:', error);
|
||||||
|
await this.bot.sendMessage(chatId, '❌ Произошла ошибка. Попробуйте еще раз.');
|
||||||
|
this.clearProfileEditState(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление поля профиля
|
||||||
|
async updateProfileField(userId: string, field: string, value: any): Promise<void> {
|
||||||
|
const profile = await this.profileService.getProfileByTelegramId(userId);
|
||||||
|
if (!profile) {
|
||||||
|
throw new Error('Profile not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates: any = {};
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'name':
|
||||||
|
updates.name = value;
|
||||||
|
break;
|
||||||
|
case 'age':
|
||||||
|
updates.age = value;
|
||||||
|
break;
|
||||||
|
case 'bio':
|
||||||
|
updates.bio = value;
|
||||||
|
break;
|
||||||
|
case 'hobbies':
|
||||||
|
updates.hobbies = value;
|
||||||
|
break;
|
||||||
|
case 'city':
|
||||||
|
// В БД поле называется 'location', но мы используем city в модели
|
||||||
|
updates.city = value;
|
||||||
|
break;
|
||||||
|
case 'job':
|
||||||
|
// В БД поле называется 'occupation', но мы используем job в модели
|
||||||
|
updates.job = value;
|
||||||
|
break;
|
||||||
|
case 'education':
|
||||||
|
updates.education = value;
|
||||||
|
break;
|
||||||
|
case 'height':
|
||||||
|
updates.height = value;
|
||||||
|
break;
|
||||||
|
case 'religion':
|
||||||
|
updates.religion = value === 'нет' ? null : value;
|
||||||
|
break;
|
||||||
|
case 'age_range':
|
||||||
|
updates.searchPreferences = {
|
||||||
|
minAge: value.minAge,
|
||||||
|
maxAge: value.maxAge,
|
||||||
|
maxDistance: profile.searchPreferences?.maxDistance || 50
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'distance':
|
||||||
|
updates.searchPreferences = {
|
||||||
|
minAge: profile.searchPreferences?.minAge || 18,
|
||||||
|
maxAge: profile.searchPreferences?.maxAge || 50,
|
||||||
|
maxDistance: value
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'photo':
|
||||||
|
// Добавляем фото к существующим
|
||||||
|
profile.addPhoto(value);
|
||||||
|
await this.profileService.updateProfile(profile.userId, {
|
||||||
|
photos: profile.photos
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updates).length > 0) {
|
||||||
|
await this.profileService.updateProfile(profile.userId, updates);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,18 @@ export interface ProfileData {
|
|||||||
bio?: string;
|
bio?: string;
|
||||||
photos: string[]; // Просто массив file_id
|
photos: string[]; // Просто массив file_id
|
||||||
interests: string[];
|
interests: string[];
|
||||||
|
hobbies?: string; // Хобби через запятую
|
||||||
city?: string;
|
city?: string;
|
||||||
education?: string;
|
education?: string;
|
||||||
job?: string;
|
job?: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
religion?: string;
|
||||||
|
datingGoal?: 'serious' | 'casual' | 'friends' | 'unsure' | 'one_night' | 'fwb' | 'marriage_abroad' | 'sugar' | 'polyamory';
|
||||||
|
lifestyle?: {
|
||||||
|
smoking?: 'never' | 'sometimes' | 'regularly';
|
||||||
|
drinking?: 'never' | 'sometimes' | 'regularly';
|
||||||
|
kids?: 'have' | 'want' | 'dont_want' | 'unsure';
|
||||||
|
};
|
||||||
location?: {
|
location?: {
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
@@ -35,10 +43,18 @@ export class Profile {
|
|||||||
bio?: string;
|
bio?: string;
|
||||||
photos: string[];
|
photos: string[];
|
||||||
interests: string[];
|
interests: string[];
|
||||||
|
hobbies?: string;
|
||||||
city?: string;
|
city?: string;
|
||||||
education?: string;
|
education?: string;
|
||||||
job?: string;
|
job?: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
religion?: string;
|
||||||
|
datingGoal?: 'serious' | 'casual' | 'friends' | 'unsure' | 'one_night' | 'fwb' | 'marriage_abroad' | 'sugar' | 'polyamory';
|
||||||
|
lifestyle?: {
|
||||||
|
smoking?: 'never' | 'sometimes' | 'regularly';
|
||||||
|
drinking?: 'never' | 'sometimes' | 'regularly';
|
||||||
|
kids?: 'have' | 'want' | 'dont_want' | 'unsure';
|
||||||
|
};
|
||||||
location?: {
|
location?: {
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
@@ -62,10 +78,14 @@ export class Profile {
|
|||||||
this.bio = data.bio;
|
this.bio = data.bio;
|
||||||
this.photos = data.photos || [];
|
this.photos = data.photos || [];
|
||||||
this.interests = data.interests || [];
|
this.interests = data.interests || [];
|
||||||
|
this.hobbies = data.hobbies;
|
||||||
this.city = data.city;
|
this.city = data.city;
|
||||||
this.education = data.education;
|
this.education = data.education;
|
||||||
this.job = data.job;
|
this.job = data.job;
|
||||||
this.height = data.height;
|
this.height = data.height;
|
||||||
|
this.religion = data.religion;
|
||||||
|
this.datingGoal = data.datingGoal;
|
||||||
|
this.lifestyle = data.lifestyle;
|
||||||
this.location = data.location;
|
this.location = data.location;
|
||||||
this.searchPreferences = data.searchPreferences || {
|
this.searchPreferences = data.searchPreferences || {
|
||||||
minAge: 18,
|
minAge: 18,
|
||||||
@@ -109,6 +129,21 @@ export class Profile {
|
|||||||
return this.photos[0];
|
return this.photos[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получить хобби как хэштеги
|
||||||
|
getFormattedHobbies(): string {
|
||||||
|
if (!this.hobbies) return '';
|
||||||
|
return this.hobbies
|
||||||
|
.split(',')
|
||||||
|
.map(hobby => `#${hobby.trim()}`)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Установить хобби из строки
|
||||||
|
setHobbies(hobbiesString: string): void {
|
||||||
|
this.hobbies = hobbiesString;
|
||||||
|
this.updatedAt = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
// Получить профиль для показа
|
// Получить профиль для показа
|
||||||
getDisplayProfile() {
|
getDisplayProfile() {
|
||||||
return {
|
return {
|
||||||
@@ -118,10 +153,14 @@ export class Profile {
|
|||||||
bio: this.bio,
|
bio: this.bio,
|
||||||
photos: this.photos,
|
photos: this.photos,
|
||||||
interests: this.interests,
|
interests: this.interests,
|
||||||
|
hobbies: this.getFormattedHobbies(),
|
||||||
city: this.city,
|
city: this.city,
|
||||||
education: this.education,
|
education: this.education,
|
||||||
job: this.job,
|
job: this.job,
|
||||||
height: this.height,
|
height: this.height,
|
||||||
|
religion: this.religion,
|
||||||
|
datingGoal: this.datingGoal,
|
||||||
|
lifestyle: this.lifestyle,
|
||||||
isVerified: this.isVerified
|
isVerified: this.isVerified
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -381,4 +381,34 @@ export class MatchingService {
|
|||||||
// Используем ProfileService для правильного маппинга данных
|
// Используем ProfileService для правильного маппинга данных
|
||||||
return this.profileService.mapEntityToProfile(candidateData);
|
return this.profileService.mapEntityToProfile(candidateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VIP функция: поиск кандидатов по цели знакомства
|
||||||
|
async getCandidatesWithGoal(userProfile: Profile, targetGoal: string): Promise<Profile[]> {
|
||||||
|
const swipedUsersResult = await query(`
|
||||||
|
SELECT swiped_id
|
||||||
|
FROM swipes
|
||||||
|
WHERE swiper_id = $1
|
||||||
|
`, [userProfile.userId]);
|
||||||
|
|
||||||
|
const swipedUserIds = swipedUsersResult.rows.map((row: any) => row.swiped_id);
|
||||||
|
swipedUserIds.push(userProfile.userId); // Исключаем себя
|
||||||
|
|
||||||
|
let candidateQuery = `
|
||||||
|
SELECT DISTINCT p.*, u.telegram_id, u.username, u.first_name, u.last_name
|
||||||
|
FROM profiles p
|
||||||
|
JOIN users u ON p.user_id = u.id
|
||||||
|
WHERE p.is_visible = true
|
||||||
|
AND p.is_active = true
|
||||||
|
AND p.gender = $1
|
||||||
|
AND p.dating_goal = $2
|
||||||
|
AND p.user_id NOT IN (${swipedUserIds.map((_: any, i: number) => `$${i + 3}`).join(', ')})
|
||||||
|
ORDER BY p.created_at DESC
|
||||||
|
LIMIT 50
|
||||||
|
`;
|
||||||
|
|
||||||
|
const params = [userProfile.interestedIn, targetGoal, ...swipedUserIds];
|
||||||
|
const result = await query(candidateQuery, params);
|
||||||
|
|
||||||
|
return result.rows.map((row: any) => this.profileService.mapEntityToProfile(row));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,13 +50,15 @@ export class ProfileService {
|
|||||||
await query(`
|
await query(`
|
||||||
INSERT INTO profiles (
|
INSERT INTO profiles (
|
||||||
id, user_id, name, age, gender, looking_for, bio, photos, interests,
|
id, user_id, name, age, gender, looking_for, bio, photos, interests,
|
||||||
location, education, occupation, height, latitude, longitude,
|
hobbies, location, education, occupation, height, religion, dating_goal,
|
||||||
verification_status, is_active, is_visible, created_at, updated_at
|
latitude, longitude, verification_status, is_active, is_visible,
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
|
created_at, updated_at
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)
|
||||||
`, [
|
`, [
|
||||||
profileId, userId, profile.name, profile.age, profile.gender, profile.interestedIn,
|
profileId, userId, profile.name, profile.age, profile.gender, profile.interestedIn,
|
||||||
profile.bio, profile.photos, profile.interests,
|
profile.bio, profile.photos, profile.interests, profile.hobbies,
|
||||||
profile.city, profile.education, profile.job, profile.height,
|
profile.city, profile.education, profile.job, profile.height,
|
||||||
|
profile.religion, profile.datingGoal,
|
||||||
profile.location?.latitude, profile.location?.longitude,
|
profile.location?.latitude, profile.location?.longitude,
|
||||||
'unverified', true, profile.isVisible, profile.createdAt, profile.updatedAt
|
'unverified', true, profile.isVisible, profile.createdAt, profile.updatedAt
|
||||||
]);
|
]);
|
||||||
@@ -106,6 +108,15 @@ export class ProfileService {
|
|||||||
return result.rows[0].id;
|
return result.rows[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получение пользователя по Telegram ID
|
||||||
|
async getUserByTelegramId(telegramId: string): Promise<any | null> {
|
||||||
|
const result = await query(`
|
||||||
|
SELECT * FROM users WHERE telegram_id = $1
|
||||||
|
`, [parseInt(telegramId)]);
|
||||||
|
|
||||||
|
return result.rows.length > 0 ? result.rows[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Создание пользователя если не существует
|
// Создание пользователя если не существует
|
||||||
async ensureUser(telegramId: string, userData: any): Promise<string> {
|
async ensureUser(telegramId: string, userData: any): Promise<string> {
|
||||||
// Используем UPSERT для избежания дублирования
|
// Используем UPSERT для избежания дублирования
|
||||||
@@ -146,7 +157,7 @@ export class ProfileService {
|
|||||||
|
|
||||||
// Строим динамический запрос обновления
|
// Строим динамический запрос обновления
|
||||||
Object.entries(updates).forEach(([key, value]) => {
|
Object.entries(updates).forEach(([key, value]) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined && key !== 'updatedAt') { // Исключаем updatedAt из цикла
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'photos':
|
case 'photos':
|
||||||
case 'interests':
|
case 'interests':
|
||||||
@@ -175,6 +186,7 @@ export class ProfileService {
|
|||||||
return existingProfile;
|
return existingProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Всегда добавляем updated_at в конце
|
||||||
updateFields.push(`updated_at = $${paramIndex++}`);
|
updateFields.push(`updated_at = $${paramIndex++}`);
|
||||||
updateValues.push(new Date());
|
updateValues.push(new Date());
|
||||||
updateValues.push(userId);
|
updateValues.push(userId);
|
||||||
@@ -409,10 +421,18 @@ export class ProfileService {
|
|||||||
bio: entity.bio,
|
bio: entity.bio,
|
||||||
photos: parsePostgresArray(entity.photos),
|
photos: parsePostgresArray(entity.photos),
|
||||||
interests: parsePostgresArray(entity.interests),
|
interests: parsePostgresArray(entity.interests),
|
||||||
|
hobbies: entity.hobbies,
|
||||||
city: entity.location || entity.city,
|
city: entity.location || entity.city,
|
||||||
education: entity.education,
|
education: entity.education,
|
||||||
job: entity.occupation || entity.job,
|
job: entity.occupation || entity.job,
|
||||||
height: entity.height,
|
height: entity.height,
|
||||||
|
religion: entity.religion,
|
||||||
|
datingGoal: entity.dating_goal,
|
||||||
|
lifestyle: {
|
||||||
|
smoking: entity.smoking,
|
||||||
|
drinking: entity.drinking,
|
||||||
|
kids: entity.has_kids
|
||||||
|
},
|
||||||
location: entity.latitude && entity.longitude ? {
|
location: entity.latitude && entity.longitude ? {
|
||||||
latitude: entity.latitude,
|
latitude: entity.latitude,
|
||||||
longitude: entity.longitude
|
longitude: entity.longitude
|
||||||
@@ -431,6 +451,18 @@ export class ProfileService {
|
|||||||
|
|
||||||
// Преобразование camelCase в snake_case
|
// Преобразование camelCase в snake_case
|
||||||
private camelToSnake(str: string): string {
|
private camelToSnake(str: string): string {
|
||||||
|
// Специальные случаи для некоторых полей
|
||||||
|
const specialCases: { [key: string]: string } = {
|
||||||
|
'interestedIn': 'looking_for',
|
||||||
|
'job': 'occupation',
|
||||||
|
'city': 'location',
|
||||||
|
'datingGoal': 'dating_goal'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (specialCases[str]) {
|
||||||
|
return specialCases[str];
|
||||||
|
}
|
||||||
|
|
||||||
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user