docs: синхронизировать ISSUES.md, обновить README.md, очистить AGENTS.md

- ISSUES.md: закрыто 7 задач с ссылками на коммиты (on_command_error, !hp харкод, харкод URL, utils/__init__.py, print→logging, автогенерация !hp, дублирование команд, healthcheck)
- AGENTS.md: удалена пустая секция 'Структура проекта' (есть в README.md)
- README.md: обновлена архитектура (добавлены utils/__init__.py, utils/morning_runner.py, console_commands/help.py, Docker-файлы), исправлено кол-во тестов (262→204), добавлена секция 'Запуск в Docker', убрана ссылка на AGENTS.md как основной документ, добавлена консольная команда help, обновлены таблицы утилит
This commit is contained in:
deadzilla 2026-06-09 21:37:07 +05:00
parent 52fdbc3778
commit 47e4b60111
3 changed files with 77 additions and 33 deletions

View File

@ -52,6 +52,4 @@ model_requirements:
- Используй type hints для всех функций - Используй type hints для всех функций
- Документируй публичные методы через docstrings - Документируй публичные методы через docstrings
- Следуй PEP 8 - Следуй PEP 8
- Используй f-строки вместо конкатенации - Используй f-строки вместо конкатенации
### Структура проекта

View File

@ -2,10 +2,14 @@
## Баги ## Баги
- [ ] `on_command_error`: проверка `ctx.interaction` может падать — при текстовых командах (`command_prefix`) `ctx.interaction` может быть `None` - [x] `on_command_error`: проверка `ctx.interaction` может падать — при текстовых командах (`command_prefix`) `ctx.interaction` может быть `None`
- [ ] `commands/help.py`: список команд хардкодится вручную — при добавлении новой команды `!hp` покажет устаревший список - **Закрыто:** коммит `d698380` — добавлен `hasattr(ctx, 'interaction')` и проверка `ctx.command` на `None`
- [ ] `console_commands/pogoda.py`: хардкодит URL `wttr.in`, дублируя логику из `utils/pogoda.py` - [x] `commands/help.py`: список команд хардкодился вручную — при добавлении новой команды `!hp` показывал устаревший список
- [ ] `utils/pogoda.py` + `utils/cat.py`: нет отдельного модуля `utils/__init__.py` с публичным API — импорты разбросаны - **Закрыто:** коммит `2690ee7` — собирает команды автоматически из `ctx.bot.commands`
- [x] `console_commands/pogoda.py`: хардкодил URL `wttr.in`, дублируя логику из `utils/pogoda.py`
- **Закрыто:** коммит `560dc55` — использует `API_URL_WEATHER` из `utils.pogoda`
- [x] `utils/pogoda.py` + `utils/cat.py`: нет отдельного модуля `utils/__init__.py` с публичным API — импорты разбросаны
- **Закрыто:** коммит `560dc55` — создан `utils/__init__.py` с `__all__` и явными импортами
--- ---
@ -13,9 +17,12 @@
### Высокий приоритет ### Высокий приоритет
- [ ] **Заменить все `print()` на `logging`** — в `utils/pogoda.py`, `utils/morning_runner.py`, `console_commands/`, `bot.py` - [x] **Заменить все `print()` на `logging`** — в `utils/pogoda.py`, `utils/morning_runner.py`, `console_commands/`, `bot.py`
- [ ] **Автоматическая генерация `!hp`** — собирать список команд из `bot.commands` вместо хардкода - **Закрыто:** коммит `52fdbc3` — системные сообщения (startup, shutdown, retry, ошибки API) переведены на `logging`; `print()` оставлены в `console_commands/` как пользовательский вывод в терминал
- [ ] **Убрать дублирование `commands/` и `console_commands/`** — вынести общую логику в `utils/`, оставить тонкие обёртки - [x] **Автоматическая генерация `!hp`** — собирать список команд из `bot.commands` вместо хардкода
- **Закрыто:** коммит `2690ee7`
- [x] **Убрать дублирование `commands/` и `console_commands/`** — вынести общую логику в `utils/`, оставить тонкие обёртки
- **Закрыто:** коммит `560dc55``format_weather_data_for_console()`, `gather_morning()`, `utils/__init__.py`
- [ ] **Валидация конфигурации** — проверить формат `MORNING_TIME` (`ЧЧ:ММ`) и наличие `MORNING_CHANNEL_ID` при запуске - [ ] **Валидация конфигурации** — проверить формат `MORNING_TIME` (`ЧЧ:ММ`) и наличие `MORNING_CHANNEL_ID` при запуске
### Средний приоритет ### Средний приоритет
@ -28,9 +35,11 @@
### Низкий приоритет ### Низкий приоритет
- [ ] **Улучшить Dockerfile** — multi-stage сборка, healthcheck - [ ] **Улучшить Dockerfile** — multi-stage сборка
- [x] **Добавить healthcheck** — в Dockerfile
- **Закрыто:** healthcheck присутствует в Dockerfile (`CMD python -c "import discord; print('Bot is running')"`), добавлен коммитом `7bfb96c`
- [ ] **Добавить pre-commit хуки** — lint (flake8/ruff), форматирование (black) - [ ] **Добавить pre-commit хуки** — lint (flake8/ruff), форматирование (black)
- [ ] **Добавить type hints** повсеместно — не все функции имеют аннотации - [ ] **Добавить type hints** повсеместно — не все функции имеют аннотации (отсутствуют в `utils/pogoda.py`, `utils/news.py`, присутствуют в `utils/cat.py`, `utils/morning_runner.py`)
- [ ] **Интеграционные тесты** — сейчас только unit-тесты с моками - [ ] **Интеграционные тесты** — сейчас только unit-тесты с моками
--- ---
@ -38,4 +47,4 @@
## Замечания ## Замечания
- [ ] `README.md` ссылается на `AGENTS.md` как основной документ проекта, но AGENTS.md — инструкции для AI-ассистента - [ ] `README.md` ссылается на `AGENTS.md` как основной документ проекта, но AGENTS.md — инструкции для AI-ассистента
- [ ] В тестовых файлах используется `asyncio.run()` внутри синхронных тестов — может конфликтовать с event loop - [ ] В тестовых файлах используется `asyncio.run()` внутри синхронных тестов — может конфликтовать с event loop (подтверждено: 50+ вхождений в `test_fetch_cat.py`, `test_fetch_rss.py`, `test_fetch_weather.py`)

