Telegram-бот на VPS: long polling или webhook, что выбрать

Как запустить Telegram-бота на VPS: сравнение getUpdates и webhook, пример на python-telegram-bot и защита токена. Что делать, если бот не получает сообщения.

Команда EGN 8 мин чтения
Telegram-бот на VPS: long polling или webhook, что выбрать
Содержание 18
  1. Коротко
  2. Два способа получать обновления
  3. Что выбрать
  4. Шаги
  5. Переход с webhook на polling
  6. Примеры
  7. Симптом → причина → решение
  8. Пример: проверка webhook одной командой
  9. Пример: ответ с клавиатурой
  10. Чек-лист перед запуском
  11. Что делать, если бот начал падать
  12. Частые ошибки
  13. Итог
  14. Вопросы и ответы
  15. Можно ли запускать Telegram-бота на домашнем компьютере?
  16. Long polling нагружает сервер?
  17. Нужен ли домен для long polling?
  18. Что такое privacy mode?

Telegram-бот на VPS решает ту же задачу, что и Discord-бот: работает, пока вы спите. Разница в способе получения сообщений. Бот может сам спрашивать Telegram о новых обновлениях или ждать, пока Telegram пришлёт их на ваш адрес. От выбора зависит, нужен ли вам домен, сертификат и открытый порт.

Коротко

Для большинства ботов выбирайте long polling (getUpdates): бот сам запрашивает обновления, домен и сертификат не нужны. Webhook (setWebhook) имеет смысл, когда нужна мгновенная доставка и у вас есть HTTPS на порту 443, 80, 88 или 8443. Токен берите у @BotFather, храните в файле окружения и запускайте службой systemd.

Два способа получать обновления

У бота есть два взаимоисключающих способа получать сообщения. Первый — long polling через метод getUpdates: бот держит исходящий запрос к Telegram, и тот отдаёт новые сообщения, как только они появились. Второй — webhook через setWebhook: Telegram отправляет каждое обновление HTTPS-запросом POST на адрес вашего сервера.

Схема: getUpdates — бот запрашивает обновления, setWebhook — Telegram присылает их на ваш сервер
Схема: getUpdates — бот запрашивает обновления, setWebhook — Telegram присылает их на ваш сервер

Официальная документация Bot API прямо указывает: пока webhook установлен, getUpdates не работает. Поэтому переключаться между ними нужно аккуратно, сначала отключив старый способ.

Для webhook Telegram требует HTTPS и поддерживает порты 443, 80, 88 и 8443. На VPS это означает домен, сертификат Let’s Encrypt и настроенный nginx или другой веб-сервер. Подробно эта схема разобрана в статье Сайт на VPS с nginx и Let’s Encrypt.

Что выбрать

Long polling подходит, если бот отвечает на команды и не обрабатывает тысячи сообщений в секунду. Он проще в настройке, работает за NAT и не требует домена. Минус — небольшая задержка и постоянный исходящий запрос, но для обычного бота это незаметно.

Webhook выбирайте, если у вас уже есть HTTPS-сайт, бот принимает много обновлений или вы строите сервис на несколько серверов. В этом случае Telegram не держит запросы к вашей машине, а присылает данные сам.

Шаги

  1. Создайте бота у @BotFather: команда /newbot, затем имя и username. BotFather выдаст токен. Сохраните его, но не публикуйте.
  2. Подготовьте VPS: Ubuntu 24.04, обновления системы, ключи SSH вместо пароля и пользователь без root для бота.
  3. Установите окружение и библиотеку python-telegram-bot версии 21:
Терминал
sudo apt update && sudo apt install -y python3-venv
sudo useradd -r -s /usr/sbin/nologin tgbot
sudo mkdir -p /opt/tg-bot && sudo chown tgbot:tgbot /opt/tg-bot
cd /opt/tg-bot
python3 -m venv venv
./venv/bin/pip install "python-telegram-bot>=21,<22"
  1. Напишите bot.py. Токен берётся из окружения:
