Создание Webhook для Telegram-ботов на Aiogram3
1

Создание Webhook для Telegram-ботов на Aiogram3

Автор: admin

Вебхуки (webhooks) являются предпочтительным способом получения обновлений от Telegram для продакшн-ботов. В отличие от polling (опроса), вебхуки позволяют Telegram отправлять обновления на ваш сервер в реальном времени, что более эффективно и масштабируемо.

Рекомендуем услуги
Популярные решения, которые могут вам подойти:

В этой статье мы подробно рассмотрим процесс создания вебхука для бота на Aiogram 3.x, начиная с настройки окружения и заканчивая деплоем.

Создание Webhook для Aiogram: Полное руководство

1. Настройка окружения

1.1 Создание виртуального окружения

Создание виртуального окружения

Цель: Изолировать окружение проекта от глобального Python.

2 минут Начинающий

Что потребуется

Установленный Python 3.8+

1. Создание и активация виртуального окружения

Виртуальное окружение изолирует зависимости проекта и предотвращает конфликты с системными пакетами.

bash
# Создание виртуального окружения
python -m venv venv

# Активация (Windows)
venv\Scripts\activate

# Активация (Linux/Mac)
source venv/bin/activate

Результат

Готовое изолированное окружение: Все пакеты будут установлены локально в папке venv.

1.2 Установка зависимостей

Установка зависимостей

Необходимые пакеты: Aiogram — основная библиотека, pip-tools для управления зависимостями, python-dotenv для переменных окружения, aiohttp для веб-сервера.

3 минут Начинающий

Что потребуется

Активное виртуальное окружение

1. Файл requirements.in

Создайте файл с основными зависимостями:

plaintext
aiogram==3.22.0
pip-tools==7.5.2
python-dotenv==1.2.1
aiohttp==3.11.13

2. Установка зависимостей

Установите pip-tools и скомпилируйте зависимости:

bash
# Установка pip-tools
pip install pip-tools==7.5.2

# Компиляция зависимостей
pip-compile requirements.in

# Установка всех зависимостей
pip install -r requirements.txt

Результат

Все зависимости установлены: Файлы requirements.in и requirements.txt созданы, пакеты готовы к использованию.

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

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

Цель: Организовать код в логические модули для удобства разработки и поддержки.

3 минут Начинающий

Что потребуется

Создана корневая папка проекта

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

Создайте следующую структуру папок и файлов:

Внимание!

Эта структура обеспечивает чистоту кода и упрощает масштабирование проекта.

plaintext
project/
├── .env
├── .gitignore
├── requirements.in
├── requirements.txt
├── config.py
├── commands.py
├── dp.py
├── middleware.py
├── run_webhook.py
├── run_polling.py
├── handlers/
│   ├── __init__.py
│   ├── h_common.py
│   └── h_weather.py
└── middleware/
    ├── __init__.py
    └── test_middlware.py

Результат

Проект структурирован: Код разделён на модули, готов к наполнению функциональностью.

2. Настройка конфигурации

2.1 Файл .env

Переменные окружения

Цель: Хранить конфиденциальные данные вне кода для безопасности и удобства деплоя.

2 минут Начинающий

Что потребуется

Токен бота от @BotFather

1. .env файл

Внимание!

Никогда не коммитьте .env файл в репозиторий! Добавьте его в .gitignore.

Создайте файл .env в корне проекта:

Внимание!

Важно: Для локальной разработки используйте ngrok или localtunnel для создания публичного URL. В продакшене используйте реальный домен с HTTPS.

plaintext
BOT_TOKEN=ваш_токен_бота
WH_BASE_URL=https://ваш-домен.ngrok.io
WH_PATH=/webhook

Результат

Конфигурация готова: Переменные окружения настроены, токен и URL вебхука защищены.

2.2 Файл config.py

Загрузка переменных окружения

Цель: Централизованная загрузка конфигурации с проверкой обязательных переменных.

2 минут Начинающий

Что потребуется

Созданный .env файл

1. Конфигурация проекта

Создайте файл config.py для загрузки переменных окружения:

python
import os
from pathlib import Path
from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent

env_file = BASE_DIR / '.env'
load_dotenv(dotenv_path=env_file)

TOKEN = os.getenv('BOT_TOKEN')
WH_BASE_URL = os.getenv('WH_BASE_URL')
WH_MAIN_BOT_PATH = os.getenv('WH_PATH')

# Проверка наличия токена
if not TOKEN:
    raise ValueError("BOT_TOKEN не найден в .env файле")

Результат

Конфиг загружен: Все переменные доступны через импорт config, ошибка при отсутствии токена.

3. Создание обработчиков

3.1 Команды бота (commands.py)

Команды бота

