Discord Bot
Discord-бот для Магнитогорска. Команды погоды, новостей, котиков и утреннего дайджеста.
Установка
pip install -r requirements.txt
Запуск
python bot.py
Введите номер команды в терминале или !команда в Discord.
Настройка
-
Скопируйте
.env.exampleв.env:cp .env.example .env -
Вставьте токен бота в
.env:DISCORD_TOKEN=ваш_токен
Токен получите на Discord Developer Portal.
Команды Discord
| Команда | Описание |
|---|---|
!pg |
Прогноз погоды для Магнитогорска |
!nw |
Топ-5 статей и топ-5 новостей по AI с Habr |
!hp |
Список всех команд бота с описанием (автогенерация из bot.commands) |
!morning |
Погода + топ-5 статей + топ-5 новостей + котик (утренний дайджест) |
!cat |
Случайный котик |
!msg <текст> |
Повторить текст в чате |
Команды терминала
| Номер | Команда | Описание |
|---|---|---|
| 1 | news |
Топ-5 статей + топ-5 новостей с Habr |
| 2 | pogoda |
Прогноз погоды для Магнитогорска |
| 3 | morning |
Погода + топ-5 статей + топ-5 новостей + котик |
| 4 | cat |
Вывести URL случайного котика |
| 5 | help |
Показать список всех команд |
| 0 | stop |
Остановка бота |
Номера команд генерируются автоматически из
ALL_CONSOLE_COMMANDSв порядке определения вconsole_commands/__init__.py.
Архитектура
bot.py # Точка входа, инициализация бота, console_input()
commands/ # Discord команды (cogs)
__init__.py # ALL_COMMANDS — явные импорты
pg.py # !pg — погода (обёртка над utils.pogoda)
news.py # !nw — статьи + новости с Habr
cat.py # !cat — случайный котик
morning.py # !morning — утренний дайджест (обёртка над utils.morning_runner)
help.py # !hp — список команд (автогенерация из bot.commands)
console_commands/ # Консольные команды
__init__.py # ALL_CONSOLE_COMMANDS — явные импорты
stop.py # stop — остановка бота
news.py # news — новости с Habr
pogoda.py # pogoda — погода в терминале
morning.py # morning — утренний дайджест в терминале
cat.py # cat — вывод URL котика
help.py # help — список всех команд
utils/ # Утилиты (API-клиенты, конвертации)
__init__.py # __all__ — публичный API утилит
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()
rate_limiter.py # RateLimiter (токен-бакет), cat/weather/meteo/rss лимитеры
morning_runner.py # Scheduler, MorningData, gather_morning(), run_morning()
tests/ # pytest-тесты
test_pogoda.py # translate_weather, pressure_to_mmhg, wmo_to_russian, format_weather_data_for_console
test_fetch_cat.py # fetch_cat
test_fetch_rss.py # fetch_rss
test_fetch_weather.py # fetch_weather, fetch_open_meteo
test_format_articles.py # truncate_title, _parse_date, format_articles
test_commands_pg.py # Pg cog
test_bot.py # инициализация бота
test_morning_runner.py# тесты morning runner-а
test_help_discord.py # команда !hp — проверка формата вывода и контента
test_help_console.py # консольная help — проверка списка команд
ISSUES.md # Задачи и баг-трекер проекта
pytest.ini # Конфигурация pytest (asyncio_mode = auto)
Dockerfile # Сборка образа бота (Python 3.14-slim, healthcheck)
docker-compose.yml # Запуск бота в Docker
.dockerignore # Исключения для Docker-контекста
Добавление Discord команды
- Создать файл
commands/имя.pyс классом, наследующимcommands.Cog - Добавить импорт в
commands/__init__.py - Добавить класс в
ALL_COMMANDS
Добавление консольной команды
- Создать файл
console_commands/имя.pyс функциейfunc(stop_event, bot) - Добавить импорт в
console_commands/__init__.py - Добавить функцию в
ALL_CONSOLE_COMMANDS
Запуск тестов
python -m pytest tests/ -v
Структура тестов
| Файл | Что тестирует | Кол-во |
|---|---|---|
test_pogoda.py |
translate_weather(), pressure_to_mmhg(), wmo_to_russian(), format_weather_data_for_console() |
93 |
test_fetch_cat.py |
fetch_cat() |
10 |
test_fetch_rss.py |
fetch_rss() |
20 |
test_fetch_weather.py |
fetch_weather(), fetch_open_meteo() |
20 |
test_format_articles.py |
truncate_title(), _parse_date(), format_articles() |
24 |
test_commands_pg.py |
Pg cog, команда !pg |
13 |
test_bot.py |
инициализация бота | 7 |
test_morning_runner.py |
morning runner-а | 68 |
test_help_discord.py |
команда !hp |
2 |
test_help_console.py |
консольная help |
2 |
test_rate_limiter.py |
RateLimiter (токен-бакет) |
5 |
Итого: 209 тестов.
Запуск в Docker
Сборка и запуск
docker-compose up --build
Передайте токен через переменную окружения:
DISCORD_TOKEN=ваш_токен docker-compose up
Особенности
- База:
python:3.14-slim - Healthcheck: проверка каждые 30 сек (старт-период 60 сек)
- Консольный ввод отключён в Docker (stdin недоступен)
- Версия Python настраивается через
ARG PYTHON_VERSION
API и внешние сервисы
Погода (!pg, !morning)
- Основной:
wttr.in/Magnitogorsk(бесплатный, без ключа) - Fallback:
api.open-meteo.com(бесплатный, без ключа) - Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
- Fallback срабатывает автоматически при неуспешных попытках
- Rate-limiting: 1 req/sec, burst 3 (wttr.in); 2 req/sec, burst 5 (Open-Meteo). Настраивается через
.env - WMO weather codes → русский перевод в
wmo_to_russian()
Конвертации
- Давление: hPa → мм рт. ст. (
* 0.750062) - Ветер: км/ч → м/с (
/ 3.6) - Погодные описания: английский → русский (
translate_weather())
Новости (!nw, !morning)
- Articles:
https://habr.com/ru/rss/hubs/artificial_intelligence/articles/top/daily/?fl=ru - News:
https://habr.com/ru/rss/hubs/artificial_intelligence/news/top/daily/?fl=ru - Парсинг RSS 2.0 и Atom форматов
- Извлечение ссылок из
<guid isPermaLink="true">и авторов из<dc:creator> - Rate-limiting: 1 req/sec, burst 2. Настраивается через
.env - Формат вывода: заголовок → дата → ссылка
Котики (!cat, !morning)
- API:
https://api.thecatapi.com/v1/images/search - Rate-limiting: 1 req/sec, burst 3. Настраивается через
.env - Картинка встраивается в Discord Embed
Структура данных погоды
Команда !pg возвращает:
Температура: X°C (ощущается как Y°C)
Описание: Z
Влажность: X%
Ветер: X м/с
Давление: X мм рт. ст.
Формат дат
Даты форматируются как дд.мм.гггг через datetime.strptime с форматом %a, %d %b %Y %H:%M:%S %z.
Конфигурация
| Переменная | Описание | Где взять |
|---|---|---|
DISCORD_TOKEN |
Токен бота | Discord Developer Portal |
MORNING_TIME |
Время запуска утреннего дайджеста | .env (формат ЧЧ:ММ, по умолчанию 07:00) |
MORNING_CHANNEL_ID |
ID канала для утреннего дайджеста | Правый клик по каналу → Копировать ID |
CAT_API_RATE |
Rate-limit TheCatAPI (токенов/сек) | .env, по умолчанию 1 |
CAT_API_BURST |
Burst-бакет TheCatAPI | .env, по умолчанию 3 |
WEATHER_API_RATE |
Rate-limit wttr.in (токенов/сек) | .env, по умолчанию 1 |
WEATHER_API_BURST |
Burst-бакет wttr.in | .env, по умолчанию 3 |
OPEN_METEO_API_RATE |
Rate-limit Open-Meteo (токенов/сек) | .env, по умолчанию 2 |
OPEN_METEO_API_BURST |
Burst-бакет Open-Meteo | .env, по умолчанию 5 |
HABR_RSS_RATE |
Rate-limit Habr RSS (токенов/сек) | .env, по умолчанию 1 |
HABR_RSS_BURST |
Burst-бакет Habr RSS | .env, по умолчанию 2 |
Зависимости
discord.py>=2.3.2
python-dotenv>=1.0.0
requests>=2.31.0
pytest>=7.4.0
pytest-asyncio>=0.21.0
Безопасность
.envв.gitignore— токен никогда не должен попадать в репозиторий- Используйте
.env.exampleкак шаблон
Формат новостей
Каждая новость выводится в формате:
Заголовок статьи
дд.мм.гггг https://habr.com/ru/articles/...
Заголовки обрезаются до 60 символов с суффиксом ....
Ссылки отображаются в виде <url> для предотвращения embed-превью в Discord.
Основные функции утилит
utils/pogoda.py
| Функция | Описание |
|---|---|
fetch_weather() |
Основная функция получения погоды с wttr.in |
fetch_open_meteo() |
Fallback при ошибках основного API |
wmo_to_russian() |
Перевод WMO кодов погоды в русское описание |
translate_weather() |
Перевод погодных описаний на русский язык |
pressure_to_mmhg() |
Конвертация давления из hPa в мм рт. ст. |
format_weather_data_for_console() |
Форматирование данных погоды для вывода в консоль |
format_weather_for_embed() |
Форматирование погоды для Discord embed (с заголовком) |
utils/news.py
| Функция | Описание |
|---|---|
fetch_rss() |
Получение RSS ленты (статьи или новости) |
truncate_title() |
Обрезка заголовка до заданной длины |
format_articles() |
Форматирование списка статей для вывода |
utils/cat.py
| Функция | Описание |
|---|---|
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) |
utils/rate_limiter.py
| Функция / Класс | Описание |
|---|---|
RateLimiter |
Токен-бакет: rate (токенов/сек), burst (макс. бакет) |
RateLimiter.acquire() |
Асинхронно ждать освобождения токена перед запросом |
cat_limiter |
Лимитер для TheCatAPI (1/s, burst 3) |
weather_limiter |
Лимитер для wttr.in (1/s, burst 3) |
open_meteo_limiter |
Лимитер для Open-Meteo (2/s, burst 5) |
habr_rss_limiter |
Лимитер для Habr RSS (1/s, burst 2) |
Description
Languages
Python
99.4%
Dockerfile
0.6%