docs: обновить AGENTS.md и очистить ISSUES.md с новым TODO-списком

This commit is contained in:
deadzilla 2026-05-26 21:48:19 +05:00
parent 1f9338d3ba
commit e386ff3c42
2 changed files with 80 additions and 65 deletions

View File

@ -20,19 +20,51 @@ python bot.py
- **Все git-коммиты согласовывать с пользователем перед созданием.** - **Все git-коммиты согласовывать с пользователем перед созданием.**
- **Все сообщения 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 и внешние сервисы ## API и внешние сервисы
### Погода (!pogoda) ### Погода (!pogoda, !morning)
- **Основной**: `wttr.in` (бесплатный, без ключа) - **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа)
- **Fallback**: `api.open-meteo.com` (бесплатный, без ключа) - **Fallback**: `api.open-meteo.com` (бесплатный, без ключа)
- Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках - Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
- Fallback срабатывает автоматически при неуспешных попытках - Fallback срабатывает автоматически при неуспешных попытках
- WMO weather codes → русский перевод в `_wmo_to_russian()` - WMO weather codes → русский перевод в `wmo_to_russian()`
### Конвертации ### Конвертации
- Давление: hPa → мм рт. ст. (`* 0.750062`) - Давление: hPa → мм рт. ст. (`* 0.750062`)
- Ветер: км/ч → м/с (`/ 3.6`) - Ветер: км/ч → м/с (`/ 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 мм рт. ст. Давление: X мм рт. ст.
``` ```
## Консольные команды
Запускаются через терминал при старте бота. Список выводится при запуске.
| Команда | Описание |
|---------|----------|
| `pogoda` | Вывести погоду в Магнитогорск |
| `news` | Вывести топ-5 статей AI с Habr |
| `morning` | Вывести погоду, топ-5 статей и котик |
| `cat` | Заглушка |
| `stop` | Остановить бота |
## Безопасность ## Безопасность
- `.env` в `.gitignore` — токен никогда не должен попадать в репозиторий. - `.env` в `.gitignore` — токен никогда не должен попадать в репозиторий.
- Используй `.env.example` как шаблон. - Используй `.env.example` как шаблон.

View File

@ -1,4 +1,4 @@
# Issues — список проблем проекта # Tasks — список задач проекта
> Состояние на: 2026-05-26 > Состояние на: 2026-05-26
@ -6,79 +6,51 @@
## 🔴 Критические ## 🔴 Критические
### 1. Retry без задержки между попытками ✅ РЕШЕНО ### 1. Фикс `posts is None``TypeError` в `!news`
- **Где:** `commands/pogoda.py`, `console_commands/pogoda.py` - [ ] `commands/news.py`: добавить проверку `if posts is None` **до** вызова `format_articles(posts, ...)`
- **Проблема:** Цикл `for attempt in range(3)` не содержит `time.sleep()`. Все 3 попытки выполняются мгновенно, что бессмысленно при временных сетевых сбоях. - [ ] `commands/news.py`: аналогично проверить `if articles is None` перед `format_articles(articles, ...)`
- **AGENTS.md обещает:** "Retry: 3 попытки с экспоненциальной задержкой" — но задержки нет. - [ ] `commands/morning.py`: проверить все 3 вызова `format_articles()``articles`, `posts`, и убедиться, что `None` не проходит
- **Решение:** Добавить `time.sleep(2 ** attempt)` внутри цикла retry.
- **Статус:** Исправлено. Добавлена экспоненциальная задержка: 2 сек, 4 сек между попытками.
### 2. Блокировка event loop в async-методе ✅ РЕШЕНО ### 2. Убрать эмодзи из `console_commands/cat.py`
- **Где:** `commands/news.py`, `commands/cat.py`, `commands/pogoda.py` - [ ] Заменить `print("🐱 тут должен быть котик")` на `print("Заглушка: тут должен быть котик")`
- **Проблема:** Асинхронные методы вызывали синхронный `requests.get()`, блокируя весь event loop.
- **Решение:** Обернуть `requests.get()` в `await asyncio.to_thread()` и заменить `time.sleep()` на `await asyncio.sleep()`.
- **Статус:** Исправлено. В `news.py`, `cat.py`, `pogoda.py` все HTTP-запросы выполняются в отдельных потоках, а ожидания — неблокирующие.
--- ---
## 🟡 Средние ## 🟡 Средние
### 3. Дублирование кода между Discord и console командами ✅ РЕШЕНО ### 3. Убрать пустой embed в `!morning` при всех ошибках
- **Где:** `console_commands/pogoda.py`, `console_commands/news.py` (и их аналоги в `commands/`) - [ ] `commands/morning.py`: добавить fallback-сообщение в `description_lines`, если все API вернули `None`
- **Проблема:** Логика погоды и новостей полностью продублирована. Изменения нужно вносить в два места. - [ ] Проверить, что `embed.description` никогда не пустой
- **Решение:** Вынести общую логику в `utils/` (`utils/pogoda.py`, `utils/news.py`) и использовать её из обоих мест.
- **Статус:** Исправлено. Созданы `utils/pogoda.py` и `utils/news.py`. Оба файла используют единые функции без дублирования.
### 4. `from datetime import datetime` внутри метода ✅ РЕШЕНО ### 4. Заменить ручную загрузку Cogs на `bot.load_extension()`
- **Где:** `utils/news.py``_parse_date()`, строка 67 - [ ] `bot.py`: заменить цикл `for cog_class in ALL_COMMANDS: await bot.add_cog(cog)` на `bot.load_extension()`
- **Проблема:** Импорт внутри метода — антипаттерн. Замедляет выполнение, нарушает PEP 8. - [ ] Обновить `commands/__init__.py` — добавить метаданные для порядка загрузки
- **Решение:** Перенести `from datetime import datetime` в начало файла. - [ ] Протестировать, что все Cogs загружаются корректно
- **Статус:** Исправлено. Импорт вынесен на уровень модуля.
### 5. Консольная команда `cat` — заглушка ✅ РЕШЕНО ### 5. Оптимизировать `translate_weather()` — O(n) линейный поиск
- **Где:** `console_commands/cat.py` - [ ] `utils/pogoda.py`: отсортировать ключи `mapping` по длине (убывание)
- **Проблема:** Функция просто печатает `"🐱 тут должен быть котик"`. Заглушка видна пользователю. - [ ] Использовать `any(key.lower() in text.lower() for key in sorted_keys)` для приоритета точных совпадений
- **Решение:** Заглушка стоит намеренно — команда `cat` была добавлена как пример, но API котиков не подключён. Это не баг, а planned behavior. - [ ] Или заменить на `re` с регулярным выражением
- **Статус:** Зафиксировано как намеренное поведение.
### 6. `on_command_error` не сообщает пользователю об ошибке ✅ РЕШЕНО ### 6. Вынести `self.api_url` в константу класса
- **Где:** `bot.py``on_command_error()` - [ ] `commands/pogoda.py`: заменить `self.api_url = ...` на `API_URL = "..."` на уровне класса
- **Проблема:** Ошибки команд просто печатаются в `stdout`. Пользователь в чате не видит, что команда выполнилась с ошибкой. - [ ] `commands/morning.py`: аналогично
- **Решение:** Добавить нейтральное сообщение в Discord и детали в лог. - [ ] `console_commands/pogoda.py`: вынести URL в `utils/pogoda.py` как константу
- **Статус:** Исправлено. Детали ошибки → `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()`.
--- ---
## 🟢 Малые улучшения ## 🟢 Малые улучшения
### 9. Linear search в `_translate_weather` ### 7. Добавить `__all__` в `__init__.py`
- **Где:** `commands/pogoda.py`, `console_commands/pogoda.py` - [ ] `commands/__init__.py`: `__all__ = ["ALL_COMMANDS"]`
- **Проблема:** Метод `_translate_weather()` перебирает весь словарь `mapping` циклом `for key, value in mapping.items()`. При большом словаре — неэффективно. - [ ] `console_commands/__init__.py`: `__all__ = ["ALL_CONSOLE_COMMANDS"]`
- **Решение:** Сортировать ключи по длине (убывание) и использовать `re` или `any(key.lower() in text.lower() for key in sorted_keys)`. - [ ] `utils/__init__.py`: добавить `__all__` с основными функциями
### 10. Нет `__all__` в `__init__.py` ### 8. Убрать дублирование парсинга погоды в console_commands
- **Где:** `commands/__init__.py`, `console_commands/__init__.py` - [ ] `console_commands/pogoda.py`: вынести форматирование результата в `utils/pogoda.py` как `format_weather_data()`
- **Проблема:** Нет явного экспорта публичного API модулей. - [ ] `console_commands/morning.py`: использовать `format_weather_data()` вместо inline-парсинга
- **Решение:** Добавить `__all__ = ["ALL_COMMANDS"]` / `__all__ = ["ALL_CONSOLE_COMMANDS"]`. - [ ] `commands/morning.py`: использовать `format_weather_data()` вместо inline-парсинга
### 11. Жёстко заданные URL в разных файлах ### 9. Добавить `raise_exception=True` в `bot.run()`
- **Где:** `commands/news.py` и `console_commands/news.py` - [ ] `bot.py`: добавить `raise_exception=True` для видимого падения при ошибках токена/сети
- **Проблема:** Константы `RSS_URL_ARTICLES` и `RSS_URL_POSTS` продублированы. - [ ] Добавить логирование в `KeyboardInterrupt` блок
- **Решение:** Вынести в `config.py` и импортировать оттуда.
### 12. Потенциальная гонка при остановке бота
- **Где:** `bot.py`
- **Проблема:** `stop_event.set()` и `bot.close()` вызываются из `console_input()`, `KeyboardInterrupt` и `console_commands/stop.py`. Возможна гонка.
- **Решение:** Добавить флаг `is_stopping` или использовать `asyncio.Lock`.