Commit Graph

54 Commits

Author SHA1 Message Date
Toutsu 3c967dc3e3 feat(rendering): display description, system, duration, format, type and location in Telegram game card 2026-06-13 10:55:03 +03:00
Toutsu 014b5edd31 feat(bot): add online/offline wizard locations
PR Checks / test-and-build (pull_request) Successful in 15m52s
Add format and location steps to the Telegram /newsession wizard, persist offline addresses in sessions.location_address, and render online links/offline addresses in schedule messages.

Bump version to 3.10.0.
2026-06-10 11:29:25 +03:00
Toutsu 29f6f6a827 fix(web): include PublicationMode/IsMembersOnly in showcase SQL to fix /showcase 500
PR Checks / test-and-build (pull_request) Successful in 8m17s
Dapper.AOT generated a 19-parameter ctor for ShowcaseSessionRow based on the
SELECT list in GetShowcaseSessionsAsync / GetShowcaseSessionAsync. After
adding PublicationMode and IsMembersOnly to ShowcaseSessionDto in v3.7.0 the
record itself was extended, but the SELECT still returned 19 columns, so the
materializer threw "A parameterless default constructor or one matching
signature (...) is required" and every request to /showcase returned 500.

Add s.publication_mode and (s.publication_mode = 'ClubOnly') to both SELECT
lists and propagate them through the ShowcaseSessionDto construction. The
field list now matches the generated constructor exactly.

Version bump 3.7.0 -> 3.7.1 (patch).
2026-06-03 22:21:31 +03:00
Toutsu 22e9859fdf fix(web): allow cancelling pending applications; drop contradictory message guard
PR Checks / test-and-build (pull_request) Successful in 7m50s
Address review feedback from PR #119:

- LeaveClubMembershipAsync: was rejecting Pending rows because the SQL
  required status = 'Active', so clicking "Отозвать заявку" on a Pending
  membership surfaced a misleading "Active membership X not found"
  InvalidOperationException. Now the method first tries Active -> Left
  and falls back to Pending -> Rejected so the same UI flow covers both
  states.
- PublicClub.razor TrySubmitApplicationAsync: removed the empty-input
  guard that contradicted the "(необязательно)" label and the server
  side (AuthorizedMembershipService already trims and accepts null).

No tests broken (493 still passing), no public-API changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:33:28 +03:00
Toutsu 6cb2fbe610 feat(web): add private club showcases with membership flow (v3.7.0)
PR Checks / test-and-build (pull_request) Successful in 7m28s
Implements Issue #110: game masters can now publish sessions
exclusively to a club's private showcase, gated behind a
member application and approval flow. Adds a 4-state
publication_mode (None/Catalog/ClubOnly/Both) replacing the
binary is_public, plus a club_memberships table with
Pending/Active/Rejected/Left lifecycle and partial unique
index ensuring a single Active row per (group, player).

Highlights
- V030 migration: club_memberships, publication_mode, drop
  is_public, recreate partial indexes, portfolio_games gains
  publication_mode.
- PublicationMode enum + extensions in GmRelay.Shared.
- ISessionStore gains 12 membership/showcase methods;
  AuthorizedMembershipService owns the membership flow with
  GM-only approve/reject authorization.
- PublicClub / PublicMasterProfile / PublicSession: member-
  aware queries (ClubOnly visible only to Active members).
- New pages: MyClubMemberships (/profile/memberships) and
  ClubApplications (/group/{id}/applications).
- GroupDetails and EditSession switch from a bool toggle to
  a 4-state publication_mode selector.
- NavMenu adds Moji kluby, PublicLayout adds Kluby.

Tests: 4 new test files (PublicationMode, ClubMemberships,
AuthorizedMembershipService, ClubShowcaseSource) + updates
to PublicClubPages, AuthorizedSessionService/Portfolio
service FakeSessionStore, CampaignTemplatesNavigation.
493 tests pass.

Bump version 3.6.0 -> 3.7.0 across Directory.Build.props,
compose.yaml, deploy.yml, NavMenu.razor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:09:22 +03:00
Claude 242ff99a83 feat(web): authorize portfolio management and reviews 2026-06-02 15:01:29 +03:00
Toutsu f2c9f34ab4 feat(web): add portfolio persistence 2026-06-02 14:46:57 +03:00
Toutsu e5945288ac feat(web): add local portfolio cover storage 2026-06-02 12:35:00 +03:00
Toutsu 7d1489445e feat(web): define portfolio contracts and validation 2026-06-02 12:21:55 +03:00
Toutsu b52d4000b4 fix(web): restore public game pages
PR Checks / test-and-build (pull_request) Successful in 11m56s
Use the existing group_managers.created_at column when picking owner profile links for public pages.

