0

I'm writing an own TelegramBot. In that Bot there is a function subscribe/unsubscribe. When user starts "Subscribtion" TelegramBot should send a message "1111" each three seconds. But after unsubscription message keep sending. Could someone help me with that issue?

Method for start subscription:

private async Task OnStartSubscribeAsync(string userName, long userId,
    ITelegramBotClient _client, long chatId)
{
    var user = new UserDTO
    {
        UserId = userId.ToString(),
        UserName = userName
    };
    await _userService.StartSubscribeAsync(user);
    await _client.SendTextMessageAsync(chatId, "You subscribed successfully ");

    try
    {
        await CheckTick(_client, chatId);
    }
    catch (OperationCanceledException e)
    {
        await _client.SendTextMessageAsync(chatId, "STOPPED");
    }
    finally
    {
        tokenSource.Dispose();
    }

    var articles = await ReturnNewArticles();
    foreach (var item in articles)
    {
        var linkButton = KeyboardGoOver("Перейти", (EncodeUrl(item.Href)));
        await _client.SendPhotoAsync(chatId: chatId, photo: item.Image,
            caption: $"*{item.Title}*",
            parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown,
            replyMarkup: linkButton);
    }
}

Method for sending message with delay:

private Task CheckTick(ITelegramBotClient _client, long chatId)
{
    return Task.Run(async () =>
    {
        tokenSource.Token.ThrowIfCancellationRequested();

        while (true)
        {
            await Task.Delay(3000);
            await _client.SendTextMessageAsync(chatId, "1111");
            if (tokenSource.Token.IsCancellationRequested)
            {
                tokenSource.Token.ThrowIfCancellationRequested();
            }
        }

    }, tokenSource.Token);
}

Method for unsubscribe:

private async Task OnStopSubscibeAsync(string userName, long userId,
    ITelegramBotClient _client, long chatId)
{
    var user = new UserDTO()
    {
        UserId = userId.ToString(),
        UserName = userName
    };
    await _userService.StopSubscribeAsync(user);
    tokenSource.Cancel();

    await _client.SendTextMessageAsync(chatId, "You unsubscribed successfully");

}

Definition of tokenSource: private CancellationTokenSource tokenSource = new();

I think there are some issues with CancelationToken with threads. When I tried to debug, I didn't hit to block "catch".

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Ilya
  • 5
  • 3
  • So the `tokenSource` field is assigned to a new `CancellationTokenSource` instance only once, and it's never reassigned, even after its cancellation? – Theodor Zoulias Nov 01 '21 at 07:23
  • @TheodorZoulias, you are right. Does it incorrect? – Ilya Nov 01 '21 at 07:26
  • Have you tried putting a breakpoint on tokenSource.Cancel() in OnStopSubscibeAsync and on tokenSource.Dispose() in OnStartSubscribeAsync? Which one is hit first? – Steeeve Nov 01 '21 at 07:27
  • @Steeeve, yes I tried, but it called just tokenSouce.Stop(). After that messeages keep sending and it doesn't call a sourceTorken.Dispose(). Maybe I have a two different toukenSource for two threads, for example? – Ilya Nov 01 '21 at 07:37
  • 1
    You should include all the code that touches `tokenSource`. In general it is no problem passing the CancellationTokenSource.Token to many tasks, so the problem has to do with how you are creating/canceling/disposing the source. – Steeeve Nov 01 '21 at 07:42
  • The problem you describe could be a result of race conditions while replacing `CancellationTokenSource` instances. If your program uses just a single `CancellationTokenSource` instance for its whole lifetime, my suggestion will be to make clear your intentions by declaring the `tokenSource` field as `readonly`, so that we can safely dismiss this possibility. – Theodor Zoulias Nov 01 '21 at 07:47
  • Probably you will need one CTS per client as you can have more than one subscribed client, so you can't have a single one for all clients. So it should reside somewhere in the implementation of `ITelegramBotClient`. – Steeeve Nov 01 '21 at 07:52
  • I made a field as a readonly, but it doesn't help. ITelegramBotClient is a just library for working with Telegram. Should I create a tokenSource in the OnStartSubscribe method? – Ilya Nov 01 '21 at 08:00
  • You'll have to associate one CTS with a client, whatever way. Otherwise you don't know which one of them to cancel. I don't know if ITelegramBotClient has a unique identifier, you could use it in a Dictionary for example. – Steeeve Nov 01 '21 at 08:06
  • thanks, but where should I create a CTS? Should it be in the OnStartSubscribe method? – Ilya Nov 01 '21 at 08:23
  • How/where are you calling the `OnStopSubscibeAsync` method? Also could replace this: `await Task.Delay(3000)` with this: `await Task.Delay(3000, tokenSource.Token)`, to see if it makes any difference? – Theodor Zoulias Nov 01 '21 at 08:24
  • @Ilya Yes, OnStartSubscribe would be the right place. – Steeeve Nov 01 '21 at 08:26
  • @TheodorZoulias, I'm not an expirence in awsync/await. What difference between these calls? – Ilya Nov 01 '21 at 08:31
  • Ilya configuring the `Task.Delay` with a token means that when the token is canceled, the `Task.Delay` task will also get canceled immediately. – Theodor Zoulias Nov 01 '21 at 08:33
  • @Steeeve I don't know how it's working. But Dictionary in method OnStopReceving is always null. Could you help me? – Ilya Nov 01 '21 at 09:19
  • As a side note, checking `token.IsCancellationRequested` before `token.ThrowIfCancellationRequested()` is redundant. The `ThrowIfCancellationRequested` does this check internally ([source code](https://referencesource.microsoft.com/mscorlib/system/threading/CancellationToken.cs.html#d2864d73804288fc)). – Theodor Zoulias Nov 01 '21 at 09:20
  • Post the code how you tried to solve it, we can't only gues where the problem resides. – Steeeve Nov 01 '21 at 09:21
  • I edited question and added a new functionality. Please, check) – Ilya Nov 01 '21 at 09:37
  • Ilya could you format the code properly, and also split long lines of code by adding line-breaks so that the code is visible without horizontal scrolling? – Theodor Zoulias Nov 01 '21 at 09:48
  • And even more important, add the classes containing the methods and fields. It is unclear how the methods relate to each other and where they get called. – Steeeve Nov 01 '21 at 09:50
  • Actually no, we shouldn't try to debug a continuously changing piece of code. If you don't mind, I am rolling back the question to the revision 3. – Theodor Zoulias Nov 01 '21 at 09:51
  • @TheodorZoulias I took that part of code from MSDN. Sorry< I don't understand how I could resolve that issue – Ilya Nov 01 '21 at 12:53
  • Ilya you could include in the question the link of the MSDN document, where the code is originated from. – Theodor Zoulias Nov 01 '21 at 15:23

0 Answers0