简体   繁体   中英

What is the right way to retrieve data from Adaptive Card without displayed card again after submit?

I'm using Adaptive Card in my Bot with waterfall dialog. I want to retrieve the data that the user gave in the form and show it in chat in another adaptive card after the user click the submit button. But when I click submit button, empty card re-prompts again.

I have read this post Stackoverflow and trying solutions with postback channel data. And it only worked in the emulator, I can retrieve all data. But when I have deployed it to Azure and MsTeams channel, after each click it re-prompt again and again.

My OnTurnAsync method :

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {

            await base.OnTurnAsync(turnContext, cancellationToken);


            var activity = turnContext.Activity;

            if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
            {
                activity.Text = JsonConvert.SerializeObject(activity.Value);

            }

            await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
            await UserState.SaveChangesAsync(turnContext, false, cancellationToken);

        }

My DialogExtensions.cs I've taken the code from Gags08, and it's work perfectly on emulator, but not on teams :

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Newtonsoft.Json.Linq;

namespace Microsoft.BotBuilderSamples
{
    public static class DialogExtensions
    {
        public static async Task Run(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
        {

            var dialogSet = new DialogSet(accessor);
            dialogSet.Add(dialog);


            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
            // Ensure that message is a postBack (like a submission from Adaptive Cards)
            if (dialogContext.Context.Activity.GetType().GetProperty("ChannelData") != null)
            {
                var channelData = JObject.Parse(dialogContext.Context.Activity.ChannelData.ToString());
                if (channelData.ContainsKey("postBack"))
                {
                    var postbackActivity = dialogContext.Context.Activity;
                    // Convert the user's Adaptive Card input into the input of a Text Prompt
                    // Must be sent as a string
                    postbackActivity.Text = postbackActivity.Value.ToString();
             await dialogContext.Context.SendActivityAsync(postbackActivity);
                }
            }
            var results = await dialogContext.ContinueDialogAsync(cancellationToken);
            if (results.Status == DialogTurnStatus.Empty)
            {
                await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
            }
         }
    }

And my card :

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "Image",
            "style": "Person",
            "url": "data:image/png;
            "size": "Small",
            "id": "image",
            "horizontalAlignment": "Center"
        },
        {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": "FTP Creation Card",
            "id": "title",
            "horizontalAlignment": "Center"
        },
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "items": [
                        {
                            "type": "FactSet",
                            "facts": [
                                {
                                    "title": "Fill in all the fields",
                                    "value": "with customer data"
                                },
                                {
                                    "title": "Click Submit",
                                    "value": "and wait notification to your email"
                                }
                            ],
                            "id": "Exploration"
                        }
                    ],
                    "width": "stretch"
                }
            ]
        },
        {
            "type": "Input.Text",
            "placeholder": "First Name",
            "id": "Name"
        },
        {
            "type": "Input.Text",
            "placeholder": "Last Name",
            "id": "LastName"
        },
        {
            "type": "Input.Text",
            "placeholder": "Nickname",
            "id": "Login"
        },
        {
            "type": "Input.Text",
            "placeholder": "Customer Email address",
            "id": "Email"
        },
        {
            "type": "Input.Text",
            "placeholder": "Company Name",
            "id": "Company"
        },
        {
            "type": "Input.Text",
            "placeholder": "Manager",
            "id": "Manager"
        },
        {
            "type": "Input.Text",
            "placeholder": "Optional :  employees",
            "id": "InternalUsers"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Sumbit",

            "style": "positive",
            "id": "submit"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0"
}


I'm new to bots and maybe I'm wrong but it seems to me that the problem is in the message property "ChannelData". The postback key is available only in the emulator, so it does not intercept it through teams. I have connected to my Azure bot through emulator and it's work perfectly. Tell me how to work correctly with cards through Teams and is there any solution to this problem?

Your issue stems from a mostly-correct-at-the-time answer I wrote back in January that I have since edited but some people still use inadvertently occasionally. I've been playing whack-a-mole trying to edit it out of existence for awhile now.

Your OnTurnAsync method is good. You should delete this block of code from Run since it 1) basically tries to do the same thing as part of OnTurnAsync , and 2) is better to leave that functionality under OnTurnAsync (like you already have):

 
 
 
  
  if (dialogContext.Context.Activity.GetType().GetProperty("ChannelData") != null) { var channelData = JObject.Parse(dialogContext.Context.Activity.ChannelData.ToString()); if (channelData.ContainsKey("postBack")) { var postbackActivity = dialogContext.Context.Activity; // Convert the user's Adaptive Card input into the input of a Text Prompt // Must be sent as a string postbackActivity.Text = postbackActivity.Value.ToString(); await dialogContext.Context.SendActivityAsync(postbackActivity); } }
 
  

Basically, the reason this fails is because Emulator sends card input as a "postback", but other channels do not. However, ALL channels send it in Activity.Value while leaving Activity.Text blank, hence the code in OnTurnAsync . The reason that it's re-prompting is likely because in Run , you're telling it to send a message ( await dialogContext.Context.SendActivityAsync(postbackActivity); ), which messes up the flow of the dialog.

See this answer for additional context.

We also have a really good blog post about this .

Finally, I have modified DialogExtensions.cs for Teams channel and delete all code from OnTurnAsync method. When we send post data from our card, as in a previous example, we trying to catch "postback" property, which exists only in emulator. But in teams, channelData contains only id of our conversation and "source" key with type of our activity. This key exists only when we try to submit our data from adaptive card. When I have changed it, I can use values from card in string type as in emulator :

   namespace Microsoft.BotBuilderSamples
{
    public static class DialogExtensions
    {
        public static async Task Run(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
        {

            var dialogSet = new DialogSet(accessor);
            dialogSet.Add(dialog);


            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);


            if (dialogContext.Context.Activity.GetType().GetProperty("ChannelData") != null)
            {
                var channelData = JObject.Parse(dialogContext.Context.Activity.ChannelData.ToString());
                 //Check property in teams channel
                if (channelData.ContainsKey("source"))
                {
                    var postbackActivity = dialogContext.Context.Activity;
                    // Convert the user's Adaptive Card input into the input of a Text Prompt
                    // Must be sent as a string
                    postbackActivity.Text = postbackActivity.Value.ToString();


                }
            }
            var results = await dialogContext.ContinueDialogAsync(cancellationToken);
            if (results.Status == DialogTurnStatus.Empty)
            {
                await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
            }

I understand that it's not the clear solution in that situation. But I managed to get the adaptive card to work in MS teams only in this way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM