Telegram-бот на Python: от токена до продакшена
Чтобы создать Telegram-бота на Python, получите токен у @BotFather командой /newbot и отправляйте HTTPS-запросы к Bot API. Обновления приходят двумя способами: через getUpdates (long polling) или вебхук — но только одним из них одновременно. В продакшене защищайте вебхук параметром secret_token, соблюдайте лимиты и при ошибке 429 ждите retry_after секунд.
Обновлено:
Как устроен Telegram-бот?
Бот — специальный аккаунт Telegram, которым управляет ваша программа. Любое действие — обычный HTTPS-запрос вида https://api.telegram.org/bot<token>/ИМЯ_МЕТОДА. Когда пользователь пишет боту, Telegram сохраняет событие как update: вы забираете его через getUpdates, либо Telegram сам отправляет его POST-запросом на ваш вебхук. Ответ — ещё один запрос, например sendMessage.
Бот отличается от обычного пользователя: он не может первым начать диалог — человек должен сначала написать боту или добавить его в группу. В группах по умолчанию бот видит только адресованные ему сообщения (privacy mode, подробнее ниже).
Специальная библиотека для Python не обязательна: Bot API — это HTTP поверх JSON. Но готовый клиент берёт на себя маскировку токена, загрузку файлов (multipart), классификацию ошибок и повторные попытки. Примеры ниже написаны на клиенте Telegram асинхронного Python SDK Socialift (pip install socialift, Python 3.11+).
Создание бота в BotFather: 5 шагов
Откройте @BotFather
Найдите в Telegram официального бота @BotFather и отправьте команду /newbot.
Задайте имя
Имя (name) видно в карточке контакта, его можно поменять позже — например, «Помощник магазина».
Выберите username
Username — 5–32 символа: латиница, цифры и подчёркивание, в конце обязательно «bot» (например, shop_uz_bot). Изменить его потом нельзя — выбирайте внимательно.
Сохраните токен
BotFather выдаст токен вида 110201543:AAHdqTc... У кого токен — тот управляет ботом. Не храните его в коде: используйте переменную окружения TELEGRAM_BOT_TOKEN или хранилище секретов.
Проверьте токен
Если запрос getMe (в SDK —
bot.get_me()) возвращает профиль бота, токен рабочий. При утечке выпустите новый командой /token в BotFather — старый перестанет работать.
Первый запрос: проверка токена и отправка сообщения
Токен не в коде — Telegram.from_env() читает его из TELEGRAM_BOT_TOKEN.
import asyncio
from socialift import Telegram
async def main():
async with Telegram.from_env() as bot:
me = await bot.get_me()
print("Бот:", me["username"])
# chat_id — числовой ID или @username для канала
await bot.send_message("@my_channel", "<b>Привет!</b> Бот запущен.",
parse_mode="HTML")
asyncio.run(main())Чтобы писать в канал, бот должен быть его администратором. В личный чат можно писать только пользователю, который уже нажал /start, — его числовой ID приходит в update.
Текст сообщения — 1–4096 символов, подпись к медиа (caption) — 0–1024 символа. Длинный текст разбивайте на несколько сообщений.
getUpdates или вебхук: что выбрать?
Способы взаимоисключающие: пока установлен вебхук, getUpdates не работает. Незабранные обновления хранятся на сервере Telegram не дольше 24 часов.
| Критерий | getUpdates (long polling) | Вебхук |
|---|---|---|
| Как работает | Программа сама запрашивает Telegram и ждёт ответ до timeout секунд | Telegram отправляет каждый update POST-запросом на ваш HTTPS-адрес |
| Требования к серверу | Публичный адрес не нужен — работает даже на ноутбуке | Публичный HTTPS и валидный SSL-сертификат; редиректы не поддерживаются |
| Порты | Не важны | Только 443, 80, 88 или 8443 |
| Параллельность | Один процесс читает очередь | max_connections 1–100, по умолчанию 40 |
| Подтверждение | offset = последний update_id + 1 | Ответ 2XX; иначе Telegram повторяет доставку |
| Когда удобно | Локальная разработка, небольшие боты | Продакшен, serverless, несколько ботов |
Локальная разработка: цикл getUpdates
from socialift import Telegram
async def run_polling():
async with Telegram.from_env() as bot:
await bot.delete_webhook() # при активном вебхуке getUpdates не работает
offset = None
while True:
updates = await bot.get_updates(offset=offset, timeout=25)
for update in updates:
offset = update["update_id"] + 1
msg = update.get("message") or {}
if msg.get("text") == "/start":
await bot.send_message(msg["chat"]["id"], "Добро пожаловать!")Пересчитывайте offset после каждого ответа — иначе те же обновления придут снова. По умолчанию timeout в Telegram равен 0 (короткий опрос); в рабочем режиме задавайте положительное значение.
Защита вебхука через secret_token
Telegram не подписывает тело вебхука. Вместо этого secret_token, переданный в set_webhook, приходит в каждом запросе в заголовке X-Telegram-Bot-Api-Secret-Token — по нему вы отсекаете поддельные запросы.
import os
from aiohttp import web
from socialift import Telegram
from socialift.webhooks import verify_telegram_request
SECRET = os.environ["TG_WEBHOOK_SECRET"] # 1–256 символов: A-Z, a-z, 0-9, _ и -
async def setup(bot: Telegram) -> None:
await bot.set_webhook("https://example.com/tg/webhook", secret_token=SECRET,
allowed_updates=["message", "callback_query"])
info = await bot.get_webhook_info()
print(info["pending_update_count"], info.get("last_error_message"))
async def handle(request: web.Request) -> web.Response:
if not verify_telegram_request(request.headers, SECRET):
return web.Response(status=403)
update = await request.json()
await queue.put(update) # тяжёлую работу — в очередь, ответ не задерживаем
return web.Response(text="ok")verify_telegram_request сравнивает заголовок за постоянное время. Секрет генерируйте через secrets.token_urlsafe(32) — он состоит только из разрешённых символов.
Если вебхук молчит, сначала смотрите last_error_message и pending_update_count в ответе get_webhook_info(). Telegram повторяет неудачную доставку, поэтому отбрасывайте дубли по update_id.
Фото, inline-кнопки и клавиатура-меню
async def send_catalog(bot, chat_id: int) -> None:
inline = {"inline_keyboard": [[
{"text": "Каталог", "url": "https://example.com/catalog"},
{"text": "Оформить заказ", "callback_data": "order:42"},
]]}
await bot.send_photo(chat_id, "https://example.com/banner.jpg",
caption="Новая коллекция", reply_markup=inline)
menu = {"keyboard": [[{"text": "Контакты"}, {"text": "Корзина"}]],
"resize_keyboard": True}
await bot.send_message(chat_id, "Выберите раздел:", reply_markup=menu)
async def on_callback(bot, query: dict) -> None:
await bot.answer_callback_query(query["id"], text="Принято")callback_data — 1–64 байта: кладите туда короткий ключ, а не весь объект. На нажатие кнопки обязательно вызывайте answer_callback_query, иначе пользователь будет видеть индикатор загрузки на кнопке.
Клавиатура-меню (ReplyKeyboardMarkup) не работает в каналах. Фото можно передать как file_id, HTTPS-ссылку или байты.
Лимиты на размер файлов и сообщений
Значения для официального сервера Bot API (api.telegram.org).
| Ситуация | Лимит |
|---|---|
| Текст сообщения | 1–4096 символов |
| Подпись к медиа (caption) | 0–1024 символа |
| Фото, загрузка multipart | 10 МБ; сумма ширины и высоты не больше 10000, соотношение сторон не больше 20 |
| Другие файлы, загрузка multipart | 50 МБ |
| Отправка по HTTPS-ссылке | фото 5 МБ, прочие файлы 20 МБ |
Повторная отправка по file_id | без ограничений (file_id уникален для каждого бота) |
| Скачивание файла (getFile) | 20 МБ; ссылка действует минимум 1 час |
| Собственный локальный сервер Bot API | загрузка до 2000 МБ, скачивание без ограничений |
Почему бот не видит сообщения в группе (privacy mode)
Любой бот, добавленный в группу, по умолчанию работает в privacy mode. В этом режиме он получает только адресованные ему команды (/command@имя_бота), сообщения, отправленные через бота, и ответы на его сообщения. Служебные сообщения, сообщения из личных чатов и посты каналов, где бот состоит, приходят независимо от режима.
Если боту нужны все сообщения группы, есть два пути: сделать его администратором (боты-админы получают всё) или отключить режим командой /setprivacy в BotFather. После смены режима бота нужно удалить из группы и добавить заново — иначе изменение не вступит в силу.
Telegram рекомендует отключать режим только при реальной необходимости. Часто хватает принудительного ответа (force reply) на сообщение бота. Текущий режим бота виден всем в списке участников группы.
Лимиты массовой рассылки
Бесплатные лимиты из Bot FAQ. При превышении Telegram возвращает HTTP 429 и parameters.retry_after в ответе.
| Куда | Лимит |
|---|---|
| В один чат | рекомендуется не больше 1 сообщения в секунду; короткие всплески допускаются |
| В группу | не больше 20 сообщений в минуту |
| Массовые уведомления разным пользователям | около 30 сообщений в секунду |
| С платными рассылками | до 1000 сообщений в секунду; каждое сообщение сверх 30 в секунду — 0,1 Stars |
Примечания и ограничения
Чтобы включить платные рассылки в BotFather, на балансе бота должно быть не меньше 100 000 Stars и не меньше 100 000 активных пользователей в месяц. Без них Telegram советует растягивать рассылку на более длинный интервал, например 8–12 часов.
Правильная обработка ошибки 429
import asyncio
import socialift
async def broadcast(bot, chat_ids: list[int], text: str) -> None:
for chat_id in chat_ids:
try:
await bot.send_message(chat_id, text)
except socialift.RateLimitError as exc:
await asyncio.sleep(exc.retry_after or 5)
await bot.send_message(chat_id, text)
except socialift.PermissionError:
mark_inactive(chat_id) # пользователь заблокировал бота (403)
await asyncio.sleep(0.05) # ~20 сообщений/с — ниже порога в 30/сSDK превращает ответ 429 в RateLimitError и сам повторяет запрос по стандартной RetryPolicy (3 попытки); если попытки исчерпаны, ошибка поднимается к вам. Если retry_after нет, делайте резервную паузу.
Ошибка 403 (например, пользователь заблокировал бота) повтором не лечится — исключайте такой чат из рассылки.
Перед выходом в продакшен
- Токен в переменной окружения или хранилище секретов; в логах его нет.
- Вебхук на HTTPS, с
secret_token, заголовок проверяется в каждом запросе. - В
allowed_updatesтолько нужные типы (например, message и callback_query). - Повторные update отбрасываются по
update_id. - Ответ вебхуку уходит быстро, тяжёлая обработка — в очереди.
- Ошибки 429 и 403 обрабатываются отдельно, рассылка идёт медленнее 30 сообщений в секунду.
- Для сценариев в группах настроен privacy mode или права администратора.
Частые вопросы
Создать Telegram-бота — это платно?
Нет. Создание бота в BotFather и использование Bot API бесплатны. Платить приходится только за опциональные функции, например за платные рассылки быстрее 30 сообщений в секунду — они оплачиваются Telegram Stars.
Может ли бот написать пользователю первым?
Нет. По правилам Telegram пользователь должен сначала написать боту или добавить его в группу. Поэтому распространяйте ссылку t.me/имя_бота через сайт, рекламу или QR-код и сохраняйте chat ID после /start.
Что делать, если токен бота утёк?
Выпустите новый токен командой /token в BotFather — старый перестанет работать. Затем обновите переменную окружения на сервере и переустановите вебхук. Никогда не кладите токен в git или во фронтенд-код.
Вебхук установлен, но обновления не приходят. Что проверить?
Сначала прочитайте last_error_message в ответе getWebhookInfo. Затем проверьте, что порт — 443, 80, 88 или 8443, SSL-сертификат валиден, а адрес не делает редирект. И помните: пока вебхук активен, getUpdates не работает.
Как боту читать все сообщения в группе?
Сделайте бота администратором группы или отключите privacy mode командой /setprivacy в BotFather и добавьте бота в группу заново. Сообщения других ботов он всё равно не получит, а метода для чтения истории чата в Bot API нет — бот видит только новые обновления.
Какая библиотека нужна для Telegram-бота на Python?
Bot API — это HTTPS и JSON, поэтому подойдёт любой HTTP-клиент. Готовый клиент решает загрузку файлов, разбор ошибок и повторы: в SDK Socialift это асинхронный клиент Telegram, а для методов без отдельной обёртки есть bot.call("имяМетода").