Sending messages and cards in Microsoft Bot Framework

In this article we are going to have a look at the data we receive on the server (where our bot runs) when the user sends a message, how we can access that data and also how to send back data in different ways. We only focus on how to deal with this server-side, not on the user’s front end.

How can my bot access info coming from a user?

Via an object called TurnContext. It provides information to process an incoming activity during the length of the turn. What is a turn? A turn starts when the user sends an activity and ends after the server sends an activity back.

// src/bot.ts
import {TurnContext} from 'botbuilder';

export class MyBot {
    public onTurn = async (turnContext: TurnContext) => {
        // process the activity
    };
}

What info does TurnContext contain?

TurnContext and what info we get from it

As we can see above we get plenty of info:

  • ID of the channel the message was sent in (“emulator”)
  • Name of the user that send the message (“User”)
  • Name of the recipient (“Bot”)
  • type of this activity (“ConversationUpdate”)
  • timestamp when this message was sent
  • and more

In the example above the Bot Framework Service sends an activity of type ConversationUpdate. It does that when a party joins the conversation. For example, on starting a conversation with the Bot Framework Emulator we see two conversation update activities: one for the user joining the conversation and one for the bot joining. To distinguish these conversation update activities, check whether the members added property includes a member other than the bot.

Receiving a user message and sending text back

public onTurn = async (turnContext: TurnContext) => {
// without the following check the bot would immediately react on intialisation
if (turnContext.activity.type === ActivityTypes.Message) {
    // receiving
    let text = turnContext.activity.text;
    // sending
    await turnContext.sendActivity("Welcome!");
  }
}

Sending files

The following example will send an image, but the same applies for audio, video or other files.

public onTurn = async (turnContext: TurnContext) => {
    if (turnContext.activity.type === ActivityTypes.Message) {
        const reply = {
            type: ActivityTypes.Message,
            attachments: [this.getInternetAttachment()],
            text: 'This is an internet attachment.'
        };

        await turnContext.sendActivity(reply);
    }
};

private getInternetAttachment() {
    return {
        name: 'imageName.png',
        contentType: 'image/png',
        contentUrl: 'http://bothe.at/img/whatever.png'
    }
}

Sending a hero card

A hero card allows you to combine several elements together, such as images, text and action buttons.

import {ActionTypes, ActivityTypes, TurnContext, CardFactory} from 'botbuilder';

public onTurn = async (turnContext: TurnContext) => {
    if (turnContext.activity.type === ActivityTypes.Message) {
        const buttons = [
            {type: ActionTypes.ImBack, title: '1. Inline Attachment', value: '1'},
            {type: ActionTypes.ImBack, title: '2. Internet Attachment', value: '2'},
            {type: ActionTypes.ImBack, title: '3. Uploaded Attachment', value: '3'}
        ];

        const card = CardFactory.heroCard('', undefined,
            buttons, {text: 'You can upload an image or select one of the following choices.'});

        const reply = {type: ActivityTypes.Message, attachments: [card]};

        await turnContext.sendActivity(reply);
    }
};

Implementing click behavior for hero card buttons

There are many types of actions, such as opening a URL, calling a phone number, playing audio or video, showing an image, downloading a file or signing in using OAuth described on the Microsoft Doc page.

We use MessageFactory here which is just a helper class within the Bot Framework SDK to automate creation steps for you, and it is supported by most channels.

public onTurn = async (turnContext: TurnContext) => {
    if (turnContext.activity.type === ActivityTypes.Message) {
        const hero = MessageFactory.attachment(
            CardFactory.heroCard(
                'Holler Back Buttons',
                ['https://example.com/whiteShirt.jpg'],
                [{
                    type: ActionTypes.ImBack,
                    title: 'ImBack',
                    value: 'You can ALL hear me! Shout Out Loud'
                },
                    {
                        type: ActionTypes.PostBack,
                        title: 'PostBack',
                        value: 'Shh! My Bot friend hears me. Much Quieter'
                    },
                    {
                        type: ActionTypes.OpenUrl,
                        title: 'OpenUrl',
                        value: 'https://en.wikipedia.org/wiki/{cardContent.Key}'
                    }]
            )
        );

        await turnContext.sendActivity(hero);
    }
};

Sending an Adaptive Card

Adaptive Card is another way to send rich messages in card format, but it is not supported by all channels.

The AdaptiveCard type of FlightIternary was picked from BotBuilder Sample on Github.

import {ActivityTypes, TurnContext, CardFactory} from 'botbuilder';
import * as FlightItineraryCard from './resources/FlightItineraryCard';

export class MyBot {
    public onTurn = async (turnContext: TurnContext) => {
        if (turnContext.activity.type === ActivityTypes.Message) {
            await turnContext.sendActivity({
                text: 'Here is an Adaptive Card:',
                attachments: [CardFactory.adaptiveCard(FlightItineraryCard.default)]
            });
        }
    };
}

Sending a Carousel of Cards

import {ActivityTypes, TurnContext, CardFactory, MessageFactory} from 'botbuilder';

export class MyBot {
    public onTurn = async (turnContext: TurnContext) => {
        if (turnContext.activity.type === ActivityTypes.Message) {

            let messageWithCarouselOfCards = MessageFactory.carousel([
                CardFactory.heroCard('title1', ['imageUrl1'], ['button1']),
                CardFactory.heroCard('title2', ['imageUrl2'], ['button2']),
                CardFactory.heroCard('title3', ['imageUrl3'], ['button3'])
            ]);

            await turnContext.sendActivity(messageWithCarouselOfCards);
        }
    };
}

Sending action buttons

We are sending buttons that will disappear once the user tapped on them.

import {ActivityTypes, TurnContext, MessageFactory} from 'botbuilder';

export class MyBot {
    public onTurn = async (turnContext: TurnContext) => {
        if (turnContext.activity.type === ActivityTypes.Message) {

            const reply = MessageFactory.suggestedActions(['Red', 'Yellow', 'Blue'], 'What is the best color?');

            await turnContext.sendActivity(reply);
        }
    };
}

About Author

Mathias Bothe To my job profile

I am Mathias, born 41 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 17 years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I am founder of bosy.com, creator of the security service platform BosyProtect© and initiator of several other software projects.

No comments yet.

Leave a comment