Как сделать так, чтобы кнопка отмены в адаптивных картах отображалась в чате чата веб-канала, разработанном в MS Bot framework SDK V4 на C #? - PullRequest
0 голосов
/ 07 ноября 2019

У меня есть чат Bot, разработанный для веб-канала с использованием MS Bot Framework SDK V4 на C #, в котором есть несколько диалоговых классов, каждый из которых выполняет определенные задачи. В главном корневом диалоге у меня есть набор параметров, отображаемых Вариант 1,2,3,4 ... 6. Теперь, когда я выбираю опцию 5, я перенаправляюсь в новый класс диалога, где

У меня есть адаптивная карта, которую я спроектировал с 3 наборами контейнеров, один принимает входной текст через текстовые поля, а второй контейнер имеет несколько флажков длябыть выбранным и третий контейнер содержит 2 кнопки отправки и отмены. Для этих кнопок я поставил данные как Cancel = 0 и 1 соответственно. В этом диалоге опций 5 я управляю на основе данных отмены-0 или 1, если это 1, я делаю диалог завершения и показываю опции отображения по умолчанию 1,2,3,4 ... 6. Теперь я нажал на кнопку «Отправить», введя правильные значения, и процесс был успешно завершен, в результате текущий диалог завершился, и снова отображается основной набор параметров.

Здесь я провел какое-то отрицательное тестированиегде я прокрутил вверх и нажал кнопку отмены, которая отображалась выше. В результате первая опция (опция 1), отображаемая в наборе опций от 1 до 6, была выбрана по умолчанию, и операции с опциями выполнялись автоматически, даже если я выбрал отмену, а не первую опцию. Но этого не происходит, когда я выбираю кнопку отправки, отображаемую в адаптивной карточке, после прокрутки вверх отображается запрос на повторную попытку, чтобы выбрать любую из следующих опций, где, например, когда я нажимаю кнопку «Отмена», она по умолчанию переходит к 1-й опции.

Ниже приведены данные, относящиеся к диалогу и адаптивной карте:

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "text": "Request For Model/License",
            "horizontalAlignment": "Center",
            "color": "Accent",
            "id": "RequestforModel/License",
            "spacing": "None",
            "wrap": true
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "Requester Name* : ",
                    "id": "RequesterNameLabel",
                    "weight": "Bolder",
                    "wrap": true,
                    "spacing": "None"
                },
                {
                    "type": "Input.Text",
                    "placeholder": "Enter Requester Name",
                    "id": "RequesterName",
                    "spacing": "None"
                },
                {
                    "type": "TextBlock",
                    "text": "Requester Email* : ",
                    "id": "RequesterEmailLabel",
                    "weight": "Bolder",
                    "wrap": true,
                    "spacing": "Small"
                },
                {
                    "type": "Input.Text",
                    "placeholder": "Enter Requester Email",
                    "id": "RequesterEmail",
                    "style": "Email",
                    "spacing": "None"
                },
                {
                    "type": "TextBlock",
                    "text": "Customer Name* : ",
                    "id": "CustomerNameLabel",
                    "weight": "Bolder",
                    "wrap": true,
                    "spacing": "Small"
                },
                {
                    "type": "Input.Text",
                    "placeholder": "Enter Customer Name",
                    "id": "CustomerName",
                    "spacing": "None"
                },
                {
                    "type": "TextBlock",
                    "text": "Select Request Type : ",
                    "id": "RequestTypeText",
                    "horizontalAlignment": "Left",
                    "wrap": true,
                    "weight": "Bolder",
                    "size": "Medium",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "placeholder": "--Select--",
                    "choices": [
                        {
                            "title": "Both",
                            "value": "Both"
                        },
                        {
                            "title": "1",
                            "value": "1"
                        },
                        {
                            "title": "2",
                            "value": "2"
                        }
                    ],
                    "id": "RequestType",
                    "value": "Both",
                    "spacing": "None"
                }
            ],
            "horizontalAlignment": "Left",
            "style": "default",
            "bleed": true,
            "id": "Requesterdata"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "Select Asset* :",
                    "id": "Assetheader",
                    "horizontalAlignment": "Left",
                    "wrap": true,
                    "weight": "Bolder",
                    "size": "Medium",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "placeholder": "",
                    "choices": [
                        {
                            "title": "chekcbox1",
                            "value": "chekcbox1"
                        },
                        {
                            "title": "chekcbox2",
                            "value": "chekcbox2"
                        },
                        {
                            "title": "chekcbox3",
                            "value": "chekcbox3"
                        },
                        {
                            "title": "chekcbox4",
                            "value": "chekcbox4"
                        },
                        {
                            "title": "chekcbox5",
                            "value": "chekcbox5"
                        }
                    ],
                    "isMultiSelect": true,
                    "id": "AssetsList",
                    "wrap": true,
                    "spacing": "None"
                }
            ],
            "id": "Assetdata",
            "style": "default",
            "horizontalAlignment": "Left",
            "bleed": true
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "ActionSet",
                    "actions": [
                        {
                            "type": "Action.Submit",
                            "title": "Cancel",
                            "id": "CanclBtn",
                            "style": "positive",
                            "data": {
                                "Cancel": 1
                            }
                        },
                        {
                            "type": "Action.Submit",
                            "title": "Submit",
                            "id": "SubmitBtn",
                            "style": "positive",
                            "data": {
                                "Cancel": 0
                            }
                        }
                    ],
                    "id": "Action1",
                    "horizontalAlignment": "Center",
                    "spacing": "Small",
                    "separator": true
                }
            ]
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0",
    "id": "ModelLicenseRequestForm",
    "lang": "Eng"
}