Python
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Бот работает.")

def main():
    app = Application.builder().token(os.environ["BOT_TOKEN"]).build()
    app.add_handler(CommandHandler("start", start))
    app.run_polling(allowed_updates=Update.ALL_TYPES)

if __name__ == "__main__":
    main()
  1. Создайте файл /opt/tg-bot/.env и положите в него токен (без кавычек):
server.properties
BOT_TOKEN=вставьте-токен-из-BotFather
Терминал
sudo chown tgbot:tgbot /opt/tg-bot/.env
sudo chmod 600 /opt/tg-bot/.env
  1. Опишите службу /etc/systemd/system/tg-bot.service:
Конфиг
[Unit]
Description=Telegram bot
After=network-online.target
Wants=network-online.target

[Service]
User=tgbot
WorkingDirectory=/opt/tg-bot
EnvironmentFile=/opt/tg-bot/.env
ExecStart=/opt/tg-bot/venv/bin/python bot.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
  1. Запустите и проверьте журнал:
Терминал
sudo systemctl daemon-reload
sudo systemctl enable --now tg-bot
sudo journalctl -u tg-bot -f
  1. Отправьте боту команду /start и проверьте ответ.
Верх карточки VPS: кнопка «Консоль» открывает терминал, где можно выполнять команды настройки
Верх карточки VPS: кнопка «Консоль» открывает терминал, где можно выполнять команды настройки

Переход с webhook на polling

Если бот раньше работал через webhook, а теперь вы переходите на long polling, сначала отключите webhook. Метод deleteWebhook делает это одним запросом к Bot API. Иначе бот будет получать ошибку при каждом getUpdates, а сообщения будут уходить на старый адрес.

Проверить состояние можно методом getWebhookInfo: если в ответе есть непустой url, webhook ещё активен.

Примеры

Команда «/start» в группе. В группе при включённом privacy mode (так по умолчанию) бот видит только команды, ответы на свои сообщения и упоминания. Чтобы он читал все сообщения группы, отключите privacy mode в BotFather командой /setprivacy или сделайте бота администратором группы.

Перезапуск после ошибки. Если процесс упал из-за необработанного исключения, Restart=always поднимает его через пять секунд. Кратковременные сетевые сбои при long polling библиотека в основном переживает сама, но не рассчитывайте на это и смотрите журнал.

Утёкший токен. Команда /revoke в BotFather выпускает новый токен. Старый перестаёт работать, а бот нужно обновить в .env и перезапустить службу.

Симптом → причина → решение

Симптом Частая причина Что сделать
Бот не отвечает на /start Служба не запущена или упала sudo systemctl status tg-bot, затем журнал
Conflict: terminated by other getUpdates request Запущены два экземпляра с одним токеном Остановите лишний процесс: ps aux | grep bot.py
InvalidToken или 401 Unauthorized в журнале Токен неверный или отозван Получите новый токен у BotFather (/token или /revoke) и обновите .env
Сообщения не приходят после webhook Webhook всё ещё активен Вызовите deleteWebhook, затем перезапустите бота
Бот не получает обновления, в журнале таймауты С VPS нет доступа к api.telegram.org Проверьте curl -sS -m 10 https://api.telegram.org, при необходимости смените локацию или сеть
Ответы приходят с задержкой Тяжёлый код в обработчике Вынесите долгие операции в фоновую задачу

Пример: проверка webhook одной командой

Если вы не уверены, работает ли webhook, проверьте его через getWebhookInfo. Токен подставляется из файла окружения, поэтому в историю команд попадает только имя переменной. Выполняйте команду от root или от пользователя, который может читать .env.

Терминал
source /opt/tg-bot/.env
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getWebhookInfo"

В ответе поле url должно быть пустым при long polling. Не публикуйте вывод целиком: он безопасен, но привычка не светить URL с токеном (.../bot<токен>/...) в чатах и тикетах спасает от утечек.

