Ласкаво просимо на лекцію, де ми створимо реального Telegram-бота, який зможе взаємодіяти з зовнішніми API. На прикладі бота прогнозу погоди ми опануємо роботу з асинхронними HTTP-запитами і навчимося використовувати дані, отримані з зовнішнього джерела.
Отже, навіщо нам потрібен зовнішній API? Telegram-боти обмежені логікою, яку ви пишете, але їх сила в тому, що вони можуть бути "розумними" інтерфейсами для зовнішніх сервісів. Наприклад:
- Прогноз погоди: тягнути дані про погоду з OpenWeatherMap.
- Курси валют: перевіряти обмінні курси через фінансові API.
- Переклади: виконувати машинний переклад тексту через API Google Translate.
- Новини: доставляти актуальні заголовки з RSS-стрічок.
Усе це вимагає навичок роботи з зовнішніми API – і саме на них ми зосередимося сьогодні.
Типова архітектура
Бот взаємодіє з зовнішнім API за наступною схемою:
- Користувач надсилає повідомлення в Telegram.
- Бот приймає це повідомлення і викликає API FastAPI за допомогою вебхуків.
- FastAPI обробляє запит і робить асинхронний HTTP-запит до зовнішнього API.
- Відповідь від зовнішнього API парситься і повертається користувачу через Telegram.
Виглядає ця схема так:
[Telegram] <=> [Бот через FastAPI] <=> [Зовнішній API (наприклад, OpenWeatherMap)]
Крок 1: Підключення зовнішнього API
Для нашого прикладу будемо використовувати OpenWeatherMap API. Цей сервіс дозволяє отримувати дані про погоду за назвою міста. Для початку вам потрібно зареєструватися на OpenWeatherMap і отримати API-ключ.
У нашому проєкті вже використовуються бібліотеки python-telegram-bot і FastAPI. Нам також знадобиться інструмент для відправки HTTP-запитів. Встановимо httpx:
pip install httpx
Крок 2: Оновлюємо код бота
Тепер ми можемо реалізувати функціонал запиту прогнозу погоди. Ось як це робиться.
- Підключення зовнішнього 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, одиниці виміру температури (метричні) і мову опису.
- Обробка команди
/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: Покращення взаємодії
Поки все працює, але давайте додамо кілька покращень.
- Обробка помилок 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 "Місто вказано невірно. Перевірте, чи існує воно."
Тепер бот коректно повідомляє, якщо сталася помилка.
- Підказки команд
Щоб користувачеві було простіше орієнтуватися, можна розширити команду/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, такі як курси валют або перекладачі, щоб розширити функціонал бота.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