Discord Bot

Discord-бот для Магнитогорска. Команды погоды, новостей, котиков и утреннего дайджеста.

Установка

pip install -r requirements.txt

Запуск

python bot.py

Введите номер команды в терминале или !команда в Discord.

Настройка

  1. Скопируйте .env.example в .env:

    cp .env.example .env
    
  2. Вставьте токен бота в .env:

    DISCORD_TOKEN=ваш_токен
    

Токен получите на Discord Developer Portal.

Команды Discord

Команда Описание
!pg Прогноз погоды для Магнитогорска
!nw Топ-5 статей и топ-5 новостей по AI с Habr
!hp Список всех команд бота с описанием (автогенерация из bot.commands)
!morning Погода + топ-5 статей + топ-5 новостей + котик (утренний дайджест)
!cat Случайный котик
!msg <текст> Повторить текст в чате
!status Статус бота: пинг к Discord gateway, uptime
!stats Количество серверов, каналов, пользователей

Команды терминала

Номер Команда Описание
1 news Топ-5 статей + топ-5 новостей с Habr
2 pogoda Прогноз погоды для Магнитогорска
3 morning Погода + топ-5 статей + топ-5 новостей + котик
4 cat Вывести URL случайного котика
5 help Показать список всех команд
6 status Статус бота: пинг и uptime
7 stats Количество серверов, каналов, пользователей
8 logs Последние строки лога (tail -20)
9 reload Горячая перезагрузка всех cogs
10 trigger morning Ручной запуск morning-дайджеста
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)
  status.py             # !status — статус бота: пинг, uptime
  stats.py              # !stats — серверы, каналы, пользователи
console_commands/       # Консольные команды
  __init__.py           # ALL_CONSOLE_COMMANDS — явные импорты
  admin.py              # admin — CLI для docker exec (pogoda, news, cat, morning, help)
  stop.py               # stop — остановка бота
  news.py               # news — новости с Habr
  pogoda.py             # pogoda — погода в терминале
  morning.py            # morning — утренний дайджест в терминале
  cat.py                # cat — вывод URL котика
  help.py               # help — список всех команд
  status.py             # status — статус бота в терминале
  stats.py              # stats — статистика серверов в терминале
  logs.py               # logs — последние строки лога (tail)
  reload.py             # reload — горячая перезагрузка cogs
  trigger_morning.py    # trigger morning — ручной запуск morning-дайджеста
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()
  logger.py             # setup_logging() — консоль + файл с ротацией по размеру
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 — проверка списка команд
  test_logger.py        # setup_logging — уровни, обработчики, формат
  test_admin.py         # admin.py — CLI-скрипт для docker exec
  test_commands_status.py # команда !status — embed и uptime
  test_commands_stats.py  # команда !stats — подсчёт серверов и каналов
ISSUES.md               # Задачи и баг-трекер проекта
pytest.ini              # Конфигурация pytest (asyncio_mode = auto)
Dockerfile              # Сборка образа бота (Python 3.14-slim, healthcheck)
docker-compose.yml      # Запуск бота в Docker
.dockerignore           # Исключения для Docker-контекста

Добавление Discord команды

  1. Создать файл commands/имя.py с классом, наследующим commands.Cog
  2. Добавить импорт в commands/__init__.py
  3. Добавить класс в ALL_COMMANDS

Добавление консольной команды

  1. Создать файл console_commands/имя.py с функцией func(stop_event, bot)
  2. Добавить импорт в console_commands/__init__.py
  3. Добавить функцию в 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_logger.py setup_logging (уровни, обработчики, формат) 9
test_rate_limiter.py RateLimiter (токен-бакет) 5
test_admin.py admin.py — CLI для docker exec 5
test_commands_status.py команда !status (embed, uptime) 6
test_commands_stats.py команда !stats (серверы, каналы) 4
test_console_logs.py команда logs (чтение лога) 4
test_console_reload.py команда reload (перезагрузка cogs) 2
test_console_trigger_morning.py команда trigger morning (запуск дайджеста) 3

Итого: 243 теста.

Запуск в Docker

Сборка и запуск

docker-compose up --build

Передайте токен через переменную окружения:

DISCORD_TOKEN=ваш_токен docker-compose up

Особенности

  • База: python:3.14-slim
  • Healthcheck: проверка каждые 30 сек (старт-период 60 сек)
  • Консольный ввод отключён в Docker (stdin недоступен)
  • Версия Python настраивается через ARG PYTHON_VERSION

Администрирование через docker exec

Для управления ботом из терминала (без Discord-чата) используйте admin.py:

docker exec discord-bot python admin.py pogoda
docker exec discord-bot python admin.py news
docker exec discord-bot python admin.py cat
docker exec discord-bot python admin.py morning
docker exec discord-bot python admin.py logs     # последние строки лога
docker exec discord-bot python admin.py help
docker stop discord-bot                          # остановка бота

Команды reload и trigger morning доступны только через интерактивный терминал бота (требуют запущенного экземпляра бота).

Результат выводится в stdout терминала. Команды используют те же utils, что и Discord-команды.

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

Логирование

При запуске бота автоматически создаётся директория logs/ и файл logs/bot.log.

  • Консоль: все сообщения выводятся в stdout
  • Файл: logs/bot.log с ротацией по размеру
    • maxBytes: 5 МБ — при достижении файл архивируется
    • backupCount: 5 — хранится до 5 бэкапов (bot.log.1bot.log.5)
    • Максимальный объём: ~25 МБ
  • Уровень: настраивается через LOG_LEVEL в .env (по умолчанию INFO)

Консольная команда logs (номер 8) выводит последние 20 строк logs/bot.log.

Зависимости

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
Бот анонсер начала дня. Погода + новости + отельный команды.
Readme 546 KiB
Languages
Python 99.4%
Dockerfile 0.6%