Files
weather_bot/weather.py
2026-04-02 09:53:14 +03:00

135 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import aiohttp
from datetime import datetime
import asyncio
import logging
async def get_coords_by_city(city_name: str):
"""Ищет координаты по названию города."""
url = f"https://geocoding-api.open-meteo.com/v1/search?name={city_name}&count=1&language=ru&format=json"
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, timeout=5) as response:
if response.status != 200:
return None, None, None, None
data = await response.json()
results = data.get("results")
if not results:
return None, None, None, None
city_data = results[0]
return (
city_data.get("latitude"),
city_data.get("longitude"),
city_data.get("name", city_name),
city_data.get("timezone")
)
except Exception:
return None, None, None, None
async def get_timezone_by_coords(lat: float, lon: float) -> str | None:
"""Определяет часовой пояс по координатам, делая запрос к API погоды."""
# Делаем минимальный запрос к API, чтобы получить часовой пояс
data = await fetch_weather_data(lat, lon, 1)
if data and "timezone" in data:
return data["timezone"]
return None
async def fetch_weather_data(lat: float, lon: float, days: int):
"""Базовая функция для запроса данных о погоде на N дней."""
url = (
f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}"
f"&current=temperature_2m"
f"&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max"
f"&timezone=auto&forecast_days={days}"
)
# Логика с повторными попытками для надежности
for attempt in range(3):
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, timeout=10) as response:
if response.status == 200:
return await response.json()
logging.warning(f"Попытка {attempt+1}: API погоды вернуло статус {response.status}")
except Exception as e:
logging.warning(f"Попытка {attempt+1}: Ошибка при запросе к API погоды: {e}")
if attempt < 2: # Не спим после последней попытки
await asyncio.sleep(attempt + 2) # Спим 2, 3 секунды
return None
async def get_daily_weather_summary(lat: float, lon: float) -> str:
"""Прогноз на сегодня."""
data = await fetch_weather_data(lat, lon, 1)
if not data or "daily" not in data or "current" not in data:
return "Не удалось получить данные о погоде от сервера."
current_temp = data["current"].get("temperature_2m", "N/A")
temp_max = data["daily"]["temperature_2m_max"][0]
temp_min = data["daily"]["temperature_2m_min"][0]
precip_prob = data["daily"]["precipitation_probability_max"][0]
temp_diff = round(temp_max - temp_min, 1)
return (
f"🌤 **Погода на сегодня:**\n\n"
f"🌡 **Сейчас:** {current_temp}°C\n"
f"📊 **Дневная норма:** от {temp_min}°C до {temp_max}°C\n"
f"📉 **Перепад температур за день:** {temp_diff}°C\n"
f"🌧 **Макс. вероятность осадков:** {precip_prob}%\n\n"
f"Одевайтесь по погоде и хорошего дня!"
)
async def get_tomorrow_weather_summary(lat: float, lon: float) -> str:
"""Прогноз на завтра (берем индекс [1] из массивов)."""
data = await fetch_weather_data(lat, lon, 2)
if not data or "daily" not in data:
return "Не удалось получить данные о погоде от сервера."
temp_max = data["daily"]["temperature_2m_max"][1]
temp_min = data["daily"]["temperature_2m_min"][1]
precip_prob = data["daily"]["precipitation_probability_max"][1]
temp_diff = round(temp_max - temp_min, 1)
return (
f"📅 **Погода на завтра:**\n\n"
f"📊 **Дневная норма:** от {temp_min}°C до {temp_max}°C\n"
f"📉 **Перепад температур:** {temp_diff}°C\n"
f"🌧 **Макс. вероятность осадков:** {precip_prob}%\n\n"
f"Запланируйте свой день с учетом прогноза!"
)
async def get_weekly_weather_summary(lat: float, lon: float) -> str:
"""Прогноз на 7 дней."""
data = await fetch_weather_data(lat, lon, 7)
if not data or "daily" not in data:
return "Не удалось получить данные о погоде от сервера."
dates = data["daily"]["time"]
max_temps = data["daily"]["temperature_2m_max"]
min_temps = data["daily"]["temperature_2m_min"]
precips = data["daily"]["precipitation_probability_max"]
week_days = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]
summary = "📆 **Прогноз на неделю:**\n\n"
for i in range(7):
# Преобразуем дату "YYYY-MM-DD" в красивый формат "DD.MM (День недели)"
date_obj = datetime.strptime(dates[i], "%Y-%m-%d")
day_name = week_days[date_obj.weekday()]
formatted_date = date_obj.strftime("%d.%m")
t_min = min_temps[i]
t_max = max_temps[i]
prob = precips[i]
# Эмодзи для осадков, чтобы легче читалось
precip_emoji = "☔️" if prob > 40 else ("☁️" if prob > 10 else "☀️")
summary += f"🔹 **{formatted_date} ({day_name}):** от {t_min}°C до {t_max}°C | {precip_emoji} {prob}%\n"
return summary