JavaRush /Курсы /Модуль 4: FastAPI /Асинхронная обработка сообщений от пользователя

Асинхронная обработка сообщений от пользователя

Модуль 4: FastAPI
18 уровень , 5 лекция
Открыта

Если вы уже работали с Python, то наверняка слышали про ключевые слова async и await.

Асинхронное программирование позволяет нам выполнять задачи "одновременно", даже если они требуют некоторого времени для получения данных (например, запросы к внешним API или взаимодействие с базами данных).

Для управления такими задачами Python использует event loop (цикл событий), который переключается между задачами, когда одна из них "ожидает".

Вот простой пример асинхронного кода:


import asyncio

async def say_hello():
    await asyncio.sleep(2)
    print("Привет, мир!")

async def main():
    await say_hello()

# Запуск event loop
asyncio.run(main())

Здесь функция say_hello "спит" 2 секунды, но блокировка основного потока выполнения здесь отсутствует — Python может заняться другими задачами, пока мы, например, ждём ответа от сервера.

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


Асинхронность в Telegram-ботах

Теперь давайте применим наши знания асинхронного программирования в разработке Telegram-бота.

Пример 1: Асинхронное ожидание сообщений

Представим, что ваш бот обрабатывает сообщения и параллельно должен отправлять ответы. С библиотекой python-telegram-bot версии 20+ у нас теперь есть полноценная поддержка асинхронного программирования.


from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters

# Асинхронная функция для обработки команды /start
async def start(update: Update, context):
    await update.message.reply_text("Привет! Я ваш асинхронный бот.")

# Асинхронная функция для обработки текстовых сообщений
async def echo(update: Update, context):
    await update.message.reply_text(f"Вы сказали: {update.message.text}")

if __name__ == "__main__":
    # Создаём приложение
    application = Application.builder().token("Ваш_API_токен").build()

    # Регистрируем обработчики
    application.add_handler(CommandHandler("start", start))
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))

    # Запуск приложения
    application.run_polling()

Здесь всё просто: мы используем ключевое слово async для объявлений функций и await для работы с асинхронными действиями. В нашем случае это вызовы метода reply_text.


Асинхронный запрос к внешнему API

Скорее всего, ваш бот рано или поздно захочет пообщаться с другими API. Например, проверить погоду, перевести текст или узнать курс валют. Вот пример кода, где бот запрашивает случайный факт у внешнего REST API.

Пример 2: Асинхронная работа с HTTP-запросами


import aiohttp
from telegram import Update
from telegram.ext import Application, CommandHandler

# Асинхронная функция для общения с внешним API
async def get_random_fact():
    url = "https://uselessfacts.jsph.pl/random.json?language=en"
    async with aiohttp.ClientSession() as session:  # Создаём сессию
        async with session.get(url) as response:  # Отправляем запрос
            data = await response.json()  # Асинхронно читаем JSON
            return data.get("text", "Не удалось получить факт.")

# Асинхронная команда /fact
async def fact(update: Update, context):
    fact_text = await get_random_fact()  # Ждём результат от API
    await update.message.reply_text(f"Случайный факт: {fact_text}")

if __name__ == "__main__":
    application = Application.builder().token("Ваш_API_токен").build()

    application.add_handler(CommandHandler("fact", fact))

    application.run_polling()

В этом примере мы используем библиотеку aiohttp для отправки асинхронного HTTP-запроса. Когда пользователь отправляет /fact, бот запрашивает внешний API, ждёт ответ и возвращает пользователю случайный факт.


Улучшение производительности бота

Представьте, что ваш бот должен ждать какое-то время перед отправкой сообщения (например, для эффекта "набора текста"). Асинхронность позволяет сделать это элегантно:


import asyncio

async def typing_effect(update: Update, context):
    await update.message.reply_text("Подождите, я думаю...")
    await asyncio.sleep(2)  # Задержка
    await update.message.reply_text("Ответ готов!")

Простой, но эффективный способ показать пользователю, что бот "работает".


Частые ошибки и их избегание

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

Другой типичной проблемой является неправильная обработка ошибок в асинхронных функциях. Если внешнее API недоступно или возвращает ошибку, вы можете получить некрасивый трейсбек. Лучше всего всегда использовать блок try-except в асинхронных вызовах:


async def safe_request():
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get("https://example.com/api") as response:
                return await response.json()
    except Exception as e:
        print(f"Ошибка запроса: {e}")

Интеграция с вебхуками

Теперь давайте объединим всё, что мы изучили, с помощью вебхуков, чтобы наш бот мог обрабатывать сообщения в реальном времени. В этом случае FastAPI будет выступать в роли "моста", обрабатывая входящие запросы от Telegram.

Пример 3: Асинхронный бот с вебхуками


from fastapi import FastAPI, Request
from telegram import Update
from telegram.ext import Application, CommandHandler

app = FastAPI()

application = Application.builder().token("Ваш_API_токен").build()

@app.post("/")
async def handle_webhook(request: Request):
    json_data = await request.json()
    update = Update.de_json(json_data, application.bot)
    await application.process_update(update)

async def start(update: Update, context):
    await update.message.reply_text("Привет! Этот бот работает через вебхуки.")

application.add_handler(CommandHandler("start", start))

if __name__ == "__main__":
    import uvicorn
    # Указываем вебхук URL (например, https://yourserver.com/)
    application.bot.set_webhook("https://ваш-домен/")
    uvicorn.run(app, host="0.0.0.0", port=8000)

Теперь Telegram отправляет обновления вашему серверу, который обрабатывает их с помощью FastAPI.


Практическое применение

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

1
Задача
Модуль 4: FastAPI, 18 уровень, 5 лекция
Недоступна
Запрос к внешнему API через Telegram-бота
Запрос к внешнему API через Telegram-бота
1
Задача
Модуль 4: FastAPI, 18 уровень, 5 лекция
Недоступна
Асинхронный бот с FastAPI и вебхуками
Асинхронный бот с FastAPI и вебхуками
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