Bump version -> 3.5.1
2026-05-29 09:27:01 +03:00
Toutsu 0c1d3abd7e feat(web): add public master profiles
PR Checks / test-and-build (pull_request) Successful in 12m32s
Add sanitized public GM profiles with publication controls, public /gm/{slug} pages, and links from public game surfaces.

Bump version -> 3.5.0
2026-05-29 00:08:14 +03:00
Toutsu 6d59737d07 feat(web): update public session detail with showcase fields
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 16:13:35 +03:00
Toutsu a5f4a68c6a fix(web): add public-session guards and ON CONFLICT to RegisterFromShowcaseAsync 2026-05-28 15:40:21 +03:00
Toutsu b2497ed877 feat(web): add showcase query and registration methods 2026-05-28 15:27:18 +03:00
Toutsu 3418d1a46c feat: add public club pages
PR Checks / test-and-build (pull_request) Successful in 12m47s
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
2026-05-28 12:23:47 +03:00
Toutsu fac5d75c7e Fix Discord co-GM management
Deploy Telegram Bot / build-and-push (push) Successful in 5m58s
Deploy Telegram Bot / scan-images (push) Successful in 3m39s
Deploy Telegram Bot / deploy (push) Successful in 34s
2026-05-27 16:32:47 +03:00
Toutsu 383e2c1d8d fix: create Telegram topics for template batches
PR Checks / test-and-build (pull_request) Successful in 12m56s
Create a Telegram forum topic when Web creates a batch from a campaign template, persist thread ownership on the generated sessions, and send the batch schedule into that topic.

Bump version -> 3.1.1
2026-05-27 13:50:18 +03:00
Toutsu 040b0a3cdb refactor: завершить platform migration и удалить deprecated telegram_* scaffolding
PR Checks / test-and-build (pull_request) Failing after 13m15s
- Добавлены миграции 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>
2026-05-26 16:41:15 +03:00
Toutsu c2cc7fd9a8 fix(web): show discord sessions and integration labels
Deploy Telegram Bot / build-and-push (push) Successful in 5m46s
Deploy Telegram Bot / scan-images (push) Successful in 3m29s
Deploy Telegram Bot / deploy (push) Successful in 29s
2026-05-26 14:43:33 +03:00
Toutsu 3447acd8c4 fix(discord): update sessions via interactions
Deploy Telegram Bot / build-and-push (push) Successful in 6m14s
Deploy Telegram Bot / scan-images (push) Successful in 3m12s
Deploy Telegram Bot / deploy (push) Successful in 31s
2026-05-26 14:24:06 +03:00
Toutsu 9c59240f48 fix: connection leak in UpsertDiscordUserAsync + false conflict in LinkIdentityAsync
PR Checks / test-and-build (pull_request) Successful in 7m25s
- UpsertDiscordUserAsync: restore await using on opened connection
- LinkIdentityAsync: compute effectiveCurrentPrimary before existingLink check
  to prevent false conflict when current user is a secondary identity

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 13:59:41 +03:00
Toutsu baa25f2e1e feat: unify Telegram and Discord accounts via identity linking
PR Checks / test-and-build (pull_request) Successful in 7m6s
- 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>
2026-05-25 13:51:10 +03:00
Toutsu 7a2ed808c4 fix: replace cookie-based Discord OAuth CSRF with server-side state store
Deploy Telegram Bot / build-and-push (push) Successful in 4m19s
Deploy Telegram Bot / scan-images (push) Successful in 1m24s
Deploy Telegram Bot / deploy (push) Successful in 11s
- Replace __DiscordOAuthState cookie (blocked by third-party cookie policies)
  with in-memory DiscordOAuthStateStore singleton
- State is created server-side and validated on callback, eliminating
  cross-site cookie transmission issues entirely
- Removed CryptographicOperations dependency from Program.cs

Bump version → 2.8.1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 13:18:23 +03:00
Toutsu 7e02e86cd6 fix: add Discord OAuth token exchange logging for production diagnostics
PR Checks / test-and-build (pull_request) Failing after 6m20s
- Log status code and response body when Discord /oauth2/token fails
- Helps identify why ExchangeCodeAsync returns null in production

