我已经使用C#中的MS Bot Framework SDK V4为Web频道开发了聊天Bot,该Bot具有多个瀑布对话框类,每个类均执行特定任务。在主根对话框中,我显示了一组选项1,2,3,4 ... 6。现在,当我选择一个选项5时,我将重定向到新的对话框类,其中
我有一个自适应卡,我设计了3组容器,其中一个通过文本框输入文本,第二个容器有一些复选框可供选择,第三个容器包含2个按钮“提交”和“取消”按钮。对于这些按钮,我分别将数据设置为Cancel = 0和1。 在此选项5对话框中,我基于数据cancel-0或1(如果为1)进行控制,我正在结束对话框并显示默认显示选项1,4 ... 6。 现在,我通过输入有效值单击“提交”按钮,并且该过程已成功完成,结果当前对话框已结束,并且再次显示了主要选项集。
在这里,我进行了某种否定性测试,其中我向上滚动并单击了上面显示的“取消”按钮。这导致在选项1到6的集合中显示的第一个选项(选项1)默认为y,即使我选择了cancel而不是第一个选项,该选项操作也会自动执行。但这在我向上滚动后选择自适应卡中显示的提交按钮时显示重试提示以选择以下任一选项,而当我单击“取消”时,默认情况下它将变为第一选项。
请在下面找到对话框相关和自适应卡相关的数据:
{
"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","wrap": true,"spacing": "None"
},{
"type": "Input.Text","placeholder": "Enter Requester Name","id": "RequesterName",{
"type": "TextBlock","text": "Requester Email* : ","id": "RequesterEmailLabel","spacing": "Small"
},"placeholder": "Enter Requester Email","id": "RequesterEmail","style": "Email","text": "Customer Name* : ","id": "CustomerNameLabel","placeholder": "Enter Customer Name","id": "CustomerName","text": "Select Request Type : ","id": "RequestTypeText","horizontalAlignment": "Left","size": "Medium",{
"type": "Input.ChoiceSet","placeholder": "--Select--","choices": [
{
"title": "Both","value": "Both"
},{
"title": "1","value": "1"
},{
"title": "2","value": "2"
}
],"id": "RequestType","value": "Both","spacing": "None"
}
],"style": "default","bleed": true,"id": "Requesterdata"
},"text": "Select Asset* :","id": "Assetheader","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","id": "Assetdata","bleed": true
},"items": [
{
"type": "actionSet","actions": [
{
"type": "action.Submit","title": "Cancel","id": "CanclBtn","style": "positive","data": {
"Cancel": 1
}
},{
"type": "action.Submit","title": "Submit","id": "SubmitBtn","data": {
"Cancel": 0
}
}
],"id": "action1","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();
}
});
请注意,我故意没有提供完整的代码,以便于理解和其他目的。
我保留取消按钮的主要想法是取消当前操作,以便用户可以转到主对话框选项,选择要执行的其他任务
查询为:
- 如果以上逻辑不正确,如何启用自适应卡中的取消按钮?
- 我们在自适应卡中可以有取消按钮吗?还是一个错误的假设,我们不能取消选择?
已于2019年11月8日更新
下面的更新是为了使我的查询更清楚和更好地理解:
1)通过Web通道启动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)由于显示了初始对话框options对话框,因此在用户的前端显示了以下提示选项:
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(),});
});
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的此过程中,发生或观察到了一些事情,以及在积极测试或消极测试中都发生了其他事情:
-
用户提供的自适应卡中的数据显示为Option5的一部分,然后单击“提交”按钮,用户将创建消息请求ID等,如上述代码所示,同时结束对话框和Option1的相同默认选项到6显示为defaultDisplayoptions对话框类的一部分
-
现在,用户再次向上滚动并单击“提交”按钮,但是正如我们观察到的那样,根据代码和显示的选项,用户位于defaultoptions对话框中
显示用户: 抱歉,我不明白。请从下面显示的选项中选择一个:
Option1 Option2 Option3 Option4 Option5 Option6
这正在按需且按预期工作,因此这里没有问题。
-
这是相同的情况,我曾经单击“提交”按钮
-
现在我上一次单击取消按钮,这次控件直接转到Displayoptions-> Option1,并且打印了该块中的语句
调试时,我注意到displayoptions对话框中的stepcontext已预先填充或预选为Option1的文本值或选项,而我没有选择该选项,因此它正在其下打印语句。
不确定其执行方式以及执行原因。因此,我以为自己可能以这种方式(我做的方式)包括了取消按钮(可能是错误的),因此我在本文中询问了查询如何在自适应卡中实现取消按钮功能。
但是,如果我做的是正确的方法,您能告诉我为什么这个问题是wrt only cancel button的地方,当控制权转到DiaplayOptions对话框时,选项1被预先选择了,为什么一切正常,wrt Submit按钮(在这种情况下,任何时候都没有问题)。
考虑到我更新的信息和查询,您能在问题上帮我吗?