Telegrator provides powerful concurrency control mechanisms to manage how your bot handles multiple simultaneous updates. This is crucial for preventing race conditions, managing resources, and implementing sequential flows.
Control the maximum number of handlers that can execute simultaneously using MaximumParallelWorkingHandlers:
using Telegrator;var options = new TelegratorOptions{ // Limit to 10 concurrent handlers MaximumParallelWorkingHandlers = 10};var bot = new TelegratorClient("YOUR_BOT_TOKEN", options);bot.Handlers.AddHandler<MyHandler>();bot.StartReceiving();
// Bot that calls an external API with rate limitingvar options = new TelegratorOptions{ // API allows 5 concurrent requests MaximumParallelWorkingHandlers = 5};var bot = new TelegratorClient(token, options);[MessageHandler]public class ApiCallHandler : MessageHandler{ private readonly HttpClient _httpClient; public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { // This will never have more than 5 instances running at once var response = await _httpClient.GetAsync( "https://api.example.com/data", cancellation); var data = await response.Content.ReadAsStringAsync(cancellation); await Reply(data, cancellationToken: cancellation); return Result.Ok(); }}
Setting MaximumParallelWorkingHandlers to null (default) allows unlimited concurrent handlers.
Setting it to 1 makes all handler execution sequential.
using Telegram.Bot.Types.Enums;using Telegrator.Attributes;[CommandHandler][CommandAllias("upload")][MightAwait(UpdateType.Message)] // This handler might await a messagepublic class UploadCommandHandler : CommandHandler{ public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { await Reply("Please send me a file to upload.", cancellationToken: cancellation); // Wait for the next message from this user var fileMessage = await AwaitingProvider .Message(HandlingUpdate) .AddFilter(Filter<Update>.If(ctx => ctx.Input.Message?.Document != null)) .Await(cancellation); // Process the file var fileName = fileMessage.Document?.FileName ?? "unknown"; await Reply($"Received file: {fileName}", cancellationToken: cancellation); return Result.Ok(); }}
If you don’t add [MightAwait], the awaited update types won’t be routed to your bot,
and Await() will hang indefinitely!
Add filters to awaited updates to ensure you receive the expected data:
[CommandHandler][CommandAllias("guess")][MightAwait(UpdateType.Message)]public class GuessingGameHandler : CommandHandler{ public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { var random = new Random(); int targetNumber = random.Next(1, 11); await Reply("I'm thinking of a number between 1 and 10. Guess it!", cancellationToken: cancellation); while (!cancellation.IsCancellationRequested) { // Wait for a message containing a number var guessMessage = await AwaitingProvider .Message(HandlingUpdate) .AddFilter(Filter<Update>.If(ctx => { var text = ctx.Input.Message?.Text; return text != null && int.TryParse(text, out _); })) .Await(cancellation); int guess = int.Parse(guessMessage.Text ?? "0"); if (guess == targetNumber) { await Client.SendTextMessageAsync( Input.Chat.Id, $"🎉 Correct! The number was {targetNumber}!", cancellationToken: cancellation); break; } else if (guess < targetNumber) { await Client.SendTextMessageAsync( Input.Chat.Id, "📈 Too low! Try again.", cancellationToken: cancellation); } else { await Client.SendTextMessageAsync( Input.Chat.Id, "📉 Too high! Try again.", cancellationToken: cancellation); } } return Result.Ok(); }}
By default, awaiting is user-specific. Customize this behavior:
using Telegrator.StateKeeping;[CommandHandler][CommandAllias("groupvote")][MightAwait(UpdateType.Message)]public class GroupVoteHandler : CommandHandler{ public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { await Reply("Starting group vote! Reply with your choice.", cancellationToken: cancellation); // Wait for ANY user in this chat (not just the command sender) var voteMessage = await AwaitingProvider .Message(HandlingUpdate) .Await(new ChatIdResolver(), cancellation); await Reply( $"Vote received from {voteMessage.From?.FirstName}: {voteMessage.Text}", cancellationToken: cancellation); return Result.Ok(); }}
Control whether awaiting handlers receive updates exclusively:
var options = new TelegratorOptions{ // When true, updates go ONLY to awaiting handlers (not regular handlers) // When false, updates go to both awaiting and regular handlers ExclusiveAwaitingHandlerRouting = true};var bot = new TelegratorClient(token, options);
true: When you want awaited updates to be handled ONLY by the awaiting handler
false: When you want normal handlers to also process updates during awaits
// With ExclusiveAwaitingHandlerRouting = true[CommandHandler][CommandAllias("ask")][MightAwait(UpdateType.Message)]public class AskHandler : CommandHandler{ public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { await Reply("What's your name?", cancellationToken: cancellation); // The next message will ONLY go to this awaiter, // not to any other MessageHandler var response = await AwaitingProvider .Message(HandlingUpdate) .Await(cancellation); await Reply($"Hello, {response.Text}!", cancellationToken: cancellation); return Result.Ok(); }}[MessageHandler] // This handler WON'T receive the awaited messagepublic class EchoHandler : MessageHandler{ public override async Task<Result> Execute( IHandlerContainer<Message> container, CancellationToken cancellation) { await Reply($"Echo: {Input.Text}", cancellationToken: cancellation); return Result.Ok(); }}
Provide a global cancellation token for graceful shutdown:
var cancellationTokenSource = new CancellationTokenSource();var options = new TelegratorOptions{ GlobalCancellationToken = cancellationTokenSource.Token};var bot = new TelegratorClient(token, options);bot.StartReceiving();// Later, to shut down gracefully:cancellationTokenSource.Cancel();
// Multiple users can be processed in parallel,// but each user's messages are processed sequentially// (Implement using custom concurrency control in handlers)