I am trying to send an adaptive card which has 2 options for user to select. When user submit the response from adaptive card I am receiving :
Newtonsoft.Json.JsonReaderException: Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject. Path ‘[‘BotAccessors.DialogState’].DialogStack.$values[0].State.options.Prompt.attachments.$values[0].content.body’.
Full code example Link : Manage a complex conversation flow with dialogs
Modification made in HotelDialogs.cs:-
public static async Task<DialogTurnResult> PresentMenuAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
// Greet the guest and ask them to choose an option.
await stepContext.Context.SendActivityAsync(
"Welcome to Contoso Hotel and Resort.",
cancellationToken: cancellationToken);
//return await stepContext.PromptAsync(
// Inputs.Choice,
// new PromptOptions
// {
// Prompt = MessageFactory.Text("How may we serve you today?"),
// RetryPrompt = Lists.WelcomeReprompt,
// Choices = Lists.WelcomeChoices,
// },
// cancellationToken);
var reply = stepContext.Context.Activity.CreateReply();
reply.Attachments = new List<Attachment>
{
new Attachment
{
Content = GetAnswerWithFeedbackSelectorCard("Choose: "),
ContentType = AdaptiveCard.ContentType,
},
};
return await stepContext.PromptAsync(
"testPrompt",
new PromptOptions
{
Prompt = reply,
RetryPrompt = Lists.WelcomeReprompt,
},
cancellationToken).ConfigureAwait(true);
}
Note: ["testPrompt"] I tried with Text Prompt and slightly customizing the TextPrompt to read Activity Value. If Text prompt is not the appropriate prompt for adaptive card response,please let me know is there any other prompt that can be used or some custom prompt will be helpful for this kind of scenario.
Custom Prompt:-
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace HotelBot
{
public class CustomPrompt : Prompt<string>
{
public CustomPrompt(string dialogId, PromptValidator<string> validator = null)
: base(dialogId, validator)
{
}
protected async override Task OnPromptAsync(ITurnContext turnContext, IDictionary<string, object> state, PromptOptions options, bool isRetry, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (isRetry && options.RetryPrompt != null)
{
await turnContext.SendActivityAsync(options.RetryPrompt, cancellationToken).ConfigureAwait(false);
}
else if (options.Prompt != null)
{
await turnContext.SendActivityAsync(options.Prompt, cancellationToken).ConfigureAwait(false);
}
}
protected override Task<PromptRecognizerResult<string>> OnRecognizeAsync(ITurnContext turnContext, IDictionary<string, object> state, PromptOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
var result = new PromptRecognizerResult<string>();
if (turnContext.Activity.Type == ActivityTypes.Message)
{
var message = turnContext.Activity.AsMessageActivity();
if (!string.IsNullOrEmpty(message.Text))
{
result.Succeeded = true;
result.Value = message.Text;
}
else if (message.Value != null)
{
result.Succeeded = true;
result.Value = message.Value.ToString();
}
}
return Task.FromResult(result);
}
}
}
Card Creation Method:-
private static AdaptiveCard GetAnswerWithFeedbackSelectorCard(string answer)
{
if (answer == null)
{
return null;
}
AdaptiveCard card = new AdaptiveCard();
card.Body = new List<AdaptiveElement>();
var choices = new List<AdaptiveChoice>()
{
new AdaptiveChoice()
{
Title = "Reserve Table",
Value = "1",
},
new AdaptiveChoice()
{
Title = "Order food",
Value = "0",
},
};
var choiceSet = new AdaptiveChoiceSetInput()
{
IsMultiSelect = false,
Choices = choices,
Style = AdaptiveChoiceInputStyle.Expanded,
Value = "1",
Id = "Feedback",
};
var text = new AdaptiveTextBlock()
{
Text = answer,
Wrap = true,
};
card.Body.Add(text);
card.Body.Add(choiceSet);
card.Actions.Add(new AdaptiveSubmitAction() { Title = "Submit" });
return card;
}
Thanks!
Hit this issue today. This looks like a known issue and there is a workaround available on GitHub
https://github.com/Microsoft/AdaptiveCards/issues/2148#issuecomment-462708622
this is current problem, do we know when we will be able create multi-turn conversation flow using adaptive card in V4 bot framework and wait for Adaptive card's response in stepcontext.result variable instead always sending user to original OnTurn method.
After digging for some way forward I came across:
Issue#614
Thus to make adaptive card response work from Dialog, I made a compatible adaptive card prompt by one modification each in Prompt.cs and TextPrompt.cs from Microsoft bot framework.
Prompt.cs => Prompt2.cs ; TextPrompt.cs => CustomPrompt.cs
Prompt2.cs :
CustomPrompt.cs :
Thus workaround until official release of Adaptive Card Prompt for dialog in V4 botframework, is to use this custom prompt.
Usage: (Only for sending adaptive cards which have submit actions)
Referring to the example in the question section:
The response for the adaptive card submit action will be received in the next waterfall step : ProcessInputAsync()
choice will be JSON string of the body posted by the adaptive card.