Adaptive Cards with Bots - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki

Prerequisite: You should have a basic knowledge about Adaptive Cards and forked the repository now.

Adaptive Cards for Shopping List Bots

The shopping list bot uses an adaptive card when asked to show all items in the list. Through the adaptive card, chat participants can directly check and uncheck which items in the list should be completed. Here is how the adaptive card might look like.

[/images/AdaptiveCards/AdaptiveCardShoppingListRendered.jpg ](/microsoft-campus-community/workshop-shopping-list-bot/wiki/height=350px|Picture-of-shopping-list-adaptive-card-rendered-as-bot-web-chat-message.-The-list-contains-completed-'noodles'-item-and-not-completed-items-'2-apples'-and-'1-kg-flour'.)

Here is the JSON payload that defines the structure for the adaptive card above. To learn how it comes together, you should read the Adaptive Cards page for this workshop first.

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": "${title}"
        },
        {
            "$data": "${items}",
            "id": "${id}",
            "type": "Input.Toggle",
            "title": "${if(unit.value, unit.value, '')} ${if(unit.value && unit.unitName, unit.unitName, '')}  ${itemName}",
            "value": "${marked}"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Save"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.1"
}

JavaScript Adaptive Card library

Microsoft has NPM adaptivecards and NPM adaptivecards-templating which help to put together the adaptive card in the bot.

Make Adaptive Card available in Bot

The JSON payload for the adaptive card is saved in the shopping-list-bot/resources/shoppingListCard.json file in the shopping list bot project. This way, you can import the adaptive card payload anywhere in the bot const ShoppingListCard = require('../../resources/shoppingListCard.json'); to send it in a message to a chat.

Send Adaptive Card

Before sending an adaptive card, it is important to know that not all chat messaging channels support adaptive cards. Microsoft has a list of Card Support by Channel. It shows you which messaging channels provide full support of adaptive cards, which channels send an adaptive card as an image to the chat. Hence, there is no interaction possible, and which channels do not support adaptive cards at all. Overall, the bot cannot assume that the shopping list items can be sent as an adaptive card. To deal with this, the shopping-list-bot/src/helpers/adaptiveCardsAvailable.ts defines two helper functions to check if a channel supports adaptive cards.

The only case where the shopping list bot sends the adaptive card is the GetAllItemsDialog in the shopping-list-bot/src/dialogs/getAllItemsDialog.ts file. In the showItemsStep of the GetAllItemsDialog, the bot sends the items received as input of the dialog. The bot takes the channelId from the channel. It receives the command to get all items and check if the channel supports adaptive cards. If adaptive cards support is available, the ShoppingListCard JSON imported in the previous step is used to construct the adaptive card.

  1. Turn the JSON into an adaptive card JavaScript library object const shoppingListAdaptiveCardTemplate = new Template(ShoppingListCard);.
  2. Add the data for this adaptive card instance. For the title of this adaptive card, the bot uses Shopping List. The list of items itself is constructed from the items input array. Note that we need to transform the elements in the items array a bit because adaptive cards expect the marked property for the checkbox toggle as string and not boolean as it is stored in the shopping list item.
    const currentShoppingListPayload = shoppingListAdaptiveCardTemplate.expand({
                         $root: {
                             title: "Shopping List",
                             items: items.map(item =>  {return {
                                 itemName: item.itemName,
                                 marked: item.marked.toString(),
                                 unit: item.unit, 
                                 id: item.id
                             }})
                         }
                     });
    
  3. Constructing the adaptive card in the previous step is not specific to the Bot Framework. In this step, the adaptive card is parsed by the bot framework's functionality to turn the adaptive card into a message attachment for the bot framework.
     const shoppingListAdaptiveCard = new AdaptiveCard();
     shoppingListAdaptiveCard.parse(currentShoppingListPayload);
     const itemsAdaptiveCardAttachment = CardFactory.adaptiveCard(shoppingListAdaptiveCard);
    
  4. Lastly, the message containing the adaptive card as an attachment is send to the chat await stepContext.context.sendActivity({ attachments: [itemsAdaptiveCardAttachment] });.

If a channel does not support adaptive cards, all the items in the list are sent to the chat as regular text messages.

Handle Adaptive Cards Action

Adaptive cards do not just display information. An adaptive card can be interacted with and send the result back to the adaptive card author through an action. There are several types of actions that can be handled differently by the bot. The adaptive card to show a shopping list in the bot uses an action of type Submit to allow chat participants to change whether an item is marked as completed. The bot will receive an activity that is not a message when a chat participant triggers the Submit action by clicking the Save button in the adaptive card.

The shopping list bot will handle a Submit activity through custom middleware. In the Bot Framework, middleware classes are placed before the bot's logic. Every activity in and out from the bot flows through the middleware chain.

The middleware to handle a response from a shopping list adaptive card is defined in the shopping-list-bot/src/middleware/ShoppingListAdaptiveCardResponseMiddleware.ts file. For every activity between the chat and the bot, the onTurn function is called. If the activities value attribute exists and has the form of the adaptive card's response, then we assume that we should update the items according to the marked values in the value object. The shopping list adaptive card's response is an object with a property for each item in the list. Each property's name is the id of the item, and the value is the string "true" or "false". Each item's marked value is updated by making a call to the shopping list API.

After executing the logic in the onTurn function, it is important to call await next(); to execute the next middleware or if all middleware has run the bot's activity handlers.

Below the await next(); call, you can write more middleware logic. It is executed after the bot has executed its logic before the bot's response activity is returned to the chat. This is not needed for the middleware to handle the shopping list adaptive card action.

NEXT: Test your Bot locally