Друзья, приветствую!

Если вы следите за моими статьями, то знаете, что на Хабре у меня опубликовано более 10 объемных публикаций на тему разработки телеграмм ботов через замечательный Python-фреймворк Aiogram 3.

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

И, как вы догадались из названия статьи, сегодня я расскажу вам как, без особых заморочек и трудностей, перейти от поллинга к вебхукам. Прочитав эту статью у вас больше не будет страха перед настройкой, запуском и работой с такими ботами.

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

В общем, будет интересно и познавательно!

Что такое вебхуки и поллинг?

Прежде чем мы приступим к коду, давайте разберемся в отличиях подхода поллинга и вебхуков, да и в целом, что это все такое.

Поллинг (Polling)

Поллинг — это метод, при котором бот регулярно отправляет запросы на сервер Telegram, чтобы узнать, не пришли ли новые сообщения. Процесс выглядит так:

  1. Бот спрашивает сервер Telegram: «Есть ли для меня новые сообщения?»

  2. Сервер Telegram отвечает: «Вот, держи!» — если есть новые сообщения, или «Нет, ничего нового» — если нет.

  3. Бот обрабатывает полученные сообщения и снова отправляет запрос, чтобы проверить, не появилось ли чего нового.

Преимущества поллинга:

  • Легче настроить, так как не требуется внешний сервер.

  • Работает даже если у вас нет домена и веб‑сервера.

Недостатки поллинга:

  • Требует больше ресурсов: бот постоянно отправляет запросы к Telegram, даже если новых сообщений нет.

  • Задержка: поскольку бот делает запросы с интервалом, новые сообщения обрабатываются с небольшой задержкой. Сама задержка при малой нагрузке на бота может быть незаметной, а при большом количестве обращений к боту на единицу времени может стать критической.

Вебхуки (Webhooks)

Вебхуки работают по-другому: вместо того, чтобы бот сам проверял, есть ли новые сообщения, Telegram сам отправляет сообщения боту, как только они приходят. Процесс выглядит так:

  1. Бот сообщает серверу Telegram: «Вот мой адрес (URL), отправляй все новые сообщения сюда.»

  2. Когда кто‑то отправляет сообщение боту (про), Telegram тут же пересылает его на указанный URL.

  3. Бот принимает сообщение и сразу его обрабатывает.

Преимущества вебхуков:

  • Меньше нагрузки на сервер, так как Telegram отправляет сообщения только при их поступлении.

  • Мгновенная реакция: бот обрабатывает сообщения сразу, как только они приходят.

Недостатки вебхуков:

  • До недавнего времени, а именно, до вашего прочтения этой статьи, недостатком можно было назвать трудности в настройке, запуске и поддержке. Мол нужно доменное имя, некий сервер, какие‑то там защищенные https протоколы и прочее. Дочитав эту статью до конца таких проблем у вас больше не будет.

Если подвести итоги данного блока, то можно сказать что вебхуки — это идеальный выбор для сложных и активных ботов, которые обрабатывают много сообщений и должны работать быстро и с минимальными задержками.

Чем мы сегодня займемся?

Сегодня мы создадим простого бота на базе aiogram 3, работающего на вебхуках. Я проведу вас через все этапы разработки и покажу, как запустить такого бота локально, сохраняя привычную структуру проекта и комфорт работы, как при использовании поллинга.

Разработка будет простой и удобной, но самое интересное — мы доведем дело до конца и выполним деплой бота. Я предложу максимально доступный метод, не требующий ни покупки доменного имени, ни настройки HTTPS, ни аренды VPS.

Мы создадим функционального бота, запустим его локально, а затем развернем на удаленном сервере. И всё это без лишних затрат и сложностей. Готовы? Тогда приступим!

Подготовка

Для подготовки желательно, чтобы у вас были базовые представления о Python и, в частности, о создании Telegram-ботов с использованием aiogram 3. Как я уже упоминал, этой теме я посвятил более 10 крупных статей, так что всю необходимую информацию вы можете найти в моем профиле на Хабре.

Получаем токен бота

