Key Features
| Language | C# |
| Framework | NET9 |
| Project type | Library |
| Status | Completed |
| License | MIT |
| Nuget | |
| Downloads | |
| Github | Laraue.Telegram.NET |
A Few More Words About Telegram Bots
Telegram offers a versatile platform, allowing to build bots for automating tasks, create interactive tools. It provides a low-barrier entry point to learn bot development, automate project workflows, and create engaging interfaces for the applications, eliminating from the requirement to invent interface blocks, design etc.
The Main Problems This Library Tries to Solve
The Telegram API is designed for easy bot creation but doesn't enforce good architectural patterns. This often leads to monolithic entry points filled with if-else chains, which are hard to maintain.
if (request.Message?.Text == "/Start")
{
Start();
}
else if (request.Message?.Text == "/Settings")
{
OpenSettings()
}
else if (request.Callback?.Data.StartsWith("/ChangeSettings"))
{
ChangeSettings();
}
// And other commandsThis library aims to help engineers write clean, maintainable Telegram bots by avoiding spaghetti code.
The Library Vision
There's no need to reinvent the wheel when well-established patterns like MVC already exist. The library allows engineers to write ASP.NET-like controllers for Telegram bots using attributes such as:
[TelegramMessageRoute("/new")]for handling messages[TelegramCallbackRoute("/answer")]for handling callbacks
This makes it easy to see all available bot commands, improving development and maintenance.
How to Use the Library
Controller Definition
Define Telegram controllers in code:
public class MenuController : TelegramController
{
private readonly IMenuService service;
public SettingsController(IMenuService service)
{
service = service;
}
[TelegramMessageRoute("/start")]
public Task ShowMenuAsync(TelegramRequestContext requestContext)
{
return service.HandleStartAsync(requestContext.Update.Message!);
}
}Services requested in the constructor are resolved from the Microsoft DI container. The attribute
TelegramMessageRoute("/start") means the user message "/start" will be processed by the method above. The parameter TelegramRequestContext will contain the object of the request and allows to directly get the Message object.
Library Registration
The engineer should decide how to handle telegram requests. There are two ways
Webhooks
Set the webhook URL in Telegram: https://api.telegram.org/bot(token)/setWebhook?url=https://site/address-no-one-knows. Then map the endpoint in your application:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTelegramCore(new TelegramBotClientOptions("51182636:AAHiPDQ8kVcbs2WZWG4Z..."));
var app = builder.Build();
app.MapTelegramRequests("address-no-one-knows");
app.Run();
Long Polling
The way means the application will call Telegram to get new updates for the bot. Useful for local environments or when horizontal scaling isn't feasible:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTelegramCore(new TelegramBotClientOptions("51182636:AAHiPDQ8kVcbs2WZWG4Z..."));
builder.Services.AddTelegramLongPoolingService();
var app = builder.Build();
app.Run();
Authentication
To enable authentication, define a User<UserKey> object and register the functionality:
services.AddTelegramCore()
.AddTelegramAuthentication<User, Guid, TelegramUserQueryService, RequestContext>();TelegramUserQueryServiceimplements ITelegramUserQueryService, and handles user lookup/store logic.RequestContextis a wrapper aroundTelegramRequestContext<Guid>to simplify usage.
public sealed class RequestContext : TelegramRequestContext<Guid> // Here Guid is the key of TUser
{
}Now, in controllers, you can access user information:
public class MenuController : TelegramController
{
[TelegramMessageRoute("/me")]
public Task MeAsync(RequestContext request, CancellationToken ct)
{
long telegramUserId = request.Update.GetUserId();
Guid systemUserId = request.UserId;
// other code
}
}Autorization
Protect endpoints using roles:
public static class Roles
{
public const string Admin = "Admin";
}Apply the attribute to protected endpoints:
public class AdminController : TelegramController
{
[RequiresUserRole(Roles.Admin)]
[TelegramMessageRoute("/stat")]
public Task SendStatAsync(RequestContext request, CancellationToken ct)
{
// write a logic
}
}Implement IUserRoleProvider to define how roles are assigned to users:
.AddScoped<IUserRoleProvider, UserRoleProvider>()Alternatively, use the built-in StaticUserRoleProvider that loads roles from app options.
Middlewares
Extend request handling or intercept requests before they reach the app layer, similar to ASP.NET:
public class LogExceptionsMiddleware : ITelegramMiddleware
{
private readonly ITelegramMiddleware next;
private readonly TelegramRequestContext telegramRequestContext;
public LogExceptionsMiddleware(
ITelegramMiddleware next,
TelegramRequestContext telegramRequestContext)
{
next = next;
telegramRequestContext = telegramRequestContext;
}
public async Task<object?> InvokeAsync(CancellationToken ct = default)
{
try
{
return await next.InvokeAsync(ct);
}
catch (BadTelegramRequestException ex)
{
logger.LogError(ex, "Error occured");
}
return null;
}
}Register the middleware:
services.AddTelegramMiddleware<LogExceptionsMiddleware>();Localization
Enable localization by implementing BaseCultureInfoProvider:
public class LocalizationProvider : BaseCultureInfoProvider
{
private readonly RequestContext context;
private readonly IUserRepository userRepository;
public LocalizationProvider(
RequestContext context,
IOptions<TelegramRequestLocalizationOptions> options,
ILogger<BaseCultureInfoProvider> logger,
IUserRepository userRepository)
: base(context, options, logger)
{
context = context;
userRepository = userRepository;
}
protected override async Task<TelegramProviderCultureResult> DetermineProviderCultureResultAsync(
CultureInfo userInterfaceCulture,
CancellationToken cancellationToken = default)
{
var settings = await userRepository
.GetSettingsAsync(context.UserId, cancellationToken);
return new TelegramProviderCultureResult(
new CultureInfo(settings.Code),
new CultureInfo(settings.Code));
}
}Set up localization:
.AddTelegramRequestLocalization<LocalizationProvider>()
.Configure<TelegramRequestLocalizationOptions>(opt =>
{
opt.AvailableLanguages = ["en", "fr"];
opt.DefaultLanguage = ["en"];
})Now use standard Microsoft localization features with resx files:
Resources/Buttons.resx
Resources/Buttons.fr.resxAccess localized strings like Resources.Buttons.Menu — the correct language is selected automatically based on the user's settings.
Challenges
The main solved problems will be described in the separated articles
- How to make the architecture modular so users only include what they need
- How to design an extendable system that allows adding new request handling features
Timeline
- Jan 2023 Base version with core functionality based on webhooks
- Feb 2023 Authorization package added
- Jan 2024 Localization package added
- Aug 2025 Long pooling mode added
Real Use Cases
The library is widely used in the Learn Language project and is responsible for all communication with telegram. Also, the project SPB Real Estate use this library to provide the Telegram interface.