Разработка Telegram-бота на Node.js и grammY: сбор лидов, сессии, Webhook и защита от ошибок 429

Разработка Telegram-ботов на Node.js давно вышла за рамки простых скриптов-автоответчиков. Современные бизнес-решения требуют надежной обработки транзакций, устойчивости к высоким нагрузкам, безопасной работы с вебхуками и гибкого ведения диалогов (FSM). Популярная ранее библиотека node-telegram-bot-api морально устарела. Сегодня стандартом индустрии для JavaScript/TypeScript разработчиков становится фреймворк grammY.

В этом руководстве мы создадим готового к продакшену Telegram-бота на Node.js. Мы реализуем безопасную обработку реферальных ссылок (Deep Linking), построим линейный диалог сбора контактов клиента с генерацией уникального ID заявки, настроим Express-сервер для Webhook с валидацией секретного токена и разберем, как не упасть при получении лимитов HTTP 429.

Инициализация проекта и безопасная обработка Deep Linking

Начнем с инициализации проекта и базовой настройки. Для работы нам понадобятся пакеты grammy и dotenv для безопасного хранения токена бота в переменных окружения.

При первом запуске бота пользователем часто требуется передать контекст — например, ID партнера или промокод. Для этого используется механизм Deep Linking: ссылки формата t.me/YourBot?start=payload. При переходе по такой ссылке Telegram отправляет команду /start с параметром в текстовом поле. Наша задача — безопасно извлечь и валидировать этот параметр.

import { Bot } from "grammy";
import dotenv from "dotenv";

dotenv.config();

const token = process.env.BOT_TOKEN;
if (!token) {
throw new Error("BOT_TOKEN не задан в переменных окружения!");
}

// Инициализация бота
const bot = new Bot(token);

// Обработка команды /start с поддержкой deep-linking
bot.command("start", async (ctx) => {
const payload = ctx.match; // Содержит строку после ?start=

if (payload) {
// Валидируем входящие данные: разрешаем только латиницу, цифры и дефис
const cleanPayload = payload.replace(/[^a-zA-Z0-9_-]/g, "");

if (cleanPayload.length > 64) {
return ctx.reply("Ошибка: Некорректный реферальный код.");
}

await ctx.reply(`Добро пожаловать! Вы перешли по партнерской ссылке: ${cleanPayload}`);
} else {
await ctx.reply("Приветствуем! Нажмите /register, чтобы оставить заявку на обслуживание.");
}
});

// Запуск в режиме Long Polling (для локальной разработки)
if (process.env.NODE_ENV !== "production") {
bot.start();
console.log("Бот запущен локально через Long Polling");
}

Пошаговый сбор лидов с плагином Conversations

Для реализации пошаговых сценариев (например, анкетирования или записи на услуги) в grammY используется мощный плагин @grammyjs/conversations. В отличие от классических конечных автоматов (FSM), где приходится вручную сохранять шаг пользователя в базу данных и писать десятки ветвлений, плагин позволяет писать последовательный асинхронный код с использованием await conversation.waitFor().

Перед тем как начать диалог, настроим встроенное хранилище сессий. В реальном продакшене рекомендуется заменить дефолтное хранилище в оперативной памяти (In-Memory) на адаптер Redis, чтобы сессии не сбрасывались при перезапуске Node.js процесса.

Также учтите важное ограничение Telegram API: размер параметра callback_data в инлайн-кнопках не должен превышать 64 байта. Передавать туда сложные структуры данных нельзя — генерируйте короткие хэши и связывайте их с сущностями в БД.

import { session } from "grammy";
import { conversations, createConversation } from "@grammyjs/conversations";
import crypto from "crypto";

// Настройка middleware сессий
bot.use(session({ initial: () => ({}) }));
bot.use(conversations());

/**
* Сценарий сбора контактных данных лида
*/
async function collectLeadConversation(conversation, ctx) {
await ctx.reply("Пожалуйста, введите ваше имя и фамилию:");

// Ожидаем текстовое сообщение от пользователя
const nameCtx = await conversation.waitFor("message:text");
const clientName = nameCtx.message.text.trim();

await ctx.reply("Отлично! Теперь отправьте ваш номер телефона для связи:");

const phoneCtx = await conversation.waitFor("message:text");
const clientPhone = phoneCtx.message.text.trim();

// Генерация криптографически стойкого ID лида
const leadId = crypto.randomBytes(7).toString("hex");

// Сохраняем лид во внешнюю БД (имитация через conversation.external)
await conversation.external(async () => {
// В реальном проекте здесь будет: await db.leads.insert({ id: leadId, name: clientName, phone: clientPhone })
console.log(`[DB] Создан лид #${leadId}: ${clientName} (${clientPhone})`);
});

await ctx.reply(`Спасибо! Ваша заявка #${leadId} успешно зарегистрирована. Наш менеджер свяжется с вами.`);
}

