Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
556 views
in Technique[技术] by (71.8m points)

botframework - I am using Bot framework V4.3, I want to retrieve adaptive card submit values

I'm using Bot framework V4.3, I have been using adaptive card in waterfall dialog, to get user information, I would want to get values once user clicks submit button and also I would like to go back to previous step if user click back button.

Here is how my adaptive card looks like

Adaptive card

I have tried the solution given by @mdrichardson in Stack Overflow But the adaptive card re-prompts again.

And the below code help us to go back to previous step but how to implement it to back button of adaptive card.

stepContext.ActiveDialog.State["stepIndex"] =(int)stepContext.ActiveDialog.State["stepIndex"] - 2;

Adding adaptive card to dialog. I had even used TextPrompt instead of ChoicePrompt

AddDialog(new ChoicePrompt("AdaptiveCardPrompt") { Style = ListStyle.None });

This is how I'm displaying adaptive card. My adaptive card is in Json format

cardAttachment = CreateAdaptiveCardAttachment();

return await stepContext.PromptAsync("AdaptiveCardPrompt",
    new PromptOptions
    {
        Prompt = (Activity)MessageFactory.Attachment(new Attachment
        {
            ContentType = AdaptiveCard.ContentType,
            Content = cardAttachment.Content
        }),
    }, cancellationToken);

Kindly help me in solving this issue. Thank you in advance

Edit from Botframework Support: Please do not use the code block below. It only works in Emulator. Instead, use:

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

Edit 1: @mdrichardson Here is how I have setup the dialog call

        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 in OnTurnAsync method

 if (turnContext.Activity.Type == ActivityTypes.Message)
 {
      await Dialog.Run(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
 }

Edit 2 : I modified the code and I was able to go to next waterfall step. But I'm facing another issue here. Next prompt is not getting displayed but I can see it in Log This is how it shows in Emulator

Emulator View

Once user clicks the button control lands in MoreInfoAsync method

 private async Task<DialogTurnResult> MoreInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
 {
            var goback = JObject.Parse(stepContext.Result.ToString());
            stepContext.Values["AdaptiveCardDetails"] = stepContext.Result.ToString();

            if (goback.ContainsKey("goBack"))
            {
                return await stepContext.ReplaceDialogAsync(InitialDialogId);
            }
            // stepContext.ActiveDialog.State["stepIndex"] = (int)stepContext.ActiveDialog.State["stepIndex"] - 2;

            else
                return await stepContext.PromptAsync("MoreInfo", new PromptOptions { Prompt = MessageFactory.Text("Tell Me more.") }, cancellationToken);
 }

I would like to go to initial dialog so I'm using ReplaceDialogAsync.

MoreInfo dialog is not displayed in emulator but its shown in log

Edit 3: Here is the complete code of waterfall steps

          // This array defines how the Waterfall will execute.
                        var waterfallSteps = new WaterfallStep[]
                        {
                            ChoiceAsync,
                            CardAsync,
                            MoreInfoAsync,
                            ConfirmAsync
                        };
                        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
                        AddDialog(new ChoicePrompt("ChoiceType"));
                        AddDialog(new TextPrompt("AdaptiveCardPrompt"));
                        AddDialog(new TextPrompt("MoreInfo"));
                        InitialDialogId = nameof(WaterfallDialog);

            private async Task<DialogTurnResult> ChoiceAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
            {
                options = new PromptOptions()
                {
                    Prompt = MessageFactory.Text("Select the Choice"),
                    RetryPrompt = MessageFactory.Text("That was not a valid choice."),
                    Choices = GetChoices(),
                    Style = ListStyle.HeroCard
                };
                return await stepContext.PromptAsync("ChoiceType", options, cancellationToken);
            }


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

                var cardAttachment = new Attachment();
                stepContext.Values["leaveType"] = stepContext.Result.ToString();
                cardAttachment = CreateAdaptiveCardAttachment();
                return await stepContext.PromptAsync("AdaptiveCardPrompt",
                new PromptOptions
                {
                     Prompt = (Activity)MessageFactory.Attachment(new Attachment
                     {
                         ContentType = AdaptiveCard.ContentType,
                         Content = cardAttachment.Content,
                     }),
                }, cancellationToken);
            }

            private async Task<DialogTurnResult> MoreInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
            {
                var goback = JObject.Parse(stepContext.Result.ToString());
                stepContext.Values["AdaptiveCardDetails"] = stepContext.Result.ToString();

                if (goback.ContainsKey("goBack"))
                {
                    return await stepContext.ReplaceDialogAsync(InitialDialogId);
                }
                else return await stepContext.PromptAsync("MoreInfo", new PromptOptions { Prompt = MessageFactory.Text("Tell Me more.") }, cancellationToken);
            }

            private async Task<DialogTurnResult> ConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
            {
                stepContext.Values["MoreInfo"] = stepContext.Result;
                //As of now I wouldn't perform any task here so I'll end
                return await stepContext.EndDialogAsync();
            }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Dealing with the Re-Prompt

The issue is with your OnTurnAsync() method:

 if (turnContext.Activity.Type == ActivityTypes.Message)
 {
      await Dialog.Run(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
 }

Every time a user sends a message, it causes a new instance of your dialog to be run. Since Adaptive Card Input gets sent as a PostBack message (which is still a message), it causes the Dialog to run again, re-prompting the user.

If you're going to run dialogs from OnTurnAsync() or OnMessageAsync(), there's a couple of different things you should do, either:

  1. Use if/switch statements. For example, if the message contains "help", run the HelpDialog, or

  2. Start a dialog that saves user responses and skips steps as necessary. You can see an example of this in Core Bot's Booking Dialog. Notice how it's saving the user response in each step with something like bookingDetails.TravelDate = (string)stepContext.Result; and checks to see if it exists in the previous step before prompting with something like if (bookingDetails.TravelDate == null). For yours, you might store something like userProfile.AdaptiveCardDetails or something.

Back Button

To get the back button working, let's say it looks like this in your Adaptive Card:

{
    "type": "Action.Submit",
    "title": "Back",
    "data": {
        "goBack": "true",
    }
},

When the user clicks "Back", the bot will receive an activity with:

enter image description here

Since the user wants to go back and you don't need the data, you could do something like:

var activity = turnContext.Activity;

if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value.GetType().GetProperty("goBack"))
{
    dc.Context.Activity.Text = "Back";
}

and then in your Dialog step:

if (stepContext.Result == "Back")
{
    stepContext.ActiveDialog.State["stepIndex"] = (int)stepContext.ActiveDialog.State["stepIndex"] - 2;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...