Fix JSON format issues with photos and add multi-photo gallery support

This commit is contained in:
2025-09-18 10:38:29 +09:00
parent bdd7d0424f
commit e275a9856b
13 changed files with 953 additions and 77 deletions

View File

@@ -10,6 +10,7 @@ import { VipController } from '../controllers/vipController';
import { VipService } from '../services/vipService';
import { TranslationController } from '../controllers/translationController';
import { t } from '../services/localizationService';
import { LikeBackHandler } from './likeBackHandler';
export class CallbackHandlers {
private bot: TelegramBot;
@@ -22,6 +23,7 @@ export class CallbackHandlers {
private vipController: VipController;
private vipService: VipService;
private translationController: TranslationController;
private likeBackHandler: LikeBackHandler;
constructor(bot: TelegramBot, messageHandlers: MessageHandlers) {
this.bot = bot;
@@ -34,6 +36,7 @@ export class CallbackHandlers {
this.vipController = new VipController(bot);
this.vipService = new VipService();
this.translationController = new TranslationController();
this.likeBackHandler = new LikeBackHandler(bot);
}
register(): void {
@@ -167,6 +170,12 @@ export class CallbackHandlers {
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);
@@ -385,9 +394,15 @@ export class CallbackHandlers {
await this.bot.sendMessage(chatId, '👍 Лайк отправлен!');
await this.showNextCandidate(chatId, telegramId);
}
} catch (error) {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке лайка');
console.error('Like error:', error);
} catch (error: any) {
// Проверяем, связана ли ошибка с уже существующим свайпом
if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') {
await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее');
await this.showNextCandidate(chatId, telegramId);
} else {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке лайка');
console.error('Like error:', error);
}
}
}
@@ -402,9 +417,15 @@ export class CallbackHandlers {
await this.matchingService.performSwipe(telegramId, targetTelegramId, 'pass');
await this.showNextCandidate(chatId, telegramId);
} catch (error) {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке дизлайка');
console.error('Dislike error:', error);
} catch (error: any) {
// Проверяем, связана ли ошибка с уже существующим свайпом
if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') {
await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее');
await this.showNextCandidate(chatId, telegramId);
} else {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке дизлайка');
console.error('Dislike error:', error);
}
}
}
@@ -443,9 +464,73 @@ export class CallbackHandlers {
await this.bot.sendMessage(chatId, '💖 Супер лайк отправлен!');
await this.showNextCandidate(chatId, telegramId);
}
} catch (error: any) {
// Проверяем, связана ли ошибка с уже существующим свайпом
if (error.message === 'Already swiped this profile' || error.code === 'ALREADY_SWIPED') {
await this.bot.sendMessage(chatId, '❓ Вы уже оценивали этот профиль ранее');
await this.showNextCandidate(chatId, telegramId);
} else {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке супер лайка');
console.error('Superlike error:', error);
}
}
}
// Обработка обратного лайка из уведомления
async handleLikeBack(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
try {
// Получаем информацию о пользователях
const targetProfile = await this.profileService.getProfileByUserId(targetUserId);
if (!targetProfile) {
await this.bot.sendMessage(chatId, '❌ Не удалось найти профиль');
return;
}
// Получаем telegram ID целевого пользователя для свайпа
const targetTelegramId = await this.profileService.getTelegramIdByUserId(targetUserId);
if (!targetTelegramId) {
await this.bot.sendMessage(chatId, '❌ Не удалось найти пользователя');
return;
}
// Выполняем свайп
const result = await this.matchingService.performSwipe(telegramId, targetTelegramId, 'like');
if (result.isMatch) {
// Это матч!
await this.bot.sendMessage(
chatId,
'🎉 *Поздравляем! Это взаимно!*\n\n' +
`Вы и *${targetProfile.name}* понравились друг другу!\n` +
'Теперь вы можете начать общение.',
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: '💬 Начать общение', callback_data: `start_chat:${targetUserId}` }],
[{ text: '👀 Посмотреть профиль', callback_data: `view_profile:${targetUserId}` }]
]
}
}
);
} else {
await this.bot.sendMessage(
chatId,
'❤️ Вам понравился профиль ' + targetProfile.name + '!\n\n' +
'Если вы также понравитесь этому пользователю, будет создан матч.',
{
reply_markup: {
inline_keyboard: [
[{ text: '🔍 Продолжить поиск', callback_data: 'start_browsing' }]
]
}
}
);
}
} catch (error) {
await this.bot.sendMessage(chatId, '❌ Ошибка при отправке супер лайка');
console.error('Superlike error:', error);
console.error('Error in handleLikeBack:', error);
await this.bot.sendMessage(chatId, '❌ Произошла ошибка при обработке лайка');
}
}
@@ -476,9 +561,28 @@ export class CallbackHandlers {
return;
}
for (let i = 1; i < targetProfile.photos.length; i++) {
const photoFileId = targetProfile.photos[i];
await this.bot.sendPhoto(chatId, photoFileId);
// Отправляем фотографии в виде медиа-группы (коллажа)
// Создаем массив объектов медиа для группового отправления
const mediaGroup = targetProfile.photos.slice(1).map((photoFileId, index) => ({
type: 'photo' as const,
media: photoFileId,
caption: index === 0 ? `📸 Дополнительные фото ${targetProfile.name}` : undefined
}));
try {
// Отправляем все фото одним сообщением (медиа-группой)
await this.bot.sendMediaGroup(chatId, mediaGroup);
} catch (error) {
console.error('Error sending media group:', error);
// Если не получилось отправить медиа-группой, отправляем по одной
for (let i = 1; i < targetProfile.photos.length; i++) {
try {
await this.bot.sendPhoto(chatId, targetProfile.photos[i]);
} catch (photoError) {
console.error(`Error sending photo ${i}:`, photoError);
}
}
}
const keyboard: InlineKeyboardMarkup = {
@@ -807,6 +911,7 @@ export class CallbackHandlers {
// Вспомогательные методы
async showProfile(chatId: number, profile: Profile, isOwner: boolean = false, viewerId?: string): Promise<void> {
const hasMultiplePhotos = profile.photos.length > 1;
const mainPhotoFileId = profile.photos[0]; // Первое фото - главное
let profileText = '👤 ' + profile.name + ', ' + profile.age + '\n';
@@ -876,20 +981,43 @@ export class CallbackHandlers {
if (hasValidPhoto) {
try {
await this.bot.sendPhoto(chatId, mainPhotoFileId, {
caption: profileText,
reply_markup: keyboard
});
if (hasMultiplePhotos) {
// Если есть несколько фото, отправляем их как медиа-группу (коллаж)
const mediaGroup = profile.photos.map((photoFileId, index) => ({
type: 'photo' as const,
media: photoFileId,
caption: index === 0 ? profileText : undefined,
parse_mode: index === 0 ? 'Markdown' as const : undefined
}));
// Сначала отправляем медиа-группу
await this.bot.sendMediaGroup(chatId, mediaGroup);
// Затем отправляем отдельное сообщение с кнопками
await this.bot.sendMessage(chatId, '📸 Выберите действие:', {
reply_markup: keyboard
});
} else {
// Если только одно фото, отправляем его с текстом
await this.bot.sendPhoto(chatId, mainPhotoFileId, {
caption: profileText,
reply_markup: keyboard,
parse_mode: 'Markdown'
});
}
} catch (error) {
console.error('Error sending profile photos:', error);
// Если не удалось отправить фото, отправляем текст
await this.bot.sendMessage(chatId, '🖼 Фото недоступно\n\n' + profileText, {
reply_markup: keyboard
reply_markup: keyboard,
parse_mode: 'Markdown'
});
}
} else {
// Отправляем как текстовое сообщение
await this.bot.sendMessage(chatId, profileText, {
reply_markup: keyboard
reply_markup: keyboard,
parse_mode: 'Markdown'
});
}
}

View File

@@ -168,25 +168,24 @@ export class EnhancedChatHandlers {
// ===== СИСТЕМА УВЕДОМЛЕНИЙ =====
// Отправить уведомление о новом сообщении
// Отправить уведомление о новом сообщении - теперь используем NotificationService
async sendMessageNotification(receiverTelegramId: string, senderName: string, messagePreview: string, matchId: string): Promise<void> {
try {
const receiverChatId = parseInt(receiverTelegramId);
// Получаем идентификаторы пользователей для использования в NotificationService
const receiverUserId = await this.profileService.getUserIdByTelegramId(receiverTelegramId);
const sender = await this.chatService.getMatchInfo(matchId, receiverTelegramId);
await this.bot.sendMessage(
receiverChatId,
`💌 *Новое сообщение от ${senderName}*\n\n` +
`"${this.escapeMarkdown(messagePreview)}"\n\n` +
'👆 Нажмите "Открыть чат" для ответа',
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: '💬 Открыть чат', callback_data: `open_native_chat_${matchId}` }],
[{ text: '📱 Все чаты', callback_data: 'native_chats' }]
]
}
}
if (!receiverUserId || !sender?.otherUserId) {
console.error('Failed to get user IDs for notification');
return;
}
// Используем сервис уведомлений для отправки более красивого уведомления
await this.notificationService.sendMessageNotification(
receiverUserId,
sender.otherUserId,
messagePreview,
matchId
);
} catch (error) {
console.error('Error sending message notification:', error);

View File

@@ -0,0 +1,76 @@
import TelegramBot from 'node-telegram-bot-api';
import { ProfileService } from '../services/profileService';
import { MatchingService } from '../services/matchingService';
export class LikeBackHandler {
private bot: TelegramBot;
private profileService: ProfileService;
private matchingService: MatchingService;
constructor(bot: TelegramBot) {
this.bot = bot;
this.profileService = new ProfileService();
this.matchingService = new MatchingService();
}
// Функция для обработки обратного лайка из уведомления
async handleLikeBack(chatId: number, telegramId: string, targetUserId: string): Promise<void> {
try {
// Получаем информацию о пользователях
const [userId, targetProfile] = await Promise.all([
this.profileService.getUserIdByTelegramId(telegramId),
this.profileService.getProfileByUserId(targetUserId)
]);
if (!userId || !targetProfile) {
await this.bot.sendMessage(chatId, '❌ Не удалось найти профиль');
return;
}
// Проверяем, есть ли уже свайп
const existingSwipe = await this.matchingService.getSwipeBetweenUsers(userId, targetUserId);
if (existingSwipe) {
await this.bot.sendMessage(chatId, '❓ Вы уже оценили этот профиль ранее.');
return;
}
// Создаем свайп (лайк)
const result = await this.matchingService.createSwipe(userId, targetUserId, 'like');
if (result.isMatch) {
// Это матч!
await this.bot.sendMessage(
chatId,
'🎉 *Поздравляем! Это взаимно!*\n\n' +
`Вы и *${targetProfile.name}* понравились друг другу!\n` +
'Теперь вы можете начать общение.',
{
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: '💬 Начать общение', callback_data: `start_chat:${targetUserId}` }],
[{ text: '👀 Посмотреть профиль', callback_data: `view_profile:${targetUserId}` }]
]
}
}
);
} else {
await this.bot.sendMessage(
chatId,
'❤️ Вам понравился профиль ' + targetProfile.name + '!\n\n' +
'Если вы также понравитесь этому пользователю, будет создан матч.',
{
reply_markup: {
inline_keyboard: [
[{ text: '🔍 Продолжить поиск', callback_data: 'start_browsing' }]
]
}
}
);
}
} catch (error) {
console.error('Error in handleLikeBack:', error);
await this.bot.sendMessage(chatId, '❌ Произошла ошибка при обработке лайка');
}
}
}