Телеграм-бот на Python — это программа, которая через Bot API Telegram принимает сообщения пользователя и отвечает на них: текстом, кнопками или меню команд. Ниже — дорожная карта от пустого файла до рабочего бота, у которого есть reply-клавиатура, inline-кнопки и меню команд. Сразу разберемся, чем reply-кнопки отличаются от inline, и что делать, если бот не отвечает.
Содержание
- Дорожная карта: от нуля до рабочего бота
- Шаг 1-2: установка и токен BotFather
- Шаг 3: минимальный рабочий бот целиком
- Reply-кнопки и inline-кнопки: в чем разница
- Шаг 4: reply-клавиатура с быстрыми ответами
- Шаг 5: inline-кнопки со ссылкой и действием
- Шаг 6: меню команд бота
- Если не получилось: типовые симптомы
- Выводы
- Где применяется / связь с практикой
- FAQ
Код написан под библиотеку python-telegram-bot версии 21-22 (async-ветка). API этих библиотек сильно менялся между версиями, поэтому примеры из старых статей (2021-2024) на свежем пакете часто не запускаются.
Дорожная карта: от нуля до рабочего бота
- Установить Python 3.9+ и библиотеку python-telegram-bot.
- Получить токен бота у BotFather в самом Telegram.
- Запустить минимальный бот, который отвечает на
/start. - Добавить reply-клавиатуру с быстрыми ответами.
- Добавить inline-кнопки со ссылками и действиями.
- Настроить меню команд (список у поля ввода).
Артефакт — один файл bot.py: запускаете python bot.py, и бот отвечает в чате.
Шаг 1-2: установка и токен BotFather
Библиотека ставится через pip. В терминале:
pip install "python-telegram-bot>=21,<23"
Токен нельзя придумать — его выдает бот BotFather. Откройте чат с @BotFather, отправьте /newbot, задайте имя и username (оканчивается на bot). В ответ придет строка вида 123456789:AA... — это токен для Bot API.
Токен — это пароль от бота. Не публикуйте его в репозитории и не коммитьте в git. Если засветили — выпустите новый командой /revoke в BotFather.
Шаг 3: минимальный рабочий бот целиком
Сначала — полный файл, который копируется и запускается. Подставьте токен в TOKEN:
# python-telegram-bot 21.x/22.x (async)
from telegram import Update
from telegram.ext import (
Application, CommandHandler, MessageHandler, ContextTypes, filters
)
TOKEN = "ВСТАВЬТЕ_ТОКЕН_ОТ_BOTFATHER"
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("Привет! Я бот от Otus. Напиши мне что-нибудь.")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
# отвечает тем же текстом, что прислал пользователь
await update.message.reply_text(update.message.text)
def main():
app = Application.builder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
app.run_polling() # слушает Telegram, пока не остановите Ctrl+C
if __name__ == "__main__":
main()
Ожидаемый результат: python bot.py запускается без ошибок, окно «висит». В чате /start дает ответ «Привет! Я бот от Otus…», а любое сообщение бот повторяет обратно.
Теперь по частям. Application — ядро бота (builder().token(...).build()). CommandHandler("start", start) связывает команду /start с функцией. MessageHandler(filters.TEXT & ~filters.COMMAND, echo) ловит любой текст, кроме команд. run_polling() запускает опрос серверов Telegram. Обработчики асинхронные (async def), ответ идет через await ...reply_text(...) — в версиях 20+ весь код бота асинхронный.
Reply-кнопки и inline-кнопки: в чем разница
В Telegram два вида кнопок, их легко перепутать — разведем явно.
| Признак | Reply-кнопки (ReplyKeyboardMarkup) |
Inline-кнопки (InlineKeyboardMarkup) |
|---|---|---|
| Где показываются | На месте обычной клавиатуры устройства | Прямо под конкретным сообщением |
| Что делает нажатие | Отправляет в чат текст кнопки как обычное сообщение | Не пишет в чат, а вызывает действие (callback) или ссылку |
| Класс кнопки | KeyboardButton |
InlineKeyboardButton |
| Типичное применение | Главное меню, быстрые ответы | Ссылки, лайки, пагинация, подтверждение |
| Нужен отдельный обработчик | Нет, приходит как текст | Да, CallbackQueryHandler для callback-кнопок |
Ориентир: нужна готовая фраза от пользователя — берите reply; нужен переход по ссылке или действие без записи в чат — inline.
Шаг 4: reply-клавиатура с быстрыми ответами
Reply-клавиатуру собирают из рядов, каждый ряд — список кнопок. Замените функцию start на эту:
from telegram import ReplyKeyboardMarkup, KeyboardButton
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
keyboard = ReplyKeyboardMarkup(
[
[KeyboardButton("Повтори это"), KeyboardButton("А это?")],
[KeyboardButton("Помощь")],
],
resize_keyboard=True, # подгоняется под размер экрана
)
await update.message.reply_text("Выбери кнопку:", reply_markup=keyboard)
Результат: после /start под полем ввода появляются три кнопки в два ряда. Тап по «Повтори это» отправляет в чат этот текст, и его ловит наш echo. Отдельный обработчик для reply-кнопок не нужен: это обычные сообщения.
Шаг 5: inline-кнопки со ссылкой и действием
У inline-кнопки два режима: url — открыть ссылку, callback_data — передать боту метку действия. Добавьте команду /links:
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import CallbackQueryHandler
async def links(update: Update, context: ContextTypes.DEFAULT_TYPE):
keyboard = InlineKeyboardMarkup([
[InlineKeyboardButton("Журнал Otus", url="https://otus.ru/journal/")],
[InlineKeyboardButton("Показать привет", callback_data="say_hello")],
])
await update.message.reply_text("Полезное:", reply_markup=keyboard)
async def on_click(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer() # обязательно: убирает "часики" на кнопке
if query.data == "say_hello":
await query.edit_message_text("Привет от Otus!")
Зарегистрируйте оба обработчика в main:
app.add_handler(CommandHandler("links", links))
app.add_handler(CallbackQueryHandler(on_click))
Результат: /links показывает две кнопки. «Журнал Otus» открывает ссылку. «Показать привет» не пишет в чат, но текст сообщения меняется на «Привет от Otus!». Без await query.answer() кнопка «крутится» у пользователя.
Шаг 6: меню команд бота
Меню команд — список у поля ввода, где видно доступные команды с описанием. Задается один раз через set_my_commands:
from telegram import BotCommand
async def setup_commands(app: Application):
await app.bot.set_my_commands([
BotCommand("start", "Запустить бота"),
BotCommand("links", "Полезные ссылки"),
])
Подключается через post_init при сборке приложения:
app = Application.builder().token(TOKEN).post_init(setup_commands).build()
Результат: появляется кнопка меню, при нажатии — список из трех команд с подписями. Обновление у клиентов Telegram может занять несколько минут.
Если не получилось: типовые симптомы
Диагностика по симптому:
- Бот молчит на
/start. Проверьте, что скрипт запущен и не упал, токен верный, и вы пишете своему боту (по username из BotFather). Unauthorized/401. Токен неверный или отозван — выпустите новый через/revoke.ModuleNotFoundError: No module named 'telegram'. Библиотека не установлена в то окружение, где запускаете бота — повторитеpip installв нужном venv.- Reply-кнопки не появились. На мобильном тапните иконку клавиатуры у поля ввода.
- Inline-кнопка «крутится» после нажатия. Вы не вызвали
await query.answer(). - Ошибка про event loop в Jupyter.
run_polling()рассчитан на запуск скриптом; в ноутбуке цикл событий уже занят — запускайте бот как отдельный.py-файл.
Выводы
- Токен выдает BotFather в Telegram; хранить его как пароль, не публикуя в коде.
- Reply-кнопки отправляют в чат готовый текст (обычные сообщения); inline-кнопки вызывают действие или ссылку и требуют
CallbackQueryHandler. - Для inline-кнопок с callback обязателен
await query.answer(), иначе кнопка зависает у пользователя. - Меню команд задается один раз через
set_my_commandsи обновляется у клиентов с задержкой. - Код зависит от версии библиотеки: примеры — под python-telegram-bot 21-22 (async); под другой пакет или мажорную версию синтаксис другой.
Где применяется / связь с практикой
Бот — частая первая практическая задача начинающего Python-разработчика: тут и работа с внешним API, и обработка событий, и асинхронный код. Та же логика масштабируется на рабочие сценарии: боты поддержки, уведомления, интеграции.
Освойте тему на практике
Чтобы разобраться в Python системно, а не только в ботах, — это тема курса Python Basic в Otus: основы языка, функции, работа с библиотеками. Посмотреть формат до старта помогают бесплатные вебинары Otus.
Смежные темы: Создание Телеграм-бота для генерации изображений с Docker и Stable Diffusion, Как научиться программировать с нуля: с чего начать.
FAQ
Можно ли писать бота на webhook, а не на polling?
Да. run_polling() удобен для разработки, а на боевом сервере с доменом и HTTPS обычно используют webhook (run_webhook). Логика обработчиков не меняется.
Reply-кнопки видны всем в групповом чате?
По умолчанию reply-клавиатура показывается адресату сообщения; в группах ее поведение регулирует параметр selective. Inline-кнопки видны всем под сообщением.
Чем aiogram отличается от python-telegram-bot?
Это две разные библиотеки-обертки над одним Bot API. aiogram (ветка 3.x) изначально асинхронный; python-telegram-bot — более распространенная универсальная библиотека. Синтаксис разный, поэтому код из статьи на aiogram без переписывания не запустится.



