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);
    }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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;
                }
            }
        }
Ejemplo n.º 4
0
 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());
    }
Ejemplo n.º 6
0
        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);
    }
Ejemplo n.º 8
0
        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];
Ejemplo n.º 9
0
 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);
 }
Ejemplo n.º 10
0
        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());
    }
Ejemplo n.º 13
0
        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);
    }
Ejemplo n.º 15
0
        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);
    }
Ejemplo n.º 17
0
        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);
            }
        }
Ejemplo n.º 18
0
        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);
    }
Ejemplo n.º 20
0
        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);
        }
Ejemplo n.º 21
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);
        }