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

Розбираємо, що автоматизувати в бізнесі та як це працює на практиці. Без спаму.