Skip to content

Unseen-social-network/MaxHub

Repository files navigation

MaxHub

Бот для мессенджера MAX (dev.max.ru), «всё в одном»: совместные списки дел, «Слово дня», конвертер изображений и рецензии на напитки.

Описание бота (используется при регистрации у MasterBot): «Совместные списки дел, "Слово дня" и конвертер файлов — всё в одном боте. /help — список команд.»

Функции

  1. Совместный список дел — общий todo-лист на чат:

    • /todo add <текст> — добавить дело
    • /todo list — показать список (нумерованный, с инлайн-кнопками ✅/🗑)
    • /todo done <n> — отметить дело выполненным
    • /todo del <n> — удалить дело
  2. «Слово дня» — ежедневная рассылка слова с определением и примером:

    • /word — показать слово дня прямо сейчас
    • /word sub — подписать чат на ежедневную рассылку (09:00 по времени из TZ)
    • /word unsub — отписать чат
  3. Конвертер изображений — пришлите картинку, бот предложит кнопками формат (PNG/JPG/WEBP/PDF) и вернёт файл. Лимит входного файла — 20 МБ.

  4. Рецензии на напитки — общий журнал впечатлений о напитках на чат:

    • /drink add — пошаговое добавление (категория → название → описание → оценка → фаворит)
    • /drink list — список с фильтром по категории/только избранное и сортировкой (дата/оценка/название/фаворит)
    • /drink search <текст> — поиск по названию, описанию и категории
    • /drink fav — только избранные
    • /drink del <id> — удалить рецензию
    • /drink help — краткая справка

    Категория вводится пользователем свободным текстом — никаких предустановленных категорий нет. Значение нормализуется (обрезаются пробелы, приводится к нижнему регистру для сравнения и дедупликации), в фильтрах показываются только категории, которые реально встречаются среди рецензий чата.

  5. Админ-рассылка (/broadcast, только для ADMIN_IDS) — мастеровый сценарий: текст → превью с числом активных получателей → подтверждение → фоновая рассылка в личку всем, кто писал боту за последние BROADCAST_ACTIVE_DAYS дней.

  6. /v (только для ADMIN_IDS) — версия, git sha и время сборки текущего деплоя.

  7. Mini App (/app) — веб-версия части механик: список дел (чтение и добавление), слово дня с подпиской, рецензии на напитки (список, добавление, поиск, фильтр по категориям и сортировка), рассылка для админов. Открывается кнопкой прямо в чате, работает на отдельном пути MINIAPP_PATH того же домена.

  8. /start, /help — список команд.

Стек

Python 3.12+ / uv, библиотека maxapi, FastAPI + uvicorn (webhook), PostgreSQL 16 + SQLAlchemy 2.x (async) + Alembic, APScheduler, Docker + Caddy (авто-HTTPS). Подробности — в CLAUDE.md.

Локальная разработка

make install                # uv sync
cp .env.example .env        # заполните BOT_TOKEN своим токеном для локальных проверок
make migrate                 # alembic upgrade head (нужен запущенный postgres, см. make up-local)
make run                      # MODE=polling, локальный запуск без вебхука

Полезные команды (см. полный список через make help):

make lint && make format      # ruff check + ruff format
make test                      # тесты (нужен запущенный postgres)
make up-local                   # docker compose -f docker/docker-compose.local.yml up --build (bot + postgres)
make migration m="..."           # новая миграция

Получение токена бота

  1. Откройте MasterBot в MAX и создайте нового бота.
  2. Укажите имя MaxHub и описание: «Совместные списки дел, "Слово дня" и конвертер файлов — всё в одном боте. /help — список команд.»
  3. Сохраните выданный токен — он понадобится как BOT_TOKEN в .env на сервере.

Бот при старте дополнительно синхронизирует имя и описание через API (best-effort, метод не входит в официальную swagger-спецификацию MAX — при ошибке синхронизации бот просто логирует предупреждение и продолжает работать).

Настройка сервера

Требования: Docker + плагин docker compose, доменное имя с A-записью, указывающей на сервер, открытые порты 80 и 443.

По умолчанию используется каталог /opt/maxbot — при необходимости его можно изменить через секрет DEPLOY_PATH (см. раздел «GitHub Secrets» ниже).

sudo mkdir -p /opt/maxbot
cd /opt/maxbot

Создайте /opt/maxbot/.env:

BOT_TOKEN=...                # токен от MasterBot
MAX_API_URL=https://platform-api2.max.ru  # хост API MAX, менять только если MAX попросит перейти на другой
DOMAIN=bot.example.com       # ваш домен
WEBHOOK_PATH=/webhook
MINIAPP_PATH=/miniapp
PORT=8080
MODE=webhook
ADMIN_IDS=111111,222222      # user_id админов через запятую
DATABASE_URL=postgresql+asyncpg://maxhub:${POSTGRES_PASSWORD}@postgres:5432/maxhub
TZ=Europe/Moscow
BROADCAST_ACTIVE_DAYS=30
POSTGRES_PASSWORD=...        # придумайте пароль для Postgres
IMAGE_TAG=latest

docker-compose.prod.nginx.yml не нужно копировать вручную — workflow deploy.yml сам обновляет его на сервере при каждом релизе (см. «Процесс релиза» ниже) и запускает docker compose -f docker-compose.prod.nginx.yml pull && up -d. Для самой первой раскатки до первого тега можно скопировать файл и поднять стек вручную:

docker compose -f docker-compose.prod.nginx.yml pull
docker compose -f docker-compose.prod.nginx.yml up -d

