TgCore 1.3.1

dotnet add package TgCore --version 1.3.1
                    
NuGet\Install-Package TgCore -Version 1.3.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TgCore" Version="1.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TgCore" Version="1.3.1" />
                    
Directory.Packages.props
<PackageReference Include="TgCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TgCore --version 1.3.1
                    
#r "nuget: TgCore, 1.3.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TgCore@1.3.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TgCore&version=1.3.1
                    
Install as a Cake Addin
#tool nuget:?package=TgCore&version=1.3.1
                    
Install as a Cake Tool

TgCore

A flexible .NET engine for creating Telegram bots
The library helps quickly and structuredly build bots in C#, abstracting the routine work with the Telegram API and providing basic infrastructure.

Installation:

  dotnet add package TgCore

Contacts & Links

Limitations and development

The library does not cover the entire Telegram Bot API. However, the existing functionality is sufficient for creating high-quality and functional bots.

.NET Requirements

  • .NET 8.0 or higher
  • Works on Windows, Linux, and macOS
  • Using the latest stable .NET versions is recommended for better performance and compatibility

Minimum working bot

using TgCore;

// 1. Create a bot instance
var bot = TelegramBot.Default("YOUR_BOT_TOKEN");

// 2. Create handlers
async Task UpdateHandler(Update update, CancellationToken ct)
{
    if (update.Type == UpdateType.Message && update.Text != null)
    {
        await bot.Requests.SendText(update.GetFrom!.Id, $"You said: {update.Text}", ct:ct);
    }
}

async Task ErrorHandler(Exception exception, CancellationToken ct)
{
    Console.WriteLine($"Error: {exception.Message}");
}

// 3. Register handlers
bot.AddUpdateHandler(UpdateHandler);
bot.AddErrorHandler(ErrorHandler);

// 4. Start the bot
await bot.Run();

// 5. Stop (if needed)
await bot.Stop();

// 6. Restart (if needed)
await bot.Restart();

Basic concepts

UpdateHandler Handles all user actions:

async Task UpdateHandler(Update update, CancellationToken ct)
{
    // Determine the event type
    switch (update.Type)
    {
        case UpdateType.Message:
            // Work with the message
            await HandleMessage(update, ct);
            break;

        case UpdateType.CallbackQuery:
            // Handle button click
            await bot.Message.AnswerCallbackQuery(update.CallbackQuery!.Id, ct:ct);
            await HandleCallback(update, ct);
            break;

        // Other update types...
    }
}

ErrorHandler Handles all API errors:

async Task ErrorHandler(Exception exception, CancellationToken ct)
{
    // Log the error
    Console.WriteLine($"Error: {exception}");
}

Working with requests

// Simple text
await bot.Requests.SendText(chatId, "Hello!");

// With formatting
await bot.Requests.SendText(
    chatId: chatId,
    text: "*Bold text* and `code`",
    parseMode: ParseMode.Markdown,
    keyboard: InlineKeyboard.Create()
        .Row(InlineButton.CreateUrl("Link", "https://github.com/AQSIDE"))
        .Row(InlineButton.CreateData("Button 1", "btn1"), InlineButton.CreateData("Button 2", "btn1"))
        .Build(),
    lifeTime: TimeSpan.FromSeconds(30) // Self-deletion in 30 seconds
);

// Photo with caption
await bot.Requests.SendMedia(
    chatId: chatId,
    file: InputFile.FromUrl(InputFileType.Photo, "https://example.com/photo.jpg"),
    caption: "My photo 📸",
    keyboard: InlineKeyboard.Create()
        .Row(InlineButton.CreateData("👍", "like"), InlineButton.CreateData("👎", "dislike"))
        .Build()
);        


// Direct API requests
var result = await bot.Requests.SendRequest<Message>(
    method: TelegramMethods.SEND_MESSAGE,
    body: new
    {
        chat_id = chatId,
        photo = "https://example.com/photo.jpg",
        caption = "Photo via direct request",
        reply_markup = new { inline_keyboard = new[] { new[] { new { text = "Test", callback_data = "test" } } } }
    }, 
    ct:ct
);

if (result.Ok)
{
    Console.WriteLine($"Message sent with ID: {result.Result!.MessageId}");
}

// Deletion
bool success = await bot.Requests.DeleteMessage(chatId, messageId);

Configuration

var client = new TelegramClient("YOUR_BOT_TOKEN"); // Telegram client (base implementation)

