273 lines
12 KiB
JavaScript
273 lines
12 KiB
JavaScript
const TelegramBot = require('node-telegram-bot-api');
|
||
const logger = require('../utils/logger');
|
||
|
||
// Создаем бота
|
||
const token = process.env.TELEGRAM_BOT_TOKEN;
|
||
let bot = null;
|
||
|
||
// Добавим хранилище для кодов подтверждения
|
||
const verificationCodes = new Map(); // Формат: { telegramId: { code: '123456', token: 'auth_token', expires: timestamp } }
|
||
|
||
/**
|
||
* Инициализация Telegram бота
|
||
* @returns {Object|null} - Объект с методами для работы с ботом или null, если инициализация не удалась
|
||
*/
|
||
function initTelegramBot() {
|
||
if (!token) {
|
||
console.warn('TELEGRAM_BOT_TOKEN not set, Telegram integration disabled');
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
// Создаем бота с опцией polling
|
||
bot = new TelegramBot(token, { polling: true });
|
||
console.log('Telegram bot initialized');
|
||
|
||
// Регистрируем обработчики событий
|
||
registerHandlers();
|
||
|
||
return {
|
||
bot,
|
||
sendMessage: (chatId, text) => bot.sendMessage(chatId, text)
|
||
};
|
||
} catch (error) {
|
||
console.error('Error initializing Telegram bot:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Регистрация обработчиков событий для бота
|
||
*/
|
||
function registerHandlers() {
|
||
// Обработчик /start
|
||
bot.onText(/\/start(.*)/, async (msg, match) => {
|
||
const chatId = msg.chat.id;
|
||
const param = match[1] ? match[1].trim() : '';
|
||
|
||
console.log(`Получена команда /start с параметром: "${param}" от пользователя ${chatId}`);
|
||
|
||
if (param.startsWith('auth_')) {
|
||
// Это токен авторизации через deep link
|
||
const authToken = param.replace('auth_', '');
|
||
console.log(`Обработка токена авторизации: ${authToken}`);
|
||
|
||
try {
|
||
// Проверяем, существует ли токен
|
||
const { pool } = require('../db');
|
||
const tokenResult = await pool.query(
|
||
'SELECT user_id, expires_at FROM telegram_auth_tokens WHERE token = $1',
|
||
[authToken]
|
||
);
|
||
|
||
if (tokenResult.rows.length === 0 || new Date(tokenResult.rows[0].expires_at) < new Date()) {
|
||
bot.sendMessage(chatId, '❌ Недействительный или истекший токен авторизации.');
|
||
return;
|
||
}
|
||
|
||
// Генерируем код подтверждения
|
||
const verificationCode = Math.floor(100000 + Math.random() * 900000).toString(); // 6-значный код
|
||
|
||
// Сохраняем в хранилище
|
||
verificationCodes.set(chatId.toString(), {
|
||
code: verificationCode,
|
||
token: authToken,
|
||
expires: Date.now() + 5 * 60 * 1000 // Срок действия 5 минут
|
||
});
|
||
|
||
// Отправляем код пользователю
|
||
bot.sendMessage(chatId,
|
||
'🔐 Для завершения связывания аккаунта, пожалуйста, введите этот код:\n\n' +
|
||
`<code>${verificationCode}</code>\n\n` +
|
||
'Код действителен в течение 5 минут.',
|
||
{ parse_mode: 'HTML' }
|
||
);
|
||
} catch (error) {
|
||
console.error('Error processing auth token:', error);
|
||
bot.sendMessage(chatId, '❌ Произошла ошибка при обработке запроса авторизации.');
|
||
}
|
||
} else {
|
||
// Получаем последний активный токен для этого чата, если есть
|
||
const { pool } = require('../db');
|
||
try {
|
||
const lastTokenResult = await pool.query(`
|
||
SELECT token FROM telegram_auth_tokens
|
||
WHERE expires_at > NOW() AND used = FALSE
|
||
ORDER BY created_at DESC LIMIT 1
|
||
`);
|
||
|
||
if (lastTokenResult.rows.length > 0) {
|
||
const authToken = lastTokenResult.rows[0].token;
|
||
|
||
// Генерируем код подтверждения
|
||
const verificationCode = Math.floor(100000 + Math.random() * 900000).toString(); // 6-значный код
|
||
|
||
// Сохраняем в хранилище
|
||
verificationCodes.set(chatId.toString(), {
|
||
code: verificationCode,
|
||
token: authToken,
|
||
expires: Date.now() + 5 * 60 * 1000 // Срок действия 5 минут
|
||
});
|
||
|
||
// Отправляем код пользователю
|
||
bot.sendMessage(chatId,
|
||
'🔐 Для завершения связывания аккаунта, пожалуйста, введите этот код:\n\n' +
|
||
`<code>${verificationCode}</code>\n\n` +
|
||
'Код действителен в течение 5 минут.',
|
||
{ parse_mode: 'HTML' }
|
||
);
|
||
return;
|
||
}
|
||
} catch (error) {
|
||
console.error('Error checking last token:', error);
|
||
}
|
||
|
||
// Если нет активного токена, отправляем стандартное сообщение
|
||
bot.sendMessage(chatId,
|
||
'Привет! Я бот для аутентификации в DApp for Business.\n\n' +
|
||
'Для связи с вашим аккаунтом используйте кнопку на сайте.'
|
||
);
|
||
}
|
||
});
|
||
|
||
// Обработчик для проверки кода подтверждения
|
||
bot.on('message', async (msg) => {
|
||
const chatId = msg.chat.id;
|
||
const text = msg.text;
|
||
|
||
// Игнорируем команды
|
||
if (text && text.startsWith('/')) return;
|
||
|
||
// Проверяем, есть ли ожидающая верификация для этого чата
|
||
const verificationData = verificationCodes.get(chatId.toString());
|
||
|
||
if (verificationData && text === verificationData.code) {
|
||
// Код верный, проверяем срок действия
|
||
if (Date.now() > verificationData.expires) {
|
||
bot.sendMessage(chatId, '❌ Срок действия кода истек. Пожалуйста, начните процесс заново.');
|
||
verificationCodes.delete(chatId.toString());
|
||
return;
|
||
}
|
||
|
||
// Код верный и актуальный, завершаем аутентификацию
|
||
try {
|
||
const result = await linkTelegramAccount(chatId.toString(), verificationData.token);
|
||
|
||
if (result.success) {
|
||
bot.sendMessage(chatId,
|
||
'✅ Аутентификация успешна!\n\n' +
|
||
'Ваш Telegram аккаунт связан с DApp for Business.\n' +
|
||
'Теперь вы можете использовать бота для общения с системой.'
|
||
);
|
||
} else {
|
||
bot.sendMessage(chatId,
|
||
'❌ Ошибка аутентификации: ' + (result.error || 'неизвестная ошибка')
|
||
);
|
||
}
|
||
|
||
// Удаляем данные верификации
|
||
verificationCodes.delete(chatId.toString());
|
||
} catch (error) {
|
||
console.error('Error completing authentication:', error);
|
||
bot.sendMessage(chatId, '❌ Произошла ошибка при завершении аутентификации.');
|
||
}
|
||
} else if (verificationData) {
|
||
// Есть ожидающая верификация, но код неверный
|
||
bot.sendMessage(chatId, '❌ Неверный код. Пожалуйста, попробуйте еще раз.');
|
||
} else {
|
||
// Нет ожидающей верификации
|
||
bot.sendMessage(chatId, 'Я могу помочь с аутентификацией. Используйте кнопку на сайте для начала процесса.');
|
||
}
|
||
});
|
||
|
||
// Добавить обработку прямых команд аутентификации
|
||
bot.onText(/\/auth (.+)/, async (msg, match) => {
|
||
const chatId = msg.chat.id;
|
||
const authToken = match[1].trim();
|
||
|
||
console.log(`Получена прямая команда авторизации с токеном: ${authToken}`);
|
||
|
||
try {
|
||
// Связываем Telegram ID с аккаунтом по токену
|
||
const result = await linkTelegramAccount(chatId.toString(), authToken);
|
||
console.log(`Результат связывания: ${JSON.stringify(result)}`);
|
||
|
||
if (result.success) {
|
||
bot.sendMessage(chatId,
|
||
'✅ Аутентификация успешна!\n\n' +
|
||
'Ваш Telegram аккаунт связан с DApp for Business.\n' +
|
||
'Теперь вы можете использовать бота для общения с системой.'
|
||
);
|
||
} else {
|
||
bot.sendMessage(chatId,
|
||
'❌ Ошибка аутентификации: ' + (result.error || 'неизвестная ошибка')
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error linking telegram account:', error);
|
||
bot.sendMessage(chatId, '❌ Произошла ошибка при связывании аккаунта.');
|
||
}
|
||
});
|
||
|
||
// Обработка ошибок
|
||
bot.on('polling_error', (error) => {
|
||
logger.error(`[polling_error] ${JSON.stringify(error)}`);
|
||
});
|
||
|
||
console.log('Telegram bot handlers registered');
|
||
}
|
||
|
||
/**
|
||
* Связывание Telegram ID с аккаунтом пользователя
|
||
* @param {string} telegramId - ID пользователя в Telegram
|
||
* @param {string} authToken - Токен авторизации
|
||
* @returns {Promise<Object>} - Результат операции
|
||
*/
|
||
async function linkTelegramAccount(telegramId, authToken) {
|
||
try {
|
||
console.log(`Попытка связать Telegram ID ${telegramId} с токеном ${authToken}`);
|
||
|
||
// Здесь должен быть код для связывания через API или напрямую с БД
|
||
const { pool } = require('../db');
|
||
|
||
// Проверяем токен авторизации
|
||
const tokenResult = await pool.query(
|
||
'SELECT user_id, expires_at FROM telegram_auth_tokens WHERE token = $1',
|
||
[authToken]
|
||
);
|
||
|
||
console.log(`Результат запроса токена: ${JSON.stringify(tokenResult.rows)}`);
|
||
|
||
if (tokenResult.rows.length === 0 || new Date(tokenResult.rows[0].expires_at) < new Date()) {
|
||
console.log('Токен не найден или истек');
|
||
return { success: false, error: 'Недействительный или истекший токен' };
|
||
}
|
||
|
||
const userId = tokenResult.rows[0].user_id;
|
||
console.log(`Найден пользователь с ID: ${userId}`);
|
||
|
||
// Добавляем идентификатор Telegram для пользователя
|
||
await pool.query(
|
||
'INSERT INTO user_identities (user_id, identity_type, identity_value, verified, created_at) ' +
|
||
'VALUES ($1, $2, $3, true, NOW()) ' +
|
||
'ON CONFLICT (identity_type, identity_value) ' +
|
||
'DO UPDATE SET user_id = $1, verified = true',
|
||
[userId, 'telegram', telegramId]
|
||
);
|
||
|
||
// Отмечаем токен как использованный
|
||
await pool.query(
|
||
'UPDATE telegram_auth_tokens SET used = true WHERE token = $1',
|
||
[authToken]
|
||
);
|
||
|
||
return { success: true };
|
||
} catch (error) {
|
||
console.error('Error in linkTelegramAccount:', error);
|
||
return { success: false, error: 'Внутренняя ошибка сервера' };
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
initTelegramBot
|
||
};
|