Как сделать телеграм-бота на Python: от токена до кнопок

Как сделать телеграм-бота на Python: от токена до кнопок Полезное

Телеграм-бот — это программа, которая через официальный Bot API Telegram получает сообщения от пользователей и отправляет ответы. На Python такого бота собирают за один вечер: логику пишете вы, а доставку сообщений и хранение чатов берет на себя сервер Telegram.

Ниже — рабочий маршрут до конкретного результата: получить токен у BotFather, установить библиотеку, запустить минимального бота целиком, а потом по строкам разобрать, как он устроен, и добавить команды и кнопки. Пример базовый: без баз данных, генерации картинок и контейнеров — только каркас, на который потом наращивают функции.

Что нужно различать: бот, Bot API и библиотека

Три вещи, которые новички путают:

  • Bot API — это HTTP-интерфейс на стороне Telegram. Именно он принимает и отдает сообщения.
  • Библиотека (aiogram, pyTelegramBotAPI) — это обертка на Python над Bot API, чтобы не писать HTTP-запросы руками.
  • Ваш код — это обработчики (handlers): функции, которые решают, что ответить на конкретное сообщение или команду.

Сервер Telegram хранит очередь сообщений, а программа их забирает и отвечает. Забирать можно двумя способами: long polling (программа сама опрашивает сервер) или webhook (сервер сам шлет обновления на ваш адрес). В туториале используем polling — он не требует домена и HTTPS и запускается прямо на ноутбуке.

Шаг 1. Получаем токен у BotFather

Токен — это ключ доступа к вашему боту. Порядок действий:

  1. В Telegram найдите аккаунт @BotFather (с синей галочкой).
  2. Отправьте команду /newbot.
  3. Введите отображаемое имя бота (любое) и username — он должен заканчиваться на bot и быть уникальным, например my_first_echo_bot.
  4. BotFather пришлет строку вида 123456789:AAExampleTokenStringHere — это и есть токен.

Токен дает полный контроль над ботом, поэтому не публикуйте его в коде, репозитории или скриншотах. Если токен утек — в чате с BotFather выполните /revoke: старый ключ отключится и вы получите новый.

Шаг 2. Устанавливаем библиотеку

Для новых проектов беру aiogram — это современная асинхронная библиотека (актуальная ветка — aiogram 3.x). Асинхронность важна, когда бот обрабатывает много чатов одновременно: пока один запрос ждет ответа сети, обрабатываются другие.

Установка в отдельное виртуальное окружение:

python -m venv venv
source venv/bin/activate
pip install aiogram

В Windows активация окружения — venv\Scripts\activate.

Токен не хардкодим, а читаем из переменной окружения. Перед запуском задайте ее в терминале (Linux, macOS):

export BOT_TOKEN="123456789:AAExampleTokenStringHere"

В Windows PowerShell — $env:BOT_TOKEN="...". Так секрет остается вне кода: файл со скриптом можно спокойно коммитить.

Шаг 3. Минимальный рабочий бот целиком

Сначала — законченный файл, который копируется и запускается. Это эхо-бот: на /start он здоровается, на любой текст отвечает тем же текстом.

import os
import asyncio
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message

bot = Bot(token=os.environ["BOT_TOKEN"])
dp = Dispatcher()


@dp.message(CommandStart())
async def start_handler(message: Message):
    await message.answer("Привет! Я эхо-бот. Напиши мне что-нибудь.")


@dp.message()
async def echo_handler(message: Message):
    await message.answer(f"Ты написал: {message.text}")


async def main():
    await dp.start_polling(bot)


if __name__ == "__main__":
    asyncio.run(main())

Сохраните как bot.py и запустите python bot.py. Консоль останется занятой — это нормально, бот работает и ждет сообщений. Откройте своего бота в Telegram, нажмите Start: придет «Привет! Я эхо-бот…». Напишите «тест» — бот ответит «Ты написал: тест». Остановить бота — Ctrl+C.

Шаг 4. Разбираем по строкам

Теперь тот же код по частям.

  • import os — доступ к переменным окружения, из них берем токен.
  • Bot(token=os.environ["BOT_TOKEN"]) — объект бота. os.environ["BOT_TOKEN"] читает ранее заданную переменную; если ее нет, программа сразу упадет с KeyError, и это правильнее, чем работать без токена.
  • Dispatcher() — диспетчер: он смотрит на входящее сообщение и выбирает подходящий обработчик.
  • @dp.message(CommandStart()) — декоратор-фильтр. Функция под ним сработает только на команду /start.
  • @dp.message() без фильтра — ловит все остальные сообщения. Порядок важен: обработчики проверяются сверху вниз, поэтому более узкий фильтр /start стоит выше общего.
  • async def и await message.answer(...) — функции асинхронные, а message.answer отправляет ответ в тот же чат. await ждет завершения отправки, не блокируя другие чаты.
  • dp.start_polling(bot) — запускает опрос сервера в цикле.
  • asyncio.run(main()) — точка входа: запускает асинхронный цикл.

Ключевая идея: вы описываете обработчики, а диспетчер сам маршрутизирует к ним входящие сообщения.

Шаг 5. Добавляем команды

Команды — это сообщения, начинающиеся с /. Добавим /help. Дописать нужно импорт Command и новый обработчик выше общего echo_handler:

from aiogram.filters import Command


@dp.message(Command("help"))
async def help_handler(message: Message):
    await message.answer(
        "Я умею:\n"
        "/start - начать\n"
        "/help - эта справка\n"
        "Любой текст я повторю в ответ."
    )

