discordBot/README.md

14 KiB
Raw Blame History

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 Количество серверов, каналов, пользователей

Архитектура

bot.py                  # Точка входа, инициализация бота, команда !msg (в BotRunner._setup_events)
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 — серверы, каналы, пользователи
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_logger.py        # setup_logging — уровни, обработчики, формат
  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

Запуск тестов

python -m pytest tests/ -v

Структура тестов

Файл Что тестирует Кол-во
test_pogoda.py translate_weather(), pressure_to_mmhg(), wmo_to_russian(), format_weather_data_for_console() 27
test_fetch_cat.py fetch_cat() 10
test_fetch_rss.py fetch_rss() 21
test_fetch_weather.py fetch_weather(), fetch_open_meteo() 19
test_format_articles.py truncate_title(), _parse_date(), format_articles() 20
test_commands_pg.py Pg cog, команда !pg 13
test_bot.py инициализация бота 5
test_morning_runner.py morning runner 12
test_help_discord.py команда !hp 2
test_logger.py setup_logging (уровни, обработчики, формат) 9
test_rate_limiter.py RateLimiter (токен-бакет) 5
test_commands_status.py команда !status (embed, uptime) 6
test_commands_stats.py команда !stats (серверы, каналы) 5
Итого: 154 теста.

Запуск в Docker

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

docker-compose up --build

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

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

Особенности

  • База: python:3.14-slim
  • Healthcheck: проверка каждые 30 сек (старт-период 60 сек)
  • Версия 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
LOG_LEVEL Уровень логирования .env, по умолчанию INFO (DEBUG, WARNING, ERROR, CRITICAL)

Логирование

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

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

Зависимости

Production (requirements.txt)

discord.py>=2.3.2
python-dotenv>=1.0.0
requests>=2.31.0

Development (requirements-dev.txt)

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 Планировщик ежедневных задач (asyncio.sleep до целевого времени)

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)