SDK Customization

Introduction

While the configuration of the build method offers several ways to customize the Chatbot SDK, it is also necessary to allow external custom features.

In order to offer a fully programmable Chatbot, Inbenta implemented several functionalities:

  • Actions have a direct impact in the Chatbot. As the name indicates, they perform different actions, such as "build an answer", "open the main window", etc.
  • Subscriptions subscribe callbacks to specific actions. Their purpose is to set the Chatbot behavior; subscriptions make custom code available for the Chatbot SDK when a particular action occurs, to call other actions or to modify the data that the action sends to the state.
  • Adapters are functions that are passed to the adapter's array in the build configuration. The adapter function receives one argument: the Chatbot instance, which contains all available actions, the middleware subscriptions and the API client.
  • Custom HTML allows you to change the HMTL of several components to adapt their use and functionalities as required.

Click here to see a tutorial on how to build an adapter, along with a selection of fully functional examples that you can use and adapt to fit your specific needs.

Note: All examples shown are set in a Production environment.

Actions

Actions imply a change in the Chatbot instance. All interactions between the user and the Chatbot instance are actions. If previously subscribed, actions can trigger a subscription. (For more information, see the Subscriptions section.)

For a complete description of all actions, see the Actions list, or go directly to one of them:

Example

const messageData = {
  type:'answer',
  message:'Custom answer in conversationWindow',
}
chatBot.actions.displayChatbotMessage(messageData);

custom_answer

Subscriptions

Subscriptions allow you to subscribe callbacks to a specific action. This performs an action when a particular action occurs, or modifies the data that this action sends.

To do this, you must build them in a middleware system.

For a complete description of all actions, see the Subscriptions list or go directly to one of them:

  • onCloseSideWindow: Subscription on closeSideWindow.
  • onDisplayChatbotMessage: Subscription on displayChatbotMessage action, which can be used to modify the answer before displaying it.
  • onDisplaySystemMessage: Subscription on displaySystemMessage action, which can be used to modify the answer before displaying it.
  • onDisplayUserMessage: Subscription on displayUserMessage action, which can be used to modify the answer before displaying it.
  • onDownloadMedia: Subscription on downloadMedia action, which can be used to download media to the user's device.
  • onEscalateToAgent: Subscription on escalateToAgent action, which can be used to track when an external human-to-human chat should be initiated.
  • onHideConversationWindow: Subscription on hideConversationWindow, can be used to send the conversation transcript to an external service.
  • onRateContent: Subscription that will be triggered when the rateContent action is triggered, Inbenta SDK is subscribed, and performs the apiClient rateContent method with the given parameters.
  • onReady: Subscription that will be triggered once the build on the SDK is finished.
  • onResetSession: Subscription on resetSession action, which can be used to avoid reseting session or customize how the reset is done.
  • onSelectSystemMessageOption: Subscription on selectSystemMessageOption action, which can be used to execute custom code when the user selects a System Message option.
  • onSendMessage: Subscription on sendMessag action, can be used to modify the message sent to Inbenta API, or to send the message to another external service.
  • onShowConversationWindow: Subscription on showConversationWindow, can be used to show an special welcome message.
  • onShowSideWindow: Subscription on showSideWindow.
  • onUploadMedia: Subscription on uploadMedia action, which can be used to upload media from the user's device.
  • onSelectActionFieldOption: Subscription on selectActionFieldOption, which can be used to execute custom code when the user selects a fieldOption chatbotMessage
Subscription to actions

During the User-Chatbot interaction, different actions happen. When a subscribed action occurs, the Chatbot instance triggers the callback. The subscription gives information about the action it triggers and gives data related to this action.

Subscriptions execute custom js functions, so you can modify the data that this action created, and delay or cancel any further function subscribed to this action.

The actions subscription only accepts one parameter, which must be a function (the callback). This function is executed when the action is triggered. The callback receives parameters in the form of the data generated in the action (as described in the Actions section).

Middleware composition

To allow for the full customization of the Chatbot SDK, it must be possible to modify or remove the Inbenta Chatbot behavior. A callback subscribed to an action acts as a middleware: It is executed between the call for an action and the function that performs the action.

Inbenta allows you to perform several subscriptions on the same action. This builds a middleware chain. The chain follows a FIFO (First In, First Out) paradigm, which means the first subscribed action is the first that is executed.

