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.
  • Helpers provides tools for event binding and modifying the DOM elements of the chatbot SDK.
  • 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. Any interaction between the user and the Chatbot is an action. 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.
  • onCustomTrigger: Subscription that can be used by implementations to ready data coming from the customTrigger action.
  • 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.
  • onDomReady: Subscription that triggers when the HTML DOM elements is rendered for the first time.
  • 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
  • onStartConversation: Subscription on startConversation, which triggers when the conversation request (session start) is done.
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);
}

Helpers

The purpose of this feature is to make it easier to customize the User Interface of the chatbot. Use helpers to detect the DOM elements of the Chatbot, modify them, and set bindings to its elements. This allows you to add Chatbot SDK actions to these custom features.

The "Helpers" property of the Chatbot Instance is intended for use inside the subscriptions of the adapter. There are three different methods:

setListener

The setListener method adds the events of the HTML DOM events to the selected target.

setListener uses JQuery on functionality internally to handle the event binding of the DOM elements. It generates a unique identifier that you can use to remove the listener.

chatbot.helpers.setListener(<selector>, <event>, <callback>,<extraData>)
  • selector String : JQuery selector to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
  • event String : One or more space-separated event types, such as "click" or "mouseover".

    • Special mention to elementExists: Use elementExists event when you expect the callback function to be triggered once the Chatbot SDK detects that the given selector DOM element exists. The chatbotSDK checks for this DOM elements every time the DOM element changes, in both the sideWindow and list of messages, and triggers the callback function when it detects that the element exists.
  • callback String: Function that is called when the event is triggered. It receives the listenerID, the DOM element that triggered the listener, and the optional extraData parameter.
  • extraData[Object]: Optional parameter to get as parameter in the callback function. You can add the chatbotInstance to have access to the chatbotSDK actions and helpers.

  • return String: Unique identifier.
contactClickListenerID = chatBot.helpers.setListener('#contactButton', 'click',startContact, chatBot);

In this example, you set the click event to the Dom element with the id contactButton: When the user clicks on the button, the startContact function is called with the following parameters:

startContact(contactClickListenerID, targetElement, extraData)

The targetElement is the DOM element that triggered the element, in this case the contact button. In extraData, you have the chatbotInstance, as you used the setListener to send this parameter.

getListeners

This function returns the list of the current listeners with their data bindings.

return: Array of objects, where the keys are the uniquelistenerID generated with the setListener function.

chatbot.helpers.getListeners()
removeListener

This function removes the listener with its corresponding uniquelistenerID. It removes the event binding and also removes the listener from the listenerList of the getListeners function.

chatbot.helpers.removeListener(listenerID)

listenerID: The unique identifier of the given listener. It is used to unbind the event.

Please see here an example of an adapter that uses the helpers functionality.

Custom HTML

The Chatbot SDK is built with a default HTML template that you can modify using the html: {} statement in the build method. 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.

The Chatbot SDK allows you to customize some UI components. A component is a piece of visual element, plus a design and a behaviour. 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

The following correct implementation has only one root element, the <div class="header__actions">.

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

The following incorrect implementation 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>'
    }
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
  • conversation-window-header: This template has the HTML of the conversation window 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 below. By default, it has both "minimize" and "close" buttons, but hides the "close" button, which can be modified in the close button 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/>
Side window header
  • side-window-header: This template has the HTML of the side window header (a inbenta-bot__bubble__header div) with a few components inside. By default, it will show the side-window-header-title atomic component and the side-window-header-buttons customizable component.
        side-window-header:
            <div class="inbenta-bot__bubble__header">
                <side-window-header-title />
                <side-window-header-buttons />
            </div>
  • side-window-header-buttons: This component has the HTML of the buttons (a header__actions div) in the side window, then the components explained below. By default, it has a "close" button, which is in charge of closing the side window.
        side-window-header-buttons:
            <div class="header__actions">
                    <side-window-header-close-button />
            </div>
  • conversation-window-header: This template has the HTML of the conversation window footer. By default, it will show the conversation-window-footer-form which has the following atomic components between its tags: upload-media-button, chatbot-input, character-counter, send-button
        conversation-window-footer:
            <conversation-window-footer-form>
                <upload-media-button />
                <chatbot-input />
                <character-counter />
                <send-button />
            </conversation-window-footer-form>
Available atomic components
Conversation window header
  • 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 the original again.
Side window header
  • side-window-header-title: Shows the side window title, which by default is the content title.

  • side-window-header-close-button: Closes the sideWindow, originally placed inside side-window-header-buttons.
  • character-counter: Shows the remaining available characters left, which is obtained from the length of the query in the chatbot-input and the inputCharacterCounterMax set parameter. It is hidden by default, configurable in the character counter configuration

  • chatbot-input: Input text the user can use to write to the Chatbot. In order to be able to send the text to the Chatbot, this atomic component needs to be placed inside the conversation-window-footer-form component (see example above).

  • send-button: Shows the send message button. On click, it will send the message written in the chatbot-input atomic component.

  • upload-media-button: This button lets the user upload a file and send it to tan external system. This button will only be shown when the showUpload action has been triggered.

  • conversation-window-footer-form: This component is the form which contains all the other atomic components and provides the footer styling. The component cannot be customized, but it is used to wrap all the needed components inside the footer.
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