如何在后续瀑布步骤中检索自适应卡的表单提交

Posted

技术标签:

【中文标题】如何在后续瀑布步骤中检索自适应卡的表单提交【英文标题】:How to retrieve Adaptive Card's form submission in subsequent waterfall step 【发布时间】:2019-07-30 08:59:25 【问题描述】:

我正在使用 Bot Framework (V4),我有一个包含两个步骤的 WaterfallDialog;第一步是捕获表单数据,第二步是处理表单数据。

第一步发送回复:

private async Task<DialogTurnResult> CaptureFormStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)

    var reply = await stepContext.Context.Activity.GetReplyFromCardAsync("BusinessyForm");
    await stepContext.Context.SendActivityAsync(reply, cancellationToken);

GetReplyFromCardAsync 是一种扩展方法,它采用带有自适应卡作为附件的Activity 的 JSON 表示。自适应卡是一种类似于this 的形式。自适应卡片还有一个提交操作。

我的问题是如何将这些信息传递到下一步?

在之前的尝试中,我返回了一个提示:

return await stepContext.PromptAsync(
    "custom-prompt-id", 
    new PromptOptions  Prompt = MessageFactory.Text("Hello!") , 
    cancellationToken);

但在这种情况下,我不想提示用户提供其他信息。理想情况下,我想“保持”响应的对话状态,当检测到一个响应时,下一步将处理提交结果。

我尝试过的事情:

查看示例here 和here。两者都展示了如何呈现卡片,但没有展示如何处理后续步骤中的输入。 查看了输入 here 的卡片架构文档。 在 GitHub 的 AdaptiveCard 存储库 here 中审查了类似问题的讨论。

这可行吗?任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

自适应卡片发送的提交结果与常规用户文本略有不同。当用户在聊天中键入并发送正常消息时,它会以Context.Activity.Text 结束。当用户在自适应卡上填写输入时,它会以Context.Activity.Value 结尾,这是一个对象,其中键名是您卡中的id,值是自适应卡中的字段值。

例如json:


    "type": "AdaptiveCard",
    "body": [
        
            "type": "TextBlock",
            "text": "Test Adaptive Card"
        ,
        
            "type": "ColumnSet",
            "columns": [
                
                    "type": "Column",
                    "items": [
                        
                            "type": "TextBlock",
                            "text": "Text:"
                        
                    ],
                    "width": 20
                ,
                
                    "type": "Column",
                    "items": [
                        
                            "type": "Input.Text",
                            "id": "userText",
                            "placeholder": "Enter Some Text"
                        
                    ],
                    "width": 80
                
            ]
        
    ],
    "actions": [
        
            "type": "Action.Submit",
            "title": "Submit"
        
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0"

.. 创建一张如下所示的卡片:

如果用户在文本框中输入“Testing Testing 123”并点击提交,Context.Activity 将类似于:

 type: 'message',
  value:  userText: 'Testing Testing 123' ,
  from:  id: 'xxxxxxxx-05d4-478a-9daa-9b18c79bb66b', name: 'User' ,
  locale: '',
  channelData:  postback: true ,
  channelId: 'emulator',
  conversation:  id: 'xxxxxxxx-182b-11e9-be61-091ac0e3a4ac|livechat' ,
  id: 'xxxxxxxx-182b-11e9-ad8e-63b45e3ebfa7',
  localTimestamp: 2019-01-14T18:39:21.000Z,
  recipient:  id: '1', name: 'Bot', role: 'bot' ,
  timestamp: 2019-01-14T18:39:21.773Z,
  serviceUrl: 'http://localhost:58453' 

用户提交可见Context.Activity.Value.userText

请注意,自适应卡片提交是作为 postBack 发送的,这意味着提交数据不会作为对话的一部分出现在聊天窗口中,而是保留在自适应卡片上。

通过 Waterfall Dialogs 使用自适应卡片

在本机上,自适应卡片不像提示那样工作。出现提示时,提示将显示并等待用户输入,然后再继续。但是使用自适应卡片(即使它包含一个输入框和一个提交按钮),自适应卡片中没有任何代码会导致瀑布对话框在继续对话框之前等待用户输入。

因此,如果您使用的是接受用户输入的自适应卡片,您通常希望处理用户在瀑布对话框上下文之外提交的任何内容。

话虽如此,如果您想将自适应卡片用作瀑布对话框的一部分,则有一种解决方法。基本上,你:

    显示自适应卡片 显示文本提示 将用户的自适应卡片输入转换为文本提示的输入

在您的瀑布对话框类中(步骤 1 和 2):

private async Task<DialogTurnResult> DisplayCardAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)

    // Create the Adaptive Card
    var cardPath = Path.Combine(".", "AdaptiveCard.json");
    var cardJson = File.ReadAllText(cardPath);
    var cardAttachment = new Attachment()
    
        ContentType = "application/vnd.microsoft.card.adaptive",
        Content = JsonConvert.DeserializeObject(cardJson),
    ;

    // Create the text prompt
    var opts = new PromptOptions
    
        Prompt = new Activity
        
            Attachments = new List<Attachment>()  cardAttachment ,
            Type = ActivityTypes.Message,
            Text = "waiting for user input...", // 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);


private async Task<DialogTurnResult> HandleResponseAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)

    // Do something with step.result
    // Adaptive Card submissions are objects, so you likely need to JObject.Parse(step.result)
    await stepContext.Context.SendActivityAsync($"INPUT: stepContext.Result");
    return await stepContext.NextAsync();

在您的主机器人类 (&lt;your-bot&gt;.cs) 中,在 OnTurnAsync() 下,靠近方法的开头,在调用 await dialogContext.ContinueDialogAsync(cancellationToken) 之前的某个位置(第 3 步):

var activity = turnContext.Activity;

if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)

    activity.Text = JsonConvert.SerializeObject(activity.Value);

【讨论】:

谢谢,我会试一试,让你知道我的进展情况。 嗨@mdrichardson,为延迟道歉。我将能够测试这个明天,然后会更新你。 完美!花了一段时间,因为我在我的 await dialogContext.ContinueDialogAsync(cancellationToken) 之后错误地添加了您的第 3 步更新,它跳过了处理响应的步骤。 很高兴你能成功!我将编辑我的答案以显示更好的位置。 谢谢@mdrichardson。这种方法对我们有用。希望以后能添加自适应卡片提示对话框。【参考方案2】:

这里是nodejs版本:

你的 bot.ts 文件:

 this.onTurn(async (context, next) => 
  if (context.activity.type == ActivityTypes.Message) 
    // Ensure that message is a postBack (like a submission from Adaptive Cards)
    if (context.activity.channelData != null) 
      if (context.activity.channelData.postBack === true) 
        const postbackActivity = context.activity;
        // Convert the user's Adaptive Card input into the input of a Text Prompt
        // Must be sent as a string
        postbackActivity.text = JSON.stringify(postbackActivity.value);
        // context.activity.text = postbackActivity.value
        await context.sendActivity(postbackActivity);
      
    
  
  await next();
);

【讨论】:

以上是关于如何在后续瀑布步骤中检索自适应卡的表单提交的主要内容,如果未能解决你的问题,请参考以下文章

[BotFramework]:如何在瀑布对话框中捕获/提取通过 C# 网络聊天机器人中呈现的自适应卡片提交的值?

如何将从表单提交中检索到的参数作为参数传递给 django 中的函数

织梦做的网站 如何调用自定义表单的提交数量?

第一个表单提交插入行后续提交更新同一行

通过表单提交创建 JSON 文件

使用 curl 提交/检索表单结果