Бот слушает только 127.0.0.1:${PORT} — HTTPS и сертификат Let's Encrypt обслуживает уже работающий на хосте nginx (см. docker/nginx/README.md), а не Caddy. Бот при старте сам подпишется на вебхук https://{DOMAIN}{WEBHOOK_PATH} и применит миграции БД (alembic upgrade head выполняется в entrypoint контейнера перед запуском приложения).

Если порт 443 на сервере свободен и вы хотите, чтобы сертификат получал сам контейнер — используйте docker/docker-compose.prod.caddyfile.yml + docker/Caddyfile вместо nginx-варианта (потребует ручной правки deploy.yml, по умолчанию workflow настроен на nginx-сценарий).

GitHub Secrets (для CI/CD)

В настройках репозитория → Settings → Secrets and variables → Actions добавьте:

Secret Назначение
SSH_HOST адрес сервера для деплоя
SSH_USER пользователь для SSH
SSH_KEY приватный SSH-ключ
SSH_PORT (опционально) порт SSH, если не 22
DEPLOY_PATH (опционально) каталог с .env/docker-compose.prod.nginx.yml на сервере, если не /opt/max-bot

GITHUB_TOKEN для пуша образа в GHCR передаётся автоматически, ничего настраивать не нужно.

Процесс релиза

Пушим тег вида v*:

git tag v1.0.0
git push --tags

Это запускает ci.yml (тесты, линт, docker build). deploy.yml слушает не сам пуш тега, а завершение ci.yml (on: workflow_run) и стартует, только если тот прогон завершился успешно и был именно пушем тега — деплой упавшего/ещё выполняющегося CI невозможен. Дальше он:

  1. Соберёт образ и запушит его в ghcr.io/unseen-social-network/maxhub с тегами {tag} и latest.
  2. Скопирует актуальный docker-compose.prod.nginx.yml из репозитория на сервер (секреты в .env не трогает).
  3. Обновит в .env IMAGE_TAG, APP_VERSION, GIT_SHA, BUILD_TIME — без этого шага /v показывал бы устаревшие значения из .env, а не то, что реально запечено в образ (docker-compose.prod.nginx.yml пробрасывает эти переменные в контейнер через environment:).
  4. Допишет строку в deploy_history.log на сервере (timestamp tag sha) — по нему видно историю релизов и можно вручную откатиться, поставив в .env предыдущий IMAGE_TAG (старые образы остаются в GHCR).
  5. Перезапустит стек: docker compose -f docker-compose.prod.nginx.yml pull && up -d.

Рассылка через /broadcast

  1. Напишите боту в личку из аккаунта, указанного в ADMIN_IDS, команду /broadcast.
  2. Пришлите текст рассылки одним сообщением.
  3. Бот покажет превью текста и число активных получателей (писавших боту в личку за последние BROADCAST_ACTIVE_DAYS дней и не заблокировавших его), с кнопками «✅ Отправить» / «❌ Отменить».
  4. После подтверждения рассылка уходит фоновой задачей строго через общий лимитер (не быстрее 2 сообщений в секунду на получателя, до 30 запросов в секунду суммарно) с промежуточным прогрессом каждые ~50 получателей.
  5. По завершении бот пришлёт итог: сколько отправлено и сколько не доставлено. Пользователи, заблокировавшие бота, автоматически помечаются и исключаются из будущих рассылок.

Каждая рассылка логируется в таблицу broadcasts (админ, текст, время, счётчики отправленных/недоставленных).

Mini App

Веб-версия части механик бота — список дел (просмотр и добавление), слово дня с подпиской, рецензии на напитки (список, добавление, поиск, фильтр по категориям, сортировка) и рассылка для админов. Открывается кнопкой «📱 Открыть приложение» по команде /app прямо внутри чата (MAX-кнопка open_app), работает на пути MINIAPP_PATH (по умолчанию /miniapp) того же домена, что и вебхук — отдельного домена/nginx-конфига не требуется.

Как это работает:

  1. Библиотека MAX Bridge (https://st.max.ru/js/max-web-app.js) подключается на фронтенде и сразу даёт доступ к window.WebApp.initData — подписанной строке с данными о пользователе и чате, откуда открыли приложение.
  2. Фронтенд передаёт initData на бэкенд в заголовке X-Init-Data с каждым запросом к /miniapp/api/....
  3. Бэкенд (src/bot/miniapp/auth.py) проверяет подпись двухшаговым HMAC_SHA256 (как в Telegram WebApp, формула из dev.max.ru/docs/webapps/validation): secret_key = HMAC_SHA256("WebAppData", BOT_TOKEN), затем hash = HMAC_SHA256(secret_key, launch_params), где launch_params — пары key=value из initData (кроме hash), отсортированные по алфавиту и объединённые через \n. Запрос отклоняется, если подпись не совпадает или auth_date старше часа.
  4. Список дел, слово дня и рецензии на напитки берутся из initData.chat.id — того чата, откуда была открыта кнопка. Рассылка доступна только если initData.user.id есть в ADMIN_IDS.
  5. API рецензий (GET/POST /miniapp/api/drinks, GET /miniapp/api/drinks/categories, PATCH/DELETE /miniapp/api/drinks/{id}) строго скоуплены на chat_id из initData — обратиться к чужой рецензии из другого чата нельзя, попытка возвращает 404.

About

Совместные списки дел, "Слово дня" и конвертер файлов — всё в одном боте. /help — список команд.

Resources

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages