BotFramework.NET 2.1.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package BotFramework.NET --version 2.1.2
NuGet\Install-Package BotFramework.NET -Version 2.1.2
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="BotFramework.NET" Version="2.1.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BotFramework.NET --version 2.1.2
#r "nuget: BotFramework.NET, 2.1.2"
#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.
// Install BotFramework.NET as a Cake Addin
#addin nuget:?package=BotFramework.NET&version=2.1.2

// Install BotFramework.NET as a Cake Tool
#tool nuget:?package=BotFramework.NET&version=2.1.2

BotFramework

nuget downloads issues pull requests

Фреймворк для создания ботов под любую платформу на основе обработки запросов с помощью цепочки обязанностей

Использование

Все примеры ниже оформлены с использованием Telegram.Bot

Регистрация зависимостей

Перед началом работы зарегистрируйте зависимости и создайте ServiceProvider:

var services = new ServiceCollection();

/* Здесь Вы можете внедрить свои зависимости */

services.AddBotFramework()
        .AddHandler<ExceptionHandler>()
        .AddHandler<HelpCommand>()
        .AddHandler<SendCommand>()
        .AddHandler<MissingRequestHandler>();
       
var serviceProvider = services.BuildServiceProvider();

Метод AddBotFramework() добавляет основные классы фреймворка в контейнер зависимостей. AddHandler() регистрирует обработчик запроса указанного типа.

Конфигурация цепочки обработчиков

Как только все зависимости будут зарегистрированы, используйте BranchBuilder, чтобы сконфигурировать цепочку обработчиков:

var branchBuilder = new BranchBuilder(serviceProvider); // или var branchBuilder = serviceProvider.GetRequiredService<IBranchBuilder>();

branchBuilder.UseHandler<ExceptionHandler>()
             .UseCommand<HelpCommand>()
             .UseCommand<SendCommand>()
             .UseHandler<MissingRequestHandler>();

Метод UseHandler() добавляет обработчик в цепочку. UseCommand() добавляет команду в обработчик. По сути, команда и есть обработчик, а их отличие в том, что команда вызывается только при соблюдении определенных условий, описанных в реализации этой команды (например, запрос содержит текстовую команду). Также существует метод UseAnotherBranch(), которая конфигурирует вложенную цепочку обработчиков. Пример ниже конфигурирует цепочку обработчиков таким образом, чтобы при получении сообщения с текстовой командой и видео, запускался механизм проверки формата ролика в CheckVideoFormatHandler, а затем выполнений действий по обработке в ProcessVideoHandler:

branchBuilder.UseHandler<ExceptionHandler>()
             .UseAnotherBranch
             (
                 request => update.IsVideo() && update.IsCommand(), // Пользовательские методы
                 anotherBranchBuilder => 
                 {
                     anotherBranchBuilder.UseHandler<CheckVideoFormatHandler>()
                                         .UseHandler<ProcessVideoHandler>();
                 }
             )
             .UseCommand<HelpCommand>();

Метод Build() построит готовую цепочку в виде RequestDelegate. Достаточно вызывать этот делегат каждый раз, когда запрос для обработки будет готов будет готов. Например, получение обновления от Telegram:

var branch = branchBuilder.Build();
var request = GetLastUpdate(); // Пользовательский метод
await branch(request);

Создание обработчиков

Чтобы создать новый обработчик, реализуйте интерфейс IRequestHandler. Пример ниже уведомляет пользователя о том, что бот не может обработать запрос:

public class MissingUpdateHandler : IRequestHandler
{
    private readonly ILogger<MissingUpdateHandler> _logger;
    private readonly ITelegramBotClient _client;

    public MissingUpdateHandler(ILogger<MissingUpdateHandler> logger, ITelegramBotClient client)
    {
        _logger = logger;
        _client = client;
    }

    public async Task HandleAsync(object request, RequestDelegate nextHandler)
    {
        var update = request as Update;

        if (update is not null) 
        { 
            _logger.LogWarning($"No handler for request with type: {update.Type}");
            
            if (update.CallbackQuery is CallbackQuery callbackQuery)
            {
                await _client.AnswerCallbackQueryAsync
                (
                    callbackQuery.Id,
                    text: "Невозможно обработать Ваш запрос"
                );
            }
            else if (update.GetChatId() is long chatId && update.GetChatType() is not ChatType.Group) // Пользовательские методы
            {
                await _client.SendTextMessageAsync
                (
                    chatId,
                    text: "Некорректный запрос. Используйте /help для получения списка доступных команд"
                );
            }
        }
    }
}

Обратите внимание, что все обработчики поддерживают инъекцию зависимостей.

Создание команд

Чтобы создать новую команду, используйте интерфейс ICommandHandler. Пример ниже по команде "/help" или "/start" выводит пользователю подсказку по работе с ботом:

[CommandText("/help, /start")]
public class StartCommand : ICommandHandler
{
    private readonly ILogger<StartCommand> _logger;
    private readonly ITelegramBotClient _client;