Кроме того, необходимо обзавестись токеном вашего телеграм бота. Подробной информации на этот счет в сети очень много, поэтому, тут приведу только короткое описание.

  • Заходим в BotFather

  • Выполняем команду /newbot

  • Вводим имя. Может быть любым, после можно исправить

  • Придумываем логин. Пишем на английском, без пробелов и в конце обязательно: Bot, bot или BOT.

  • Если все хорошо получаем ссылку на бота и токен. Токен сохраняем в надежном месте.

Локально ставим Ngrok

Теперь нам нужно подготовить локальную среду для работы с вебхуками. Дело в том, что для этого требуется сервер с защищённым доменным именем (HTTPS).

Чтобы упростить задачу и вести разработку на своём компьютере, мы воспользуемся невероятно полезной программой Ngrok (не путать с Nginx). Ngrok позволяет создавать безопасные туннели к вашему локальному серверу, что делает его идеальным инструментом для разработки ботов с вебхуками.

Вы можете установить Ngrok либо через установку, либо запустив его в контейнере. Я же буду демонстрировать процесс на Windows с установкой, поскольку это самый простой и наглядный вариант.

  • Переходим на официальный сайт Ngrok

  • Регистрируемся

  • Входим в свой профиль

  • Переходим на вкладку установки

  • Тут выбираем свою операционную систему (я выбираю Windows)

  • Теперь скачаем установщик

  • Копируем строку с токеном авторизации (скрин выше). Он нам пригодится далее

  • Запускаем скачанный файл и вставляем в открывшийся терминал команду, которую мы копировали на прошлом шаге

  • Теперь запускаем сервер командой ngrok http 8000. В данном случае 8000 — это порт, который мы будем использовать в телеграмм боте для подключения. Можете указать любой.

  • После успешного запуска вы должны получить такой результат

Сгенерированную ссылку можно сразу скопировать
Сгенерированную ссылку можно сразу скопировать

Тут нас будет интересовать сгенерированна публичная ссылка. К ней мы совсем скоро прикрутим наш вебхук.

Для проверки, что ссылка корректно работает можем ее вставить в любой браузер. Если все хорошо, то вы должны увидеть такой результат:

На этом мы завершаем нашу подготовку и можем переходить к созданию нашего бота на вебхуках.

Структура проекта

Структуру бота я буду использовать стандартную, как во всех своих проектах и статьях, посвященных aiogram 3, так что если читали мои прошлые статьи, то неожиданностей для вас не будет. Для удобства предлагаю вам повторить данную структуру и у себя.

tg_bot_project/
├── handlers/
│      ├── __init__.py
│      ├── start.py
├── requirements.txt
├── .env
├── aiogram_run.py
├── create_bot.py

Файл requirements.txt

requirements.txt — файл с библиотеками для установки. Обязательно его указывайте, так как он нам пригодится на этапе деплоя.

Содержимое файла:

aiogram==3.11.0
python-decouple==3.8
  • При установке aiogram автоматически подтянется библиотека aiohttp, которая будет использоваться в качестве сервера для запуска нашего бота. Это удобно, так как aiohttp отлично справляется с обработкой вебхуков.

  • python-decouple — удобная библиотека для работы с переменными окружения файла .env.

Файл .env

Файл с переменными окружения. Вот пример заполнения для текущего проекта:

BOT_TOKEN=7095885701:BAE6oIl_QааEW5K0UtWЗZNM5TTdmoT_QZcxM
ADMIN_ID=5227842777
HOST=0.0.0.0
PORT=8000
BASE_URL=https://9264-85-175-194-59.ngrok-free.app

Данные токена бота и телеграм айди админа, естественно, выдуманные.

Хост 0.0.0.0 я использовал, чтобы избежать необходимости указывать конкретный IP-адрес сервера. Это позволяет приложению принимать запросы с любого IP-адреса, привязанного к серверу, упрощая настройку и развертывание.

Порт необходимо указать тот, который вы указывали при запуске Ngrok. Я вводил команду ngrok http 8000, так что тут указал 8000.

Ссылку указываем ту, которую сгенерировал Ngrok при запуске. Обратите внимание, при повторном запуске ссылка изменится, так что будьте внимательны!

Файл create_bot.py

Текущий файл create_bot.py у меня имеет следующий вид:

from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from decouple import config