bot = TelegramBot
    .Create(client) // Start bot builder
    .UseUpdateReceiver(
        new LongPollingReceiver(
            client,                                     // Source of incoming updates
            new[] { UpdateType.Message, UpdateType.CallbackQuery }, // Allowed update types
            limit: 100,                                 // Maximum updates per request
            timeout: 30,                                // Long polling timeout (seconds)
            startOffset: 0                              // Update ID offset
        )
    )
    .UseLoopRunner(new BotLoopRunner())                // Start bot loop runner
    .UseTelemetry()                                    // Enable automatic telemetry
    .UseDefaultParseMode(ParseMode.MarkdownV2)         // Default parse mode if not specified
    .Build();

bot.Modules                                            // Configure modules
    .UseMessageLifetime()                              // Message lifetime (auto-delete) module
    .UseRateLimit(new RateLimitModule(
        maxTokens: 20,                                 // Max 20 requests per second
        maxBurst: 3,                                   // Allowed burst size
        interval: TimeSpan.FromSeconds(1)
    ))
    .UseTextFormatter()                                // Text formatter for parse_mode (beta)
    .UseTemporaryMessageLimiter(
        new TemporaryMessageLimiterModule(             // Temporary message limiter
            maxMessageLimit: 3,                         // Maximum 3 temporary messages
            mode: TemporaryLimiterMode.Reject           // Behavior when limit is exceeded
        )
    )
    .Apply();

// Subscribe to message lifetime events
bot.Options.Lifetime.OnAdd = OnAdd;
bot.Options.Lifetime.OnDelete = OnDelete;

Advanced architecture

public class Program
{
    private static ContextFactory _contextFactory = null!;
    private static BuildFactory _buildFactory = null!;
    private static RouterManager _routerManager = null!;

    private static TelegramBot _bot = null!;

    private static async Task Main()
    {
        var client = new TelegramClient("YOUR_BOT_TOKEN");
        
        _bot = TelegramBot
            .Create(client)
            .UseDefaultParseMode(ParseMode.MarkdownV2)
            .UseTelemetry(new TelemetryConfig(interval:TimeSpan.FromMinutes(10)))
            .UseUpdateReceiver(new LongPollingReceiver(client, _allowedUpdates))
            .Build();
        
        _bot.Modules
            .UseMessageLifetime()
            .UseRateLimit()
            .UseTextFormatter() // (beta)
            .UseTemporaryMessageLimiter()
            .Apply();

        _bot.AddUpdateHandler(UpdateHandler)
            .AddErrorHandler(ErrorHandler);

        _buildFactory = new BuildFactory(_bot);
        _contextFactory = new ContextFactory(_bot);

        _routerManager = _buildFactory.BuildRoute();

        await _bot.Run();
    }

    private static async Task UpdateHandler(Update update, CancellationToken ct)
    {
        var ctx = _contextFactory.CreateContext(update);
        if (ctx == null) return;

        await _routerManager.Route(ctx, ct);
    }

    private static async Task ErrorHandler(Exception exception, CancellationToken ct)
    {
        Debug.LogError(ex.ToString());
    }
}

Bot Loop

Bot Loop is an infinite loop that triggers at a specified interval in milliseconds. It is used to simplify prototyping and avoid creating Task.Run manually.

IBotLoop interface

public interface IBotLoop
{
    // Interval between ticks in milliseconds
    int IntervalMs { get; }
    
    // Method called on each tick
    Task OnTick();
}

Implementation example

public class YourLoop : IBotLoop
{
    // Interval between ticks in milliseconds.
    public int IntervalMs { get; set; }

    // Initializes a new instance of YourLoop with the specified interval.
    public YourLoop(int intervalMs)
    {
        IntervalMs = intervalMs;
    }
    
    // Method called on each tick.
    public async Task OnTick()
    {
        // Code executed on each tick
    }
}

// Adding the loop to the bot
bot.AddLoop(new YourLoop(100));

Basic usage

// Class for delayed and recurring tasks.
// No need to create manually, it is already included in TelegramBot:
// bot.MainLoop
public class BotTaskLoop : IBotLoop
    
// Example: Adding a delayed task using Func<Task>
bot.MainLoop.AddTask(
    DateTime.Now.AddSeconds(5), // Executes after 5 seconds
    Execute                     // The function to execute
);

// Example: Adding a repeating task using Func<Task>
bot.MainLoop.AddRepeatingTask(
    TimeSpan.FromSeconds(5),    // Interval between executions
    Execute,                    // The function to execute
    DateTime.Now.AddSeconds(5)  // Start time for the first execution
);

