39132be4e8
PR Checks / test-and-build (pull_request) Successful in 6m6s
Move neutral join/leave handlers into GmRelay.Shared so Telegram and Discord share capacity, waitlist, duplicate-click, and schedule-update behavior. Add Discord component routing for join_session and leave_session buttons with deferred ephemeral replies and serialized schedule message updates. Bump version to 2.5.0 and update Discord docs. Refs #29
184 lines
13 KiB
Markdown
184 lines
13 KiB
Markdown
# 🎲 GM-Relay: TTRPG Session Scheduling Bot & Web Dashboard
|
||
|
||
**GM-Relay** — это комплексное решение для Мастеров Подземелий (ГМов), состоящее из высокопроизводительного Telegram-бота и удобного веб-интерфейса. Предназначено для автоматизации записи игроков на сессии, управления расписанием и проведения игр.
|
||
|
||
Проект разработан с упором на производительность, архитектуру Vertical Slice, Native AOT (для бота) и удобство развертывания с использованием .NET Aspire.
|
||
|
||
**Текущая версия:** `v2.5.0`.
|
||
|
||
---
|
||
|
||
## ✨ Key Features
|
||
|
||
### 🤖 Telegram Bot
|
||
- **📅 Создание расписаний (Batch Sessions)**: Создавайте сразу несколько игр одним сообщением изменения (на недельный месяц в перед).
|
||
- **🖼 Обложки расписаний**: И batch-посту можно прикрепить фото к `/newsession` или указать строку `Картинка: https://...`; бот отправит обложку перед сообщением записи.
|
||
- **⚡ Быстрые повторы расписания**: Для регулярной кампании можно указать одну дату, количество игр и интервал, а бот сам развернёт повторяющийся batch.
|
||
- **✋ Интерактивная запись и выход**: Игроки записываются на конкретные даты и самостоятельно снимают запись нажатием одной кнопки.
|
||
- **👥 Лимит мест и лист ожидания**: ГМ задаёт максимальный состав, бот не переполняет сессию, автоматически ведёт очередь ожидания и освобождённое место отдаёт первому ожидающему.
|
||
- **📁 Поддержка Форумов (Telegram Topics)**: Если `/newsession` запущен в теме форума Telegram, расписание и групповые уведомления остаются в этой теме; при запуске из корня форума бот создает отдельную тему и сообщает о необходимости прав admin/Manage Topics, если их не хватает.
|
||
- **❌ Управление сессиями**: Owner и назначенные co-GM могут создавать, отменять, удалять и переносить игры из Telegram через `/listsessions`; публичный пост записи показывает только кнопки игроков.
|
||
- **🔄 Голосование за перенос**: Быстрый поиск свободного места с через свободное недель и кнопками новых времени и дедлайном.
|
||
- **🔔 Уведомления**: Игрок получают за 24 часа, напоминание за 1 час, ссылку перед игрой, отмены и переносы; групповые уведомления при этом остаются.
|
||
- **🕐 Режим уведомлений batch**: Для каждой пачки можно выбрать `В группе и в личку` или `Только в группе`.
|
||
- **⬆️ Управление очередью**: Веб-интерфейс показывает заполненность, лист ожидания и позволяет ГМу поднять первого игрока из очереди.
|
||
- **🔄 Автоматическая синхронизация**: Любые изменения в веб-интерфейсе мгновенно обновляют сообщения с расписанием в Telegram-чатах игроков.
|
||
|
||
### Discord Bot
|
||
- **Slash-команды расписания**: GM создаёт сессию через `/newsession` и публикует актуальное расписание через `/listsessions`.
|
||
- **Кнопки записи и выхода**: игроки нажимают Join/Leave в Discord-сообщении; бот отвечает ephemeral-сообщением и обновляет schedule message.
|
||
- **Лимиты и waitlist**: при заполненном составе игрок попадает в waitlist, а при выходе участника первый ожидающий автоматически продвигается в основной состав.
|
||
|
||
### 🌐 Web Dashboard (Blazor Server)
|
||
- **🔐 Авторизация через Telegram**: Telegram Login Widget с HMAC-SHA256 валидацией.
|
||
- **📱 Telegram Mini App Dashboard**: Мобильная панель открывается из Telegram, проверяет `initData` на сервере, учитывает safe-area телефона и верхнюю панель Telegram.
|
||
- **✏️ Редактирование**: Детальное изменение дат, названий и статусов сессий.
|
||
- **🤝 Co-GM и делегирование**: Owner назначает помощников по Telegram ID; co-GM управляет расписанием, но **не может назначать других co-GM**.
|
||
- **📋 Шаблоны кампаний**: Вкладка `Шаблоны` отдельно от страницы группы: сохранение типовых параметров и запуск нового batch из шаблона.
|
||
- **📦 Bulk-операции для Batch Sessions**:
|
||
- обновить общий `title`/`link` у всей пачки;
|
||
- перенести пачку на фиксированный шаг в днях;
|
||
- клонировать batch на следующую неделю или месяц.
|
||
- **⬆️ Управление очередью**: Заполненность, лист ожидания и ручное повышение игрока из очереди.
|
||
- **📜 История изменений сессий**: Страница `/session/{id}/history` показывает аудит-лог всех значимых изменений (время, ссылка, название, участники, статус) с указанием акторов и дат.
|
||
- **📊 Статистика посещаемости**: Страница `/group/{id}/stats` показывает долю присутствия, количество пропусков и среднюю явку по каждому игроку группы.
|
||
- **🔄 Автосинхронизация**: Изменения в вебе мгновенно перерисовывают Telegram-сообщения расписания.
|
||
|
||
---
|
||
|
||
## 🛠 Технологический стек
|
||
|
||
| Компонент | Технология |
|
||
|---|---|
|
||
| Язык | C# 14 (.NET 10) |
|
||
| Архитектура | Vertical Slice + общая библиотека `GmRelay.Shared` |
|
||
| Боты | Telegram.Bot (**Native AOT**), NetCord Gateway (Discord worker) |
|
||
| Веб | Blazor Server |
|
||
| Оркестрация | .NET Aspire (`GmRelay.AppHost`) |
|
||
| БД | PostgreSQL |
|
||
| ORM | Dapper + **Dapper.AOT** (source generators) |
|
||
| Миграции | DbUp |
|
||
| Развёртывание | Docker Compose, Multi-arch (**AMD64/ARM64**) |
|
||
|
||
> [!NOTE]
|
||
> При использовании Dapper в режиме Native AOT все SQL-запросы используют строго типизированные DTO; динамические типы (`dynamic`) не поддерживаются.
|
||
|
||
---
|
||
|
||
## 🚀 Быстрый старт (Docker Compose)
|
||
|
||
**Требования:** Docker и Docker Compose.
|
||
|
||
### 1. Настройка окружения
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
**Ключевые переменные `.env`:**
|
||
```env
|
||
# Токен от @BotFather (используется ботом и как секретный ключ веб-авторизации)
|
||
TELEGRAM_BOT_TOKEN=ваш_токен_здесь
|
||
|
||
# Токен Discord application bot
|
||
DISCORD_BOT_TOKEN=ваш_discord_токен_здесь
|
||
|
||
# Имя бота без @ (для Telegram Login Widget)
|
||
TELEGRAM_BOT_USERNAME=ваше_имя_бота_здесь
|
||
|
||
# HTTPS URL Mini App, например https://your-domain.example/miniapp
|
||
TELEGRAM_MINI_APP_URL=https://your-domain.example/miniapp
|
||
|
||
POSTGRES_PASSWORD=ваш_надежный_пароль
|
||
GMRELAY_WEB_PORT=8080
|
||
```
|
||
|
||
**Настройка в @BotFather:**
|
||
- Команда `/setdomain` для работы виджета авторизации на вашем домене.
|
||
- Для Mini App настройте домен Web Dashboard и menu button на URL из `TELEGRAM_MINI_APP_URL`.
|
||
- Начиная с **v1.9.3** дополнительных действий для фикса входа не требуется: fallback выполняется внутри активного Telegram WebView по тому же HTTPS-адресу `/miniapp`.
|
||
|
||
### 2. Запуск
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
**Автоматически выполняется:**
|
||
- создание Docker-сети и volume PostgreSQL;
|
||
- подъём PostgreSQL (`db:5432`);
|
||
- запуск бота с плавной миграцией (DbUp);
|
||
- запуск отдельного Discord Gateway worker на NetCord;
|
||
- запуск веб-приложения с подключением к БД и Telegram API.
|
||
|
||
### 3. Первоначальная настройка
|
||
1. Напишите боту `/start`.
|
||
2. Создайте группу через `/newgroup`.
|
||
3. Откройте Mini App или Web Dashboard для расширенного управления.
|
||
4. Для Discord пригласите application bot на сервер с правами `bot` и `applications.commands`.
|
||
5. В Discord создайте сессию через `/newsession` или опубликуйте расписание через `/listsessions`; игроки записываются и выходят кнопками в опубликованном сообщении.
|
||
|
||
## 💾 Backup и восстановление
|
||
|
||
Проект включает автоматический ежедневный backup PostgreSQL через сервис `db-backup` в Docker Compose.
|
||
|
||
### Как это работает
|
||
- **Каждый день в 03:00** выполняется `pg_dump` базы `gmrelay_db`.
|
||
- Дампы сжимаются (`gzip`) и сохраняются в volume `pgbackups` (`/backups`).
|
||
- Формат имени: `gmrelay_db_YYYYMMDD_HHMMSS.sql.gz`.
|
||
- Ротация: по умолчанию хранятся последние **7 дней** (настраивается через `BACKUP_RETENTION_DAYS`).
|
||
|
||
### Проверка бэкапов
|
||
```bash
|
||
docker compose exec db-backup ls -la /backups
|
||
```
|
||
|
||
### Ручное создание дампа
|
||
```bash
|
||
docker compose exec db-backup sh -c "pg_dump -h db -U gmrelay -d gmrelay_db | gzip > /backups/gmrelay_db_manual.sql.gz"
|
||
```
|
||
|
||
### Восстановление из бэкапа
|
||
```bash
|
||
# Использовать последний автоматический бэкап
|
||
./scripts/restore.sh
|
||
|
||
# Или указать конкретный файл
|
||
./scripts/restore.sh backups/gmrelay_db_20260512_030000.sql.gz
|
||
```
|
||
|
||
> [!WARNING]
|
||
> Восстановление **перезаписывает текущую базу данных**. Убедитесь, что вы понимаете последствия, прежде чем запускать `restore.sh`.
|
||
|
||
### Переменные окружения (опциональные)
|
||
```env
|
||
BACKUP_RETENTION_DAYS=7
|
||
BACKUP_VOLUME_NAME=game_pgbackups
|
||
```
|
||
|
||
---
|
||
|
||
## 🗂 Структура репозитория
|
||
|
||
```
|
||
├── src/
|
||
│ ├── GmRelay.AppHost/ # .NET Aspire orchestrator
|
||
│ ├── GmRelay.Bot/ # Telegram-бот (Native AOT)
|
||
│ ├── GmRelay.DiscordBot/ # Discord Gateway worker на NetCord
|
||
│ ├── GmRelay.ServiceDefaults/ # Aspire service defaults
|
||
│ ├── GmRelay.Shared/ # Общие доменные модели
|
||
│ └── GmRelay.Web/ # Blazor Server dashboard
|
||
├── tests/
|
||
│ └── GmRelay.Bot.Tests/ # xUnit + NSubstitute
|
||
├── compose.yaml # Docker Compose (AMD64 + ARM64)
|
||
└── .env.example # Шаблон переменных окружения
|
||
```
|
||
|
||
---
|
||
|
||
## 📜 Лицензия
|
||
|
||
MIT License. См. [LICENSE](./LICENSE).
|
||
|
||
---
|
||
|
||
*Построено с ❤️ для TTRPG-сообщества.*
|