# переменные для работы
ADMIN_ID = config('ADMIN_ID')
BOT_TOKEN = config("BOT_TOKEN")
HOST = config("HOST")
PORT = int(config("PORT"))
WEBHOOK_PATH = f'/{BOT_TOKEN}'
BASE_URL = config("BASE_URL")

 
# инициализируем бота и диспетчера для работы с ним
bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
dp = Dispatcher()

В целом, если читали мои прошлые статьи по aiogram 3, для вас тут не должно быть вопросов. Если коротко, то тут мы вывели все нужные переменные для проекта, вытянув их с .env.

Далее мы инициализировали объект бота и диспетчер для работы с ним. В данном месте, как вы заметили, никаких отличий от описания бота в режиме поллинга нет.

Файл aiogram_run.py

Многие новички, столкнувшись с кодом, который вы найдете в этом файле, решают отказаться от работы с вебхуками и продолжают использовать поллинг. Однако на самом деле всё гораздо проще, чем кажется.

Сейчас я приведу полный код, а после дам подробные комментарии:

import logging
from aiogram.types import BotCommand, BotCommandScopeDefault
from aiohttp import web
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from create_bot import bot, dp, BASE_URL, WEBHOOK_PATH, HOST, PORT, ADMIN_ID
from handlers.start import router


# Функция для установки командного меню для бота
async def set_commands():
    # Создаем список команд, которые будут доступны пользователям
    commands = [BotCommand(command='start', description='Старт')]
    # Устанавливаем эти команды как дефолтные для всех пользователей
    await bot.set_my_commands(commands, BotCommandScopeDefault())


# Функция, которая будет вызвана при запуске бота
async def on_startup() -> None:
    # Устанавливаем командное меню
    await set_commands()
    # Устанавливаем вебхук для приема сообщений через заданный URL
    await bot.set_webhook(f"{BASE_URL}{WEBHOOK_PATH}")
    # Отправляем сообщение администратору о том, что бот был запущен
    await bot.send_message(chat_id=ADMIN_ID, text='Бот запущен!')


# Функция, которая будет вызвана при остановке бота
async def on_shutdown() -> None:
    # Отправляем сообщение администратору о том, что бот был остановлен
    await bot.send_message(chat_id=ADMIN_ID, text='Бот остановлен!')
    # Удаляем вебхук и, при необходимости, очищаем ожидающие обновления
    await bot.delete_webhook(drop_pending_updates=True)
    # Закрываем сессию бота, освобождая ресурсы
    await bot.session.close()


# Основная функция, которая запускает приложение
def main() -> None:
    # Подключаем маршрутизатор (роутер) для обработки сообщений
    dp.include_router(router)

    # Регистрируем функцию, которая будет вызвана при старте бота
    dp.startup.register(on_startup)

    # Регистрируем функцию, которая будет вызвана при остановке бота
    dp.shutdown.register(on_shutdown)

    # Создаем веб-приложение на базе aiohttp
    app = web.Application()

    # Настраиваем обработчик запросов для работы с вебхуком
    webhook_requests_handler = SimpleRequestHandler(
        dispatcher=dp,  # Передаем диспетчер
        bot=bot  # Передаем объект бота
    )
    # Регистрируем обработчик запросов на определенном пути
    webhook_requests_handler.register(app, path=WEBHOOK_PATH)

    # Настраиваем приложение и связываем его с диспетчером и ботом
    setup_application(app, dp, bot=bot)

    # Запускаем веб-сервер на указанном хосте и порте
    web.run_app(app, host=HOST, port=PORT)


# Точка входа в программу
if __name__ == "__main__":
    # Настраиваем логирование (информация, предупреждения, ошибки) и выводим их в консоль
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)  # Создаем логгер для использования в других частях программы
    main()  # Запускаем основную функцию

Как вы заметили, комментарии я оставил и в коде, но сейчас акцентируем внимание на действительно важных моментах.

Установка вебхука

В функции on_startup мы настраиваем вебхук для бота. Это значит, что теперь Telegram будет сразу отправлять обновления вашему боту через HTTP запросы, как только они появляются.

Управление жизненным циклом бота

  • В on_startup не только настраиваем вебхук, но и отправляем сообщение администратору о запуске бота.

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

Запуск веб-сервера на aiohttp

