init commit

This commit is contained in:
2025-09-12 21:25:54 +09:00
commit 17efb2fb53
37 changed files with 12637 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
import TelegramBot from 'node-telegram-bot-api';
import { query } from '../database/connection';
import { ProfileService } from './profileService';
import config from '../../config/default.json';
export interface NotificationData {
userId: string;
type: 'new_match' | 'new_message' | 'new_like' | 'super_like';
data: Record<string, any>;
scheduledAt?: Date;
}
export class NotificationService {
private bot?: TelegramBot;
private profileService: ProfileService;
constructor(bot?: TelegramBot) {
this.bot = bot;
this.profileService = new ProfileService();
}
// Отправить уведомление о новом лайке
async sendLikeNotification(targetTelegramId: string, likerTelegramId: string, isSuperLike: boolean = false): Promise<void> {
try {
const [targetUser, likerProfile] = await Promise.all([
this.getUserByTelegramId(targetTelegramId),
this.profileService.getProfileByTelegramId(likerTelegramId)
]);
if (!targetUser || !likerProfile || !this.bot) {
return;
}
const message = isSuperLike
? `${likerProfile.name} отправил вам суперлайк!`
: `💖 ${likerProfile.name} поставил вам лайк!`;
await this.bot.sendMessage(targetUser.telegram_id, message, {
reply_markup: {
inline_keyboard: [[
{ text: '👀 Посмотреть профиль', callback_data: `view_profile:${likerProfile.userId}` },
{ text: '💕 Начать знакомиться', callback_data: 'start_browsing' }
]]
}
});
// Логируем уведомление
await this.logNotification({
userId: targetUser.id,
type: isSuperLike ? 'super_like' : 'new_like',
data: { likerUserId: likerProfile.userId, likerName: likerProfile.name }
});
} catch (error) {
console.error('Error sending like notification:', error);
}
}
// Отправить уведомление о новом матче
async sendMatchNotification(userId: string, matchedUserId: string): Promise<void> {
try {
const [user, matchedProfile] = await Promise.all([
this.getUserByUserId(userId),
this.profileService.getProfileByUserId(matchedUserId)
]);
if (!user || !matchedProfile || !this.bot) {
return;
}
const message = `🎉 У вас новый матч с ${matchedProfile.name}!\n\nТеперь вы можете начать общение.`;
await this.bot.sendMessage(user.telegram_id, message, {
reply_markup: {
inline_keyboard: [[
{ text: '💬 Написать сообщение', callback_data: `start_chat:${matchedUserId}` },
{ text: '👀 Посмотреть профиль', callback_data: `view_profile:${matchedUserId}` }
]]
}
});
// Логируем уведомление
await this.logNotification({
userId,
type: 'new_match',
data: { matchedUserId, matchedName: matchedProfile.name }
});
} catch (error) {
console.error('Error sending match notification:', error);
}
}
// Отправить уведомление о новом сообщении
async sendMessageNotification(receiverId: string, senderId: string, messageContent: string): Promise<void> {
try {
const [receiver, senderProfile] = await Promise.all([
this.getUserByUserId(receiverId),
this.profileService.getProfileByUserId(senderId)
]);
if (!receiver || !senderProfile || !this.bot) {
return;
}
// Проверяем, не в чате ли пользователь сейчас
const isUserActive = await this.isUserActiveInChat(receiverId, senderId);
if (isUserActive) {
return; // Не отправляем уведомление, если пользователь активен в чате
}
const truncatedMessage = messageContent.length > 50
? messageContent.substring(0, 50) + '...'
: messageContent;
const message = `💬 Новое сообщение от ${senderProfile.name}:\n\n${truncatedMessage}`;
await this.bot.sendMessage(receiver.telegram_id, message, {
reply_markup: {
inline_keyboard: [[
{ text: '💬 Ответить', callback_data: `open_chat:${senderId}` }
]]
}
});
// Логируем уведомление
await this.logNotification({
userId: receiverId,
type: 'new_message',
data: { senderId, senderName: senderProfile.name, messageContent: truncatedMessage }
});
} catch (error) {
console.error('Error sending message notification:', error);
}
}
// Отправить напоминание о неактивности
async sendInactivityReminder(userId: string): Promise<void> {
try {
const user = await this.getUserByUserId(userId);
if (!user || !this.bot) {
return;
}
const message = `👋 Давно не виделись!\n\nВозможно, ваш идеальный матч уже ждет. Давайте найдем кого-то особенного?`;
await this.bot.sendMessage(user.telegram_id, message, {
reply_markup: {
inline_keyboard: [[
{ text: '💕 Начать знакомиться', callback_data: 'start_browsing' },
{ text: '⚙️ Настройки', callback_data: 'settings' }
]]
}
});
} catch (error) {
console.error('Error sending inactivity reminder:', error);
}
}
// Отправить уведомление о новых лайках (сводка)
async sendLikesSummary(userId: string, likesCount: number): Promise<void> {
try {
const user = await this.getUserByUserId(userId);
if (!user || !this.bot || likesCount === 0) {
return;
}
const message = likesCount === 1
? `💖 У вас 1 новый лайк! Посмотрите, кто это может быть.`
: `💖 У вас ${likesCount} новых лайков! Посмотрите, кто проявил к вам интерес.`;
await this.bot.sendMessage(user.telegram_id, message, {
reply_markup: {
inline_keyboard: [[
{ text: '👀 Посмотреть лайки', callback_data: 'view_likes' },
{ text: '💕 Начать знакомиться', callback_data: 'start_browsing' }
]]
}
});
} catch (error) {
console.error('Error sending likes summary:', error);
}
}
// Логирование уведомлений
private async logNotification(notificationData: NotificationData): Promise<void> {
try {
await query(`
INSERT INTO notifications (user_id, type, data, created_at)
VALUES ($1, $2, $3, $4)
`, [
notificationData.userId,
notificationData.type,
JSON.stringify(notificationData.data),
new Date()
]);
} catch (error) {
console.error('Error logging notification:', error);
}
}
// Получить пользователя по ID
private async getUserByUserId(userId: string): Promise<any> {
try {
const result = await query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
return result.rows[0] || null;
} catch (error) {
console.error('Error getting user:', error);
return null;
}
}
// Получить пользователя по Telegram ID
private async getUserByTelegramId(telegramId: string): Promise<any> {
try {
const result = await query(
'SELECT * FROM users WHERE telegram_id = $1',
[parseInt(telegramId)]
);
return result.rows[0] || null;
} catch (error) {
console.error('Error getting user by telegram ID:', error);
return null;
}
}
// Проверить, активен ли пользователь в чате
private async isUserActiveInChat(userId: string, chatWithUserId: string): Promise<boolean> {
// TODO: Реализовать проверку активности пользователя
// Можно использовать Redis для хранения состояния активности
return false;
}
// Отправить пуш-уведомление (для будущего использования)
async sendPushNotification(userId: string, title: string, body: string, data?: any): Promise<void> {
// TODO: Интеграция с Firebase Cloud Messaging или другим сервисом пуш-уведомлений
console.log(`Push notification for ${userId}: ${title} - ${body}`);
}
// Получить настройки уведомлений пользователя
async getNotificationSettings(userId: string): Promise<{
newMatches: boolean;
newMessages: boolean;
newLikes: boolean;
reminders: boolean;
}> {
try {
const result = await query(
'SELECT notification_settings FROM users WHERE id = $1',
[userId]
);
if (result.rows.length === 0) {
return {
newMatches: true,
newMessages: true,
newLikes: true,
reminders: true
};
}
return result.rows[0].notification_settings || {
newMatches: true,
newMessages: true,
newLikes: true,
reminders: true
};
} catch (error) {
console.error('Error getting notification settings:', error);
return {
newMatches: true,
newMessages: true,
newLikes: true,
reminders: true
};
}
}
// Обновить настройки уведомлений
async updateNotificationSettings(userId: string, settings: {
newMatches?: boolean;
newMessages?: boolean;
newLikes?: boolean;
reminders?: boolean;
}): Promise<void> {
try {
await query(
'UPDATE users SET notification_settings = $1 WHERE id = $2',
[JSON.stringify(settings), userId]
);
} catch (error) {
console.error('Error updating notification settings:', error);
}
}
// Планировщик уведомлений (вызывается периодически)
async processScheduledNotifications(): Promise<void> {
try {
// Получаем запланированные уведомления
const result = await query(`
SELECT * FROM scheduled_notifications
WHERE scheduled_at <= $1 AND sent = false
ORDER BY scheduled_at ASC
LIMIT 100
`, [new Date()]);
for (const notification of result.rows) {
try {
switch (notification.type) {
case 'inactivity_reminder':
await this.sendInactivityReminder(notification.user_id);
break;
case 'likes_summary':
const likesCount = notification.data?.likesCount || 0;
await this.sendLikesSummary(notification.user_id, likesCount);
break;
// Добавить другие типы уведомлений
}
// Отмечаем как отправленное
await query(
'UPDATE scheduled_notifications SET sent = true, sent_at = $1 WHERE id = $2',
[new Date(), notification.id]
);
} catch (error) {
console.error(`Error processing notification ${notification.id}:`, error);
}
}
} catch (error) {
console.error('Error processing scheduled notifications:', error);
}
}
}