// Регистрация диалога во фреймворке
bot.use(createConversation(collectLeadConversation));

// Команда для запуска диалога
bot.command("register", async (ctx) => {
await ctx.conversation.enter("collectLeadConversation");
});

Продакшн-развертывание: Webhooks с валидацией Secret Token

Для локальной разработки отлично подходит Long Polling (метод getUpdates), но в продакшене под высокими нагрузками обязательным стандартом является использование Webhooks. Вебхуки снижают задержку ответов бота и экономят ресурсы сервера, так как Telegram сам присылает события (Updates) на ваш HTTPS-сервер.

Для защиты вашего эндпоинта от посторонних запросов Telegram позволяет задать секретный токен при регистрации вебхука (параметр secret_token в методе setWebhook). Telegram будет отправлять этот токен в HTTP-заголовке X-Telegram-Bot-API-Secret-Token при каждом запросе. Наш Node.js сервер обязан проверять этот заголовок.

Ниже представлен пример интеграции бота с веб-сервером Express:

import express from "express";
import { webhookCallback } from "grammy";

if (process.env.NODE_ENV === "production") {
const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3000;
const SECRET_TOKEN = process.env.TELEGRAM_SECRET_TOKEN;

if (!SECRET_TOKEN) {
throw new Error("Критическая ошибка: TELEGRAM_SECRET_TOKEN не задан!");
}

// Middleware для проверки подлинности запросов от Telegram
const verifyTelegramSecret = (req, res, next) => {
const receivedToken = req.headers["x-telegram-bot-api-secret-token"];
if (receivedToken !== SECRET_TOKEN) {
console.warn("Попытка несанкционированного доступа к вебхуку!");
return res.status(403).send("Forbidden");
}
next();
};

// Роут вебхука
app.post(
"/telegram-webhook",
verifyTelegramSecret,
webhookCallback(bot, "express")
);

app.listen(PORT, () => {
console.log(`Сервер вебхуков запущен на порту ${PORT}`);
});
}

Ловушка лимитов: Борьба с ошибками HTTP 429 (Too Many Requests)

При масштабировании бота вы неизбежно столкнетесь с жесткими лимитами Telegram Bot API:

  • Не более 30 сообщений в секунду суммарно во все чаты;
  • Не более 1 сообщения в секунду в конкретный приватный чат;
  • Не более 20 сообщений в минуту в одну группу/супергруппу.

Если ваше приложение превышает эти показатели (например, при массовой рассылке уведомлений о статусе заказа или акциях), Telegram возвращает ошибку HTTP 429 Too Many Requests с параметром retry_after в теле ответа. Если игнорировать этот статус и продолжать спамить API, Telegram временно заблокирует токен вашего бота.

Для обхода этой проблемы в экосистеме grammY существует официальный плагин автоповторов запросов — @grammyjs/auto-retry. Он перехватывает ошибки 429, ставит запросы на паузу на указанное сервером время и автоматически повторяет их.

import { autoRetry } from "@grammyjs/auto-retry";

// Подключаем плагин автоповтора к API-клиенту бота
bot.api.config.use(
autoRetry({
maxDelaySeconds: 30, // Максимальное время ожидания перед повторной попыткой
maxRetryAttempts: 3, // Количество попыток отправить запрос повторно
})
);

Использование плагина гарантирует, что ваши пользователи гарантированно получат свои уведомления, а Node.js процесс не упадет из-за необработанного исключения в промисе.

Заключение

Мы создали надежный скелет Telegram-бота на Node.js с использованием библиотеки grammY. Мы научились безопасно принимать лиды через линейные диалоги, защитили наш Webhook-эндпоинт с помощью секретного токена и внедрили механизм автоматического обхода лимитов API. Эта архитектура готова к интеграции с CRM-системами и базами данных в реальном бизнесе.

Если вам требуется разработка профессионального и отказоустойчивого Telegram-бота под ключ, обратитесь к специалистам компании BotCreator.

Новые статьи — в Telegram

Разбираем, что автоматизировать в бизнесе и как это работает на практике. Без спама.