Telemetry

Telemetry is a system that collects runtime data about everything that is happening inside your bot. It is designed to help you understand bot behavior, performance, errors, and usage patterns in real time.

// Fluent API usage
bot = TelegramBot
    .Create(client)
    .UseTelemetry() // Enable telemetry here
    .Build();

// OR via options 

var options = new BotOptions(client) 
{
    InitialUseTelemetry = true
};

Telemetry uses a configuration object to control memory usage, compatibility, and behavior. All limits exist to prevent uncontrolled memory growth.

public TelemetryConfig(
    bool useAutoLog = true,        // Enable automatic logging to console
    TimeSpan? interval = null,     // Interval between snapshot reports
    int maxRequests = 30,          // Maximum stored HTTP requests
    int maxUpdates = 30,           // Maximum stored updates
    int maxUpdateHandlers = 30,    // Maximum stored UpdateHandler executions
    int maxErrorHandlers = 30,     // Maximum stored ErrorHandler executions
    int maxUsers = 30,             // Maximum stored users
    int maxChats = 30,             // Maximum stored chats
    int maxErrors = 30,            // Maximum stored errors
    int maxInteractionContext = 10,// Stored interaction context per user/chat
    bool allowPrivateChat = false  // Include private chats in chat statistics
)

What Telemetry Stores Each telemetry report is represented by a snapshot, which contains aggregated and detailed runtime data.

public sealed class TelemetrySnapshotDto
{
    // Unique snapshot identifier
    public long Id { get; init; }
    
    // Snapshot creation time
    public DateTime Timestamp { get; init; }

    // Updates received during the current period
    public long PeriodUpdates { get; init; }
    
    // Updates received since bot start
    public long TotalUpdates { get; init; }

    // HTTP request counters
    public long PeriodRequests { get; init; }
    public long TotalRequests { get; init; }
    
    public long PeriodSuccessfulRequests { get; init; }
    public long PeriodFailedRequests { get; init; }
    public long TotalSuccessfulRequests { get; init; }
    public long TotalFailedRequests { get; init; }

    // Error counters
    public long PeriodErrors { get; init; }
    public long TotalErrors { get; init; }

    // Unique users
    public long PeriodUniqueUsers { get; init; }
    public long TotalUniqueUsers { get; init; }
    
    // Unique chats
    public long PeriodUniqueChats { get; init; }
    public long TotalUniqueChats { get; init; }

    // Derived metrics
    public double UpdatesPerUser { get; init; }
    public double RequestsPerUser { get; init; }
    
    public double UpdatesPerChat { get; init; }
    public double RequestsPerChat { get; init; }

    // Latency statistics (avg / min / max)
    public TelemetryLatencyStats UpdateHandlerLatency { get; init; }
    public TelemetryLatencyStats ErrorHandlerLatency { get; init; }
    public TelemetryLatencyStats HTTPLatency { get; init; }

    // Detailed runtime data
    public List<TelemetryUserDto> Users { get; init; }
    public List<TelemetryChatDto> Chats { get; init; }
    public List<TelemetryUpdateDto> Updates { get; init; }
    public List<TelemetryRequestDto> Requests { get; init; }
    public List<TelemetryErrorDto> Errors { get; init; }

    // Error classification
    public List<ApiErrorStats> ApiErrors { get; init; }
    public List<LocalErrorStats> LocalErrors { get; init; }
}

Using Telemetry at Runtime

bot.Telemetry.Config;               // Access current telemetry configuration
bot.Telemetry.Enabled;              // Enable / disable telemetry collection
bot.Telemetry.LastAutoLogMessage;   // Last auto-log message
bot.Telemetry.LastSnapshot;         // Get last snapshot (can be null)
bot.Telemetry.GetSnapshot();        // Force snapshot creation
bot.Telemetry.Update(s => s.ClearUniqueCache()); // Apply snapshot update

// Report Subscription
// You can subscribe to telemetry reports and handle them manually.

bot.Telemetry.OnReport = OnReport;
bot.Telemetry.Config.UseAutoLog = false; // Recommended when using OnReport

// Called asynchronously on each report
// lastSnapshot can be null (first report)
async Task OnReport(
    TelemetrySnapshotDto snapshot,
    TelemetrySnapshotDto? lastSnapshot)
{
    // Custom processing (logging, metrics export, alerts, etc.)
}

