Add publication settings for clubs and sessions, read-only public club/session pages, dashboard controls, privacy-focused public queries, docs, and tests.
Bump version to 3.3.0
PR #106 extracted DeleteSessionHandler and ListSessionsHandler to GmRelay.Shared,
but forgot to register the shared implementations in Program.cs. This caused
an InvalidOperationException at startup on Native AOT builds because the Bot
wrappers could not resolve their shared dependencies.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Добавлены миграции V024 (backfill + deprecation comments + calendar_subscriptions platform identity) и V025 (backfill proposed_by_external_user_id)
- Все Bot handlers переведены с telegram_id/chat_id на platform + external_*
- Shared handlers очищены от COALESCE fallback с telegram_* колонками
- DiscordBot очищен от COALESCE fallback
- Web SessionService и CalendarSubscriptionService переведены на external_*
- HandleRsvpHandler: убран legacy UNION с gm_telegram_id, теперь только group_managers
- RescheduleVotingFinalizer: переведен на external_username/external_user_id
- Tests: добавлены asserts для V024/V025
- Версия обновлена до 3.1.0
Bump version → 3.1.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
V023 migration drops NOT NULL constraints on:
- game_groups.telegram_chat_id
- game_groups.gm_telegram_id
- players.telegram_id
This allows Discord (and future platforms) to create players and
game_groups without legacy Telegram identifiers.
Bump version → 3.0.10
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a Discord user linked Telegram via the Telegram Login Widget,
LinkIdentityAsync incorrectly made Discord primary and Telegram
secondary. This broke access to all Telegram groups/sessions because
ResolveEffectivePlayerIdAsync returned the (empty) Discord primary.
- In /auth/telegram callback, swap LinkIdentityAsync args so Telegram
is always treated as the current (primary) account.
- Add V022 migration to reverse any existing incorrectly-oriented
player_links where Discord is primary and Telegram is secondary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add V020 migration: player_links + identity_audit_log tables
- Add ISessionStore methods: ResolveEffectivePlayerId, LinkIdentity, UnlinkIdentity, GetLinkedIdentities
- Update SessionService to resolve effective player id for all permission checks
- Add /auth/discord/callback linking flow when already authenticated
- Add /api/me/identities GET/DELETE endpoints
- Add Profile.razor page for managing linked accounts
- Update NavMenu with profile link and v3.0.0 badge
- Bump version to 3.0.0 across all files
Bump version → 3.0.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add V019 migration: rename session_audit_log.actor_telegram_id → actor_external_user_id
- Add CSRF protection to Discord OAuth flow (state cookie with HttpOnly/Secure/Strict)
- Add Discord OAuth env vars to compose.yaml, deploy.yml, and .env.example
- Fix SQL COALESCE for nullable telegram_id in GetGroupManagersAsync and GetSessionParticipantsAsync
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both Telegram and Discord deadline services were querying ALL due
proposals without filtering by source_platform. If the Telegram
service reached a Discord proposal first, it finalized the DB state
but skipped message handling. The Discord service then saw status
!= 'Voting' and never updated the Discord vote message.
Fix: GetDueProposalIdsAsync now accepts a sourcePlatform parameter
and filters at the DB level. Each service only processes its own
platform's proposals.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ensures Telegram-initiated reschedule proposals are tagged with
source_platform so the platform-neutral finalizer can distinguish
them from Discord proposals.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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
Convert join/leave interaction commands to PlatformUser, PlatformGroup, and PlatformMessageRef. Persist and look up participants by platform identity while keeping Telegram callbacks intact. Add V017 migration and TDD coverage. Bump version to 2.1.1.
Introduce platform-neutral PlatformKind, PlatformUser, PlatformGroup, and IPlatformMessenger contracts in GmRelay.Shared.
Route Telegram session schedule updates, direct notifications, interaction replies, and calendar export through TelegramPlatformMessenger while preserving existing Telegram behavior.
Bump version -> 2.0.1
- Install wget in Web Dockerfile for compose healthcheck
- Ensure HttpListener response is always closed in BotHealthCheckHostedService
- Use ephemeral port in Bot health check test to avoid port conflicts
- Rename NpgsqlHealthCheck test to reflect actual behavior
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Web: add /health endpoint with PostgreSQL readiness check (returns 200+JSON or 503)
- Web: add /alive endpoint for liveness probe
- Bot: add BotHealthCheckHostedService serving /health on port 8081 via HttpListener
- Bot: expose port 8081 in Dockerfile and install wget for healthcheck
- compose.yaml: add healthcheck sections for bot and web services
- tests: add TDD tests for both health endpoints
Bump version -> 1.16.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add db-backup service to compose.yaml (postgres:17-alpine + cron)
- Add pgbackups volume for backup storage
- Add scripts/restore.sh for manual restore from latest backup
- Update .env.example with BACKUP_RETENTION_DAYS and BACKUP_VOLUME_NAME
- Document backup/restore flow in README
Bump version -> 1.15.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Enable NuGet lock files so Trivy has dependency targets, fail PR checks when no lock files or language-specific files are detected, and let the installer fetch the latest Trivy release.
Route new schedules to an existing forum topic when /newsession is sent inside one, create bot-owned topics only from the forum root, and keep group notifications/dashboard updates threaded to the stored topic.
Persist topic ownership so deletion only removes empty bot-created topics, add topic routing tests and smoke coverage, and bump release metadata to 1.14.0.
- RescheduleVotingDeadlineService: clear confirmation_sent_at +
confirmation_message_id when moving session back to Planned.
- HandleRescheduleTimeInputHandler.RescheduleImmediately: same reset.
- SendConfirmationHandler: add confirmation_sent_at IS NULL guard
to prevent duplicate confirmation messages if DB update fails.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Добавлена абстракция ISystemClock + SystemClock / FakeSystemClock
для тестируемого scheduling.
- Добавлена миграция V014: confirmation_sent_at в sessions.
- Обновлен SendConfirmationHandler: записывает confirmation_sent_at.
- Обновлен SessionSchedulerService:
- выделен ISessionTriggerStore / DbSessionTriggerStore
- SQL-запросы используют параметр @Now вместо now()
- добавлен публичный TickAsync для тестов
- защита от дублей через confirmation_sent_at IS NULL
- Обновлен RescheduleVotingDeadlineService: использует ISystemClock.
- Добавлены интерфейсы ISendConfirmationHandler, ISendOneHourReminderHandler,
ISendJoinLinkHandler для unit-тестируемости.
- Добавлены 8 unit-тестов SessionSchedulerService:
- все 3 триггера (T-24h, T-1h, T-5min)
- идемпотентность при повторном запуске
- ошибки handler не падают и не блокируют другие сессии
- ошибки store логируются без падения worker-а
Bump version -> 1.13.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- SessionBatchDto: добавлено поле JoinLink
- SessionViewItem: добавлено поле JoinLink
- SessionBatchViewBuilder: прокидывание JoinLink из DTO в ViewModel
- CreateSessionHandler, SessionService: обновлены все вызовы конструктора
- TelegramSessionBatchRenderer (Bot + Web): рендеринг ссылки в карточке
- Добавлены тесты на наличие ссылки в рендере
- Все 7 SQL-запросов, загружающих SessionBatchDto, обновлены с join_link AS JoinLink
- Бамп версии до 1.11.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>