WEB WIDGET CHAT BOT

The Web Widget Chat Bot allows integrating Teamwire as a chat widget into any web site. It is composed of the following main components:

  1. a chat bot (web_widget_bot.py), which provides the integration with the Teamwire platform
  2. a web server component, integrated within the bot for serving the web widget files and providing an interface for commands received from the widget
  3. a main web component (in the widget/src directory), which provides the main chat UI
  4. a JS script (in the widget/widget-script directory) capable of injecting into the target page a chat button and an iframe container for the chat ui

The source code of this bot is not part of the Teamwire SDK Bot Examples package, but it can be downloaded here:

Widget UI

The widget is implemented as an HTML page template (index.html) plus some vanilla JS code based on jQuery for DOM manipulation, and some basic CSS for styles. The source code for the chat widget is contained in the widget/src directory. The page, JS and CSS are bundled together through Webpack and made available in the widget/dist/bundles directory.

In order to simplify the integration of the widget into any web site, a reference to the widget/dist/widget-script.js script should be used. Its source files are contained in the widget/widget-script directory.

Supposing the bot is running on a publicly accessible server, for example at the url http://example.com/, in order to integrate the widget in your web page you simply have to add the following reference in your HTML:

<script src="http://example.com/widget-script.js?key=<site-key>"></script>

for example:

<script src="http://example.com/widget-script.js?key=b28b3065-6f95-2db6-9372-6482b1999c6f"></script>

The key argument in the query string is used for authentication and for selecting the correct configuration to be applied to the widget for the specific site it's embedded into. The widget-script.js file is served by the bot's internal web server and, once loaded in the host web page, it will place a button with the Teamwire logo at a fixed (sticky) position in the page. When the user clicks the button, the chat widget is presented, and a conversation can be started.

Example

Once the user enters their name and sends a first message, the message is relayed to the bot, which creates a new chat with the recipients specified in the bot configuration. Any recipient can then reply, so the bot will relay the response message to the chat widget, thus permitting the conversation between the user of the hosting web page and the Teamwire users invoved in the chat.

Bot configuration

The bot can be configured to serve the chat widget to multiple sites. The default configuration file name is config.json. The following example shows a configuration for serving the widget to two different sites:

{
    "configs": [{
        "key": "b28b5065-6f95-4db6-9272-6482b1899c6f",
        "allowed-origins": ["https://bots.teamwire.eu/widget-test/"],
        "title-primary": "How can we help you?",
        "title-secondary": "Hello,",
        "site_name": "TW Support",
        "chat_recipients": [{"first_name": "first_name", "last_name": "last_name"}],
        "permanent_session": true
    },
    {
        "key": "2f627450-821e-4a61-8931-63f2cf90427e",
        "allowed-origins": ["https://bots.teamwire.eu/testsite/"],
        "title-primary": "Chat with us",
        "title-secondary": "Hey!,",
        "site_name": "TW Website",
        "chat_recipients": [{"first_name": "first_name", "last_name": "last_name"}]
    }]
}

Each site configuration must provide the following properties

  • key: this should be a GUID, used to univokely identify the site
  • allowed-origins: in order to allow a site to use the widget, the Referer header is used for authentication, and it should prefix-match one of the values contained in this string array. In particular the Referer header received by the bot should contain any of the specified allowed-origins
  • title-primary: the primary title to be shown in the widget header
  • title-secondary: the secondary title to be shown in the widget header
  • site_name: a short handle for the site integrating the widget. This name will be used in chat titles
  • chat_recipients: a list of recipient names, to be matched with the names of existing Teamwire users
  • permanent_session: boolean (default: false). If true, the chat session created through the widget will be permanent, otherwise it will be based on the life-span of the browser tab that loaded the page.

Running the bot

In order to start the bot you simply have to run:

    $ python web-widget-bot.py

Please ensure that the following environment variables are correctly set:

  • TW_API_APPID
  • TW_API_SECRET
  • TW_API_HOST

Integrating the widget in a web site

Supposing the web widget bot is running and accessible at a url like

https://example.com/

the widget can be integrated in an html web page by simply adding the following script declaration:

<script src="https://example.com/widget-script.js?key=b28b5065-6f95-4db6-9372-6482b1999c6f"></script>

Running the bot behind a transparent proxy (e.g. nginx)

It is advised that the bot web service is placed behind a transparent proxy, in order to ensure proper SSL termination. Supposing the bot's web service is bound to port 8080 (the default port) the nginx configuration for the https://example.com site could be like the following:

location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_pass http://localhost:8080;
}