Код ниже основного корневого диалога:

AddStep(async (stepContext, cancellationToken) =>
{
    return await stepContext.PromptAsync(
        "choicePrompt",
        new PromptOptions
        {
            Prompt = stepContext.Context.Activity.CreateReply("Based on the access privileges assigned to you by your admin, below are the options you can avail. Please click/choose any one from the following: "),
            Choices = new[] { new Choice { Value = "option1" }, new Choice { Value = "option2" }, new Choice { Value = "option3" }, new Choice { Value = "option4" }, new Choice { Value = "option5" }, new Choice { Value = "option6" } }.ToList(),
            RetryPrompt = stepContext.Context.Activity.CreateReply("Sorry, I did not understand that. Please choose any one from the options displayed below: "),
        });
});

AddStep(async (stepContext, cancellationToken) =>
{
    if (response == "option1")
    {
        doing something
    }

    if (response == "option2")
    {
        return await stepContext.BeginDialogAsync(option2.Id, cancellationToken: cancellationToken);
    }

    if (response == "option3")
    {
        return await stepContext.BeginDialogAsync(option3.Id, cancellationToken: cancellationToken);
    }

    if (response == "option4")
    {
        return await stepContext.BeginDialogAsync(option4.Id, cancellationToken: cancellationToken);
    }

    if (response == "option5")
    {
        return await stepContext.BeginDialogAsync(option5.Id, cancellationToken: cancellationToken);
    }

    if (response == "option6")
    {
        return await stepContext.BeginDialogAsync(option6.Id, cancellationToken: cancellationToken);
    }

    return await stepContext.NextAsync();
});

опция5 код класса диалога:

AddStep(async (stepContext, cancellationToken) =>
{
    var cardAttachment = CreateAdaptiveCardAttachment("Adaptivecard.json");

    var reply = stepContext.Context.Activity.CreateReply();
    reply.Attachments = new List<Microsoft.Bot.Schema.Attachment>() { cardAttachment };

    await stepContext.Context.SendActivityAsync(reply, cancellationToken);
    var opts = new PromptOptions
    {
        Prompt = new Activity
        {
            Type = ActivityTypes.Message,
            // You can comment this out if you don't want to display any text. Still works.
        }
    };

    // Display a Text Prompt and wait for input
    return await stepContext.PromptAsync(nameof(TextPrompt), opts);
});

AddStep(async (stepContext, cancellationToken) =>
{
    var res = stepContext.Result.ToString();

    dynamic modelrequestdata = JsonConvert.DeserializeObject(res);

    string canceloptionvalidaiton = modelrequestdata.Cancel;
    if (canceloptionvalidaiton == "0")
    {
        // ...perform operation
        return await stepContext.EndDialogAsync();
    }
    else
    {
        return await stepContext.EndDialogAsync();
    }
});

Обратите внимание, я специально не предоставил весь код для простоты понимания и других целей.

