Telegami 0.0.4
See the version list below for details.
dotnet add package Telegami --version 0.0.4
NuGet\Install-Package Telegami -Version 0.0.4
<PackageReference Include="Telegami" Version="0.0.4" />
<PackageVersion Include="Telegami" Version="0.0.4" />
<PackageReference Include="Telegami" />
paket add Telegami --version 0.0.4
#r "nuget: Telegami, 0.0.4"
#:package Telegami@0.0.4
#addin nuget:?package=Telegami&version=0.0.4
#tool nuget:?package=Telegami&version=0.0.4
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 | Versions 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. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.4)
- Telegram.Bot (>= 22.5.0)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.4)
- Telegram.Bot (>= 22.5.0)
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.