feat(e2e): #147 group creation and bot invitation scenario

- 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>
This commit is contained in:
2026-06-16 12:17:58 +03:00
parent 4b0f328f2e
commit f4a61269c2
4 changed files with 106 additions and 15 deletions
+18 -2
View File
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using TL;
using WTelegram;
@@ -7,6 +8,7 @@ public sealed class TelegramUserClient : IDisposable
{
private readonly Client _client;
private readonly RunnerConfig _config;
private readonly ConcurrentDictionary<long, Channel> _knownChannels = new();
public TelegramUserClient(RunnerConfig config)
{
@@ -22,11 +24,11 @@ public sealed class TelegramUserClient : IDisposable
public async Task<ChatGroup> CreateGroupAsync(string title, string about = "", CancellationToken cancellationToken = default)
{
// Creating a megagroup gives us an UpdatesBase with Chats immediately.
var updates = await _client.Channels_CreateChannel(title, about, megagroup: true);
var channel = updates.Chats.Values.OfType<Channel>().FirstOrDefault()
?? throw new InvalidOperationException("Failed to create a supergroup.");
_knownChannels[channel.id] = channel;
return new ChatGroup(channel.id, channel.title);
}
@@ -44,6 +46,13 @@ public sealed class TelegramUserClient : IDisposable
await _client.Channels_InviteToChannel(channel, new InputUserBase[] { inputUser });
}
public async Task DeleteGroupAsync(ChatGroup group, CancellationToken cancellationToken = default)
{
var channel = await ResolveChannelAsync(group.Id, cancellationToken);
await _client.Channels_DeleteChannel(channel);
_knownChannels.TryRemove(group.Id, out _);
}
public async Task SendMessageAsync(ChatGroup group, string text, CancellationToken cancellationToken = default)
{
var channel = await ResolveChannelAsync(group.Id, cancellationToken);
@@ -102,12 +111,19 @@ public sealed class TelegramUserClient : IDisposable
private async Task<Channel> ResolveChannelAsync(long channelId, CancellationToken cancellationToken = default)
{
if (_knownChannels.TryGetValue(channelId, out var cached))
return cached;
var dialogs = await _client.Messages_GetAllDialogs();
var channel = dialogs.chats.Values
.OfType<Channel>()
.FirstOrDefault(c => c.id == channelId);
return channel ?? throw new InvalidOperationException($"Could not resolve channel {channelId}.");
if (channel is null)
throw new InvalidOperationException($"Could not resolve channel {channelId}.");
_knownChannels[channelId] = channel;
return channel;
}
}