Overview
Telegrator implements the Mediator Pattern through its UpdateRouter component. This design pattern centralizes complex update distribution logic, reducing coupling between components and making the system easier to understand and maintain.
The UpdateRouter
The UpdateRouter is the central mediator that coordinates the flow of Telegram updates to appropriate handlers.
Core Responsibilities
Update Reception Receives updates from Telegram Bot API via polling or webhooks
Handler Discovery Finds matching handlers based on update type and filters
Priority Management Manages handler execution order based on importance
Exception Handling Routes exceptions to appropriate exception handlers
Interface Definition
public interface IUpdateRouter : IUpdateHandler , IPollingProvider
{
/// < summary >
/// Gets the TelegratorOptions for the router.
/// </ summary >
public TelegratorOptions Options { get ; }
/// < summary >
/// Gets the IUpdateHandlersPool that manages handler execution.
/// </ summary >
public IUpdateHandlersPool HandlersPool { get ; }
/// < summary >
/// Gets or sets the IRouterExceptionHandler for handling exceptions.
/// </ summary >
public IRouterExceptionHandler ? ExceptionHandler { get ; set ; }
/// < summary >
/// Default handler container factory
/// </ summary >
public IHandlerContainerFactory ? DefaultContainerFactory { get ; set ; }
}
Update Distribution Flow
The router follows a sophisticated dispatch mechanism to ensure updates reach the right handlers.
Handler Providers
The router uses two types of providers to locate handlers:
HandlersProvider
Manages regular handlers registered for various update types.
public interface IHandlersProvider
{
bool TryGetDescriptorList ( UpdateType type , out HandlerDescriptorList ? descriptors );
UpdateHandlerBase GetHandlerInstance ( HandlerDescriptor descriptor ,
CancellationToken cancellationToken );
}
Regular handlers are the primary handlers for processing updates. They’re registered at startup and remain available throughout the bot’s lifetime.
AwaitingProvider
Manages temporary handlers waiting for specific user responses.
public interface IAwaitingProvider : IHandlersProvider
{
// Inherits from IHandlersProvider
// Provides handlers that are waiting for specific updates
}
Awaiting handlers are checked before regular handlers, allowing you to temporarily override default behavior for specific users or conversations.
Handler Discovery Process
The router uses a multi-step process to find matching handlers:
Step 1: Query by Update Type
protected virtual IEnumerable < DescribedHandlerInfo > GetHandlers (
IHandlersProvider provider ,
ITelegramBotClient client ,
Update update ,
CancellationToken cancellationToken = default )
{
Alligator . LogTrace ( "Requested handlers for UpdateType.{0}" , update . Type );
if ( ! provider . TryGetDescriptorList ( update . Type , out HandlerDescriptorList ? descriptors ))
{
Alligator . LogTrace ( "No registered, providing Any" );
provider . TryGetDescriptorList ( UpdateType . Unknown , out descriptors );
}
if ( descriptors == null || descriptors . Count == 0 )
{
Alligator . LogTrace ( "No handlers provided" );
return [];
}
return DescribeDescriptors ( provider , descriptors , client , update , cancellationToken );
}
If no handlers are registered for a specific update type, the router falls back to UpdateType.Unknown handlers. If you want to handle all update types, register your handler with UpdateType.Unknown.
Step 2: Describe Descriptors
Processes handler descriptors in reverse order (last registered = highest priority).
protected virtual IEnumerable < DescribedHandlerInfo > DescribeDescriptors (
IHandlersProvider provider ,
HandlerDescriptorList descriptors ,
ITelegramBotClient client ,
Update update ,
CancellationToken cancellationToken = default )
{
Alligator . LogTrace (
"Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})" ,
descriptors . HandlingType , update . Id );
foreach ( HandlerDescriptor descriptor in descriptors . Reverse ())
{
cancellationToken . ThrowIfCancellationRequested ();
DescribedHandlerInfo ? describedHandler = DescribeHandler (
provider , descriptor , client , update ,
out bool breakRouting , cancellationToken );
if ( breakRouting )
yield break ;
if ( describedHandler == null )
continue ;
yield return describedHandler ;
}
}
Step 3: Validate Filters
Each handler’s filters are validated before execution.
public virtual DescribedHandlerInfo ? DescribeHandler (
IHandlersProvider provider ,
HandlerDescriptor descriptor ,
ITelegramBotClient client ,
Update update ,
out bool breakRouting ,
CancellationToken cancellationToken = default )
{
breakRouting = false ;
cancellationToken . ThrowIfCancellationRequested ();
UpdateHandlerBase handlerInstance = provider . GetHandlerInstance (
descriptor , cancellationToken );
FilterExecutionContext < Update > filterContext =
new FilterExecutionContext < Update >( _botInfo , update , update , data , []);
if ( descriptor . Filters != null )
{
FiltersFallbackReport report = new FiltersFallbackReport ( descriptor , filterContext );
Result filtersResult = descriptor . Filters . Validate (
filterContext , descriptor . FormReport , ref report );
if ( filtersResult . RouteNext )
{
Result fallbackResult = handlerInstance . FiltersFallback (
report , client , cancellationToken ). Result ;
breakRouting = ! fallbackResult . RouteNext ;
return null ;
}
else if ( ! filtersResult . Positive )
{
return null ;
}
}
return new DescribedHandlerInfo (
descriptor , this , AwaitingProvider ,
client , handlerInstance , filterContext , descriptor . DisplayString );
}
Handler Execution Pool
The UpdateHandlersPool manages concurrent handler execution based on configuration.
Concurrency Control
public class TelegratorOptions
{
/// < summary >
/// Maximum number of handlers that can execute in parallel.
/// Null means unlimited concurrency.
/// </ summary >
public int ? MaximumParallelWorkingHandlers { get ; set ; }
}
Set MaximumParallelWorkingHandlers to control resource usage. A value of 1 ensures sequential processing, while null allows unlimited concurrency.
Queue Management
Handlers are enqueued and executed based on the pool’s capacity:
await HandlersPool . Enqueue ( handlers );
Routing Modes
Exclusive Awaiting Handler Routing
When enabled, awaiting handlers block regular handlers from executing:
if ( handlers . Any ())
{
await HandlersPool . Enqueue ( handlers );
// Check if awaiting handlers have exclusive routing
if ( Options . ExclusiveAwaitingHandlerRouting )
{
Alligator . LogTrace (
"Receiving Update ({0}) completed with only awaiting handlers" ,
update . Id );
return ;
}
}
This is useful for implementing conversation flows where you want to temporarily override default behavior while waiting for user input.
Exception Handling
The router provides centralized exception handling through the IRouterExceptionHandler interface.
public virtual Task HandleErrorAsync (
ITelegramBotClient botClient ,
Exception exception ,
HandleErrorSource source ,
CancellationToken cancellationToken )
{
Alligator . LogDebug ( "Handling exception {0}" , exception . GetType (). Name );
ExceptionHandler ? . HandleException ( botClient , exception , source , cancellationToken );
return Task . CompletedTask ;
}
Exception Sources
HandleErrorSource.PollingError - Errors during update polling
HandleErrorSource.HandleUpdateError - Errors during handler execution
Handler Lifetime Management
The router manages handler lifecycles through HandlerLifetimeToken:
public class HandlerLifetimeToken
{
public bool IsEnded { get ; private set ; }
public void LifetimeEnded ()
{
IsEnded = true ;
}
}
Handlers are automatically disposed after execution completes.
Best Practices
Use Awaiting Handlers for Conversations
When building conversational flows, use awaiting handlers to temporarily intercept updates for specific users: // Register an awaiting handler for a specific user
await awaitingProvider . RegisterHandler ( userId , handler );
Configure Concurrency Appropriately
Handle Exceptions Gracefully
Implement a custom exception handler to log errors and notify administrators: router . ExceptionHandler = new CustomExceptionHandler ();
Use Appropriate Result Types
Return the correct Result type to control routing:
Result.Ok() when the handler fully processed the update
Result.Next() when other handlers should also process it
Result.Fault() on errors that should stop routing
Architecture Learn about the overall framework architecture
Handlers Explore different handler types
Results Master handler result types
Filters Understand the filter validation system