Bot Framework Chatbot Activities

by Michael Szul on

No ads, no tracking, and no data collection. Enjoy this article? Buy us a ☕.

With the Bot Framework SDK v4, creating and managing a chatbot has been significantly revamped. Bot management now centers around processing activities and message types. For example, take a look at the following code:

adapter.processActivity(req, res, async (context) => {
          if (context.activity.type === "message") {
              await context.sendActivity(`You said "${context.activity.text}"`);
          } else {
              await context.sendActivity(`[${context.activity.type} event detected]`);
          }
      });
      

The adapter object represents the Bot Framework adapter that manages your chatbot, and you can attach middleware to the adapter in order to enhance your chatbot. It is the adapter's job to route messages to the appropriate channels, middleware, etc. In this snippet, you notice that we are using the processActivity() method. When a chatbot and a user communicate back and forth, they do so through what is termed "activities," which works in conjunction with the "turn context." Since we know that the Bot Framework is a translation layer, we can examine the data being sent back and forth to see these activities.

    [
              {
                  "conversation": {
                  "id": "1"
                  },
                  "id": "57db47ea-2d8a-4deb-9999-11ecffd70102",
                  "recipient": {
                  "id": "70fa7bc2-071f-3d91-b062-dc0d69686676",
                  "name": "Ginny",
                  "role": "bot"
                  },
                  "from": {
                  "id": "956a3b3c-5cf4-3c32-993e-38aa2647af52",
                  "name": "Angel",
                  "role": "user"
                  },
                  "text": "Hey Ginny, I missed the lecture yesterday, do you have a video?",
                  "timestamp": "2018-05-20T16:19:36.000Z",
                  "type": "message",
                  "channelId": "chatdown"
              },
              {
                  "conversation": {
                  "id": "1"
                  },
                  "id": "7ff1b8b2-2f06-42a7-8914-5e93f7fa68ad",
                  "recipient": {
                  "id": "956a3b3c-5cf4-3c32-993e-38aa2647af52",
                  "name": "Angel",
                  "role": "user"
                  },
                  "from": {
                  "id": "70fa7bc2-071f-3d91-b062-dc0d69686676",
                  "name": "Ginny",
                  "role": "bot"
                  },
                  "text": "",
                  "timestamp": "2018-05-20T16:19:42.000Z",
                  "type": "typing",
                  "channelId": "chatdown"
              }
          ]
      

This is some generated transcript code from the Chatdown CLI. As you can see, this is an array of objects, and each object is an activity. Each of these activities has a "type" to identity what it is. This is why we check for the type when processing the activities:

if (context.activity.type === "message") {
          ...
      }
      

There are lot of possible activity types, including:

  • message | Chatbot has sent or received a message
  • contactRelationUpdate | Relationship between bot and contact has changed
  • conversationUpdate | User has joined or left a conversation
  • typing | Chatbot or user is typing
  • endOfConversation | The conversation has ended
  • event | An event has occurred
  • messageUpdate | A previously sent message has been updated
  • messageDelete | A previously sent message has been deleted
  • messageReaction | A reaction has been sent to a message (e.g., thumbs up)
  • suggestion | A suggestion has been provided

The type property of the activity is a string, so you can check its value through a string comparison like above, but the TypeScript schema for the activity types is an exported enum. This means that, if you are writing your code in TypeScript, you do the comparison with the enum object, such as:

if(context.activity.type === ActivityTypes.Message) {
          ...
      }
      

The enum definition looks like this:

export enum ActivityTypes {
          Message = 'message',
          ContactRelationUpdate = 'contactRelationUpdate',
          ConversationUpdate = 'conversationUpdate',
          Typing = 'typing',
          Ping = 'ping',
          EndOfConversation = 'endOfConversation',
          Event = 'event',
          Invoke = 'invoke',
          DeleteUserData = 'deleteUserData',
          MessageUpdate = 'messageUpdate',
          MessageDelete = 'messageDelete',
          InstallationUpdate = 'installationUpdate',
          MessageReaction = 'messageReaction',
          Suggestion = 'suggestion',
          Trace = 'trace',
          Handoff = 'handoff'
      }
      

In our example transcript file, you can see that we have both a message activity and a "typing" activity, which will show a typing indicator to the user in the chat window. This is why a lot of Bot Framework example code does the conditional check at the beginning to verify that the activity type is a "message." There a lot of different activities types flying back and forth between the user and the bot.