【问题标题】:Handling Adaptive cards in Microsoft Bot Framework v4 - Nodejs在 Microsoft Bot Framework v4 中处理自适应卡片 - Nodejs
【发布时间】:2019-10-31 06:02:24
【问题描述】:
  return new Promise((resolve, reject) => {
                            x = context.sendActivity({
                            text: 'hi',
                             attachments: [CardFactory.adaptiveCard(menuJson)]
                            })

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

即如何使用 node js 在 bot framework v4 中处理自适应卡片?

【问题讨论】:

  • 为了澄清,“作为上下文对象”是什么意思?您希望如何使用用户输入?
  • 我不确定是否是您支持我的回答,但如果这有帮助,请随时将其标记为“已回答”,我可以将其从我的跟踪器中清除。谢谢!

标签: node.js botframework


【解决方案1】:

自适应卡片发送的提交结果与常规用户文本略有不同。当用户在聊天中键入并发送正常消息时,它会以context.activity.text 结束。当用户在自适应卡上填写输入时,它会以context.activity.value 结尾,这是一个对象,其中键名是menuJson 中的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. 显示文本提示
  3. 将用户的自适应卡片输入转换为文本提示的输入

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

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();

在您的 bot.ts 文件中(第 3 步):

const activity = dc.context.activity;

if (!activity.text && activity.value) {
    activity.text = JSON.stringify(activity.value);
}

【讨论】:

  • 常量活动 = dc.context.activity; - 什么是直流?对我来说,如果: const activity = context.activity;
【解决方案2】:

我在带有 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.使用 prompt 和 prompt validate

这种方式使用提示验证功能来处理 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)来解决问题,因为我认为更容易控制对话步骤和处理用户中断,例如,当用户想取消这个动作时,返回主对话框。

【讨论】:

  • 我真的很喜欢你的第二种方式!感谢分享!一个小注释是postback 应该是这样的驼峰式:if (activity.channelData.postBack) ...
猜你喜欢
  • 2020-10-21
  • 2022-01-08
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
  • 2020-05-30
  • 2019-10-11
  • 1970-01-01
相关资源
最近更新 更多