alpha-test

This commit is contained in:
2025-09-18 13:46:35 +09:00
parent 85027a7747
commit 5ea3e8c1f3
27 changed files with 5887 additions and 174 deletions

View File

@@ -8,6 +8,8 @@ import LocalizationService from './services/localizationService';
import { CommandHandlers } from './handlers/commandHandlers';
import { CallbackHandlers } from './handlers/callbackHandlers';
import { MessageHandlers } from './handlers/messageHandlers';
import { NotificationHandlers } from './handlers/notificationHandlers';
class TelegramTinderBot {
private bot: TelegramBot;
@@ -18,7 +20,7 @@ class TelegramTinderBot {
private commandHandlers: CommandHandlers;
private callbackHandlers: CallbackHandlers;
private messageHandlers: MessageHandlers;
private notificationHandlers: NotificationHandlers;
constructor() {
const token = process.env.TELEGRAM_BOT_TOKEN;
if (!token) {
@@ -34,6 +36,7 @@ class TelegramTinderBot {
this.commandHandlers = new CommandHandlers(this.bot);
this.messageHandlers = new MessageHandlers(this.bot);
this.callbackHandlers = new CallbackHandlers(this.bot, this.messageHandlers);
this.notificationHandlers = new NotificationHandlers(this.bot);
this.setupErrorHandling();
this.setupPeriodicTasks();
@@ -78,6 +81,7 @@ class TelegramTinderBot {
{ command: 'browse', description: '💕 Смотреть анкеты' },
{ command: 'matches', description: '💖 Мои матчи' },
{ command: 'settings', description: '⚙️ Настройки' },
{ command: 'notifications', description: '🔔 Настройки уведомлений' },
{ command: 'help', description: '❓ Помощь' }
];
@@ -94,6 +98,9 @@ class TelegramTinderBot {
// Сообщения
this.messageHandlers.register();
// Обработчики уведомлений
this.notificationHandlers.register();
}
// Обработка ошибок
@@ -137,14 +144,31 @@ class TelegramTinderBot {
}
}, 5 * 60 * 1000);
// Очистка старых данных каждый день
// Планирование периодических уведомлений раз в день в 00:05
setInterval(async () => {
try {
await this.cleanupOldData();
const now = new Date();
if (now.getHours() === 0 && now.getMinutes() >= 5 && now.getMinutes() < 10) {
console.log('🔔 Scheduling periodic notifications...');
await this.notificationService.schedulePeriodicNotifications();
}
} catch (error) {
console.error('Error scheduling periodic notifications:', error);
}
}, 5 * 60 * 1000);
// Очистка старых данных каждый день в 03:00
setInterval(async () => {
try {
const now = new Date();
if (now.getHours() === 3 && now.getMinutes() < 5) {
console.log('🧹 Running scheduled cleanup...');
await this.cleanupOldData();
}
} catch (error) {
console.error('Error cleaning up old data:', error);
}
}, 24 * 60 * 60 * 1000);
}, 5 * 60 * 1000);
}
// Очистка старых данных
@@ -152,11 +176,18 @@ class TelegramTinderBot {
console.log('🧹 Running cleanup tasks...');
try {
// Очистка старых уведомлений (старше 30 дней)
const notificationsResult = await query(`
// Очистка старых запланированных уведомлений (старше 30 дней или обработанных)
const scheduledNotificationsResult = await query(`
DELETE FROM scheduled_notifications
WHERE processed = true
AND created_at < CURRENT_TIMESTAMP - INTERVAL '30 days'
WHERE (processed = true AND created_at < CURRENT_TIMESTAMP - INTERVAL '30 days')
OR (scheduled_at < CURRENT_TIMESTAMP - INTERVAL '7 days')
`);
console.log(`🗑️ Cleaned up ${scheduledNotificationsResult.rowCount} old scheduled notifications`);
// Очистка старых уведомлений (старше 90 дней)
const notificationsResult = await query(`
DELETE FROM notifications
WHERE created_at < CURRENT_TIMESTAMP - INTERVAL '90 days'
`);
console.log(`🗑️ Cleaned up ${notificationsResult.rowCount} old notifications`);
@@ -186,7 +217,7 @@ class TelegramTinderBot {
console.log(`💬 Cleaned up ${messagesResult.rowCount} old messages`);
// Обновление статистики таблиц после очистки
await query('VACUUM ANALYZE scheduled_notifications, profile_views, swipes, messages');
await query('VACUUM ANALYZE notifications, scheduled_notifications, profile_views, swipes, messages');
console.log('✅ Cleanup completed successfully');
} catch (error) {
@@ -229,4 +260,4 @@ if (require.main === module) {
});
}
export { TelegramTinderBot };
export { TelegramTinderBot };

View File

@@ -4,6 +4,7 @@ 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';
@@ -23,6 +24,7 @@ export class CallbackHandlers {
private vipController: VipController;
private vipService: VipService;
private translationController: TranslationController;
private notificationHandlers?: NotificationHandlers;
private likeBackHandler: LikeBackHandler;
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
@@ -36,6 +38,12 @@ export class CallbackHandlers {
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);
}
@@ -272,6 +280,41 @@ export class CallbackHandlers {
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 {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция настройки уведомлений недоступна.',
show_alert: true
});
}
}
else {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция в разработке!',
@@ -870,13 +913,7 @@ export class CallbackHandlers {
);
}
// Настройки уведомлений
async handleNotificationSettings(chatId: number, telegramId: string): Promise<void> {
await this.bot.sendMessage(
chatId,
'🔔 Настройки уведомлений будут доступны в следующем обновлении!'
);
}
// Настройки уведомлений - реализация перенесена в расширенную версию
// Как это работает
async handleHowItWorks(chatId: number): Promise<void> {
@@ -1578,7 +1615,7 @@ export class CallbackHandlers {
try {
// Проверяем VIP статус пользователя
const user = await this.profileService.getUserByTelegramId(telegramId);
if (!user || !user.isPremium) {
if (!user || !user.premium) { // Изменено с isPremium на premium, чтобы соответствовать названию колонки в базе данных
const keyboard = {
inline_keyboard: [
[
@@ -2240,4 +2277,27 @@ export class CallbackHandlers {
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, '❌ Произошла ошибка при загрузке настроек уведомлений. Попробуйте позже.');
}
}
}

View File

@@ -0,0 +1,606 @@
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 { 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';
import { NotificationHandlers } from './notificationHandlers';
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 likeBackHandler: LikeBackHandler;
private notificationHandlers?: NotificationHandlers;
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
this.bot = bot;
this.profileService = new ProfileService();
this.matchingService = new MatchingService();
this.chatService = new ChatService();
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();
this.likeBackHandler = new LikeBackHandler(bot);
// Создаем экземпляр NotificationHandlers
try {
this.notificationHandlers = new NotificationHandlers(bot);
} catch (error) {
console.error('Failed to initialize NotificationHandlers:', error);
}
}
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 === '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 === '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 === '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 {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция настройки уведомлений недоступна.',
show_alert: true
});
}
}
else {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция в разработке!',
show_alert: false
});
return;
}
await this.bot.answerCallbackQuery(query.id);
} catch (error) {
console.error('Callback handler error:', error);
await this.bot.answerCallbackQuery(query.id, {
text: 'Произошла ошибка. Попробуйте еще раз.',
show_alert: true
});
}
}
// Добавим все необходимые методы для обработки коллбэков
async handleCreateProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleGenderSelection(chatId: number, telegramId: string, gender: string): Promise<void> {
// Заглушка метода
}
async handleViewMyProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleManagePhotos(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handlePreviewProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditName(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditAge(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditBio(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditHobbies(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditCity(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditJob(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditEducation(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditHeight(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditReligion(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDatingGoal(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditLifestyle(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditSearchPreferences(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleAddPhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeletePhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetMainPhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeletePhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise<void> {
// Заглушка метода
}
async handleSetMainPhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise<void> {
// Заглушка метода
}
async handleSetDatingGoal(chatId: number, telegramId: string, goal: string): Promise<void> {
// Заглушка метода
}
async handleEditSmoking(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDrinking(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditKids(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetLifestyle(chatId: number, telegramId: string, type: string, value: string): Promise<void> {
// Заглушка метода
}
async handleEditAgeRange(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDistance(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleStartBrowsing(chatId: number, telegramId: string, showAll: boolean = false): Promise<void> {
// Заглушка метода
}
async handleVipSearch(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSearchByGoal(chatId: number, telegramId: string, goal: string): Promise<void> {
// Заглушка метода
}
async handleNextCandidate(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleLike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleDislike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleSuperlike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleViewProfile(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleMorePhotos(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleViewMatches(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleOpenChats(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleOpenChat(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleSendMessage(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleViewChatProfile(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleUnmatch(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleConfirmUnmatch(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSearchSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleViewStats(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleViewProfileViewers(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleHideProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeleteProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleMainMenu(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleConfirmDeleteProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleHowItWorks(chatId: number): Promise<void> {
// Заглушка метода
}
async handleVipLike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleVipSuperlike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleVipDislike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleLanguageSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetLanguage(chatId: number, telegramId: string, languageCode: string): Promise<void> {
// Заглушка метода
}
async handleTranslateProfile(chatId: number, telegramId: string, profileUserId: number): Promise<void> {
// Заглушка метода
}
// Добавим новый метод для настроек уведомлений (вызывает NotificationHandlers)
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('Error handling notification settings:', error);
await this.bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке настроек уведомлений.');
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,606 @@
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 { 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';
import { NotificationHandlers } from './notificationHandlers';
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 likeBackHandler: LikeBackHandler;
private notificationHandlers?: NotificationHandlers;
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
this.bot = bot;
this.profileService = new ProfileService();
this.matchingService = new MatchingService();
this.chatService = new ChatService();
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();
this.likeBackHandler = new LikeBackHandler(bot);
// Создаем экземпляр NotificationHandlers
try {
this.notificationHandlers = new NotificationHandlers(bot);
} catch (error) {
console.error('Failed to initialize NotificationHandlers:', error);
}
}
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 === '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 === '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 === '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 {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция настройки уведомлений недоступна.',
show_alert: true
});
}
}
else {
await this.bot.answerCallbackQuery(query.id, {
text: 'Функция в разработке!',
show_alert: false
});
return;
}
await this.bot.answerCallbackQuery(query.id);
} catch (error) {
console.error('Callback handler error:', error);
await this.bot.answerCallbackQuery(query.id, {
text: 'Произошла ошибка. Попробуйте еще раз.',
show_alert: true
});
}
}
// Добавим все необходимые методы для обработки коллбэков
async handleCreateProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleGenderSelection(chatId: number, telegramId: string, gender: string): Promise<void> {
// Заглушка метода
}
async handleViewMyProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleManagePhotos(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handlePreviewProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditName(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditAge(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditBio(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditHobbies(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditCity(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditJob(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditEducation(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditHeight(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditReligion(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDatingGoal(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditLifestyle(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditSearchPreferences(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleAddPhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeletePhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetMainPhoto(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeletePhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise<void> {
// Заглушка метода
}
async handleSetMainPhotoByIndex(chatId: number, telegramId: string, photoIndex: number): Promise<void> {
// Заглушка метода
}
async handleSetDatingGoal(chatId: number, telegramId: string, goal: string): Promise<void> {
// Заглушка метода
}
async handleEditSmoking(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDrinking(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditKids(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetLifestyle(chatId: number, telegramId: string, type: string, value: string): Promise<void> {
// Заглушка метода
}
async handleEditAgeRange(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleEditDistance(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleStartBrowsing(chatId: number, telegramId: string, showAll: boolean = false): Promise<void> {
// Заглушка метода
}
async handleVipSearch(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSearchByGoal(chatId: number, telegramId: string, goal: string): Promise<void> {
// Заглушка метода
}
async handleNextCandidate(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleLike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleDislike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleSuperlike(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleViewProfile(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleMorePhotos(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
// Заглушка метода
}
async handleViewMatches(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleOpenChats(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleOpenChat(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleSendMessage(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleViewChatProfile(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleUnmatch(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleConfirmUnmatch(chatId: number, telegramId: string, matchId: string): Promise<void> {
// Заглушка метода
}
async handleSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSearchSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleViewStats(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleViewProfileViewers(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleHideProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleDeleteProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleMainMenu(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleConfirmDeleteProfile(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleHowItWorks(chatId: number): Promise<void> {
// Заглушка метода
}
async handleVipLike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleVipSuperlike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleVipDislike(chatId: number, telegramId: string, targetTelegramId: string): Promise<void> {
// Заглушка метода
}
async handleLanguageSettings(chatId: number, telegramId: string): Promise<void> {
// Заглушка метода
}
async handleSetLanguage(chatId: number, telegramId: string, languageCode: string): Promise<void> {
// Заглушка метода
}
async handleTranslateProfile(chatId: number, telegramId: string, profileUserId: number): Promise<void> {
// Заглушка метода
}
// Добавим новый метод для настроек уведомлений (вызывает NotificationHandlers)
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('Error handling notification settings:', error);
await this.bot.sendMessage(chatId, '❌ Произошла ошибка при загрузке настроек уведомлений.');
}
}
}

View File

@@ -3,16 +3,19 @@ import { ProfileService } from '../services/profileService';
import { MatchingService } from '../services/matchingService';
import { Profile } from '../models/Profile';
import { getUserTranslation } from '../services/localizationService';
import { NotificationHandlers } from './notificationHandlers';
export class CommandHandlers {
private bot: TelegramBot;
private profileService: ProfileService;
private matchingService: MatchingService;
private notificationHandlers: NotificationHandlers;
constructor(bot: TelegramBot) {
this.bot = bot;
this.profileService = new ProfileService();
this.matchingService = new MatchingService();
this.notificationHandlers = new NotificationHandlers(bot);
}
register(): void {
@@ -23,6 +26,12 @@ export class CommandHandlers {
this.bot.onText(/\/matches/, (msg: Message) => this.handleMatches(msg));
this.bot.onText(/\/settings/, (msg: Message) => this.handleSettings(msg));
this.bot.onText(/\/create_profile/, (msg: Message) => this.handleCreateProfile(msg));
// Регистрация обработчика настроек уведомлений
this.bot.onText(/\/notifications/, (msg: Message) => this.notificationHandlers.handleNotificationsCommand(msg));
// Регистрируем обработчики для уведомлений
this.notificationHandlers.register();
}
async handleStart(msg: Message): Promise<void> {
@@ -44,7 +53,8 @@ export class CommandHandlers {
{ text: '⭐ VIP поиск', callback_data: 'vip_search' }
],
[
{ text: '⚙️ Настройки', callback_data: 'settings' }
{ text: '⚙️ Настройки', callback_data: 'settings' },
{ text: '🔔 Уведомления', callback_data: 'notifications' }
]
]
};
@@ -84,6 +94,7 @@ export class CommandHandlers {
/browse - Просмотр анкет
/matches - Ваши матчи
/settings - Настройки
/notifications - Настройки уведомлений
/help - Эта справка
<EFBFBD> Как использовать:
@@ -191,7 +202,7 @@ export class CommandHandlers {
inline_keyboard: [
[
{ text: '🔍 Настройки поиска', callback_data: 'search_settings' },
{ text: '🔔 Уведомления', callback_data: 'notification_settings' }
{ text: '🔔 Уведомления', callback_data: 'notifications' }
],
[
{ text: '🚫 Скрыть профиль', callback_data: 'hide_profile' },
@@ -242,7 +253,10 @@ export class CommandHandlers {
{ text: '✏️ Редактировать', callback_data: 'edit_profile' },
{ text: '📸 Фото', callback_data: 'manage_photos' }
],
[{ text: '🔍 Начать поиск', callback_data: 'start_browsing' }]
[
{ text: '🔍 Начать поиск', callback_data: 'start_browsing' },
{ text: '🔔 Уведомления', callback_data: 'notifications' }
]
]
} : {
inline_keyboard: [

View File

@@ -0,0 +1,644 @@
import TelegramBot from 'node-telegram-bot-api';
import { v4 as uuidv4 } from 'uuid';
import { query } from '../database/connection';
import { NotificationService } from '../services/notificationService';
interface NotificationSettings {
newMatches: boolean;
newMessages: boolean;
newLikes: boolean;
reminders: boolean;
dailySummary: boolean;
timePreference: 'morning' | 'afternoon' | 'evening' | 'night';
doNotDisturb: boolean;
doNotDisturbStart?: string;
doNotDisturbEnd?: string;
}
export class NotificationHandlers {
private bot: TelegramBot;
private notificationService: NotificationService;
constructor(bot: TelegramBot) {
this.bot = bot;
this.notificationService = new NotificationService(bot);
}
// Метод для получения экземпляра сервиса уведомлений
getNotificationService(): NotificationService {
return this.notificationService;
}
// Обработка команды /notifications
async handleNotificationsCommand(msg: TelegramBot.Message): Promise<void> {
const telegramId = msg.from?.id.toString();
if (!telegramId) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.sendMessage(msg.chat.id, '❌ Вы не зарегистрированы. Используйте команду /start для регистрации.');
return;
}
const settings = await this.notificationService.getNotificationSettings(userId);
await this.sendNotificationSettings(msg.chat.id, settings as NotificationSettings);
} catch (error) {
console.error('Error handling notifications command:', error);
await this.bot.sendMessage(msg.chat.id, '❌ Произошла ошибка при загрузке настроек уведомлений.');
}
}
// Отправка меню настроек уведомлений
async sendNotificationSettings(chatId: number, settings: NotificationSettings): Promise<void> {
const message = `
🔔 *Настройки уведомлений*
Выберите, какие уведомления вы хотите получать:
${settings.newMatches ? '✅' : '❌'} Новые матчи
${settings.newMessages ? '✅' : '❌'} Новые сообщения
${settings.newLikes ? '✅' : '❌'} Новые лайки
${settings.reminders ? '✅' : '❌'} Напоминания
${settings.dailySummary ? '✅' : '❌'} Ежедневные сводки
⏰ Предпочтительное время: ${this.getTimePreferenceText(settings.timePreference)}
${settings.doNotDisturb ? '🔕' : '🔔'} Режим "Не беспокоить": ${settings.doNotDisturb ? 'Включен' : 'Выключен'}
${settings.doNotDisturb && settings.doNotDisturbStart && settings.doNotDisturbEnd ?
`с ${settings.doNotDisturbStart} до ${settings.doNotDisturbEnd}` : ''}
Нажмите на кнопку, чтобы изменить настройку:
`;
await this.bot.sendMessage(chatId, message, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: `${settings.newMatches ? '✅' : '❌'} Новые матчи`, callback_data: 'notif_toggle:newMatches' },
{ text: `${settings.newMessages ? '✅' : '❌'} Новые сообщения`, callback_data: 'notif_toggle:newMessages' }
],
[
{ text: `${settings.newLikes ? '✅' : '❌'} Новые лайки`, callback_data: 'notif_toggle:newLikes' },
{ text: `${settings.reminders ? '✅' : '❌'} Напоминания`, callback_data: 'notif_toggle:reminders' }
],
[
{ text: `${settings.dailySummary ? '✅' : '❌'} Ежедневные сводки`, callback_data: 'notif_toggle:dailySummary' }
],
[
{ text: `⏰ Время: ${this.getTimePreferenceText(settings.timePreference)}`, callback_data: 'notif_time' }
],
[
{ text: `${settings.doNotDisturb ? '🔕' : '🔔'} Режим "Не беспокоить"`, callback_data: 'notif_dnd' }
],
[
{ text: '↩️ Назад', callback_data: 'settings' }
]
]
}
});
}
// Обработка переключения настройки уведомления
async handleNotificationToggle(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
const telegramId = callbackQuery.from?.id.toString();
if (!telegramId || !callbackQuery.message) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Вы не зарегистрированы.' });
return;
}
// notif_toggle:settingName
const settingName = callbackQuery.data?.split(':')[1];
if (!settingName) return;
const settings = await this.notificationService.getNotificationSettings(userId);
let updatedSettings: Partial<NotificationSettings> = { ...settings };
// Инвертируем значение настройки
if (settingName in updatedSettings) {
switch(settingName) {
case 'newMatches':
updatedSettings.newMatches = !updatedSettings.newMatches;
break;
case 'newMessages':
updatedSettings.newMessages = !updatedSettings.newMessages;
break;
case 'newLikes':
updatedSettings.newLikes = !updatedSettings.newLikes;
break;
case 'reminders':
updatedSettings.reminders = !updatedSettings.reminders;
break;
case 'dailySummary':
updatedSettings.dailySummary = !updatedSettings.dailySummary;
break;
}
}
// Обновляем настройки
await this.notificationService.updateNotificationSettings(userId, updatedSettings);
// Отправляем обновленные настройки
await this.bot.answerCallbackQuery(callbackQuery.id, {
text: `✅ Настройка "${this.getSettingName(settingName)}" ${updatedSettings[settingName as keyof NotificationSettings] ? 'включена' : 'отключена'}`
});
await this.sendNotificationSettings(callbackQuery.message.chat.id, updatedSettings as NotificationSettings);
} catch (error) {
console.error('Error handling notification toggle:', error);
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Произошла ошибка при обновлении настроек.' });
}
}
// Обработка выбора времени для уведомлений
async handleTimePreference(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
if (!callbackQuery.message) return;
await this.bot.editMessageText('⏰ *Выберите предпочтительное время для уведомлений:*', {
chat_id: callbackQuery.message.chat.id,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: '🌅 Утро (9:00)', callback_data: 'notif_time_set:morning' },
{ text: '☀️ День (13:00)', callback_data: 'notif_time_set:afternoon' }
],
[
{ text: '🌆 Вечер (19:00)', callback_data: 'notif_time_set:evening' },
{ text: '🌙 Ночь (22:00)', callback_data: 'notif_time_set:night' }
],
[
{ text: '↩️ Назад', callback_data: 'notifications' }
]
]
}
});
await this.bot.answerCallbackQuery(callbackQuery.id);
}
// Обработка установки времени для уведомлений
async handleTimePreferenceSet(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
const telegramId = callbackQuery.from?.id.toString();
if (!telegramId || !callbackQuery.message) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Вы не зарегистрированы.' });
return;
}
// notif_time_set:timePreference
const timePreference = callbackQuery.data?.split(':')[1] as 'morning' | 'afternoon' | 'evening' | 'night';
if (!timePreference) return;
const settings = await this.notificationService.getNotificationSettings(userId);
// Копируем существующие настройки и обновляем нужные поля
const existingSettings = settings as NotificationSettings;
const updatedSettings: NotificationSettings = {
...existingSettings,
timePreference
};
// Обновляем настройки
await this.notificationService.updateNotificationSettings(userId, updatedSettings);
// Отправляем обновленные настройки
await this.bot.answerCallbackQuery(callbackQuery.id, {
text: `✅ Время уведомлений установлено на ${this.getTimePreferenceText(timePreference)}`
});
await this.sendNotificationSettings(callbackQuery.message.chat.id, updatedSettings);
} catch (error) {
console.error('Error handling time preference set:', error);
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Произошла ошибка при обновлении времени уведомлений.' });
}
}
// Обработка режима "Не беспокоить"
async handleDndMode(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
if (!callbackQuery.message) return;
await this.bot.editMessageText('🔕 *Режим "Не беспокоить"*\n\nВыберите действие:', {
chat_id: callbackQuery.message.chat.id,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: '✅ Включить', callback_data: 'notif_dnd_set:on' },
{ text: '❌ Выключить', callback_data: 'notif_dnd_set:off' }
],
[
{ text: '⏰ Настроить время', callback_data: 'notif_dnd_time' }
],
[
{ text: '↩️ Назад', callback_data: 'notifications' }
]
]
}
});
await this.bot.answerCallbackQuery(callbackQuery.id);
}
// Обработка установки режима "Не беспокоить"
async handleDndModeSet(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
const telegramId = callbackQuery.from?.id.toString();
if (!telegramId || !callbackQuery.message) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Вы не зарегистрированы.' });
return;
}
// notif_dnd_set:on/off
const mode = callbackQuery.data?.split(':')[1];
if (!mode) return;
const settings = await this.notificationService.getNotificationSettings(userId);
// Копируем существующие настройки и обновляем нужное поле
const existingSettings = settings as NotificationSettings;
let updatedSettings: NotificationSettings = {
...existingSettings,
doNotDisturb: mode === 'on'
};
// Если включаем режим "Не беспокоить", но не задано время, ставим дефолтные значения
if (mode === 'on' && (!updatedSettings.doNotDisturbStart || !updatedSettings.doNotDisturbEnd)) {
updatedSettings.doNotDisturbStart = '23:00';
updatedSettings.doNotDisturbEnd = '08:00';
}
// Обновляем настройки
await this.notificationService.updateNotificationSettings(userId, updatedSettings);
// Отправляем обновленные настройки
await this.bot.answerCallbackQuery(callbackQuery.id, {
text: `✅ Режим "Не беспокоить" ${mode === 'on' ? 'включен' : 'выключен'}`
});
await this.sendNotificationSettings(callbackQuery.message.chat.id, updatedSettings);
} catch (error) {
console.error('Error handling DND mode set:', error);
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Произошла ошибка при обновлении режима "Не беспокоить".' });
}
}
// Настройка времени для режима "Не беспокоить"
async handleDndTimeSetup(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
if (!callbackQuery.message) return;
await this.bot.editMessageText('⏰ *Настройка времени для режима "Не беспокоить"*\n\nВыберите один из предустановленных вариантов или введите свой:', {
chat_id: callbackQuery.message.chat.id,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: '🌙 23:00 - 08:00', callback_data: 'notif_dnd_time_set:23:00:08:00' }
],
[
{ text: '🌙 22:00 - 07:00', callback_data: 'notif_dnd_time_set:22:00:07:00' }
],
[
{ text: '🌙 00:00 - 09:00', callback_data: 'notif_dnd_time_set:00:00:09:00' }
],
[
{ text: '✏️ Ввести свой вариант', callback_data: 'notif_dnd_time_custom' }
],
[
{ text: '↩️ Назад', callback_data: 'notif_dnd' }
]
]
}
});
await this.bot.answerCallbackQuery(callbackQuery.id);
}
// Установка предустановленного времени для режима "Не беспокоить"
async handleDndTimeSet(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
const telegramId = callbackQuery.from?.id.toString();
if (!telegramId || !callbackQuery.message) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Вы не зарегистрированы.' });
return;
}
// notif_dnd_time_set:startTime:endTime
const parts = callbackQuery.data?.split(':');
if (parts && parts.length >= 4) {
const startTime = `${parts[2]}:${parts[3]}`;
const endTime = `${parts[4]}:${parts[5]}`;
const settings = await this.notificationService.getNotificationSettings(userId);
// Копируем существующие настройки и обновляем нужные поля
const existingSettings = settings as NotificationSettings;
const updatedSettings: NotificationSettings = {
...existingSettings,
doNotDisturb: true,
doNotDisturbStart: startTime,
doNotDisturbEnd: endTime
};
// Обновляем настройки
await this.notificationService.updateNotificationSettings(userId, updatedSettings);
// Отправляем обновленные настройки
await this.bot.answerCallbackQuery(callbackQuery.id, {
text: `✅ Время "Не беспокоить" установлено с ${startTime} до ${endTime}`
});
await this.sendNotificationSettings(callbackQuery.message.chat.id, updatedSettings);
}
} catch (error) {
console.error('Error handling DND time set:', error);
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Произошла ошибка при настройке времени "Не беспокоить".' });
}
}
// Запрос пользовательского времени для режима "Не беспокоить"
async handleDndTimeCustom(callbackQuery: TelegramBot.CallbackQuery): Promise<void> {
if (!callbackQuery.message) return;
// Устанавливаем ожидание пользовательского ввода
const userId = callbackQuery.from?.id.toString();
if (userId) {
await this.setUserState(userId, 'waiting_dnd_time');
}
await this.bot.editMessageText('⏰ *Введите время для режима "Не беспокоить"*\n\nУкажите время в формате:\n`с [ЧЧ:ММ] до [ЧЧ:ММ]`\n\nНапример: `с 23:30 до 07:00`', {
chat_id: callbackQuery.message.chat.id,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: '↩️ Отмена', callback_data: 'notif_dnd_time' }
]
]
}
});
await this.bot.answerCallbackQuery(callbackQuery.id);
}
// Обработка пользовательского ввода времени для режима "Не беспокоить"
async handleDndTimeInput(msg: TelegramBot.Message): Promise<void> {
const telegramId = msg.from?.id.toString();
if (!telegramId) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.sendMessage(msg.chat.id, '❌ Вы не зарегистрированы. Используйте команду /start для регистрации.');
return;
}
// Очищаем состояние ожидания
await this.clearUserState(telegramId);
// Парсим введенное время
const timeRegex = /с\s+(\d{1,2}[:\.]\d{2})\s+до\s+(\d{1,2}[:\.]\d{2})/i;
const match = msg.text?.match(timeRegex);
if (match && match.length >= 3) {
let startTime = match[1].replace('.', ':');
let endTime = match[2].replace('.', ':');
// Проверяем и форматируем время
if (this.isValidTime(startTime) && this.isValidTime(endTime)) {
startTime = this.formatTime(startTime);
endTime = this.formatTime(endTime);
const settings = await this.notificationService.getNotificationSettings(userId);
// Копируем существующие настройки и обновляем нужные поля
const existingSettings = settings as NotificationSettings;
const updatedSettings: NotificationSettings = {
...existingSettings,
doNotDisturb: true,
doNotDisturbStart: startTime,
doNotDisturbEnd: endTime
};
// Обновляем настройки
await this.notificationService.updateNotificationSettings(userId, updatedSettings);
await this.bot.sendMessage(msg.chat.id, `✅ Время "Не беспокоить" установлено с ${startTime} до ${endTime}`);
await this.sendNotificationSettings(msg.chat.id, updatedSettings);
} else {
await this.bot.sendMessage(msg.chat.id, '❌ Неверный формат времени. Пожалуйста, используйте формат ЧЧ:ММ (например, 23:30).');
}
} else {
await this.bot.sendMessage(msg.chat.id, '❌ Неверный формат ввода. Пожалуйста, введите время в формате "с [ЧЧ:ММ] до [ЧЧ:ММ]" (например, "с 23:30 до 07:00").');
}
} catch (error) {
console.error('Error handling DND time input:', error);
await this.bot.sendMessage(msg.chat.id, '❌ Произошла ошибка при настройке времени "Не беспокоить".');
}
}
// Проверка валидности времени
private isValidTime(time: string): boolean {
const regex = /^(\d{1,2}):(\d{2})$/;
const match = time.match(regex);
if (match) {
const hours = parseInt(match[1]);
const minutes = parseInt(match[2]);
return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59;
}
return false;
}
// Форматирование времени в формат ЧЧ:ММ
private formatTime(time: string): string {
const [hours, minutes] = time.split(':').map(Number);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}
// Получение текстового представления времени
private getTimePreferenceText(preference: string): string {
switch (preference) {
case 'morning': return 'Утро (9:00)';
case 'afternoon': return 'День (13:00)';
case 'evening': return 'Вечер (19:00)';
case 'night': return 'Ночь (22:00)';
default: return 'Вечер (19:00)';
}
}
// Получение названия настройки
private getSettingName(setting: string): string {
switch (setting) {
case 'newMatches': return 'Новые матчи';
case 'newMessages': return 'Новые сообщения';
case 'newLikes': return 'Новые лайки';
case 'reminders': return 'Напоминания';
case 'dailySummary': return 'Ежедневные сводки';
default: return setting;
}
}
// Получение ID пользователя по Telegram ID
private async getUserIdByTelegramId(telegramId: string): Promise<string | null> {
try {
const result = await query(
'SELECT id FROM users WHERE telegram_id = $1',
[parseInt(telegramId)]
);
return result.rows.length > 0 ? result.rows[0].id : null;
} catch (error) {
console.error('Error getting user by telegram ID:', error);
return null;
}
}
// Установка состояния ожидания пользователя
private async setUserState(telegramId: string, state: string): Promise<void> {
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) return;
// Сначала проверяем, существуют ли столбцы state и state_data
const checkColumnResult = await query(`
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'state'
`);
if (checkColumnResult.rows.length === 0) {
console.log('Adding state and state_data columns to users table...');
// Добавляем столбцы, если их нет
await query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS state VARCHAR(255) NULL;
ALTER TABLE users ADD COLUMN IF NOT EXISTS state_data JSONB DEFAULT '{}'::jsonb;
`);
}
// Теперь устанавливаем состояние
await query(
`UPDATE users
SET state = $1,
state_data = jsonb_set(COALESCE(state_data, '{}'::jsonb), '{timestamp}', to_jsonb(NOW()))
WHERE telegram_id = $2`,
[state, parseInt(telegramId)]
);
} catch (error) {
console.error('Error setting user state:', error);
}
}
// Очистка состояния ожидания пользователя
private async clearUserState(telegramId: string): Promise<void> {
try {
await query(
'UPDATE users SET state = NULL WHERE telegram_id = $1',
[parseInt(telegramId)]
);
} catch (error) {
console.error('Error clearing user state:', error);
}
}
// Регистрация обработчиков уведомлений
register(): void {
// Команда настройки уведомлений
this.bot.onText(/\/notifications/, this.handleNotificationsCommand.bind(this));
// Обработчик для кнопки настроек уведомлений в меню настроек
this.bot.on('callback_query', async (callbackQuery) => {
if (callbackQuery.data === 'notifications') {
const telegramId = callbackQuery.from?.id.toString();
if (!telegramId || !callbackQuery.message) return;
try {
const userId = await this.getUserIdByTelegramId(telegramId);
if (!userId) {
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Вы не зарегистрированы.' });
return;
}
const settings = await this.notificationService.getNotificationSettings(userId);
await this.sendNotificationSettings(callbackQuery.message.chat.id, settings as NotificationSettings);
await this.bot.answerCallbackQuery(callbackQuery.id);
} catch (error) {
console.error('Error handling notifications callback:', error);
await this.bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Произошла ошибка при загрузке настроек уведомлений.' });
}
}
else if (callbackQuery.data?.startsWith('notif_toggle:')) {
await this.handleNotificationToggle(callbackQuery);
}
else if (callbackQuery.data === 'notif_time') {
await this.handleTimePreference(callbackQuery);
}
else if (callbackQuery.data?.startsWith('notif_time_set:')) {
await this.handleTimePreferenceSet(callbackQuery);
}
else if (callbackQuery.data === 'notif_dnd') {
await this.handleDndMode(callbackQuery);
}
else if (callbackQuery.data?.startsWith('notif_dnd_set:')) {
await this.handleDndModeSet(callbackQuery);
}
else if (callbackQuery.data === 'notif_dnd_time') {
await this.handleDndTimeSetup(callbackQuery);
}
else if (callbackQuery.data?.startsWith('notif_dnd_time_set:')) {
await this.handleDndTimeSet(callbackQuery);
}
else if (callbackQuery.data === 'notif_dnd_time_custom') {
await this.handleDndTimeCustom(callbackQuery);
}
});
// Обработчик пользовательского ввода для времени "Не беспокоить"
this.bot.on('message', async (msg) => {
if (!msg.text) return;
const telegramId = msg.from?.id.toString();
if (!telegramId) return;
try {
// Сначала проверяем, существует ли столбец state
const checkColumnResult = await query(`
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'state'
`);
if (checkColumnResult.rows.length === 0) {
console.log('State column does not exist in users table. Skipping state check.');
return;
}
// Теперь проверяем состояние пользователя
const result = await query(
'SELECT state FROM users WHERE telegram_id = $1',
[parseInt(telegramId)]
);
if (result.rows.length > 0 && result.rows[0].state === 'waiting_dnd_time') {
await this.handleDndTimeInput(msg);
}
} catch (error) {
console.error('Error checking user state:', error);
}
});
}
}