Основная идея для меня, чтобы отменитьКнопка предназначена для отмены текущей операции, чтобы пользователь мог перейти к основным параметрам диалогового окна, выбрать любую другую задачу для выполнения

Запрос:

  1. Как включить кнопку отмены в адаптивной карте, еслимоя логика выше неверна?
  2. можем ли мы иметь кнопку отмены в адаптивной карте? или это неверное предположение, и у нас не может быть опции отмены?

Обновлено 8 ноября 2009 г.

Данное обновление предназначено для ясного и лучшего пониманиямой запрос:

1) При запуске BOT через веб-канал запускается главный корень диалога, который содержит все диалоги и все, что добавлено в стек:

Ниже находится основной коренькод класса диалога:

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace EchoBot.Dialogs
{
    public class MainRootDialog : ComponentDialog
    {
        public MainRootDialog(UserState userState)
            : base("root")
        {
            _userStateAccessor = userState.CreateProperty<JObject>("result");

            AddDialog(DisplayOptionsDialog.Instance);
            AddDialog(Option1.Instance);
            AddDialog(Option2.Instance);
            AddDialog(Option3.Instance);
            AddDialog(Option4.Instance);
            AddDialog(Option5.Instance);
            AddDialog(Option6.Instance);          
            AddDialog(new ChoicePrompt("choicePrompt"));
            InitialDialogId = DisplayOptionsDialog.Id;
        }
    }
}

2) Так как в диалоговом окне отображается исходный диалог для пользователя, в результате отображается начальный диалог параметров:

Option1 Option2 Option3 Option4 Option5 Option6

Этого я добился с помощью следующего кода, который я написал в классе с именем DisplayOptionsDialog:

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace EchoBot.Dialogs
{
    public class DisplayOptionsDialog : WaterfallDialog
    {
        public DisplayOptionsDialog(string dialogId, IEnumerable<WaterfallStep> steps = null)
            : base(dialogId, steps)
        {
            AddStep(async (stepContext, cancellationToken) =>
            {               

                    return await stepContext.PromptAsync(
                        "choicePrompt",
                        new PromptOptions
                        {
                            Prompt = stepContext.Context.Activity.CreateReply("Below are the options you can avail. Please click/choose any one from the following: "),
                            Choices = new[] { new Choice { Value = "Option1" }, new Choice { Value = "Option2" }, new Choice { Value = "Option3" }, new Choice { Value = "Option4" }, new Choice { Value = "Option5" }, new Choice { Value = "Option6" }}.ToList(),
                            RetryPrompt = stepContext.Context.Activity.CreateReply("Sorry, I did not understand that. Please choose any one from the options displayed below: "),
                        });

            });

            AddStep(async (stepContext, cancellationToken) =>
            {
                var response = (stepContext.Result as FoundChoice)?.Value;

                if (response == "Option1")
                {
                    await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Otpion 1 selected")); //Here there is lot of actual data printing that i am doing but due //to some sensitive inoformation i have kept a simple statment that gets //displayed but in actual code it is just printing back or responding back few //statements which again printing only                                             
                }

                if (response == "Option2")
                {
                    return await stepContext.BeginDialogAsync(Option2.Id, cancellationToken: cancellationToken);
                }

                if (response == "Option3")
                {
                    return await stepContext.BeginDialogAsync(Option3.Id, cancellationToken: cancellationToken);
                }

                if (response == "Option4")
                {
                    return await stepContext.BeginDialogAsync(Option4.Id, cancellationToken: cancellationToken);
                }

                if (response == "Option5")
                {
                    return await stepContext.BeginDialogAsync(Option5.Id, cancellationToken: cancellationToken);
                }

                if (response == "Option6")
                {
                    return await stepContext.BeginDialogAsync(Option6.Id, cancellationToken: cancellationToken);
                }               

                return await stepContext.NextAsync();
            });

            AddStep(async (stepContext, cancellationToken) => 
            {
                return await stepContext.ReplaceDialogAsync(Id);
            });

        }

        public static new string Id => "DisplayOptionsDialog";

        public static DisplayOptionsDialog Instance { get; } = new DisplayOptionsDialog(Id);
    }
}