Цель: Определить доступные команды для отображения в меню бота.

2 минут Начинающий

Что потребуется

Бот зарегистрирован в Telegram

1. Список команд

Создайте файл commands.py:

python
from aiogram.types import BotCommand

commands = [
    BotCommand(
        command="/start",
        description="📋 Приветствие",
        full_description="Начать работу с ботом"
    ),
    BotCommand(
        command="/weather",
        description="📋 Погода",
        full_description="Показать погоду в Минске"
    ),
]

Результат

Команды определены: Пользователи увидят список доступных команд в меню.

3.2 Обработчики (handlers/h_common.py)

Создание обработчика

Цель: Обрабатывать команду /start и отвечать пользователю.

3 минут Начинающий

Что потребуется

Созданная папка handlers

1. Базовый обработчик

Создайте файл handlers/h_common.py:

python
from aiogram import Router
from aiogram.filters import Command
from aiogram.types import Message

router = Router()

@router.message(Command("start"))
async def cmd_start(message: Message):
    await message.answer(
        "👋 Привет! Я бот, который работает через вебхук.\n"
        "Доступные команды:\n"
        "/start - Приветствие\n"
        "/weather - Узнать погоду"
    )

Результат

Обработчик готов: Бот отвечает на команду /start приветственным сообщением.

3.3 Обработчик погоды (handlers/h_weather.py)

Создание обработчика погоды

Цель: Обрабатывать команду /weather и показывать погоду.

3 минут Начинающий

Что потребуется

API для получения погоды (опционально)

1. Обработчик погоды

Создайте файл handlers/h_weather.py:

python
import aiohttp
from aiogram import Router, F
from aiogram.filters import Command
from aiogram.types import Message
from aiogram.fsm.context import FSMContext

router = Router()


@router.message(Command(commands=["weather"]))
async def cmd_weather(message: Message, state: FSMContext):
    await state.clear()  # Очищаем состояние

    try:
        # Получаем погоду для Минска
        weather_data = await get_weather("Minsk")

        if weather_data:
            response = (
                f"🌤 Погода в Минске:\n"
                f"📍 Температура: {weather_data['temperature']}°C\n"
                f"💧 Влажность: {weather_data['humidity']}%\n"
                f"🌬 Давление: {weather_data['pressure']} hPa\n"
                f"🌀 Ветер: {weather_data['wind_speed']} м/с\n"
                f"📝 Описание: {weather_data['description']}"
            )
        else:
            response = "❌ Не удалось получить данные о погоде"

    except Exception as e:
        response = f"⚠️ Произошла ошибка: {str(e)}"

    await message.answer(response)


async def get_weather(city: str) -> dict:
    url = f"https://wttr.in/{city}?format=j1"

    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            if response.status == 200:
                data = await response.json()
                current = data['current_condition'][0]
                return {
                    'temperature': current['temp_C'],
                    'humidity': current['humidity'],
                    'pressure': current['pressure'],
                    'wind_speed': current['windspeedKmph'],
                    'description': current['weatherDesc'][0]['value']
                }
            return None

Результат

Обработчик готов: Бот отвечает на команду /weather.

4. Middleware

4.1 Создание middleware (middleware/test_middlware.py)

Создание middleware для логирования

Цель: Перехватывать и логировать все входящие обновления перед обработкой.

3 минут Средний

Что потребуется

Понимание принципов middleware в Aiogram

1. Создание middleware

Создайте файл middleware/test_middlware.py:

python
from typing import Callable, Dict, Any, Awaitable
from aiogram import BaseMiddleware
from aiogram.types import TelegramObject


class TestMiddleware(BaseMiddleware):
    async def __call__(
        self,
        handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: Dict[str, Any]
    ) -> Any:
        # Логирование входящих обновлений
        print(f"Получено обновление: {event}")
        
        # Вызов следующего обработчика
        result = await handler(event, data)
        
        return result

Результат

Middleware готов: Все обновления логируются перед отправкой в обработчики.

5. Основной файл вебхука

5.1 run_webhook.py

Настройка вебхук сервера

Цель: Создать HTTP-сервер, который будет принимать обновления от Telegram.

10 минут Средний

Что потребуется

Настроенный .env файл Созданные обработчики

1. Основной файл вебхука

Внимание!

Проверьте валидность токена перед запуском — это предотвратит ошибки.

Создайте файл run_webhook.py:

Внимание!

В коде уже настроено логирование. Логи можно выводить в файл, раскомментировав соответствующие строки.

python
import logging
from typing import Any, Dict, Union

from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.fsm.strategy import FSMStrategy
from aiogram.utils.token import TokenValidationError, validate_token
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from aiogram.client.bot import DefaultBotProperties
from aiogram.enums import ParseMode
from aiohttp import web

