Как отправлять заявки с сайта в Telegram
Практика4 минРедакция Innovialabs
Коротко
Форма отправляет данные на ваш сервер, сервер проверяет их, сохраняет заявку и вызывает sendMessage Bot API с токеном из переменных окружения; при ошибке повторяет отправку, а заявка остаётся в базе и не теряется.
Короткий ответ
Правильная схема выглядит так:
браузер → ваш серверный обработчик → база или лог → Bot API sendMessage → чат или группа.
Токен бота хранится только на сервере. Если вызывать Bot API прямо из JavaScript на странице, токен увидит любой посетитель во вкладке «Сеть» и сможет писать от имени бота, засыпать спамом вашу группу или перехватить управление ботом.
Шаг 1. Бот и чат для заявок
- Создайте бота в BotFather и сохраните токен.
- Создайте группу для заявок и добавьте в неё бота. Для отправки сообщений права администратора не нужны.
- Узнайте
chat_id: отправьте в группу сообщение с упоминанием бота, например/start@your_bot, и откройтеhttps://api.telegram.org/bot<ТОКЕН>/getUpdates. В ответе найдитеchat.id.
Что важно знать про chat_id:
- у групп он отрицательный, у супергрупп начинается с
-100; - если группа превратилась в супергруппу, ID меняется — Bot API вернёт ошибку с полем
migrate_to_chat_id; getUpdatesне работает, если у бота настроен вебхук;- чтобы получать заявки в личку, нужно сначала самому запустить бота — без этого он не сможет вам написать.
Шаг 2. Переменные окружения
TELEGRAM_BOT_TOKEN=123456789:replace-with-your-token
TELEGRAM_CHAT_ID=-1001234567890
Не добавляйте к этим переменным префиксы, которые делают их доступными в браузере, например NEXT_PUBLIC_ в Next.js. Файл .env не должен попадать в репозиторий.
Шаг 3. Серверный обработчик
Пример route handler для Next.js. Та же логика переносится на любой бэкенд.
// app/api/lead/route.ts
const API = `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`;
const esc = (s: string) =>
s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
async function notifyTelegram(text: string): Promise<boolean> {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const res = await fetch(API, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: process.env.TELEGRAM_CHAT_ID, text, parse_mode: "HTML" }),
signal: AbortSignal.timeout(5000),
});
if (res.ok) return true;
const data = await res.json().catch(() => ({}));
if (res.status !== 429 && res.status < 500) {
console.error("Telegram rejected lead", res.status, data.description);
return false; // ошибка настройки: повтор не поможет
}
const wait = data.parameters?.retry_after ?? 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
} catch {
// таймаут или сеть: пробуем ещё раз
}
}
return false;
}
export async function POST(req: Request) {
const body = await req.json().catch(() => null);
if (!body) return Response.json({ ok: false }, { status: 400 });
if (body.website) return Response.json({ ok: true }); // honeypot: тихо игнорируем спам-бота
const name = String(body.name ?? "").trim().slice(0, 100);
const phone = String(body.phone ?? "").trim().slice(0, 30);
if (!name || !phone) return Response.json({ ok: false }, { status: 400 });
const lead = await saveLead({ name, phone }); // сначала сохраняем заявку
const text = `<b>Новая заявка #${lead.id}</b>\nИмя: ${esc(name)}\nТелефон: ${esc(phone)}`;
if (!(await notifyTelegram(text))) await markForRetry(lead.id);
return Response.json({ ok: true });
}
saveLead и markForRetry — ваши функции работы с базой.
Шаг 4. Обработка сбоев
Telegram — канал уведомлений, а не хранилище заявок. Поэтому:
- сначала сохраняйте заявку в базу или хотя бы в лог, потом отправляйте уведомление;
- ставьте таймаут на запрос к Bot API, чтобы форма не зависала;
- повторяйте только при
429(ждитеretry_after) и ошибках5xx, с ограниченным числом попыток; - не повторяйте при
400и403: это неверныйchat_id, бот удалён из группы или ошибка разметки — нужен алерт, а не повтор; - дублируйте канал для неотправленных заявок: фоновая очередь повторов или письмо на email;
- отвечайте пользователю успехом, когда заявка сохранена, а не когда дошло уведомление.
В serverless-окружении долгие ожидания внутри запроса нежелательны, поэтому повторы лучше переносить в фоновую задачу.
Защита от спама и ошибок разметки
- экранируйте
&,<и>приparse_mode: "HTML"— иначе символ в имени сломает сообщение; - ограничивайте длину полей: у сообщения в Telegram есть лимит длины;
- добавьте скрытое поле-ловушку (honeypot) и ограничение частоты запросов по IP;
- валидируйте данные на сервере, даже если они уже проверены в браузере.