Skip to content

Instantly share code, notes, and snippets.

@mdrichardson
Last active November 15, 2019 16:07
Show Gist options
  • Save mdrichardson/320e4e20aac6252c1acac602032c34a2 to your computer and use it in GitHub Desktop.
Save mdrichardson/320e4e20aac6252c1acac602032c34a2 to your computer and use it in GitHub Desktop.
AdaptiveCardPrompt

Adaptive Card Prompt

Abstract

By far, the most common issue that the Support Team answers is along the lines of, "How do I capture input from Adaptive Cards with my bot?" There are no official samples or documentation to show a user how to accomplish this. Therefore, the workaround to make this work is difficult to intuit or discover.

There are two different approaches we can take towards making this easier for our customers:

  1. Provide samples and documentation that show how to accomplish this
  2. Provide an AdaptiveCardPrompt that makes this easy to implement

Introduction

Many of our customers use Adaptive Cards to gather user input. This is likely preferred by many of our customers to using multi-turn interactions with their bots to gather user information because it is more efficient and provides a better user experience.

Because Adaptive Card input is sent through Activity.Value and not Activity.Text, it will not show up in a Waterfall Dialog's StepContext.Result. This makes it difficult for many users to use Adaptive Cards to gather user data; they don't know the "correct" way to accomplish this in their bots.

Since this is both a popular desire for our customers and we don't provide anything official that either makes this easy or shows them how to do it, this is the most common issue that our Support Team receives.

Similarly the Support Team frequently sees the following Adaptive Card features requested:

  • Ability to require certain input fields
  • Ensure that the bot only responds to input from specific Adaptive Cards if there are many displayed in the chat window and the user responds with the "wrong" one

Current Workaround

When presented with these customer issues, our current recommendation is generally to:

  1. Send the adaptive card as an attachment to a blank text prompt
  2. In OnTurn, copy Activity.Value to Activity.Text. In C#, this gets a little more difficult and "hacky" due to needing null coalescence checks and JSON serialization

Possible Solutions

Provide Documentation and Samples

The "easiest" solution would be to simply provide documentation on how to do this and/or a sample that uses ActivityPrompt, which returns the entire context.Activity as the StepContext.Result.

Pros

  • Little and relatively simple extra code to support
  • The documentation or sample will also show how to address similar issues that may be solved with ActivityPrompt

Cons

  • The solution doesn't directly address the problem
  • Does not add other frequently-requested features related to Adaptive Cards
  • We have a related Blog Post, but that doesn't seem to have decreased the rate we see this issue

Provide an AdaptiveCardPrompt

We could provide an AdaptiveCardPrompt that solves this issue in a very user-friendly way by simply having the user include the Adaptive Card in the AdaptiveCardPrompt's Prompt property, or passed into it's constructor.

I have created this as PR for both the C# and Node SDKs along with:

  • Unit tests for both SDKs
  • Samples of usage in Experimental Samples for both SDKs

It also includes the following key features frequently requested by customers:

  • Card can be passed in constructor or as prompt/reprompt activity attachment
  • Includes validation for specified required input fields
  • Displays custom message if user replies via text and not card input
  • Ensures input is only valid if it comes from the appropriate card (not one shown previous to prompt)

Pros

  • Very easy for developers to gather user input using Adaptive Cards
  • Provides many frequently-requested features that would take significant work for a developer to implement themselves

Cons

  • Adds additional, complex code for the SDK to support and maintain
  • Would still benefit from official Samples and Documentation

Usage

private static async Task<DialogTurnResult> ShowCardAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // STANDARD FOR ADAPTIVE CARD USE
    var cardPath = Path.Combine("../Resources/", "adaptiveCard.json");
    var cardJson = File.ReadAllText(cardPath);
    var cardAttachment = new Attachment()
    {
        ContentType = "application/vnd.microsoft.card.adaptive",
        Content = JsonConvert.DeserializeObject(cardJson),
    };
    // END STANDARD FOR ADAPTIVE CARD USE

    // SPECIFIC TO ADAPTIVECARDPROMPT
    // Adaptive Card can be either passed in `AdaptiveCardPromptSettings`...
    var promptSettings = new AdaptiveCardPromptSettings()
    {
        Card = cardAttachment
    };

    // ...or directly to the prompt's `PromptOptions.Prompt`
    return await stepContext.PromptAsync(nameof(AdaptiveCardPrompt), new PromptOptions()
    {
        Prompt = new Activity { Attachments = new List<Attachment>() { cardAttachment } }
    });

}

private async Task<DialogTurnResult> DisplayResultsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Use the result
    var result = stepContext.Result as JObject;
    // Do something with `result`
}

Example

AdaptiveCardPrompt Example

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment