Telegami 0.0.4

There is a newer version of this package available.
See the version list below for details.
dotnet add package Telegami --version 0.0.4
                    
NuGet\Install-Package Telegami -Version 0.0.4
                    
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="Telegami" Version="0.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Telegami" Version="0.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Telegami" />
                    
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 Telegami --version 0.0.4
                    
#r "nuget: Telegami, 0.0.4"
                    
#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 Telegami@0.0.4
                    
#: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=Telegami&version=0.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Telegami&version=0.0.4
                    
Install as a Cake Tool

Telegami

A simple library for building Telegram bots.

The goal of Telegami is to keep things straightforward and easy to use.
It builds on top of the low-level Telegram.Bot library, which handles communication with the Telegram Bot API.

There’s also the TelegramBotFramework, but it's a bit too complex for many common use cases.

Inspired by the Node.js Telegraf library, Telegami aims to offer a clear and intuitive experience for handling basic bot scenarios.

Current status: Active development — breaking changes may occur between releases.

Architecture

  • Uses Microsoft.Extensions.DependencyInjection for dependency injection, following the .NET standard.
  • Each message is handled in a new DI scope — similar to the per-request model in ASP.NET.
  • The syntax is inspired by the Telegraf library and combines well with ASP.NET minimal APIs, allowing you to inject dependencies directly into delegates.
  • Only one handler will process each incoming message — the first one whose condition matches.
  • Handlers are evaluated in the order they are added to the bot.

Basic Usage Examples

Getting Started

var serviceCollection = new ServiceCollection();
serviceCollection.AddTelegamiBot("BOT_TOKEN_FROM_BOT_FATHER");

var serviceProvider = serviceCollection.BuildServiceProvider();

var botsManager = serviceProvider.GetRequiredService<TelegamiBotsManager>();
var bot = botsManager.Get();

// Handle /start command
bot.Start(async ctx =>
{
    await ctx.ReplyAsync("Hello! I'm a bot. How can I help you?");
});

// Handle /custom command
bot.Command("custom", async ctx =>
{
    await ctx.ReplyAsync($"This is a custom command handler. Arguments: '{ctx.BotCommand!.Arguments}'");
});

await botsManager.LaunchAsync();

That's it! Bot is working and can handle /start and /custom commands.

Specific message type

// response only on Sticker message type
bot.On(MessageType.Sticker, async ctx => { await ctx.ReplyAsync($"What a nice sticker!"); });

// response only on Text message type
bot.On(MessageType.Text, async ctx => { await ctx.ReplyAsync("Echo: " + ctx.Message.Text); });

// response to any message
bot.On(async ctx => { await ctx.ReplyAsync("Type: " + ctx.Message.Type); });

Dependency injection

class MyCustomService{}

var serviceCollection = new ServiceCollection();
serviceCollection.AddTelegamiBot("BOT_TOKEN_FROM_BOT_FATHER");

// register service
serviceCollection.AddScoped<MyCustomService>();

var serviceProvider = serviceCollection.BuildServiceProvider();

var botsManager = serviceProvider.GetRequiredService<TelegamiBotsManager>();
var bot = botsManager.Get();

// ask for it!
bot.Command("dependency", async (MessageContext ctx, MyCustomService myCustomService)  =>
{
    await ctx.ReplyAsync($"This is a dependency injection handler for service: " + myCustomService.GetType().Name);
});

// .. other code

Advanced scenarios

Often, it is necessary to interact with the user and not just process commands (e.g., collect name, last name, and age).
That's why we have Scene. Scene is bound to the user ID, chat ID, and thread ID, which means that different users can interact, and each one will have their own data. Also, when a user is in a Scene, all messages are handled by the scene, not by the bot.

Let's check out two approaches: Scene and WizardScene.

Scene

Easy to use and it's like bot inside bot with special handlers Enter and Leave.


// let's add command, so we can start scene
bot.Command("person", async ctx => { await ctx.EnterSceneAsync("person_scene"); });

// let's define scene, where we will collect all required information
var personCardScene = new Scene("person_scene");
// Enter will be executed when EnterSceneAsync is invoked with message context that was at that moment
personCardScene.Enter(async ctx => await ctx.SendAsync("Hi! What's your name?"));

// Leave will be executed when LeaveSceneAsync is invoked with message context that was at that moment
personCardScene.Leave(async ctx =>
{
    var person = ctx.Session.Get<Person>() ?? new Person();

    await ctx.ReplyAsync($"Your name is {person.Name} {person.LastName}, you are {person.Age} years old.");
});