location /ws {
    proxy_pass http://localhost:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

As you can notice, we have to explicitly expose both the root / and the /ws locations with a proxy_pass directive.

In most cases, however it is necessary to expose the service at a public location different form the root /. For example we could expose the chat widget at the following public location:

https://example.com/web-widget/widget-script.js

The nginx configuration for the above location /web-widget could be like the following:

location /web-widget {
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_pass http://localhost:8080;
    }

    location /web-widget/ws {
       proxy_pass http://localhost:8080;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
    }

IMPORTANT NOTE: using this last configuration will not work unless the bot is instructed about its public location. This is done by setting the WEB_WIDGET_BOT_WEB_PREFIX environment variable before running the bot. For example, if the widget is made accessible through:

https://example.com/web-widget/widget-script.js

the WEB_WIDGET_BOT_WEB_PREFIX variable should be set as:

export WEB_WIDGET_BOT_WEB_PREFIX=/web-widget

The leading / is required

Running the bot with Docker

If you are Teamwire Partner, you can request access to the Teamwire's Harbor Registry where you can find a standard image of the Web Chat Widget bot to be directly deployed in your own staging or production environment. Please contact support@teamwire.eu for additional information about how to get access to Teamwire's Harbor Registry.

Provided that the above requirement is met, you can get the Web Chat Widget bot image with:

$ docker pull harbor.teamwire.eu/bots/web-widget-bot:1.6.0

then you can launch it with:

docker run -d --restart always --network host  --name web-widget-bot  \
    -e TW_API_APPID="<YOUR APP ID>"                 \
    -e TW_API_SECRET="<YOUR API SECRET"             \
    -e TW_API_HOST="https://backend.teamwire.eu"    \
    -e WEB_WIDGET_BOT_PORT="8081"                   \
    -e WEB_WIDGET_BOT_CONFIG="/config/config.json"  \
    -e WEB_WIDGET_BOT_WEB_PREFIX="/chat-widget"     \
    -v /path/to/config/dir:/config                  \
    harbor.teamwire.eu/web-widget-bot:1.6.0

The above docker run command creates a new container named web-widget-bot and passes all the required environment variables to it. In particular, the bot's web backend will listen on TCP port 8081, exposed at the URL path /chat-widget/. In other words, the chat widget script will be served at http://localhost:8081/chat-widget/widget-script.js. In order to make a custom configuration file available to the bot, a container volume is set, mounting the /path/to/config/dir external directory to the internal /config directory. The WEB_WIDGET_BOT_CONFIG environment variable ensures that the configuration file is loaded from the volume.

Creating a Docker image for the bot

NOTE: this is needed only in case you want to build your own Docker image for the bot, e.g. because you want to run your own customised version of it. In case you simply want to run the standard Docker image provided by Teamwire, please refer to the previous section.

To build a Docker image for the bot you need access to the Teamwire SDK docker image named bots/sdk-base which is available for Teamwire Partners on the Teamwire's Harbor Registry.

Provided that the above requisite is met, the Docker image for the bot can be created with:

$ docker build --rm --no-cache -t teamwire/web-widget-bot -f Dockerfile .

This will create the teamwire/web-widget-bot Docker image, which can then be deployed in a Docker environment, like detailed in the previous section.

For example:

docker run -d --restart always --network host  --name web-widget-bot  \
    -e TW_API_APPID="<YOUR APP ID>"                 \
    -e TW_API_SECRET="<YOUR API SECRET"             \
    -e TW_API_HOST="https://backend.teamwire.eu"    \
    -e WEB_WIDGET_BOT_PORT="8081"                   \
    -e WEB_WIDGET_BOT_CONFIG="/config/config.json"  \
    -e WEB_WIDGET_BOT_WEB_PREFIX="/chat-widget"     \
    -v /path/to/config/dir:/config                  \
    teamwire/web-widget-bot

Customising the Chat Widget

Widget build system

The chat widget sources are all contained under the widget directory, which in particular contains the following subdirectories:

  • widget-script: sources for widget-script.js, which is ultimately integrated into the target web page and which is responsible of injecting the chat button and chat widget iframe into it
  • src: sources for the chat widget proper, which is loaded into the iframe

The build system for these components, which generates the scripts and bundles in the dist directory is based on Webpack and one custom Node.js script.

Requisites

  • Node.js
  • npm or yarn

Install dependencies

cd widget
npm install

Rebuilding the widget components

For development builds, use:

npm run build-dev

For production builds, use:

npm run build-prod

The build artifacts will be generated in the dist directory.

Technical notes

Widget to bot protocol

The web-widget-bot acts as a gateway between the chat widget and the Teamwire backend, implementing chat creation, messages loading and message reception through the Teamwire SDK. Moreover it provides all functionality for properly serving the widget assets to the web pages the widget is integrated into.

The communication between the widget and the bot, and vice-versa, is completely handled through a websocket channel, over which a basic JSON-RPC protocol is implemented. This ensures both real time communication and the ability to quickly identify situations involving a loss of connectivity.

The JSON command format is the following:

  • id: progressive numeric command identifier. Subsequent commands will have increasing command ids
  • method: string, name of the command to be performed
  • params: dict, any JSON-serializable data

example:

{
    "id": 1,
    "method": "send_message",
    "params": {
        "sessionId": "aabbccddeeff",
        "text": "hello"
    }
}

Responses to commands must repeat back the command id received, additionally they will contain response or error data. Example:

{
    "id": 1,
    "result": {
        "success": true
    }
}

or for example in case of error:

{
    "id": 1,
    "error": "backend disconnected"
}

Please see /widget/src/js/ws.js, /widget/src/js/api.js for additional details on the client component implementation.

Messages reception

The chat widget, besides the ability to send commands to the bot, is subscribed to "response_message" events, which are dispatched by the bot to the widget through the websocket channel any time it receives a new text message.

See /widget/src/js/api.js.

Note: the websocket connection is initiated every time the widget is open, and it is terminated within an interval of 5s after the widget is closed (i.e. if the widget is closed and reopened within 5s the websocket is not affected).

Button and widget injection script

Once loaded in the target web page, the integration script will inject the chat button and the iframe holding the widget into it. In particular, as visible in widget/dist/widget-script.js, the button, iframe, and css style elements are created in the page by injecting html and css code at the appropriate places inside of the page's DOM. The html and css code used to construct the DOM elements are contained as simple strings in widget-script.js.

In order to ease the development of such injected components, the final widget-script.js script is built in a custom way by the widget build system.

In particular, its source code can be found in the widget/widget-script directory, which contains the following:

  • widget-script-template.js: template of widget-script.js
  • widget-button-iframe.html: html code for the chat button and iframe
  • widget-style.css: button and iframe styling

The same directory also contains the build-widget.js node.js script, which is used ot build the widget script from the three files above by minimising both the html and the css sources and embedding them as strings in the template.

The build-widget.js script is automatically invoked any time the widget is rebuilt