Архитектура и HTTP‑сервер
Этот раздел описывает, как устроен веб‑слой Telegram‑бота: какие функции отвечают за запуск HTTP‑сервера, приём webhook‑запросов и передачу их в бизнес‑логику. Ниже приведены примеры кода из реальных модулей, чтобы было легче сопоставлять описание с проектом.
Точка входа и создание приложения
Главная точка входа находится в файле telegram-bot/main.py. Здесь определяется синхронная функция create_app(), которая создаёт aiohttp‑приложение, добавляет маршруты для проверки статуса и подключает интеграцию с aiogram:
def create_app() -> web.Application:
# Увеличиваем лимит тела запроса для загрузки DOCX (по умолчанию aiohttp 1 МБ)
app = web.Application(client_max_size=50 * 1024 ** 2) # 50 МБ
async def health_check(request):
return web.json_response({"status": "ok", "service": "telegram-bot"})
app.router.add_get("/health", health_check)
app.router.add_get("/", health_check)
app.router.add_post("/cargo-assigned", handle_cargo_assignment)
app.router.add_post("/cargo-docx", handle_cargo_docx)
@web.middleware
async def logging_middleware(request, handler):
...
return await handler(request)
app.middlewares.append(logging_middleware)
SimpleRequestHandler(
dispatcher=dp,
bot=bot,
secret_token=None,
).register(app, path=config.WEBHOOK_SECRET_PATH)
setup_application(app, dp, bot=bot)
return app
Параметры функции create_app
| Name | Type | Description | Default |
|---|---|---|---|
| — | — | Функция не принимает аргументов и возвращает web.Application. |
— |
Возвращаемое значение
| Name | Type | Description |
|---|---|---|
app |
web.Application |
Готовое aiohttp‑приложение Telegram‑бота. |
В этом фрагменте видно, что:
web.Application(...)создаётся с увеличеннымclient_max_size, чтобы принимать DOCX черезPOST /cargo-docx;- маршруты
GET /иGET /healthобрабатываются общей функциейhealth_check; - есть дополнительные интеграционные эндпоинты:
POST /cargo-assigned— уведомления о взятой заявке (вызовы со стороны MCP‑сервера);POST /cargo-docx— приём и пересылка DOCX пользователю (вызовы со стороны MCP‑сервера);- middleware
logging_middlewareлогирует входящие запросы (в том числе выводит тело webhook‑запроса для путиconfig.WEBHOOK_SECRET_PATH).
Контракты (структура запросов/ответов) для POST /cargo-assigned и POST /cargo-docx описаны на странице HTTP API (служебные эндпоинты).
Через SimpleRequestHandler по пути config.WEBHOOK_SECRET_PATH подключается обработчик webhook‑запросов от Telegram к диспетчеру dp.
Инициализация и запуск сервера оформлены в функции main():
async def main() -> None:
await on_startup()
app = create_app()
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, config.APP_HOST, config.APP_PORT)
await site.start()
await asyncio.Future()
Параметры функции main
| Name | Type | Description | Default |
|---|---|---|---|
| — | — | Точка входа; не принимает аргументов и ничего не возвращает. | — |
Возвращаемое значение
| Name | Type | Description |
|---|---|---|
| — | — | Функция работает до остановки процесса и явного значения не возвращает. |
Здесь вызывается on_startup(), затем создаётся приложение, запускается HTTP‑сервер на config.APP_HOST:config.APP_PORT, и процесс переходит в ожидание, пока приложение не будет остановлено.
Жизненный цикл: запуск и остановка
Функция on_startup() выполняет начальную инициализацию: подключает роутеры обработчиков, заранее создаёт экземпляр OpenAIService через get_openai_service(), инициализирует Telethon и настраивает webhook в Telegram с помощью WebhookService:
async def on_startup() -> None:
dp.include_router(messages.router)
get_openai_service()
await init_telethon()
webhook_service = WebhookService(bot)
await webhook_service.setup()
Остановка бота обрабатывается отдельной функцией on_shutdown(), которая закрывает HTTP‑сессию бота и останавливает Telethon:
async def on_shutdown() -> None:
await bot.session.close()
await shutdown_telethon()
Параметры функций запуска/остановки
| Name | Type | Description | Default |
|---|---|---|---|
on_startup |
— | Асинхронная функция без аргументов; выполняет первоначальную инициализацию. | — |
on_shutdown |
— | Асинхронная функция без аргументов; аккуратно завершает работу HTTP‑клиента бота. | — |
Возвращаемое значение
| Name | Type | Description |
|---|---|---|
| — | — | Обе функции завершаются без явного возвращаемого значения. |
После выполнения on_shutdown() можно безопасно останавливать aiohttp‑сервер.
Создание Bot и Dispatcher
Создание экземпляров Bot и Dispatcher вынесено в модуль telegram-bot/bot/loader.py. Это позволяет использовать одни и те же объекты во всех частях приложения:
from aiogram import Bot, Dispatcher
from config.settings import config
bot = Bot(token=config.BOT_TOKEN, parse_mode="HTML")
dp = Dispatcher()
Модуль loader.py импортируется в main.py и других файлах, которые взаимодействуют с Telegram API и инфраструктурой aiogram.
Обработчик бизнес‑сообщений
За обработку бизнес‑сообщений отвечает модуль telegram-bot/handlers/messages.py. Он фильтрует входящие события бизнес‑чата (игнорирует исходящие сообщения, в том числе отправленные через Telethon) и разделяет сценарии «текст» и «документ»:
router = Router()
@router.business_message()
async def handle_business_message(message: Message):
if not _is_incoming_business_message(message):
return
if message.document:
await _handle_business_document(message)
return
await _handle_business_text(message)
Текстовый сценарий делегируется в utils/ai_processor.process_ai_request: обработчик передаёт примитивные значения (tg_id, request_text, message_id, business_connection_id) и не завязывает процессор на тип Message.
WebhookService и настройка webhook в Telegram
Сервис WebhookService описан в файле telegram-bot/services/webhook.py. Его задача — убедиться, что Telegram настроен на отправку webhook‑запросов в правильное место:
BUSINESS_ALLOWED_UPDATES = [
"message",
"business_message",
"edited_business_message",
"deleted_business_messages",
"business_connection",
]
class WebhookService:
def __init__(self, bot: Bot) -> None:
self._bot = bot
self._webhook_url = config.WEBHOOK_URL
async def setup(self) -> None:
info = await self._bot.get_webhook_info()
if info.url == self._webhook_url:
return
await self._bot.set_webhook(
url=self._webhook_url,
drop_pending_updates=False,
allowed_updates=BUSINESS_ALLOWED_UPDATES,
)
В конструкторе класс получает экземпляр Bot и вычисляет webhook_url из config.WEBHOOK_URL. Метод setup() сначала запрашивает текущие настройки webhook через get_webhook_info(). Если URL совпадает с ожидаемым, метод просто завершает работу. Если URL отличается или не задан, вызывается set_webhook() с нужным адресом и списком разрешённых типов обновлений. Дополнительно в классе может быть метод get_info(), возвращающий краткое состояние webhook для диагностик.
Параметры класса WebhookService
| Name | Type | Description | Default |
|---|---|---|---|
bot |
Bot |
Экземпляр Telegram‑бота, через который выполняются запросы get_webhook_info и set_webhook. |
— |
setup |
метод | Асинхронный метод без аргументов; проверяет текущий webhook и при необходимости перенастраивает его. | — |
Поток обработки запроса целиком
Если посмотреть на поток обработки запроса целиком, он выглядит так. Telegram отправляет бизнес‑сообщение на настроенный webhook URL. aiohttp‑приложение в main.py принимает HTTP‑запрос и передаёт его в aiogram‑диспетчер через SimpleRequestHandler. Модуль handlers/messages.py получает объект Message и вызывает process_ai_request из utils/ai_processor.py.
Внутри process_ai_request определяется user_id через MCP‑инструмент verify_user, при необходимости сохраняется телефон, обрабатываются служебные запросы (сброс контекста, смена роли), а затем текст передаётся в OpenAIService.process_message. Ответ модели возвращается в виде строки, отправляется пользователю и записывается в таблицу dialogue_history. Детали того, как устроено определение пользователя, работа с БД и интеграция с OpenAI, описаны в отдельных разделах про конфигурацию и MCP.