Первый бот на Python: /start
Сейчас ты напишешь свои первые тридцать строк кода, после которых живой бот в Telegram ответит тебе сообщением — и это будет полностью твоё творение.
Хэндлер — это функция-обработчик, которую aiogram вызывает в ответ на определённое сообщение, команду или нажатие кнопки. Считай её ловушкой: «если придёт /start — выполни вот это».
Зачем нам этот урок
Представь: ты открываешь Telegram, находишь своего бота, пишешь ему /start — и он мгновенно отвечает: «Привет! Я Цыплёнок-помощник 🐤». Не ты сидишь и печатаешь ответ вручную, а программа, которую ты сам написал и запустил. Магия? Нет, всего лишь Python и немного аккуратности.
В прошлом уроке ты уже сходил к BotFather и получил токен — тот самый секретный ключ вида 123456:ABC.... Сейчас мы возьмём этот токен и оживим бота. Это и есть наш сквозной проект курса — бот Цыплёнок-помощник. Сегодня он научится здороваться, а дальше, от урока к уроку, мы будем добавлять ему кнопки, память, погоду и многое другое.
Вот к чему мы придём за этот урок — простой, но полностью рабочий бот:
import asyncio
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message
TOKEN = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
bot = Bot(token=TOKEN)
dp = Dispatcher()
@dp.message(CommandStart())
async def start_handler(message: Message):
await message.answer("Привет! Я Цыплёнок-помощник 🐤")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())Результат: в чате бот ответит «Привет! Я Цыплёнок-помощник 🐤» в ответ на команду /start. Не пугайся незнакомых слов вроде async и polling — сейчас разберём каждую строчку до того, как ты её напишешь.
Как вообще бот общается с Telegram
Давай сначала разберёмся, что происходит, когда ты пишешь боту. Самая удобная аналогия — официант в кафе.
Ты (пользователь) сидишь за столиком и делаешь заказ. Официант (твоя программа-бот) не готовит еду сам — он бегает между тобой и кухней (серверами Telegram). Ты сказал «хочу пиццу» — официант записал, отнёс на кухню, забрал готовое блюдо и принёс тебе. Bot API — это и есть набор правил, по которым официант общается с кухней: какие фразы говорить, чтобы заказ поняли.
Теперь важный вопрос: откуда официант узнаёт, что у столика появился новый заказ? Есть два способа.
- Polling — официант сам каждые пару секунд подходит к кухне и спрашивает: «Есть что-нибудь новенькое для меня?». Если есть — забирает. Это самый простой способ, и именно его мы используем сегодня. Запускается одной строкой и работает прямо с твоего компьютера.
- Webhook — наоборот, кухня сама зовёт официанта, когда появляется заказ. Быстрее, но требует своего сервера с адресом в интернете. К этому мы вернёмся гораздо позже.
Для первого бота polling — идеальный выбор: ничего настраивать в интернете не нужно, запустил скрипт на ноутбуке — и бот живой, пока скрипт работает.
Может возникнуть вопрос: а почему бы боту просто не «слушать» постоянно, как телефон ждёт звонка? Дело в том, что твоя программа не торчит в интернете под собственным адресом — её никто снаружи не может вызвать напрямую. Поэтому при polling инициатива идёт от бота: это он стучится к Telegram, а не наоборот. Зато такой подход не требует ни доменного имени, ни белого IP, ни настройки сервера — всё, что нужно, это рабочий интернет и запущенный скрипт. Именно поэтому в начале пути polling выбирают практически все: он прощает ошибки и позволяет сосредоточиться на логике бота, а не на инфраструктуре.
Знакомимся с инструментами aiogram
Чтобы наш Python-код умел разговаривать с Telegram, мы используем библиотеку aiogram — это асинхронная библиотека для написания ботов, в курсе берём версию 3.x. Она берёт на себя всю рутину общения с Bot API, а нам оставляет только интересное: что бот говорит и на что реагирует.
В aiogram есть два главных объекта, и без них не обходится ни один бот.
Объект Bot — это «рот и руки» бота
Bot — объект, который умеет отправлять запросы в Telegram: слать сообщения, фото, кнопки. Когда нужно что-то сказать пользователю, мы обращаемся именно к нему. Чтобы создать его, нужен только токен:
bot = Bot(token=TOKEN)Результат: создаётся объект bot, через который позже пойдут все исходящие сообщения. Сам по себе он ничего не отправляет — ждёт команды от тебя.
Объект Dispatcher — это «диспетчер заказов»
Dispatcher — объект, который принимает входящие обновления и раздаёт их нужным хэндлерам. Вернёмся к кафе: Dispatcher — это менеджер зала, который смотрит на каждый входящий Update (единицу входящих данных: новое сообщение, нажатую кнопку или другое событие) и решает, какому хэндлеру его передать. Пришла команда /start — менеджер находит функцию, которая отвечает за /start, и вызывает её.
dp = Dispatcher()Результат: создаётся диспетчер dp, к которому мы будем «подвешивать» хэндлеры. Имена bot и dp мы будем использовать во всех уроках курса — привыкай к ним.
Шаг 1. Устанавливаем aiogram и готовим файл
Открой терминал (командную строку) в папке, где будет жить твой проект, и установи библиотеку. Делается это один раз:
pip install aiogramРезультат: в терминале побегут строчки загрузки, и в конце появится что-то вроде Successfully installed aiogram-3.x.x. Значит, библиотека готова к работе.
Теперь создай в этой же папке файл bot.py — это будет главный файл нашего проекта на весь курс. Внутри него мы и пишем код. Имя именно bot.py, потому что мы будем ссылаться на него в следующих уроках.
Шаг 2. Импортируем нужное и создаём объекты
Начало файла — это импорты и создание двух наших объектов. Разберём по строчкам:
import asyncio
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message
TOKEN = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
bot = Bot(token=TOKEN)
dp = Dispatcher()Результат: код пока ничего не отправляет, но создаёт фундамент бота. Что здесь происходит:
import asyncio— стандартный модуль Python для асинхронной работы. Он нужен, чтобы запустить бота правильно (об этом ниже).from aiogram import Bot, Dispatcher— берём из aiogram два класса, с которыми уже познакомились.from aiogram.filters import CommandStart— берём готовый фильтр, который ловит именно команду/start. Команда — это сообщение, начинающееся с/, на которое бот реагирует особым образом.from aiogram.types import Message— это тип входящего сообщения; используем его, чтобы код был понятнее (и редактор подсказывал методы).TOKEN = "..."— сюда вставляешь свой токен от BotFather. Пока, для первого запуска, можно так. Но запомни: токен — это пароль от твоего бота, его нельзя показывать никому. В одном из следующих уроков мы спрячем его в переменные окружения.
Шаг 3. Пишем хэндлер на /start
Вот сердце нашего бота — функция, которая срабатывает на команду /start:
@dp.message(CommandStart())
async def start_handler(message: Message):
await message.answer("Привет! Я Цыплёнок-помощник 🐤")Результат: когда пользователь пишет боту /start, в чат приходит сообщение «Привет! Я Цыплёнок-помощник 🐤». Теперь разберём по косточкам, потому что тут три новые штуки.
Что за @ перед функцией?
Строка @dp.message(CommandStart()) — это декоратор. Считай его наклейкой-стикером, который ты лепишь на функцию и говоришь диспетчеру: «эй, dp, вот эта функция отвечает за сообщения, которые проходят фильтр CommandStart(), то есть за команду /start». Так aiogram запоминает, кого звать, когда придёт нужное сообщение. Это и есть регистрация хэндлера.
Почему async и await?
Функция объявлена как async def, а внутри стоит await. Не пугайся — это и есть та самая «асинхронность», которой все боятся. На самом деле идея простая: пока бот ждёт, что Telegram примет сообщение (а это занимает доли секунды на отправку по сети), он не висит без дела, а может обслуживать других пользователей. await означает «подожди, пока это действие выполнится, но не блокируй остальных». Тебе как новичку достаточно правила: методы aiogram, которые что-то отправляют, вызывай через await, а функции-хэндлеры объявляй через async def. Со временем интуиция придёт сама.
Что такое message.answer?
Параметр message — это то самое входящее сообщение от пользователя (тип Message). У него есть удобный метод .answer(текст), который отправляет ответ в тот же чат, откуда пришло сообщение. То есть await message.answer("...") читается как «ответь в этот чат вот таким текстом».
Шаг 4. Запускаем бота через polling
Осталось завести мотор. Добавляем в конец файла:
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())Результат: при запуске bot.py бот начинает каждые доли секунды спрашивать Telegram «есть новости?» и реагировать на сообщения. В терминале будет видно, что бот запущен и слушает обновления.
dp.start_polling(bot)— запускает тот самый polling: диспетчер начинает получать обновления и раздавать их хэндлерам. Мы передаём емуbot, чтобы было через что отвечать.asyncio.run(main())— стандартный способ запустить асинхронную функцию. Без негоasync def main()просто не выполнится — её обязательно нужно «завести» черезasyncio.run.if __name__ == "__main__":— привычная питоновская строчка, которая говорит «выполни это, только если файл запустили напрямую».
Теперь в терминале набери python bot.py, открой Telegram, найди своего бота по имени из BotFather и напиши ему /start. Если он ответил — поздравляю, ты только что оживил свою первую программу-бота! Чтобы остановить бота, вернись в терминал и нажми Ctrl+C.
Маленький сниппет: как aiogram «узнаёт» команду
Чтобы у тебя не осталось ощущения магии, посмотрим на стандартном Python, как примерно работает проверка «это команда /start или обычный текст?». Aiogram делает это внутри себя сложнее, но суть та же — смотрит на начало строки:
def is_start_command(text):
text = text.strip()
return text == "/start" or text.startswith("/start ")
for msg in ["/start", "привет", "/start now", " /start "]:
print(repr(msg), "->", is_start_command(msg))Вывод:
'/start' -> True 'привет' -> False '/start now' -> True ' /start ' -> True
Видишь? Никакого волшебства: просто проверка строки. Фильтр CommandStart() делает похожее, только аккуратнее и с учётом параметров команды. Понимание этого снимает страх — внутри библиотеки обычный код, такой же, какой пишешь ты.
Кстати, обрати внимание на одну деталь в нашем сниппете: перед сравнением мы вызвали text.strip(), чтобы убрать случайные пробелы по краям. Без этого строка " /start " не совпала бы с "/start", и бот промолчал бы на, казалось бы, правильную команду. Такие мелочи — частая причина «он почему-то не отвечает» у новичков. Хорошая новость: фильтры aiogram уже учитывают подобные тонкости за тебя, поэтому в реальном боте тебе не придётся писать такие проверки руками — ты просто навешиваешь CommandStart() или Command("help") и занимаешься интересным.
Частые ошибки и подводные камни
На первом боте почти все спотыкаются об одно и то же. Давай я заранее покажу грабли, чтобы ты на них не наступил.
1. Забыл await перед методом отправки
Если написать message.answer("...") без await, бот не ответит, а в терминале мелькнёт предупреждение вроде coroutine ... was never awaited. Правило простое: всё, что отправляет в Telegram, идёт через await.
2. Хэндлер объявлен как обычная функция
Если написать def start_handler(...) вместо async def, aiogram не сможет его правильно вызвать. Хэндлеры — всегда async def.
3. Неверный или чужой токен
Если скопировал токен с пробелом, лишним символом или вообще вставил не тот — при запуске вылетит ошибка вроде Token is invalid или Unauthorized. Проверь, что токен скопирован целиком и точь-в-точь как выдал BotFather. И помни: токен нельзя выкладывать в публичный репозиторий или показывать друзьям — по нему любой сможет управлять твоим ботом.
4. Бот запущен дважды
Если запустить python bot.py в двух терминалах с одним токеном, Telegram начнёт ругаться (Conflict: terminated by other getUpdates request). С одним токеном — только один работающий polling. Останови лишний процесс через Ctrl+C.
5. Закрыл терминал — бот умер
Пока polling запущен с твоего компьютера, бот живёт ровно столько, сколько работает скрипт. Закрыл окно терминала или выключил ноутбук — бот замолчал. Это нормально для разработки; о том, как держать бота онлайн постоянно, поговорим в модуле про деплой.
Мини-практика: добавь /help своими руками
Теперь твоя очередь. Задание: научи Цыплёнка отвечать ещё и на команду /help — например, текстом «Я умею здороваться по /start. Скоро научусь большему! 🐤».
Подсказки, чтобы не застрять:
- Тебе понадобится фильтр на конкретную команду. Импортируй его:
from aiogram.filters import Command. - Регистрируй новый хэндлер так же, как
start_handler, но с декоратором@dp.message(Command("help")). - Внутри новой
async def-функции вызовиawait message.answer("...")со своим текстом. - Перезапусти
bot.pyи проверь обе команды в чате.
Вот как примерно должен выглядеть новый кусок (но сначала попробуй сам, а потом сверься):
from aiogram.filters import Command
@dp.message(Command("help"))
async def help_handler(message: Message):
await message.answer("Я умею здороваться по /start. Скоро научусь большему! 🐤")Результат: теперь бот отвечает и на /start, и на /help — каждая команда своим сообщением. Если получилось — ты уже понимаешь главный принцип aiogram: один хэндлер на одно событие.
Итоги
Сегодня ты прошёл огромный шаг — от пустого файла до живого бота, который отвечает в реальном чате. Давай закрепим, что у тебя теперь в голове:
- Бот общается с Telegram через Bot API, как официант с кухней, а обновления получает через polling — сам регулярно спрашивает «есть новости?».
- Объект
botотправляет сообщения, объектdp(Dispatcher) принимает обновления и раздаёт их хэндлерам. - Хэндлер — это
async def-функция с декоратором@dp.message(...), которая срабатывает на нужное событие. - Методы отправки вызываются через
await, а сам бот запускается черезasyncio.run(main())иdp.start_polling(bot).
В следующем уроке мы научим Цыплёнка не просто отвечать одинаковым текстом, а показывать кнопки и меню — те самые удобные клавиатуры под полем ввода и под сообщениями. Бот станет заметно живее, а пользоваться им будет приятнее. До встречи в следующем уроке — и не забудь сохранить свой bot.py!