feat(e2e): #148 /newsession scenario from creation to publication

- 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>
This commit is contained in:
2026-06-16 12:22:10 +03:00
parent f4a61269c2
commit 892f39401c
4 changed files with 231 additions and 5 deletions
+50
View File
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Text;
using TL;
using WTelegram;
@@ -69,6 +70,12 @@ public sealed class TelegramUserClient : IDisposable
.ToList();
}
public async Task<Message?> GetLatestBotMessageAsync(ChatGroup group, CancellationToken cancellationToken = default)
{
var messages = await GetRecentMessagesAsync(group, limit: 20, cancellationToken);
return messages.FirstOrDefault(m => m.from_id is not PeerUser userPeer || userPeer.user_id != _client.UserId);
}
public async Task<Message?> WaitForBotReplyAsync(
ChatGroup group,
string? containsText = null,
@@ -104,6 +111,49 @@ public sealed class TelegramUserClient : IDisposable
await SendMessageAsync(group, command, cancellationToken);
}
public async Task ClickInlineButtonAsync(
ChatGroup group,
string callbackData,
int? messageId = null,
CancellationToken cancellationToken = default)
{
var channel = await ResolveChannelAsync(group.Id, cancellationToken);
var targetMessageId = messageId ?? (await GetLatestBotMessageAsync(group, cancellationToken))?.id
?? throw new InvalidOperationException("No bot message found to click.");
await _client.Messages_GetBotCallbackAnswer(
channel,
targetMessageId,
Encoding.UTF8.GetBytes(callbackData));
}
public async Task ClickInlineButtonByTextAsync(
ChatGroup group,
string buttonTextContains,
int? messageId = null,
CancellationToken cancellationToken = default)
{
var message = messageId.HasValue
? (await GetRecentMessagesAsync(group, limit: 20, cancellationToken)).FirstOrDefault(m => m.id == messageId.Value)
: await GetLatestBotMessageAsync(group, cancellationToken);
if (message is null)
throw new InvalidOperationException("No bot message found to click.");
if (message.reply_markup is not ReplyInlineMarkup markup)
throw new InvalidOperationException("Latest bot message has no inline keyboard.");
var button = markup.rows
.SelectMany(r => r.buttons)
.OfType<KeyboardButtonCallback>()
.FirstOrDefault(b => b.text.Contains(buttonTextContains, StringComparison.OrdinalIgnoreCase));
if (button is null)
throw new InvalidOperationException($"No inline button matching '{buttonTextContains}' found.");
await ClickInlineButtonAsync(group, Encoding.UTF8.GetString(button.data), message.id, cancellationToken);
}
public void Dispose()
{
_client.Dispose();