Files
umb/bot/utils/weather.py
T
Галингер Р.С. 0f674f8832 refactor: review fixes and code improvements
- Add shared proxy module (bot/utils/proxy.py) to eliminate duplicate SOCKS5 parsing
- Fix AI client: escape HTML before Markdown→HTML conversion, unify timeouts,
  make health check optional and disabled by default, handle 429 retries
- Fix ai.py: correct forwarded message handling (aiogram 3.x forward_origin),
  pass relevant summaries via extra_system_content
- Fix dialogue.py: only respond to Astra's messages, use system context instead
  of prompt injection, answer on the limit message before phase transition
- Fix voice.py: load Whisper model in thread pool, safe WAV path generation
- Improve database.py: composite indexes, Boolean is_active, upsert file_id cache,
  add context cleanup helper
- Update weather.py and yadisk_download.py to use shared proxy connector
- Update yadisk.py: validate URL before cache clear, add download size limit,
  wrap sync file ops in to_thread
- Reuse S3 client via lru_cache
- Update setup_commands with /aiclear and /aiuser
- Update README, Dockerfile (Python 3.11), docker-compose (mount models)
- Pin dependency versions, remove unused httpx[socks]
- Add basic pytest tests for layout converter and voice normalization
2026-07-07 18:42:10 +07:00

97 lines
4.1 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 logging
from datetime import datetime
import aiohttp
from bot.utils.proxy import get_proxy_connector
from config import API_WEATHER
logger = logging.getLogger(__name__)
def get_wind_direction(deg: float) -> str:
if deg >= 337.5 or deg < 22.5:
return "Север"
if deg < 67.5:
return "Северо-восток"
if deg < 112.5:
return "Восток"
if deg < 157.5:
return "Юго-восток"
if deg < 202.5:
return "Юг"
if deg < 247.5:
return "Юго-запад"
if deg < 292.5:
return "Запад"
return "Северо-запад"
async def get_weather(message) -> None:
text_parts = message.text.split(maxsplit=1)
if len(text_parts) == 1:
await message.answer("Пожалуйста, укажите город.")
return
city = text_parts[1].strip()
url = "https://api.openweathermap.org/data/2.5/weather"
params = {"q": city, "appid": API_WEATHER, "units": "metric", "lang": "ru"}
connector = get_proxy_connector()
try:
async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=30, sock_connect=15, sock_read=15),
connector=connector,
) as session:
async with session.get(url, params=params) as response:
if response.status == 404:
await message.answer("Город не найден. Пожалуйста, уточните запрос.")
return
response.raise_for_status()
weather_data = await response.json()
weather_description = weather_data["weather"][0]["description"]
temperature = weather_data["main"]["temp"]
feels_like = weather_data["main"]["feels_like"]
temp_min = weather_data["main"]["temp_min"]
temp_max = weather_data["main"]["temp_max"]
humidity = weather_data["main"]["humidity"]
pressure = int(weather_data["main"]["pressure"] / 1.333)
wind_speed = weather_data["wind"]["speed"]
wind_deg = weather_data["wind"].get("deg", 0)
wind_direction = get_wind_direction(wind_deg)
rain_1h = weather_data.get("rain", {}).get("1h", 0)
clouds_all = weather_data["clouds"]["all"]
visibility = weather_data.get("visibility", 0)
sunrise_time = datetime.fromtimestamp(weather_data["sys"]["sunrise"]).strftime("%H:%M")
sunset_time = datetime.fromtimestamp(weather_data["sys"]["sunset"]).strftime("%H:%M")
weather_message = (
f"Погода в городе {city}:\n\n"
f"Описание: {weather_description}\n"
f"Температура: {temperature}°C (ощущается как {feels_like}°C)\n"
f"Минимальная температура: {temp_min}°C\n"
f"Максимальная температура: {temp_max}°C\n"
f"Влажность: {humidity}%\n"
f"Давление: {pressure} мм рт.ст\n"
f"Скорость ветра: {wind_speed} м/с, направление: {wind_direction}\n"
f"Осадки за последний час: {rain_1h} мм\n"
f"Облачность: {clouds_all}%\n"
f"Видимость: {visibility} м\n"
f"Восход: {sunrise_time}, закат: {sunset_time}"
)
await message.answer(weather_message, parse_mode=None)
except aiohttp.ClientResponseError as exc:
logger.error("HTTP ошибка погоды: %s", exc)
await message.answer("При получении данных произошла ошибка, попробуйте еще раз.")
except aiohttp.ClientError as exc:
logger.error("Ошибка запроса погоды: %s", exc)
await message.answer("Не удалось связаться с погодным сервисом.")
except Exception as exc:
logger.exception("Неожиданная ошибка погоды: %s", exc)
await message.answer("Произошла ошибка при обработке погоды.")
finally:
if connector:
await connector.close()