Each callback is responsible for calling the next callback with the action data. If only one callback has been subscribed, the next callback executes the original action.

Example In the following example, we add the string "Custom Chatbot message" to any answer the Chatbot displays, unless the message displayed is a "no answer" response. To do this, we execute a return and we do not send the next() premise. In any other case, we will modify the displayed message and execute the next(), which allow the execution of the displayChatbotMessage action.

chatBot.subscriptions.onDisplayChatbotMessage(function(messageData, next) {
    // BEFORE DisplayChatbotMessage action execution
    if (messageData.message == "Sorry, we couldn't find any answers to your question."){
      console.log("hidden message")
      return
    } else {
      messageData.message = 'Custom chatbot message! ' + messageData.message;
      return next(messageData);
    }
});

See below the result of the subscription: The first and third message were modified, and the second message was not displayed, because the message matched the string. Finally, the displayChatbotMessage action was not executed since there was a return and the next() callback was not called.

subscription_example

Adapters

An adapter is a function that has access to the Chatbot object. The Chatbot object is passed as the only argument of the function.

function myAdapter(bot) {
  // subscribe middlewares
  bot.subscriptions.onDisplayUserMessage(myCallback);
}

You must provide the adapters in the configuration of the SDK build method. Add a property called adapters, which has to be an array. Adapters will be executed in the same order in the adapters array. There is no limit of how many adapters you want to use.

function myAdapter(bot) {
  // adapter stuff
}

const configuration = {
  // other configuration, like labels, environment...
  adapters: [
    myAdapter,
    otherAdapter
  ]
}

Note: The purpose of adapters is to create middleware chains with the subscribed middlewares before the Chatbot is created. It is not possible to subscribe or unsubscribe any middleware after the Chatbot creation, so the order of middlewares will be the same during the Chatbot runtime (i.e. after its creation). This way, the developer customizing the Chatbot can know the order of middlewares by checking the order of adapters.

Important considerations

Actions

As described above, you can subscribe middlewares to certain actions. With all middlewares subscribed for each action, the Chatbot creates a middleware chain that makes a function composition of the original action. This is done after the last adapter is executed. In order to make sure that all middlewares are in the middleware chain for any given action, actions will throw an error if they are called in the main scope of an adapter.

function myAdapter(bot) {
  bot.subscriptions.onSendMessage(
    function(messageData, next) {
      // This function scope is not the main scope of an adapter, so actions will work
      bot.actions.displaySystemMessage({message: 'Sending a message to the Inbenta API...'});

      return next(messageData);
    }
  );

  setTimeout(function() {
    // This function scope is not the main scope of an adapter, so actions will work
    bot.actions.displaySystemMessage({message: 'System message with 2 sec of delay'});
  }, 2000);

  // This scope is the main scope of an adapter, so the following line will throw an error
  bot.actions.displaySystemMessage({message: "This message won't be displayed, an error will be thrown instead"});
}
Subscriptions

In order to create the middleware chains only once during the build, adding subscriptions after the build will not add middleware in the middleware chain. For this reason, subscriptions only work in the main scope of an adapter.

function myAdapter(bot) {
  // In this scope (myAdapter main scope) subscriptions will work

  setTimeout(function() {
    // In this scope, which will be executed after finishing build process, subscriptions won't have effect.
    bot.subscriptions.onDisplayUserMessage(function(messageData, next) {
      messageData.message = 'This modification will not have effect, so the original message will be displayed';
      return next(messageData);
    });
  }, 1);
}

Custom HTML

The Chatbot SDK is built with a default HTML template that you can modify. Here is how you can create a Chatbot instance with your own custom HTML.

In the build method, a new configuration is available: the html:{} statement.

Like modern front-end UI libraries (React, Vue), Inbenta uses the concept of component. A component is a piece of visual element, plus a design and a behaviour.

The Chatbot SDK allows you to customize some components of its UI. An example of a customizable component would be conversation-window-header, which contains the necessary HTML to render the header of the conversationWindow.

On the other hand, there are also "atomic" components, such as the "reset session" button: it has its own HTML structure and uses CSS for its design and behaviour (It displays a systemMessage in a modal format). This component has its own HTML and functionality, and it is not possible to modify its HTML.

You can use all these components (atomic or not) inside the customizable components when you modify their HTML, and they will keep the original functionality.

Important: You can only insert one root element in the custom HTML. This is a technical restriction that can not be circumvented.