3) С тех пор, как пользователь выбрал Option5, я сразу перейду к коду класса диалога option5:

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace EchoBot.Dialogs
{
    public class Option5Dialog : WaterfallDialog
    {
        public const string cards = @"./ModelAdaptivecard.json";
        public Option5Dialog(string dialogId, IEnumerable<WaterfallStep> steps = null)
           : base(dialogId, steps)
        {

            AddStep(async (stepContext, cancellationToken) =>
           {
               var cardAttachment = CreateAdaptiveCardAttachment(cards);

               var reply = stepContext.Context.Activity.CreateReply();
               reply.Attachments = new List<Microsoft.Bot.Schema.Attachment>() { cardAttachment };

               await stepContext.Context.SendActivityAsync(reply, cancellationToken);
               var opts = new PromptOptions
               {
                   Prompt = new Activity
                   {
                       Type = ActivityTypes.Message,
                       // You can comment this out if you don't want to display any text. Still works.
                   }
               };

               // Display a Text Prompt and wait for input
               return await stepContext.PromptAsync(nameof(TextPrompt), opts);
           });

            AddStep(async (stepContext, cancellationToken) =>
            {               
                var activityTextformat = stepContext.Context.Activity.TextFormat;


                if (activityTextformat == "plain")
                {
                    await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Sorry, i did not understand that please enter proper details in below displayed form and click on submit button for processing your request"));
                    return await stepContext.ReplaceDialogAsync(Id, cancellationToken: cancellationToken);
                }

                else
                {
                    var res = stepContext.Result.ToString();

                    dynamic modelrequestdata = JsonConvert.DeserializeObject(res);

                    string canceloptionvalidaiton = modelrequestdata.Cancel;

                    if (canceloptionvalidaiton == "0")
                    {
                        string ServiceRequesterName = modelrequestdata.RequesterName;
                        string ServiceRequesterEmail = modelrequestdata.RequesterEmail;
                        string ServiceRequestCustomerName = modelrequestdata.CustomerName;
                        string ServiceRequestType = modelrequestdata.RequestType;
                        string ServiceRequestAssetNames = modelrequestdata.AssetsList;


                        //checking wehther data is provided or not
                        if (string.IsNullOrWhiteSpace(ServiceRequesterName) || string.IsNullOrWhiteSpace(ServiceRequesterEmail) || string.IsNullOrWhiteSpace(ServiceRequestCustomerName) || string.IsNullOrWhiteSpace(ServiceRequestAssetNames))
                        {
                            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Mandatory fields such as Requester name,Requester Email,Cusomter Name or Asset details are not selected are not provided"));

                            return await stepContext.ReplaceDialogAsync(Id, cancellationToken: cancellationToken);
                        }
                        else
                        {
                            await stepContext.Context.SendActivityAsync(MessageFactory.Text("Data recorded successfully"));
                            await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank You!.Looking forward to see you again."));

                            return await stepContext.EndDialogAsync();
                        }
                    }
                    else
                    {
                        await stepContext.Context.SendActivityAsync(MessageFactory.Text("Looks like you have cancelled the Model/License request"));
                        await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank You!.Looking forward to see you again."));

                        return await stepContext.EndDialogAsync();
                    }
                }
            });
        }
        public static new string Id => "Option5Dialog";

        public static Option5Dialog Instance { get; } = new Option5Dialog(Id);

        public static Microsoft.Bot.Schema.Attachment CreateAdaptiveCardAttachment(string filePath)
        {
            var adaptiveCardJson = File.ReadAllText(filePath);
            var adaptiveCardAttachment = new Microsoft.Bot.Schema.Attachment()
            {
                ContentType = "application/vnd.microsoft.card.adaptive",
                Content = JsonConvert.DeserializeObject(adaptiveCardJson),
            };
            return adaptiveCardAttachment;
        }
    }
}

Вот что произошло или наблюдалось во время этого процесса варианта 5 и других вещей как в положительном, так и в отрицательном тестировании:

  1. Данные, предоставленные пользователем на адаптивной карточке, отображаются как часть Option5 и нажимаются на кнопку отправки. Пользователь получает идентификатор запроса сообщения и т. Д., Как показано в приведенном выше коде, в то же время диалоговое окно заканчивается и те же параметры по умолчаниюпараметров с 1 по 6 отображаются как часть класса диалога defaultDisplayoptions

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

отображается пользователь: Извините, я не понял этого. Пожалуйста, выберите любой из вариантов, показанных ниже:

Опция1 Опция2 Опция3 Опция4 Опция5 Опция6

