Introduction

Adaptive Cards are an open card exchange format enabling developers to exchange UI content in a common and consistent way. Adaptive cards are expressed in a json format which are created based upon certain predefined rules and regulations. Following such rules and regulation ensures that 

  1. Every one can follow same sort of authoring practices
  2. Cost of rework is very less
  3. Cards are completely declarative and lack any code
  4. Cards are completely portable making them platform agnostic

Since the schema for cards is fixed, authors can author the cards and Host applications can then write their own renderers based upon the schema. Consider an example as follows, A developer authors a feedback card which is used by a feedback collector bot. This bot is connected to FaceBook, and Skype channels. The FaceBook and Skype have their own set of renderers written for the Adaptive Cards, so the same card sent by the bot to these two different channel will look and feel different.

↑Back To Top


Tools and Frameworks Used

Following tools and frameworks should be installed on the development machine

↑Back To Top


Problem Statement

Traditionally in Microsoft Bot Framework, there is a possibility of  creating different rich cards which can be sent to the user to display information and sometimes to collect it. These rich cards are 

  1. Animated Card: A card that can play GIFs
  2. Audio Card: A card that sends a playable audio to the user
  3. Hero Card: A card with one large image and several buttons on it
  4. Thumbnail Card: A card with thumbnail image, buttons and text
  5. Receipt Card:A card that enables the bot to provide the receipt to user.
  6. Sign In Card: A Card that enables the user to sign in to perform certain operations
  7. Video Card: A card that can play videos to the user.

Adaptive Cards provide a way to combine the abilities of all of the above cards using a single format. In case of using adaptive cards to collect data from the users, the bot channels send out the data in only a specific format. In order to consume this data, it is necessary to understand this exchange of adaptive card data. This article aims to discuss how to read said data using a sample scenario.

↑Back To Top


Sample Bot

The bot is simple bot. This bot sends a form to the user to select one of two colors : Green or Red and replies back to the user with the selected color. Sample is shown below.

↑Back To Top


Concept

The json definition for the adaptive card shown in the Sample Bot section is as follows.

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "id": "textBlock",
            "text": "Select a Color"
        },
        {
            "type": "Input.ChoiceSet",
            "placeholder": "Placeholder text",
            "choices": [
                {
                    "title": "Green",
                    "value": "Green"
                },
                {
                    "title": "Red",
                    "value": "Red"
                }
            ],
            "id":"choiceset",
            "style": "expanded"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "id": "submit",
            "title": "Submit",
            "data":{
                        "action": "colorselector"
                 
            }
        }
    ],
    "version": "1.0"
}

The Message Activity that is sent by the bot channel to the Azure Bot service when the Submit button of the adaptive card is clicked is shown below.

{
  "channelData": {
    "postback": true
  },
  "channelId": "emulator",
  "conversation": {
    "id": "4f1c47d0-f119-11e8-bad3-4f12f90d227a|livechat"
  },
  "from": {
    "id": "24474995-17e2-4290-be1f-79e27ee6140c",
    "name": "User"
  },
  "id": "5ce33270-f119-11e8-975a-45ab236171db",
  "localTimestamp": "2018-11-26T11:49:46+10:30",
  "locale": "",
  "recipient": {
    "id": "15",
    "name": "Bot",
    "role": "bot"
  },
  "serviceUrl": "http://localhost:58248",
  "timestamp": "2018-11-26T01:19:46.967Z",
  "type": "message",
  "value": {
    "action": "colorselector",
    "choiceset": "Green"
  }
}

The Value object on the above activity looks like below.

"value": {
    "action": "colorselector",
    "choiceset": "Green"
  }

It can be noted that the value sent by the bot channel is nothing but the data object defined in the adaptive card and the color selected using the radio button (this is defined as an object with id "choiceset" in the adaptive card). Once this is understood, the task of handling the submit button is really easy.

Another important point to note is that whenever the submit button is clicked, the activity will be sent back by the channel as a "PostBack" if the there is some reactive data in the adaptive card. If the Submit task is just sending out a string value then it will be sent back as "ImBack"

↑Back To Top


Code