В функции main создаем веб-приложение с помощью aiohttp, которое будет принимать запросы от Telegram. Мы используем SimpleRequestHandler для обработки этих запросов и связи с вашим ботом.

После этого запускаем веб-сервер на указанном хосте и порте, чтобы ваше приложение aiohttp могло принимать и обрабатывать запросы от Telegram.

Таким образом, мы передали работу по получению обновлений от Telegram серверу aiohttp.

Как вы могли заметить, все не так уж и трудно, правда? И хочу подчеркнуть, что все это дело мы описали и совсем скоро запустим прямо с локальной машины!

Файл hendlers/start.py

В этом файле мы опишем простую логику нашего бота. Пусть бот реагирует на команду /start заготовленным сообщением и добавим простой режим эхо-бота. Этого простого примера вам будет достаточно на 100%, чтоб перебраться от поллинга к вебхукам, так как в хендлерах вообще никаких отличий от работы в режиме вебхуков не будет.

Выполним импорты:

from aiogram import Router
from aiogram.filters import CommandStart
from aiogram.types import Message
  • Router импортируем для удобства масштабирования бота.

  • CommandStart для удобной обработки команды /start

  • Message для аннотаций

Теперь назначим наш роутер:

router = Router()

Теперь напишу 2 функции. Первая, которая будет реагировать на команду /start, а вторая, которая будет выступать в режиме эхо-бота. Вы же, в свою очередь, можете тут использовать вообще любой код хендлеров, который вы использовали в своих поллинг ботах. Повторюсь, отличий никаких!

# функция для реагирования на команду /start
@router.message(CommandStart())
async def command_start_handler(message: Message) -> None:
    await message.answer(f"Привет, <b>{message.from_user.full_name}</b>! Как дела?")


# функция эхо-бот
@router.message()
async def echo_handler(message: Message) -> None:
    await message.answer(f'Повторяю: <b>{message.text}</b>')

На этапе настройки мы присвоили parse_mode=ParseMode.HTML, так что теперь можем использовать HTML-теги в коде без необходимости указания ParseMode. Я использовал <b></b>, который делает текст жирным.

Ну что, запустим бота!

Для этого нам необходимо запустить файл aiogram_run.py.

После этого, если все настроено корректно, в консоли вы должны получить такой результат:

Бот при запуске отправил мне сообщение, что доказывает, что все настроено корректно.

Кроме того, меню корректно подгрузилось.

Теперь попробую выполнить команду /start

Я получил просто мгновенный ответ от бота, что не может не радовать.

Напишу пару сообщений боту.

Вижу, что режим эхо-бота так же работает. А теперь заглянем в консоль, в которой мы запустили бота:

Тут вы можете заметить, что логи теперь летят, в том числе и от aiohttp. Каждый наш запрос теперь сопровождается POST запросом на стороне aiohttp, этого мы и добивались.

Как видите, друзья, абсолютно ничего сложного и страшного нет в том, чтоб ботов писать через технологию вебхуков, особенно, когда речь идет про aiogram 3.

Теперь запустим бота на удаленном сервере.

Простой деплой бота с вебхуками

Для деплоя я воспользуюсь сервисом Amvera Cloud.

Этот сервис мне нравится за простоту: здесь легко поднять веб-приложение, Telegram-бота, сайт или любой скрипт, который требует постоянной работы.

Чтобы развернуть свой проект на Amvera, достаточно создать проект, подготовить простой файл настроек (буквально 5 строк кода) или Dockerfile и доставить их в папку проекта через GIT. Остальное сервис берет на себя.

В случае с ботом на вебхуках, как и с любым веб-приложением, к проекту автоматически привязывается бесплатное доменное имя с поддержкой HTTPS, что идеально подходит для наших задач.

К тому же, приятно получить 111 рублей просто за регистрацию. С учетом демократичных цен на сервисе, этих денег вполне достаточно для уверенного тестирования своих проектов.

Для деплоя я воспользуюсь файлом конфигурации и GIT для загрузки файлов.

Приступим.

  • Регистриуемся на Amvera Cloud, если ещё не было регистрации

  • Заходим в раздел проектов

  • Создаем новый проект. На данном этапе необходимо указать название проекта и выбрать тариф. Для нашего бота вполне подойдет один из начальных тарифов.

  • Второй экран пока пропускаем, нажимая на «Далее»

  • На открывшемся экране необходимо указать некоторые данные. В результате чего система сама сгенерирует config файл. В моем случае данные следующие:

