TeleFlow 1.3.1

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

🧠 TeleFlow (Telegram.Bot.Extensions.Handlers)

A modern, extensible Telegram bot framework for .NET β€” built for structured conversations, FSM-driven flows, and developer joy.

✨ Key Features

  • Multi-Step Commands (FSM) – build rich conversational flows without state spaghetti.
  • Built-in DatePicker – interactive inline calendar, no more β€œenter date in DD/MM/YYYY.”
  • Interceptor & Validator pipeline – plug checks or transformations before handlers.
  • Navigation between commands – move seamlessly through dialog stages.
  • Async first – fully asynchronous execution pipeline.
  • Fluent configuration via lambdas – ASP.NET-Core-style setup for familiarity.
  • Photo and Media support – receive, process, and send images out of the box.

πŸš€ Quick Start

1️⃣ Install

dotnet add package TeleFlow

2️⃣ Create your bot entry point


using demo.Services;
using TeleFlow.Factories;
using TeleFlow.Models;
using TeleFlow.Services;
using Telegram.Bot;
using TeleFlow.Services.Markup;
using TeleFlow.Services.Messaging;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;

string BOT_TOKEN = System.IO.File.ReadAllText("bot-token.txt");

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSingleton<IMessageServiceFactory<IMessageServiceWithEdit<Message>, Message>, UniversalMessageServiceFactory>();
builder.Services.AddSingleton<IMessageServiceFactory<string>, UniversalMessageServiceFactory>();
builder.Services.AddSingleton<IMessageServiceFactory<ImageMessageServiceMessage>, UniversalMessageServiceFactory>();



builder.Services.AddSingleton<IReplyMarkupManagerFactory, ReplyMarkupManagerFactory>();
builder.Services.AddSingleton<InlineMarkupManagerFactory, DemoInlineMarkupManagerFactory>();

builder.Services.AddSingleton<IMediaDownloaderServiceFactory, MediaDownloaderServiceFactory>();

builder.Services.AddSingleton<IUpdateDistributorArgsBuilder<UpdateDistributorNextHandlerBuildArgs>, UpdateDistributorArgsBuilder>();

builder.Services.AddSingleton<UpdateDistributorFactory<UpdateDistributorNextHandlerBuildArgs>, DemoUpdateDistributorFactory>();

builder.Services.AddHttpClient("tgwebhook")
                .RemoveAllLoggers()
                .AddTypedClient<ITelegramBotClient>(httpClient => new TelegramBotClient(BOT_TOKEN, httpClient));

builder.Services.ConfigureTelegramBotMvc();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();


3️⃣ Define your first command

using TeleFlow.Builders;
using TeleFlow.Factories;
using TeleFlow.Models;
using TeleFlow.Services;

namespace demo;

public class DemoUpdateDistributorFactory : UpdateDistributorFactory<UpdateDistributorNextHandlerBuildArgs>
{

    protected override void SetupUpdateListenerFactoryBuilder(UpdateListenerCommandFactoryBuilder<UpdateDistributorNextHandlerBuildArgs> builder)
    {
        builder.WithLambda("/start", (args) => {
            return new SendTextCommand(args.BuildTimeArgs.FromUpdateDistributorArgs.MessageServiceString, "Welcome to the bot!");
        });
    }

    public DemoUpdateDistributorFactory(IUpdateDistributorArgsBuilder<UpdateDistributorNextHandlerBuildArgs> updateDistributorArgsBuilder) : base(updateDistributorArgsBuilder)
    {
    }
}

🧩 Building Multi-Step Commands (FSM)

TeleFlow lets you model interactive dialogs as state machines:


public class DemoUpdateDistributorFactory : UpdateDistributorFactory<UpdateDistributorNextHandlerBuildArgs>
{

    protected override void SetupUpdateListenerFactoryBuilder(UpdateListenerCommandFactoryBuilder<UpdateDistributorNextHandlerBuildArgs> builder)
    {
        builder
        .WithMultiStep<DemoViewModel>("/multistep", options =>
        {
            options
            .SetDefaultStateValue(new())
            .WithValidation(options =>
            {
                options
                .WithStepWithValidationLambdaFactory((args, next) =>
                {
                    return new ContactShareStepCommand(args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.ReplyMarkupManager, (userInput) =>
                    {
                        args.State.CurrentValue.PhoneNumber = userInput.PhoneNumber;
                    }, "Please Share Your Phone. This will NOT go anywhere", "Share My Phone",
                    new PhoneNumberBelongsToUserValidator(args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.MessageServiceString, args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.ChatIdProvider, "The input was invalid", "The phone number does not belong to you."),
                    next);
                }, "Phone Number")
                .WithStepWithValidationLambdaFactoryGoBackButton((args, next, validator) =>
                {
                    return new TextInputStepCommand(args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.MessageServiceString, "Please enter your full name", async (message) =>
                    {
                        args.State.CurrentValue.UserFullName = message;
                    }, next, validator);
                }, "User Full Name")
                .WithStepWithValidationLambdaFactory((args, next) =>
                {
                    return new EnumValueSelectionStepCommand<DemoEnum>(args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.MessageService, "Please select one of the values",
                    async (userInput) =>
                    {
                        args.State.CurrentValue.LibraryRating = userInput;
                    },
                    (enumValue) =>
                    {
                        if (enumValue == DemoEnum.None)
                            return null;

                        return enumValue.ToString();
                    }, new(), next);
                }, "Library Rating")
                .WithStepWithValidationLambdaFactory((args, next) =>
                {
                    return new ListValueSelectionStepCommand<DemoListObject>(args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.MessageService, "Please select the value.",
                    async (userInput) =>
                    {
                        args.State.CurrentValue.ListObject = userInput;
                    },
                    new(3),
                    async () =>
                    {
                        return new List<DemoListObject>() { new() { DisplayName = "List Object 1", Value = "Value 1" }, new() { DisplayName = "List Object 2", Value = "Value 2" } };
                    },
                    (listObject) =>
                    {
                        return listObject.DisplayName;
                    }, next);
                }, "List Object Selection");
            })
            .WithLambdaResult(args =>
            {
                return new LambdaHandler<DemoViewModel>(async (vm) =>
                {
                    StringBuilder sb = new();

                    sb.AppendLine("You have successfully completed the multi step command. Here is the data you entered:");
                    sb.AppendLine($"Full Name: {vm.UserFullName}");
                    sb.AppendLine($"Library Rating: {vm.LibraryRating}");
                    sb.AppendLine($"List Object: {vm.ListObject.DisplayName} with value {vm.ListObject.Value}");
                    sb.AppendLine($"Phone: {vm.PhoneNumber}");

                    await args.BuildTimeArgs.FromUpdateDistributorArgs.MessageServiceString.SendMessage(sb.ToString());
                    await args.BuildTimeArgs.Navigator.Handle("/start");
                });
            });
        })
    }

