JavaRush /Курсы /Модуль 4: FastAPI /Пример бота, взаимодействующего с внешним API (например, ...

Пример бота, взаимодействующего с внешним API (например, погода)

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

Добро пожаловать в лекцию, где мы создадим настоящего Telegram-бота, который сможет взаимодействовать с внешними API. На примере бота прогноза погоды мы освоим работу с асинхронными HTTP-запросами и научимся использовать данные, полученные из внешнего источника.

Итак, зачем нам нужен внешний API? Telegram-боты ограничены логикой, которую вы пишете, но их сила в том, что они могут быть "умными" интерфейсами для внешних сервисов. Например:

  • Прогноз погоды: вытягивать данные о погоде из OpenWeatherMap.
  • Курсы валют: проверять обменные курсы через финансовые API.
  • Переводы: выполнять машинный перевод текста через API Google Translate.
  • Новости: доставлять актуальные заголовки из RSS-лент.

Всё это требует навыков работы с внешними API – и именно на них мы сосредоточимся сегодня.


Типичная архитектура

Бот взаимодействует с внешним API по следующей схеме:

  1. Пользователь отправляет сообщение в Telegram.
  2. Бот принимает это сообщение и вызывает API FastAPI с помощью вебхуков.
  3. FastAPI обрабатывает запрос и делает асинхронный HTTP-запрос к внешнему API.
  4. Ответ от внешнего API парсится и возвращается пользователю через Telegram.

Выглядит эта схема так:


[Telegram] <=> [Бот через FastAPI] <=> [Внешний API (например, OpenWeatherMap)]

Шаг 1: Подключение внешнего API

Для нашего примера будем использовать OpenWeatherMap API. Этот сервис позволяет получать данные о погоде по названию города. Для начала вам нужно зарегистрироваться на OpenWeatherMap и получить API-ключ.

В нашем проекте уже используется библиотеки python-telegram-bot и FastAPI. Нам также потребуется инструмент для отправки HTTP-запросов. Установим httpx:

pip install httpx

Шаг 2: Обновляем код бота

Теперь мы можем реализовать функционал запроса прогноза погоды. Вот как это делается.

  1. Подключение внешнего API в коде
    Создадим функцию для взаимодействия с OpenWeatherMap. Эта функция должна быть асинхронной, так как HTTP-запросы занимают время.

import httpx

OPENWEATHER_API_KEY = "ваш_ключ_от_OpenWeatherMap"
WEATHER_URL = "https://api.openweathermap.org/data/2.5/weather"

async def get_weather(city: str) -> str:
    """Получает данные о погоде из OpenWeatherMap API."""
    params = {
        "q": city,
        "appid": OPENWEATHER_API_KEY,
        "units": "metric",
        "lang": "ru"
    }
    async with httpx.AsyncClient() as client:
        response = await client.get(WEATHER_URL, params=params)
        if response.status_code == 200:
            data = response.json()
            temp = data["main"]["temp"]
            description = data["weather"][0]["description"]
            return f"🌡️ Температура в {city}: {temp}°C, {description}."
        else:
            return "Не удалось получить данные о погоде. Проверьте название города."

Комментарий:

  • httpx.AsyncClient используется для выполнения асинхронных HTTP-запросов.
  • Параметры запроса (params) включают название города, ключ API, единицы измерения температуры (метрические) и язык описания.
  1. Обработка команды /weather
    Теперь давайте добавим в бота обработку команды /weather. Пользователь отправляет, например, /weather Москва, а бот отвечает данными о погоде.

from telegram import Update
from telegram.ext import CommandHandler, ContextTypes

async def weather_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Обрабатывает команду /weather и возвращает прогноз погоды."""
    if not context.args:
        await update.message.reply_text("Пожалуйста, напишите название города после команды, например: /weather Москва")
        return

    city = " ".join(context.args)
    weather_info = await get_weather(city)
    await update.message.reply_text(weather_info)

# Регистрируем обработчик команды
application.add_handler(CommandHandler("weather", weather_command))

Комментарий:

  • Если пользователь не указывает город, бот выдает подсказку.
  • context.args позволяет извлечь параметры, переданные вместе с командой.

Шаг 3: Проверяем бота

Теперь запустим нашего бота и попробуем отправить команду /weather. Например, введем /weather Москва.

Ожидаемый результат:

🌡️ Температура в Москва: 5°C, ясно.

Если вы указали некорректное название города, бот ответит:

Не удалось получить данные о погоде. Проверьте название города.

Шаг 4: Улучшение взаимодействия

Пока всё работает, но давайте добавим пару улучшений.

  1. Обработка ошибок API
    Иногда внешние API возвращают загадочные ошибки, либо сам сервер может быть недоступен. В таких случаях важно не оставлять пользователя в неведении.
    Добавим обработку исключений в нашу функцию get_weather:

async def get_weather(city: str) -> str:
    """Получает данные о погоде из OpenWeatherMap API с обработкой ошибок."""
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(WEATHER_URL, params={
                "q": city,
                "appid": OPENWEATHER_API_KEY,
                "units": "metric",
                "lang": "ru"
            })
            response.raise_for_status()  # Поднимает ошибку для кодов 4xx/5xx
            data = response.json()
            temp = data["main"]["temp"]
            description = data["weather"][0]["description"]
            return f"🌡️ Температура в {city}: {temp}°C, {description}."
    except httpx.RequestError:
        return "Не удалось подключиться к серверу OpenWeatherMap. Попробуйте позже."
    except httpx.HTTPStatusError:
        return "Город указан неверно. Проверьте, существует ли он."

Теперь бот корректно сообщает, если произошла ошибка.

  1. Подсказки команд
    Чтобы пользователю было проще ориентироваться, можно расширить команду /help, добавив описание новой команды:

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Справка по доступным командам."""
    help_text = """
Доступные команды:
/start - Запустить бота.
/help - Справка по командам.
/weather <город> - Узнать погоду в указанном городе.
"""
    await update.message.reply_text(help_text)

application.add_handler(CommandHandler("help", help_command))

Теперь команда /help рассказывает про возможность узнать погоду.


Полный код

Полный рабочий пример выглядит следующим образом:


from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
import httpx

OPENWEATHER_API_KEY = "ваш_api_ключ_от_OpenWeatherMap"
WEATHER_URL = "https://api.openweathermap.org/data/2.5/weather"

async def get_weather(city: str) -> str:
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(WEATHER_URL, params={
                "q": city,
                "appid": OPENWEATHER_API_KEY,
                "units": "metric",
                "lang": "ru"
            })
            response.raise_for_status()
            data = response.json()
            temp = data["main"]["temp"]
            description = data["weather"][0]["description"]
            return f"🌡️ Температура в {city}: {temp}°C, {description}."
    except httpx.RequestError:
        return "Не удалось подключиться к серверу OpenWeatherMap. Попробуйте позже."
    except httpx.HTTPStatusError:
        return "Город указан неверно. Проверьте, существует ли он."

async def weather_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not context.args:
        await update.message.reply_text("Напишите название города после команды, например: /weather Москва")
        return
    city = " ".join(context.args)
    weather_info = await get_weather(city)
    await update.message.reply_text(weather_info)

async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text("Привет! Я бот прогноза погоды. Введите /help, чтобы узнать, что я умею.")

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    help_text = """
Доступные команды:
/start - Запустить бота.
/help - Справка по командам.
/weather <город> - Узнать погоду в указанном городе.
"""
    await update.message.reply_text(help_text)

application = ApplicationBuilder().token("ваш_токен_бота").build()
application.add_handler(CommandHandler("start", start_command))
application.add_handler(CommandHandler("help", help_command))
application.add_handler(CommandHandler("weather", weather_command))

if __name__ == "__main__":
    application.run_polling()

Запустите этот код, и ваш бот будет готов к работе!


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

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