View File

@ -2,10 +2,6 @@
Discord-бот для Магнитогорска. Команды погоды, новостей, котиков и утреннего дайджеста. Discord-бот для Магнитогорска. Команды погоды, новостей, котиков и утреннего дайджеста.
## 📖 Документация
Основной документ проекта — [`AGENTS.md`](./AGENTS.md). Содержит детальные инструкции по архитектуре, командам и работе с ботом.
## Установка ## Установка
```bash ```bash
@ -39,8 +35,8 @@ python bot.py
| Команда | Описание | | Команда | Описание |
|---------|----------| |---------|----------|
| `!pg` | Прогноз погоды для Магнитогорска | | `!pg` | Прогноз погоды для Магнитогорска |
| `!nw` | Топ-5 свежих статей по AI с Habr | | `!nw` | Топ-5 статей и топ-5 новостей по AI с Habr |
| `!hp` | Список всех команд бота с описанием | | `!hp` | Список всех команд бота с описанием (автогенерация из `bot.commands`) |
| `!morning` | Погода + топ-5 статей + топ-5 новостей + котик (утренний дайджест) | | `!morning` | Погода + топ-5 статей + топ-5 новостей + котик (утренний дайджест) |
| `!cat` | Случайный котик | | `!cat` | Случайный котик |
| `!msg <текст>` | Повторить текст в чате | | `!msg <текст>` | Повторить текст в чате |
@ -53,19 +49,22 @@ python bot.py
| 2 | `pogoda` | Прогноз погоды для Магнитогорска | | 2 | `pogoda` | Прогноз погоды для Магнитогорска |
| 3 | `morning` | Погода + топ-5 статей + топ-5 новостей + котик | | 3 | `morning` | Погода + топ-5 статей + топ-5 новостей + котик |
| 4 | `cat` | Вывести URL случайного котика | | 4 | `cat` | Вывести URL случайного котика |
| 5 | `help` | Показать список всех команд |
| 0 | `stop` | Остановка бота | | 0 | `stop` | Остановка бота |
> Номера команд генерируются автоматически из `ALL_CONSOLE_COMMANDS` в порядке определения в `console_commands/__init__.py`.
## Архитектура ## Архитектура
``` ```
bot.py # Точка входа, инициализация бота, console_input() bot.py # Точка входа, инициализация бота, console_input()
commands/ # Discord команды (cogs) commands/ # Discord команды (cogs)
__init__.py # ALL_COMMANDS — явные импорты __init__.py # ALL_COMMANDS — явные импорты
pg.py # !pg — погода с retry + fallback pg.py # !pg — погода (обёртка над utils.pogoda)
news.py # !nw — новости с Habr news.py # !nw — статьи + новости с Habr
cat.py # !cat — случайный котик cat.py # !cat — случайный котик
morning.py # !morning — утренний дайджест morning.py # !morning — утренний дайджест (обёртка над utils.morning_runner)
help.py # !hp — список команд help.py # !hp — список команд (автогенерация из bot.commands)
console_commands/ # Консольные команды console_commands/ # Консольные команды
__init__.py # ALL_CONSOLE_COMMANDS — явные импорты __init__.py # ALL_CONSOLE_COMMANDS — явные импорты
stop.py # stop — остановка бота stop.py # stop — остановка бота
@ -73,23 +72,29 @@ console_commands/ # Консольные команды
pogoda.py # pogoda — погода в терминале pogoda.py # pogoda — погода в терминале
morning.py # morning — утренний дайджест в терминале morning.py # morning — утренний дайджест в терминале
cat.py # cat — вывод URL котика cat.py # cat — вывод URL котика
help.py # help — список всех команд
utils/ # Утилиты (API-клиенты, конвертации) utils/ # Утилиты (API-клиенты, конвертации)
pogoda.py # fetch_weather(), fetch_open_meteo(), wmo_to_russian(), translate_weather(), pressure_to_mmhg(), format_weather_data_for_console() __init__.py # __all__ — публичный API утилит
news.py # fetch_rss(), format_articles(), truncate_title(), parse_date() pogoda.py # fetch_weather(), fetch_open_meteo(), wmo_to_russian(), translate_weather(), pressure_to_mmhg(), format_weather_data_for_console(), format_weather_for_embed()
news.py # fetch_rss(), format_articles(), truncate_title()
cat.py # fetch_cat() cat.py # fetch_cat()
morning_runner.py # Scheduler, MorningData, gather_morning(), run_morning()
tests/ # pytest-тесты tests/ # pytest-тесты
test_pogoda.py # translate_weather, pressure_to_mmhg, wmo_to_russian, format_weather_data_for_console test_pogoda.py # translate_weather, pressure_to_mmhg, wmo_to_russian, format_weather_data_for_console
test_fetch_cat.py # fetch_cat test_fetch_cat.py # fetch_cat
test_fetch_rss.py # fetch_rss test_fetch_rss.py # fetch_rss
test_fetch_weather.py # fetch_weather, fetch_open_meteo test_fetch_weather.py # fetch_weather, fetch_open_meteo
test_format_articles.py # truncate_title, parse_date, format_articles test_format_articles.py # truncate_title, _parse_date, format_articles
test_commands_pg.py # Pg cog test_commands_pg.py # Pg cog
test_bot.py # инициализация бота test_bot.py # инициализация бота
test_morning_runner.py# тесты morning runner-а test_morning_runner.py# тесты morning runner-а
test_help_discord.py # команда !hp — проверка формата вывода и контента (2 теста) test_help_discord.py # команда !hp — проверка формата вывода и контента
test_help_console.py # консольная help — проверка списка команд (2 теста) test_help_console.py # консольная help — проверка списка команд
ISSUES.md # Задачи и баг-трекер проекта ISSUES.md # Задачи и баг-трекер проекта
pytest.ini # Конфигурация pytest pytest.ini # Конфигурация pytest (asyncio_mode = auto)
Dockerfile # Сборка образа бота (Python 3.14-slim, healthcheck)
docker-compose.yml # Запуск бота в Docker
.dockerignore # Исключения для Docker-контекста
``` ```
### Добавление Discord команды ### Добавление Discord команды
@ -118,12 +123,35 @@ python -m pytest tests/ -v
| `test_fetch_cat.py` | `fetch_cat()` | 10 | | `test_fetch_cat.py` | `fetch_cat()` | 10 |
| `test_fetch_rss.py` | `fetch_rss()` | 20 | | `test_fetch_rss.py` | `fetch_rss()` | 20 |
| `test_fetch_weather.py` | `fetch_weather()`, `fetch_open_meteo()` | 20 | | `test_fetch_weather.py` | `fetch_weather()`, `fetch_open_meteo()` | 20 |
| `test_format_articles.py` | `truncate_title()`, `parse_date()`, `format_articles()` | 24 | | `test_format_articles.py` | `truncate_title()`, `_parse_date()`, `format_articles()` | 24 |
| `test_commands_pg.py` | `Pg` cog, команда `!pg` | 13 | | `test_commands_pg.py` | `Pg` cog, команда `!pg` | 13 |
| `test_bot.py` | инициализация бота | 7 | | `test_bot.py` | инициализация бота | 7 |
| `test_morning_runner.py` | morning runner-а | 68 | | `test_morning_runner.py` | morning runner-а | 68 |
| `test_help_discord.py` | команда `!hp` | 2 |
| `test_help_console.py` | консольная `help` | 2 |
**Итого: 262 теста (4 новых для help).** **Итого: 204 теста.**
## Запуск в Docker
### Сборка и запуск
```bash
docker-compose up --build
```
Передайте токен через переменную окружения:
```bash
DISCORD_TOKEN=ваш_токен docker-compose up
```
### Особенности
- База: `python:3.14-slim`
- Healthcheck: проверка каждые 30 сек (старт-период 60 сек)
- Консольный ввод отключён в Docker (stdin недоступен)
- Версия Python настраивается через `ARG PYTHON_VERSION`
## API и внешние сервисы ## API и внешние сервисы
@ -200,7 +228,7 @@ pytest-asyncio>=0.21.0
Заголовки обрезаются до 60 символов с суффиксом `...`. Заголовки обрезаются до 60 символов с суффиксом `...`.
Ссылки отображаются без `<>` и `https://` префикса для предотвращения embed-превью в Discord. Ссылки отображаются в виде `<url>` для предотвращения embed-превью в Discord.
## Основные функции утилит ## Основные функции утилит
@ -214,13 +242,13 @@ pytest-asyncio>=0.21.0
| `translate_weather()` | Перевод погодных описаний на русский язык | | `translate_weather()` | Перевод погодных описаний на русский язык |
| `pressure_to_mmhg()` | Конвертация давления из hPa в мм рт. ст. | | `pressure_to_mmhg()` | Конвертация давления из hPa в мм рт. ст. |
| `format_weather_data_for_console()` | Форматирование данных погоды для вывода в консоль | | `format_weather_data_for_console()` | Форматирование данных погоды для вывода в консоль |
| `format_weather_for_embed()` | Форматирование погоды для Discord embed (с заголовком) |
### utils/news.py ### utils/news.py
| Функция | Описание | | Функция | Описание |
|---------|----------| |---------|----------|
| `fetch_rss()` | Получение RSS ленты (статьи или новости) | | `fetch_rss()` | Получение RSS ленты (статьи или новости) |
| `parse_date()` | Парсинг даты из RSS |
| `truncate_title()` | Обрезка заголовка до заданной длины | | `truncate_title()` | Обрезка заголовка до заданной длины |
| `format_articles()` | Форматирование списка статей для вывода | | `format_articles()` | Форматирование списка статей для вывода |
@ -229,3 +257,12 @@ pytest-asyncio>=0.21.0
| Функция | Описание | | Функция | Описание |
|---------|----------| |---------|----------|
| `fetch_cat()` | Получение URL случайного котика | | `fetch_cat()` | Получение URL случайного котика |
### utils/morning_runner.py
| Функция / Класс | Описание |
|----------------|----------|
| `MorningData` | dataclass с полями weather, articles, posts, cat_url |
| `gather_morning()` | Параллельный сбор всех данных для дайджеста |
| `run_morning()` | Формирование и отправка embed в канал Discord |
| `Scheduler` | Планировщик ежедневных задач (discord.ext.tasks.loop) |