Стрелками я указал на поля, которые необходимо заполнить. Тут важно, что порт в настройках соответствовал порту из .env файла в боте. После заполнения всех данных нажимаем «Завершить». Настройки, в случае чего, можно будет подправить в файле.

  • Подключаем бесплатный домен

После всех настроек заходим в созданный проект и там перемещаемся на вкладку «Настройки». Тут нам необходимо активировать домен и скопировать его. В моем случае доменное имя aiogram3webhook-yakvenalex.amvera.io

  • Теперь переходим на вкладку «Репозиторий». Тут нас будет интересовать команда по привязке репозитория через GIT.

Теперь вернемся в нашего бота, остановим его и выполним несколько команд для доставки файлов бота в Амверу.

Выполняем инициализацию локального GIT-репозитория

git init

Привязываем удаленный репозиторий (подставьте свою ссылку):

git remote add amvera https://git.amvera.ru/LOGIN/Project_name

Получаем файл настроек из удаленного репозитория

git pull amvera master

Если впервые работаете, то после этой команды появится окно GIT для авторизации. Тут вводим логин и пароль от профиля кабинета Амверы.

После этой команды вы получите файл настроек. Проверьте его, чтоб все данные были заполнены корректно. У меня настройки (файл amvera.yml) имеет следующий вид:

---
meta:
  environment: python
  toolchain:
    name: pip
    version: 3.12
build:
  requirementsPath: requirements.txt
run:
  scriptName: aiogram_run.py
  persistenceMount: /data
  containerPort: 8000

При необходимости внесите правки в данный файл.

Теперь в файле .env заменим BASE_URL на тот, который мы получили внутри проекта, активируя свой домен. На выходе должно получится нечто похожее в файле .env:

BOT_TOKEN=BOT_TOKEN
ADMIN_ID=ADMIN_ID
HOST=0.0.0.0
PORT=8000
BASE_URL=https://aiogram3webhook-yakvenalex.amvera.io (своя ссылка с Amvera)

Порт совпадает с портом из настроек, хост не меняем, в BASE_URL подставляем полученную ссылку из проекта в Amvera.

Теперь отправим файлы в Amvera

git add .
git commit -m "init commit"
git push amvera master

Теперь, если мы перейдем во вкладку «Репозиторий» и обновим страницу, то увидим, что наши файлы появились.

Теперь нам остается только подождать 2-3 минуты и наш проект сам соберется и поднимется, а бот известит нас сообщением о том, что он запущен.

В проекте так-же есть лог сборки и лог приложения. Заглянем в «Лог приложения»

Видим, что там у нас все стабильно.

Заключение

Друзья, надеюсь, что после прочтения данной статьи каждый из вас понял, что писать ботов на вебхуках ничем не сложнее, чем делать то же самое, но на поллинге. При этом, овладев данной технологией, вы автоматически становитесь более квалифицированным разработчиком, ведь, большинство так и продолжит думать, что вебхуки это сложно.

Как обычно, полный исходник кода проекта, как и эксклюзивный контент, который я не публикую на Хабре — вы найдете в моем телеграмм канале «Легкий путь в Python»!

Если эта статья оказалась вам полезной, пожалуйста, сообщите об этом лайком, подпиской и приятным комментарием. Для меня это важно, так как на написание подобных статей тратится много времени, а отклик, зачастую, минимален.

Благодарю за внимание и у меня пока все.

Комментарии (4)


  1. kravlad
    16.08.2024 04:05

    К недостаткам вэбхуков я бы добавил сложность работы с медиагруппами. Не просто понять какое сообщение последнее


    1. yakvenalex Автор
      16.08.2024 04:05

      Медиагруппы это большой недостаток API телеграмм. Оно рабоатете одинаково не очень и на поллинге и на хуках)


      1. kravlad
        16.08.2024 04:05
        +1

        Ну на поллинге оно хотябы всю пачку сразу шлет, а на хуках по одному и сиди гадай последний это был или нет)


        1. yakvenalex Автор
          16.08.2024 04:05

          Есть такое дело))