Пример: ответ с клавиатурой

Команда /menu показывает кнопки, а нажатие обрабатывается отдельным обработчиком. Так бот выглядит живее, чем с набором текстовых команд:

Python
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CallbackQueryHandler

async def menu(update, context):
    keyboard = [[InlineKeyboardButton("Статус", callback_data="status")]]
    await update.message.reply_text("Выберите действие:", reply_markup=InlineKeyboardMarkup(keyboard))

async def on_button(update, context):
    query = update.callback_query
    await query.answer()
    await query.edit_message_text("Бот работает.")

# в main():
# app.add_handler(CommandHandler("menu", menu))
# app.add_handler(CallbackQueryHandler(on_button))

Кнопки с callback_data обрабатываются через CallbackQueryHandler. Не забывайте вызывать query.answer(), иначе у пользователя будет крутиться индикатор ожидания.

Чек-лист перед запуском

Перед тем как пустить бота к пользователям, проверьте:

  1. Токен хранится в .env с правами 600, а в коде вызывается через os.environ.
  2. Служба стартует после reboot и перезапускается после падения.
  3. Бот отвечает на /start и на одну команду с ошибкой, например на неверный ввод. Ошибка не должна ронять процесс.
  4. В журнале нет исключений при обычной работе: journalctl -u tg-bot --since "1 hour ago".
  5. Команда /revoke в BotFather известна тем, кто за ботом следит: она нужна при утечке.
  6. Если используется webhook, сертификат действителен, а getWebhookInfo не содержит ошибок в поле last_error_message.

Что делать, если бот начал падать

Сначала посмотрите журнал за последний час. Если ошибка сетевая, например таймаут до Telegram, это временная проблема, и служба сама повторит запрос. Если исключение в вашем коде, поправьте обработчик и перезапустите службу. Если бот падает раз в несколько минут без причины, проверьте память командой free -h: процесс мог не хватать памяти и его остановило ядро.

Частые ошибки

  • Бот молчит, хотя служба работает. Смотрите журнал: Conflict: terminated by other getUpdates request означает, что запущены два экземпляра бота с одним токеном.
  • Webhook включён, а polling не работает. Вызовите deleteWebhook, затем перезапустите бота.
  • Токен в коде или в репозитории. Такой токен надо отзывать через /revoke. Держите его в .env с правами 600.
  • Нет доступа к api.telegram.org с VPS. Бот сам обращается к Telegram, поэтому доступность API проверяйте именно с сервера: curl -sS -m 10 https://api.telegram.org. Если запрос не проходит, смотрите сеть, DNS и локацию сервера. Доступность самого мессенджера для ваших пользователей от этого не зависит.
  • Webhook без HTTPS. Telegram принимает webhook только по HTTPS на портах 443, 80, 88 или 8443. Для теста используйте long polling.
  • Служба падает при старте. Проверьте, что путь в ExecStart указывает на Python внутри venv, а .env читается пользователем tgbot.

Итог

Telegram-бот на VPS проще всего запустить через long polling: отдельный пользователь, venv, файл .env и служба systemd с Restart=always. Webhook добавляет домен и HTTPS, зато даёт мгновенную доставку. Выбирайте по задаче, а не по моде, и не забывайте про deleteWebhook, если меняете способ получения обновлений.

Вопросы и ответы

Можно ли запускать Telegram-бота на домашнем компьютере?

Можно, но он будет работать только при включённой машине и стабильном интернете. VPS решает это.

Long polling нагружает сервер?

Нет, для обычного бота нагрузка минимальна. Long polling держит один исходящий запрос.

Нужен ли домен для long polling?

Нет. Домен и сертификат нужны только для webhook.

Что такое privacy mode?

Это настройка, из-за которой бот в группе видит только команды и упоминания. Меняется в BotFather командой /setprivacy.

Сервер под игры за пару минут — egnetwork.ru

Читать далее

Вся рубрика →