在Microsoft Bot Framework v4中处理自适应卡-Node.js

时间:2019-01-12 01:25:11

标签: node.js botframework

  return new Promise((resolve, reject) => {
                            x = context.sendActivity({
                            text: 'hi',
                             attachments: [CardFactory.adaptiveCard(menuJson)]
                            })

我正在尝试发送自适应卡,其中包含一个Input.text字段...现在我的问题是如何使用上下文对象从程序中的用户获取输入数据?

即如何使用节点js处理bot框架v4中的自适应卡?

2 个答案:

答案 0 :(得分:3)

自适应卡发送的“提交”结果与常规用户文本略有不同。当用户键入聊天并发送普通消息时,它最终以context.activity.text结尾。当用户填写自适应卡上的输入时,它以context.activity.value结尾,id是一个对象,其中键名是menuJson中的{ "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" } ,而值是字段自适应卡中的值。

例如json:

context.activity

..创建一张看起来像这样的卡片:

Test Adaptive Card

如果用户在文本框中输入“ Testing Testing 123”,然后单击Submit,则{ 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

可以在context.activity.channelData中看到用户提交的内容。

请注意,自适应卡的提交是作为postBack发送的(在async displayCard(step) { // Display the Adaptive Card await step.context.sendActivity({ text: 'Adaptive Card', attachments: [yourAdaptiveCard], }); // Display a Text Prompt return await step.prompt('textPrompt', 'waiting for user input...'); } async handleResponse(step) { // Do something with step.result // Adaptive Card submissions are objects, so you likely need to JSON.parse(step.result) ... return await step.next(); 中可见),这意味着提交数据不会在对话的一部分中显示在聊天窗口中,而是保留在自适应卡上

将自适应卡与Waterfall Dialogs 一起使用

您的问题并不完全与此相关,但是由于您可能最终尝试这样做,因此我认为将其包含在答案中可能很重要。

从本质上讲,自适应卡不能像提示一样工作。带有提示,提示将显示并等待用户输入,然后继续。但是对于自适应卡(即使其中包含一个输入框和一个提交按钮),自适应卡中也没有代码,该代码将导致瀑布对话框在继续对话框之前先等待用户输入。

因此,如果您使用的是接受用户输入的自适应卡,则通常要处理用户在“瀑布对话框”上下文之外提交的所有内容。

话虽如此,如果您想在瀑布对话框中使用自适应卡,则有一种解决方法。基本上,您:

  1. 显示自适应卡
  2. 显示文字提示
  3. 将用户的自适应卡输入转换为文本提示输入

在“瀑布对话框”文件中(步骤1和2):

bot.ts

在您的// Ensure that message is a postBack (like a submission from Adaptive Cards if (dc.context.activity.type === 'message' && dc.context.activity.channelData.postback) { const activity = dc.context.activity; // Convert the user's Adaptive Card input into the input of a Text Prompt // Must be sent as a string activity.text = JSON.stringify(activity.value); dc.context.sendActivity(activity); 文件中(第3步):

ISO-BMFF container metadata for video frame indicates that the frame is not a keyframe, but the video frame contents indicate the opposite.

答案 1 :(得分:0)

我在带有WaterfallDialog的ComponentDialog中使用自适应卡,我想处理Input.submit操作。

我的问题是:如何处理响应,获取输入值并正确进入下一步对话框?

我尝试两种方法来解决我的问题。

我的自适应卡的json如下:

{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "TextBlock",
            "text": "Create Schedule",
            "size": "large",
            "weight": "bolder"
        },
        {
            "type": "TextBlock",
            "text": "Name",
            "size": "small"
        },
        {
            "type": "Input.Text",
            "id": "name"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Save",
            "data": {
                "result": "save"
            }
        },
        {
            "type": "Action.Submit",
            "title": "Cancel",
            "data": {
                "result": "cancel"
            }
        }
    ] 
}

1。使用提示并提示验证

这种方式使用提示验证功能来处理Input.submit回发操作。

因为回发操作不会发送文本消息(不在频道中显示),这使TextPrompt的默认验证无法通过(发送retryPrompt),所以我编写了一个提示验证函数,并将响应验证为回发操作。

class MyDialog extends ComponentDialog{
    constructor(dialogId) {
        // ...
        this.addDialog(new TextPrompt('textPropmt', this.promptValidator);
        this.addDialog(new WaterfallDailog(dialogId, [
            // dialog steps
            async function(step){
                await step.context.sendActivity({
                    attachments: [CardFactory.adaptiveCard(FormCard)]
                })
                await step.prompt('TextPrompt', 'waiting for your submit.')
            },

            async function(step){
                await step.context.sendActivity('get response.');

                // get adaptive card input value
                const resultValue = step.context.activity.value; 

                return await step.endDialog();
            }
        ]));
    }

    // prompt validate function
    async promptValidator(promptContext){
        const activity = promptContext.context.activity;
        return activity.type === 'message' && activity.channelData.postback;
    }

    // ..
}

2。使用Dialog.EndOfTurn

这种方式使用Dialog.EndOfTurn结束转弯。如果用户发送了任何响应,则该漫游器将转到下一个对话框步骤。

请记住检查响应是否为自适应卡提交操作(回发),否则请做一些拒绝或重试的操作。

class MyDialog extends ComponentDialog{
    constructor(dialogId) {
        // ...

        this.addDialog(new WaterfallDialog(dialogId, [
            // dialog steps
            async function(step) {
                await step.context.sendActivity({
                    attachments: [CardFactory.adaptiveCard(FormCard)]
                });

                return Dialog.EndOfTurn;
            },

            async function(step) {
                await step.context.sendActivity('get response.');
                const activity = step.context.activity;

                if (activity.channelData.postback) {
                    // get adaptive card input value
                    const resultValue = activity.value;
                } else {
                    await step.context.sendActivity("Sorry, I don't understand.");
                }

                return await step.endDialog();
            }
        ]));
    }
    // ...
}

最后,我会选择第二种方法(Dialog.EndOfTurn)解决该问题,因为我认为控制对话框步骤和处理用户中断比较容易,例如,当用户想要取消此操作并返回主对话框。