from config import TOKEN, WH_BASE_URL, WH_MAIN_BOT_PATH
from commands import commands
from dp import dp_connector


logging.basicConfig(
    level=logging.INFO,
    format="[%(asctime)s] [%(levelname)s] [%(lineno)d] [%(name)s] [%(message)s]",
)

PORT = 8888

def is_bot_token(value: str) -> Union[bool, Dict[str, Any]]:
    """Валидация токена бота"""
    try:
        validate_token(value)
    except TokenValidationError:
        return False
    return True

async def on_startup(bot: Bot):
    """Настройка вебхука при старте"""
    webhook_url = f"{WH_BASE_URL}{WH_MAIN_BOT_PATH}"
    await bot.set_webhook(webhook_url)
    await bot.set_my_commands(commands=commands)
    logging.info(f"Вебхук установлен: {webhook_url}")

def webhook():
    """Запуск вебхук сервера"""
    # Создание приложения aiohttp
    app = web.Application()
    
    # Настройка сессии
    session = AiohttpSession()
    
    # Настройка бота
    bot_settings = {
        "token": TOKEN,
        "session": session,
        "default": DefaultBotProperties(
            parse_mode=ParseMode.HTML,
        )
    }
    bot = Bot(**bot_settings)
    
    # Настройка диспетчера с FSM
    dp = Dispatcher(
        storage=MemoryStorage(),
        fsm_strategy=FSMStrategy.USER_IN_CHAT
    )
    
    # Регистрация обработчиков старта
    dp.startup.register(on_startup)
    
    # Подключение всех обработчиков
    dp_connector(dp)
    
    # Регистрация вебхук обработчика
    SimpleRequestHandler(
        dispatcher=dp,
        bot=bot
    ).register(app, path=WH_MAIN_BOT_PATH)
    
    # Настройка приложения
    setup_application(app, dp, bot=bot)
    
    # Запуск сервера
    web.run_app(app, host='0.0.0.0', port=PORT)

if __name__ == "__main__":
    # Проверка токена
    if not is_bot_token(TOKEN):
        logging.error("Неверный токен бота")
        exit(1)
    
    logging.info(f"Запуск вебхук сервера на порту {PORT}")
    webhook()

Результат

Вебхук сервер запущен: Бот принимает обновления через вебхук на порту 8888.

5.2 Коннектор диспетчера (dp.py)

Коннектор диспетчера

Цель: Централизованное подключение всех роутеров и middleware к диспетчеру.

3 минут Средний

Что потребуется

Созданные роутеры и middleware

1. Подключение компонентов

Создайте файл dp.py:

python
from aiogram.dispatcher.dispatcher import Dispatcher

# Импортируем middleware и handlers
from middleware import test_middlware
from handlers import h_common, h_weather


def dp_connector(dp: Dispatcher) -> Dispatcher:
    """Подключение всех компонентов к диспетчеру"""
    
    # Подключение middleware
    dp.update.middleware(test_middlware.TestMiddleware())
    
    # Подключение роутеров
    dp.include_router(h_common.router)
    dp.include_router(h_weather.router)
    
    return dp

Результат

Все компоненты подключены: Диспетчер готов к обработке обновлений.

6. Альтернатива: Polling режим

6.1 run_polling.py

Настройка polling режима

Цель: Запустить бота в режиме опроса для локальной разработки.

3 минут Начинающий

Что потребуется

Настроенные обработчики

1. Polling режим

Внимание!

Перед запуском polling обязательно очистите вебхук, чтобы избежать конфликтов.

Создайте файл run_polling.py:

Внимание!

Используйте polling для разработки и тестирования, вебхуки для продакшена.

python
import asyncio
import logging
from aiogram import Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.fsm.strategy import FSMStrategy
from aiogram.types import BotCommandScopeDefault

from config import bot
from commands import commands
from dp import dp_connector


async def main():
    logging.basicConfig(
        level=logging.INFO,
        format="[%(asctime)s] [%(levelname)s] [%(lineno)d] [%(name)s] [%(message)s]",
    )
    
    dp = Dispatcher(
        storage=MemoryStorage(),
        fsm_strategy=FSMStrategy.USER_IN_CHAT
    )
    
    # Установка команд
    await bot.set_my_commands(
        commands=commands,
        scope=BotCommandScopeDefault()
    )
    
    # Подключение всех компонентов
    dp_connector(dp)
    
    # Очистка вебхука перед запуском polling
    await bot.delete_webhook(drop_pending_updates=True)
    
    # Запуск polling
    await dp.start_polling(
        bot,
        allowed_updates=dp.resolve_used_update_types()
    )

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        print("Бот остановлен")