Bump version → 2.8.1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 12:46:56 +03:00
Toutsu 66dc53f12f fix(web): address PR review critical issues for Discord OAuth
PR Checks / test-and-build (pull_request) Successful in 6m6s
- 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>
2026-05-25 12:07:40 +03:00
Toutsu 50f5307aac feat(web): finalize Discord OAuth and platform-agnostic auth
PR Checks / test-and-build (pull_request) Successful in 5m47s
- Bump version to 2.8.0 across all versioned files
- Fix AuthorizedSessionServiceTests for platform-agnostic identity
- Update Razor Pages to use *ForCurrentUserAsync APIs
- Add backward-compatible constructors to WebGameGroup/WebGroupManager
- Make DiscordOAuthOptions properties non-required for config binding

Bump version → 2.8.0

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:47:54 +03:00
Toutsu 9d4256353d feat(web): refactor SessionStore and AuthorizedSessionService to platform-agnostic identity
- ISessionStore: all methods use (platform, external_user_id)
- SessionService: updated SQL queries and added UpsertDiscordUserAsync
- AuthorizedSessionService: resolves identity from HttpContext, no longer accepts telegram_id params
- SessionAccessDeniedException now accepts string externalUserId
- Added ExternalUserId/ExternalUsername to WebGroupManager and WebParticipant

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:08:10 +03:00
Toutsu 543fc42a6d feat(web): add platform-agnostic identity extraction from ClaimsPrincipal
- TryGetPlatformIdentity returns (platform, external_user_id)
- TryGetDiscordId for Discord-specific flows
- Backward-compatible fallback for legacy Telegram auth without Platform claim
- GetAvatarUrl helper for Discord avatars

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:02:29 +03:00
Toutsu bfed400b4d feat(web): add Discord OAuth service and authorization endpoints
- DiscordOAuthOptions for client_id, secret, redirect_uri
- DiscordAuthService exchanges code for token and fetches user profile
- /auth/discord and /auth/discord/callback endpoints
- CreateDiscordPrincipal for cookie auth claims
- Telegram principal now includes Platform claim for forward compatibility

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:02:13 +03:00
Toutsu 11b145a967 chore: add platform identity and platform_messages for multi-platform support (#23)
PR Checks / test-and-build (pull_request) Successful in 9m36s
TDD cycle for issue #23:
- RED: 9 migration smoke tests (file presence + schema expectations)
- GREEN: V016 migration adding platform identity columns
- GREEN: CreateSessionHandler, JoinSessionHandler, Web SessionService updated
  with dual-write to legacy and new identity columns + COALESCE fallbacks
- GREEN: get_group_attendance_stats recreated for external_username
- Bump version to 2.0.0

Changes:
- V016__add_platform_identity.sql:
  - players: platform, external_user_id, external_username
  - game_groups: platform, external_group_id, external_channel_id
  - platform_messages table with cross-platform message tracking
  - Backfill all existing Telegram data into new columns
  - Recreate get_group_attendance_stats with COALESCE fallback
- V012__add_attendance_stats.sql: use COALESCE(external_username, telegram_username)
- CreateSessionHandler: dual-write + COALESCE fallbacks in SELECTs
- JoinSessionHandler: dual-write to new identity columns
- Web SessionService: dual-write to new identity columns
- PlatformIdentityMigrationTests (9 smoke tests covering all handlers)
- Version synced: Directory.Build.props, compose.yaml, deploy.yml, NavMenu.razor → 2.0.0

Legacy telegram_* columns preserved for backward compatibility.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 10:48:10 +03:00
Toutsu de9f56c97d feat(#21): support selected telegram topics for schedules
PR Checks / test-and-build (pull_request) Failing after 3m18s
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.
2026-05-12 12:07:51 +03:00
Toutsu c0c8f852d2 feat(#19): добавить ссылку на игру в карточку батча
PR Checks / test-and-build (pull_request) Successful in 3m49s
- 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>
2026-05-10 18:13:55 +03:00
Hermes Agent 6d90ba8274 feat(#15): add SessionHistory.razor, navigation links, and bump version to 1.10.2 2026-05-07 12:20:44 +00:00
Hermes Agent 35894bf89e feat(#15): session audit log domain, store, and instrumentation 2026-05-07 12:16:54 +00:00
root b03929174a fix: move PlayerAttendanceStats out of interface scope
PR Checks / test-and-build (pull_request) Failing after 2m53s
The record was nested inside ISessionStore, making it ISessionStore.PlayerAttendanceStats.
C# does not infer nested types in return signatures; callers and implementors failed
with CS0246 / CS0738. Moving it to namespace scope resolves the build.
2026-05-07 11:16:13 +00:00
root 7e2747ec73 feat: implement GetGroupAttendanceStatsAsync (#14)
PR Checks / test-and-build (pull_request) Failing after 2m57s
2026-05-07 11:05:38 +00:00
Toutsu 116bed16a8 feat(#14): add PlayerAttendanceStats record + interface method 2026-05-07 13:26:01 +03:00
Toutsu b205967f1a feat(#13): add CalendarSubscriptionService with token generation and ICS rendering 2026-05-07 10:15:06 +03:00
Toutsu 7457315d6f feat(#13): add SubscriptionNotFoundException 2026-05-07 10:13:45 +03:00
root 89b5196676 fix(#22): resolve Telegram namespace collision and add missing MoscowTime using
Deploy Telegram Bot / build-and-push (push) Successful in 7m24s
Deploy Telegram Bot / deploy (push) Successful in 12s
2026-05-06 09:23:52 +00:00
root 14b9bf15f2 refactor(#22): разделить SessionBatchRenderer на neutral view и Telegram renderer
- SessionBatchViewBuilder в Shared собирает нейтральную view model
- TelegramSessionBatchRenderer в Bot/Web рендерит HTML + InlineKeyboardMarkup
- DiscordSessionBatchRenderer заглушка подготовлена
- BatchMessageEditor перенесён из Shared в Bot/Web
- Удалён SessionBatchRenderer, убран Telegram.Bot из Shared.csproj
- Обновлены все вызовы (7 handler-ов + Web SessionService + smoke tests)
- Новые тесты на builder и Telegram renderer
2026-05-06 08:28:25 +00:00
root c1f5d96e25 feat: show participant list, kick player, auto-promote waitlist 2026-05-04 17:11:23 +00:00
Toutsu c874f7b797 fix: combine session image and text into single Telegram message
Deploy Telegram Bot / build-and-push (push) Successful in 4m2s
Deploy Telegram Bot / deploy (push) Successful in 10s
When creating a session with an image, send it as a single SendPhoto
with the schedule text as caption (+ reply markup), instead of two
separate messages. Falls back to two messages if caption exceeds
Telegram's 1024-char limit.

Also adds BatchMessageEditor helper that transparently handles
EditMessageText vs EditMessageCaption depending on whether the batch
message is a text or photo message. Updated all handlers and web
service to use this helper.

Version bump to 1.9.7.
2026-05-04 10:33:06 +03:00
Toutsu 2a76ec0fb8 fix: stabilize mini app login and safe area
Deploy Telegram Bot / build-and-push (push) Successful in 3m53s
Deploy Telegram Bot / deploy (push) Successful in 17s
2026-04-28 20:25:18 +03:00
Toutsu 41f2ea6e90 feat: add telegram mini app dashboard
Deploy Telegram Bot / build-and-push (push) Successful in 23s
Deploy Telegram Bot / deploy (push) Successful in 10s
2026-04-28 14:56:55 +03:00
Toutsu 0218890a7a feat: add campaign templates and recurring schedules
Deploy Telegram Bot / build-and-push (push) Successful in 3m49s
Deploy Telegram Bot / deploy (push) Successful in 10s
2026-04-28 10:01:18 +03:00
Toutsu 2529df4157 feat: support co-gm group delegation
Deploy Telegram Bot / build-and-push (push) Successful in 3m51s
Deploy Telegram Bot / deploy (push) Successful in 11s
2026-04-27 14:27:16 +03:00
Toutsu a8f2b10956 feat: send personal player notifications
Deploy Telegram Bot / build-and-push (push) Successful in 3m36s
Deploy Telegram Bot / deploy (push) Successful in 11s
2026-04-27 10:11:11 +03:00
Toutsu 621ef553e7 feat: add web batch bulk operations
Deploy Telegram Bot / build-and-push (push) Successful in 3m21s
Deploy Telegram Bot / deploy (push) Successful in 11s
2026-04-27 09:31:51 +03:00
Toutsu 9c91057798 feat: add session capacity waitlist
Deploy Telegram Bot / build-and-push (push) Failing after 4m42s
Deploy Telegram Bot / deploy (push) Has been skipped
2026-04-24 13:28:01 +03:00