Aura Bot architecture

Description of Aura Bot architecture and starting components

Introduction to Bot Framework service

A bot is an application, particularly an HTTP server, that allows interaction with users in a conversational way. Different means of interaction are available, such as text, cards with images, video or audio and speech.

Users interact with the bot through a given channel, that is the final application used directly by the users. For instance, the bot can be accessible from Facebook, WhatsApp or from any private application, such as My O2 or Mi Movistar. Every message exchanged between the user and the bot is called an activity.

Microsoft Bot Framework Service, that is the main component of Azure Bot Service, is responsible for sending the user’s request to the particular bot instance, in this case, to the corresponding instance of aura-bot. Channels communicating with a bot should include additional information required by this specific bot to handle the message in the body of the request, i.e., in the activity. aura-bot provides a definition of the channelData field of the activity, so channels are able to send and receive this information needed to handle Aura use cases.

aura-bot, as a bot based on Microsoft Bot Framework, understands different types of activities. The most basic types of activities used by aura-bot are:

  • Message: Means to transmit the user’s needs to the bot.
  • Event: Events are in charge of the end-to-end communication between aura-bot and channels. They are managed by the incoming-event-middleware.
  • ConversationUpdate: Activity that describes a change in conversation’s members, description, existence, etc.

Communication between BotFramework Service and aura-bot is based on HTTP. BotFramework Service sends an HTTP POST request to the bot, with the incoming activity from the user. The bot should respond with a 200 HTTP status code to this request. In aura-bot, this is executed before going on with the processing of the activity, so the echo message of the user is returned before the activity generated as an answer. These activities are sent to BotFramework Service as HTTP POST request, that is responded with 200 HTTP status code, too.

Once an activity arrives at the bot, the HTTP server should pass it to the BotFramework Adapter, that starts the activity processing.

At this moment, a turn starts, that refers to all the steps done from the moment that an activity enters the bot until all the answer activities are returned to the user. Each turn lays on a TurnContext, that holds all the information about the current activity. Bot Framework automatically includes in the TurnContext object some information, but aura-bot extends this object with extra information that makes it easier to provide a response to the user.

Please, check detailed information about Microsoft Bot Framework Service and the related SDK.

Aura Bot core architecture

The following diagram shows all the components running in any aura-bot instance. They are shown as they are loaded during bot start-up.

Legend
Blue boxes: singleton components
Yellow boxes within them: components that are handled the same way
Middlewares:
- M: mandatory ones
- O: optional, executed only if they are properly configured
Execution mode:
- F: execution flow stops when middleware fails
- T: execution continues when middleware fails

Aura Bot architecture

Aura Bot messages flowchart

As explained before, messages are a type of activities that manage requests from the users: aura-bot receives a request from a user and provides an answer back.

The following diagram include the messages flow through aura-bot.

Aura Bot message flow

Aura Bot starting components and modules: Aura orchestrator

Currently, aura-bot includes an orchestrator that controls how the system is loaded and indicates which components and modules are required for the bot start-up.

The orchestrator handles a sorted array of components that must implement a series of methods that may receive the bot configuration if necessary. Modules are then initialized asynchronously, although they were synchronous.

This behavior simplifies both the definition of the singleton modules and how the orchestrator initializes them, with a minimum loss of performance during start-up.

        const orderedModules = [ModuleA, ModuleB, ModuleC];
        // Instantiate the Service App.
        const appOrchestrator = new Orchestrator();
        // Add configuration, that will be required by almost all other modules
        appOrchestrator.setConfigurationManager(Configuration);
        // Add the dependent modules in order.
        appOrchestrator.addModules(sortedModules);
        // Initiate the App.
        await appOrchestrator.init();

The orchestrator module is created during the server start-up with the following parameters:

  • ConfigurationManager:

Class that handles aura-bot configuration, that is the component in charge of loading the environment variables.

  • Singleton Modules:

They are: MiniBotServiceMock, AuraChannelsConfiguration, AuraMongoDb.Connector, KpiHandler, HttpMonkeyPatcher, PluginManager, LocaleManager, AuraCacheManagement, AuraBotServer.

These modules are loaded in the given order. If any of these modules does not start correctly, aura-bot does not start. Otherwise, AuraBotServer is up and running at the end of the process. All these modules are available all along the bot.

  • MiniBotServiceMock is only started if the bot is running locally through Aura minibot.

How to define singleton modules in Aura

In order to develop a new singleton module that can be used by aura-bot both internally and during the dialog execution, developers should keep in mind the following issues:

  • All modules added to the orchestrator need and publish a static variable called instance, that holds the singleton instance of that module.
  • Modules must have a public static method: init, that could be async or not depending on the needs of the module start-up. This method creates the singleton instance (and sets it to then instance variable). This method should only be called once by module or must throw an exception. It is only called by the orchestrator.
  • It is also recommended to provide an empty private constructor that avoids the creation of new instances of the module.

An example of implementation is shown below:

export class Class1 {
    public static instance: Class1;
    private constructor() {
        // Stuff here...
    }
    public static async init(config: Configuration): Promise<Class1 > {
        if (!Class1 .instance) {
            Class1 .instance = new Class1 ();
            // More stuff here

            return Class1 .instance;
        } else {
            throw new Error('An instance of Class1  already exists');
        }
    }
}

To use any of these modules:

  • Import its dependency.
  • Access the module instance property and call the required method.

The following example shows how to get a text translated by the LocaleManager.

import { LocaleManager } from '@telefonica/aura-utilities/lib/aura-locale-manager';

LocaleManager.instance.getText('errors:error.invalidScopes', auraUser, corr);