Результат

Polling запущен: Бот работает в режиме опроса, вебхук очищен.

7. Локальная разработка с туннелированием

7.1 Использование localtunnel

Настройка localtunnel

Цель: Создать публичный HTTPS URL для локального тестирования вебхуков.

3 минут Средний

Что потребуется

Установленный Node.js и npm

1. Запуск localtunnel

Localtunnel создаёт публичный URL для вашего локального сервера:

Внимание!

Полученный URL копируем в .env как WH_BASE_URL, WH_PATH оставляем пустым.

bash
# Установка localtunnel глобально
npm install -g localtunnel

# Создание туннеля
lt --port 8888

# Пример полученного URL:
# https://wild-pumas-turn.loca.lt

Результат

Туннель создан: Ваш локальный сервер доступен из интернета по HTTPS.

7.2 Использование ngrok

Настройка ngrok

Цель: Альтернативный способ создания публичного URL для вебхуков.

3 минут Средний

Что потребуется

Скачанный и настроенный ngrok

1. Запуск ngrok

Скачайте ngrok с официального сайта и запустите:

Внимание!

ngrok — более стабильный инструмент для туннелирования с расширенными возможностями.

bash
# Запуск туннеля
ngrok http 8888

# Пример полученного URL:
# https://abc123.ngrok.io

Результат

Туннель создан: Локальный сервер доступен через ngrok.

8. Настройка для продакшена

8.1 Требования для продакшена

Продакшен требования

Цель: Обеспечить стабильную работу вебхука в боевом окружении.

15 минут Продвинутый

Что потребуется

Домен и хостинг

1. Продакшен требования

Для работы вебхука в продакшене необходимо:

plaintext
1. HTTPS: Вебхук должен работать по HTTPS
2. Доменное имя: Собственный домен
3. Стабильный хостинг: Сервер должен быть всегда доступен
4. Nginx + SSL: Настройка прокси и SSL сертификатов

Результат

Продакшен готов: Вебхук работает по HTTPS на стабильном сервере.

8.2 Пример конфигурации Nginx

Настройка Nginx

Цель: Настроить проксирование запросов к вашему приложению.

10 минут Продвинутый

Что потребуется

Установленный Nginx

1. Конфигурация Nginx

Настройте Nginx как прокси для вашего приложения:

Внимание!

Не забудьте настроить SSL сертификаты (Let's Encrypt) для HTTPS.

nginx
server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location /webhook {
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Результат

Nginx настроен: Запросы проксируются на ваше приложение.

9. Отладка и мониторинг

9.1 Логирование

Настройка логирования

Цель: Отслеживать работу бота и находить ошибки.

2 минут Начинающий

Что потребуется

Созданный run_webhook.py

1. Настройка логирования

Внимание!

В продакшене используйте логирование в файл для анализа ошибок.

В коде уже настроено логирование. Для записи в файл раскомментируйте строки:

python
logging.basicConfig(
    level=logging.INFO,
    format="[%(asctime)s] [%(levelname)s] [%(lineno)d] [%(name)s] [%(message)s]",
    filename='webhook.log',  # Раскомментировать для записи в файл
    filemode='a'             # Режим добавления
)

Результат

Логирование настроено: Все события записываются в консоль или файл.

9.2 Проверка статуса вебхука

Мониторинг вебхука

Цель: Проверять состояние вебхука через команду бота.

3 минут Средний

Что потребуется

Бот запущен

1. Команда для проверки статуса

Добавьте команду для мониторинга состояния вебхука:

python
@router.message(Command("webhook_status"))
async def cmd_webhook_status(message: Message, bot: Bot):
    webhook_info = await bot.get_webhook_info()
    await message.answer(
        f"📊 Статус вебхука:\n"
        f"URL: {webhook_info.url}\n"
        f"Активен: {webhook_info.pending_update_count}\n"
        f"Последняя ошибка: {webhook_info.last_error_message or 'Нет'}"
    )

Результат

Статус доступен: Вы можете проверить состояние вебхука в любой момент.

10. Заключение

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

  • Скорость: Мгновенная доставка обновлений
  • Экономия ресурсов: Не нужно постоянно опрашивать API
  • Масштабируемость: Лучше подходит для больших проектов

Недостатки:

  • Сложность настройки: Требуется публичный HTTPS сервер
  • Ограничение: Один вебхук на бота
  • Безопасность: Нужно обеспечить защиту эндпоинта

Рекомендации:

  • Используйте polling для разработки и тестирования
  • Переходите на вебхуки для продакшена
  • Всегда проверяйте валидность токена
  • Настраивайте логирование для мониторинга
  • Используйте переменные окружения для конфигурации

Полезные ссылки