Telegraft

Справочник по Bot API

Два способа получить апдейт, для продакшена годится один

Бот получает апдейты либо вызовом `getUpdates` в цикле (long polling), либо регистрацией HTTPS-эндпоинта через `setWebhook`, после чего Telegram сам шлёт на него POST. Одно исключает другое: вызов `getUpdates` при установленном webhook возвращает ошибку, пока webhook не удалён.

Webhook против long polling: точные цифры

Порты для webhook
443, 80, 88 или 8443, только по HTTPS
Выбор для продакшена
Webhook. Long polling — инструмент разработки
Взаимно исключают друг друга
getUpdates возвращает ошибку, пока зарегистрирован webhook
Транспорт
HTTPS на порту 443, 80, 88 или 8443
Контракт обработчика
Сразу ответить 200, обрабатывать асинхронно
Диагностика
getWebhookInfo показывает очередь и последнюю ошибку

As of 2025-10-01, Telegram Bot API 13.4

Что это значит на практике

Long polling держит запрос открытым, пока не придёт апдейт или не истечёт таймаут, и сразу же открывает следующий. Ему не нужен ни публичный адрес, ни сертификат, ни входящее правило в файрволе — именно поэтому на ноутбуке это правильный выбор. И ничего в нём не масштабируется: каждый экземпляр, опрашивающий один и тот же токен, конкурирует за одни и те же апдейты, процесс обязан оставаться живым, чтобы вообще что-то получать, а на перезапуске остаётся окно, в котором апдейты копятся на стороне сервера.

Webhook переворачивает отношения. Telegram сам доставляет каждый апдейт методом POST на HTTPS-адрес, который вы зарегистрировали один раз, — а значит бот становится обработчиком запроса, а не долгоживущим процессом, и именно это делает реальностью бессерверное исполнение. Нужны действующий сертификат, порт 443, 80, 88 или 8443, публично доступный адрес и обработчик, который отвечает быстро.

Последнее требование подводит чаще остальных. Медленный или падающий эндпоинт Telegram считает нездоровым и повторяет доставку, поэтому обработчик, который выполняет всю работу до ответа, под нагрузкой обрабатывает один и тот же апдейт дважды. Правильная форма — сразу подтвердить кодом 200 и делать работу после: на Cloudflare Workers это `ctx.waitUntil`, на Node — явная очередь. Ответить первым делом не оптимизация, а то, что не даёт одной медленной записи в базу превратиться в три доставки одного и того же сообщения.

Переключение между режимами — живая операция с острым краем. `deleteWebhook` принимает `drop_pending_updates`, и применять его или нет — настоящее решение, а не значение по умолчанию: сброс выбрасывает всё, что накопилось, пока эндпоинт лежал, и это верно после долгой недоступности разговорного бота и неверно для платёжного, у которого в очереди может лежать завершённая оплата. `getWebhookInfo` показывает число ожидающих апдейтов и последнюю ошибку, и это первое, что надо смотреть, когда бот замолчал.

Для продакшена ответ — webhook, и нюансов тут немного. Он дешевле в эксплуатации, переживает перезапуски, не теряя апдейтов, масштабируется горизонтально, потому что распределением занимается сам Telegram, и он обязателен для проверки секретного токена, которая не даёт посторонним слать на ваш адрес выдуманные апдейты.

Как это обрабатывать в коде

// Acknowledge first, work afterwards. The order is the whole point.
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    if (request.headers.get('X-Telegram-Bot-Api-Secret-Token') !== env.WEBHOOK_SECRET) {
      return new Response('Not found', { status: 404 })
    }

    const update = await request.json()

    // Telegram retries anything slow or failing. Doing the work before responding is
    // how one slow write becomes three copies of the same message.
    ctx.waitUntil(handleUpdate(update, env))
    return new Response('ok')
  },
}
Код 200 уходит до того, как начинается работа. Всё остальное в надёжности webhook следует из правильного порядка этих двух шагов.

Какие вопросы это порождает

Можно ли одновременно держать long polling и зарегистрированный webhook?

Нет. Они исключают друг друга по замыслу: пока webhook зарегистрирован, `getUpdates` возвращает ошибку. Это обычная причина того, что локальный бот вдруг перестаёт что-либо получать, — webhook остался с прошлого деплоя, и лечится это вызовом `deleteWebhook`.

Что происходит с апдейтами, пока мой эндпоинт недоступен?

Telegram ставит их в очередь и какое-то время повторяет доставку, а затем выбрасывает. `getWebhookInfo` сообщает, сколько апдейтов ждёт и какой была последняя ошибка, — поэтому с него диагностику начинают, а не заканчивают.

Стоит ли сбрасывать накопленные апдейты при передеплое?

Зависит от того, что может лежать в очереди, и решать это стоит осознанно, а не копированием сниппета. Сброс уместен для разговорного бота после долгого простоя, где проигрывание часа устаревших сообщений только собьёт людей с толку. Он неуместен для платёжного бота или бота записи, где ожидающий апдейт может оказаться единственным свидетельством того, что оплата прошла.

Годится ли самоподписанный сертификат для эндпоинта бота?

Telegram позволяет загрузить самоподписанный сертификат вместе с вызовом setWebhook, но смысла в этом почти не осталось: бесплатные автоматические сертификаты есть у всех. Обычный сертификат убирает целый класс отказов, связанных с истечением срока и цепочкой доверия, а такие отказы крайне неприятно диагностировать снаружи.

Смежные ограничения