Первый бот на 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. Скоро научусь большему! 🐤».

Подсказки, чтобы не застрять:

  1. Тебе понадобится фильтр на конкретную команду. Импортируй его: from aiogram.filters import Command.
  2. Регистрируй новый хэндлер так же, как start_handler, но с декоратором @dp.message(Command("help")).
  3. Внутри новой async def-функции вызови await message.answer("...") со своим текстом.
  4. Перезапусти 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!

Проверьте себя
1. Что делает объект Dispatcher (dp) в боте на aiogram?
AХранит токен бота и шифрует его
BПринимает входящие обновления и раздаёт их нужным хэндлерам
CОтправляет сообщения, фото и кнопки в Telegram
DПодключает бота к базе данных SQLite
2. Зачем перед message.answer(...) ставят await?
AЧтобы код выполнялся быстрее в десять раз
BЭто просто стиль оформления, без await тоже работает
CЧтобы дождаться выполнения асинхронного действия, не блокируя обслуживание других пользователей
DЧтобы Telegram запомнил токен бота
3. Что такое polling в контексте Telegram-бота?
AСпособ, при котором бот сам регулярно спрашивает у Telegram, есть ли новые обновления
BСпособ, при котором Telegram присылает обновления на адрес твоего сервера
CКоманда, начинающаяся со слэша
DБиблиотека для работы с базой данных
4. Как правильно объявить функцию-хэндлер на команду /start?
Adef start_handler(message): ...
B@dp.message(CommandStart())\nasync def start_handler(message: Message): ...
C@bot.command('/start')\ndef start_handler(): ...
Dasync def start_handler(): return '/start'
5. Почему токен бота нельзя выкладывать в открытый доступ?
AИз-за этого бот начнёт отвечать медленнее
BTelegram заблокирует команду /start
CПо токену любой сможет управлять твоим ботом, как по паролю
DЭто увеличит размер файла bot.py
6. Что произойдёт, если запустить два процесса python bot.py с одним и тем же токеном?
AБот станет отвечать в два раза быстрее
BTelegram выдаст конфликт getUpdates — с одним токеном работает только один polling
CНичего, оба процесса спокойно работают вместе
DТокен автоматически сменится на новый