Understand the structure of an echo bot

APPLIES TO: SDK v4

The Bot Framework templates and samples are written for ASP.NET (C#), restify (JavaScript), and aiohttp (Python). However, the web service features aren't part of the Bot Framework SDK, but part of the web framework you choose to use.

All bot applications share some common features.

Feature Description
Resource provisioning The bot as a web app needs to create a web service, bot adapter, and bot object.
Messaging endpoint The web app needs to implement a messaging endpoint on which to receive activities and forward activities to the bot adapter.
Bot adapter The adapter receives activities from the messaging endpoint, forwards them to the bot's turn handler, and catches any errors or exceptions the bot's logic doesn't catch.
Bot object The bot object handles the bot's reasoning or logic for the turn.

You can create an echo bot from the templates, as described in Create a bot, or you can copy an echo bot project from the Microsoft/BotBuilder-Samples repository.

The C# and JavaScript templates have built-in support for streaming connections. However, this article doesn't cover streaming features.

Note

The Bot Framework JavaScript, C#, and Python SDKs will continue to be supported, however, the Java SDK is being retired with final long-term support ending in November 2023.

Existing bots built with the Java SDK will continue to function.

For new bot building, read about choosing the right copilot solution.

For more information, see The future of bot building.

Prerequisites

Bot templates

A bot is a web application, and templates are provided for each language.

The Bot Framework includes both VSIX and .NET templates.

The templates generate an ASP.NET MVC Core web app. If you look at the ASP.NET fundamentals, you'll see similar code in files such as Program.cs and Startup.cs. These files are required for all web apps and aren't bot-specific.

Note

You can install the templates from within Visual Studio.

  1. In the menu, select Extensions then Manage Extensions.
  2. In the Manage Extensions dialog, search for and install Bot Framework v4 SDK templates for Visual Studio.

For information about deploying .NET bots to Azure, see how to Provision and publish a bot.

The appsettings.json file specifies the configuration information for your bot, such as its app ID, and password among other things. If using certain technologies or using this bot in production, you'll need to add your specific keys or URL to this configuration. For this echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time.

The EchoBot.csproj file specifies dependencies and their associated versions for your bot. This is all set up by the template and your system. Additional dependencies can be installed using NuGet package manager or the dotnet add package command.

Resource provisioning

To function as a web app, your bot needs to create a web service, bot adapter, and bot object.

For most bots, you would also create storage layer and memory management objects for the bot. However, the echo bot doesn't need to persist state between turns. And for some bots, you may need to create other objects that the bot object or adapter will require.

In ASP.NET, you register objects and object creation methods in the Startup.cs file. The ConfigureServices method loads the connected services and their keys (if there are any) from appsettings.json, connects state, and so on. Here, the adapter and bot are defined to be available through dependency injection. Then, the Configure method finishes the configuration of your app.

ConfigureServices and Configure are called by the runtime when the app starts.

Messaging endpoint

The template implements a web service with a messaging endpoint. When it receives a request, the service extracts the authentication header and request payload and forwards them to the adapter.

The C# and JavaScript SDKs support streaming connections. While the echo bot doesn't use any of the streaming features, the adapter in the C# and JavaScript templates is designed to support them.

Each incoming request represents the start of a new turn.

Controllers\BotController.cs

// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
    private readonly IBotFrameworkHttpAdapter _adapter;
    private readonly IBot _bot;

    public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
    {
        _adapter = adapter;
        _bot = bot;
    }

    [HttpPost, HttpGet]
    public async Task PostAsync()
    {
        // Delegate the processing of the HTTP POST to the adapter.
        // The adapter will invoke the bot.
        await _adapter.ProcessAsync(Request, Response, _bot);
    }
}

The bot adapter

The adapter receives activities from the messaging endpoint, forwards them to the bot's turn handler, and catches any errors or exceptions the bot's logic doesn't catch. The adapter also forwards activities from your bot to the user's channel.

The adapter allows you to add your own on turn error handler.

Startup.cs

The adapter to use is defined in the ConfigureServices method.

// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

AdapterWithErrorHandler.cs

public class AdapterWithErrorHandler : CloudAdapter
{
    public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger)
        : base(auth, logger)
    {
        OnTurnError = async (turnContext, exception) =>
        {
            // Log any leaked exception from the application.
            // NOTE: In production environment, you should consider logging this to
            // Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
            // to add telemetry capture to your bot.
            logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

            // Send a message to the user
            await turnContext.SendActivityAsync("The bot encountered an error or bug.");
            await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code.");

            // Send a trace activity, which will be displayed in the Bot Framework Emulator
            await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
        };
    }
}

The bot logic

The echo bot uses an activity handler and implements handlers for the activity types it recognizes and reacts to, in this case, the conversation update and message activities.

  • A conversation update activity includes information on who has joined or left the conversation. For non-group conversations, both the bot and the user join the conversation when it starts. For group conversations, a conversation update is generated whenever someone joins or leaves the conversation, whether that's the bot or a user.
  • A message activity represents a message the user sends to the bot.

The echo bot welcomes a user when they join the conversation and echoes back any messages they send to the bot.

Startup.cs

The bot to use is defined in the ConfigureServices method.

// Create the Bot Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

Bots\EchoBot.cs

public class EchoBot : ActivityHandler
{
    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        var replyText = $"Echo: {turnContext.Activity.Text}";
        await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
    }

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        var welcomeText = "Hello and welcome!";
        foreach (var member in membersAdded)
        {
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
            }
        }
    }
}

Next steps