Это работает по мере необходимости и, как ожидается, здесь никаких проблем.

Это тот же самый случай, сколько раз я нажимал на кнопку «Отправить»

Теперь я поднялся и нажал кнопку «Отмена», на этот раз элемент управления перешел к Displayoptions-> Option1 и оператор, присутствующий в этом блоке, напечатан

Когда я отлажен, я замечаю, что stepcontext в диалоге displayoptions имеет текстовое значение или выбор, предварительно заполненный или предварительно выбранный как Option1 безЯ выбираю эту опцию, в результате она печатает операторы под ней.

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

Однако, если то, что я сделал, является правильным способом, скажите, пожалуйста, почему проблема заключается только в кнопке отмены, когда, когда управление переходит в диалоговое окно DiaplayOptions, вариант 1 каким-то образом выбирается заранее, где все работает нормально с кнопкой отправки(в этом случае никаких проблем вообще нет).

Не могли бы вы помочь мне в решении этой проблемы, учитывая мою обновленную информацию и запрос?

1 Ответ

0 голосов
/ 13 ноября 2019

Я получил ваш код по электронной почте и сумел извлечь ответы на некоторые из моих вопросов.

Мы знаем, что вы должны манипулировать действием контекста поворота, прежде чем передавать его в диалоговое окно, илииначе ваши текстовые подсказки не могли работать с объектными действиями отправки.

Код, который я запрашивал, находится в вашем DialogExtensions.Run классе:

Activity activity = dialogContext.Context.Activity;
object rawChannelData = activity.ChannelData;

if (dialogContext.Context.Activity.Value != null && dialogContext.Context.Activity.Text == null)
{
    dialogContext.Context.Activity.Text = turnContext.Activity.Value.ToString();
}

Вы можете видеть, чтоэто плохое место, потому что вы, очевидно, забыли, что оно было даже там. Еще одна причина, по которой это нехорошо, заключается в том, что вы должны использовать вместо этого встроенный метод DialogExtensions.RunAsync.

Что происходит, так это то, что вы передаете сериализованный JSON из адаптивной картыотправить действие в любой активный диалог. Поэтому, если активное диалоговое окно является приглашением выбора, оно попытается интерпретировать этот сериализованный JSON как один из вариантов выбора. При нажатии кнопки отмены этот JSON будет содержать "Cancel": 1, а 1 заставит распознаватель думать, что вы хотите перейти с опцией 1.

Самым простым решением, конечно, является просто переделка вашей Adaptive Cardтак что он не будет содержать никаких чисел, но, конечно, это будет специальное исправление, которое может не сработать для всех ваших будущих сценариев.

Вы на самом деле не сказали, каково ваше ожидаемое / желаемое поведение,но я могу придумать два основных варианта:

  1. Вы хотите, чтобы действия отправки игнорировались, когда они нажимались за пределами этого приглашения
  2. Вы хотите, чтобы кнопка отмены отменяла любойдиалог независимо от того, какой диалог активен

По вашему коду я могу догадаться, что вы, вероятно, хотели, чтобы кнопки работали только в этом одном приглашении. Поскольку вы используете веб-чат, вы могли бы рассмотреть решение на стороне клиента, в котором вы бы создали собственный рендерер Adaptive Cards, позволяющий отключить отправку действий после использования карты. Я предполагаю, что решение сложнее, чем вы хотели бы, но есть также способы заставить бот игнорировать действия при определенных обстоятельствах. Вы можете посмотреть на приглашение адаптивной карты Майкла Ричардсона для некоторых идей, а также проголосовать за мой проект сообщества адаптивных карт .

Если вы хотите, чтобы кнопка отменыработать для любого диалога, просто убедитесь, что вы отвечаете на его действия, вызывая CancelAllDialogsAsync вместо ContinueDialogAsync.

Как генерируется «ответ» в вашем главном корневом диалоге

Это строка, которую я просил:

var response = (stepContext.Result as FoundChoice)?.Value;

Вы необъяснимым образом пропустили эту строку в своем "главном корневом диалоге", хотя я заметил, что строка была включена, когда выИзлишне вставил этот код под именем DisplayOptionsDialog. В будущем вы сможете получать более качественную помощь быстрее, если не пропустите важную информацию или, по крайней мере, предоставите ее по запросу.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...