Описание изменений
This commit is contained in:
@@ -1,218 +1,143 @@
|
||||
const TelegramBot = require('node-telegram-bot-api');
|
||||
const logger = require('../utils/logger');
|
||||
const { pool } = require('../db');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Создаем бота
|
||||
const token = process.env.TELEGRAM_BOT_TOKEN;
|
||||
let bot = null;
|
||||
|
||||
/**
|
||||
* Функция для отправки кода подтверждения
|
||||
*/
|
||||
async function sendVerificationCode(chatId) {
|
||||
try {
|
||||
// Генерируем код и токен
|
||||
const code = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
const authToken = crypto.randomBytes(32).toString('hex');
|
||||
|
||||
// Создаем пользователя и сохраняем код в базу данных
|
||||
const result = await pool.query(
|
||||
`WITH new_user AS (
|
||||
INSERT INTO users (created_at)
|
||||
VALUES (NOW())
|
||||
RETURNING id
|
||||
)
|
||||
INSERT INTO telegram_auth_tokens
|
||||
(user_id, token, verification_code, telegram_id, expires_at)
|
||||
VALUES (
|
||||
(SELECT id FROM new_user),
|
||||
$1, $2, $3,
|
||||
NOW() + INTERVAL '5 minutes'
|
||||
)
|
||||
RETURNING user_id`,
|
||||
[authToken, code, chatId.toString()]
|
||||
);
|
||||
|
||||
// Отправляем код с инлайн-кнопкой
|
||||
const sentMessage = await bot.sendMessage(chatId,
|
||||
'Привет! Я бот для аутентификации в DApp for Business.\n\n' +
|
||||
'🔐 Ваш код подтверждения:\n\n' +
|
||||
`<code>${code}</code>\n\n` +
|
||||
'Введите этот код на сайте для завершения авторизации.\n' +
|
||||
'Код действителен в течение 5 минут.',
|
||||
{
|
||||
parse_mode: 'HTML',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: '🔄 Получить новый код', callback_data: 'new_code' }]
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Удаляем сообщение через 30 секунд
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await bot.deleteMessage(chatId, sentMessage.message_id);
|
||||
await bot.sendMessage(chatId,
|
||||
'Для получения нового кода используйте команду /start или меню команд',
|
||||
{
|
||||
reply_markup: {
|
||||
keyboard: [
|
||||
[{ text: '/start' }]
|
||||
],
|
||||
resize_keyboard: true,
|
||||
persistent: true
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error deleting message:', error);
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
return { code, token: authToken, userId: result.rows[0].user_id };
|
||||
} catch (error) {
|
||||
console.error('Error sending verification code:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для проверки кода
|
||||
*/
|
||||
async function verifyCode(code) {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`SELECT token, telegram_id, user_id
|
||||
FROM telegram_auth_tokens
|
||||
WHERE verification_code = $1
|
||||
AND expires_at > NOW()
|
||||
AND NOT used`,
|
||||
[code]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return { success: false, error: 'Неверный или истекший код' };
|
||||
}
|
||||
|
||||
const { token, telegram_id, user_id } = result.rows[0];
|
||||
|
||||
// Помечаем токен как использованный
|
||||
await pool.query(
|
||||
'UPDATE telegram_auth_tokens SET used = true WHERE token = $1',
|
||||
[token]
|
||||
);
|
||||
|
||||
// Добавляем Telegram ID в таблицу идентификаторов
|
||||
await pool.query(
|
||||
`INSERT INTO user_identities
|
||||
(user_id, identity_type, identity_value, verified, created_at)
|
||||
VALUES ($1, 'telegram', $2, true, NOW())
|
||||
ON CONFLICT (identity_type, identity_value)
|
||||
DO UPDATE SET verified = true`,
|
||||
[user_id, telegram_id]
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
telegramId: telegram_id,
|
||||
userId: user_id
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error verifying code:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация Telegram бота
|
||||
*/
|
||||
function initTelegramBot() {
|
||||
if (!token) {
|
||||
console.warn('TELEGRAM_BOT_TOKEN not set');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Создаем бота с опцией обработки ошибок
|
||||
bot = new TelegramBot(token, {
|
||||
polling: {
|
||||
autoStart: true,
|
||||
params: {
|
||||
timeout: 10
|
||||
}
|
||||
},
|
||||
class TelegramBotService {
|
||||
constructor(token) {
|
||||
this.bot = new TelegramBot(token, {
|
||||
polling: true,
|
||||
request: {
|
||||
timeout: 30000, // увеличиваем таймаут до 30 секунд
|
||||
proxy: process.env.HTTPS_PROXY // используем прокси если есть
|
||||
timeout: 30000 // 30 секунд таймаут
|
||||
}
|
||||
});
|
||||
this.verificationCodes = new Map();
|
||||
this.setupHandlers();
|
||||
|
||||
logger.info('TelegramBot service initialized');
|
||||
}
|
||||
|
||||
console.log('Telegram bot initialized');
|
||||
|
||||
// Очищаем все предыдущие обработчики
|
||||
bot.removeAllListeners();
|
||||
|
||||
// Устанавливаем команды бота с обработкой ошибок
|
||||
bot.setMyCommands([
|
||||
{ command: '/start', description: 'Получить код подтверждения' },
|
||||
{ command: '/help', description: 'Показать справку' }
|
||||
]).catch(error => {
|
||||
console.warn('Error setting bot commands:', error);
|
||||
// Продолжаем работу даже если не удалось установить команды
|
||||
setupHandlers() {
|
||||
this.bot.on('message', this.handleMessage.bind(this));
|
||||
this.bot.on('callback_query', this.handleCallbackQuery.bind(this));
|
||||
|
||||
// Обработка ошибок
|
||||
this.bot.on('polling_error', (error) => {
|
||||
logger.error('Telegram polling error:', error);
|
||||
});
|
||||
|
||||
this.bot.on('error', (error) => {
|
||||
logger.error('Telegram bot error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Обработчик команды /start
|
||||
bot.onText(/\/start/, async (msg) => {
|
||||
async handleMessage(msg) {
|
||||
try {
|
||||
const chatId = msg.chat.id;
|
||||
try {
|
||||
await sendVerificationCode(chatId);
|
||||
} catch (error) {
|
||||
console.error('Error handling /start:', error);
|
||||
await bot.sendMessage(chatId, 'Произошла ошибка. Пожалуйста, попробуйте позже.')
|
||||
.catch(err => console.error('Error sending error message:', err));
|
||||
const text = msg.text;
|
||||
|
||||
logger.info(`Received message from ${chatId}: ${text}`);
|
||||
|
||||
if (text.startsWith('/start')) {
|
||||
await this.handleStart(msg);
|
||||
} else if (this.verificationCodes.has(chatId)) {
|
||||
await this.handleVerificationCode(msg);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error handling message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик ошибок polling
|
||||
bot.on('polling_error', (error) => {
|
||||
console.error('Telegram bot polling error:', error);
|
||||
// Перезапускаем polling при ошибке
|
||||
setTimeout(() => {
|
||||
try {
|
||||
bot.startPolling();
|
||||
} catch (e) {
|
||||
console.error('Error restarting polling:', e);
|
||||
async handleCallbackQuery(query) {
|
||||
try {
|
||||
const chatId = query.message.chat.id;
|
||||
await this.bot.answerCallbackQuery(query.id);
|
||||
|
||||
logger.info(`Handled callback query from ${chatId}`);
|
||||
} catch (error) {
|
||||
logger.error('Error handling callback query:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async handleStart(msg) {
|
||||
const chatId = msg.chat.id;
|
||||
try {
|
||||
await this.bot.sendMessage(
|
||||
chatId,
|
||||
'Добро пожаловать! Используйте этого бота для аутентификации в приложении.'
|
||||
);
|
||||
logger.info(`Sent welcome message to ${chatId}`);
|
||||
} catch (error) {
|
||||
logger.error(`Error sending welcome message to ${chatId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
async handleVerificationCode(msg) {
|
||||
const chatId = msg.chat.id;
|
||||
const code = msg.text.trim();
|
||||
|
||||
try {
|
||||
const verificationData = this.verificationCodes.get(chatId);
|
||||
|
||||
if (!verificationData) {
|
||||
await this.bot.sendMessage(chatId, 'Нет активного кода подтверждения.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() > verificationData.expires) {
|
||||
this.verificationCodes.delete(chatId);
|
||||
await this.bot.sendMessage(chatId, 'Код подтверждения истек. Запросите новый.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (verificationData.code === code) {
|
||||
await this.bot.sendMessage(chatId, 'Код подтвержден успешно!');
|
||||
this.verificationCodes.delete(chatId);
|
||||
} else {
|
||||
await this.bot.sendMessage(chatId, 'Неверный код. Попробуйте еще раз.');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error handling verification code for ${chatId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
async sendVerificationCode(chatId, code) {
|
||||
try {
|
||||
// Сохраняем код с временем истечения (15 минут)
|
||||
this.verificationCodes.set(chatId, {
|
||||
code,
|
||||
expires: Date.now() + 15 * 60 * 1000
|
||||
});
|
||||
|
||||
await this.bot.sendMessage(
|
||||
chatId,
|
||||
`Ваш код подтверждения: ${code}\nВведите его в приложении.`
|
||||
);
|
||||
|
||||
logger.info(`Sent verification code to ${chatId}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error sending verification code to ${chatId}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async verifyCode(code) {
|
||||
try {
|
||||
for (const [chatId, data] of this.verificationCodes.entries()) {
|
||||
if (data.code === code) {
|
||||
if (Date.now() > data.expires) {
|
||||
this.verificationCodes.delete(chatId);
|
||||
return { success: false, error: 'Код истек' };
|
||||
}
|
||||
this.verificationCodes.delete(chatId);
|
||||
return { success: true, telegramId: chatId.toString() };
|
||||
}
|
||||
}, 10000); // пробуем перезапустить через 10 секунд
|
||||
});
|
||||
|
||||
// Обработчик остановки polling
|
||||
bot.on('stop', () => {
|
||||
console.log('Bot polling stopped');
|
||||
// Пробуем перезапустить
|
||||
setTimeout(() => {
|
||||
try {
|
||||
bot.startPolling();
|
||||
} catch (e) {
|
||||
console.error('Error restarting polling after stop:', e);
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
return bot;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error initializing Telegram bot:', error);
|
||||
return null;
|
||||
}
|
||||
return { success: false, error: 'Неверный код' };
|
||||
} catch (error) {
|
||||
logger.error('Error verifying code:', error);
|
||||
return { success: false, error: 'Внутренняя ошибка' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Экспортируем функции
|
||||
module.exports = {
|
||||
initTelegramBot,
|
||||
verifyCode,
|
||||
sendVerificationCode
|
||||
};
|
||||
module.exports = TelegramBotService;
|
||||
Reference in New Issue
Block a user