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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user