alpha-test
This commit is contained in:
14
migrations/add_user_state_columns.sql
Normal file
14
migrations/add_user_state_columns.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- Добавление столбцов state и state_data в таблицу users для обработки состояний пользователя
|
||||
|
||||
-- Добавляем столбец state для хранения текущего состояния пользователя
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS state VARCHAR(255) NULL;
|
||||
|
||||
-- Добавляем столбец state_data для хранения дополнительных данных о состоянии
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS state_data JSONB DEFAULT '{}'::jsonb;
|
||||
|
||||
-- Добавляем индекс для быстрого поиска по state
|
||||
CREATE INDEX IF NOT EXISTS idx_users_state ON users(state);
|
||||
|
||||
-- Комментарий к столбцам
|
||||
COMMENT ON COLUMN users.state IS 'Текущее состояние пользователя (например, ожидание ввода)';
|
||||
COMMENT ON COLUMN users.state_data IS 'Дополнительные данные о состоянии пользователя в формате JSON';
|
||||
76
scripts/README.md
Normal file
76
scripts/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Исправление проблем с уведомлениями в боте
|
||||
|
||||
Этот набор скриптов предназначен для исправления проблем с обработкой уведомлений в боте.
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
После внедрения системы уведомлений и связанных с ней изменений в базе данных, возникла проблема с обработкой callback запросов. Бот перестал реагировать на все callback запросы, кроме тех, что связаны с уведомлениями.
|
||||
|
||||
Проблема вызвана следующими факторами:
|
||||
1. Отсутствие или неверная структура таблиц в базе данных для хранения уведомлений
|
||||
2. Отсутствие необходимых полей `state` и `state_data` в таблице `users`
|
||||
3. Отсутствие правильной регистрации обработчиков уведомлений в файле `bot.ts`
|
||||
|
||||
## Решение
|
||||
|
||||
Для решения проблемы были созданы следующие скрипты:
|
||||
|
||||
### 1. `fix_notification_callbacks.js`
|
||||
Проверяет и создает необходимые таблицы и столбцы в базе данных:
|
||||
- Таблицы `notifications`, `scheduled_notifications`, `notification_templates`
|
||||
- Столбцы `notification_settings`, `state`, `state_data` в таблице `users`
|
||||
|
||||
### 2. `update_bot_with_notifications.js`
|
||||
Обновляет файл `bot.ts`:
|
||||
- Добавляет импорт класса `NotificationHandlers`
|
||||
- Добавляет объявление поля `notificationHandlers` в класс `TelegramTinderBot`
|
||||
- Добавляет создание экземпляра `NotificationHandlers` в конструкторе
|
||||
- Добавляет регистрацию обработчиков уведомлений в методе `registerHandlers`
|
||||
|
||||
### 3. `fix_all_notifications.js`
|
||||
Запускает оба скрипта последовательно для полного исправления проблемы
|
||||
|
||||
## Как использовать
|
||||
|
||||
1. Остановите бота, если он запущен:
|
||||
```bash
|
||||
# Нажмите Ctrl+C в терминале, где запущен бот
|
||||
# или найдите процесс и завершите его
|
||||
```
|
||||
|
||||
2. Запустите комплексный скрипт исправления:
|
||||
```bash
|
||||
node scripts/fix_all_notifications.js
|
||||
```
|
||||
|
||||
3. После успешного выполнения скрипта перезапустите бота:
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
## Проверка результата
|
||||
|
||||
После запуска бота убедитесь, что:
|
||||
1. Бот отвечает на все callback запросы (включая кнопки, не связанные с уведомлениями)
|
||||
2. Настройки уведомлений работают корректно (команда /notifications или кнопка в меню настроек)
|
||||
3. Уведомления о лайках, супер-лайках и новых матчах приходят пользователям
|
||||
|
||||
## Если проблемы остались
|
||||
|
||||
Если после выполнения всех шагов проблемы остались, выполните следующие проверки:
|
||||
|
||||
1. Проверьте логи бота на наличие ошибок
|
||||
2. Проверьте структуру базы данных:
|
||||
```sql
|
||||
\dt -- Список всех таблиц
|
||||
\d notifications -- Структура таблицы notifications
|
||||
\d scheduled_notifications -- Структура таблицы scheduled_notifications
|
||||
\d notification_templates -- Структура таблицы notification_templates
|
||||
\d users -- Убедитесь, что поля state, state_data и notification_settings существуют
|
||||
```
|
||||
|
||||
3. Проверьте код в файлах:
|
||||
- `src/bot.ts`: должен содержать импорт, создание и регистрацию `NotificationHandlers`
|
||||
- `src/handlers/callbackHandlers.ts`: должен правильно обрабатывать все callback-запросы
|
||||
|
||||
В случае обнаружения ошибок, исправьте их вручную и перезапустите бота.
|
||||
58
scripts/addPremiumColumn.js
Normal file
58
scripts/addPremiumColumn.js
Normal file
@@ -0,0 +1,58 @@
|
||||
// Скрипт для добавления колонки premium в таблицу users и установки premium для всех пользователей
|
||||
require('dotenv').config();
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function setAllUsersToPremium() {
|
||||
try {
|
||||
console.log('Проверяем наличие столбца premium в таблице users...');
|
||||
|
||||
const result = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
AND column_name = 'premium'
|
||||
);
|
||||
`);
|
||||
|
||||
if (!result.rows[0].exists) {
|
||||
console.log('🔄 Добавляем столбец premium...');
|
||||
await pool.query(`ALTER TABLE users ADD COLUMN premium BOOLEAN DEFAULT false;`);
|
||||
console.log('✅ Столбец premium успешно добавлен');
|
||||
} else {
|
||||
console.log('✅ Столбец premium уже существует');
|
||||
}
|
||||
|
||||
console.log('Устанавливаем премиум-статус для всех пользователей...');
|
||||
|
||||
const updateResult = await pool.query(`
|
||||
UPDATE users
|
||||
SET premium = true
|
||||
WHERE true
|
||||
RETURNING id, telegram_id, premium
|
||||
`);
|
||||
|
||||
console.log(`✅ Успешно установлен премиум-статус для ${updateResult.rows.length} пользователей:`);
|
||||
updateResult.rows.forEach(row => {
|
||||
console.log(`ID: ${row.id.substr(0, 8)}... | Telegram ID: ${row.telegram_id} | Premium: ${row.premium}`);
|
||||
});
|
||||
|
||||
console.log('🎉 Все пользователи теперь имеют премиум-статус!');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при установке премиум-статуса:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
console.log('Соединение с базой данных закрыто');
|
||||
}
|
||||
}
|
||||
|
||||
setAllUsersToPremium();
|
||||
88
scripts/checkCallbackHandlers.js
Normal file
88
scripts/checkCallbackHandlers.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// Скрипт для анализа и отладки проблем с обработчиками коллбэков
|
||||
require('dotenv').config();
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function analyzeCallbackHandlers() {
|
||||
const filePath = path.join(__dirname, '..', 'src', 'handlers', 'callbackHandlers.ts');
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
// Проверяем наличие реализаций методов
|
||||
const methodsToCheck = [
|
||||
'handleCreateProfile',
|
||||
'handleGenderSelection',
|
||||
'handleViewMyProfile',
|
||||
'handleEditProfile',
|
||||
'handleManagePhotos',
|
||||
'handleStartBrowsing',
|
||||
'handleSettings'
|
||||
];
|
||||
|
||||
const issues = [];
|
||||
let debugInfo = [];
|
||||
|
||||
methodsToCheck.forEach(method => {
|
||||
debugInfo.push(`Проверяем метод: ${method}`);
|
||||
|
||||
// Проверяем наличие полной реализации метода (не только сигнатуры)
|
||||
const methodSignatureRegex = new RegExp(`async\\s+${method}\\s*\\([^)]*\\)\\s*:\\s*Promise<void>\\s*{`, 'g');
|
||||
const hasSignature = methodSignatureRegex.test(content);
|
||||
|
||||
const methodBodyRegex = new RegExp(`async\\s+${method}\\s*\\([^)]*\\)\\s*:\\s*Promise<void>\\s*{[\\s\\S]+?}`, 'g');
|
||||
const methodMatch = content.match(methodBodyRegex);
|
||||
|
||||
debugInfo.push(` Сигнатура найдена: ${hasSignature}`);
|
||||
debugInfo.push(` Реализация найдена: ${methodMatch !== null}`);
|
||||
|
||||
if (methodMatch) {
|
||||
const methodContent = methodMatch[0];
|
||||
debugInfo.push(` Длина метода: ${methodContent.length} символов`);
|
||||
|
||||
// Проверяем, содержит ли метод только заглушку
|
||||
const isStub = methodContent.includes('// Заглушка метода') ||
|
||||
(!methodContent.includes('await') && methodContent.split('\n').length <= 3);
|
||||
|
||||
if (isStub) {
|
||||
issues.push(`❌ Метод ${method} содержит только заглушку, нет реальной реализации`);
|
||||
} else {
|
||||
debugInfo.push(` Метод ${method} имеет полную реализацию`);
|
||||
}
|
||||
} else if (hasSignature) {
|
||||
issues.push(`❌ Метод ${method} имеет только сигнатуру, но нет реализации`);
|
||||
} else {
|
||||
issues.push(`❌ Метод ${method} не найден в файле`);
|
||||
}
|
||||
});
|
||||
|
||||
// Проверяем регистрацию обработчиков для NotificationHandlers
|
||||
const notificationHandlersRegex = /this\.notificationHandlers\s*=\s*new\s+NotificationHandlers\(bot\);/g;
|
||||
const hasNotificationHandlers = notificationHandlersRegex.test(content);
|
||||
debugInfo.push(`NotificationHandlers инициализирован: ${hasNotificationHandlers}`);
|
||||
|
||||
// Проверяем обработку коллбэка notifications
|
||||
const notificationsCallbackRegex = /if\s*\(data\s*===\s*['"]notifications['"].*?\)/g;
|
||||
const hasNotificationsCallback = notificationsCallbackRegex.test(content);
|
||||
debugInfo.push(`Обработчик для callback 'notifications' найден: ${hasNotificationsCallback}`);
|
||||
|
||||
// Выводим результаты
|
||||
console.log('\n=== Анализ CallbackHandlers.ts ===\n');
|
||||
if (issues.length > 0) {
|
||||
console.log('НАЙДЕНЫ ПРОБЛЕМЫ:');
|
||||
issues.forEach(issue => console.log(issue));
|
||||
console.log('\nРЕКОМЕНДАЦИИ:');
|
||||
console.log('1. Восстановите оригинальные реализации методов вместо заглушек');
|
||||
console.log('2. Убедитесь, что методы содержат необходимую бизнес-логику');
|
||||
console.log('3. Проверьте, что все коллбэки правильно обрабатываются');
|
||||
} else {
|
||||
console.log('✅ Проблем не обнаружено');
|
||||
}
|
||||
|
||||
console.log('\n=== Отладочная информация ===\n');
|
||||
debugInfo.forEach(info => console.log(info));
|
||||
|
||||
// Проверяем количество методов в файле
|
||||
const asyncMethodsCount = (content.match(/async\s+handle[A-Za-z]+\s*\(/g) || []).length;
|
||||
console.log(`\nВсего async методов в файле: ${asyncMethodsCount}`);
|
||||
}
|
||||
|
||||
analyzeCallbackHandlers();
|
||||
74
scripts/checkUserTable.js
Normal file
74
scripts/checkUserTable.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USER || 'postgres',
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
database: process.env.DB_NAME || 'telegram_tinder_db',
|
||||
password: process.env.DB_PASSWORD || 'postgres',
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function checkUserTableStructure() {
|
||||
try {
|
||||
// Получаем информацию о структуре таблицы users
|
||||
const result = await pool.query(`
|
||||
SELECT column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'users'
|
||||
ORDER BY ordinal_position;
|
||||
`);
|
||||
|
||||
console.log('=== Структура таблицы users ===');
|
||||
console.table(result.rows);
|
||||
|
||||
// Проверяем наличие столбцов state и state_data
|
||||
const stateColumn = result.rows.find(row => row.column_name === 'state');
|
||||
const stateDataColumn = result.rows.find(row => row.column_name === 'state_data');
|
||||
|
||||
if (!stateColumn) {
|
||||
console.log('❌ Столбец state отсутствует в таблице users');
|
||||
} else {
|
||||
console.log('✅ Столбец state присутствует в таблице users');
|
||||
}
|
||||
|
||||
if (!stateDataColumn) {
|
||||
console.log('❌ Столбец state_data отсутствует в таблице users');
|
||||
} else {
|
||||
console.log('✅ Столбец state_data присутствует в таблице users');
|
||||
}
|
||||
|
||||
// Добавляем эти столбцы, если их нет
|
||||
if (!stateColumn || !stateDataColumn) {
|
||||
console.log('🔄 Добавление отсутствующих столбцов...');
|
||||
|
||||
await pool.query(`
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS state VARCHAR(255) NULL,
|
||||
ADD COLUMN IF NOT EXISTS state_data JSONB DEFAULT '{}'::jsonb;
|
||||
`);
|
||||
|
||||
console.log('✅ Столбцы успешно добавлены');
|
||||
}
|
||||
|
||||
// Проверяем наличие других таблиц, связанных с уведомлениями
|
||||
const tablesResult = await pool.query(`
|
||||
SELECT tablename
|
||||
FROM pg_catalog.pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename IN ('notifications', 'notification_settings', 'scheduled_notifications');
|
||||
`);
|
||||
|
||||
console.log('\n=== Таблицы для уведомлений ===');
|
||||
console.table(tablesResult.rows);
|
||||
|
||||
// Закрываем соединение
|
||||
await pool.end();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при проверке структуры таблицы:', error);
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
checkUserTableStructure();
|
||||
259
scripts/createNotificationTables.js
Normal file
259
scripts/createNotificationTables.js
Normal file
@@ -0,0 +1,259 @@
|
||||
const { Pool } = require('pg');
|
||||
const dotenv = require('dotenv');
|
||||
const uuid = require('uuid');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
});
|
||||
|
||||
async function createNotificationTables() {
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
console.log('Creating UUID extension if not exists...');
|
||||
await client.query(`
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
`);
|
||||
|
||||
// Проверяем существование таблицы notifications
|
||||
const notificationsExists = await client.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_name = 'notifications'
|
||||
) as exists
|
||||
`);
|
||||
|
||||
if (!notificationsExists.rows[0].exists) {
|
||||
console.log('Creating notifications table...');
|
||||
await client.query(`
|
||||
CREATE TABLE notifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
data JSONB,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('Creating index on notifications...');
|
||||
await client.query(`
|
||||
CREATE INDEX idx_notifications_user_id ON notifications (user_id);
|
||||
CREATE INDEX idx_notifications_type ON notifications (type);
|
||||
CREATE INDEX idx_notifications_created_at ON notifications (created_at);
|
||||
`);
|
||||
} else {
|
||||
console.log('Notifications table already exists.');
|
||||
}
|
||||
|
||||
// Проверяем существование таблицы scheduled_notifications
|
||||
const scheduledExists = await client.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_name = 'scheduled_notifications'
|
||||
) as exists
|
||||
`);
|
||||
|
||||
if (!scheduledExists.rows[0].exists) {
|
||||
console.log('Creating scheduled_notifications table...');
|
||||
await client.query(`
|
||||
CREATE TABLE scheduled_notifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
data JSONB,
|
||||
scheduled_at TIMESTAMP NOT NULL,
|
||||
processed BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('Creating index on scheduled_notifications...');
|
||||
await client.query(`
|
||||
CREATE INDEX idx_scheduled_notifications_user_id ON scheduled_notifications (user_id);
|
||||
CREATE INDEX idx_scheduled_notifications_scheduled_at ON scheduled_notifications (scheduled_at);
|
||||
CREATE INDEX idx_scheduled_notifications_processed ON scheduled_notifications (processed);
|
||||
`);
|
||||
} else {
|
||||
console.log('Scheduled_notifications table already exists.');
|
||||
}
|
||||
|
||||
// Проверяем существование таблицы notification_templates
|
||||
const templatesExists = await client.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_name = 'notification_templates'
|
||||
) as exists
|
||||
`);
|
||||
|
||||
if (!templatesExists.rows[0].exists) {
|
||||
console.log('Creating notification_templates table...');
|
||||
await client.query(`
|
||||
CREATE TABLE notification_templates (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
type VARCHAR(50) NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
message_template TEXT NOT NULL,
|
||||
button_template JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
} else {
|
||||
console.log('Notification_templates table already exists.');
|
||||
}
|
||||
|
||||
// Проверяем наличие колонки notification_settings в таблице users
|
||||
const settingsColumnExists = await client.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_name = 'users' AND column_name = 'notification_settings'
|
||||
) as exists
|
||||
`);
|
||||
|
||||
if (!settingsColumnExists.rows[0].exists) {
|
||||
console.log('Adding notification_settings column to users table...');
|
||||
await client.query(`
|
||||
ALTER TABLE users
|
||||
ADD COLUMN notification_settings JSONB DEFAULT '{
|
||||
"newMatches": true,
|
||||
"newMessages": true,
|
||||
"newLikes": true,
|
||||
"reminders": true,
|
||||
"dailySummary": true,
|
||||
"timePreference": "evening",
|
||||
"doNotDisturb": false
|
||||
}'::jsonb
|
||||
`);
|
||||
} else {
|
||||
console.log('Notification_settings column already exists in users table.');
|
||||
}
|
||||
|
||||
// Заполнение таблицы шаблонов уведомлений базовыми шаблонами
|
||||
if (!templatesExists.rows[0].exists) {
|
||||
console.log('Populating notification templates...');
|
||||
|
||||
const templates = [
|
||||
{
|
||||
type: 'new_like',
|
||||
title: 'Новый лайк!',
|
||||
message_template: '❤️ *{{name}}* поставил(а) вам лайк!\n\nВозраст: {{age}}\n{{city}}\n\nОтветьте взаимностью или посмотрите профиль.',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
|
||||
[
|
||||
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
|
||||
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
|
||||
],
|
||||
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'super_like',
|
||||
title: 'Супер-лайк!',
|
||||
message_template: '⭐️ *{{name}}* отправил(а) вам супер-лайк!\n\nВозраст: {{age}}\n{{city}}\n\nВы произвели особое впечатление! Ответьте взаимностью или посмотрите профиль.',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
|
||||
[
|
||||
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
|
||||
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
|
||||
],
|
||||
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'new_match',
|
||||
title: 'Новый матч!',
|
||||
message_template: '🎊 *Ура! Это взаимно!* 🎊\n\nВы и *{{name}}* понравились друг другу!\nВозраст: {{age}}\n{{city}}\n\nСделайте первый шаг - напишите сообщение!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '💬 Начать общение', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[
|
||||
{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' },
|
||||
{ text: '📋 Все матчи', callback_data: 'view_matches' }
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'new_message',
|
||||
title: 'Новое сообщение!',
|
||||
message_template: '💌 *Новое сообщение!*\n\nОт: *{{name}}*\n\n"{{message}}"\n\nОтветьте на сообщение прямо сейчас!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📩 Ответить', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[
|
||||
{ text: '👤 Профиль', callback_data: 'view_profile:{{userId}}' },
|
||||
{ text: '📋 Все чаты', callback_data: 'view_matches' }
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'match_reminder',
|
||||
title: 'Напоминание о матче',
|
||||
message_template: '💕 У вас есть матч с *{{name}}*, но вы еще не начали общение!\n\nНе упустите шанс познакомиться поближе!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '💬 Начать общение', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'inactive_matches',
|
||||
title: 'Неактивные матчи',
|
||||
message_template: '⏰ У вас {{count}} неактивных матчей!\n\nПродолжите общение, чтобы не упустить интересные знакомства!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📋 Открыть матчи', callback_data: 'view_matches' }],
|
||||
[{ text: '💕 Смотреть новые анкеты', callback_data: 'start_browsing' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'like_summary',
|
||||
title: 'Сводка лайков',
|
||||
message_template: '💖 У вас {{count}} новых лайков!\n\nПосмотрите, кто проявил к вам интерес сегодня!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть лайки', callback_data: 'view_likes' }],
|
||||
[{ text: '💕 Начать знакомиться', callback_data: 'start_browsing' }]
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const template of templates) {
|
||||
await client.query(`
|
||||
INSERT INTO notification_templates (id, type, title, message_template, button_template)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
`, [
|
||||
uuid.v4(),
|
||||
template.type,
|
||||
template.title,
|
||||
template.message_template,
|
||||
JSON.stringify(template.button_template)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
console.log('Successfully created notification tables');
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error('Error creating notification tables:', err);
|
||||
} finally {
|
||||
client.release();
|
||||
pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
createNotificationTables().catch(err => console.error('Failed to create notification tables:', err));
|
||||
142
scripts/fixCallbackHandlers.js
Normal file
142
scripts/fixCallbackHandlers.js
Normal file
@@ -0,0 +1,142 @@
|
||||
// Скрипт для восстановления оригинальной функциональности callbackHandlers.ts
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Находим самую последнюю версию файла callbackHandlers.ts в репозитории
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
try {
|
||||
console.log('Поиск оригинальной версии CallbackHandlers.ts с полной функциональностью...');
|
||||
|
||||
// Находим коммиты, содержащие значительные изменения в файле (более 1000 символов)
|
||||
const commits = execSync('git log --format="%H" -- src/handlers/callbackHandlers.ts')
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n');
|
||||
|
||||
console.log(`Найдено ${commits.length} коммитов с изменениями файла`);
|
||||
|
||||
// Пробуем разные коммиты, начиная с последнего, чтобы найти полную реализацию
|
||||
let foundFullImplementation = false;
|
||||
let fullImplementationContent = '';
|
||||
|
||||
for (const commit of commits) {
|
||||
console.log(`Проверяем коммит ${commit.substring(0, 8)}...`);
|
||||
|
||||
try {
|
||||
const fileContent = execSync(`git show ${commit}:src/handlers/callbackHandlers.ts`).toString();
|
||||
|
||||
// Проверяем, содержит ли файл полные реализации методов
|
||||
const hasFullImplementations = !fileContent.includes('// Заглушка метода') &&
|
||||
fileContent.includes('await this.bot.sendMessage');
|
||||
|
||||
if (hasFullImplementations) {
|
||||
console.log(`✅ Найдена полная реализация в коммите ${commit.substring(0, 8)}`);
|
||||
fullImplementationContent = fileContent;
|
||||
foundFullImplementation = true;
|
||||
break;
|
||||
} else {
|
||||
console.log(`❌ Коммит ${commit.substring(0, 8)} не содержит полной реализации`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Ошибка при проверке коммита ${commit}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFullImplementation) {
|
||||
console.error('❌ Не удалось найти полную реализацию в истории коммитов');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Теперь получаем текущую версию файла с поддержкой уведомлений
|
||||
console.log('Получаем текущую версию с поддержкой уведомлений...');
|
||||
const currentFilePath = path.join(__dirname, '..', 'src', 'handlers', 'callbackHandlers.ts');
|
||||
const currentContent = fs.readFileSync(currentFilePath, 'utf-8');
|
||||
|
||||
// Сначала создаем бэкап текущего файла
|
||||
const backupPath = currentFilePath + '.backup-' + Date.now();
|
||||
fs.writeFileSync(backupPath, currentContent);
|
||||
console.log(`✅ Создан бэкап текущей версии: ${path.basename(backupPath)}`);
|
||||
|
||||
// Извлекаем код для поддержки уведомлений из текущей версии
|
||||
console.log('Извлекаем код для поддержки уведомлений...');
|
||||
|
||||
// Находим импорт NotificationHandlers
|
||||
const notificationImportRegex = /import\s+{\s*NotificationHandlers\s*}\s*from\s*['"]\.\/notificationHandlers['"]\s*;/;
|
||||
const notificationImport = currentContent.match(notificationImportRegex)?.[0] || '';
|
||||
|
||||
// Находим объявление поля notificationHandlers
|
||||
const notificationFieldRegex = /private\s+notificationHandlers\?\s*:\s*NotificationHandlers\s*;/;
|
||||
const notificationField = currentContent.match(notificationFieldRegex)?.[0] || '';
|
||||
|
||||
// Находим инициализацию notificationHandlers в конструкторе
|
||||
const notificationInitRegex = /\/\/\s*Создаем экземпляр NotificationHandlers[\s\S]*?try\s*{[\s\S]*?this\.notificationHandlers\s*=\s*new\s*NotificationHandlers[\s\S]*?}\s*catch[\s\S]*?}/;
|
||||
const notificationInit = currentContent.match(notificationInitRegex)?.[0] || '';
|
||||
|
||||
// Находим метод handleNotificationSettings
|
||||
const notificationSettingsMethodRegex = /async\s+handleNotificationSettings[\s\S]*?}\s*}/;
|
||||
const notificationSettingsMethod = currentContent.match(notificationSettingsMethodRegex)?.[0] || '';
|
||||
|
||||
// Находим обработку callback для notifications в handleCallback
|
||||
const notificationCallbackRegex = /\/\/\s*Настройки уведомлений[\s\S]*?else\s+if\s*\(data\s*===\s*['"]notifications['"][\s\S]*?}\s*}/;
|
||||
const notificationCallback = currentContent.match(notificationCallbackRegex)?.[0] || '';
|
||||
|
||||
// Получаем часть обработки коллбэков для уведомлений
|
||||
const notificationToggleRegex = /\/\/\s*Обработка переключения настроек уведомлений[\s\S]*?else\s+if[\s\S]*?notif_[\s\S]*?}\s*}/;
|
||||
const notificationToggle = currentContent.match(notificationToggleRegex)?.[0] || '';
|
||||
|
||||
console.log(`✅ Извлечены блоки кода для уведомлений`);
|
||||
|
||||
// Интегрируем код уведомлений в оригинальную версию
|
||||
console.log('Интегрируем код уведомлений в оригинальную версию...');
|
||||
|
||||
// 1. Добавляем импорт
|
||||
let newContent = fullImplementationContent;
|
||||
if (notificationImport) {
|
||||
newContent = newContent.replace(/import\s*{[^}]*}\s*from\s*['"]\.\/messageHandlers['"]\s*;/,
|
||||
match => match + '\n' + notificationImport);
|
||||
}
|
||||
|
||||
// 2. Добавляем объявление поля
|
||||
if (notificationField) {
|
||||
newContent = newContent.replace(/private\s+translationController\s*:\s*TranslationController\s*;/,
|
||||
match => match + '\n ' + notificationField);
|
||||
}
|
||||
|
||||
// 3. Добавляем инициализацию в конструкторе
|
||||
if (notificationInit) {
|
||||
newContent = newContent.replace(/this\.translationController\s*=\s*new\s*TranslationController\(\);/,
|
||||
match => match + '\n ' + notificationInit);
|
||||
}
|
||||
|
||||
// 4. Добавляем обработку коллбэков для уведомлений
|
||||
if (notificationCallback) {
|
||||
newContent = newContent.replace(/else\s+{\s*await\s+this\.bot\.answerCallbackQuery\(query\.id[\s\S]*?return;/,
|
||||
match => notificationCallback + '\n ' + match);
|
||||
}
|
||||
|
||||
// 5. Добавляем обработку переключения настроек уведомлений
|
||||
if (notificationToggle) {
|
||||
newContent = newContent.replace(/else\s+{\s*await\s+this\.bot\.answerCallbackQuery\(query\.id[\s\S]*?return;/,
|
||||
match => notificationToggle + '\n ' + match);
|
||||
}
|
||||
|
||||
// 6. Добавляем метод handleNotificationSettings в конец класса
|
||||
if (notificationSettingsMethod) {
|
||||
newContent = newContent.replace(/}(\s*)$/, notificationSettingsMethod + '\n}$1');
|
||||
}
|
||||
|
||||
// Сохраняем обновленный файл
|
||||
const outputPath = currentFilePath + '.fixed';
|
||||
fs.writeFileSync(outputPath, newContent);
|
||||
console.log(`✅ Создана исправленная версия файла: ${path.basename(outputPath)}`);
|
||||
|
||||
console.log('\nИнструкция по восстановлению:');
|
||||
console.log(`1. Проверьте файл ${path.basename(outputPath)}`);
|
||||
console.log('2. Если все выглядит правильно, выполните команду:');
|
||||
console.log(` Move-Item -Force "${path.basename(outputPath)}" "${path.basename(currentFilePath)}"`);
|
||||
console.log('3. Перезапустите бота');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Произошла ошибка:', error);
|
||||
}
|
||||
170
scripts/fixDatabaseStructure.js
Normal file
170
scripts/fixDatabaseStructure.js
Normal file
@@ -0,0 +1,170 @@
|
||||
// Скрипт для исправления проблемы с ботом
|
||||
require('dotenv').config();
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Получаем данные подключения из .env
|
||||
console.log('Параметры подключения к БД:');
|
||||
console.log('DB_USERNAME:', process.env.DB_USERNAME);
|
||||
console.log('DB_HOST:', process.env.DB_HOST);
|
||||
console.log('DB_NAME:', process.env.DB_NAME);
|
||||
console.log('DB_PASSWORD:', process.env.DB_PASSWORD ? '[указан]' : '[не указан]');
|
||||
console.log('DB_PORT:', process.env.DB_PORT);
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function fixDatabase() {
|
||||
try {
|
||||
console.log('Начинаем исправление базы данных...');
|
||||
|
||||
// Проверяем существование таблицы users
|
||||
const tableResult = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
);
|
||||
`);
|
||||
|
||||
if (!tableResult.rows[0].exists) {
|
||||
console.error('Таблица users не найдена!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Таблица users существует');
|
||||
|
||||
// Проверяем и добавляем столбцы state и state_data, если они отсутствуют
|
||||
console.log('Проверяем наличие столбцов state и state_data...');
|
||||
|
||||
const stateColumnResult = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
AND column_name = 'state'
|
||||
);
|
||||
`);
|
||||
|
||||
const stateDataColumnResult = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
AND column_name = 'state_data'
|
||||
);
|
||||
`);
|
||||
|
||||
if (!stateColumnResult.rows[0].exists) {
|
||||
console.log('🔄 Добавляем столбец state...');
|
||||
await pool.query(`ALTER TABLE users ADD COLUMN state VARCHAR(255) NULL;`);
|
||||
console.log('✅ Столбец state успешно добавлен');
|
||||
} else {
|
||||
console.log('✅ Столбец state уже существует');
|
||||
}
|
||||
|
||||
if (!stateDataColumnResult.rows[0].exists) {
|
||||
console.log('🔄 Добавляем столбец state_data...');
|
||||
await pool.query(`ALTER TABLE users ADD COLUMN state_data JSONB DEFAULT '{}'::jsonb;`);
|
||||
console.log('✅ Столбец state_data успешно добавлен');
|
||||
} else {
|
||||
console.log('✅ Столбец state_data уже существует');
|
||||
}
|
||||
|
||||
// Проверка наличия таблиц для уведомлений
|
||||
console.log('Проверяем наличие таблиц для уведомлений...');
|
||||
|
||||
const tablesCheck = await Promise.all([
|
||||
checkTableExists('notifications'),
|
||||
checkTableExists('notification_settings'),
|
||||
checkTableExists('scheduled_notifications')
|
||||
]);
|
||||
|
||||
// Создаем отсутствующие таблицы
|
||||
if (!tablesCheck[0]) {
|
||||
console.log('🔄 Создаем таблицу notifications...');
|
||||
await pool.query(`
|
||||
CREATE TABLE notifications (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
content JSONB NOT NULL DEFAULT '{}',
|
||||
is_read BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_notifications_user_id ON notifications(user_id);
|
||||
CREATE INDEX idx_notifications_type ON notifications(type);
|
||||
CREATE INDEX idx_notifications_created_at ON notifications(created_at);
|
||||
`);
|
||||
console.log('✅ Таблица notifications успешно создана');
|
||||
}
|
||||
|
||||
if (!tablesCheck[1]) {
|
||||
console.log('🔄 Создаем таблицу notification_settings...');
|
||||
await pool.query(`
|
||||
CREATE TABLE notification_settings (
|
||||
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
||||
new_matches BOOLEAN DEFAULT true,
|
||||
new_messages BOOLEAN DEFAULT true,
|
||||
new_likes BOOLEAN DEFAULT true,
|
||||
reminders BOOLEAN DEFAULT true,
|
||||
daily_summary BOOLEAN DEFAULT false,
|
||||
time_preference VARCHAR(20) DEFAULT 'evening',
|
||||
do_not_disturb BOOLEAN DEFAULT false,
|
||||
do_not_disturb_start TIME,
|
||||
do_not_disturb_end TIME,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
console.log('✅ Таблица notification_settings успешно создана');
|
||||
}
|
||||
|
||||
if (!tablesCheck[2]) {
|
||||
console.log('🔄 Создаем таблицу scheduled_notifications...');
|
||||
await pool.query(`
|
||||
CREATE TABLE scheduled_notifications (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
content JSONB NOT NULL DEFAULT '{}',
|
||||
scheduled_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
processed BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_scheduled_notifications_user_id ON scheduled_notifications(user_id);
|
||||
CREATE INDEX idx_scheduled_notifications_scheduled_at ON scheduled_notifications(scheduled_at);
|
||||
CREATE INDEX idx_scheduled_notifications_processed ON scheduled_notifications(processed);
|
||||
`);
|
||||
console.log('✅ Таблица scheduled_notifications успешно создана');
|
||||
}
|
||||
|
||||
console.log('✅ Исправление базы данных завершено успешно');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при исправлении базы данных:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
async function checkTableExists(tableName) {
|
||||
const result = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
);
|
||||
`, [tableName]);
|
||||
|
||||
const exists = result.rows[0].exists;
|
||||
console.log(`${exists ? '✅' : '❌'} Таблица ${tableName} ${exists ? 'существует' : 'отсутствует'}`);
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
fixDatabase();
|
||||
48
scripts/fix_all_notifications.js
Normal file
48
scripts/fix_all_notifications.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Комплексный скрипт для исправления всех проблем с уведомлениями
|
||||
* Запускает последовательно оба скрипта исправления
|
||||
*/
|
||||
|
||||
const { exec } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔧 Запуск комплексного исправления проблем с уведомлениями...');
|
||||
|
||||
// Путь к скриптам
|
||||
const fixNotificationCallbacksScript = path.join(__dirname, 'fix_notification_callbacks.js');
|
||||
const updateBotWithNotificationsScript = path.join(__dirname, 'update_bot_with_notifications.js');
|
||||
|
||||
// Запуск первого скрипта для исправления таблиц и колонок
|
||||
console.log('\n📊 Шаг 1/2: Проверка и исправление таблиц базы данных...');
|
||||
exec(`node ${fixNotificationCallbacksScript}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`❌ Ошибка при запуске скрипта исправления таблиц: ${error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(stdout);
|
||||
|
||||
if (stderr) {
|
||||
console.error(`❌ Ошибки при выполнении скрипта: ${stderr}`);
|
||||
}
|
||||
|
||||
// Запуск второго скрипта для обновления bot.ts
|
||||
console.log('\n📝 Шаг 2/2: Обновление файла bot.ts для регистрации обработчиков уведомлений...');
|
||||
exec(`node ${updateBotWithNotificationsScript}`, (error2, stdout2, stderr2) => {
|
||||
if (error2) {
|
||||
console.error(`❌ Ошибка при запуске скрипта обновления bot.ts: ${error2}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(stdout2);
|
||||
|
||||
if (stderr2) {
|
||||
console.error(`❌ Ошибки при выполнении скрипта: ${stderr2}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ Все исправления успешно выполнены!');
|
||||
console.log('🔄 Пожалуйста, перезапустите бота для применения изменений:');
|
||||
console.log(' npm run start');
|
||||
console.log('\n💡 Уведомления должны теперь работать корректно!');
|
||||
});
|
||||
});
|
||||
332
scripts/fix_notification_callbacks.js
Normal file
332
scripts/fix_notification_callbacks.js
Normal file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* Скрипт для проверки и исправления проблем с обработчиками уведомлений в боте
|
||||
*/
|
||||
|
||||
const { Client } = require('pg');
|
||||
const fs = require('fs');
|
||||
|
||||
// Конфигурация базы данных
|
||||
const dbConfig = {
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
database: 'telegram_tinder',
|
||||
user: 'postgres',
|
||||
password: 'postgres'
|
||||
};
|
||||
|
||||
// Подключение к базе данных
|
||||
const client = new Client(dbConfig);
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log('Подключение к базе данных...');
|
||||
await client.connect();
|
||||
console.log('Успешно подключено к базе данных');
|
||||
|
||||
// Шаг 1: Проверка существования необходимых таблиц для уведомлений
|
||||
console.log('\n=== Проверка таблиц для уведомлений ===');
|
||||
|
||||
// Проверяем таблицу notifications
|
||||
let notificationsTableExists = await checkTableExists('notifications');
|
||||
if (!notificationsTableExists) {
|
||||
console.log('Таблица notifications не найдена. Создаем...');
|
||||
await createNotificationsTable();
|
||||
console.log('Таблица notifications успешно создана');
|
||||
} else {
|
||||
console.log('Таблица notifications уже существует');
|
||||
}
|
||||
|
||||
// Проверяем таблицу scheduled_notifications
|
||||
let scheduledNotificationsTableExists = await checkTableExists('scheduled_notifications');
|
||||
if (!scheduledNotificationsTableExists) {
|
||||
console.log('Таблица scheduled_notifications не найдена. Создаем...');
|
||||
await createScheduledNotificationsTable();
|
||||
console.log('Таблица scheduled_notifications успешно создана');
|
||||
} else {
|
||||
console.log('Таблица scheduled_notifications уже существует');
|
||||
}
|
||||
|
||||
// Проверяем таблицу notification_templates
|
||||
let notificationTemplatesTableExists = await checkTableExists('notification_templates');
|
||||
if (!notificationTemplatesTableExists) {
|
||||
console.log('Таблица notification_templates не найдена. Создаем...');
|
||||
await createNotificationTemplatesTable();
|
||||
console.log('Таблица notification_templates успешно создана');
|
||||
console.log('Заполняем таблицу базовыми шаблонами...');
|
||||
await populateDefaultTemplates();
|
||||
console.log('Шаблоны успешно добавлены');
|
||||
} else {
|
||||
console.log('Таблица notification_templates уже существует');
|
||||
}
|
||||
|
||||
// Шаг 2: Проверка существования столбца notification_settings в таблице users
|
||||
console.log('\n=== Проверка столбца notification_settings в таблице users ===');
|
||||
|
||||
const notificationSettingsColumnExists = await checkColumnExists('users', 'notification_settings');
|
||||
if (!notificationSettingsColumnExists) {
|
||||
console.log('Столбец notification_settings не найден. Добавляем...');
|
||||
await addNotificationSettingsColumn();
|
||||
console.log('Столбец notification_settings успешно добавлен');
|
||||
} else {
|
||||
console.log('Столбец notification_settings уже существует');
|
||||
}
|
||||
|
||||
// Шаг 3: Проверка существования столбцов state и state_data в таблице users
|
||||
console.log('\n=== Проверка столбцов state и state_data в таблице users ===');
|
||||
|
||||
const stateColumnExists = await checkColumnExists('users', 'state');
|
||||
if (!stateColumnExists) {
|
||||
console.log('Столбец state не найден. Добавляем...');
|
||||
await addStateColumn();
|
||||
console.log('Столбец state успешно добавлен');
|
||||
} else {
|
||||
console.log('Столбец state уже существует');
|
||||
}
|
||||
|
||||
const stateDataColumnExists = await checkColumnExists('users', 'state_data');
|
||||
if (!stateDataColumnExists) {
|
||||
console.log('Столбец state_data не найден. Добавляем...');
|
||||
await addStateDataColumn();
|
||||
console.log('Столбец state_data успешно добавлен');
|
||||
} else {
|
||||
console.log('Столбец state_data уже существует');
|
||||
}
|
||||
|
||||
console.log('\nВсе таблицы и столбцы успешно проверены и созданы при необходимости.');
|
||||
console.log('Механизм уведомлений должен работать корректно.');
|
||||
|
||||
console.log('\n=== Проверка регистрации обработчиков уведомлений ===');
|
||||
console.log('Подсказка: убедитесь, что в файле bot.ts создается экземпляр NotificationHandlers и регистрируются его обработчики:');
|
||||
console.log(`
|
||||
// Настройка обработчиков уведомлений
|
||||
const notificationHandlers = new NotificationHandlers(bot);
|
||||
notificationHandlers.register();
|
||||
|
||||
// Запуск обработчика запланированных уведомлений
|
||||
setInterval(() => {
|
||||
const notificationService = new NotificationService(bot);
|
||||
notificationService.processScheduledNotifications();
|
||||
}, 60000); // Проверяем каждую минуту
|
||||
`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка выполнения скрипта:', error);
|
||||
} finally {
|
||||
await client.end();
|
||||
console.log('\nСоединение с базой данных закрыто.');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkTableExists(tableName) {
|
||||
const query = `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_name = $1
|
||||
) as exists
|
||||
`;
|
||||
|
||||
const result = await client.query(query, [tableName]);
|
||||
return result.rows[0].exists;
|
||||
}
|
||||
|
||||
async function checkColumnExists(tableName, columnName) {
|
||||
const query = `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_name = $1 AND column_name = $2
|
||||
) as exists
|
||||
`;
|
||||
|
||||
const result = await client.query(query, [tableName, columnName]);
|
||||
return result.rows[0].exists;
|
||||
}
|
||||
|
||||
async function createNotificationsTable() {
|
||||
await client.query(`
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE notifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
data JSONB,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
async function createScheduledNotificationsTable() {
|
||||
await client.query(`
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE scheduled_notifications (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
data JSONB,
|
||||
scheduled_at TIMESTAMP NOT NULL,
|
||||
processed BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
async function createNotificationTemplatesTable() {
|
||||
await client.query(`
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE notification_templates (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
type VARCHAR(50) NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
message_template TEXT NOT NULL,
|
||||
button_template JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
async function addNotificationSettingsColumn() {
|
||||
await client.query(`
|
||||
ALTER TABLE users
|
||||
ADD COLUMN notification_settings JSONB DEFAULT '{
|
||||
"newMatches": true,
|
||||
"newMessages": true,
|
||||
"newLikes": true,
|
||||
"reminders": true,
|
||||
"dailySummary": true,
|
||||
"timePreference": "evening",
|
||||
"doNotDisturb": false
|
||||
}'::jsonb
|
||||
`);
|
||||
}
|
||||
|
||||
async function addStateColumn() {
|
||||
await client.query(`
|
||||
ALTER TABLE users ADD COLUMN state VARCHAR(255) NULL
|
||||
`);
|
||||
}
|
||||
|
||||
async function addStateDataColumn() {
|
||||
await client.query(`
|
||||
ALTER TABLE users ADD COLUMN state_data JSONB DEFAULT '{}'::jsonb
|
||||
`);
|
||||
}
|
||||
|
||||
async function populateDefaultTemplates() {
|
||||
const templates = [
|
||||
{
|
||||
type: 'new_like',
|
||||
title: 'Новый лайк!',
|
||||
message_template: '❤️ *{{name}}* поставил(а) вам лайк!\n\nВозраст: {{age}}\n{{city}}\n\nОтветьте взаимностью или посмотрите профиль.',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
|
||||
[
|
||||
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
|
||||
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
|
||||
],
|
||||
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'super_like',
|
||||
title: 'Супер-лайк!',
|
||||
message_template: '⭐️ *{{name}}* отправил(а) вам супер-лайк!\n\nВозраст: {{age}}\n{{city}}\n\nВы произвели особое впечатление! Ответьте взаимностью или посмотрите профиль.',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }],
|
||||
[
|
||||
{ text: '❤️ Лайк в ответ', callback_data: 'like_back:{{userId}}' },
|
||||
{ text: '⛔️ Пропустить', callback_data: 'dislike_profile:{{userId}}' }
|
||||
],
|
||||
[{ text: '💕 Открыть все лайки', callback_data: 'view_likes' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'new_match',
|
||||
title: 'Новый матч!',
|
||||
message_template: '🎊 *Ура! Это взаимно!* 🎊\n\nВы и *{{name}}* понравились друг другу!\nВозраст: {{age}}\n{{city}}\n\nСделайте первый шаг - напишите сообщение!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '💬 Начать общение', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[
|
||||
{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' },
|
||||
{ text: '📋 Все матчи', callback_data: 'view_matches' }
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'new_message',
|
||||
title: 'Новое сообщение!',
|
||||
message_template: '💌 *Новое сообщение!*\n\nОт: *{{name}}*\n\n"{{message}}"\n\nОтветьте на сообщение прямо сейчас!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📩 Ответить', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[
|
||||
{ text: '👤 Профиль', callback_data: 'view_profile:{{userId}}' },
|
||||
{ text: '📋 Все чаты', callback_data: 'view_matches' }
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'match_reminder',
|
||||
title: 'Напоминание о матче',
|
||||
message_template: '💕 У вас есть матч с *{{name}}*, но вы еще не начали общение!\n\nНе упустите шанс познакомиться поближе!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '💬 Начать общение', callback_data: 'open_chat:{{matchId}}' }],
|
||||
[{ text: '👀 Посмотреть профиль', callback_data: 'view_profile:{{userId}}' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'inactive_matches',
|
||||
title: 'Неактивные матчи',
|
||||
message_template: '⏰ У вас {{count}} неактивных матчей!\n\nПродолжите общение, чтобы не упустить интересные знакомства!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📋 Открыть матчи', callback_data: 'view_matches' }],
|
||||
[{ text: '💕 Смотреть новые анкеты', callback_data: 'start_browsing' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'like_summary',
|
||||
title: 'Сводка лайков',
|
||||
message_template: '💖 У вас {{count}} новых лайков!\n\nПосмотрите, кто проявил к вам интерес сегодня!',
|
||||
button_template: {
|
||||
inline_keyboard: [
|
||||
[{ text: '👀 Посмотреть лайки', callback_data: 'view_likes' }],
|
||||
[{ text: '💕 Начать знакомиться', callback_data: 'start_browsing' }]
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const template of templates) {
|
||||
await client.query(`
|
||||
INSERT INTO notification_templates (type, title, message_template, button_template)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (type) DO UPDATE
|
||||
SET title = EXCLUDED.title,
|
||||
message_template = EXCLUDED.message_template,
|
||||
button_template = EXCLUDED.button_template,
|
||||
updated_at = NOW()
|
||||
`, [
|
||||
template.type,
|
||||
template.title,
|
||||
template.message_template,
|
||||
JSON.stringify(template.button_template)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Запуск скрипта
|
||||
main();
|
||||
73
scripts/setPremiumStatus.js
Normal file
73
scripts/setPremiumStatus.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// Скрипт для установки премиум-статуса всем пользователям
|
||||
require('dotenv').config();
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Проверяем и выводим параметры подключения
|
||||
console.log('Параметры подключения к БД:');
|
||||
console.log('DB_USERNAME:', process.env.DB_USERNAME);
|
||||
console.log('DB_HOST:', process.env.DB_HOST);
|
||||
console.log('DB_NAME:', process.env.DB_NAME);
|
||||
console.log('DB_PASSWORD:', process.env.DB_PASSWORD ? '[указан]' : '[не указан]');
|
||||
console.log('DB_PORT:', process.env.DB_PORT);
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function setAllUsersToPremium() {
|
||||
try {
|
||||
console.log('Устанавливаем премиум-статус для всех пользователей...');
|
||||
|
||||
// Проверка соединения с БД
|
||||
console.log('Проверка соединения с БД...');
|
||||
const testResult = await pool.query('SELECT NOW()');
|
||||
console.log('✅ Соединение успешно:', testResult.rows[0].now);
|
||||
|
||||
// Проверка наличия столбца premium
|
||||
console.log('Проверяем наличие столбца premium в таблице users...');
|
||||
|
||||
const checkResult = await pool.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('🔄 Добавляем столбец premium...');
|
||||
await pool.query(`ALTER TABLE users ADD COLUMN premium BOOLEAN DEFAULT false;`);
|
||||
console.log('✅ Столбец premium успешно добавлен');
|
||||
} else {
|
||||
console.log('✅ Столбец premium уже существует');
|
||||
}
|
||||
|
||||
// Устанавливаем premium=true для всех пользователей
|
||||
const updateResult = await pool.query(`
|
||||
UPDATE users
|
||||
SET premium = true
|
||||
WHERE true
|
||||
RETURNING id, telegram_id, premium
|
||||
`);
|
||||
|
||||
console.log(`✅ Успешно установлен премиум-статус для ${updateResult.rows.length} пользователей:`);
|
||||
updateResult.rows.forEach(row => {
|
||||
console.log(`ID: ${row.id.substr(0, 8)}... | Telegram ID: ${row.telegram_id} | Premium: ${row.premium}`);
|
||||
});
|
||||
|
||||
console.log('🎉 Все пользователи теперь имеют премиум-статус!');
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка при установке премиум-статуса:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
console.log('Соединение с базой данных закрыто');
|
||||
}
|
||||
}
|
||||
|
||||
setAllUsersToPremium();
|
||||
85
scripts/testCallbacks.js
Normal file
85
scripts/testCallbacks.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// Скрипт для проверки работы callback-хэндлеров и уведомлений
|
||||
require('dotenv').config();
|
||||
const TelegramBot = require('node-telegram-bot-api');
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
// Функция для имитации callback-запроса к боту
|
||||
async function testCallback() {
|
||||
try {
|
||||
console.log('Начинаем тестирование callback-хэндлеров и уведомлений...');
|
||||
|
||||
// Используем последнего пользователя из базы данных
|
||||
const userResult = await pool.query(`
|
||||
SELECT * FROM users ORDER BY last_active_at DESC NULLS LAST LIMIT 1
|
||||
`);
|
||||
|
||||
if (userResult.rows.length === 0) {
|
||||
console.error('❌ Пользователи не найдены в базе данных');
|
||||
return;
|
||||
}
|
||||
|
||||
const user = userResult.rows[0];
|
||||
console.log(`Выбран тестовый пользователь: ${user.first_name || 'Без имени'} (ID: ${user.telegram_id})`);
|
||||
|
||||
// Получаем токен бота из переменных окружения
|
||||
const token = process.env.TELEGRAM_BOT_TOKEN;
|
||||
if (!token) {
|
||||
console.error('❌ Токен бота не найден в переменных окружения');
|
||||
return;
|
||||
}
|
||||
|
||||
// Создаем экземпляр бота
|
||||
const bot = new TelegramBot(token);
|
||||
|
||||
// Отправляем тестовое уведомление пользователю
|
||||
console.log(`Отправляем тестовое уведомление пользователю ID: ${user.telegram_id}...`);
|
||||
|
||||
try {
|
||||
const result = await bot.sendMessage(
|
||||
user.telegram_id,
|
||||
`🔔 *Тестовое уведомление*\n\nЭто проверка работы уведомлений и callback-хэндлеров.\n\nВаш премиум-статус: ${user.premium ? '✅ Активен' : '❌ Не активен'}`,
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: '🔔 Уведомления', callback_data: 'notification_settings' },
|
||||
{ text: '❤️ Профиль', callback_data: 'view_profile' }
|
||||
],
|
||||
[
|
||||
{ text: '⚙️ Настройки', callback_data: 'settings' }
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log('✅ Тестовое сообщение успешно отправлено!');
|
||||
console.log('Информация о сообщении:', JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка при отправке тестового сообщения:', error.message);
|
||||
if (error.response && error.response.body) {
|
||||
console.error('Детали ошибки:', JSON.stringify(error.response.body, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка при тестировании:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
console.log('Соединение с базой данных закрыто');
|
||||
console.log('Тестирование завершено!');
|
||||
}
|
||||
}
|
||||
|
||||
// Запускаем тестирование
|
||||
testCallback();
|
||||
81
scripts/testVipMethod.js
Normal file
81
scripts/testVipMethod.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// Скрипт для тестирования метода checkPremiumStatus
|
||||
require('dotenv').config();
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function testCheckPremiumMethod() {
|
||||
try {
|
||||
console.log('Тестирование метода checkPremiumStatus...');
|
||||
|
||||
// Получаем пользователя для тестирования
|
||||
const userResult = await pool.query(`
|
||||
SELECT id, telegram_id, first_name, username, premium
|
||||
FROM users
|
||||
ORDER BY last_active_at DESC NULLS LAST
|
||||
LIMIT 1
|
||||
`);
|
||||
|
||||
if (userResult.rows.length === 0) {
|
||||
console.error('❌ Пользователи не найдены в базе данных');
|
||||
return;
|
||||
}
|
||||
|
||||
const user = userResult.rows[0];
|
||||
console.log(`Выбран тестовый пользователь: ${user.first_name || user.username || 'Без имени'} (Telegram ID: ${user.telegram_id})`);
|
||||
console.log(`Текущий премиум-статус: ${user.premium ? '✅ Активен' : '❌ Не активен'}`);
|
||||
|
||||
// Проверка работы метода checkPremiumStatus
|
||||
console.log('\nЭмулируем вызов метода checkPremiumStatus из vipService:');
|
||||
const result = await pool.query(`
|
||||
SELECT id, premium
|
||||
FROM users
|
||||
WHERE telegram_id = $1
|
||||
`, [user.telegram_id]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
console.log('❌ Пользователь не найден');
|
||||
} else {
|
||||
const isPremium = result.rows[0].premium || false;
|
||||
console.log(`Результат метода: isPremium = ${isPremium ? '✅ true' : '❌ false'}`);
|
||||
|
||||
if (!isPremium) {
|
||||
console.log('\nПремиум-статус отсутствует. Устанавливаем премиум...');
|
||||
await pool.query(`
|
||||
UPDATE users
|
||||
SET premium = true
|
||||
WHERE telegram_id = $1
|
||||
`, [user.telegram_id]);
|
||||
|
||||
// Проверяем обновление
|
||||
const updatedResult = await pool.query(`
|
||||
SELECT premium
|
||||
FROM users
|
||||
WHERE telegram_id = $1
|
||||
`, [user.telegram_id]);
|
||||
|
||||
const updatedPremium = updatedResult.rows[0].premium;
|
||||
console.log(`Обновленный статус: isPremium = ${updatedPremium ? '✅ true' : '❌ false'}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Тестирование завершено');
|
||||
console.log('🔧 Теперь проверьте функциональность VIP поиска в боте');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка при тестировании:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
console.log('Соединение с базой данных закрыто');
|
||||
}
|
||||
}
|
||||
|
||||
// Запускаем тест
|
||||
testCheckPremiumMethod();
|
||||
75
scripts/testVipStatus.js
Normal file
75
scripts/testVipStatus.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// Скрипт для тестирования VIP функционала
|
||||
require('dotenv').config();
|
||||
const { Pool } = require('pg');
|
||||
|
||||
// Создаем пул соединений
|
||||
const pool = new Pool({
|
||||
user: process.env.DB_USERNAME,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
port: parseInt(process.env.DB_PORT || '5432')
|
||||
});
|
||||
|
||||
async function testVipStatus() {
|
||||
try {
|
||||
console.log('Тестирование функционала VIP статуса...');
|
||||
|
||||
// Получаем список пользователей с информацией о premium статусе
|
||||
const users = await pool.query(`
|
||||
SELECT id, telegram_id, username, first_name, premium
|
||||
FROM users
|
||||
ORDER BY last_active_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('Список пользователей и их премиум статус:');
|
||||
users.rows.forEach(user => {
|
||||
console.log(`ID: ${user.id.substr(0, 8)}... | Telegram ID: ${user.telegram_id} | Имя: ${user.first_name || user.username || 'Не указано'} | Premium: ${user.premium ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
// Если premium у всех false, устанавливаем premium = true
|
||||
const nonPremiumUsers = users.rows.filter(user => !user.premium);
|
||||
if (nonPremiumUsers.length > 0) {
|
||||
console.log('\nОбнаружены пользователи без премиум статуса. Устанавливаем премиум...');
|
||||
|
||||
for (const user of nonPremiumUsers) {
|
||||
await pool.query(`
|
||||
UPDATE users
|
||||
SET premium = true
|
||||
WHERE id = $1
|
||||
RETURNING id, telegram_id, premium
|
||||
`, [user.id]);
|
||||
|
||||
console.log(`✅ Установлен премиум для пользователя ${user.first_name || user.username || user.telegram_id}`);
|
||||
}
|
||||
} else {
|
||||
console.log('\nВсе пользователи уже имеют премиум-статус!');
|
||||
}
|
||||
|
||||
// Проверяем результат
|
||||
const updatedUsers = await pool.query(`
|
||||
SELECT id, telegram_id, username, first_name, premium
|
||||
FROM users
|
||||
ORDER BY last_active_at DESC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\nОбновленный список пользователей и их премиум статус:');
|
||||
updatedUsers.rows.forEach(user => {
|
||||
console.log(`ID: ${user.id.substr(0, 8)}... | Telegram ID: ${user.telegram_id} | Имя: ${user.first_name || user.username || 'Не указано'} | Premium: ${user.premium ? '✅' : '❌'}`);
|
||||
});
|
||||
|
||||
console.log('\n✅ Тестирование VIP функционала завершено');
|
||||
console.log('🔧 Проверьте доступность VIP поиска в боте через меню или команды');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка при тестировании VIP статуса:', error);
|
||||
} finally {
|
||||
await pool.end();
|
||||
console.log('Соединение с базой данных закрыто');
|
||||
}
|
||||
}
|
||||
|
||||
// Запускаем тест
|
||||
testVipStatus();
|
||||
104
scripts/update_bot_with_notifications.js
Normal file
104
scripts/update_bot_with_notifications.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Скрипт для проверки и исправления регистрации NotificationHandlers в bot.ts
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const botFilePath = path.join(__dirname, '../src/bot.ts');
|
||||
|
||||
// Проверка существования файла bot.ts
|
||||
if (!fs.existsSync(botFilePath)) {
|
||||
console.error(`❌ Файл ${botFilePath} не найден`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Чтение содержимого файла bot.ts
|
||||
let botContent = fs.readFileSync(botFilePath, 'utf8');
|
||||
|
||||
// Проверка импорта NotificationHandlers
|
||||
if (!botContent.includes('import { NotificationHandlers }')) {
|
||||
console.log('Добавляем импорт NotificationHandlers в bot.ts...');
|
||||
|
||||
// Находим последний импорт
|
||||
const importRegex = /^import.*?;/gms;
|
||||
const matches = [...botContent.matchAll(importRegex)];
|
||||
|
||||
if (matches.length > 0) {
|
||||
const lastImport = matches[matches.length - 1][0];
|
||||
const lastImportIndex = botContent.lastIndexOf(lastImport) + lastImport.length;
|
||||
|
||||
// Добавляем импорт NotificationHandlers
|
||||
botContent =
|
||||
botContent.slice(0, lastImportIndex) +
|
||||
'\nimport { NotificationHandlers } from \'./handlers/notificationHandlers\';\n' +
|
||||
botContent.slice(lastImportIndex);
|
||||
|
||||
console.log('✅ Импорт NotificationHandlers добавлен');
|
||||
} else {
|
||||
console.error('❌ Не удалось найти место для добавления импорта');
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка объявления NotificationHandlers в классе
|
||||
if (!botContent.includes('private notificationHandlers')) {
|
||||
console.log('Добавляем объявление notificationHandlers в класс...');
|
||||
|
||||
const classPropertiesRegex = /class TelegramTinderBot {([^}]+?)constructor/s;
|
||||
const classPropertiesMatch = botContent.match(classPropertiesRegex);
|
||||
|
||||
if (classPropertiesMatch) {
|
||||
const classProperties = classPropertiesMatch[1];
|
||||
const updatedProperties = classProperties + ' private notificationHandlers: NotificationHandlers;\n ';
|
||||
|
||||
botContent = botContent.replace(classPropertiesRegex, `class TelegramTinderBot {${updatedProperties}constructor`);
|
||||
|
||||
console.log('✅ Объявление notificationHandlers добавлено');
|
||||
} else {
|
||||
console.error('❌ Не удалось найти место для добавления объявления notificationHandlers');
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка создания экземпляра NotificationHandlers в конструкторе
|
||||
if (!botContent.includes('this.notificationHandlers = new NotificationHandlers')) {
|
||||
console.log('Добавляем инициализацию notificationHandlers в конструктор...');
|
||||
|
||||
const initializationRegex = /(this\.callbackHandlers = new CallbackHandlers[^;]+;)/;
|
||||
const initializationMatch = botContent.match(initializationRegex);
|
||||
|
||||
if (initializationMatch) {
|
||||
const callbackHandlersInit = initializationMatch[1];
|
||||
const updatedInit = callbackHandlersInit + '\n this.notificationHandlers = new NotificationHandlers(this.bot);';
|
||||
|
||||
botContent = botContent.replace(initializationRegex, updatedInit);
|
||||
|
||||
console.log('✅ Инициализация notificationHandlers добавлена');
|
||||
} else {
|
||||
console.error('❌ Не удалось найти место для добавления инициализации notificationHandlers');
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка регистрации notificationHandlers в методе registerHandlers
|
||||
if (!botContent.includes('this.notificationHandlers.register()')) {
|
||||
console.log('Добавляем регистрацию notificationHandlers...');
|
||||
|
||||
const registerHandlersRegex = /(private registerHandlers\(\): void {[^}]+?)}/s;
|
||||
const registerHandlersMatch = botContent.match(registerHandlersRegex);
|
||||
|
||||
if (registerHandlersMatch) {
|
||||
const registerHandlersBody = registerHandlersMatch[1];
|
||||
const updatedBody = registerHandlersBody + '\n // Обработчики уведомлений\n this.notificationHandlers.register();\n }';
|
||||
|
||||
botContent = botContent.replace(registerHandlersRegex, updatedBody);
|
||||
|
||||
console.log('✅ Регистрация notificationHandlers добавлена');
|
||||
} else {
|
||||
console.error('❌ Не удалось найти место для добавления регистрации notificationHandlers');
|
||||
}
|
||||
}
|
||||
|
||||
// Запись обновленного содержимого в файл
|
||||
fs.writeFileSync(botFilePath, botContent, 'utf8');
|
||||
|
||||
console.log('✅ Файл bot.ts успешно обновлен');
|
||||
console.log('🔔 Перезапустите бота для применения изменений');
|
||||
51
src/bot.ts
51
src/bot.ts
@@ -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 };
|
||||
|
||||
@@ -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, '❌ Произошла ошибка при загрузке настроек уведомлений. Попробуйте позже.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
606
src/handlers/callbackHandlers.ts.backup-1758166633763
Normal file
606
src/handlers/callbackHandlers.ts.backup-1758166633763
Normal 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, '❌ Произошла ошибка при загрузке настроек уведомлений.');
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/handlers/callbackHandlers.ts.original
Normal file
BIN
src/handlers/callbackHandlers.ts.original
Normal file
Binary file not shown.
606
src/handlers/callbackHandlers.ts.stub
Normal file
606
src/handlers/callbackHandlers.ts.stub
Normal 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, '❌ Произошла ошибка при загрузке настроек уведомлений.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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: [
|
||||
|
||||
644
src/handlers/notificationHandlers.ts
Normal file
644
src/handlers/notificationHandlers.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
1316
src/services/notificationService.ts.new
Normal file
1316
src/services/notificationService.ts.new
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user