namespace GmRelay.E2E.Runner; /// /// E2E scenario that walks through the GmRelay /newsession wizard in a Telegram group /// and verifies that a session is created and visible in the Web dashboard. /// public sealed class NewSessionScenario { private readonly TelegramUserClient _client; private readonly RunnerConfig _config; public NewSessionScenario(TelegramUserClient client, RunnerConfig config) { _client = client; _config = config; } public async Task RunAsync( ChatGroup group, NewSessionInputs inputs, CancellationToken cancellationToken = default) { await _client.SendCommandAsync(group, "newsession", cancellationToken); await WaitForStepAsync(group, WizardStep.Type, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.Type, "single"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.Title, cancellationToken); await _client.SendMessageAsync(group, inputs.Title, cancellationToken); await WaitForStepAsync(group, WizardStep.Description, cancellationToken); await _client.SendMessageAsync(group, "-", cancellationToken); await WaitForStepAsync(group, WizardStep.Cover, cancellationToken); await _client.SendMessageAsync(group, "-", cancellationToken); await WaitForStepAsync(group, WizardStep.System, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.System, "Dnd5e"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.Duration, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.Duration, "240"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.DateTime, cancellationToken); await _client.SendMessageAsync(group, inputs.ScheduledAtMoscow, cancellationToken); await WaitForStepAsync(group, WizardStep.Capacity, cancellationToken); await _client.SendMessageAsync(group, inputs.MaxPlayers.ToString(System.Globalization.CultureInfo.InvariantCulture), cancellationToken); await WaitForStepAsync(group, WizardStep.Format, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.Format, "online"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.Location, cancellationToken); await _client.SendMessageAsync(group, inputs.JoinLink, cancellationToken); await WaitForStepAsync(group, WizardStep.Visibility, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.Visibility, "public"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.Publish, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Choice(WizardStep.Publish, "yes"), cancellationToken: cancellationToken); await WaitForStepAsync(group, WizardStep.Confirm, cancellationToken); await _client.ClickInlineButtonAsync( group, WizardCallback.Create(), cancellationToken: cancellationToken); var confirmation = await _client.WaitForBotReplyAsync( group, containsText: "Создано", timeout: TimeSpan.FromSeconds(60), cancellationToken); if (confirmation is null) throw new InvalidOperationException("Wizard did not confirm session creation."); Console.WriteLine($"[scenario] session created (msg id={confirmation.id})"); return new ScenarioResult(group, confirmation.id); } private async Task WaitForStepAsync( ChatGroup group, string expectedStep, CancellationToken cancellationToken = default, TimeSpan? timeout = null) { var deadline = DateTime.UtcNow + (timeout ?? TimeSpan.FromSeconds(30)); while (DateTime.UtcNow < deadline) { cancellationToken.ThrowIfCancellationRequested(); var message = await _client.GetLatestBotMessageAsync(group, cancellationToken); if (message?.reply_markup is TL.ReplyInlineMarkup markup) { var dataButtons = markup.rows .SelectMany(r => r.buttons) .OfType() .Select(b => System.Text.Encoding.UTF8.GetString(b.data)) .ToList(); if (dataButtons.Any(d => d.StartsWith($"wizard:{expectedStep}", StringComparison.Ordinal))) return; } await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); } throw new TimeoutException($"Wizard did not reach step '{expectedStep}' in time."); } } public sealed record NewSessionInputs( string Title, string ScheduledAtMoscow, int MaxPlayers, string JoinLink);