Bot Builder SDK 4 — продолжение работы с диалогом до его явного завершения

Привет. После выхода Bot Builder SDK версии 4, мне несколько раз задали один интересный вопрос, а именно, при использовании диалогов, как сохранять незавершённых диалог до выполнения какого-то действия явного его завершения. Реализуется это всё не чуть не сложнее чем в третьей версии, единственное, в текущей документации это не до конца явно описано. Я заглянул в исходный код SDK и ниже опишу что нужно для корректной работы.

На самом деле, всё куда проще чем кажется. Но давайте начнём процесс с анализа. Если мы добавляем диалог, который, например, будет приводить весь текст в верхний регистр, то, вероятно первое, что мы напишем, будет выглядеть следующим образом:

public class ToUpper: IDialog
{
    public async Task DialogBegin(DialogContext dc, IDictionary<string, object> dialogArgs = null)
    {
        var message = dc.Context.Activity.Text;
        if (string.IsNullOrEmpty(message))
        {
            await dc.Context.SendActivity("Введите текст:");
            return;
        }

        await dc.Context.SendActivity(message.ToUpper());
        await dc.End();
    }
}

Метод, OnTurn чат-бота, при этом, будет выглядеть следующим образом:

public async Task OnTurn(ITurnContext context)
{

    var state = context.GetConversationState<Dictionary<string, object>>();
    var dialogCtx = _dialogs.CreateContext(context, state);

    if (context.Activity.Type == ActivityTypes.Message)
    {
        await dialogCtx.Continue();

        var message = context.Activity.Text.Trim();
        var indexOfSpace = message.IndexOf(" ", StringComparison.Ordinal);
        var command = indexOfSpace != -1 ? message.Substring(0, indexOfSpace).ToLower() : message.ToLower();

        switch (command)
        {
            case "toupper":

                context.Activity.Text = indexOfSpace >= 0
                    ? context.Activity.Text.Substring(indexOfSpace, message.Length - indexOfSpace)
                    : String.Empty;
                await dialogCtx.Begin("toUpper");
                break;
        }
    }
}

Работать подобный чат-бот будет следующим образом:

Вызов диалога в Bot Builder 4+

Если мы сразу передаём нужный текст, всё отлично отрабатывает. Но уже если текст не был передан, то обратно в данный диалог мы не попадаем. Чтобы понять почему это происходит, необходимо заглянуть внутрь метода Continue из этой строки:

await dialogCtx.Continue();

Он представляет из себя следующее:

public async Task Continue()
{
    // Check for a dialog on the stack
    if (ActiveDialog != null)
    {
        // Lookup dialog
        var dialog = Dialogs.Find(ActiveDialog.Id);
        if (dialog == null)
        {
            throw new Exception($"DialogSet.continue(): Can't continue dialog. A dialog with an id of '{ActiveDialog.Id}' wasn't found.");
        }

        // Check for existence of a continue() method
        if (dialog is IDialogContinue)
        {
                // Continue execution of dialog
                await ((IDialogContinue)dialog).DialogContinue(this);
        }
    }
}

Сразу становится понятно, что продолжение выполнения текущего диалога будет происходить в том случае, если он реализует интерфейс IDialogContinue. Интерфейс IDialogContinue, наследуется от IDialog и представляет из себя следующее:

    public interface IDialogContinue : IDialog
    {
        Task DialogContinue(DialogContext dc);
    }

Вернёмся к нашему чат-боту, диалог ToUpper наследуем от IDialogContinue и реализуем метод DialogContinue, весь класс будет представлять из себя следующее:

public class ToUpper: IDialogContinue
{
    public async Task DialogBegin(DialogContext dc, IDictionary<string, object> dialogArgs = null)
    {
        var message = dc.Context.Activity.Text;
        if (string.IsNullOrEmpty(message))
        {
            await dc.Context.SendActivity("Введите текст:");
            return;
        }

        await dc.Context.SendActivity(message.ToUpper());
        await dc.End();
    }

    public async Task DialogContinue(DialogContext dc)
    {
        await DialogBegin(dc);
    }
}

Запустим чат-бот и проверим как он работает:

Диалог, наследованный от IDialogContinue в Builder 4+

Всё отработало именно так, как и ожидалось. Завершение работы с чат-ботом происходит только в случае его явного завершения вызовом End или при явном переключении на другие диалоги.

Так что, в случае возникновения вопросов, не ленитесь заглянуть в исходный код проекта, ведь документация может быть не всегда актуальной, либо недостаточно явно описывать решения нужных проблем, а наличие репозитория с исходным кодом позволяет всегда получать актуальную и полную информацию о принципах работы проекта.

Приятного программирования.

Добавить комментарий