Telegram Bot: Broadcast – Użycie Extensions.Polling

W porzednim wpisie zmusiliśmy bota do pobierania wiadomości przy użyciu bardzo prymitywnego i chamskiego pollingu. Odpytywaliśmy nowe wiadomości z API co sekundę.

To bardzo toporne rozwiązanie. Nic nie potrafi poza odczytywaniem wiadomości i np. obsługę błędów musimy sobie sami napisać.

Telegram.Bot.Extensions.Polling

Z pomocą przyjdzie nam fajny dodatek Telegram.Bot.Extensions.Polling, który ładnie uporządkuje nam kod i doda kilka usprawnień.

Otwieramy nuget’a i instalujemy Polling

Robimy porządki

Przypomnijmy jak wygląda główna pętla programu

        public async Task Run()
        {
            await _bot.Init(); // inicjalizujemy bota
            await Task.Run(async () =>
            {
                while (true)
                {
                    await _bot.Process(); // tutaj się wszystko rozgrywa
                    await Task.Delay(1000); // czekamy sekundę
                }
            });
        }

Prawda, że paskudna? Zatąpimy ją teraz elementami z w/w liba. Nie będziemy już potrzebować pętli, wszystko zinicjujemy w _bot.Init()

        public async Task Run()
        {
            await _bot.Init();
        }

Od razu się można poczuć lepiej widząc taką ilośc kodu!

Zobaczmy na Init() co tam mu w duszy gra…

        public async Task Init()
        { 
            const int lastUpdate = -1;
            
            var messages = await GetMessages(-1); // pobieramy ostatnie wiadomości
            if (!messages.Any())
            {
                return;
            }

            _messageOffset = messages.Last().Id; // ost. id wiadomości to offset
        }

Pamiętacie? Na początku odczytywaliśmy wszystkie ostatnie wiadomości aby wyciągnąc na wstępie offset, którego użyliśmy do pobierania najnowszych wiadomości w metodzie Process(). Fuj.

Co powiecie na takiego zgrabnego inita?

        public async Task Init()
        {
            var receiverOptions = new ReceiverOptions
            {
                // chcemy wyłącznie wiadomości
                AllowedUpdates = new[] { UpdateType.Message },
                // pobieramy wszystkie wiadomości jakich nie przetworzyliśmy
                ThrowPendingUpdates = true
            };

            // włączamy odbieranie wiadomości
            await _client.ReceiveAsync(
                UpdateHandlerAsync,
                ErrorHandlerAsync,
                receiverOptions
            );
        }

        private async Task UpdateHandlerAsync(ITelegramBotClient client, Update update, CancellationToken token)
        {
            throw new NotImplementedException();
        }

        private async Task ErrorHandlerAsync(ITelegramBotClient client, Exception ex, CancellationToken token)
        {
            throw new NotImplementedException();
        }

Mamy tutaj 3 kroki:
1️⃣ włączamy nasłuch na nowe wiadomości
2️⃣ nowa wiadomość to trafi do metody UpdateHandlerAsync
3️⃣ wszelkie błędy ze strony API uruchomią metodę ErrorHandlerAsync

Poprzednio obsługę otrzymywania oraz procesowania nowych wiadomości załatwialiśmy w metodzie Process.

public async Task Process()
{
	// pobieramy nowe wiadomości
	var newUpdates = await GetMessages(_messageOffset);

        // latalmy po zebranych wiadomościach
	foreach (var update in newUpdates)
	{
		// kazdą wiadomość wysyłamy do naszych grup (CHAT_ID)
		foreach (var chatId in _config.Chats)
		{
		       // wysłanie wiadomości dalej
		}
	}



      // aktualizujemy offset
      if (messages.Any())
      {
           _messageOffset = messages.Last().Id;
      }
}

Teraz przetwarzanie musimy przenieść do metody UpdateHandlerAsync

  private async Task UpdateHandlerAsync(ITelegramBotClient client, Update update, CancellationToken token)
        {
             // kazdą wiadomość wysyłamy do naszych grup (CHAT_ID)
             foreach (var chatId in _config.Chats)
	     {
		// wysłanie wiadomości dalej
	     }
        }

Pozbyliśmy się pętli, nie musimy się martwić o _messageOffset . Pobieranie wiadomości zrzuciliśmy na coś co na pewno lepiej działa od poprzedniej metody.

⚠️ Ważne: nie musimy wymyślać wszystkiego na nowo (DRY!)

A co z obsługą błędów? Możemy ja bezproblemu zaimplementowac w metodzie ErrorHandlerAsync. Mamy tam zwykły `Extension` i możemy sobie z nim zrobić co tylko chcemy. Wrzucić do loga, powiadomić kogoś o błędzię czy też wysłać użytkownikowi wiadomość, że „something is no yes” i aby spróbował ponownie.

Podsumowanie

Dzisiaj zastąpiliśmy naszą prymitywną pętlę requestująca co sekundę API bota libem, który ładnie i sprawnie nam to opakował. Co za tym idzie uprościliśmy kod.

Polling nie jest oczywiście jedyną metodą pobierania wiadomości z API. Poza tym ma wady:
👎 opóźnienie
👎 wysyłanie cały czas requestów do serwera przez co zaśmiecamy łącze
👎 niepotrzebnie obciążamy server zbędnymi requestami

👍 Dla mnie plusem pollingu jest prostota. Bardzo łatwo go zrozumieć i wdrożyć.

Mamy jakaś alternatywe do pollingu? Jasne, że tak: webhooki. W tej metodzie już nie odpytujemy API o nowe wiadomości. To API nam wysyła na nasz URL wszelkie informacje. Ale to temat na osobny post…

Tymczasem zapraszam do dodania się do newslettera aby niczego nie przegapić!

👉 https://high-five.cc/newsletter

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.