- 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
97 lines
4.1 KiB
Python
97 lines
4.1 KiB
Python
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()
|