    public StartCommand(ILogger<StartCommand> logger, ITelegramBotClient client)
    {
        _logger = logger;
        _client = client;
    }

    public async Task HandleAsync(object request, RequestDelegate nextHandler)
    {
        var stringBuilder = new StringBuilder();

        stringBuilder.AppendLine("<b>С помощью данного бота Вы можете: ...</b>");

        var update = request as Update;

        if (update is not null)
        { 
            var message = update.Message;
            var chatId = message.Chat.Id;

            await _client.SendChatActionAsync
            (
                chatId, 
                chatAction: ChatAction.Typing
            );

            await _client.SendTextMessageAsync
            (
                chatId,
                text: stringBuilder.ToString(),
                parseMode: ParseMode.Html,
                disableWebPagePreview: true
            );

            _logger?.LogInformation("Help/Start command processed");
        }
    }

    public bool CanHandle(IServiceProvider serviceProvider, object request)
    {
        var botInfo = _client.GetMeAsync()
                             .GetAwaiter()
                             .GetResult();

        return request is Update update &&
               update.IsCommand() &&
               update.Message is Message message &&
               message.IsContainsBotMention(botInfo) &&
               this.TextIsCommandAlias(message.Text);
    }
}

Атрибут CommandText позволяет через запятую определить на какие текстовые команды будет реагировать данный обработчик. Метод CanHandle() определяет при каких условиях команда может быть выполнена. В примере выше, условие выполнится, если сообщение включает в себя упоминание бота и является текстовой командой, указанной в атрибуте CommandText. Команды также, как и обычные обработчики, поддерживают инъекцию зависимостей. В дополнение к классическому DI, CanHandle() содержит в качестве параметра IServiceProvider.

Создание пошаговых обработчиков

Начиная с v2.1.1 Вы можете конфигурировать пошаговые обработчики, используя метод UseStepsFor() из интерфейса IBranchBuilder:

var branchBuilder = new BranchBuilder(serviceProvider); // или var branchBuilder = serviceProvider.GetRequiredService<IBranchBuilder>();

branchBuilder.UseHandler<ExceptionHandler>()
             .UseStepsFor<Command>(stepsBuilder => 
             {
                stepsBuilder.UseStepHandler<AskFirstnameHandler>()
                            .UseStepHandler<AskLastnameHandler>();
             });

Пошаговые обработчики можно определить только для команд. Сами по себе пошаговые обработчики являются командами, но без ссылки на следующий обработчик в цепочке. Каждый пошаговый обработчик позволяет получить запрос из предыдущего шага и ответ на него из текущего шага. Определить пошаговый обработчик можно используя абстрактный класс StepHandlerBase<TPreviousRequest, TCurrentRequest>:

internal class AskLastnameHandler : StepHandlerBase<string, string>
{
    public override Task HandleAsync(string previousRequest, string currentRequest)
    {
        Console.WriteLine($"Имя: {previousRequest}");
        Console.WriteLine($"Фамилия: {currentRequest}");
            
        return Task.CompletedTask;
    }
}

Создание бота

<details> <summary>До v1.0.2</summary>

BotFramework имеет абстракцию BotBase для того, чтобы изолировать вызовы RequestDelegate в одном месте. Это вовсе необязательно и вы вольны реализовывать вызовы Вашей цепочки так, как Вам хочется. Пример ниже показывает, как LongPolling-сервис вызывает RequestDelegate с именем rootHandler при получении новых обновлений от Telegram:

public class TelegramBot : BotBase
{
    private readonly ILogger _logger;
    private readonly ILongPollingService _longPollingService;
    private readonly CancellationTokenSource _cancellationTokenSource;

    public TelegramBot(RequestDelegate rootHandler, ILogger<TelegramBot> logger, 
        ILongPollingService longPollingService) : base(rootHandler)
    {
        _logger = logger;
        _longPollingService = longPollingService;

        _cancellationTokenSource = new CancellationTokenSource();
    }

    public override void Run()
    {
        _longPollingService.Receive
        (
            _rootHandler,
            _cancellationTokenSource.Token
        );

        _logger?.LogInformation("Bot is running...");
    }

    public override void Stop()
    {
        _cancellationTokenSource.Cancel();

        _logger?.LogInformation("Bot stopped");
    }
}

</details>

<details> <summary> Начиная с v2.1.1</summary>

Абстракция BotBase вырезана и больше не используется

</details>

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
5.1.0 354 11/9/2022
5.0.0 336 11/4/2022
4.0.0 440 7/12/2022
3.1.0 437 7/11/2022
3.0.0 441 7/2/2022
2.1.8 422 4/24/2022
2.1.7 296 11/22/2021
2.1.6 364 11/22/2021
2.1.5 575 11/21/2021
2.1.4 1,175 11/20/2021
2.1.3 360 11/19/2021
2.1.2 312 11/18/2021
2.1.1 445 11/17/2021
1.0.2 353 10/24/2021
1.0.1 405 10/24/2021