2026-03-07 23:22:40 +04:00
# Getting Started with Telegrator
---
This guide will walk you through the core concepts and advanced features of **Telegrator ** — a modern, aspect-oriented, mediator-based framework for building powerful and maintainable Telegram bots in C#.
- [1. Installation ](#1-installation )
2026-03-07 23:30:47 +04:00
- [2. Quick Start ](#2-quick-start )
- [2.1. Your First Bot ](#21-your-first-bot )
- [2.2. Basic Handler Types ](#22-basic-handler-types )
- [2.3. Simple Filters ](#23-simple-filters )
- [3. Core Concepts ](#3-core-concepts )
- [3.1. Handler System ](#31-handler-system )
- [3.2. Filter System ](#32-filter-system )
- [3.3. State Management ](#33-state-management )
- [3.4. Update Routing ](#34-update-routing )
- [4. Intermediate Topics ](#4-intermediate-topics )
- [4.1. Advanced Filters ](#41-advanced-filters )
- [4.2. Awaiting Mechanism ](#42-awaiting-mechanism )
- [4.3. Branching Handlers ](#43-branching-handlers )
- [4.4. Advanced Hosting Integration ](#44-advanced-hosting-integration )
- [5. Advanced Topics ](#5-advanced-topics )
- [5.1. Aspects & Cross-Cutting Concerns ](#51-aspects--cross-cutting-concerns )
- [5.2. Custom Extensions ](#52-custom-extensions )
- [5.3. Performance Optimization ](#53-performance-optimization )
- [5.4. Error Handling ](#54-error-handling )
- [6. Integration & Deployment ](#6-integration--deployment )
- [6.1. Console Applications ](#61-console-applications )
- [6.2. ASP.NET Core Hosting ](#62-aspnet-core-hosting )
- [6.3. Webhook Deployment ](#63-webhook-deployment )
- [6.4. Configuration Management ](#64-configuration-management )
- [7. Best Practices & Patterns ](#7-best-practices--patterns )
- [7.1. Project Organization ](#71-project-organization )
- [7.2. Testing Strategies ](#72-testing-strategies )
- [7.3. Common Patterns ](#73-common-patterns )
- [7.4. Performance Tips ](#74-performance-tips )
- [7.5. Logging System ](#75-logging-system )
- [8. FAQ & Troubleshooting ](#8-faq--troubleshooting )
- [8.1. Common Issues ](#81-common-issues )
- [8.2. Debugging Guide ](#82-debugging-guide )
- [9. Links ](#9-links )
2026-03-07 23:22:40 +04:00
---
## 1. Installation
**Telegrator ** is distributed as a NuGet package. You can install it using the .NET CLI, the NuGet Package Manager Console, or by managing NuGet packages in Visual Studio.
### Prerequisites
2026-03-07 23:30:47 +04:00
- .NET >= 5.0 `or` .NET Core >= 2.0 `or` Framework >= 4.6.1 (.NET Standard 2.0 compatible)
2026-03-07 23:22:40 +04:00
- A Telegram Bot Token from [@BotFather ](https://t.me/BotFather ).
### .NET CLI
``` shell
dotnet add package Telegrator
```
### Package Manager Console
``` shell
Install-Package Telegrator
```
### Hosting Integrations
- .NET Core >= 8.0
- `Telegrator.Hosting` : For console/background services
2026-03-07 23:30:47 +04:00
- `Telegrator.Hosting.Web` : For ASP.NET Core/Webhook
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
``` shell
# For console/background services
dotnet add package Telegrator.Hosting
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
# For webhook hosting
dotnet add package Telegrator.Hosting.Web
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
---
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
## 2. Quick Start
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
This section will get you up and running with Telegrator quickly. You'll learn the basics and create your first bot in minutes.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 2.1. Your First Bot
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
Let's create a simple bot that replies to any private message containing "hello":
2026-03-07 23:22:40 +04:00
``` csharp
using Telegrator ;
using Telegrator.Handlers ;
using Telegrator.Annotations ;
using Telegram.Bot.Types ;
using Telegram.Bot.Types.Enums ;
[MessageHandler]
[ChatType(ChatType.Private)]
[TextContains("hello", StringComparison.InvariantCultureIgnoreCase)]
public class HelloHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Hello! Nice to meet you!" , cancellationToken : cancellation ) ;
return Ok ;
}
}
class Program
{
static void Main ( string [ ] args )
{
2026-03-07 23:30:47 +04:00
var bot = new TelegratorClient ( "<YOUR_BOT_TOKEN>" ) ;
2026-03-07 23:22:40 +04:00
bot . Handlers . AddHandler < HelloHandler > ( ) ;
bot . StartReceiving ( ) ;
Console . ReadLine ( ) ;
}
}
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **`[MessageHandler]`**: Marks the class as a handler for message updates
> 2. **`[ChatType(ChatType.Private)]`**: Only processes private chat messages
> 3. **`[TextContains("hello")]`**: Only processes messages containing "hello" (case-insensitive)
> 4. **`TelegratorClient`**: Main bot client that manages Telegram connection
> 5. **`bot.Handlers.AddHandler<HelloHandler>()`**: Registers the handler
> 6. **`bot.StartReceiving()`**: Starts the long-polling loop
> 7. **`Reply(...)`**: Sends a reply to the original message
### 2.2. Basic Handler Types
Telegrator provides several handler types for different update types:
#### MessageHandler
Handles text messages and media:
``` csharp
[MessageHandler]
[TextContains("hello")]
public class GreetingHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Hello there!" ) ;
return Ok ;
}
}
```
#### CommandHandler
Handles bot commands (messages starting with `/` ):
``` csharp
[CommandHandler]
[CommandAlias("start")]
public class StartHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Welcome! Use /help to see available commands." ) ;
return Ok ;
}
}
```
#### CallbackQueryHandler
Handles button clicks and inline keyboard interactions:
``` csharp
[CallbackQueryHandler]
[TextStartsWith("action_")]
public class ActionHandler : CallbackQueryHandler
{
public override async Task < Result > Execute ( IHandlerContainer < CallbackQuery > container , CancellationToken cancellation )
{
await AnswerCallbackQuery ( "Action completed!" ) ;
return Ok ;
}
}
```
#### AnyUpdateHandler
Handles any type of update:
``` csharp
[AnyUpdateHandler]
public class LoggingHandler : AnyUpdateHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Update > container , CancellationToken cancellation )
{
Console . WriteLine ( $"Received update: {container.HandlingUpdate.Type}" ) ;
return Ok ;
}
}
```
### 2.3. Simple Filters
Filters determine when your handlers should run. Here are the most common ones:
#### Text Filters
``` csharp
[TextContains("hello")] // Message contains "hello"
[TextStartsWith("/")] // Message starts with "/"
[TextStartsWith("/", Modifiers = FilterModifier.Not)] // Message does NOT start with "/"
```
#### User Filters
``` csharp
[FromUserId(123456789)] // Only from specific user ID
[FromUser("John")] // Only from user with specific name
[FromUsername("john_doe")] // Only from user with specific username
```
#### Chat Filters
``` csharp
[ChatType(ChatType.Private)] // Only private chats
[ChatType(ChatType.Group)] // Only group chats
[Mentioned] // Only if bot was mentioned with @
```
#### Command Filters
``` csharp
[CommandAlias("start")] // Only for /start command
[CommandAlias("help")] // Only for /help command
```
#### Combining Filters
Filters are combined with AND logic by default. You can use modifiers:
2026-03-07 23:22:40 +04:00
``` csharp
[MessageHandler]
[ChatType(ChatType.Private)]
[TextContains("hello", Modifiers = FilterModifier.Not)]
public class NotHelloHandler : MessageHandler
{
// Runs for private messages that do NOT contain "hello"
}
[MessageHandler]
[TextContains("bot", Modifiers = FilterModifier.OrNext)]
[Mentioned()]
2026-03-07 23:30:47 +04:00
public class BotMentionHandler : MessageHandler
{
// Runs for messages that contain "bot" OR if bot was mentioned
}
```
> **Filter Modifiers:**
> - `FilterModifier.Not` - Inverts the filter
> - `FilterModifier.OrNext` - Combines with next filter using OR logic
> - Can be combined: `Modifiers = FilterModifier.Not | FilterModifier.OrNext`
---
## 3. Core Concepts
This section covers the fundamental concepts and architecture of Telegrator.
### 3.1. Handler System
Telegrator is built around several core ideas:
- **Aspect-Oriented Handlers**: Each handler is a focused, reusable class that reacts to a specific type of update (message, command, callback, etc.).
- **Aspect-Oriented Programming**: Built-in support for pre and post-execution processing through aspects, enabling separation of cross-cutting concerns.
- **Mediator Pattern**: All updates are routed through a central `UpdateRouter` , which dispatches them to the appropriate handlers based on filters and priorities.
- **Filters as Attributes**: Handler classes are decorated with filter attributes that declaratively specify when the handler should run.
- **State Management**: Built-in mechanisms for managing user/chat state without external storage.
- **Concurrency Control**: Fine-grained control over how many handlers run in parallel, both globally and per-handler.
#### Handler Lifecycle
1. **Registration ** : Handlers are registered with the bot during startup
2. **Discovery ** : The framework automatically discovers handlers using reflection
3. **Filtering ** : Updates are filtered to determine which handlers should run
4. **Execution ** : Selected handlers are executed in order of priority
5. **Cleanup ** : Resources are cleaned up after execution
#### Handler Priority & Importance
- **Importance**: Internal priority based on handler type (CommandHandler > MessageHandler > AnyUpdateHandler)
- **Priority**: User-defined global priority for execution order
- **Combined Order**: Importance first, then Priority
#### Implicit Handlers from Methods
You can create handlers directly from methods without defining full handler classes. This is useful for simple handlers or quick prototyping:
``` csharp
// Simple echo handler
[MessageHandler]
[TextStartsWith("/", Modifiers = FilterModifier.Not)]
private static async Task < Result > EchoHandler ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
await container . Reply ( $"You said: \" { container . Input . Text } \ "" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Command handler with inline keyboard
[CommandHandler]
[CommandAlias("menu")]
private static async Task < Result > MenuHandler ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
var keyboard = new InlineKeyboardMarkup ( new [ ]
{
InlineKeyboardButton . WithCallbackData ( "Option 1" , "option1" ) ,
InlineKeyboardButton . WithCallbackData ( "Option 2" , "option2" )
} ) ;
await container . Reply ( "Choose an option:" , replyMarkup : keyboard , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Callback query handler
[CallbackQueryHandler]
[CallbackData("option1")]
private static async Task < Result > Option1Handler ( IHandlerContainer < CallbackQuery > container , CancellationToken cancellationToken )
{
await container . AnswerCallbackQuery ( "You selected Option 1!" , cancellationToken : cancellationToken ) ;
await container . EditMessage ( "You selected Option 1!" ) ;
return Ok ;
}
// Register all methods as handlers
builder . Handlers . AddMethod < Message > ( EchoHandler ) ;
builder . Handlers . AddMethod < Message > ( MenuHandler ) ;
builder . Handlers . AddMethod < CallbackQuery > ( Option1Handler ) ;
```
**Advanced Example with State Management: **
``` csharp
public enum UserState
{
Start = SpecialState . NoState ,
WaitingForName ,
WaitingForAge
}
// Start conversation
[CommandHandler]
[CommandAlias("register")]
[EnumState<UserState>(UserState.Start)]
private static async Task < Result > StartRegistration ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
container . ForwardEnumState < UserState > ( ) ;
await container . Reply ( "Please enter your name:" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Handle name input
[MessageHandler]
[EnumState<UserState>(UserState.WaitingForName)]
private static async Task < Result > HandleName ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
var name = container . Input . Text ;
container . ForwardEnumState < UserState > ( ) ;
await container . Reply ( $"Hello {name}! Please enter your age:" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Handle age input
[MessageHandler]
[EnumState<UserState>(UserState.WaitingForAge)]
private static async Task < Result > HandleAge ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
if ( int . TryParse ( container . Input . Text , out int age ) )
{
container . DeleteEnumState < UserState > ( ) ;
await container . Reply ( $"Registration complete! Name: {name}, Age: {age}" , cancellationToken : cancellationToken ) ;
}
else
{
await container . Reply ( "Please enter a valid age (number):" , cancellationToken : cancellationToken ) ;
}
return Ok ;
2026-03-07 23:22:40 +04:00
}
2026-03-07 23:30:47 +04:00
// Register state management handlers
builder . Handlers . AddMethod < Message > ( StartRegistration ) ;
builder . Handlers . AddMethod < Message > ( HandleName ) ;
builder . Handlers . AddMethod < Message > ( HandleAge ) ;
2026-03-07 23:22:40 +04:00
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Method Signature**: Methods must return `Task<Result>` and accept `IHandlerContainer<T>` and `CancellationToken`
> 2. **Attributes**: Apply the same filter attributes as regular handlers (`[MessageHandler]`, `[CommandHandler]`, etc.)
> 3. **Container Methods**: Use extension methods like `container.Reply()`, `container.Response()`, etc.
> 4. **Registration**: Use `AddMethod<T>()` to register methods as handlers
> 5. **State Management**: Same state management patterns as regular handlers
> 6. **Flexibility**: Can be used for simple handlers or complex multi-step conversations
### 3.2. Filter System
Filters are the gatekeepers of your bot logic. They determine when handlers should be executed.
#### Filter Types
- **Text Filters**: `TextContains` , `TextStartsWith` , `RegexFilter`
- **User Filters**: `FromUserId` , `FromUser` , `FromUsername`
- **Chat Filters**: `ChatType` , `Mentioned` , `HasReply`
- **Command Filters**: `CommandAlias`
- **State Filters**: `EnumState` , `NumericState` , `StringState`
#### Filter Composition
- **AND Logic**: Multiple filters are combined with AND by default
- **OR Logic**: Use `FilterModifier.OrNext` to combine with OR
- **NOT Logic**: Use `FilterModifier.Not` to invert a filter
- **Combined Modifiers**: Use bitwise OR to combine modifiers
#### Custom Filters
You can create custom filters by inheriting from `FilterAnnotation<T>` :
``` csharp
public class AdminOnlyAttribute : FilterAnnotation < Message >
{
private readonly List < long > _adminIds = [ ] ;
public void AddAdmin ( long id )
= > _adminIds . Add ( id ) ;
public override bool CanPass ( FilterExecutionContext < Message > context )
= > _adminIds . Contains ( context . Input . From ? . Id ) ;
}
```
#### Hosting Integration - Access to DI Container
When using Telegrator.Hosting, filters can access the DI container and configuration through `HostedTelegramBotInfo` :
``` csharp
public class DatabaseUserFilterAttribute : FilterAnnotation < Message >
{
public override bool CanPass ( FilterExecutionContext < Message > context )
{
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
using ( var scope = botInfo . Services . CreateScope ( ) )
{
var configuration = scope . ServiceProvider . GetRequiredService < IConfiguration > ( ) ;
var dbContext = scope . ServiceProvider . GetRequiredService < UsersDbContext > ( ) ;
var telegramId = context . Input . From ? . Id ;
if ( telegramId = = null )
return false ;
var user = dbContext . Users . FirstOrDefault ( u = > u . TelegramId = = telegramId ) ;
return user ? . IsActive = = true ;
}
}
}
// Usage in handler
[MessageHandler]
[DatabaseUserFilter]
public class ActiveUserHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Hello, active user!" ) ;
return Ok ;
}
}
```
**Configuration-based Filter: **
``` csharp
public class ConfigurableFilterAttribute : FilterAnnotation < Message >
{
private readonly string _configKey ;
public ConfigurableFilterAttribute ( string configKey )
{
_configKey = configKey ;
}
public override bool CanPass ( FilterExecutionContext < Message > context )
{
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
var configuration = botInfo . Services . GetRequiredService < IConfiguration > ( ) ;
var allowedUsers = configuration . GetSection ( _configKey ) . Get < List < long > > ( ) ? ? [ ] ;
return allowedUsers . Contains ( context . Input . From ? . Id ? ? 0 ) ;
}
}
// Usage
[MessageHandler]
[ConfigurableFilter("AllowedUsers")]
public class RestrictedHandler : MessageHandler
{
// Handler implementation
}
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Key Points: **
- **Hosting Only**: This feature is only available when using `Telegrator.Hosting`
- **Type Casting**: Cast `context.BotInfo` to `HostedTelegramBotInfo`
- **Service Access**: Use `botInfo.Services.GetRequiredService<T>()` to access DI services
- **Configuration Access**: Use `botInfo.Services.GetRequiredService<IConfiguration>()` for settings
- **Null Safety**: Always check if the cast is successful before using services
### 3.3. State Management
2026-03-07 23:22:40 +04:00
Telegrator provides built-in state management for multi-step conversations (wizards, forms, quizzes) without a database.
> [!NOTE]
2026-03-07 23:30:47 +04:00
> Each type of `StateKeeper`'s (EnumStateKeeper, NumericStateKeeper) is shared between **EVERY** handler in project.
2026-03-07 23:22:40 +04:00
**Types of State: **
- **NumericState**: Integer-based steps
- **StringState**: Named steps
- **EnumState**: Enum-based scenarios
**How to Use: **
1. Define your state (enum/int/string)
2. Use a state filter attribute on your handler:
- `[EnumState<MyEnum>(MyEnum.Step1)]`
- `[NumericState(1)]`
2026-03-07 23:30:47 +04:00
- `[StringState("waiting_input")]`
2026-03-07 23:22:40 +04:00
3. Change state inside the handler using extension methods:
- `container.ForwardEnumState<MyEnum>()`
- `container.ForwardNumericState()`
2026-03-07 23:30:47 +04:00
- `container.ForwardStringState()`
2026-03-07 23:22:40 +04:00
- `container.DeleteEnumState<MyEnum>()`
**Example: **
``` csharp
public enum QuizState
{
Start = SpecialState . NoState , Q1 , Q2
}
[CommandHandler]
[CommandAlias("quiz")]
[EnumState<QuizState>(QuizState.Start)]
public class StartQuizHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
container . ForwardEnumState < QuizState > ( ) ;
await Reply ( "Quiz started! Question 1: What is the capital of France?" ) ;
return Ok ;
}
}
[MessageHandler]
[EnumState<QuizState>(QuizState.Q1)]
public class Q1Handler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
if ( Input . Text . Trim ( ) . Equals ( "Paris" , StringComparison . InvariantCultureIgnoreCase ) )
await Reply ( "Correct!" ) ;
else
await Reply ( "Incorrect. The answer is Paris." ) ;
container . ForwardEnumState < QuizState > ( ) ;
await Reply ( "Question 2: What is 2 + 2?" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Enum State Definition**: `QuizState` enum defines the conversation flow with `Start = SpecialState.NoState` indicating no initial state.
> 2. **State Filter**: `[EnumState<QuizState>(QuizState.Start)]` ensures the handler only runs when the user is in the "Start" state.
> 3. **State Transition**: `container.ForwardEnumState<QuizState>()` moves the user to the next state (Q1).
> 4. **Next Handler**: The `Q1Handler` will only run when the user is in state `QuizState.Q1`.
> 5. **State Management**: Each handler manages its own state transition, creating a clear conversation flow.
2026-03-07 23:30:47 +04:00
### 3.4. Update Routing
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
The `UpdateRouter` is the central component that manages how updates flow through your bot.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### How Updates Are Processed
1. **Reception ** : Updates are received from Telegram via long-polling or webhook
2. **Filtering ** : Each registered handler is checked against the update using its filters
3. **Selection ** : Handlers that pass all filters are selected for execution
4. **Prioritization ** : Selected handlers are sorted by Importance and Priority
5. **Execution ** : Handlers are executed in order, with aspects applied
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Router Configuration
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
var options = new TelegratorOptions
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
MaximumParallelWorkingHandlers = 10 ,
ExclusiveAwaitingHandlerRouting = true ,
ExceptIntersectingCommandAliases = true
} ;
var bot = new TelegratorClient ( "<BOT_TOKEN>" , options ) ;
2026-03-07 23:22:40 +04:00
```
2026-03-07 23:30:47 +04:00
#### Error Handling
The router includes built-in error handling:
- **Exception Handler**: Global exception handler for all errors
- **Handler Errors**: Individual handler errors are caught and logged
- **Recovery**: The router continues processing other handlers even if one fails
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Performance Considerations
- **Parallel Execution**: Multiple handlers can run simultaneously
- **Concurrency Limits**: Control the number of parallel executions
- **Resource Management**: Automatic cleanup of resources after execution
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
---
## 4. Intermediate Topics
This section covers intermediate concepts that build upon the core concepts.
### 4.1. Advanced Filters
You can create custom filters by inheriting from `FilterAnnotation<T>` :
2026-03-07 23:22:40 +04:00
``` csharp
using Telegram.Bot.Types ;
2026-03-07 23:30:47 +04:00
using Telegrator.Attributes ;
2026-03-07 23:22:40 +04:00
using Telegrator.Handlers ;
2026-03-07 23:30:47 +04:00
public class AdminOnlyAttribute : FilterAnnotation < Message >
2026-03-07 23:22:40 +04:00
{
private readonly List < long > _adminIds = [ ] ;
public void AddAdmin ( long id ) = > _adminIds . Add ( id ) ;
public void RemoveAdmin ( long id ) = > _adminIds . Remove ( id ) ;
public override bool CanPass ( FilterExecutionContext < Message > context )
= > _adminIds . Contains ( context . Input . From ? . Id ) ;
}
[MessageHandler]
[AdminOnly]
public class AdminHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Hello, admin!" ) ;
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
// Usage
2026-03-07 23:22:40 +04:00
AdminOnlyAttribute . AddAdmin ( 123456789 ) ;
bot . StartReceiving ( ) ;
```
> **How is it working?**
> 1. **Custom Filter**: `AdminOnlyAttribute` inherits from `FilterAnnotation<Message>` to create a reusable filter attribute.
> 2. **Filter Logic**: `CanPass()` method checks if the message sender's ID matches the admin ID.
2026-03-07 23:30:47 +04:00
> 3. **Usage**: The filter is applied as an attribute `[AdminOnly]` to restrict access to users that are not registered as admins.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 4.2. Awaiting Mechanism
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
Use `AwaitingProvider` to wait for a user's next update (message or callback) inside a handler:
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
[CommandHandler]
[CommandAlias("ask")]
public class AskHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "What is your name?" ) ;
var nextMessage = await container . AwaitMessage ( ) . BySenderId ( cancellation ) ;
await Reply ( $"Hello, {nextMessage.Text}!" ) ;
return Ok ;
}
}
2026-03-07 23:22:40 +04:00
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Awaiting Provider**: `container.AwaitMessage()` creates a temporary handler that waits for the next message.
> 2. **Sender Filter**: `.BySenderId(cancellation)` ensures only messages from the same user are captured.
> 3. **Async Flow**: The handler pauses execution until the user responds, then continues with the conversation.
> 4. **Context Preservation**: The original handler context is maintained during the awaiting process.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 4.3. Branching Handlers
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
For complex scenarios where a single handler needs to handle multiple different update types or conditions, you can use `BranchingUpdateHandler` to create handlers with multiple entry points.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Example: **
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
[MessageHandler]
public class ComplexHandler : BranchingMessageHandler
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
[TextContains("hello")]
public async Task < Result > HandleGreeting ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
await Reply ( "Hello there!" ) ;
return Ok ;
}
[TextContains("help")]
public async Task < Result > HandleHelp ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "How can I help you?" ) ;
return Ok ;
}
[FromUser("John")]
public async Task < Result > HandleAdmin ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Admin command received!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Branching Command Handler: **
``` csharp
2026-03-07 23:22:40 +04:00
[CommandHandler]
2026-03-07 23:30:47 +04:00
public class SettingsHandler : BranchingCommandHandler
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
[CommandAlias("settings")]
public async Task < Result > ShowSettings ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
await Reply ( "Settings menu:" ) ;
// Show settings options
2026-03-07 23:22:40 +04:00
return Ok ;
}
2026-03-07 23:30:47 +04:00
[CommandAlias("settings", "language")]
public async Task < Result > SetLanguage ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
var language = Arguments . FirstOrDefault ( ) ;
await Reply ( $"Language set to: {language}" ) ;
return Ok ;
}
[CommandAlias("settings", "theme")]
public async Task < Result > SetTheme ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
var theme = Arguments . FirstOrDefault ( ) ;
await Reply ( $"Theme set to: {theme}" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Multiple Entry Points**: Each method with filters becomes a separate handler entry point.
> 2. **Individual Filtering**: Each method can have its own set of filters and conditions.
> 3. **Shared Context**: All methods share the same handler instance and context.
> 4. **Automatic Registration**: Each method is automatically registered as a separate handler.
> 5. **Command Arguments**: In `BranchingCommandHandler`, you can access command arguments via the `Arguments` property.
> 6. **Flexible Routing**: Perfect for complex bots with many related commands or message patterns.
You can extend Telegrator by creating custom filters, attributes, and state keepers.
### 4.4. Advanced Hosting Integration
When using Telegrator.Hosting, you can create powerful filters that integrate with your application's services and configuration.
#### Database-Integrated Filters
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
public class PremiumUserFilterAttribute : FilterAnnotation < Message >
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
public override bool CanPass ( FilterExecutionContext < Message > context )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
var dbContext = botInfo . Services . GetRequiredService < ApplicationDbContext > ( ) ;
var user = dbContext . Users
. FirstOrDefault ( u = > u . TelegramId = = context . Input . From ? . Id ) ;
return user ? . SubscriptionLevel = = SubscriptionLevel . Premium ;
2026-03-07 23:22:40 +04:00
}
}
2026-03-07 23:30:47 +04:00
// Usage
2026-03-07 23:22:40 +04:00
[MessageHandler]
2026-03-07 23:30:47 +04:00
[PremiumUserFilter]
public class PremiumFeatureHandler : MessageHandler
2026-03-07 23:22:40 +04:00
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
2026-03-07 23:30:47 +04:00
await Reply ( "Welcome to premium features!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Configuration-Driven Filters
``` csharp
public class EnvironmentFilterAttribute : FilterAnnotation < Message >
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
private readonly string _environment ;
public EnvironmentFilterAttribute ( string environment )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
_environment = environment ;
}
public override bool CanPass ( FilterExecutionContext < Message > context )
{
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
var configuration = botInfo . Services . GetRequiredService < IConfiguration > ( ) ;
var currentEnv = configuration [ "Environment" ] ? ? "Production" ;
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
return currentEnv . Equals ( _environment , StringComparison . OrdinalIgnoreCase ) ;
2026-03-07 23:22:40 +04:00
}
}
2026-03-07 23:30:47 +04:00
// Usage
[MessageHandler]
[EnvironmentFilter("Development")]
public class DevOnlyHandler : MessageHandler
2026-03-07 23:22:40 +04:00
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
2026-03-07 23:30:47 +04:00
await Reply ( "This feature is only available in development!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Multi-Service Integration
``` csharp
public class RateLimitFilterAttribute : FilterAnnotation < Message >
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
public override bool CanPass ( FilterExecutionContext < Message > context )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
var cache = botInfo . Services . GetRequiredService < IDistributedCache > ( ) ;
var configuration = botInfo . Services . GetRequiredService < IConfiguration > ( ) ;
var userId = context . Input . From ? . Id . ToString ( ) ;
if ( string . IsNullOrEmpty ( userId ) )
return false ;
var cacheKey = $"rate_limit:{userId}" ;
var currentCount = cache . GetString ( cacheKey ) ;
if ( int . TryParse ( currentCount , out int count ) & & count > = 10 )
return false ;
cache . SetString ( cacheKey , ( count + 1 ) . ToString ( ) ,
new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan . FromMinutes ( 1 ) } ) ;
return true ;
2026-03-07 23:22:40 +04:00
}
}
2026-03-07 23:30:47 +04:00
// Usage
[MessageHandler]
[RateLimitFilter]
public class RateLimitedHandler : MessageHandler
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
await Reply ( "Message processed!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
```
2026-03-07 23:30:47 +04:00
#### 2.6.1. Custom Filter Attributes
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Custom Filter Attribute Example: **
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
using Telegram.Bot.Types ;
using Telegrator.Attributes ;
using Telegrator.Handlers ;
public class AdminOnlyAttribute ( ) : FilterAnnotation < Message >
{
private readonly List < long > _adminIds = [ ] ;
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
public void AddAdmin ( long id ) = > _adminIds . Add ( id ) ;
public void RemoveAdmin ( long id ) = > _adminIds . Remove ( id ) ;
2026-03-07 23:22:40 +04:00
public override bool CanPass ( FilterExecutionContext < Message > context )
2026-03-07 23:30:47 +04:00
= > _adminIds . Contains ( context . Input . From ? . Id ) ;
2026-03-07 23:22:40 +04:00
}
[MessageHandler]
2026-03-07 23:30:47 +04:00
[AdminOnly]
public class AdminHandler : MessageHandler
2026-03-07 23:22:40 +04:00
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
2026-03-07 23:30:47 +04:00
await Reply ( "Hello, admin!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
// ...
AdminOnlyAttribute . AddAdmin ( 123456789 ) ;
bot . StartReceiving ( ) ;
2026-03-07 23:22:40 +04:00
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Custom Filter**: `AdminOnlyAttribute` inherits from `FilterAnnotation<Message>` to create a reusable filter attribute.
> 2. **Filter Logic**: `CanPass()` method checks if the message sender's ID matches the admin ID.
> 3. **Usage**: The filter is applied as an attribute `[AdminOnly]` to restrict access to users that are not registered as admins.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### 2.6.2. Branching Handlers
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
For complex scenarios where a single handler needs to handle multiple different update types or conditions, you can use `BranchingUpdateHandler` to create handlers with multiple entry points.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Example: **
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
[MessageHandler]
public class ComplexHandler : BranchingMessageHandler
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
[TextContains("hello")]
public async Task < Result > HandleGreeting ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
await Reply ( "Hello there!" ) ;
return Ok ;
}
[TextContains("help")]
public async Task < Result > HandleHelp ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "How can I help you?" ) ;
return Ok ;
}
[FromUser("John")]
public async Task < Result > HandleAdmin ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Admin command received!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
2026-03-07 23:30:47 +04:00
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Branching Command Handler: **
``` csharp
[CommandHandler]
public class SettingsHandler : BranchingCommandHandler
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
[CommandAlias("settings")]
public async Task < Result > ShowSettings ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Settings menu:" ) ;
// Show settings options
return Ok ;
}
[CommandAlias("settings", "language")]
public async Task < Result > SetLanguage ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
var language = Arguments . FirstOrDefault ( ) ;
await Reply ( $"Language set to: {language}" ) ;
return Ok ;
}
[CommandAlias("settings", "theme")]
public async Task < Result > SetTheme ( IHandlerContainer < Message > container , CancellationToken cancellation )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
var theme = Arguments . FirstOrDefault ( ) ;
await Reply ( $"Theme set to: {theme}" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Multiple Entry Points**: Each method with filters becomes a separate handler entry point.
> 2. **Individual Filtering**: Each method can have its own set of filters and conditions.
> 3. **Shared Context**: All methods share the same handler instance and context.
> 4. **Automatic Registration**: Each method is automatically registered as a separate handler.
> 5. **Command Arguments**: In `BranchingCommandHandler`, you can access command arguments via the `Arguments` property.
> 6. **Flexible Routing**: Perfect for complex bots with many related commands or message patterns.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### 2.6.3. Custom Aspects
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
You can create custom aspects by implementing `IPreProcessor` or `IPostProcessor` interfaces to add cross-cutting concerns to your handlers.
**Creating a Custom Pre-Processor: **
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
using Telegrator.Aspects ;
public class RateLimitProcessor : IPreProcessor
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
private readonly Dictionary < long , DateTime > _lastExecution = new ( ) ;
private readonly TimeSpan _cooldown = TimeSpan . FromSeconds ( 5 ) ;
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
public async Task < Result > BeforeExecution ( IHandlerContainer container )
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:30:47 +04:00
var userId = container . HandlingUpdate . Message ? . From ? . Id ;
if ( userId = = null ) return Ok ;
if ( _lastExecution . TryGetValue ( userId . Value , out var lastExec ) )
{
if ( DateTime . Now - lastExec < _cooldown )
{
return Result . Fault ( ) ; // Stop execution - rate limit exceeded
}
}
_lastExecution [ userId . Value ] = DateTime . Now ;
return Ok ;
}
}
```
**Creating a Custom Post-Processor: **
``` csharp
using Telegrator.Aspects ;
public class MetricsProcessor : IPostProcessor
{
private int _totalExecutions = 0 ;
private readonly object _lock = new ( ) ;
public async Task < Result > AfterExecution ( IHandlerContainer container )
{
lock ( _lock )
{
_totalExecutions + + ;
}
Console . WriteLine ( $"Total handler executions: {_totalExecutions}" ) ;
return Ok ;
2026-03-07 23:22:40 +04:00
}
2026-03-07 23:30:47 +04:00
}
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Applying Custom Aspects: **
``` csharp
[MessageHandler]
[BeforeExecution<RateLimitProcessor>]
[AfterExecution<MetricsProcessor>]
public class RateLimitedHandler : MessageHandler
{
2026-03-07 23:22:40 +04:00
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
2026-03-07 23:30:47 +04:00
await Reply ( "Message processed!" ) ;
2026-03-07 23:22:40 +04:00
return Ok ;
}
}
```
> **How is it working?**
2026-03-07 23:30:47 +04:00
> 1. **Custom Processors**: Implement `IPreProcessor` or `IPostProcessor` to create reusable aspects
> 2. **Flow Control**: Return `Result.Fault()` from pre-processors to stop handler execution
> 3. **State Management**: Processors can maintain their own state for rate limiting, metrics, etc.
> 4. **Reusability**: Custom aspects can be applied to multiple handlers via attributes
> 5. **Separation of Concerns**: Business logic remains separate from cross-cutting concerns
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 2.7. Integration
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
Telegrator works in console, hosted applications, and ASP.NET Core (webhook) projects.
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Console App Example: **
2026-03-07 23:22:40 +04:00
``` csharp
var bot = new TelegratorClient ( "<YOUR_BOT_TOKEN>" ) ;
2026-03-07 23:30:47 +04:00
bot . Handlers . CollectHandlersDomainWide ( ) ;
2026-03-07 23:22:40 +04:00
bot . StartReceiving ( ) ;
```
2026-03-07 23:30:47 +04:00
**Hosting Example: **
``` csharp
using Telegrator.Hosting ;
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
var builder = TelegramBotHost . CreateBuilder ( ) ;
builder . Handlers . AddHandler < StartHandler > ( ) ;
var host = builder . Build ( ) ;
await host . StartAsync ( ) ;
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
**Web Hosting Example: **
``` csharp
using Telegrator.Hosting.Web ;
var webOptions = new TelegramBotWebOptions ( ) ;
var webHost = TelegramBotWebHost . CreateBuilder ( webOptions ) ;
webHost . Handlers . AddHandler < StartHandler > ( ) ;
var host = webHost . Build ( ) ;
await host . StartAsync ( ) ;
```
2026-03-07 23:38:14 +04:00
**Note: ** For web hosting, you need to configure both `TelegratorOptions` (for bot token) and `WebhookerOptions` (for webhook settings) in your `appsettings.json` file.
2026-03-07 23:30:47 +04:00
> **How is it working?**
> 1. **Console Integration**: `TelegratorClient` provides a simple way to create bots in console applications.
> 2. **Domain-Wide Collection**: `CollectHandlersDomainWide()` automatically discovers and registers all handlers in the current assembly.
> 3. **ASP.NET Core Integration**: `TelegramBotHost.CreateBuilder()` provides a builder pattern for hosting bots in ASP.NET Core applications.
> 4. **Webhook Integration**: `TelegramBotWebHost.CreateBuilder()` provides webhook hosting for production deployments.
> 5. **Dependency Injection**: Handlers and their dependencies are automatically registered with the DI container.
### 2.8. Aspects & Cross-Cutting Concerns
Telegrator provides a powerful aspect-oriented programming (AOP) system for handling cross-cutting concerns like logging, validation, authorization, and error handling. This system allows you to separate business logic from infrastructure concerns.
**Key Concepts: **
- **Pre-Execution Aspects**: Code that runs before handler execution
- **Post-Execution Aspects**: Code that runs after handler execution
- **Self-Processing**: Handler implements interfaces directly
- **Typed Processing**: External processor classes via attributes
**Common Use Cases: **
- Input validation
- Logging and monitoring
- Authorization and access control
- Error handling and recovery
- Performance metrics collection
- Audit trails
#### Self-Processing Example
``` csharp
using Telegrator.Aspects ;
[MessageHandler]
public class LoggingHandler : MessageHandler , IPreProcessor , IPostProcessor
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Message processed successfully!" ) ;
return Ok ;
}
public async Task < Result > BeforeExecution ( IHandlerContainer container )
{
var user = container . HandlingUpdate . Message ? . From ;
Console . WriteLine ( $"[{DateTime.Now:HH:mm:ss}] User {user?.Id} ({user?.Username}) sent: {container.HandlingUpdate.Message?.Text}" ) ;
return Ok ;
}
public async Task < Result > AfterExecution ( IHandlerContainer container )
{
Console . WriteLine ( $"[{DateTime.Now:HH:mm:ss}] Message processing completed" ) ;
return Ok ;
}
}
```
#### Typed Processing Example
``` csharp
using Telegrator.Aspects ;
// Validation processor
public class MessageValidationProcessor : IPreProcessor
{
public async Task < Result > BeforeExecution ( IHandlerContainer container )
{
var message = container . HandlingUpdate . Message ;
if ( message ? . Text = = null )
return Result . Fault ( ) ; // Stop execution
if ( message . Text . Length > 1000 )
return Result . Fault ( ) ; // Message too long
return Ok ;
}
}
// Logging processor
public class LoggingProcessor : IPostProcessor
{
public async Task < Result > AfterExecution ( IHandlerContainer container )
{
Console . WriteLine ( $"Handler execution completed for update {container.HandlingUpdate.Id}" ) ;
return Ok ;
}
}
// Handler with external processors
[MessageHandler]
[BeforeExecution<MessageValidationProcessor>]
[AfterExecution<LoggingProcessor>]
public class ValidatedHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Valid message received and processed!" ) ;
return Ok ;
}
}
```
#### Combined Approach Example
``` csharp
using Telegrator.Aspects ;
[MessageHandler]
[BeforeExecution<AuthorizationProcessor>]
public class SecureHandler : MessageHandler , IPostProcessor
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Secure operation completed!" ) ;
return Ok ;
}
// Custom post-processing
public async Task < Result > AfterExecution ( IHandlerContainer container )
{
Console . WriteLine ( $"Secure operation completed for user {container.HandlingUpdate.Message?.From?.Id}" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Self-Processing**: Handlers implement `IPreProcessor` and/or `IPostProcessor` interfaces directly
> 2. **Typed Processing**: External processor classes are applied via `[BeforeExecution<T>]` and `[AfterExecution<T>]` attributes
> 3. **Execution Order**: Pre-execution aspects run first, then handler main logic, then post-execution aspects
> 4. **Flow Control**: Return `Result.Fault()` from pre-execution to stop handler execution
> 5. **Separation of Concerns**: Business logic is separated from cross-cutting concerns like logging and validation
---
## 3. Step-by-Step Tutorials
### 3.1. Minimal Bot Creation
See [Practice: Minimal Bot ](#practice-minimal-bot ).
### 3.2. Command Filtering
Message is considered command if is start with '/' and has not null or empty name.
Instead of using the `MessageHandler` for command (such as `/start` ) you should use `CommandHandler` .
``` csharp
[CommandHandler]
[CommandAlias("start")]
public class StartHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Welcome! Use /help to see available commands." ) ;
return Ok ;
}
}
[CommandHandler]
[CommandAlias("help")]
public class HelpHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Available commands:\n/start - Start the bot\n/help - Show this help" ) ;
return Ok ;
}
}
[MessageHandler]
[TextStartsWith("/", Modifiers = FilterModifier.Not)]
public class EchoHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( $"You said: \" { Input . Text } \ "" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Command Handlers**: `[CommandHandler]` and `[CommandAlias]` work together to handle specific commands like `/start` and `/help`.
> 2. **Echo Handler**: `[TextStartsWith("/", Modifiers = FilterModifier.Not)]` catches all messages that don't start with "/" (non-commands).
> 3. **Handler Separation**: Each command has its own dedicated handler, making the code modular and maintainable.
> 4. **Filter Modifiers**: The `Not` modifier inverts the filter logic to exclude command messages.
### 3.3. State Management Wizard
``` csharp
[CommandHandler]
[CommandAlias("wizard")]
[NumericState(SpecialState.NoState)]
public class StartWizardHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
container . CreateNumericState ( ) ; // This code is not necessary, as "Forward" method can automatically creates state if needed, but its recomended to use
container . ForwardNumericState ( ) ;
await Reply ( "What is your name?" ) ;
return Ok ;
}
}
[MessageHandler]
[NumericState(1)]
public class NameHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
container . ForwardNumericState ( ) ;
await Reply ( $"Nice to meet you, {Input.Text}! How old are you?" ) ;
return Ok ;
}
}
[MessageHandler]
[NumericState(2)]
public class AgeHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
if ( int . TryParse ( Input . Text , out int age ) )
{
await Reply ( $"Thank you! You are {age} years old. Wizard completed!" ) ;
container . DeleteNumericState ( ) ;
}
else
{
await Reply ( "Please enter a valid age (number)." ) ;
}
return Ok ;
}
}
```
> **How is it working?**
> 1. **Numeric State**: `[NumericState(SpecialState.NoState)]` starts the wizard when no state exists.
> 2. **State Creation**: `container.CreateNumericState()` initializes the numeric state for the user.
> 3. **State Progression**: `container.ForwardNumericState()` moves to the next step (1, then 2).
> 4. **State Cleanup**: `container.DeleteNumericState()` removes the state when the wizard completes.
> 5. **Input Validation**: The age handler validates numeric input and provides feedback.
### 3.4. CallbackQuery Handling
``` csharp
[CommandHandler]
[CommandAlias("menu")]
public class MenuHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
var keyboard = new InlineKeyboardMarkup ( new [ ]
{
InlineKeyboardButton . WithCallbackData ( "Option 1" , "option1" ) ,
InlineKeyboardButton . WithCallbackData ( "Option 2" , "option2" )
} ) ;
await Reply ( "Choose an option:" , replyMarkup : keyboard , cancellationToken : cancellation ) ;
return Ok ;
}
}
[CallbackQueryHandler]
[CallbackData("option1")]
public class Option1Handler : CallbackQueryHandler
{
public override async Task < Result > Execute ( IHandlerContainer < CallbackQuery > container , CancellationToken cancellation )
{
await AnswerCallbackQuery ( "You selected Option 1!" , cancellationToken : cancellation ) ;
await EditMessage ( "You selected Option 1!" ) ;
return Ok ;
}
}
[CallbackQueryHandler]
[CallbackData("option2")]
public class Option2Handler : CallbackQueryHandler
{
public override async Task < Result > Execute ( IHandlerContainer < CallbackQuery > container , CancellationToken cancellation )
{
await AnswerCallbackQuery ( "You selected Option 2!" , cancellationToken : cancellation ) ;
await EditMessage ( "You selected Option 2!" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Inline Keyboard**: `InlineKeyboardMarkup` creates interactive buttons with `CallbackData` identifiers.
> 2. **CallbackQuery Handlers**: `[CallbackQueryHandler]` and `[CallbackData]` work together to handle button clicks.
> 3. **Response Methods**: `AnswerCallbackQuery()` provides immediate feedback, while `EditMessage()` updates the message.
> 4. **Handler Separation**: Each button option has its own dedicated handler for clean code organization.
### 3.5. Implicit Handlers from Methods
You can create handlers directly from methods without defining full handler classes. This is useful for simple handlers or quick prototyping:
``` csharp
// Simple echo handler
[MessageHandler]
[TextStartsWith("/", Modifiers = FilterModifier.Not)]
private static async Task < Result > EchoHandler ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
await container . Reply ( $"You said: \" { container . Input . Text } \ "" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Command handler with inline keyboard
[CommandHandler]
[CommandAlias("menu")]
private static async Task < Result > MenuHandler ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
var keyboard = new InlineKeyboardMarkup ( new [ ]
{
InlineKeyboardButton . WithCallbackData ( "Option 1" , "option1" ) ,
InlineKeyboardButton . WithCallbackData ( "Option 2" , "option2" )
} ) ;
await container . Reply ( "Choose an option:" , replyMarkup : keyboard , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Callback query handler
[CallbackQueryHandler]
[CallbackData("option1")]
private static async Task < Result > Option1Handler ( IHandlerContainer < CallbackQuery > container , CancellationToken cancellationToken )
{
await container . AnswerCallbackQuery ( "You selected Option 1!" , cancellationToken : cancellationToken ) ;
await container . EditMessage ( "You selected Option 1!" ) ;
return Ok ;
}
// Register all methods as handlers
builder . Handlers . AddMethod < Message > ( EchoHandler ) ;
builder . Handlers . AddMethod < Message > ( MenuHandler ) ;
builder . Handlers . AddMethod < CallbackQuery > ( Option1Handler ) ;
```
**Advanced Example with State Management: **
``` csharp
public enum UserState
{
Start = SpecialState . NoState ,
WaitingForName ,
WaitingForAge
}
// Start conversation
[CommandHandler]
[CommandAlias("register")]
[EnumState<UserState>(UserState.Start)]
private static async Task < Result > StartRegistration ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
container . ForwardEnumState < UserState > ( ) ;
await container . Reply ( "Please enter your name:" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Handle name input
[MessageHandler]
[EnumState<UserState>(UserState.WaitingForName)]
private static async Task < Result > HandleName ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
var name = container . Input . Text ;
container . ForwardEnumState < UserState > ( ) ;
await container . Reply ( $"Hello {name}! Please enter your age:" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Handle age input
[MessageHandler]
[EnumState<UserState>(UserState.WaitingForAge)]
private static async Task < Result > HandleAge ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
if ( int . TryParse ( container . Input . Text , out int age ) )
{
container . DeleteEnumState < UserState > ( ) ;
await container . Reply ( $"Registration complete! Name: {name}, Age: {age}" , cancellationToken : cancellationToken ) ;
}
else
{
await container . Reply ( "Please enter a valid age (number):" , cancellationToken : cancellationToken ) ;
}
return Ok ;
}
// Register state management handlers
builder . Handlers . AddMethod < Message > ( StartRegistration ) ;
builder . Handlers . AddMethod < Message > ( HandleName ) ;
builder . Handlers . AddMethod < Message > ( HandleAge ) ;
```
> **How is it working?**
> 1. **Method Signature**: Methods must return `Task<Result>` and accept `IHandlerContainer<T>` and `CancellationToken`
> 2. **Attributes**: Apply the same filter attributes as regular handlers (`[MessageHandler]`, `[CommandHandler]`, etc.)
> 3. **Container Methods**: Use extension methods like `container.Reply()`, `container.Response()`, etc.
> 4. **Registration**: Use `AddMethod<T>()` to register methods as handlers
> 5. **State Management**: Same state management patterns as regular handlers
> 6. **Flexibility**: Can be used for simple handlers or complex multi-step conversations
---
## 4. Advanced Topics
### 4.1. Handler Importance & Priority
Telegrator provides two different mechanisms for controlling handler execution order: `Importance` and `Priority` . These serve different purposes in the framework's execution model.
#### Importance (Internal Type Priority)
`Importance` is an internal parameter used to control priority between different handler types that process the same update type. It's automatically set by the framework based on the handler type.
**Built-in Importance Values: **
- `CommandHandler` : Importance = 1 (highest priority for message updates)
- `MessageHandler` : Importance = 0 (default priority for message updates)
- `CallbackQueryHandler` : Importance = 0 (default for callback updates)
- `AnyUpdateHandler` : Importance = -1 (lowest priority, catches all updates)
**Example of Importance in Action: **
``` csharp
[CommandHandler] // Importance = 1 (automatically set)
[CommandAlias("start")]
public class StartHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Command handler executed first!" ) ;
return Ok ;
}
}
[MessageHandler] // Importance = 0 (automatically set)
[TextContains("hello")]
public class HelloHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Message handler executed second!" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Automatic Importance**: The framework automatically sets importance based on handler type.
> 2. **Command Priority**: Commands are processed before regular messages due to higher importance.
> 3. **Type-based Ordering**: This ensures critical handlers (like commands) run before general handlers.
> 4. **Framework Control**: Importance is managed internally and shouldn't be manually overridden.
#### Priority (Global Execution Control)
`Priority` is a user-controlled parameter that regulates the execution order among all registered handlers in the application, regardless of their type.
**Priority Usage: **
``` csharp
[MessageHandler(Priority = 10)] // High priority among all handlers
public class HighPriorityHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "This handler runs with high priority!" ) ;
return Ok ;
}
}
[MessageHandler(Priority = 0)] // Default priority
public class NormalPriorityHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "This handler runs with normal priority!" ) ;
return Ok ;
}
}
[MessageHandler(Priority = -10)] // Low priority
public class LowPriorityHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "This handler runs with low priority!" ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Global Priority**: `Priority` controls execution order across all handler types.
> 2. **Higher Priority First**: Handlers with higher priority numbers run before those with lower numbers.
> 3. **User Control**: Priority is manually set by developers for custom execution ordering.
> 4. **Default Priority**: When not specified, handlers have priority 0.
> 5. **Cross-Type Ordering**: Priority works across different handler types (MessageHandler, CommandHandler, etc.).
#### Combined Execution Order
The final execution order is determined by both Importance and Priority:
1. **First ** : Handlers are sorted by `Importance` (type-based priority)
2. **Second ** : Within the same importance level, handlers are sorted by `Priority` (user-defined priority)
**Example: **
``` csharp
[CommandHandler(Priority = 5)] // Importance = 1, Priority = 5
[CommandAlias("admin")]
public class AdminCommandHandler : CommandHandler
{
// Executes first (highest importance + high priority)
}
[CommandHandler(Priority = 0)] // Importance = 1, Priority = 0
[CommandAlias("start")]
public class StartCommandHandler : CommandHandler
{
// Executes second (highest importance + normal priority)
}
[MessageHandler(Priority = 10)] // Importance = 0, Priority = 10
[TextContains("urgent")]
public class UrgentMessageHandler : MessageHandler
{
// Executes third (normal importance + high priority)
}
[MessageHandler(Priority = 0)] // Importance = 0, Priority = 0
[TextContains("hello")]
public class HelloMessageHandler : MessageHandler
{
// Executes last (normal importance + normal priority)
}
```
### 4.2. Dependency Injection (DI)
Telegrator is designed to work seamlessly with DI containers (e.g., ASP.NET Core). Handlers and their dependencies are automatically registered.
``` csharp
[MessageHandler]
public class MyHandler : MessageHandler
{
private readonly IMyService _myService ;
private readonly ILogger < MyHandler > _logger ;
public MyHandler ( IMyService myService , ILogger < MyHandler > logger )
{
_myService = myService ;
_logger = logger ;
}
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
_logger . LogInformation ( "MyHandler executed!" ) ;
var result = _myService . DoSomething ( ) ;
await Reply ( result ) ;
return Ok ;
}
}
```
> **How is it working?**
> 1. **Constructor Injection**: Dependencies (`IMyService`, `ILogger`) are automatically injected by the DI container.
> 2. **Service Registration**: When using `Telegrator.Hosting`, services are automatically registered with the DI container.
> 3. **Handler Instantiation**: Telegrator creates handler instances through the DI container, resolving all dependencies.
> 4. **Logging Integration**: Built-in logging support allows for comprehensive debugging and monitoring.
### 4.3. Custom State Keepers
You can implement your own state keeper by inheriting from `StateKeeperBase<TKey, TState>` . This allows for advanced scenarios (e.g., per-message state, custom key resolution).
### 4.4. Automatic Handler Discovery
Telegrator provides automatic discovery and registration of handlers across your entire application domain using the `CollectHandlersDomainWide()` method.
**How it works: **
- Scans all loaded assemblies in the current domain
- Automatically discovers classes decorated with handler attributes
- Registers them with the bot without manual registration
**Example: **
``` csharp
var bot = new TelegratorClient ( "<YOUR_BOT_TOKEN>" ) ;
bot . Handlers . CollectHandlersDomainWide ( ) ; // Automatically finds and registers all handlers
bot . StartReceiving ( ) ;
```
**Benefits: **
- No need to manually register each handler
- Reduces boilerplate code
- Ensures all handlers are discovered automatically
- Perfect for large applications with many handlers
> **How is it working?**
> 1. **Domain Scanning**: `CollectHandlersDomainWide()` scans all assemblies loaded in the current AppDomain.
> 2. **Reflection Discovery**: Uses reflection to find all classes decorated with handler attributes.
> 3. **Automatic Registration**: Each discovered handler is automatically registered with the `HandlersCollection`.
> 4. **Handler Types**: Supports all handler types: `MessageHandler`, `CommandHandler`, `CallbackQueryHandler`, etc.
### 4.5. Hosting Integration
**Note: ** For automatic handler discovery, you can use `bot.Handlers.CollectHandlersDomainWide()` instead of manually adding each handler with `AddHandler<T>()` . This automatically discovers and registers all handlers in the current assembly.
2026-03-07 23:22:40 +04:00
Telegrator provides seamless integration with .NET's generic host through the `Telegrator.Hosting` package, making it easy to build production-ready bot applications.
2026-03-07 23:30:47 +04:00
**Installation: **
``` shell
dotnet add package Telegrator.Hosting
```
**Dependencies: **
- `Microsoft.Extensions.Hosting` - .NET Generic Host
- `Microsoft.Extensions.DependencyInjection` - Dependency Injection
- `Microsoft.Extensions.Configuration` - Configuration management
- `Microsoft.Extensions.Logging` - Logging infrastructure
**Core Components: **
- `TelegramBotHost` - The main hosted service that manages the bot lifecycle
- `TelegramBotHostBuilder` - Builder pattern for configuring the bot host
- `TelegramBotOptions` - Configuration options for the bot
**Basic Example: **
``` csharp
var builder = TelegramBotHost . CreateBuilder ( ) ;
// Configure services
builder . Services . AddSingleton < IMyService , MyService > ( ) ;
// Configure handlers
builder . Handlers . CollectHandlersDomainWide ( ) ;
// Building host
var host = builder . Build ( ) ;
await host . Run ( ) ;
```
> **How is it working?**
> 1. **Generic Host Integration**: `TelegramBotHost` implements `IHost` and integrates with .NET's generic host.
> 2. **Lifecycle Management**: The host manages the bot's startup, shutdown, and graceful termination.
> 3. **Dependency Injection**: All handlers and services are automatically registered with the DI container.
> 4. **Configuration**: Supports standard .NET configuration patterns (appsettings.json, environment variables, etc.).
> 5. **Logging**: Integrates with .NET's logging infrastructure for comprehensive monitoring.
> 6. **Health Checks**: Can be integrated with .NET's health check system for production monitoring.
### 4.6. Web Hosting (Webhook)
Telegrator provides webhook hosting through the `Telegrator.Hosting.Web` package for production deployments.
**Installation: **
``` shell
dotnet add package Telegrator.Hosting.Web
```
**Dependencies: **
- `Microsoft.AspNetCore.App` - ASP.NET Core
- `Microsoft.Extensions.Hosting` - .NET Generic Host
- `Microsoft.Extensions.DependencyInjection` - Dependency Injection
**Core Components: **
- `TelegramBotWebHost` - The main web hosted service for webhook handling
- `TelegramBotWebHostBuilder` - Builder pattern for configuring the web host
2026-03-07 23:38:14 +04:00
- `WebhookerOptions` - Configuration options for webhook settings
2026-03-07 23:30:47 +04:00
**Configuration Requirements: **
2026-03-07 23:38:14 +04:00
- `TelegratorOptions` must be configured as it contains the bot token
- `WebhookerOptions` must be configured through external sources (appsettings.json) for webhook settings
2026-03-07 23:30:47 +04:00
**Basic Example: **
``` csharp
var webOptions = new TelegramBotWebOptions ( ) ;
var builder = TelegramBotWebHost . CreateBuilder ( webOptions ) ;
// Configure services
builder . Services . AddSingleton < IMyService , MyService > ( ) ;
// Configure handlers
builder . Handlers . CollectHandlersDomainWide ( ) ;
// Building host
var host = builder . Build ( ) ;
await host . StartAsync ( ) ;
```
**Configuration via appsettings.json: **
``` json
{
2026-03-07 23:38:14 +04:00
"TelegratorOptions" : {
2026-03-07 23:30:47 +04:00
"Token" : "YOUR_BOT_TOKEN"
} ,
2026-03-07 23:38:14 +04:00
"WebhookerOptions" : {
2026-03-07 23:30:47 +04:00
"WebhookUri" : "https://your-domain.com/webhook" ,
"SecretToken" : "your-secret-token" ,
"MaxConnections" : 40 ,
"DropPendingUpdates" : true
} ,
"HostOptions" : {
"ShutdownTimeout" : 10 ,
"BackgroundServiceExceptionBehavior" : "StopHost"
} ,
"Logging" : {
"LogLevel" : {
"Default" : "Information" ,
"Telegrator" : "Debug"
}
}
}
```
> **How is it working?**
> 1. **Webhook Integration**: `TelegramBotWebHost` handles incoming webhook requests from Telegram.
> 2. **Security**: Supports secret token validation for secure webhook handling.
> 3. **Scalability**: Webhook hosting is more efficient for high-traffic bots compared to long-polling.
> 4. **Production Ready**: Includes health checks, logging, and monitoring capabilities.
> 5. **SSL Required**: Webhook hosting requires HTTPS for production use.
### 4.7. Configuration Management
The hosting integration provides comprehensive configuration management through standard .NET configuration patterns.
**Required Configuration: **
The bot token must be configured either in `appsettings.json` or through environment variables:
``` json
{
2026-03-07 23:38:14 +04:00
"TelegratorOptions" : {
2026-03-07 23:30:47 +04:00
"Token" : "YOUR_BOT_TOKEN"
}
}
```
**Environment Variables: **
You can also use environment variables for sensitive configuration:
``` shell
export TelegramBotClientOptions__Token = "YOUR_BOT_TOKEN"
```
**Advanced Configuration: **
``` json
{
2026-03-07 23:38:14 +04:00
"TelegratorOptions" : {
2026-03-07 23:30:47 +04:00
"Token" : "YOUR_BOT_TOKEN" ,
"BaseUrl" : "https://api.telegram.org"
} ,
"HostOptions" : {
"ShutdownTimeout" : 10 ,
"BackgroundServiceExceptionBehavior" : "StopHost"
} ,
"ReceiverOptions" : {
"DropPendingUpdates" : true ,
"Limit" : 10 ,
"Timeout" : 30
} ,
"Logging" : {
"LogLevel" : {
"Default" : "Information"
}
}
}
```
> **How is it working?**
> 1. **Automatic Configuration Binding**: The hosting integration automatically binds configuration sections to their respective options classes.
> 2. **Environment Variable Support**: Configuration can be overridden using environment variables with the `__` separator.
> 3. **Hierarchical Configuration**: Supports multiple configuration sources (appsettings.json, environment variables, command line, etc.).
> 4. **Type Safety**: Configuration is strongly typed and validated at startup.
> 5. **Production Ready**: Sensitive data like bot tokens can be securely managed through environment variables or secret management systems.
### 4.8. Error Handling and Logging
You can subscribe to error events or set a custom exception handler:
``` csharp
bot . UpdateRouter . ExceptionHandler = new DefaultRouterExceptionHandler ( ( client , exception , source , cancellationToken ) = >
{
Console . WriteLine ( $"An error occurred: {exception.Message}" ) ;
return Task . CompletedTask ;
} ) ;
```
**Custom Exception Handler: **
``` csharp
public class CustomExceptionHandler : IRouterExceptionHandler
{
private readonly ILogger < CustomExceptionHandler > _logger ;
public CustomExceptionHandler ( ILogger < CustomExceptionHandler > logger )
{
_logger = logger ;
}
public Task HandleException ( ITelegramBotClient client , Exception exception , HandleErrorSource source , CancellationToken cancellationToken )
{
_logger . LogError ( exception , "Error occurred in {Source}" , source ) ;
// You can implement custom error handling logic here
// For example, send error notifications to administrators
return Task . CompletedTask ;
}
}
```
> **How is it working?**
> 1. **Exception Handler**: `ExceptionHandler` property allows you to set a custom exception handler for the entire bot.
> 2. **Error Context**: The handler receives the bot client, exception, source information, and cancellation token.
> 3. **Global Error Handling**: This provides a centralized way to handle all exceptions that occur during update processing.
> 4. **Logging Integration**: Perfect place to log errors or send notifications to administrators.
### 4.9. Performance Optimization
- Use appropriate concurrency limits for resource-intensive operations
- Avoid thread-blocking operations in handlers
- Use state management for multi-step processes
- Use `AwaitingProvider` for complex conversation flows
- Consider webhook hosting for high-traffic bots
### 4.10. Best Practices
- Organize handlers, filters, and state keepers in separate folders
- Use feature modules for large bots
- Prefer declarative filters over manual `if` statements
- Keep handlers focused and single-responsibility
- Use dependency injection for better testability
- Implement proper error handling and logging
- Use webhook hosting for production deployments
**Aspect-Oriented Programming Best Practices: **
- **Separation of Concerns**: Keep aspects focused on a single responsibility (logging, validation, authorization, etc.)
- **Reusability**: Create generic aspects that can be applied to multiple handlers
- **Performance**: Avoid heavy operations in aspects that run frequently
- **Error Handling**: Always handle exceptions in aspects gracefully
- **Testing**: Test aspects independently from handlers
- **Documentation**: Document the purpose and behavior of custom aspects
- **State Management**: Use thread-safe collections for aspects that maintain state
**Common Aspect Patterns: **
- **Validation Aspect**: Check input data before processing
- **Logging Aspect**: Record execution details for debugging
- **Authorization Aspect**: Verify user permissions
- **Metrics Aspect**: Collect performance and usage data
- **Audit Aspect**: Track user actions for compliance
- **Rate Limiting Aspect**: Prevent abuse by limiting request frequency
---
## 5. Advanced Topics
This section covers advanced concepts and techniques for building sophisticated bots.
### 5.1. Aspects & Cross-Cutting Concerns
Telegrator provides a powerful aspect-oriented programming (AOP) system for handling cross-cutting concerns like logging, validation, authorization, and error handling.
#### Self-Processing Example
``` csharp
using Telegrator.Aspects ;
[MessageHandler]
public class LoggingHandler : MessageHandler , IPreProcessor , IPostProcessor
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Message processed successfully!" ) ;
return Ok ;
}
public async Task < Result > BeforeExecution ( IHandlerContainer container )
{
var user = container . HandlingUpdate . Message ? . From ;
Console . WriteLine ( $"[{DateTime.Now:HH:mm:ss}] User {user?.Id} ({user?.Username}) sent: {container.HandlingUpdate.Message?.Text}" ) ;
return Ok ;
}
public async Task < Result > AfterExecution ( IHandlerContainer container )
{
Console . WriteLine ( $"[{DateTime.Now:HH:mm:ss}] Message processing completed" ) ;
return Ok ;
}
}
```
#### Typed Processing Example
``` csharp
using Telegrator.Aspects ;
// Validation processor
public class MessageValidationProcessor : IPreProcessor
{
public async Task < Result > BeforeExecution ( IHandlerContainer container )
{
var message = container . HandlingUpdate . Message ;
if ( message ? . Text = = null )
return Result . Fault ( ) ; // Stop execution
if ( message . Text . Length > 1000 )
return Result . Fault ( ) ; // Message too long
return Ok ;
}
}
// Handler with external processors
[MessageHandler]
[BeforeExecution<MessageValidationProcessor>]
public class ValidatedHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
await Reply ( "Valid message received and processed!" ) ;
return Ok ;
}
}
```
### 5.2. Custom Extensions
You can extend Telegrator by creating custom filters, aspects, and state keepers.
#### Custom Filters
``` csharp
public class RateLimitFilter : FilterAnnotation < Message >
{
private readonly Dictionary < long , DateTime > _lastExecution = new ( ) ;
private readonly TimeSpan _cooldown = TimeSpan . FromSeconds ( 5 ) ;
public override bool CanPass ( FilterExecutionContext < Message > context )
{
var userId = context . Input . From ? . Id ;
if ( userId = = null ) return true ;
if ( _lastExecution . TryGetValue ( userId . Value , out var lastExec ) )
{
if ( DateTime . Now - lastExec < _cooldown )
return false ; // Rate limit exceeded
}
_lastExecution [ userId . Value ] = DateTime . Now ;
return true ;
}
}
```
#### Custom State Keepers
``` csharp
public class CustomStateKeeper : StateKeeperBase < string , string >
{
protected override string GetKey ( IHandlerContainer container )
= > container . HandlingUpdate . Message ? . From ? . Id . ToString ( ) ? ? "unknown" ;
protected override string GetValue ( IHandlerContainer container )
= > container . HandlingUpdate . Message ? . Text ? ? "" ;
protected override void SetValue ( IHandlerContainer container , string value )
{
// Custom state storage logic
}
}
```
### 5.3. Performance Optimization
Optimize your bot for high performance:
#### Concurrency Settings
``` csharp
var options = new TelegratorOptions
{
MaximumParallelWorkingHandlers = 20 , // Adjust based on server capabilities
ExclusiveAwaitingHandlerRouting = true ,
ExceptIntersectingCommandAliases = true
} ;
```
#### Memory Management
- Use `using` statements for disposable resources
- Implement proper cleanup in custom aspects
- Monitor memory usage in long-running bots
#### Caching Strategies
``` csharp
public class CachingAspect : IPreProcessor
{
private readonly Dictionary < string , object > _cache = new ( ) ;
public async Task < Result > BeforeExecution ( IHandlerContainer container )
{
var key = container . HandlingUpdate . Message ? . Text ;
if ( _cache . TryGetValue ( key , out var cached ) )
{
// Use cached result
return Ok ;
}
return Ok ;
}
}
2026-03-07 23:22:40 +04:00
```
2026-03-07 23:30:47 +04:00
### 5.4. Error Handling
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
Implement robust error handling for your bot:
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Global Exception Handler
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
var bot = new TelegratorClient ( "<BOT_TOKEN>" ) ;
bot . ExceptionHandler = new CustomExceptionHandler ( ) ;
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
public class CustomExceptionHandler : IRouterExceptionHandler
{
public Task HandleException ( ITelegramBotClient client , Exception exception , HandleErrorSource source , CancellationToken cancellationToken )
{
Console . WriteLine ( $"Error in {source}: {exception.Message}" ) ;
return Task . CompletedTask ;
}
}
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Handler-Level Error Handling
``` csharp
[MessageHandler]
public class SafeHandler : MessageHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
try
{
// Risky operation
await Reply ( "Operation completed!" ) ;
return Ok ;
}
catch ( Exception ex )
{
await Reply ( "Sorry, something went wrong." ) ;
return Result . Fault ( ) ;
}
}
}
```
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
---
## 6. Integration & Deployment
### 6.1. Console Applications
Simple console bot setup:
``` csharp
class Program
{
static void Main ( string [ ] args )
{
var bot = new TelegratorClient ( "<YOUR_BOT_TOKEN>" ) ;
bot . Handlers . CollectHandlersDomainWide ( ) ;
bot . StartReceiving ( ) ;
Console . ReadLine ( ) ;
}
}
```
### 6.2. ASP.NET Core Hosting
Host your bot in ASP.NET Core applications:
``` csharp
using Telegrator.Hosting ;
var builder = TelegramBotHost . CreateBuilder ( ) ;
builder . Handlers . AddHandler < StartHandler > ( ) ;
2026-03-07 23:22:40 +04:00
var host = builder . Build ( ) ;
2026-03-07 23:30:47 +04:00
await host . StartAsync ( ) ;
2026-03-07 23:22:40 +04:00
```
2026-03-07 23:30:47 +04:00
### 6.3. Webhook Deployment
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
Deploy your bot using webhooks for production:
2026-03-07 23:22:40 +04:00
``` csharp
2026-03-07 23:30:47 +04:00
using Telegrator.Hosting.Web ;
var webOptions = new TelegramBotWebOptions ( ) ;
var webHost = TelegramBotWebHost . CreateBuilder ( webOptions ) ;
webHost . Handlers . AddHandler < StartHandler > ( ) ;
var host = webHost . Build ( ) ;
await host . StartAsync ( ) ;
```
**Configuration (appsettings.json): **
``` json
2026-03-07 23:22:40 +04:00
{
2026-03-07 23:38:14 +04:00
"TelegratorOptions" : {
2026-03-07 23:30:47 +04:00
"Token" : "YOUR_BOT_TOKEN"
} ,
2026-03-07 23:38:14 +04:00
"WebhookerOptions" : {
2026-03-07 23:30:47 +04:00
"WebhookUri" : "https://your-domain.com/webhook" ,
"SecretToken" : "your-secret-token" ,
"MaxConnections" : 40 ,
"DropPendingUpdates" : true
}
}
```
### 6.4. Configuration Management
Manage your bot configuration:
``` csharp
// From appsettings.json
var configuration = new ConfigurationBuilder ( )
. AddJsonFile ( "appsettings.json" )
. Build ( ) ;
var botOptions = configuration . GetSection ( "TelegratorOptions" ) . Get < TelegratorOptions > ( ) ;
var bot = new TelegratorClient ( "<BOT_TOKEN>" , botOptions ) ;
```
---
## 7. Best Practices & Patterns
### 7.1. Project Organization
Organize your bot code effectively:
```
MyBot/
├── Handlers/
│ ├── Commands/
│ │ ├── StartHandler.cs
│ │ └── HelpHandler.cs
│ ├── Messages/
│ │ ├── EchoHandler.cs
│ │ └── GreetingHandler.cs
│ └── Callbacks/
│ └── ButtonHandler.cs
├── Filters/
│ ├── AdminFilter.cs
│ └── RateLimitFilter.cs
├── Aspects/
│ ├── LoggingAspect.cs
│ └── ValidationAspect.cs
├── State/
│ └── QuizState.cs
└── Program.cs
```
### 7.2. Testing Strategies
Test your bot components:
``` csharp
[Test]
public async Task StartHandler_ShouldReplyWithWelcome ( )
{
// Arrange
var handler = new StartHandler ( ) ;
var container = CreateMockContainer ( ) ;
// Act
var result = await handler . Execute ( container , CancellationToken . None ) ;
// Assert
Assert . That ( result . Positive , Is . True ) ;
}
```
### 7.3. Common Patterns
#### Command Pattern
``` csharp
[CommandHandler]
[CommandAlias("settings")]
public class SettingsHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
var keyboard = new InlineKeyboardMarkup ( new [ ]
{
new [ ] { InlineKeyboardButton . WithCallbackData ( "Language" , "settings_lang" ) } ,
new [ ] { InlineKeyboardButton . WithCallbackData ( "Theme" , "settings_theme" ) }
} ) ;
await Reply ( "Choose a setting:" , replyMarkup : keyboard ) ;
return Ok ;
}
}
```
#### Wizard Pattern
``` csharp
[CommandHandler]
[CommandAlias("wizard")]
[StringState("no_state")]
public class StartWizardHandler : CommandHandler
{
public override async Task < Result > Execute ( IHandlerContainer < Message > container , CancellationToken cancellation )
{
container . ForwardStringState ( ) ;
await Reply ( "Step 1: What is your name?" ) ;
return Ok ;
}
}
```
### 7.4. Performance Tips
- **Use webhooks** for production bots
- **Implement caching** for frequently accessed data
- **Limit concurrent executions** based on server capabilities
- **Use async/await** properly throughout your code
- **Monitor memory usage** in long-running bots
- **Implement proper error handling** to prevent crashes
### 7.5. Logging System
2026-03-07 23:38:14 +04:00
Telegrator provides a centralized logging system called "TelegratorLogging" that allows integration with various logging frameworks while maintaining zero dependencies in the core library.
2026-03-07 23:30:47 +04:00
#### Overview
The logging system consists of:
2026-03-07 23:38:14 +04:00
- **TelegratorLogging** - Centralized static logging system
2026-03-07 23:30:47 +04:00
- **ITelegratorLogger** - Core logging interface
- **NullLogger** - No-op logger
- **ConsoleLogger** - Simple console output
- **MicrosoftLoggingAdapter** - Integration with Microsoft.Extensions.Logging
#### Basic Usage
**Console Logging: **
``` csharp
using Telegrator.Logging ;
// Add console adapter
2026-03-07 23:38:14 +04:00
TelegratorLogging . AddAdapter ( new ConsoleLoggerAdapter ( LogLevel . Debug , includeTimestamp : true ) ) ;
2026-03-07 23:30:47 +04:00
// Use logging
2026-03-07 23:38:14 +04:00
TelegratorLogging . LogInformation ( "Bot started" ) ;
TelegratorLogging . LogError ( "Something went wrong" , exception ) ;
2026-03-07 23:30:47 +04:00
```
**Custom Logger Adapter: **
``` csharp
public class CustomLogger : ITelegratorLogger
{
public void Log ( LogLevel level , string message , Exception ? exception = null )
{
// Your logging implementation
Console . WriteLine ( $"[{level}] {message}" ) ;
if ( exception ! = null )
Console . WriteLine ( $"Exception: {exception.Message}" ) ;
}
}
// Add custom adapter
2026-03-07 23:38:14 +04:00
TelegratorLogging . AddAdapter ( new CustomLogger ( ) ) ;
2026-03-07 23:30:47 +04:00
```
#### Hosting Integration
**With Microsoft.Extensions.Logging: **
``` csharp
using Telegrator.Hosting.Logging ;
var loggerFactory = LoggerFactory . Create ( builder = >
{
builder . AddConsole ( ) ;
builder . AddDebug ( ) ;
2026-03-07 23:22:40 +04:00
} ) ;
2026-03-07 23:30:47 +04:00
// Add Microsoft.Extensions.Logging adapter
ILogger < Program > logger = loggerFactory . CreateLogger < Program > ( ) ;
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter ( logger ) ;
2026-03-07 23:38:14 +04:00
TelegratorLogging . AddAdapter ( adapter ) ;
2026-03-07 23:30:47 +04:00
var bot = new TelegratorClient ( "<BOT_TOKEN>" ) ;
2026-03-07 23:22:40 +04:00
```
2026-03-07 23:30:47 +04:00
#### Log Levels
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
- **Trace** - Most detailed logging
- **Debug** - Detailed debugging information
- **Information** - General information
- **Warning** - Warning messages
- **Error** - Error messages
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
#### Simple Logging
All logging methods support simple message logging:
``` csharp
// In your handlers or aspects
2026-03-07 23:38:14 +04:00
TelegratorLogging . LogInformation ( "Handler executed" ) ;
TelegratorLogging . LogError ( "Something went wrong" , exception ) ;
TelegratorLogging . LogWarning ( "User exceeded rate limit" ) ;
2026-03-07 23:30:47 +04:00
```
#### Performance Considerations
2026-03-07 23:38:14 +04:00
- **TelegratorLogging** has minimal overhead with thread-safe adapter management
2026-03-07 23:30:47 +04:00
- **NullLogger** has zero overhead
- **ConsoleLogger** is lightweight for development
- **MicrosoftLoggingAdapter** delegates to the underlying framework
- Simple interface reduces complexity and improves performance
#### Best Practices
1. **Configure adapters early ** in your application startup
2. **Use ConsoleLogger for development ** and debugging
3. **Use MicrosoftLoggingAdapter for ASP.NET Core ** applications
4. **Include relevant context ** in log messages
5. **Set appropriate log levels ** based on your needs
6. **Multiple adapters ** can be registered for different outputs
2026-03-07 23:22:40 +04:00
---
2026-03-07 23:30:47 +04:00
## 8. FAQ & Troubleshooting
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 8.1. Common Issues
### 8.1. Q: My handler is not being triggered. What should I do?
2026-03-07 23:22:40 +04:00
- Check handler registration (use `bot.Handlers.AddHandler<MyHandler>()` or domain-wide collection)
- Check filter attributes and update types
- Enable debug logging
2026-03-07 23:30:47 +04:00
- Verify that the handler class inherits from the correct base class
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 8.2. Q: How can I access the `ITelegramBotClient` or the original `Update` object inside a handler?
- Use `Client` , `Update` , and `Input` properties in your handlers container
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 8.3. Q: How do I handle errors?
2026-03-07 23:22:40 +04:00
- Set a custom exception handler or subscribe to error events
2026-03-07 23:30:47 +04:00
- Use try-catch blocks in individual handlers for specific error handling
- Implement proper logging for debugging
2026-03-07 23:22:40 +04:00
2026-03-07 23:30:47 +04:00
### 8.4. Q: How can I organize my code for a large bot?
2026-03-07 23:22:40 +04:00
- Use folders, feature modules, and namespaces
- Keep handlers focused and modular
2026-03-07 23:30:47 +04:00
- Use dependency injection for better separation of concerns
- Implement proper state management for complex flows
### 8.5. Q: What's the difference between `Reply()` and `Responce()` methods?
- `Reply()` sends a reply to the original message (with reply markup)
- `Responce()` sends a new message to the chat (without reply markup)
- Both methods are available in `MessageHandler` and `CallbackQueryHandler`
**Note: ** `Responce()` has a typo in the name but is intentionally kept for backward compatibility. Both methods serve different purposes in message handling.
### 8.6. Q: How do I implement webhook hosting?
- Use `Telegrator.Hosting.Web` package
- Configure `TelegramBotWebOptions` with your bot token and webhook URL
- Ensure your server has HTTPS enabled
- Set up proper SSL certificates for production use
### 8.7. Q: How can I add logging or validation to all handlers?
**A:** Use the aspect system with `IPreProcessor` and `IPostProcessor` interfaces:
- **Self-processing**: Implement interfaces directly in your handler
- **Typed processing**: Create external processor classes and apply with attributes
- **Combined approach**: Use both methods together
This allows you to implement cross-cutting concerns without modifying handler business logic.
### 8.8. Q: Can I stop handler execution from an aspect?
**A:** Yes! Return `Result.Fault()` from a pre-execution processor to stop handler execution. Return `Ok` to continue.
### 8.9. Q: What's the execution order of aspects?
**A:** Aspects execute in the following order:
1. Pre-execution aspects (self-processing first, then typed)
2. Handler main execution
3. Post-execution aspects (self-processing first, then typed)
### 8.10. Q: How do I create reusable aspects for multiple handlers?
**A:** Create external processor classes that implement `IPreProcessor` or `IPostProcessor` , then apply them using `[BeforeExecution<T>]` or `[AfterExecution<T>]` attributes. This allows you to share the same aspect logic across multiple handlers.
### 8.11. Q: Can I create handlers from methods instead of full classes?
**A:** Yes! You can create implicit handlers from methods using the same attributes and patterns as regular handlers:
``` csharp
[MessageHandler, TextEquals("Hello", StringComparison.InvariantCultureIgnoreCase)]
private static async Task < Result > HelloWorld ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
await container . Reply ( "Hello, World!" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Register the method as a handler
builder . Handlers . AddMethod < Message > ( HelloWorld ) ;
```
**Key Points: **
- **Method Signature**: Must return `Task<Result>` and accept `IHandlerContainer<T>` and `CancellationToken`
- **Attributes**: Apply the same filter attributes as regular handlers
- **Container Methods**: Use extension methods like `container.Reply()` , `container.Response()` , etc.
- **Registration**: Use `AddMethod<T>()` to register methods as handlers
- **Benefits**: Quick prototyping, simple handlers, and code reuse
This approach is perfect for simple handlers or when you want to avoid creating full handler classes.
### 8.12. Q: Can I access DI container and configuration in filters?
**A:** Yes! When using Telegrator.Hosting, you can access the DI container and configuration in custom filters by casting `context.BotInfo` to `HostedTelegramBotInfo` :
``` csharp
public class DatabaseUserFilterAttribute : FilterAnnotation < Message >
{
public override bool CanPass ( FilterExecutionContext < Message > context )
{
// Cast to HostedTelegramBotInfo to access services
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
// Access DI container
var dbContext = botInfo . Services . GetRequiredService < UsersDbContext > ( ) ;
var configuration = botInfo . Services . GetRequiredService < IConfiguration > ( ) ;
// Use services in filter logic
var user = dbContext . Users . FirstOrDefault ( u = > u . TelegramId = = context . Input . From ? . Id ) ;
return user ? . IsActive = = true ;
}
}
```
**Key Points: **
- **Hosting Only**: This feature is only available when using `Telegrator.Hosting`
- **Type Casting**: Cast `context.BotInfo` to `HostedTelegramBotInfo`
- **Service Access**: Use `botInfo.Services.GetRequiredService<T>()` to access DI services
- **Configuration Access**: Use `botInfo.Services.GetRequiredService<IConfiguration>()` for settings
- **Null Safety**: Always check if the cast is successful before using services
This allows you to create powerful filters that integrate with your application's database, configuration, and other services.
### 8.8. Q: Can I stop handler execution from an aspect?
**A:** Yes! Return `Result.Fault()` from a pre-execution processor to stop handler execution. Return `Ok` to continue.
### 8.9. Q: What's the execution order of aspects?
**A:** Aspects execute in the following order:
1. Pre-execution aspects (self-processing first, then typed)
2. Handler main execution
3. Post-execution aspects (self-processing first, then typed)
### 8.10. Q: How do I create reusable aspects for multiple handlers?
**A:** Create external processor classes that implement `IPreProcessor` or `IPostProcessor` , then apply them using `[BeforeExecution<T>]` or `[AfterExecution<T>]` attributes. This allows you to share the same aspect logic across multiple handlers.
### 8.11. Q: Can I create handlers from methods instead of full classes?
**A:** Yes! You can create implicit handlers from methods using the same attributes and patterns as regular handlers:
``` csharp
[MessageHandler, TextEquals("Hello", StringComparison.InvariantCultureIgnoreCase)]
private static async Task < Result > HelloWorld ( IHandlerContainer < Message > container , CancellationToken cancellationToken )
{
await container . Reply ( "Hello, World!" , cancellationToken : cancellationToken ) ;
return Ok ;
}
// Register the method as a handler
builder . Handlers . AddMethod < Message > ( HelloWorld ) ;
```
**Key Points: **
- **Method Signature**: Must return `Task<Result>` and accept `IHandlerContainer<T>` and `CancellationToken`
- **Attributes**: Apply the same filter attributes as regular handlers
- **Container Methods**: Use extension methods like `container.Reply()` , `container.Response()` , etc.
- **Registration**: Use `AddMethod<T>()` to register methods as handlers
- **Benefits**: Quick prototyping, simple handlers, and code reuse
This approach is perfect for simple handlers or when you want to avoid creating full handler classes.
### 8.12. Q: Can I access DI container and configuration in filters?
**A:** Yes! When using Telegrator.Hosting, you can access the DI container and configuration in custom filters by casting `context.BotInfo` to `HostedTelegramBotInfo` :
``` csharp
public class DatabaseUserFilterAttribute : FilterAnnotation < Message >
{
public override bool CanPass ( FilterExecutionContext < Message > context )
{
// Cast to HostedTelegramBotInfo to access services
if ( context . BotInfo is not HostedTelegramBotInfo botInfo )
return false ;
// Access DI container
var dbContext = botInfo . Services . GetRequiredService < UsersDbContext > ( ) ;
var configuration = botInfo . Services . GetRequiredService < IConfiguration > ( ) ;
// Use services in filter logic
var user = dbContext . Users . FirstOrDefault ( u = > u . TelegramId = = context . Input . From ? . Id ) ;
return user ? . IsActive = = true ;
}
}
```
**Key Points: **
- **Hosting Only**: This feature is only available when using `Telegrator.Hosting`
- **Type Casting**: Cast `context.BotInfo` to `HostedTelegramBotInfo`
- **Service Access**: Use `botInfo.Services.GetRequiredService<T>()` to access DI services
- **Configuration Access**: Use `botInfo.Services.GetRequiredService<IConfiguration>()` for settings
- **Null Safety**: Always check if the cast is successful before using services
This allows you to create powerful filters that integrate with your application's database, configuration, and other services.
### 8.2. Debugging Guide
#### Enable Debug Logging
For detailed information about the logging system, see [section 7.5 - Logging System ](#75-logging-system ).
**Quick Start: **
``` csharp
using Telegrator.Logging ;
// Add console adapter for debugging
2026-03-07 23:38:14 +04:00
TelegratorLogging . AddConsoleAdapter ( LogLevel . Debug , includeTimestamp : true ) ;
2026-03-07 23:30:47 +04:00
// Use logging
2026-03-07 23:38:14 +04:00
TelegratorLogging . LogInformation ( "Bot started" ) ;
TelegratorLogging . LogError ( "Something went wrong" , exception ) ;
2026-03-07 23:30:47 +04:00
```
#### Common Debugging Steps
1. **Check Handler Registration ** : Verify handlers are properly registered
2. **Verify Filters ** : Ensure filters are correctly configured
3. **Test Individual Handlers ** : Test handlers in isolation
4. **Monitor Logs ** : Check application logs for errors
5. **Use Breakpoints ** : Set breakpoints in handler methods
#### Performance Monitoring
- Monitor handler execution times
- Check memory usage patterns
- Track concurrent execution counts
- Monitor error rates and types
#### Simple Logging
``` csharp
// In your handlers or aspects
2026-03-07 23:38:14 +04:00
TelegratorLogging . LogInformation ( "Handler executed" ) ;
TelegratorLogging . LogError ( "Something went wrong" , exception ) ;
TelegratorLogging . LogWarning ( "User exceeded rate limit" ) ;
2026-03-07 23:30:47 +04:00
```
2026-03-07 23:22:40 +04:00
---
2026-03-07 23:30:47 +04:00
## 9. Links
2026-03-07 23:22:40 +04:00
- [Main Repository ](https://github.com/Rikitav/Telegrator )
- [Wiki & Examples ](https://github.com/Rikitav/Telegrator/wiki/ )
- [NuGet Package ](https://www.nuget.org/packages/Telegrator )
- [Issues & Discussions ](https://github.com/Rikitav/Telegrator/issues )
---
2026-03-07 23:30:47 +04:00
> **Feel free to contribute, ask questions, or open issues!**
2026-03-07 23:22:40 +04:00
2026-03-07 23:38:14 +04:00
В главных ролях :
> Сишарпилло Крокодилло,
> Дыкий Сишарп,
> Шарпенко Михаил Дотнетович
Кастинг и Тестирование :
> не проводилось