- Add NewSessionScenario that walks the Telegram wizard:
single game, title, skip description/cover, D&D 5e, 4h, datetime,
capacity, online format, join link, public visibility, publish, confirm
- Add ClickInlineButtonAsync / ClickInlineButtonByTextAsync to TelegramUserClient
- Add local WizardCallback/Step constants mirroring GmRelay.Shared wizard wire format
- Program.cs now runs full flow: group setup + /newsession + cleanup
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add GroupSetupScenario: create supergroup, invite GmRelay bot, send /start,
wait for reply, then delete the group
- Extend TelegramUserClient with DeleteGroupAsync and channel cache
- Update Program.cs to run the scenario with cleanup in finally
- Update README status table and runner documentation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add standalone C# console runner tests/e2e/runner/ using WTelegramClient
- Provide TelegramUserClient wrapper: login, create supergroup, invite bot,
send messages/commands, read recent messages, wait for bot reply
- Add .env.example and runner .gitignore to keep secrets/session files out of git
- Update E2E README with runner instructions and status table
- Runner project intentionally excluded from GM-Relay.slnx to avoid CI/AOT impact
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add Playwright-based E2E tests in tests/e2e/dashboard/
- Authenticate via /auth/telegram-webapp using helpers/telegram_init_data.py
- Cover dashboard load and session edit flow
- Add requirements.txt and package dashboard folder
- Update README with setup and test descriptions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add TelegramAuthPayloadBuilder in GmRelay.Shared for C# tests.
- Refactor TelegramAuthServiceTests to use the shared builder.
- Add Python equivalent (telegram_init_data.py) for E2E runner.
- Add self-contained Python tests and E2E README.
Closes#144
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Remove legacy DiscordNewSessionCommand/Handler and their tests.
- Rename /newsession-wizard to /newsession.
- Add shared pool capacity step before Format/Location.
- Render Format and Location in Discord wizard; Location uses a modal.
- Propagate Format, JoinLink and LocationAddress in BuildCommand.
- Publish created sessions through existing IPlatformMessenger pipeline.
- Update README, version bump to 3.11.1, sync compose/deploy/NavMenu.
For non-managers /listsessions now shows player-friendly actions:
- ✅ Записаться <date> when not registered
- ✖️ Выйти <date> when already active
- ✖️ Выйти из ожидания <date> when waitlisted
Extend SessionListItemDto and the shared SQL query with IsUserActive
and IsUserWaitlisted flags so the renderer can choose the right button.
Update tests to cover all three player states.
Set /start, /newsession, /listsessions, /exportcalendar and /help
via setMyCommands for both private chats and group chats so users
see the command list when typing '/'.
Also update /help text to list all commands first and then show the
example.
The /listsessions buttons for owners/co-GMs only showed emoji + date,
so it was unclear what each button did. Add explicit verb labels:
- ❌ Отменить <date>
- ⏰ Перенести <date>
- ⬆️ С ожидания <date>
- 🗑 Удалить <date>
Update the renderer test to assert the new labels.
The /listsessions buttons for owners/co-GMs only showed emoji + date,
so it was unclear what each button did. Add explicit verb labels:
- ❌ Отменить <date>
- ⏰ Перенести <date>
- ⬆️ С ожидания <date>
- 🗑 Удалить <date>
Update the renderer test to assert the new labels.
The /listsessions buttons for owners/co-GMs only showed emoji + date,
so it was unclear what each button did. Add explicit verb labels:
- ❌ Отменить <date>
- ⏰ Перенести <date>
- ⬆️ С ожидания <date>
- 🗑 Удалить <date>
Update the renderer test to assert the new labels.
Slow ARM64 runners hit the default timeout while initializing the
container image scan after pulling. Extend the timeout so image scans
can complete reliably.
The scan-images job runs on a fresh runner that does not have the images
built by the build-and-push job. Login to the registry and pull the
images before scanning, otherwise Trivy cannot find them.
Merge pull request #139: feat(rendering): display description, system, duration, format, type and location in Telegram game card
Bump version to 3.11.0.
The ARM64 runner cannot reliably start PostgreSQL containers and apply
migrations within the test timeouts. Exclude the three Testcontainers
collections from pr-checks.yml while keeping all unit tests and SAST
builds. Integration tests remain runnable locally and via dotnet test.
Slow ARM64 runners hit the default timeout while downloading the Trivy
checks bundle and analyzing workflow YAML files. Extend the timeout so
PR checks can complete reliably.
Prevent offline sessions with empty join links from entering the 5-minute join-link notification flow, omit blank link lines from direct reminders, and add offline persistence/reminder regression coverage.
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.
After the shared create handler persists sessions, create a Telegram topic when needed, send the schedule/signup message, and store thread_id/batch_message_id/topic_created_by_bot for the batch. Add a Testcontainers regression test for the wizard SubmitDraftAsync happy path. Bump version to 3.9.9.
Add a PostgreSQL integration regression test for new-platform-group session creation. The production failure was a missing Platform parameter in the group_managers insert, leaving @Platform in SQL and causing PostgreSQL 42883. Bump version to 3.9.8.
The previous comment claimed a 0 MaxPlayers would also be blocked, but
the code never actually enforced that. The wizard's text-input path
already guarantees cap >= MinCapacity, so 0 is unreachable. Drop the
inaccurate half-sentence to keep the comment faithful to the code.
After 3.9.6 fixed long-polling, the bot finally reaches the final
'✅ Создать' step. Users pressing '♾ Без лимита' on the Capacity step
get a valid payload where Single.MaxPlayers = null (the legitimate
no-limit choice from GameCreationWizard.ApplyCapacityChoice 'no_limit'),
but CreateSessionHandler.IsComplete then reports 'лимит мест' as
missing, blocking session creation.
This regression existed since 3.9.3 (when 'no_limit' was added) but
stayed invisible because 3.9.4 and 3.9.5 never reached SubmitDraft
(libgssapi-krb5 missing → long-polling hung). Once 3.9.6 restored
polling, the bug surfaced immediately.
Fix: drop the null-MaxPlayers check from IsComplete for Single type.
Null is a valid 'no limit' state and must pass through to BuildCommands
→ shared handler, which already accepts null MaxPlayers correctly.
Closes#131.
Bump version 3.9.6 -> 3.9.7
Telegram bot's long-polling hangs after the first GetUpdates request
because libgssapi-krb5.so.2 is missing from the runtime-deps:10.0-noble
final image. .NET runtime attempts dlopen() of libgssapi during the
HTTPS handshake; without the library the HttpClient connection pool
enters an unrecoverable state and TelegramBotService never receives
new updates, even though SessionSchedulerService keeps sending
outgoing messages successfully.
Symptom (Loki, container gmrelaybot-bot-1):
Telegram bot polling started
Polling error, retrying in 5s
Telegram.Bot.Exceptions.RequestException: Bot API Service Failure
Cannot load library libgssapi_krb5.so.2
After the single Polling error, no Error handling update, no further
Polling error, and getUpdates from outside returns [] forever.
Fix: install libgssapi-krb5-2 alongside wget in the final stage of
src/GmRelay.Bot/Dockerfile. This also future-proofs Npgsql GSS/SSPI
Kerberos authentication for PostgreSQL.
Closes#129.
Bump version 3.9.5 -> 3.9.6
Fix two wizard FSM bugs reported after v3.9.4:
1. Capacity waitlist buttons could still advance the draft without a
numeric MaxPlayers value. The final submit validation then rejected
the draft with 'Не заполнены поля: лимит мест'. Now waitlist:on/off
stay on Capacity until MaxPlayers is set; users must either enter a
numeric limit or explicitly choose '♾ Без лимита'.
2. PickClub computed NextAfterVisibility before SetClubId, so the first
club click left the wizard on PickClub and the second click advanced.
Now ClubId is saved first and NextAfterVisibility is evaluated after
that mutation, so a valid club click advances on the first try.
TDD:
- WaitlistChoiceWithoutCapacity_StaysOnCapacityStep covers waitlist:on/off.
- PickClub_ValidGuid_AdvancesToPublishOnFirstClick covers the single-click club path.
- Stale Capacity waitlist callback test updated to the safer no-advance contract.
Closes#127
Test-only patch release. Sync the four canonical version sources:
- Directory.Build.props
- compose.yaml (bot, discord, web image tags)
- .gitea/workflows/deploy.yml (VERSION env)
- src/GmRelay.Web/Components/Layout/NavMenu.razor (visible nav-version)
The new NavMenu_ShouldExposeCurrentProjectVersion test reads the
version from Directory.Build.props, so this bump does NOT need a
hand-edited test literal — verified locally that the test passes
on 3.9.4 with no manual changes.
Two non-blocking suggestions from the PR #124 code review:
1. Symmetric coverage for the Discord PoolSlotCapacity no-limit button.
The Capacity step already had a customId-shape assertion for the
'♾ Без лимита' button; PoolSlotCapacity only had a label-presence
assertion. If ChoiceButtonCustomId's wire format ever diverged between
the two steps, only Capacity would catch it. Convert the single Fact
to a Theory with two InlineData rows so a regression in either step
produces a targeted test failure.
2. Brittle hard-coded version literal in NavMenu_ShouldExposeCurrent
ProjectVersion. The test had to be hand-edited on every version bump
(we hit this in PR #124 — bumping 3.9.2 -> 3.9.3 broke CI). Read the
version from Directory.Build.props via XDocument instead so the test
only fails when the rendered NavMenu actually disagrees with the
canonical version, not when someone forgot to update a literal.
Tolerant of whitespace, comments and attribute order; the <Version>
element is a plain string body in the MSBuild schema.
No production code changes; production version is bumped in a
follow-up commit.
Closes#125
The version-bump commit changed the visible version in NavMenu.razor
from 3.9.2 to 3.9.3 but this test hard-coded the old literal. Update
the assertion to match the new release tag.
Sync the four canonical version sources for the patch release:
- Directory.Build.props
- compose.yaml (bot, discord, web image tags)
- .gitea/workflows/deploy.yml (VERSION env)
- src/GmRelay.Web/Components/Layout/NavMenu.razor (visible nav-version)
In the session creation wizard (Telegram + Discord), the Capacity step
only exposed waitlist on/off buttons. The 'no waitlist' button silently
advanced to the next step without setting MaxPlayers, so users who tried
to create a session with no player cap were blocked with
'Не заполнены поля: лимит мест'.
The DB contract and CreateSessionCommand already supported null
MaxPlayers (int?, ck_sessions_max_players check in V006), and the web
form already exposes 'Без лимита' as an empty InputNumber — only the
wizard flow was broken.
Changes:
- Add '♾ Без лимита' choice button to Capacity in shared
WizardStepViewBuilder.BuildCapacity (Telegram) and to
RenderCapacity / RenderPoolSlotCapacity in DiscordWizardStep (Discord).
- Add 'no_limit' branch to GameCreationWizard.ApplyCapacityChoice that
sets MaxPlayers to null and advances to Visibility.
- Change GameCreationWizard.SetMaxPlayers signature from int to int? so
the 'no limit' branch compiles.
- Change CreateSessionCommand builder in both Telegram and Discord
submitters to take int? maxPlayers and drop the '?? 0' that would
have turned null into 0 (violating the DB CHECK and the 'no limit'
contract).
- In Discord BuildConfirmDescription, render '👥 Без лимита, waitlist
вкл/выкл' when MaxPlayers is null (the previous code silently
omitted the line).
- Expose BuildCommand as internal in both submitters and add
InternalsVisibleTo('GmRelay.Bot.Tests') to the DiscordBot assembly
for unit-test access.
Tests (9 new):
- WizardStepRenderTests.CapacityStep_HasWaitlistButtons — asserts the
'Без лимита' button is present.
- GameCreationWizardStepTransitionsTests.NoLimitCapacityButton_… —
asserts the choice advances to Visibility and leaves MaxPlayers null
in the JSON draft.
- GameCreationWizardStepTransitionsTests.ChoiceCallback_AdvancesToExpectedStep —
new Theory row for Capacity/no_limit.
- CreateSessionHandlerBuildCommandTests (new) — null/value propagation
through the Telegram submitter's BuildCommand.
- DiscordWizardStepCapacityRenderTests (new) — 'Без лимита' button is
rendered for both Capacity and PoolSlotCapacity, with the expected
custom-id shape.
- DiscordWizardSubmitterBuildCommandTests (new) — null/value
propagation through the Discord submitter's BuildCommand.
Closes#123