    public DemoUpdateDistributorFactory(IUpdateDistributorArgsBuilder<UpdateDistributorNextHandlerBuildArgs> updateDistributorArgsBuilder) : base(updateDistributorArgsBuilder)
    {
    }
}


πŸ“… Using the DatePicker

Inline calendar selection is as simple as:


public class DemoUpdateDistributorFactory : UpdateDistributorFactory<UpdateDistributorNextHandlerBuildArgs>
{

    protected override void SetupUpdateListenerFactoryBuilder(UpdateListenerCommandFactoryBuilder<UpdateDistributorNextHandlerBuildArgs> builder)
    {
        builder
        .WithMultiStep<DemoViewModel>("/multistep", options =>
        {
            options
            .SetDefaultStateValue(new())
            .WithValidation(options =>
            {
                options
                .WithStepWithValidationLambdaFactory((args, next) =>
                {
                    return new DateSelectionStepCommand(next, args.UpdateListenerBuilderArgs.BuildTimeArgs.FromUpdateDistributorArgs.MessageService, (date) =>
                    {
                        args.State.CurrentValue.SelectedDate = date;
                    }, "Pick a date", new(DateOnly.FromDateTime(DateTime.Today).AddYears(-18), TeleFlow.Services.DateSelectionView.YearSelection), null, new(DateOnly.FromDateTime(DateTime.Today).AddYears(-14), "You are too young!"));
                }, "Date");
            })
            .WithLambdaResult(args =>
            {
                return new LambdaHandler<DemoViewModel>(async (vm) =>
                {
                    StringBuilder sb = new();

                    sb.AppendLine("You have successfully completed the multi step command. Here is the data you entered:");
                    sb.AppendLine($"Date: {vm.SelectedDate.ToShortDateString()}");

                    await args.BuildTimeArgs.FromUpdateDistributorArgs.MessageServiceString.SendMessage(sb.ToString());
                    await args.BuildTimeArgs.Navigator.Handle("/start");
                });
            });
        })
    }

    public DemoUpdateDistributorFactory(IUpdateDistributorArgsBuilder<UpdateDistributorNextHandlerBuildArgs> updateDistributorArgsBuilder) : base(updateDistributorArgsBuilder)
    {
    }
}


🧭 Navigation & Interceptors

Interceptors run before your handler executes β€” useful for auth, validation, or logging.


🧠 Concepts in TeleFlow

Concept Purpose
Command Handler Encapsulates single user action.
MultiStep Command Defines sequential interaction with user state.
Navigator Moves user between commands/states.
Interceptors / Validators Pre-processing logic.
DatePicker Inline UI component for dates.
Factory Builder Internal system creating command handlers dynamically.

alt text

🧰 Advanced Topics

  • Error Handling & Retry Policies
  • Dependency Injection
  • Custom Interceptors
  • Testing Command Flows

πŸ“¦ Roadmap

  • Inline Keyboards API V2
  • JSON-based Dialog Serialization
  • Redis state storage extension (TeleFlow.Redis)
  • Admin Panel Toolkit

πŸ’¬ Community


🧩 Contributing

  1. Fork the repo
  2. Create your feature branch (git checkout -b feature/foo)
  3. Commit changes (git commit -m 'Add foo')
  4. Push (git push origin feature/foo)
  5. Open a PR

🧠 License

MIT Β© 2025 β€” Created by Gleb Bannyy


πŸͺ„ One-Sentence Pitch (for NuGet and socials)

TeleFlow β€” Fluent and async Telegram bot framework for .NET with FSM, interceptors & built-in DatePicker.


βœ… Summary of what you need to add

Placeholder What to include
Program.cs snapshot Minimal bot initialization & configuration pipeline
StartCommand Simplest ICommandHandler example
MultiStep example Step-by-step FSM collecting data
DatePicker example Show inline calendar interaction
Interceptor example Auth or validation interceptor
Config snapshot Real lambda setup using your builder
Flow diagram (optional) How Update β†’ Factory β†’ Handler pipeline works
Custom Interceptor + DI Example of advanced extensibility
CONTRIBUTING excerpt How to run/test locally
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 was computed.  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 TeleFlow:

Package Downloads
TeleFlow.Polling

TeleFlow meta-package with polling hosting support.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 78 3/9/2026
2.0.0 91 2/24/2026
1.3.1 173 10/31/2025