discordBot/ISSUES.md

79 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ISSUES — Задачи и баг-трекер
---
## Открытые задачи
### Средний приоритет
- [ ] **Отсутствует `pyproject.toml`** — проект использует `requirements.txt` + `requirements-dev.txt` без единого файла конфигурации. Рекомендуется `pyproject.toml` с `[project]`, настройками ruff и pytest
- [ ] **`Scheduler._task` без `add_done_callback`** — если `_scheduler_loop` завершится unexpectedly (не через `CancelledError`), это не будет залогировано. Добавить callback для логирования нештатного завершения task
- [ ] **Тест `test_fetch_weather_http_error_no_fallback` — raises bare `Exception` вместо `requests.HTTPError`** — в реальности `raise_for_status()` бросает `requests.HTTPError`, который ловится `except requests.RequestException` и корректно переходит на fallback. Тест проверяет несуществующий сценарий
### Низкий приоритет
- [ ] **`commands/stats.py` — list comprehension вместо generator** — `len([ch for ch in guild.channels if ...])` создаёт временный список. Заменить на `sum(1 for ch in guild.channels if ...)`
- [ ] **`TextHelpCommand.send_bot_help()` — дублирование логики** — ветки `if cog_or_none is None` и `else` делают одинаковую работу по итерации команд. Объединить в общий цикл
- [ ] **`commands/morning.py` не ловит исключения `run_morning`** — если `run_morning` выбросит, лог `!morning завершен` не запишется, а ошибка уйдёт в `on_command_error`. Добавить `try/except` для локальной обработки и логирования
- [ ] **Файл `nul` в корне проекта** — артефакт Windows (29 байт). Удалить из рабочей директории
---
## Завершено
- [x] ~~Эмодзи в embed-сообщениях~~ — удалены `🌅`, `✅`, `❌` из `utils/morning_runner.py`
- [x] ~~f-string в logger~~ — замена на `%`-формат в `bot.py`
- [x] ~~`import time` внутри `__init__`~~ — перенесён на уровень модуля в `bot.py`
- [x] ~~Проверка наличия `.env`~~`.env` в `.gitignore`, есть `.env.example`
- [x] ~~Команда `!msg` в BotRunner~~ — удалена из проекта
- [x] ~~Отсутствует `.gitignore`~~ — файл существует
- [x] ~~Отсутствует Dockerfile~~ — файл существует
- [x] ~~Отсутствует `.env.example`~~ — файл существует
- [x] ~~**Type hints в production-коде**~~ — добавлены аннотации ко всем 20 функциям
- [x] ~~**`asyncio.iscoroutinefunction` deprecated**~~ — monkey-patch в `bot.py` + `conftest.py`
- [x] ~~**Утечка корутины Scheduler в тестах**~~ — mock `_start_scheduler` вместо `asyncio.create_task`
- [x] ~~**Graceful shutdown (SIGTERM)**~~ — реализован через `on_shutdown` listener + `async with self.bot`
- [x] ~~**Пустой `__init__` в Morning**~~ — удалён
- [x] ~~**Global RateLimiter на модульном уровне**~~ — добавлены factory-функции `make_*_limiter()`
- [x] ~~**Type hints в тестах**~~ — добавлены `-> None` ко всем 140 test-функциям
- [x] ~~**`import` внутри функций в тестах**~~ — вынесены наверх модулей (35 вхождений)
- [x] ~~**Добавить pre-commit хуки**~~`.pre-commit-config.yaml` (ruff + ruff-format), `requirements-dev.txt`
- [x] ~~**Интеграционные тесты**~~ — 9 тестов загрузок когов, команд и утилит
- [x] ~~**`format_weather_for_embed` отсутствует в коде**~~ — заменено на `format_weather_for_message` в README.md
- [x] ~~**`_WEATHER_MAPPING` не отсортирован по убыванию длины ключей**~~ — отсортирован список по убыванию `len(key)`, добавлен комментарий (`utils/pogoda.py`)
- [x] ~~**Глобальные `requests.Session` не закрываются**~~ — добавлена `close_all_sessions()` в `utils/__init__.py`, вызов из `_on_shutdown` в `bot.py`
- [x] ~~**`run_morning` fallback может отправить дайджест в несколько каналов**~~ — fallback перебирает каналы целевого сервера вместо `bot.get_all_channels()` (`utils/morning_runner.py`)
- [x] ~~**`!nw`: последовательные вызовы API вместо параллельных**~~ — заменено на `asyncio.gather()` (`commands/news.py`)
- [x] ~~**`format_weather_data_for_console` выводит `None` в текст**~~ — добавлена явная проверка `is None` для всех полей (`utils/pogoda.py`), обновлён тест (`tests/test_commands_pg.py`)
- [x] ~~**`_parse_date` хрупкий fallback**~~ — валидация формата через regex, ISO даты конвертируются в DD.MM.YYYY (`utils/news.py`)
- [x] ~~**`wmo_to_russian` создаёт dict на каждый вызов**~~ — вынесен в константу `_WMO_MAPPING` (`utils/pogoda.py`)
- [x] ~~**`TextHelpCommand` пропускает команды без cog**~~ — standalone-команды показываются если не hidden (`bot.py`)
- [x] ~~**`translate_weather(" ")` возвращает пробелы**~~ — добавлен `.strip()` перед проверкой (`utils/pogoda.py`)
- [x] ~~**`pressure_to_mmhg(mb: Any)`**~~ — заменён на `float | int | str | None` (`utils/pogoda.py`)
- [x] ~~**`conftest.py` и `bot.py` дублируют monkey-patch**~~ — вынесен в `utils/compat.py`, оба файла импортируют оттуда
- [x] ~~**`RateLimiter` тесты зависят от реального времени**~~ — добавлен `_time_func` параметр, все тесты используют контролируемую функцию времени
- [x] ~~**`Dockerfile` не копирует `conftest.py`**~~`conftest.py` убран из Dockerfile (тестовый файл не нужен в production)
- [x] ~~**`requirements.txt` без пиннинга версий**~~`>=` заменён на `~=` (compatible release)
- [x] ~~**Закомментированный тест `pressure_to_mmhg(0)`**~~ — удалена устаревшая закомментированная строка
- [x] ~~**`format_articles(None)` бросает `TypeError`**~~ — добавлена валидация `None`, graceful fallback с сообщением
- [x] ~~**`setup_logging()` создаёт дублирующиеся handlers**~~ — добавлен `root.handlers.clear()` (`utils/logger.py`)
- [x] ~~**Первый `logger.info()` теряется**~~`setup_logging()` вызывается до первого лога (`bot.py`)
- [x] ~~**`translate_weather()` уязвим к ложным substring-совпадениям**~~ — точное совпадение приоритизируется (`utils/pogoda.py`)
- [x] ~~**`fromstring` импортирован внутри функции `fetch_rss`**~~ — вынесен на уровень модуля (`utils/news.py`)
- [x] ~~**`Scheduler._start_scheduler()` создаёт task синхронно**~~`__init__` больше не создаёт task; `start()` стал async-методом (`utils/morning_runner.py`, `bot.py`)
- [x] ~~**`Pg.__init__` хранит `self.api_url` как инстанс-переменную**~~ — удалён `__init__`, используется `API_URL_WEATHER` напрямую (`commands/pg.py`)
- [x] ~~**`fetch_cat()` не передаёт `x-api-key`**~~ — добавлена поддержка `CAT_API_KEY` из окружения, заголовок `x-api-key` передаётся при наличии ключа (`utils/cat.py`)
- [x] ~~**Глобальные экземпляры RateLimiter + factory-функции**~~ — дизайн подтверждён: глобальные синглтоны для production, factory-функции для тестов. Добавлен поясняющий комментарий (`utils/rate_limiter.py`)
- [x] ~~**`@pytest.mark.asyncio` избыточен**~~ — удалены 5 декораторов из `tests/test_integration.py` (`pytest.ini` имеет `asyncio_mode = auto`)
- [x] ~~**`Dockerfile` не копирует `tests/`**~~ — отклонено: Docker — production-окружение, тесты туда не нужны
- [x] ~~**Приватные атрибуты на объекте бота**~~`START_TIME` вынесен на уровень модуля `bot.py`, `self.bot._scheduler` удалён (не использовался). Обновлены `commands/status.py` и тесты
- [x] ~~**Избыточная проверка `ctx` в `on_command_error`**~~ — удалены проверки `ctx and`, так как `ctx` гарантированно передан discord.py (`bot.py`)
- [x] ~~**`ruff format --check` падает на 5 файлах**~~ — применён `ruff format` к 7 файлам
- [x] ~~**`commands/news.py` — дублирование кода для статей и постов**~~ — вынесено в `_format_feed_section()` (`commands/news.py`)
- [x] ~~**Город захардкожен в `API_URL_WEATHER`**~~ — вынесен в `WEATHER_CITY` env-переменную, шаблон `"Погода: {city}:"` (`utils/pogoda.py`, `.env.example`)