public async Task ExceptionIsCaughtByErrorHandler() { var mockClient = new MockTelegramBotClient { Options = { ExceptionToThrow = new("Oops") } }; var cts = new CancellationTokenSource(); bool seenException = false; var receiver = new QueuedUpdateReceiver(mockClient, errorHandler: (ex, ct) => { Assert.Same(mockClient.Options.ExceptionToThrow, ex); seenException = true; cts.Cancel(); return(Task.CompletedTask); }); await using var enumerator = receiver.GetAsyncEnumerator(cts.Token); await Assert.ThrowsAsync <OperationCanceledException>(async() => await enumerator.MoveNextAsync()); Assert.True(seenException); }
public async Task StopsIfStoppedAndOutOfUpdates() { var mockClient = new MockTelegramBotClient("1", "2", "3"); var receiver = new QueuedUpdateReceiver(mockClient); receiver.StartReceiving(); Task stopTask = Task.Run(() => { Task.Delay(150).Wait(); receiver.StopReceiving(); }); int updateCount = 0; await foreach (var update in receiver.YieldUpdatesAsync()) { updateCount++; } Assert.Equal(3, updateCount); Assert.Equal(0, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); Assert.False(receiver.IsReceiving); Assert.True(stopTask.IsCompleted); }
public async Task StartPolling(TimeSpan?timeout = null) { _isStopPolling = false; if (timeout.HasValue) { _botClient.Timeout = timeout.Value; } var updateReceiver = new QueuedUpdateReceiver(_botClient); updateReceiver.StartReceiving(); _logger.LogInformation("Polling started!"); await foreach (var update in updateReceiver.YieldUpdatesAsync()) { await HandleUpdate(update); if (_isStopPolling) { _logger.LogInformation("Polling stopped!"); break; } } }
public TelegramHostedService( IServiceProvider serviceProvider, QueuedUpdateReceiver queuedUpdateReceiver, ILogger <TelegramHostedService> logger) { _serviceProvider = serviceProvider; _queuedUpdateReceiver = queuedUpdateReceiver; _logger = logger; }
public void CallingGetEnumeratorTwiceThrows() { var mockClient = new MockTelegramBotClient(); var receiver = new QueuedUpdateReceiver(mockClient); _ = receiver.GetAsyncEnumerator(); Assert.Throws <InvalidOperationException>(() => receiver.GetAsyncEnumerator()); }
public TelegramBotWorker(ILogger <TelegramBotWorker> logger, ITelegramBotClient telegramBotClient, IServiceScopeFactory serviceScopeFactory) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _bot = telegramBotClient ?? throw new ArgumentNullException(nameof(telegramBotClient)); _updateReceiver = new QueuedUpdateReceiver(_bot); _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); using var scope = _serviceScopeFactory.CreateScope(); _databaseLog = scope.ServiceProvider.GetRequiredService <IRepositoryService>() ?? throw new ArgumentNullException(nameof(_databaseLog)); }
public async Task ReturnsReceivedPendingUpdates() { var mockClient = new MockTelegramBotClient("foo-bar", "123", "one-two-three", "456"); var receiver = new QueuedUpdateReceiver(mockClient); mockClient.Options.RequestDelay = 250; Assert.Equal(4, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); await using var enumerator = receiver.GetAsyncEnumerator(); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(3, mockClient.MessageGroupsLeft); Assert.Equal(1, receiver.PendingUpdates); Assert.Equal("foo", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(3, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); Assert.Equal("bar", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(2, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); Assert.Equal("123", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(1, mockClient.MessageGroupsLeft); Assert.Equal(2, receiver.PendingUpdates); Assert.Equal("one", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(1, mockClient.MessageGroupsLeft); Assert.Equal(1, receiver.PendingUpdates); Assert.Equal("two", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(1, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); Assert.Equal("three", enumerator.Current.Message !.Text); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(0, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); Assert.Equal("456", enumerator.Current.Message !.Text); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var updateReceiver = new QueuedUpdateReceiver(_botClient); updateReceiver.StartReceiving(_botOptions.Value.AllowedUpdates, cancellationToken: stoppingToken); await foreach (var update in updateReceiver.YieldUpdatesAsync().WithCancellation(stoppingToken)) { _logger.LogDebug("Message with type {0} received from chat {1}", update.Message.Type, update.Message.Chat.Id); var photo = update.Message.Photo?[^ 1];
public Worker( ILogger <Worker> logger, ITelegramBotClient telegramBotClient, IRepositoryService repositoryService, IUpdateHandlerService updateHandlerService) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _bot = telegramBotClient ?? throw new ArgumentNullException(nameof(telegramBotClient)); _databaseLog = repositoryService ?? throw new ArgumentNullException(nameof(repositoryService)); _updateHandlerService = updateHandlerService ?? throw new ArgumentNullException(nameof(updateHandlerService)); _updateReceiver = new QueuedUpdateReceiver(_bot); }
private static async Task RunBot() { var me = await _bot.GetMeAsync(); Console.Title = me.Username; Configuration.StartupTime = DateTimeOffset.UtcNow; Configuration.Version = Assembly.GetEntryAssembly().GetCustomAttribute <AssemblyInformationalVersionAttribute>().InformationalVersion; try { LoadChatSettings(); LoadReactions(); LoadBoyans(); Console.WriteLine($"Start listening for @{me.Username}"); var cts = new CancellationTokenSource(); AppDomain.CurrentDomain.ProcessExit += (s, ev) => { OnExit(cts); }; Console.CancelKeyPress += (s, ev) => { OnExit(cts); }; SetupPeriodicSettingsSaving(); var receiverOptions = new ReceiverOptions { AllowedUpdates = new UpdateType[] { UpdateType.Message, UpdateType.CallbackQuery }, ThrowPendingUpdates = true }; var updateReceiver = new QueuedUpdateReceiver(_bot, receiverOptions, HandleErrorAsync); try { await foreach (var update in updateReceiver.WithCancellation(cts.Token)) { await HandleUpdateAsync(update, cts.Token); } } catch (RequestException e) { } catch (OperationCanceledException exception) { } } catch (Exception ex) { _bot.SendTextMessageAsync(chatId: Configuration.MasterId, text: $"Exception:{Environment.NewLine}{ex.Message}").Wait(); } }
public async Task MoveNextThrowsIfEnumeratorIsDisposed() { var mockClient = new MockTelegramBotClient("foo"); var receiver = new QueuedUpdateReceiver(mockClient); var enumerator = receiver.GetAsyncEnumerator(); await enumerator.MoveNextAsync(); await enumerator.DisposeAsync(); await Assert.ThrowsAnyAsync <OperationCanceledException>(async() => await enumerator.MoveNextAsync()); }
public async Task ThrowsOnMoveNextIfCancelled() { var mockClient = new MockTelegramBotClient("foo", "bar"); var receiver = new QueuedUpdateReceiver(mockClient); var cts = new CancellationTokenSource(); await using var enumerator = receiver.GetAsyncEnumerator(cts.Token); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal("foo", enumerator.Current.Message !.Text); cts.Cancel(); await Assert.ThrowsAnyAsync <OperationCanceledException>(async() => await enumerator.MoveNextAsync()); }
public async Task ReturnsReceivedPendingUpdates() { var mockClient = new MockTelegramBotClient("foo-bar", "123"); var receiver = new QueuedUpdateReceiver(mockClient); Assert.Equal(2, mockClient.MessageGroupsLeft); Assert.Equal(0, receiver.PendingUpdates); receiver.StartReceiving(); await Task.Delay(200); Assert.Equal(0, mockClient.MessageGroupsLeft); Assert.Equal(3, receiver.PendingUpdates); await foreach (var update in receiver.YieldUpdatesAsync()) { Assert.Equal("foo", update.Message.Text); break; } Assert.Equal(2, receiver.PendingUpdates); await foreach (var update in receiver.YieldUpdatesAsync()) { Assert.Equal("bar", update.Message.Text); break; } Assert.Equal(1, receiver.PendingUpdates); await foreach (var update in receiver.YieldUpdatesAsync()) { Assert.Equal("123", update.Message.Text); break; } Assert.Equal(0, receiver.PendingUpdates); receiver.StopReceiving(); await foreach (var update in receiver.YieldUpdatesAsync()) { // No pending updates and we've called StopReceiving Assert.False(true); } }
public async Task ReceivesUpdatesInTheBackground() { var mockClient = new MockTelegramBotClient("1", "2", "3"); var receiver = new QueuedUpdateReceiver(mockClient); Assert.Equal(3, mockClient.MessageGroupsLeft); await foreach (var update in receiver) { Assert.Equal("1", update.Message !.Text); await Task.Delay(100); break; } Assert.Equal(0, mockClient.MessageGroupsLeft); Assert.Equal(2, receiver.PendingUpdates); }
public Enumerator(QueuedUpdateReceiver receiver, CancellationToken cancellationToken) { _receiver = receiver; _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default); _token = _cts.Token; _messageOffset = receiver._receiverOptions?.Offset ?? 0; _limit = receiver._receiverOptions?.Limit ?? default; _allowedUpdates = receiver._receiverOptions?.AllowedUpdates; _channel = Channel.CreateUnbounded <Update>( new() { SingleReader = true, SingleWriter = true } ); Task.Run(ReceiveUpdatesAsync); }
public async Task ReceivingIsCanceledOnExceptionIfThereIsNoErrorHandler() { var mockClient = new MockTelegramBotClient { Options = { ExceptionToThrow = new("Oops") } }; var receiver = new QueuedUpdateReceiver(mockClient); await using var enumerator = receiver.GetAsyncEnumerator(); await Assert.ThrowsAsync <OperationCanceledException>(async() => await enumerator.MoveNextAsync()); Exception ex = await Assert.ThrowsAsync <Exception>(async() => await enumerator.MoveNextAsync()); Assert.Same(mockClient.Options.ExceptionToThrow, ex.InnerException); }
public async Task StartReceiving(CancellationTokenSource cts) { var cancellationToken = cts.Token; var receiverOptions = new ReceiverOptions { AllowedUpdates = { } // receive all update types }; var updateReceiver = new QueuedUpdateReceiver(_botClient, receiverOptions); try { await foreach (Update update in updateReceiver.WithCancellation(cts.Token)) { await HandleUpdate(update); } } catch (OperationCanceledException exception) { _logger.LogError("error while receiving tg message @{exception}", exception); } }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { var botName = await _botClient.GetMeAsync(); _logger.Information($"Bot's name: {botName.FirstName}"); var updateReceiver = new QueuedUpdateReceiver(_botClient); updateReceiver.StartReceiving(new UpdateType[] { UpdateType.Message }); await foreach (Update update in updateReceiver.YieldUpdatesAsync()) { await _messageHandler.Handle(update.Message); } } catch (Exception e) { _logger.Error(e, "{nameof(Worker)}.{nameof(ExecuteAsync)} Some unhandled error occurred, stopping application."); _hostApplicationLifetime.StopApplication(); } }
public async Task DoesntReceiveAfterCancellation() { var mockClient = new MockTelegramBotClient("foo", "bar", "foo"); var receiver = new QueuedUpdateReceiver(mockClient); mockClient.Options.RequestDelay = 200; var cts = new CancellationTokenSource(); await using var enumerator = receiver.GetAsyncEnumerator(cts.Token); Assert.True(await enumerator.MoveNextAsync()); Assert.Equal(2, mockClient.MessageGroupsLeft); Assert.Equal("foo", enumerator.Current.Message !.Text); cts.CancelAfter(50); await Assert.ThrowsAnyAsync <OperationCanceledException>(async() => await enumerator.MoveNextAsync()); await Task.Delay(500); Assert.Equal(2, mockClient.MessageGroupsLeft); }
public static async Task <int> RunBot(string?botToken, CancellationToken cancellationToken = default) { var(botConfig, loadBotConfigErrMsg) = await BotConfig.LoadBotConfigAsync(cancellationToken); if (loadBotConfigErrMsg is not null) { Console.WriteLine(loadBotConfigErrMsg); return(1); } // Priority: commandline option > environment variable > config file if (string.IsNullOrEmpty(botToken)) { botToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN"); } if (string.IsNullOrEmpty(botToken)) { botToken = botConfig.BotToken; } if (string.IsNullOrEmpty(botToken)) { Console.WriteLine("Please provide a bot token with command line option `--bot-token`, environment variable `TELEGRAM_BOT_TOKEN`, or in the config file."); return(-1); } try { var bot = new TelegramBotClient(botToken); Console.WriteLine("Created Telegram bot instance with API token."); var me = await bot.GetMeAsync(cancellationToken); if (string.IsNullOrEmpty(me.Username)) { throw new Exception("Error: bot username is null or empty."); } await bot.SetMyCommandsAsync(UpdateHandler.BotCommandsPublic, null, null, cancellationToken); Console.WriteLine($"Registered {UpdateHandler.BotCommandsPublic.Length} bot commands for all chats."); var privateChatCommands = UpdateHandler.BotCommandsPrivate.Concat(UpdateHandler.BotCommandsPublic); await bot.SetMyCommandsAsync(privateChatCommands, BotCommandScope.AllPrivateChats(), null, cancellationToken); Console.WriteLine($"Registered {privateChatCommands.Count()} bot commands for private chats."); Console.WriteLine($"Started Telegram bot: @{me.Username} ({me.Id})."); var updateHandler = new UpdateHandler(me.Username, botConfig); var updateReceiver = new QueuedUpdateReceiver(bot, null, UpdateHandler.HandleErrorAsync); await updateHandler.HandleUpdateStreamAsync(bot, updateReceiver, cancellationToken); } catch (ArgumentException ex) { Console.WriteLine($"Invalid access token: {ex.Message}"); } catch (HttpRequestException ex) { Console.WriteLine($"A network error occurred: {ex.Message}"); } catch (Exception ex) { Console.WriteLine(ex); } return(0); }
public static async Task <int> RunBot(string?botToken, CancellationToken cancellationToken = default) { var(config, loadConfigErrMsg) = await Config.LoadConfigAsync(cancellationToken); if (loadConfigErrMsg is not null) { Console.WriteLine(loadConfigErrMsg); return(1); } var(data, loadDataErrMsg) = await Data.LoadDataAsync(cancellationToken); if (loadDataErrMsg is not null) { Console.WriteLine(loadDataErrMsg); return(1); } _ = Task.Run(() => SaveDataHourlyAsync(data, cancellationToken), CancellationToken.None); // Priority: commandline option > environment variable > config file if (string.IsNullOrEmpty(botToken)) { botToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN"); } if (string.IsNullOrEmpty(botToken)) { botToken = config.BotToken; } if (string.IsNullOrEmpty(botToken)) { Console.WriteLine("Please provide a bot token with command line option `--bot-token`, environment variable `TELEGRAM_BOT_TOKEN`, or in the config file."); return(-1); } try { var bot = new TelegramBotClient(botToken); Console.WriteLine("Created Telegram bot instance with API token."); var me = await bot.GetMeAsync(cancellationToken); if (me.CanReadAllGroupMessages is not true) { config.EnableStats = false; } if (string.IsNullOrEmpty(me.Username)) { throw new Exception("Error: bot username is null or empty."); } var updateHandler = new UpdateHandler(me.Username, config, data); await bot.SetMyCommandsAsync(updateHandler.Commands, null, null, cancellationToken); Console.WriteLine($"Registered {updateHandler.Commands.Count()} bot commands."); Console.WriteLine($"Started Telegram bot: @{me.Username} ({me.Id})."); var updateReceiver = new QueuedUpdateReceiver(bot, null, UpdateHandler.HandleErrorAsync); await updateHandler.HandleUpdateStreamAsync(bot, updateReceiver, cancellationToken); } catch (ArgumentException ex) { Console.WriteLine($"Invalid access token: {ex.Message}"); } catch (HttpRequestException ex) { Console.WriteLine($"A network error occurred: {ex.Message}"); } catch (Exception ex) { Console.WriteLine(ex); } var saveDataErrMsg = await Data.SaveDataAsync(data, CancellationToken.None); if (saveDataErrMsg is not null) { Console.WriteLine(saveDataErrMsg); return(1); } return(0); }