// 
personCardScene.On(MessageType.Text, async ctx =>
{
    var person = ctx.Session.Get<Person>() ?? new Person();

    // get name
    if (string.IsNullOrEmpty(person.Name))
    {
        person.Name = ctx.Message.Text;
        ctx.Session.Set(person);
        await ctx.ReplyAsync($"What's your last name?");
        return;
    }

    // get last name
    if (string.IsNullOrEmpty(person.LastName))
    {
        person.LastName = ctx.Message.Text;
        ctx.Session.Set(person);
        await ctx.ReplyAsync($"What's your age?");
        return;
    }

    // age
    if (!int.TryParse(ctx.Message.Text, out var age))
    {
        await ctx.ReplyAsync($"Age should be a number!");
        return;
    }

    person.Age = age;
    // and leave!, so message handling is returned to regular bot
    await ctx.LeaveSceneAsync();
});

// also it's required to add scene to bot, so it knows about it
bot.AddScene(personCardScene);

WizardScene

WizardScene has stages that can be invoked one by one. Let’s see the same example with this approach.
For the wizard, we have a special class, WizardContext, to track the wizard's step, and WizardContext<TState> to track the step and provide a strongly-typed state.

// let's add command, so we can start scene
bot.Command("person_wizard", async ctx => { await ctx.EnterSceneAsync("person_wizard_scene"); });

var wizardScene = new WizardScene("person_wizard_scene",
    async (MessageContext ctx, WizardContext wiz) =>
    {
        // first step will be invoked on Enter
        await ctx.SendAsync("Hi! What's your name?");
        // now we change step and next message will be processed with it
        wiz.Next();
    },
    async (MessageContext ctx, WizardContext<Person> wiz) =>
    {
        if (string.IsNullOrEmpty(ctx.Message.Text))
        {
            // we didn't call Next(), so next message will be handled by this step again
            await ctx.SendAsync("Incorrect message, please send text");
            return;
        }
        // set name, that all, state will be saved automatically
        wiz.State.Name = ctx.Message.Text;

        await ctx.SendAsync("Hi! What's your last name?");
        wiz.Next();
    },
    async (MessageContext ctx, WizardContext<Person> wiz) =>
    {
        if (string.IsNullOrEmpty(ctx.Message.Text))
        {
            // we didn't call Next(), so next message will be handled by this step again
            await ctx.SendAsync("Incorrect message, please send text");
            return;
        }

        wiz.State.LastName = ctx.Message.Text;

        await ctx.SendAsync("Hi! What's your age?");
        wiz.Next();
    },
    async (MessageContext ctx, WizardContext<Person> wiz) =>
    {
        if (string.IsNullOrEmpty(ctx.Message.Text))
        {
            await ctx.SendAsync("Incorrect message, please send text");
            return;
        }

        if (!int.TryParse(ctx.Message.Text, out var age))
        {
            await ctx.SendAsync("Age should be a number!");
            return;
        }

        wiz.State.Age = age;

        await ctx.SendAsync($"Thank you! Your information is:\n{wiz.State}");
        // and leave scene at the end
        await ctx.LeaveSceneAsync();
    }
);

bot.AddScene(wizardScene);

Middleware

Inspired by ASP.NET

Error handler example

Same logic as in ASP.NET every middleware have next delegate, so we can process MessageContext.

var serviceCollection = new ServiceCollection();
serviceCollection.AddTelegamiBot("BOT_TOKEN_FROM_BOT_FATHER");

var serviceProvider = serviceCollection.BuildServiceProvider();

var botsManager = serviceProvider.GetRequiredService<TelegamiBotsManager>();

var bot = botsManager.Get();
bot.Use<GlobalErrorHandlerMiddleware>();

//.. other code

await bot.LaunchAsync();

class GlobalErrorHandlerMiddleware : ITelegamiMiddleware
{
    public async Task InvokeAsync(MessageContext ctx, MessageContextDelegate next)
    {
        try
        {
            await next(ctx);
        }
        catch (Exception e)
        {
            System.Console.WriteLine(e);
            await ctx.ReplyAsync("error: " + e.Message);
        }
    }
}
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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Telegami:

Package Downloads
Telegami.Sessions.LiteDB

Library to replace in memory session storage with LiteDB for Telegami bot

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.13 147 5/6/2025
0.0.12 144 5/6/2025
0.0.11 146 4/30/2025
0.0.10 155 4/28/2025
0.0.9 171 4/21/2025
0.0.8 170 4/21/2025
0.0.7 183 4/18/2025
0.0.6 181 4/18/2025
0.0.4 196 4/17/2025
0.0.2 187 4/17/2025