Important Notes. Telemetry permanently stores unique user and chat IDs to provide accurate lifetime statistics. This cache is not cleared automatically. To manually reset unique user/chat counters, use:

bot.Telemetry.Update(s => s.ClearUniqueCache());

Modules

Modules are built-in features that simplify working with the API.

IMessageLifetimeModule Manages message lifetime and allows automatic deletion after a set time.

public interface IMessageLifetimeModule
{
    // Invoked when a message is added.
    // Parameters: long chatId/userId, long messageId
    Func<long, long, Task>? OnAdd { get; set; }
    
    // Invoked when a message is deleted.
    // Parameters: long chatId/userId, long messageId
    Func<long, long, Task>? OnDelete { get; set; }
    
    // Marks a message for automatic deletion after the specified lifetime.
    public Task Set(long chatId, long messageId, TimeSpan lifetime);
    
    // Removes the deletion mark from a message.
    public Task<bool> Remove(long chatId, long messageId);
    
    // Deletes a message immediately.
    public Task<bool> Delete(long chatId, long messageId);
    
    // Clears all deletion marks for a specific chatId/userId.
    public void ClearMessages(long chatId);
}

// Activating the module
bot.Options.Lifetime = new MessageLifetimeModule(_bot, _bot.MainLoop)
{
    // Enable module logging
    UseLogging = true
};

// Usage example
await bot.Requests.SendText(
    user.Id,
    $"⏳ <b>{user.Username}</b>, {TextFormatter.WaitTime(activity.timeLeft.TotalSeconds)}", 
    replyId:context.MessageId,
    lifeTime:TimeSpan.FromSeconds(5)); // Message will be deleted after 5 seconds

IRateLimitModule Manages request rate limits automatically to avoid exceeding Telegram API limits.

public interface IRateLimitModule
{
    // Waits until the next request can be sent according to the rate limit.
    ValueTask WaitAsync(CancellationToken ct = default);
}

// Activating the module
bot.Options.RateLimit = new RateLimitModule(
            maxTokens:20,                       // Maximum 20 requests per second
            maxBurst:3,                         // Maximum burst size
            interval: TimeSpan.FromSeconds(1)); // interval

ITemporaryMessageLimiterModule Limits temporary messages in chat, used with ILifetimeModule to prevent spam.

public interface ITemporaryMessageLimiterModule
{
    // Maximum number of temporary messages allowed per chat
    int MaxMessageLimit { get; set; }

    // Checks if a new message can be sent in the chat
    Task<bool> CanSend(long chatId);
    
    // Registers a sent temporary message
    Task RegisterMessage(long chatId, long messageId);
    
    // Unregisters a temporary message
    Task UnregisterMessage(long chatId, long messageId);
}

// Activating the module
_bot.Options.TemporaryMessageLimiter = new TemporaryMessageLimiterModule(
            maxMessageLimit:3,                         // Maximum of 3 temporary messages for user/chat
            mode:TemporaryLimiterMode.ReplaceOldest,  // Mode when the limit is exceeded
            lifetimeModule:_bot.Options.Lifetime)     // Uses ILifetimeModule
        {
            // Enable module logging
            UseLogging = true,
        };
       
// Operation modes
public enum TemporaryLimiterMode
{
    Reject,         // Limit exceeded: the new message will not be sent
    ReplaceOldest,  // Deletes the oldest message and sends the new one
    ReplaceNewest   // Deletes the newest message, keeping the old one
}

ITextFormatterModule Automatically formats outgoing message text. This module is applied when text is sent as part of a request.

Why this exists. Telegram will throw a BadRequest error if the message contains invalid or unclosed Markdown tags. TextFormatterModule automatically fixes such cases, making message sending safer and more predictable.

public interface ITextFormatterModule
{
    string Process(string text, ParseMode mode);
}

Default Implementation

// Formats text if it contains unclosed tags.
// Applied to Markdown and MarkdownV2 to prevent BadRequest errors.
public class TextFormatterModule : ITextFormatterModule

public string Process(string text, ParseMode mode)
{
    if (mode == ParseMode.None) return text;

    if (mode == ParseMode.Markdown)
        text = ProcessMarkdown(text, mode);
    else if (mode == ParseMode.MarkdownV2)
        text = ProcessMarkdownV2(text, mode);

    return text;
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.1 111 2/5/2026
1.2.1 101 1/29/2026
1.2.0 105 1/27/2026
1.1.3 108 1/25/2026
1.0.1 105 1/23/2026
1.0.0 102 1/23/2026