The bot sends the adaptive card shown in the Sample Bot section as a welcome message to the user. Since this is a sample implementation to understand the concept of the submit action on the adaptive card, the message is sent when an ConversationUpdate activity is sent by the channel to the bot service. This activity is emitted by the channel when a user or bot joins the conversation.

Note: The ConversationUpdate activity is exclusive to the DirectLine Channel and should be used with it only. In case other channels like Skype or FaceBook are used, the ConversationUpdate activity will not be emitted by the channel and the welcome message will not be sent. Refer How to properly send a greeting message and common issues from customers on the botframework blog to handle the welcome messages.

The welcome message can be sent by the bot using following code  in OnTurnAsync method.

case ActivityTypes.ConversationUpdate:
                    foreach(var member in turnContext.Activity.MembersAdded)
                    {
                        if(member.Id != turnContext.Activity.Recipient.Id)
                        {
                            adaptiveCard = File.ReadAllText(@".\wwwroot\ColorSelector.json");
                            var reply = turnContext.Activity.CreateReply();
                            reply.Attachments = new List<Attachment>()
                            {
                                new Attachment()
                                {
                                    ContentType = "application/vnd.microsoft.card.adaptive",
                                    Content = JsonConvert.DeserializeObject(adaptiveCard)
                                }
                            };
                            await turnContext.SendActivityAsync(reply, cancellationToken:cancellationToken);  
                        }
                    }
                    break;

When the user clicks on the Submit button on the adaptive card, the channel emits a object indicating that the adaptive card has sent out complex data and the activity is marked as a postback by the channel. The object sent by the channel is of format 

"channelData": {
    "postback": true
  }

This can be handled in the code as follows

case ActivityTypes.Message:
                    var token = JToken.Parse(turnContext.Activity.ChannelData.ToString());
                    string selectedcolor = string.Empty;
                    if(System.Convert.ToBoolean(token["postback"].Value<string>()))
                    {
                        JToken commandToken = JToken.Parse(turnContext.Activity.Value.ToString());
                        string command = commandToken["action"].Value<string>();
 
                        if(command.ToLowerInvariant() == "colorselector")
                        {
                            selectedcolor = commandToken["choiceset"].Value<string>();
                        }
                         
                    }
 
                    await turnContext.SendActivityAsync($"You Selected {selectedcolor}", cancellationToken: cancellationToken);
 
                    break;

The code is basically checking if the "postback" property sent by the channel is true and if it is then the data in the "Value" object is read and checked and a descision is made on basis of it.
↑Back To Top


Testing

A sample test case is shown below.

↑Back To Top


Sample

The sample to study the concept discussed in this article can be downloaded from the TechNet gallery at Sample Code For Reactive Adaptive Card Bot Demo.

↑Back To Top


Conclusion

In this article, the approach on how to handle the reactive adaptive cards in a chat bot was seen. It can be seen that once the way channel is sending out the data back to the bot service, it is easy to put in the code which will handle the post back.

↑Back To Top


See Also

Following articles on the TechNet wiki discuss the bot framework and can be referred for additional reading.

  1. Microsoft Bot Framework: Real Life Example of Authentication Using Azure Bot Service And FitBit Web API
  2. ASP.NET Core 2.0: Building Weather Assistant Chatbot using Bot Builder SDK v4

↑Back To Top


Further Reading

Microsoft Bot Framework is a beautiful framework which makes it easy to create chat bots. That being said, the bot framework requires a lot of initial learning and the developers need to invest quality time to understand the concept and nuances of the bot framework. It is highly suggested that the readers of this article familiarize themselves with the concepts. Following resources will prove invaluable in the learning process.

  1. Azure Bot Service Documentation
  2. Official Bot Framework Blog
  3. Bot Builder Gitter Page(To discuss and ask questions)

To learn more about adaptive card and to play around with them, following resources will prove useful.

  1. Adaptive Card: Landing page
  2. Adaptive Cards Samples
  3. Adaptive Cards Designer

↑Back To Top


References

Following articles were referred while writing this article.

  1. Add rich card attachments to messages
  2. Adaptive Cards

↑Back To Top