Example

Here is a correct implementation: there is only one root element, the <div class="header__actions">.

    config.html = {
        'conversation-window-header-buttons':
            '<div class="header__actions">'
                    +'<reset-session-button />'
            +'</div>'
    }

Here is an incorrect implementation: it will prompt an error and display the HTML incorrectly. If you need to add this custom label, you must add it inside the header actions or create a parent div element that can contain both header__actions and the custom label.

    config.html = {
        'conversation-window-header-buttons':
            '<div class="header__actions">'
                    +'<reset-session-button />'
            +'</div>'
            +'<div>Custom Label </div>'
    }
Available atomic components
  • hide-conversation-window-button: Minimizes/hides the conversationWindow, originally placed inside the conversation-window-header.

  • reset-session-button: Calls the displaySystemMessage action that asks for a confirmation before you leave the conversation. Upon confirmation, it resets the session, originally placed inside the conversation-window-header.

  • close-custom-conversation-window: When there is a custom-conversationWindow, hides it and shows again the original conversationWindow.
Customizable components

The HTML components that are now available for customizations are listed below. The original HTML of the components is provided and can be copied and modified to allow any custom feature desired.

conversation-window-header: This template has the HTML of the header. It is composed of two div elements. The first is inbenta-bot__chat__header, followed by header__title (which defines the title of the Chatbot SDK). After the second `div, there is the component.

        conversation-window-header:
            <div class="inbenta-bot__chat__header">
                <div class="header__title">
                    SDK Demo
                </div>
                <conversation-window-header-buttons />
            </div>

conversation-window-header-buttons: This component has the HTML of the buttons (a header__actions div), then the components explained above. By default, it has both "minimize" and "close" buttons, but hides the "close" button, which can be modified in the configuration.

        'conversation-window-header-buttons':
            <div class="header__actions">
                    <hide-conversation-window-button />
                    <reset-session-button />
            </div>

custom-window-header: This component is shown after displaying a custom-window, by default it will show the close-custom-conversation-window atomic component.

'custom-window-header':
  <close-custom-conversation-window/>
Custom examples
  • Custom conversation-window-header with an Star Wars icon:
    config.html = {
        'conversation-window-header':
            '<div class="inbenta-bot__chat__header">'
                +'<div class="header__title">'
                    +'<img src="custom_icon" style=" width: 30px; margin: 0; margin-right: 15px">'
                    +'SDK Demo'
                +'</div>'
                +'<conversation-window-header-buttons />'
            +'</div>',
    }

custom conversation-window

  • Custom conversation-window-header-buttons without the minimise button:
    config.html = {
        'conversation-window-header-buttons':
            '<div class="header__actions">'
                    +'<reset-session-button />'
            +'</div>'
    }

custom header-buttons

  • Custom conversation-window-header and conversation-window-header-buttons with an icon and modify the header-buttons to display a dropdown :

We use bootstrap to generate the dropdown menu, so we include the CDN to be able to use it.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    config.html = {
        'conversation-window-header':
            '<div class="inbenta-bot__chat__header">'
                +'<div class="header__title">'
                    +'<img src="custom_image" style=" width: 30px; margin: 0; margin-right: 15px">'
                    +'SDK Demo'
                +'</div>'
                +'<conversation-window-header-buttons />'
            +'</div>',

        'conversation-window-header-buttons':
            '<div class="header__actions">'
                +'<div class="dropdown">'
                      +'<div class="inbenta-bot-icon dropdown-toggle" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></div>'
                      +'<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">'
                        +'<a class="dropdown-item" href="#">Action</a>'
                        +'<a class="dropdown-item" href="#">Another action</a>'
                        +'<a class="dropdown-item" href="#">Something else here</a>'
                      +'</div>'
                    +'</div>'
                    +'<hide-conversation-window-button />'
                    +'<reset-session-button />'
            +'</div>'
    }

custom_html

  • Modified custom-window-header in order to use some of the conversation-window-header classes and attach a well formatted header title inside the custom-window, and also use the close-custom-conversation-window:
 config.html = {
     'custom-window-header':  '<div class="inbenta-bot__chat__header">'
        +'<div class="header__title">'
        +'Custom Inbenta form'
        +'</div>'
        +'<close-custom-conversation-window/>'
        +'</div>';
}

custom_ConversationHeaderhtml