Разработка 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.