
Вебхуки (webhooks) являются предпочтительным способом получения обновлений от Telegram для продакшн-ботов. В отличие от polling (опроса), вебхуки позволяют Telegram отправлять обновления на ваш сервер в реальном времени, что более эффективно и масштабируемо.
В этой статье мы подробно рассмотрим процесс создания вебхука для бота на Aiogram 3.x, начиная с настройки окружения и заканчивая деплоем.
Создание Webhook для Aiogram: Полное руководство
1. Настройка окружения
1.1 Создание виртуального окружения
Создание виртуального окружения
Цель: Изолировать окружение проекта от глобального Python.
Что потребуется
1. Создание и активация виртуального окружения
Виртуальное окружение изолирует зависимости проекта и предотвращает конфликты с системными пакетами.
# Создание виртуального окружения
python -m venv venv
# Активация (Windows)
venv\Scripts\activate
# Активация (Linux/Mac)
source venv/bin/activate
Результат
Готовое изолированное окружение: Все пакеты будут установлены локально в папке venv.
1.2 Установка зависимостей
Установка зависимостей
Необходимые пакеты: Aiogram — основная библиотека, pip-tools для управления зависимостями, python-dotenv для переменных окружения, aiohttp для веб-сервера.
Что потребуется
1. Файл requirements.in
Создайте файл с основными зависимостями:
aiogram==3.22.0
pip-tools==7.5.2
python-dotenv==1.2.1
aiohttp==3.11.13
2. Установка зависимостей
Установите pip-tools и скомпилируйте зависимости:
# Установка pip-tools
pip install pip-tools==7.5.2
# Компиляция зависимостей
pip-compile requirements.in
# Установка всех зависимостей
pip install -r requirements.txt
Результат
Все зависимости установлены: Файлы requirements.in и requirements.txt созданы, пакеты готовы к использованию.
1.3 Структура проекта
Структура проекта
Цель: Организовать код в логические модули для удобства разработки и поддержки.
Что потребуется
1. Структура проекта
Создайте следующую структуру папок и файлов:
Внимание!
Эта структура обеспечивает чистоту кода и упрощает масштабирование проекта.
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
Переменные окружения
Цель: Хранить конфиденциальные данные вне кода для безопасности и удобства деплоя.
Что потребуется
1. .env файл
Внимание!
Никогда не коммитьте .env файл в репозиторий! Добавьте его в .gitignore.
Создайте файл .env в корне проекта:
Внимание!
Важно: Для локальной разработки используйте ngrok или localtunnel для создания публичного URL. В продакшене используйте реальный домен с HTTPS.
BOT_TOKEN=ваш_токен_бота
WH_BASE_URL=https://ваш-домен.ngrok.io
WH_PATH=/webhook
Результат
Конфигурация готова: Переменные окружения настроены, токен и URL вебхука защищены.
2.2 Файл config.py
Загрузка переменных окружения
Цель: Централизованная загрузка конфигурации с проверкой обязательных переменных.
Что потребуется
1. Конфигурация проекта
Создайте файл config.py для загрузки переменных окружения:
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)
Команды бота
Цель: Определить доступные команды для отображения в меню бота.
Что потребуется
1. Список команд
Создайте файл commands.py:
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 и отвечать пользователю.
Что потребуется
1. Базовый обработчик
Создайте файл handlers/h_common.py:
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 и показывать погоду.
Что потребуется
1. Обработчик погоды
Создайте файл handlers/h_weather.py:
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 для логирования
Цель: Перехватывать и логировать все входящие обновления перед обработкой.
Что потребуется
1. Создание middleware
Создайте файл middleware/test_middlware.py:
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.
Что потребуется
1. Основной файл вебхука
Внимание!
Проверьте валидность токена перед запуском — это предотвратит ошибки.
Создайте файл run_webhook.py:
Внимание!
В коде уже настроено логирование. Логи можно выводить в файл, раскомментировав соответствующие строки.
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 к диспетчеру.
Что потребуется
1. Подключение компонентов
Создайте файл dp.py:
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 режима
Цель: Запустить бота в режиме опроса для локальной разработки.
Что потребуется
1. Polling режим
Внимание!
Перед запуском polling обязательно очистите вебхук, чтобы избежать конфликтов.
Создайте файл run_polling.py:
Внимание!
Используйте polling для разработки и тестирования, вебхуки для продакшена.
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 для локального тестирования вебхуков.
Что потребуется
1. Запуск localtunnel
Localtunnel создаёт публичный URL для вашего локального сервера:
Внимание!
Полученный URL копируем в .env как WH_BASE_URL, WH_PATH оставляем пустым.
# Установка localtunnel глобально
npm install -g localtunnel
# Создание туннеля
lt --port 8888
# Пример полученного URL:
# https://wild-pumas-turn.loca.lt
Результат
Туннель создан: Ваш локальный сервер доступен из интернета по HTTPS.
7.2 Использование ngrok
Настройка ngrok
Цель: Альтернативный способ создания публичного URL для вебхуков.
Что потребуется
1. Запуск ngrok
Скачайте ngrok с официального сайта и запустите:
Внимание!
ngrok — более стабильный инструмент для туннелирования с расширенными возможностями.
# Запуск туннеля
ngrok http 8888
# Пример полученного URL:
# https://abc123.ngrok.io
Результат
Туннель создан: Локальный сервер доступен через ngrok.
8. Настройка для продакшена
8.1 Требования для продакшена
Продакшен требования
Цель: Обеспечить стабильную работу вебхука в боевом окружении.
Что потребуется
1. Продакшен требования
Для работы вебхука в продакшене необходимо:
1. HTTPS: Вебхук должен работать по HTTPS
2. Доменное имя: Собственный домен
3. Стабильный хостинг: Сервер должен быть всегда доступен
4. Nginx + SSL: Настройка прокси и SSL сертификатов
Результат
Продакшен готов: Вебхук работает по HTTPS на стабильном сервере.
8.2 Пример конфигурации Nginx
Настройка Nginx
Цель: Настроить проксирование запросов к вашему приложению.
Что потребуется
1. Конфигурация Nginx
Настройте Nginx как прокси для вашего приложения:
Внимание!
Не забудьте настроить SSL сертификаты (Let's Encrypt) для HTTPS.
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 Логирование
Настройка логирования
Цель: Отслеживать работу бота и находить ошибки.
Что потребуется
1. Настройка логирования
Внимание!
В продакшене используйте логирование в файл для анализа ошибок.
В коде уже настроено логирование. Для записи в файл раскомментируйте строки:
logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s] [%(levelname)s] [%(lineno)d] [%(name)s] [%(message)s]",
filename='webhook.log', # Раскомментировать для записи в файл
filemode='a' # Режим добавления
)
Результат
Логирование настроено: Все события записываются в консоль или файл.
9.2 Проверка статуса вебхука
Мониторинг вебхука
Цель: Проверять состояние вебхука через команду бота.
Что потребуется
1. Команда для проверки статуса
Добавьте команду для мониторинга состояния вебхука:
@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 для разработки и тестирования
- Переходите на вебхуки для продакшена
- Всегда проверяйте валидность токена
- Настраивайте логирование для мониторинга
- Используйте переменные окружения для конфигурации



