{"id":30325691,"url":"https://github.com/win7user10/laraue.telegram.net","last_synced_at":"2026-02-21T11:02:51.912Z","repository":{"id":143070344,"uuid":"584072600","full_name":"win7user10/Laraue.Telegram.NET","owner":"win7user10","description":"Create backend for telegram bots","archived":false,"fork":false,"pushed_at":"2025-08-10T09:23:33.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-10T09:28:24.097Z","etag":null,"topics":["csharp","csharp-library","telegram","telegram-bot"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/win7user10.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-01T08:00:12.000Z","updated_at":"2025-08-10T09:23:36.000Z","dependencies_parsed_at":"2023-12-26T12:24:01.927Z","dependency_job_id":"a2e94c16-e9e3-43b4-a842-d7dd1f63da07","html_url":"https://github.com/win7user10/Laraue.Telegram.NET","commit_stats":null,"previous_names":[],"tags_count":73,"template":false,"template_full_name":null,"purl":"pkg:github/win7user10/Laraue.Telegram.NET","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.Telegram.NET","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.Telegram.NET/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.Telegram.NET/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.Telegram.NET/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/win7user10","download_url":"https://codeload.github.com/win7user10/Laraue.Telegram.NET/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.Telegram.NET/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270918404,"owners_count":24667679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","csharp-library","telegram","telegram-bot"],"created_at":"2025-08-17T23:03:04.090Z","updated_at":"2026-02-21T11:02:51.903Z","avatar_url":"https://github.com/win7user10.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Laraue.Telegram.NET\n\nThis library contains infrastructure code to write testable telegram bots for one to one conversations.\nThe library use https://github.com/TelegramBots/Telegram.Bot package inside to communicate with Telegram.\n\n## Laraue.Telegram.NET.Core\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.Telegram.NET.Core)](https://www.nuget.org/packages/Laraue.Telegram.NET.Core)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.Telegram.NET.Core)](https://www.nuget.org/packages/Laraue.Telegram.NET.Core)\n\nThe basic idea of the library is to register all possible telegram routes in classes\ninherited from _TelegramController_.\n\n```csharp\npublic class MenuController : TelegramController\n{\n    private readonly IMenuService _service;\n\n    public SettingsController(IMenuService service)\n    {\n        _service = service;\n    }\n    \n    [TelegramMessageRoute(\"/start\")] // When the user will send the '/start' message\n    public Task ShowMenuAsync(TelegramRequestContext requestContext)\n    {\n        return _service.HandleStartAsync(requestContext.Update.Message!); // execute this code\n    }\n```\n\nTo process callback queries can be used attribute _TelegramCallbackRouteAttribute_,\nany request type can be handled by implementing the attribute inherited from _TelegramBaseRouteAttribute_.\n\nTo start using this and other controllers, the library should be added with the following way:\n\nA. Using webhooks\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddOptions\u003cTelegramNetOptions\u003e();\nbuilder.Services.Configure\u003cTelegramNetOptions\u003e(builder.Configuration.GetSection(\"Telegram\"));\n\nbuilder\n    .Services\n    .AddTelegramCore(new TelegramBotClientOptions(\"5118263652:AAHiPDQ8kVcbs2WZWG4Z...\"))\n    .AddInMemoryUpdatesQueue();\n\nvar app = builder.Build();\napp.MapTelegramRequests(\"/api/telegram\");\napp.Run();\n```\n\nB. Using long pooling\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddOptions\u003cTelegramNetOptions\u003e();\nbuilder.Services.Configure\u003cTelegramNetOptions\u003e(builder.Configuration.GetSection(\"Telegram\"));\n\nbuilder\n    .Services\n    .AddTelegramCore(new TelegramBotClientOptions(\"5118263652:AAHiPDQ8kVcbs2WZWG4Z...\"))\n    .AddInMemoryUpdatesQueue();\n\nvar app = builder.Build();\napp.Run();\n```\n\nThe library takes the decision what way to use based on the property `WebhookUrl` in the app settings.\nWhen the setting is set, the application considers Webhook mode is used, otherwise long pooling mode\nis active.\n\nThe token in the TelegramBotClientOptions can be taken via @BotFather bot in the Telegram.\n_MapTelegramRequests_ method setup the address which will listen callbacks from the telegram.\nThis address should be set also on the telegram side by calling the next url:\n\n**Note**: The AddInMemoryUpdatesQueue call adds to the container InMemory queue to store telegram updates.\nIt is not recommended due to possible updates loss. The better way is to use \n`AddEfCoreUpdatesQueue\u003cMyDbContext\u003e` from the package `Laraue.Telegram.NET.UpdatesQueue.EFCore`\nto store all updates queue in the database or implementing custom `IUpdatesQueue`.\n```\nhttps://api.telegram.org/bot5118263652:AAHiPDQ8kVcbs2WZWG4Z.../setWebhook?url=https://your.host/api/telegram\n```\n\n## Laraue.Telegram.NET.Authentication\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.Telegram.NET.Authentication)](https://www.nuget.org/packages/Laraue.Telegram.NET.Authentication)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.Telegram.NET.Authentication)](https://www.nuget.org/packages/Laraue.Telegram.NET.Authentication)\n\nAs soon as each request is usually associated with the specific user, it is convenient to\nhave information about system user in the request instead of manual finding\nuser by telegram id when it is required.\nThis library helps to integrate ASP.NET.Identity with the telegram request context.\nTo use it the model of user can be defined (example for the user with id of _Guid_ type)\n```csharp\npublic class User : TelegramIdentityUser\u003cGuid\u003e\n{}\n\n```\nRegister Auth functionality in the container. Here is the using of ```User``` model with _Guid_ as identifier.\n```csharp\nservices.AddTelegramCore(new TelegramBotClientOptions(builder.Configuration[\"Telegram:Token\"]!))\n    .AddTelegramAuthentication\u003cUser, Guid\u003e()\n    .AddEntityFrameworkStores\u003cCianCrawlerDbContext\u003e()\n    .AddDefaultTokenProviders()\n```\n\nAfter that in each telegram controller method can be retrieved ```TelegramRequestContext\u003cGuid\u003e``` which contains information\nabout the user made the request.\n\n```csharp\npublic class UserController : TelegramController\n{\n    [TelegramMessageRoute(\"/me\")]\n    public void PrintMyId(TelegramRequestContext\u003cGuid\u003e requestContext)\n    {\n        Console.WriteLine(requestContext.UserId); // user id in our storage\n        Console.WriteLine(requestContext.Update.GetUserId()); // telegram user id from the update\n    }\n```\n\nTo prevent writing generic type for each request, new context class can be created and added to the container.\n```csharp\npublic sealed class RequestContext : TelegramRequestContext\u003cGuid\u003e\n{}\n```\n\n```csharp\nservices.AddTelegramAuthentication\u003cUser, Guid, RequestContext\u003e()\n```\n\nNow the class ```RequestContext``` can be injected without defining request generic type in the each request.\n\n```csharp\npublic class UserController : TelegramController\n{\n    [TelegramMessageRoute(\"/also-me\")]\n    public void PrintMyId(RequestContext requestContext)\n    {\n        Console.WriteLine(requestContext.UserId);\n    }\n```\n\n**Note** - ```AddTelegramAuthentication()``` returns ```Microsoft.AspNetCore.Identity.IdentityBuilder```,\nuse it to configure identity options.\n\nThe logic of retrieving user id field is next: from the received telegram message telegram identifier of the user that send the message takes.\nThen this identifier maps to the identifier in the database. Then this identifier sets to the request context.\n\n### Middleware\nThe package has the opportunity to extend request pipeline by adding custom middlewares. The example is the next\n```csharp\npublic class LogExceptionsMiddleware : ITelegramMiddleware\n{\n    private readonly ITelegramMiddleware _next;\n    private readonly TelegramRequestContext _telegramRequestContext;\n\n    public LogExceptionsMiddleware(\n        ITelegramMiddleware next,\n        TelegramRequestContext telegramRequestContext)\n    {\n        _next = next;\n        _telegramRequestContext = telegramRequestContext;\n    }\n    \n    public async Task\u003cobject?\u003e InvokeAsync(CancellationToken ct = default)\n    {\n        try\n        {\n            return await _next.InvokeAsync(ct);\n        }\n        catch (BadTelegramRequestException ex)\n        {\n            _logger.LogError(ex, \"Error occured\");\n        }\n\n        return null;\n    }\n}\n```\nAdding middleware to the request pipeline\n```csharp\nservices.AddTelegramMiddleware\u003cLogExceptionsMiddleware\u003e();\n```\n**Note** - middlewares executes in the order they were added.\n\n## Laraue.Telegram.NET.Interceptors\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.Telegram.NET.Interceptors)](https://www.nuget.org/packages/Laraue.Telegram.NET.Interceptors)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.Telegram.NET.Interceptors)](https://www.nuget.org/packages/Laraue.Telegram.NET.Interceptors)\n\nThe main case of the library is sometimes something from the user should be asked,\nand next his answer should be considered as answer to this question.\nThis library allow to create such functionality. To use it should be implemented how to store Type that should be used to answer\nfor question.\n```csharp\npublic class InterceptorState : BaseInterceptorState\n{\n    protected override Task\u003cstring?\u003e TryGetStringIdentifierFromStorageAsync(string userId)\n    {\n        throw new NotImplementedException();\n    }\n\n    protected override Task SetStringIdentifierToStorageAsync(string userId, string id)\n    {\n        throw new NotImplementedException();\n    }\n\n    public override Task ResetAsync(string userId)\n    {\n        throw new NotImplementedException();\n    }\n}\n```\nAdd the functionality to container. Interceptors are depending on _Laraue.Telegram.NET.Core_ \nand _Laraue.Telegram.NET.Authentication_ packages and should be registered in such order.\n```csharp\nservices.AddTelegramCore(new TelegramBotClientOptions(builder.Configuration[\"Telegram:Token\"]!))\n    .AddTelegramRequestEfCoreInterceptors\u003cInterceptorState\u003e()\n    .AddTelegramAuthentication\u003cUser, Guid\u003e()\n```\n**Note** - there is already implemented IInterceptorState with storing state in the DB via EFCore in the package\n_Laraue.Telegram.NET.Interceptors.EFCore_.\n```csharp\nservices.AddTelegramRequestEfCoreInterceptors\u003cGuid, MyDbContext\u003e(assemblies})\n```\n\nAfter interceptors state storage is setup, the interceptor can be implemented\n```csharp\npublic class UpdateAgeInterceptor : BaseRequestInterceptor\u003cGuid, uint, UpdateAgeContext\u003e\n{\n    private readonly IUserRepository _repository;\n    \n    public UpdateAgeResponseInterceptor(IUserRepository repository)\n    {\n        _repository = repository;\n    }\n    \n    protected override Task ValidateAsync(\n        TelegramRequestContext\u003cGuid\u003e requestContext,\n        InterceptResult\u003cuint\u003e interceptResult,\n        UpdateAgeContext interceptorContext)\n    {\n        if (uint.TryParse(update.Message!.Text, out var age))\n        {\n            interceptResult.SetModel(age);\n        }\n        else\n        {\n            interceptResult.SetError(\"Age should be a positive number\");\n        }\n        \n        return Task.CompletedTask;\n    }\n    \n    protected abstract Task ExecuteRouteAsync(\n        TelegramRequestContext\u003cGuid\u003e requestContext,\n        uint model,\n        UpdateAgeContext interceptorContext)\n    {\n        return _repository.UpdateUserAgeAsync(interceptorContext.UserId, model);\n    }\n}\n\n```\nThere is a short example of interceptor using\n```csharp\npublic class TestController : TelegramController\n{\n    private readonly IInterceptorState _questionState;\n    private readonly ITelegramBotClient _telegramClient;\n\n    public TestController(IInterceptorState questionState, ITelegramBotClient telegramClient)\n    {\n        _questionStorage = questionState;\n        _telegramClient = telegramClient;\n    }\n\n    [TelegramMessageRoute(\"/start\")]\n    public async Task HandleStart(TelegramRequestContext context)\n    {\n        await _client.SendTextMessageAsync(\n            context.Update.GetUserId(),\n            \"What is your age?\");\n            \n        await _questionState.SetAsync\u003cUpdateAgeInterceptor\u003e(context.UserId, new UpdateAgeInterceptor\n        {\n            UserId = context.UserId\n        });\n    }\n}\n```\n\n## Laraue.Telegram.NET.Testing\n[![latest version](https://img.shields.io/nuget/v/Laraue.Telegram.NET.Testing)](https://www.nuget.org/packages/Laraue.Telegram.NET.Testing)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.Telegram.NET.Testing)](https://www.nuget.org/packages/Laraue.Telegram.NET.Testing)\n\nThe package allows to write integration tests for host built with Telegram.NET library.\n\n### Usage\nCreate the class of Test Host implementing `TelegramTestHost` or `TelegramTestHost\u003cTUserKey\u003e` from the package.\n```csharp\npublic class AppTelegramTestHost(IServiceCollection serviceCollection)\n    : TelegramTestHost\u003cGuid\u003e(serviceCollection)\n{\n    protected override void BeforeFirstRequest()\n    {\n        // Do something before test server starts, migration run for example\n        // When tests use real DB here can be it clearing\n    }\n\n    protected override void Dispose(bool disposing)\n    {\n        // Do something when the host is no more used\n    }\n}\n```\nMy own recomendation for the next step is to create the base test class that have a method to run the host.\n`XUnit` example:\n```csharp\npublic class IntegrationTest\n{\n    protected static AppTelegramTestHost GetTelegramTestHost()\n    {\n        var builder = WebApplication.CreateBuilder();\n\n        builder.Configuration.AddJsonFile(\"appsettings.json\");\n        builder.AddApplicationServices();\n        \n        return new AppTelegramTestHost(appServices);\n    }\n}\n```\nTest infrastructure is ready. The test example:\n```csharp\npublic class StartControllerTests : IntegrationTest\n{\n    [Fact]\n    public async Task Start_ShouldSendWelcome_Always()\n    {\n        // Run the host from the base class\n        using var telegramTestHost = GetTelegramTestHost();\n    \n        // Emulate new message in chat\n        await telegramTestHost.SendUpdateAsync(new Update\n        {\n            Message = new Message\n            {\n                Text = \"/start\",\n                From = DefaultUser, // Can be determined in base class to avoid repeating\n            }\n        });\n        \n        // Ensure only one message was sent back to Telegram\n        var request = telegramTestHost\n            .Requests()\n            .Single\u003cEditMessageTextRequest\u003e();\n   \n        // Check the message text \n        request.CheckMessage(\"Hello, user\");\n        \n        // Check the buttons in the message\n        request.CheckButtonsSequentially(buttons =\u003e \n            buttons\n                .HasButtonsRow(\n                    new ButtonAssert(\"Menu\", \"menu\")));\n    }\n}\n```\nMore complex asserts can be made with the services requested from the Test Host container:\n```csharp\nusing var telegramTestHost = GetTelegramTestHost();\nusing var scope = telegramTestHost.CreateScope();\nvar dbContext = scope.ServiceProvider.GetRequiredService\u003cDatabaseContext\u003e();\n\nAssert.Single(dbContext.Users);\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwin7user10%2Flaraue.telegram.net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwin7user10%2Flaraue.telegram.net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwin7user10%2Flaraue.telegram.net/lists"}