Многие разработчики до сих пор начинающие проекты на Node.js выбирают библиотеку node-telegram-bot-api, несмотря на то что её архитектура морально устарела, а поддержка современных функций Telegram Bot API оставляет желать лучшего. Сегодня индустриальным стандартом для JavaScript и TypeScript является фреймворк grammY. Он спроектирован на основе Middleware-цепочек (аналогично Express и Koa), поддерживает плагины сессий и пошаговых сценариев, а также предоставляет встроенную защиту от превышения лимитов API.
В этом руководстве мы соберем готового к продакшену бота для приема заявок от клиентов. Мы откажемся от простых примитивов «команда — ответ» и реализуем полноценный пошаговый диалог с сохранением данных в базу, настроим защищенный Webhook и обойдем лимит 429 Too Many Requests.
Почему grammY, а не node-telegram-bot-api или Telegraf
Главное преимущество grammY — архитектура обработки входящих обновлений (Updates). Каждый Update проходит через цепочку intermediate-функций. Это позволяет гибко изолировать аутентификацию, сессии и обработку команд. Кроме того, grammY из коробки решает проблему с отбросом застрявших запросов и предлагает официальные модули для работы с диалогами (@grammyjs/conversations) и автоматическими повторными запросами при ошибочных HTTP-ответах (@grammyjs/auto-retry).
Безопасный старт и организация кода
Никогда не храните токен бота в теле файла. Токен дает полный доступ к управлению вашим ботом и его отправке сообщений от имени компании. Используйте переменные окружения (process.env.BOT_TOKEN) и проверяйте их наличие при старте приложения.
Для сборки сценария заявки нам понадобятся следующие пакеты:
grammy— ядро фреймворка;@grammyjs/conversations— плагин для реализации пошаговых диалогов (State Machine);express— HTTP-сервер для принятия Webhook.
Реализация пошагового сбора лидов (Conversations)
Вместо того чтобы вручную хранить текущий шаг пользователя (например, step = 'awaiting_phone') в глобальном объекте или базе данных, модуль conversations позволяет писать асинхронный линейный код. Функция «засыпает» на моменте ожидания ответа от пользователя и просыпается при получении следующего сообщения.
Ниже представлен рабочий пример инициализации бота и создания сценария записи лида. При успешном завершении диалога мы генерируем уникальный идентификатор заявки через модуль crypto и отправляем данные во внешнее хранилище.
const { Bot, session } = require('grammy');
const { conversations, createConversation } = require('@grammyjs/conversations');
const crypto = require('crypto');
const botToken = process.env.BOT_TOKEN;
if (!botToken) {
throw new Error('Критическая ошибка: переменная окружения BOT_TOKEN не задана!');
}
const bot = new Bot(botToken);
// Инициализация хранилища сессий для плагина conversations
bot.use(session({ initial: () => ({}) }));
bot.use(conversations());
/**
* Пошаговый сценарий сбора данных лида
*/
async function collectLeadConversation(conversation, ctx) {
await ctx.reply('Здравствуйте! Как к вам обращаться? Напишите ваше имя:');
const nameCtx = await conversation.waitFor('message:text');
const userName = nameCtx.message.text.trim();
await ctx.reply(`Приятно познакомиться, ${userName}! Укажите ваш контактный номер телефона:`);
const phoneCtx = await conversation.waitFor('message:text');
const userPhone = phoneCtx.message.text.trim();
// Генерация уникального идентификатора заявки
const leadId = crypto.randomBytes(7).toString('hex');
const telegramId = ctx.from.id;
// Безопасный вызов внешних асинхронных функций внутри conversation
await conversation.external(async () => {
// В реальном проекте здесь вызывается INSERT в PostgreSQL / MySQL / MongoDB
console.log(`[БД] Сохранение лида #${leadId}:`, {
id: leadId,
telegramId,
name: userName,
phone: userPhone,
createdAt: new Date().toISOString()
});
});
await ctx.reply(`Спасибо! Ваша заявка #${leadId} принята. Менеджер свяжется с вами в ближайшее время.`);
}
// Регистрация диалога в боте
bot.use(createConversation(collectLeadConversation));
// Команда запуска бота
bot.command('start', async (ctx) => {
await ctx.reply('Вас приветствует отдел продаж! Для подачи заявки нажмите /lead');
});
// Запуск сценария сбора лида
bot.command('lead', async (ctx) => {
await ctx.conversation.enter('collectLeadConversation');
});Защита от ошибки 429 Too Many Requests
Telegram Bot API накладывает строгие лимиты на частоту отправки сообщений:
- Не более 30 сообщений в секунду суммарно по всем пользователям;
- Не более 1 сообщения в секунду в один и тот же приватный чат;
- Не более 20 сообщений в минуту в одну группу или канал.
Если вы отправляете массовую рассылку или получаете всплеск трафика, Telegram вернет ошибку с HTTP-кодом 429 и заголовком Retry-After. Простая попытка проигнорировать эту ошибку приведет к тому, что сообщения пользователей будут потеряны.
В grammY данная проблема решается подключением API Transformer @grammyjs/auto-retry. Этот плагин перехватывает ошибки 429, временно приостанавливает отправку для конкретного чата и повторно делает запрос через промежуток времени, указанный сервером Telegram.
const { autoRetry } = require('@grammyjs/auto-retry');
// Подключение авто-ретрая к API-клиенту бота
bot.api.config.use(
autoRetry({
maxRetryAttempts: 3, // Максимальное количество повторных попыток
maxDelaySeconds: 60, // Максимальное время ожидания (в секундах)
})
);
// Глобальный обработчик ошибок (Catch-all)
bot.catch((err) => {
const ctx = err.ctx;
console.error(`Ошибка при обработке update_id ${ctx.update.update_id}:`);
const e = err.error;
if (e instanceof Error) {
console.error('Сообщение об ошибке:', e.message);
}
});Настройка Webhook для продакшена vs Long Polling для разработки
При локальной разработке удобнее использовать метод bot.start() (Long Polling). Однако в боевом окружении под высокой нагрузкой необходим Webhook.
При использовании Webhook критически важно обеспечивать безопасность: Telegram передает вам обновления по публичному URL. Чтобы злоумышленники не могли подделать отправку Updates, отправляя POST-запросы на ваш сервер, используйте заголовок secret_token. Telegram прикрепляет этот токен к каждому HTTP-запросу в заголовке X-Telegram-Bot-Api-Secret-Token.
const express = require('express');
const { webhookCallback } = require('grammy');
const app = express();
app.use(express.json());
const SECRET_TOKEN = process.env.WEBHOOK_SECRET || 'a_very_secret_hex_string_64_chars';
const PORT = process.env.PORT || 3000;
const WEBHOOK_URL = process.env.WEBHOOK_URL; // Например: https://api.yourdomain.com/telegram/webhook
// Middleware для проверки секретного токена от Telegram
app.use('/telegram/webhook', (req, res, next) => {
const incomingToken = req.headers['x-telegram-bot-api-secret-token'];
if (incomingToken !== SECRET_TOKEN) {
console.warn('[Security Warning] Отклонен запрос с невалидным secret_token');
return res.status(403).json({ error: 'Access denied: invalid secret token' });
}
next();
});
// Передача обработки входящих данных в grammY
app.post('/telegram/webhook', webhookCallback(bot, 'express'));
if (process.env.NODE_ENV === 'production') {
app.listen(PORT, async () => {
console.log(`Webhook сервер успешно запущен на порту ${PORT}`);
// Установка вебхука в Telegram API при старте сервера
await bot.api.setWebhook(WEBHOOK_URL, {
secret_token: SECRET_TOKEN,
drop_pending_updates: true // Пропускаем накопившиеся сообщения при перезапуске
});
console.log(`Webhook установлена на URL: ${WEBHOOK_URL}`);
});
} else {
// Режим разработки: запускаем Long Polling
bot.start({
onStart: (botInfo) => {
console.log(`Бот @${botInfo.username} запущен локально в режиме Long Polling`);
},
});
}Чеклист готовности бота к продакшену
- Все секреты в ENV: Никаких токенов API и ключей БД в открытом коде.
- Идемпотентность и дубликаты: Webhook может присылать один и тот же
update_idповторно при сетевых сбоях. Сохраняйте обработанныеupdate_idв Redis или Postgres и завершайте ответ со статусом200 OKв течение 1–2 секунд. - Логирование ошибок: Обязательно настраивайте
bot.catch(), чтобы падение одной сессии не останавливало весь Node.js процесс. - Контроль лимитов: Всегда подключайте
@grammyjs/auto-retryдля защиты от банов по ошибке 429.
Если вам необходима квалифицированная помощь в проектировании архитектуры Telegram-ботов, создании Mini Apps или интеграции CRM-систем, обратитесь к специалистам сервиса BotCreator.