View File

@@ -1,9 +1,33 @@
import { query } from '../database/connection';
import { query, testConnection } from '../database/connection';
async function setAllUsersToPremium() {
try {
console.log('Setting premium status for all users...');
// Проверка соединения с базой данных
console.log('Testing database connection...');
const dbConnected = await testConnection();
if (!dbConnected) {
throw new Error('Failed to connect to database. Please check your database settings.');
}
console.log('Database connection successful!');
// Проверка наличия столбца premium
const checkResult = await query(`
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users'
AND column_name = 'premium'
);
`);
if (!checkResult.rows[0].exists) {
console.log('Adding premium column to users table...');
await query(`ALTER TABLE users ADD COLUMN premium BOOLEAN DEFAULT false;`);
console.log('Premium column added successfully');
}
const result = await query(`
UPDATE users
SET premium = true

View File

@@ -564,7 +564,7 @@ export class MatchingService {
FROM profiles p
JOIN users u ON p.user_id = u.id
WHERE p.is_visible = true
AND p.is_active = true
AND u.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(', ')})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -24,9 +24,9 @@ export class VipService {
// Проверить премиум статус пользователя
async checkPremiumStatus(telegramId: string): Promise<PremiumInfo> {
try {
// Проверяем существование пользователя
// Проверяем существование пользователя и получаем его премиум статус
const result = await query(`
SELECT id
SELECT id, premium
FROM users
WHERE telegram_id = $1
`, [telegramId]);
@@ -35,12 +35,13 @@ export class VipService {
throw new BotError('User not found', 'USER_NOT_FOUND', 404);
}
// Временно возвращаем false для всех пользователей, так как колонки premium нет
// В будущем, когда колонки будут добавлены, этот код нужно будет заменить обратно
// Получаем актуальное значение премиум статуса из базы данных
const isPremium = result.rows[0].premium || false;
return {
isPremium: false,
expiresAt: undefined,
daysLeft: undefined
isPremium: isPremium,
expiresAt: undefined, // Пока не используем дату истечения
daysLeft: undefined // Пока не используем количество дней
};
} catch (error) {
console.error('Error checking premium status:', error);
@@ -51,9 +52,17 @@ export class VipService {
// Добавить премиум статус
async addPremium(telegramId: string, durationDays: number = 30): Promise<void> {
try {
// Временно заглушка, так как колонок premium и premium_expires_at нет
console.log(`[VIP] Попытка добавить премиум для ${telegramId} на ${durationDays} дней`);
// TODO: Добавить колонки premium и premium_expires_at в таблицу users
console.log(`[VIP] Добавление премиум для ${telegramId} на ${durationDays} дней`);
// Обновляем статус premium в базе данных
await query(`
UPDATE users
SET premium = true
WHERE telegram_id = $1
RETURNING id, telegram_id, premium
`, [telegramId]);
console.log(`[VIP] Премиум успешно добавлен для пользователя ${telegramId}`);
} catch (error) {
console.error('Error adding premium:', error);
throw error;
@@ -63,9 +72,17 @@ export class VipService {
// Удалить премиум статус
async removePremium(telegramId: string): Promise<void> {
try {
// Временно заглушка, так как колонок premium и premium_expires_at нет
console.log(`[VIP] Попытка удалить премиум для ${telegramId}`);
// TODO: Добавить колонки premium и premium_expires_at в таблицу users
console.log(`[VIP] Удаление премиум для ${telegramId}`);
// Обновляем статус premium в базе данных
await query(`
UPDATE users
SET premium = false
WHERE telegram_id = $1
RETURNING id, telegram_id, premium
`, [telegramId]);
console.log(`[VIP] Премиум успешно удален для пользователя ${telegramId}`);
} catch (error) {
console.error('Error removing premium:', error);
throw error;