docs: обновить AGENTS.md и очистить ISSUES.md с новым TODO-списком
This commit is contained in:
parent
7bc5bae413
commit
680a3c822a
51
AGENTS.md
51
AGENTS.md
@ -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` как шаблон.
|
||||||
|
|||||||
94
ISSUES.md
94
ISSUES.md
@ -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`.
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user