Чтобы команды показывались в меню слева от поля ввода, их список задают у BotFather командой /setcommands, либо программно через bot.set_my_commands(...). Это удобство для пользователя, а не обязательный шаг.

Шаг 6. Добавляем кнопки

Кнопки бывают двух видов, их часто путают:

Тип Класс Где появляется Что шлет при нажатии
Reply-кнопки ReplyKeyboardMarkup вместо клавиатуры обычное текстовое сообщение
Inline-кнопки InlineKeyboardMarkup под самим сообщением callback (скрытые данные)

Inline-кнопки не засоряют чат текстом, поэтому для действий берут их. Добавим кнопку в ответ на /help. Нужны импорты клавиатуры, тип CallbackQuery и «магический фильтр» F для разбора callback:

from aiogram import F
from aiogram.types import (
    CallbackQuery,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
)


@dp.message(Command("menu"))
async def menu_handler(message: Message):
    keyboard = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text="Поздороваться", callback_data="say_hi")]
        ]
    )
    await message.answer("Нажми кнопку:", reply_markup=keyboard)


@dp.callback_query(F.data == "say_hi")
async def hi_callback(callback: CallbackQuery):
    await callback.message.answer("Привет из кнопки!")
    await callback.answer()

Разбор:

  • callback_data="say_hi" — метка, которая придет обратно при нажатии. По ней и различают кнопки, если их несколько.
  • @dp.callback_query(F.data == "say_hi") — обработчик срабатывает, когда пришел callback именно с этой меткой.
  • await callback.answer() в конце обязателен: без него у кнопки останется «часики» загрузки. Этот вызов гасит индикатор и ничего не пишет в чат.

Добавьте команду /menu к обработчикам, перезапустите бота — появится сообщение с кнопкой, а по нажатию придет ответ.

Если бот молчит: диагностика по симптому

  • KeyError: 'BOT_TOKEN' при старте — переменная окружения не задана в этом же терминале. Экспортируйте токен и запускайте бота в том же окне.
  • TokenValidationError или ошибка 401 Unauthorized — токен неверный или отозван. Скопируйте его заново у BotFather без пробелов.
  • Бот запустился, но не отвечает — вероятно, запущены две копии одного бота (например, в двух терминалах). Telegram отдает обновление только одному потребителю; закройте лишние процессы. Также проверьте, что пишете именно тому боту, чей токен используете.
  • Отвечает на текст, но не на команду — общий обработчик @dp.message() стоит выше обработчика команды и перехватывает ее. Поднимите фильтры команд выше.

Альтернатива: pyTelegramBotAPI

Если асинхронность пока избыточна и нужен простой синхронный код, берут pyTelegramBotAPI (импортируется как telebot). Тот же эхо-бот на ней короче:

import os
import telebot

bot = telebot.TeleBot(os.environ["BOT_TOKEN"])


@bot.message_handler(commands=["start"])
def start(message):
    bot.send_message(message.chat.id, "Привет! Я эхо-бот.")


@bot.message_handler(content_types=["text"])
def echo(message):
    bot.send_message(message.chat.id, f"Ты написал: {message.text}")


bot.infinity_polling()

Выбор между библиотеками:

Критерий aiogram 3.x pyTelegramBotAPI
Модель асинхронная (async/await) по умолчанию синхронная
Порог входа чуть выше ниже
Много чатов сразу обрабатывает эффективно упирается в блокировки
Когда брать боты «на вырост», нагрузка быстрый прототип, учеба

Для нового проекта, который будет расти, разумнее сразу aiogram. Для первого учебного бота подойдет любая.

Выводы

  • Телеграм-бот на Python — это ваши обработчики поверх Bot API; доставку сообщений берет на себя сервер Telegram.
  • Токен получают у BotFather и хранят в переменной окружения, а не в коде; при утечке отзывают через /revoke.
  • Минимальный рабочий бот на aiogram 3.x — это объект Bot, Dispatcher и пара обработчиков под start_polling.
  • Команды задают фильтром Command, а действия удобнее вешать на inline-кнопки с callback_data и обязательным callback.answer().
  • Polling запускается локально без домена; webhook нужен позже, для боевого размещения.

Где применяется / связь с практикой

Телеграм-боты — частая первая рабочая задача Python-джуна: автоответчик поддержки, уведомления из CI, напоминания, сбор заявок. Каркас из этой статьи масштабируется до таких проектов, если добавить хранение данных, обработку ошибок и вынос токена в конфигурацию.

Освойте тему на практике

Системно разобраться с синтаксисом, функциями, окружениями и асинхронностью помогает курс Python Basic: на нем как раз доводят учебные проекты до запускаемого результата. Посмотреть формат занятий и задать вопросы преподавателю можно на открытых уроках Otus — они бесплатные.

FAQ

Нужен ли сервер, чтобы бот работал постоянно?
Для учебы — нет, хватает вашего компьютера с запущенным скриптом. Как только закрываете программу, бот перестает отвечать. Для круглосуточной работы бота размещают на VPS или в облаке и запускают как сервис.

Можно ли писать боту на русском и распознавать русские команды?
Текст сообщений может быть любым, в том числе русским, и вы сравниваете его в коде как обычную строку. Но сами команды-слэши (/start) Telegram ждет латиницей, поэтому русские «команды» реализуют как обычный текст или кнопки.

Чем polling отличается от webhook и что выбрать новичку?
При polling программа сама опрашивает сервер Telegram и не требует домена — берите его для старта. Webhook — это когда Telegram сам шлет обновления на ваш HTTPS-адрес; он нужен для боевого размещения и требует настроенного сервера с сертификатом.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)