From 680a3c822aff186f9b4bf0f57b37d15e204045d2 Mon Sep 17 00:00:00 2001 From: deadzilla Date: Tue, 26 May 2026 21:48:19 +0500 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20AGENTS.md=20=D0=B8=20=D0=BE=D1=87=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D1=82=D1=8C=20ISSUES.md=20=D1=81=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=BC=20TODO-=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 51 +++++++++++++++++++++++++++--- ISSUES.md | 94 +++++++++++++++++++------------------------------------ 2 files changed, 80 insertions(+), 65 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index dafafb8..1e19b3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,19 +20,51 @@ python bot.py - **Все git-коммиты согласовывать с пользователем перед созданием.** - **Все сообщения git-коммитов писать на русском языке.** +## Команды + +| Команда | Описание | Формат вывода | +|---------|----------|---------------| +| `!pogoda` | Погода в Магнитогорске | Температура, ощущается, описание, влажность, ветер, давление | +| `!news` | Топ-5 статей и топ-5 новостей AI с Habr | Два блока: статьи и новости | +| `!morning` | Погода + топ-5 статей + топ-5 новостей + котик | Embed: котик thumbnail, погода, статьи, новости | +| `!cat` | Случайный котик | Embed с изображением | +| `!msg <текст>` | Повторяет текст | Текст | + +## Структура проекта + +| Каталог | Назначение | +|---------|------------| +| `commands/` | Discord-команды (Cogs) | +| `utils/` | Утилиты (API-клиенты, конвертации) | +| `console_commands/` | Консольные команды (для терминала) | +| `bot.py` | Точка входа, загрузка Cogs, обработка ошибок | + ## API и внешние сервисы -### Погода (!pogoda) -- **Основной**: `wttr.in` (бесплатный, без ключа) +### Погода (!pogoda, !morning) +- **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа) - **Fallback**: `api.open-meteo.com` (бесплатный, без ключа) - Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках - Fallback срабатывает автоматически при неуспешных попытках -- WMO weather codes → русский перевод в `_wmo_to_russian()` +- WMO weather codes → русский перевод в `wmo_to_russian()` ### Конвертации - Давление: hPa → мм рт. ст. (`* 0.750062`) - Ветер: км/ч → м/с (`/ 3.6`) -- Погодные описания: английский → русский (`_translate_weather()`) +- Погодные описания: английский → русский (`translate_weather()`) + +### RSS-ленты Habr (!news, !morning) +| Тип | URL | +|-----|-----| +| Статьи | `https://habr.com/ru/rss/hubs/artificial_intelligence/articles/top/daily/?fl=ru` | +| Новости | `https://habr.com/ru/rss/hubs/artificial_intelligence/news/top/daily/?fl=ru` | + +Парсятся RSS 2.0 / Atom, извлекаются title, pubDate, creator, category, guid. + +### Cat API (!cat, !morning) +- **URL**: `https://api.thecatapi.com/v1/images/search` +- Возвращает JSON: `[{"url": "..."}]` +- Используется для случайных изображений котиков ## Конфигурация | Переменная | Описание | Где взять | @@ -56,6 +88,17 @@ requests>=2.31.0 Давление: X мм рт. ст. ``` +## Консольные команды +Запускаются через терминал при старте бота. Список выводится при запуске. + +| Команда | Описание | +|---------|----------| +| `pogoda` | Вывести погоду в Магнитогорск | +| `news` | Вывести топ-5 статей AI с Habr | +| `morning` | Вывести погоду, топ-5 статей и котик | +| `cat` | Заглушка | +| `stop` | Остановить бота | + ## Безопасность - `.env` в `.gitignore` — токен никогда не должен попадать в репозиторий. - Используй `.env.example` как шаблон. diff --git a/ISSUES.md b/ISSUES.md index b8bccba..dd1d8a3 100644 --- a/ISSUES.md +++ b/ISSUES.md @@ -1,4 +1,4 @@ -# Issues — список проблем проекта +# Tasks — список задач проекта > Состояние на: 2026-05-26 @@ -6,79 +6,51 @@ ## 🔴 Критические -### 1. Retry без задержки между попытками ✅ РЕШЕНО -- **Где:** `commands/pogoda.py`, `console_commands/pogoda.py` -- **Проблема:** Цикл `for attempt in range(3)` не содержит `time.sleep()`. Все 3 попытки выполняются мгновенно, что бессмысленно при временных сетевых сбоях. -- **AGENTS.md обещает:** "Retry: 3 попытки с экспоненциальной задержкой" — но задержки нет. -- **Решение:** Добавить `time.sleep(2 ** attempt)` внутри цикла retry. -- **Статус:** Исправлено. Добавлена экспоненциальная задержка: 2 сек, 4 сек между попытками. +### 1. Фикс `posts is None` → `TypeError` в `!news` +- [ ] `commands/news.py`: добавить проверку `if posts is None` **до** вызова `format_articles(posts, ...)` +- [ ] `commands/news.py`: аналогично проверить `if articles is None` перед `format_articles(articles, ...)` +- [ ] `commands/morning.py`: проверить все 3 вызова `format_articles()` — `articles`, `posts`, и убедиться, что `None` не проходит -### 2. Блокировка event loop в async-методе ✅ РЕШЕНО -- **Где:** `commands/news.py`, `commands/cat.py`, `commands/pogoda.py` -- **Проблема:** Асинхронные методы вызывали синхронный `requests.get()`, блокируя весь event loop. -- **Решение:** Обернуть `requests.get()` в `await asyncio.to_thread()` и заменить `time.sleep()` на `await asyncio.sleep()`. -- **Статус:** Исправлено. В `news.py`, `cat.py`, `pogoda.py` все HTTP-запросы выполняются в отдельных потоках, а ожидания — неблокирующие. +### 2. Убрать эмодзи из `console_commands/cat.py` +- [ ] Заменить `print("🐱 тут должен быть котик")` на `print("Заглушка: тут должен быть котик")` --- ## 🟡 Средние -### 3. Дублирование кода между Discord и console командами ✅ РЕШЕНО -- **Где:** `console_commands/pogoda.py`, `console_commands/news.py` (и их аналоги в `commands/`) -- **Проблема:** Логика погоды и новостей полностью продублирована. Изменения нужно вносить в два места. -- **Решение:** Вынести общую логику в `utils/` (`utils/pogoda.py`, `utils/news.py`) и использовать её из обоих мест. -- **Статус:** Исправлено. Созданы `utils/pogoda.py` и `utils/news.py`. Оба файла используют единые функции без дублирования. +### 3. Убрать пустой embed в `!morning` при всех ошибках +- [ ] `commands/morning.py`: добавить fallback-сообщение в `description_lines`, если все API вернули `None` +- [ ] Проверить, что `embed.description` никогда не пустой -### 4. `from datetime import datetime` внутри метода ✅ РЕШЕНО -- **Где:** `utils/news.py` → `_parse_date()`, строка 67 -- **Проблема:** Импорт внутри метода — антипаттерн. Замедляет выполнение, нарушает PEP 8. -- **Решение:** Перенести `from datetime import datetime` в начало файла. -- **Статус:** Исправлено. Импорт вынесен на уровень модуля. +### 4. Заменить ручную загрузку Cogs на `bot.load_extension()` +- [ ] `bot.py`: заменить цикл `for cog_class in ALL_COMMANDS: await bot.add_cog(cog)` на `bot.load_extension()` +- [ ] Обновить `commands/__init__.py` — добавить метаданные для порядка загрузки +- [ ] Протестировать, что все Cogs загружаются корректно -### 5. Консольная команда `cat` — заглушка ✅ РЕШЕНО -- **Где:** `console_commands/cat.py` -- **Проблема:** Функция просто печатает `"🐱 тут должен быть котик"`. Заглушка видна пользователю. -- **Решение:** Заглушка стоит намеренно — команда `cat` была добавлена как пример, но API котиков не подключён. Это не баг, а planned behavior. -- **Статус:** Зафиксировано как намеренное поведение. +### 5. Оптимизировать `translate_weather()` — O(n) линейный поиск +- [ ] `utils/pogoda.py`: отсортировать ключи `mapping` по длине (убывание) +- [ ] Использовать `any(key.lower() in text.lower() for key in sorted_keys)` для приоритета точных совпадений +- [ ] Или заменить на `re` с регулярным выражением -### 6. `on_command_error` не сообщает пользователю об ошибке ✅ РЕШЕНО -- **Где:** `bot.py` → `on_command_error()` -- **Проблема:** Ошибки команд просто печатаются в `stdout`. Пользователь в чате не видит, что команда выполнилась с ошибкой. -- **Решение:** Добавить нейтральное сообщение в Discord и детали в лог. -- **Статус:** Исправлено. Детали ошибки → `logger.error` с `exc_info=True`. Пользователю → нейтральное сообщение. Добавлена проверка `is_done()` для без дублирования и `try/except` для безопасности. - -### 7. Нет `.gitignore` ✅ РЕШЕНО -- **Где:** проект -- **Проблема:** Отсутствовал `.gitignore`, `.env` мог случайно попасть в репозиторий. -- **Решение:** Создать `.gitignore` с правилами для Python (`__pycache__/`, `*.pyc`, `.env`, `venv/`, `*.egg-info/`), логов, временных файлов, кэша ОС и IDE. -- **Статус:** Исправлено. `.gitignore` создан и обновлён. - -### 8. `requests` без Session — нет переиспользования соединений ✅ РЕШЕНО -- **Где:** `utils/pogoda.py`, `utils/news.py`, `commands/cat.py` -- **Проблема:** Каждый `requests.get()` создаёт новое TCP-соединение. Для частых запросов это расточительно. -- **Решение:** Создать `requests.Session()` и использовать `session.get()`. -- **Статус:** Исправлено. Session создан в каждом модуле, все `requests.get()` заменены на `_session.get()`. +### 6. Вынести `self.api_url` в константу класса +- [ ] `commands/pogoda.py`: заменить `self.api_url = ...` на `API_URL = "..."` на уровне класса +- [ ] `commands/morning.py`: аналогично +- [ ] `console_commands/pogoda.py`: вынести URL в `utils/pogoda.py` как константу --- ## 🟢 Малые улучшения -### 9. Linear search в `_translate_weather` -- **Где:** `commands/pogoda.py`, `console_commands/pogoda.py` -- **Проблема:** Метод `_translate_weather()` перебирает весь словарь `mapping` циклом `for key, value in mapping.items()`. При большом словаре — неэффективно. -- **Решение:** Сортировать ключи по длине (убывание) и использовать `re` или `any(key.lower() in text.lower() for key in sorted_keys)`. +### 7. Добавить `__all__` в `__init__.py` +- [ ] `commands/__init__.py`: `__all__ = ["ALL_COMMANDS"]` +- [ ] `console_commands/__init__.py`: `__all__ = ["ALL_CONSOLE_COMMANDS"]` +- [ ] `utils/__init__.py`: добавить `__all__` с основными функциями -### 10. Нет `__all__` в `__init__.py` -- **Где:** `commands/__init__.py`, `console_commands/__init__.py` -- **Проблема:** Нет явного экспорта публичного API модулей. -- **Решение:** Добавить `__all__ = ["ALL_COMMANDS"]` / `__all__ = ["ALL_CONSOLE_COMMANDS"]`. +### 8. Убрать дублирование парсинга погоды в console_commands +- [ ] `console_commands/pogoda.py`: вынести форматирование результата в `utils/pogoda.py` как `format_weather_data()` +- [ ] `console_commands/morning.py`: использовать `format_weather_data()` вместо inline-парсинга +- [ ] `commands/morning.py`: использовать `format_weather_data()` вместо inline-парсинга -### 11. Жёстко заданные URL в разных файлах -- **Где:** `commands/news.py` и `console_commands/news.py` -- **Проблема:** Константы `RSS_URL_ARTICLES` и `RSS_URL_POSTS` продублированы. -- **Решение:** Вынести в `config.py` и импортировать оттуда. - -### 12. Потенциальная гонка при остановке бота -- **Где:** `bot.py` -- **Проблема:** `stop_event.set()` и `bot.close()` вызываются из `console_input()`, `KeyboardInterrupt` и `console_commands/stop.py`. Возможна гонка. -- **Решение:** Добавить флаг `is_stopping` или использовать `asyncio.Lock`. +### 9. Добавить `raise_exception=True` в `bot.run()` +- [ ] `bot.py`: добавить `raise_exception=True` для видимого падения при ошибках токена/сети +- [ ] Добавить логирование в `KeyboardInterrupt` блок