Refactor: add commands/, console_commands/, !pogoda command

This commit is contained in:
deadzilla 2026-05-24 15:27:29 +05:00
parent 83fc714b8e
commit 89f17d53d8
9 changed files with 140 additions and 4 deletions

View File

@ -1,7 +1,7 @@
# AGENTS.md # AGENTS.md
## Проект ## Проект
Discord-бот на Python (discord.py). Команда `!msg <текст>` повторяет текст. Discord-бот на Python (discord.py). Команды: `!msg` (повтор текста), `!pogoda` (прогноз погоды для Магнитогорска).
## Запуск ## Запуск
``` ```
@ -16,3 +16,23 @@ python bot.py
## Конвенции ## Конвенции
Используй TODO-списки для каждого запроса с несколькими шагами. Используй TODO-списки для каждого запроса с несколькими шагами.
## Архитектура
```
commands/ # Discord команды (cogs)
__init__.py # ALL_COMMANDS — явные импорты
pogoda.py # !pogoda
console_commands/ # Консольные команды
__init__.py # ALL_CONSOLE_COMMANDS — явные импорты
stop.py # stop
```
**Добавление Discord команды:**
1. Создать файл `commands/имя.py` с классом, наследующим `commands.Cog`
2. Добавить импорт в `commands/__init__.py`
3. Добавить класс в `ALL_COMMANDS`
**Добавление консольной команды:**
1. Создать файл `console_commands/имя.py` с функцией
2. Добавить импорт в `console_commands/__init__.py`
3. Добавить функцию в `ALL_CONSOLE_COMMANDS`

23
bot.py
View File

@ -7,6 +7,8 @@ import discord
from discord.ext import commands from discord.ext import commands
from dotenv import load_dotenv from dotenv import load_dotenv
from commands import ALL_COMMANDS
load_dotenv() load_dotenv()
intents = discord.Intents.default() intents = discord.Intents.default()
@ -19,6 +21,11 @@ stop_event = threading.Event()
@bot.event @bot.event
async def on_ready(): async def on_ready():
print(f"Бот вошёл как {bot.user}") print(f"Бот вошёл как {bot.user}")
for cog_class in ALL_COMMANDS:
cog = cog_class()
await bot.add_cog(cog)
for cog in bot.cogs:
print(f" Загружен: {cog}")
@bot.command(name="msg") @bot.command(name="msg")
@ -34,13 +41,19 @@ def console_input():
if cmd == "stop": if cmd == "stop":
print("\nОстановка бота...") print("\nОстановка бота...")
stop_event.set() stop_event.set()
asyncio.run_coroutine_threadsafe(bot.close(), bot.loop) try:
bot.loop.stop()
except (AttributeError, RuntimeError):
pass
break break
elif cmd: elif cmd:
print(f"Неизвестная команда: {cmd}") print(f"Неизвестная команда: {cmd}")
except EOFError: except EOFError:
stop_event.set() stop_event.set()
asyncio.run_coroutine_threadsafe(bot.close(), bot.loop) try:
bot.loop.stop()
except (AttributeError, RuntimeError):
pass
break break
@ -58,5 +71,9 @@ if __name__ == "__main__":
bot.run(token) bot.run(token)
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nОстановка бота...") print("\nОстановка бота...")
bot.loop.stop() try:
bot.loop.stop()
except (AttributeError, RuntimeError):
pass
stop_event.set() stop_event.set()
sys.exit(0)

3
commands/__init__.py Normal file
View File

@ -0,0 +1,3 @@
from .pogoda import Pogoda
ALL_COMMANDS = [Pogoda]

Binary file not shown.

Binary file not shown.

87
commands/pogoda.py Normal file
View File

@ -0,0 +1,87 @@
import discord
from discord.ext import commands
import requests
class Pogoda(commands.Cog):
"""Команда !pogoda — прогноз погоды для Магнитогорска"""
def __init__(self):
self.api_url = "https://api.open-meteo.com/v1/forecast"
self.lat = 53.41
self.lon = 59.06
@commands.command(name="pogoda")
async def pogoda(self, ctx):
params = {
"latitude": self.lat,
"longitude": self.lon,
"current": "temperature_2m,weather_code,relative_humidity_2m,wind_speed_10m,pressure_msl",
"timezone": "Asia/Yekaterinburg",
}
try:
response = requests.get(self.api_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
except requests.RequestException as e:
await ctx.send(f"Ошибка при получении данных: {e}")
return
current = data.get("current", {})
if not current:
await ctx.send("Не удалось получить данные о погоде.")
return
temp = current.get("temperature_2m", "")
weather_code = current.get("weather_code", "")
humidity = current.get("relative_humidity_2m", "")
wind = current.get("wind_speed_10m", "")
pressure = current.get("pressure_msl", "")
description = self._get_weather_description(weather_code)
pressure_mm = self._pressure_to_mmhg(pressure)
await ctx.send(
f"🌡 Температура: {temp}°C\n"
f"📝 Описание: {description}\n"
f"💧 Влажность: {humidity}%\n"
f"💨 Ветер: {wind} км/ч\n"
f"🌍 Давление: {pressure_mm} мм рт. ст."
)
def _get_weather_description(self, code):
descriptions = {
0: "Ясно",
1: "Преимущественно ясно",
2: "Переменная облачность",
3: "Пасмурно",
45: "Туман",
48: "Изморозь",
51: "Лёгкая морось",
53: "Морось",
55: "Сильная морось",
61: "Небольшой дождь",
63: "Дождь",
65: "Сильный дождь",
66: "Ледяной дождь",
67: "Сильный ледяной дождь",
71: "Небольшой снег",
73: "Снег",
75: "Сильный снег",
77: "Снежная крупа",
80: "Небольшой ливень",
81: "Ливень",
82: "Сильный ливень",
85: "Небольшой снегопад",
86: "Сильный снегопад",
95: "Гроза",
96: "Гроза с градом",
99: "Сильная гроза с градом",
}
return descriptions.get(code, "Неизвестно")
def _pressure_to_mmhg(self, hpa):
if hpa == "":
return ""
return round(hpa * 0.750062, 1)

View File

@ -0,0 +1,5 @@
from .stop import stop
ALL_CONSOLE_COMMANDS = {
"stop": stop,
}

3
console_commands/stop.py Normal file
View File

@ -0,0 +1,3 @@
def stop(stop_event):
"""Остановка бота"""
pass

View File

@ -1,2 +1,3 @@
discord.py>=2.3.2 discord.py>=2.3.2
python-dotenv>=1.0.0 python-dotenv>=1.0.0
requests>=2.31.0