From 347aa6ced73ba4aba4612785eeebaf1fb0e7285f Mon Sep 17 00:00:00 2001 From: deadzilla Date: Tue, 26 May 2026 00:25:31 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=83=20!ca?= =?UTF-8?q?t=20=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=83=D1=8E=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=83=20pog?= =?UTF-8?q?oda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands/__init__.py | 3 +- commands/cat.py | 28 ++++++ console_commands/__init__.py | 4 + console_commands/cat.py | 3 + console_commands/pogoda.py | 168 +++++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 commands/cat.py create mode 100644 console_commands/cat.py create mode 100644 console_commands/pogoda.py diff --git a/commands/__init__.py b/commands/__init__.py index 6ffd849..4f3058e 100644 --- a/commands/__init__.py +++ b/commands/__init__.py @@ -1,4 +1,5 @@ from .pogoda import Pogoda from .news import News +from .cat import Cat -ALL_COMMANDS = [Pogoda, News] +ALL_COMMANDS = [Pogoda, News, Cat] diff --git a/commands/cat.py b/commands/cat.py new file mode 100644 index 0000000..016f14c --- /dev/null +++ b/commands/cat.py @@ -0,0 +1,28 @@ +import discord +from discord.ext import commands +import requests + + +class Cat(commands.Cog): + """Команда !cat — случайный котик""" + + @commands.command(name="cat") + async def cat(self, ctx): + """Получить случайного котика""" + try: + response = requests.get( + "https://api.thecatapi.com/v1/images/search", + timeout=10 + ) + response.raise_for_status() + data = response.json() + url = data[0]["url"] + + embed = discord.Embed( + title="🐱 Котик для тебя!", + color=discord.Color.orange() + ) + embed.set_image(url=url) + await ctx.send(embed=embed) + except requests.RequestException: + await ctx.send("Не удалось получить котика. Попробуйте позже.") diff --git a/console_commands/__init__.py b/console_commands/__init__.py index 19941b4..881d014 100644 --- a/console_commands/__init__.py +++ b/console_commands/__init__.py @@ -1,7 +1,11 @@ from .stop import stop from .news import news +from .cat import cat +from .pogoda import pogoda ALL_CONSOLE_COMMANDS = { "stop": stop, "news": news, + "cat": cat, + "pogoda": pogoda, } diff --git a/console_commands/cat.py b/console_commands/cat.py new file mode 100644 index 0000000..b3cdf41 --- /dev/null +++ b/console_commands/cat.py @@ -0,0 +1,3 @@ +def cat(stop_event, bot): + """Заглушка: тут должен быть котик""" + print("🐱 тут должен быть котик") diff --git a/console_commands/pogoda.py b/console_commands/pogoda.py new file mode 100644 index 0000000..4817b6b --- /dev/null +++ b/console_commands/pogoda.py @@ -0,0 +1,168 @@ +import requests +from requests.exceptions import ConnectionError, Timeout, SSLError + + +def pogoda(stop_event, bot): + """Вывести прогноз погоды для Магнитогорска""" + api_url = "https://wttr.in/Magnitogorsk?format=j1&lang=ru" + data = _fetch_weather(api_url) + + if data is None: + print("Не удалось получить данные о погоде.") + return + + current = data.get("current_condition", [{}])[0] + if not current: + print("Не удалось получить данные о погоде.") + return + + temp = current.get("temp_C", "—") + feels_like = current.get("FeelsLikeC", "—") + description = _translate_weather(current.get("weatherDesc", [{}])[0].get("value", "—")) + humidity = current.get("humidity", "—") + wind_kmh = current.get("windspeedKmph", "—") + try: + wind = round(int(wind_kmh) / 3.6, 1) if wind_kmh != "—" else "—" + except (ValueError, TypeError): + wind = "—" + pressure_mb = current.get("pressure", "—") + pressure_mm = _pressure_to_mmhg(pressure_mb) + + print(f"[TEMP] Температура: {temp}°C (ощущается как {feels_like}°C)") + print(f"[DESC] Описание: {description}") + print(f"[HUMID] Влажность: {humidity}%") + print(f"[WIND] Ветер: {wind} м/с") + print(f"[PRESS] Давление: {pressure_mm} мм рт. ст.") + + +def _fetch_weather(url): + """Получить данные о погоде с retry и fallback.""" + # Пробуем wttr.in с retry + for attempt in range(3): + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + return response.json() + except (SSLError, ConnectionError, Timeout): + if attempt < 2: + continue + break + except requests.RequestException: + return None + + # Fallback: Open-Meteo API (без ключа, HTTPS) + return _fetch_open_meteo() + + +def _fetch_open_meteo(): + """Fallback на Open-Meteo API.""" + url = ( + "https://api.open-meteo.com/v1/forecast?" + "latitude=53.4069&longitude=58.9797¤t=temperature," + "apparent_temperature,weather_code,wind_speed_10m," + "relative_humidity_2m,pressure_msl&timezone=Asia/Chelyabinsk" + ) + for attempt in range(3): + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + current = data.get("current", {}) + # weather_code WMO код → перевод (https://open-meteo.com/en/docs) + weather_code = current.get("weather_code", None) + desc = _wmo_to_russian(weather_code) + return { + "current_condition": [{ + "temp_C": current.get("temperature", "—"), + "FeelsLikeC": current.get("apparent_temperature", "—"), + "weatherDesc": [{"value": desc}], + "humidity": current.get("relative_humidity_2m", "—"), + "windspeedKmph": current.get("wind_speed_10m", "—"), + "pressure": current.get("pressure_msl", "—"), + }] + } + except (SSLError, ConnectionError, Timeout): + if attempt < 2: + continue + break + except requests.RequestException: + return None + + return None + + +def _wmo_to_russian(code): + """Перевод WMO weather code в русский.""" + mapping = { + 0: "Ясно", + 1: "Ясно", 2: "Переменная облачность", + 3: "Пасмурно", + 45: "Туман", 48: "Туман", + 51: "Лёгкая морось", 53: "Морось", 55: "Сильная морось", + 56: "Ледяная морось", 57: "Сильная ледяная морось", + 61: "Небольшой дождь", 63: "Дождь", 65: "Сильный дождь", + 66: "Ледяной дождь", 67: "Сильный ледяной дождь", + 71: "Небольшой снег", 73: "Снег", 75: "Сильный снег", + 77: "Снежная крупа", + 80: "Небольшой ливень", 81: "Ливень", 82: "Сильный ливень", + 85: "Снежный ливень", 86: "Сильный снежный ливень", + 95: "Гроза", 96: "Гроза с градом", 99: "Сильная гроза с градом", + } + return mapping.get(code, "Неизвестно") + + +def _translate_weather(en): + if not en: + return "—" + mapping = { + "Moderate or heavy freezing rain in area": "Ледяной дождь", + "Moderate or heavy sleet in area": "Слякоть", + "Moderate or heavy snow in area": "Снег", + "Moderate or heavy rain in area": "Дождь", + "Thundery outbreaks in nearby": "Гроза вблизи", + "Patchy rain nearby": "Местами дождь", + "Patchy snow nearby": "Местами снег", + "Patchy sleet nearby": "Местами слякоть", + "Heavy freezing rain": "Сильный ледяной дождь", + "Heavy snow": "Сильный снег", + "Heavy rain": "Сильный дождь", + "Moderate or heavy rain at times": "Дождь", + "Moderate or heavy snow at times": "Снег", + "Blowing snow": "Метель", + "Patchy light drizzle": "Местами лёгкая морось", + "Moderate or heavy freezing rain at a distance": "Ледяной дождь", + "Moderate or heavy sleet at a distance": "Слякоть", + "Light rain shower": "Небольшой дождь", + "Heavy rain shower": "Сильный дождь", + "Moderate rain": "Умеренный дождь", + "Light rain": "Небольшой дождь", + "Moderate rain at times": "Умеренный дождь", + "Heavy rain at times": "Сильный дождь", + "Light snow": "Небольшой снег", + "Moderate snow": "Умеренный снег", + "Patchy light snow": "Местами лёгкий снег", + "Partly cloudy": "Переменная облачность", + "Moderate or light sleet": "Слякоть", + "Light freezing rain": "Лёгкий ледяной дождь", + "Foggy": "Туманно", + "Fog": "Туман", + "Mist": "Туман", + "Haze": "Дымка", + "Overcast": "Пасмурно", + "Cloudy": "Облачно", + "Clear": "Ясно", + "Sunny": "Ясно", + } + for key, value in mapping.items(): + if key.lower() in en.lower(): + return value + return en + + +def _pressure_to_mmhg(mb): + if mb == "—" or not mb: + return "—" + try: + return round(float(mb) * 0.750062, 1) + except (ValueError, TypeError): + return "—"