How to Add Live Support Chat to Your Web Application with JavaScript

June 30, 2017
Written by

Programmable Chat setup

Do you need live customer support chat in your web application but don’t know how to go about building it? You’ve landed in the right place. Twilio Programmable Chat can help you create the support functionality that you envision for your business.

Let’s walk step-by-step through quickly coding a multi-party chat application.

Demo App and Code

You can create your own version of the project to get started by clicking on “Remix” button on the demo app.

Screen Shot 2017-06-08 at 9.57.53 AM.png

Starting from Scratch

If you want to start from a blank project instead of using the Glitch Remix feature, follow along with these steps.

Head over to Glitch and you will set up with a new environment. Open up package.json – a file which provides information about your project. Click the “Add package” button and add the twilio, http, path and request modules. Glitch will install the modules as soon as you click on each search result.

Screen Shot 2017-06-08 at 2.57.38 PM.png

Now click server.js on the sidebar and then delete the contents of the file. While some of it is useful, we’ll start fresh. Include the following packages in our project, which we do using require in Node.js.

var express = require('express')
Var config  = require('./config');
var twilio = require('twilio');
var http = require('http');
var path = require('path');

Time to set up our Express application.  The express module is installed by default.

// Create an Express web app
var app = express();

// Create and start an HTTP server to run our application
var server = http.createServer(app);
server.listen(config.port, function() {
    console.log('Express server running on port ' + config.port);
});

// export the HTTP server as the public module interface
module.exports = server;

Test the server by adding a “/helloworld” endpoint, shown in the following highlighted lines:

// Create an Express web app
var app = express();

app.get('/helloWorld', function (req, res) {
  res.send('Hello World')
})

// Create and start an HTTP server to run our application
var server = http.createServer(app);
server.listen(config.port, function() {
    console.log('Express server running on port ' + config.port);
});

// export the HTTP server as the public module interface
module.exports = server;

Go to your browser and test this endpoint by going to “/helloWorld”.

(In my case https://twilio-chat-qs.glitch.me/helloWorld).
You should see “Hello World” as a response

Now that we have the server up and running, we can get to building the chat application.

What will our chat app look like?

The screenshots of the Login and Chat modules below show what we will build.

Login Module

The Login module collects information for identifying:

  • chat member
  • chat channel

This module can be built with a higher level of authentication using Passwords, LDAP or Authy Phone Verification etc. But in this example, we are just going to let the user put in their information and start chatting.

Chat Modules – Showing Two Participants.

Application Components

A complete secure chat app typically has 2 components

  1. Server, that vouches for the identity of clients and grants them specific permissions to perform various functions in a chat
  2. Chat clients (web or mobile), that act as the User Interface for communication

Let’s set up the server component for access and authentication of chat clients to Twilio. The below diagram shows the flow for authenticating a chat client by requesting an access token from your server. The server obtains this token from Twilio by authenticating itself as a valid instance of Twilio Programmable Chat Service.

Access tokens are the way to grant permissions and authentication to chat clients when using Twilio Programmable Chat. You need the following information to generate an access token:

  • Twilio Account SID. Go to your Programmable Chat dashboard. In the top right-hand corner, click Show API Credentials. (You will not use the Auth Token. More on that below.)
  • Chat Service SID. Each Twilio Programmable Chat app has its own channels, conversations, message history and participants. You’ll need a Chat Service SID to identify which Chat app you’d like to use. On the Chat dashboard, click Manage Your Programmable Chat Service Instances, create a new IPM Service
  • Twilio API Keys. If you’ve worked with Twilio before, you may be used to copying the AUTH_TOKEN along with the ACCOUNT_SID. The problem with a single authentication token is that if it gets compromised (e.g. you accidentally commit it to a public repo) then you have to change it in all of your Twilio apps. Instead, we’ll create a set of API keys for use by this app only. Visit your API keys dashboard, and create a set of API Keys
  • Identity. Since a Twilio Chat Client can be an app on multiple devices, you should assign an identity, representing a user of chat, and endpoint, representing a device/app for that user. If you want to allow a user to log in from multiple devices and apps, design your endpoint accordingly.

Once you have these values , go to .env file in your project and fill in the values.

# Your primary Twilio Account SID
TWILIO_ACCOUNT_SID=

# API Key/Secret Pair - generate a pair in the console
TWILIO_IPM_API_KEY=
TWILIO_IPM_API_SECRET=
# Your Chat service instance SID
TWILIO_IPM_SERVICE_SID=

Twilio uses these credentials to create a secure token to pass to the client. We will dive into the code as needed.

Go to server.js and add the code shown below.

/* Generate an Access Token for a chat application user - it generates a random
   username for the client requesting a token, and takes a device ID as a query
   parameter.
*/
var AccessToken = require('twilio').AccessToken;
var IpMessagingGrant = AccessToken.IpMessagingGrant;
var twiliAccntInfoFromFile=config.getTwiliAccountSettingsfromFile ;

app.get('/token', function(request, response) {
    var identity = request.query.identity;
    var endpointId = request.query.endpointId;
    // Create a "grant" which enables a client to use IPM as a given user,
    // on a given device
    var ipmGrant = new IpMessagingGrant({
        serviceSid: TWILIO_IPM_SERVICE_SID,
        endpointId: endpointId
    });
    // Create an access token which we will sign and return to the client,
    // containing the grant we just created
    var token = new AccessToken(TWILIO_ACCOUNT_SID,
                                TWILIO_IPM_API_KEY,
                                TWILIO_IPM_API_SECRET);
    token.addGrant(ipmGrant);
    token.identity = identity;
    // Serialize the token to a JWT string and include it in a JSON response
    response.send({
        identity: identity,
        token: token.toJwt()
    });
});

Client Component for enabling Users to Chat

We’ll create a very basic user interface for the chat clients. Since there are multiple UI frameworks available, I will keep it simple and leave further polishing to you.

In our example, we used Bootstrap Builder to quickly put together a page for the user to enter their name, a channel name and channel privacy. You can grab the getting started template here.

The login module screen is formed of the following elements in chat.html:

  • A drop down to collect information on Channel Visibility
  • A unique identifier to be associated with a chat member
  • Name of the Channel
  • An actionable link, in the form of a button, to consume information from above 3 elements and pass it to the server component to get authentication token and initialize chat client. The button also serves the purpose of hiding login module and surfacing up the chat module (since this is a one-page app)

onclick="startChat();showChatWindow();hideSignInWindow()"

Our chat modules and chat window:

Here’s what our chat messages will look like.

Chat messages are added dynamically, so the static HTML elements are those for chat window.

<!-- START : CHAT SECTION  -->
<section id="chat-section" >
 
<div class="chatArea" id="chatWindow" hidden="true">
 
     <!-- start chat-history -->
      <div class="scrollable chatMessages" id="chatHistory" >
      </div>
      <!-- end chat-history -->
 
      <div class="typingArea">
          <div >
              <input id="chatInput" name="chatInput" type="text" placeholder="Type your Message" class="form-control input-md" autofocus>
          </div>
          <div>
              <label id="statusMessages"></label>
          </div>
       </div>
    </div>
</section>

chatHistory is where we will dynamically add HTML elements to display the new/old messages using chat’s listener events and JavaScript.

Now that we have the skeleton ready, we can get it working with Twilio Programmable Chat.

To use the Javascript SDK, include this code in your webpage:

<script src="https://media.twiliocdn.com/sdk/js/common/v0.1/twilio-common.min.js"></script>
<script src="https://media.twiliocdn.com/sdk/js/chat/v0.11/twilio-chat.min.js"></script>

On click of the button to start chat (in chat.html) the login module is hidden and the chat module is shown.

onclick="startChat();showChatWindow();hideSignInWindow()"

Get an authentication token in chat.js by passing the memberName.
       

if (memberName === "") {
  memberName = "anonymous"+timeStamp;
}
  endpointId = memberName+':'+timeStamp;
  getTokenAndSetupChat(memberName,endpointId).then(
        /* 1 . Initialise Chat Client
           2 . Join existing channel or create one 
        */
  );

Also handle getTokenAndSetupChat in chat.js:

function getTokenAndSetupChat(memberName,endpointId)
  {
    return new Promise(function(resolve, reject)
    {
      $.get('/token?identity=' + memberName + '&endpointId=' + endpointId, function( data )
               {
                     resolve(data);
                     console.log(data);
                }
          );
    });
  }

Next initialize the chat client in chat.js.

chatClient = new Twilio.Chat.Client(data.token);
chatClient.initialize().then(
    /* Join existing channel or create one */
);

Get a channel for the chat instance:

client.getChannelByUniqueName(channelName)
      .then(function((chosenChannel) {
          // Channel Exists - so Join,
          // get history of chat
          // and start listening to new messages
       }
       .catch(function(err) {
          // Channel Does not exist
          // so create one , join
          // and start listening to new messages
       }

If a channel already exists, join that channel:

myChannel.join().then(function(channel) {
  // Show on UI that channel has been joined
  //Retrieve some latest messages - say 5
});

If a channel does not exist, create and join channel:

client.createChannel({
            uniqueName: channelName,
            friendlyName: channelName,
            isPrivate        : flagIsPrivate
}).then(function(createdChannel) {
  // Show on UI that channel has been joined
  // Listen to new messages
  // Join the channel and retrieve latest messages ,
  // say 5 latest messages

Again in chat.js, on joining a channel, retrieve chat history.

myChannel.getMessages(5)
         .then(function(latestPage) {
            console.log(latestPage)
            for (i=0;i<5;i++) {
              printMessage(latestPage.items[i].author, latestPage.items[i].body);
            }
          }
);

Listen to relevant event handlers (more about channel events are mentioned at the end of this document). For brevity sake, I am keeping this simple by listening to only the minimum set of events required for creating our functional chat application.

Show the user interface message about joining of channel:

$('#statusMessages').text('Joining channel ' + channel.friendlyName);

With above three steps, the overall code in the getTokenAndSetupChat in chat.js flow would look like below:

client.getChannelByUniqueName(channelName)
.then(function(chosenChannel)
  {
    myChannel=chosenChannel;
    myChannel.join().then(function(channel)
    {
        getHistory(channel.friendlyName);
    });
    myChannel.on('messageAdded', function(message)
    {
      if ( message.author != memberName)
      printMessage(message.author, message.body)
    });
  }
)
.catch(function(err)
           {
             if ( channelType === "1")
                {
                  flagIsPrivate = false ;
                }
            else
               {
                 flagIsPrivate = true ;
               }
             <em>/*channel does not exists .Will create your channel now */</em>
             client.createChannel({
                        uniqueName: channelName,
                        friendlyName: channelName,
                        isPrivate        : flagIsPrivate
                    }).then(function(createdChannel)
                    {
                        console.log(createdChannel);
                        myChannel=createdChannel;
                        myChannel.on('messageAdded', function(message)
                            {
                              if ( message.author != memberName)
                              printMessage(message.author, message.body)
                            });
                            myChannel.join().then(function(channel)
                            {
                                $('#statusMessages').text('Joining channel '   channel.friendlyName);
                                getHistory(channel.friendlyName);
                            });    
                    });
           }
)

Start Chatting

We can now use Twilio Programmable Chat powered chat client sitting in your webpage to start sending, receiving the message. These are the steps to do that

Listen to “typingArea”, and when a user presses enter, send the message out:

$('#chatInput').on('keydown', function(e)
{
  if (e.keyCode === 13)  // 13 is the enter key.
     {
        var msg = $('#chatInput').val();
        if (msg != '' )
         {
          sendMessage(msg);
          $('#chatInput').val('');
 
         }
     }
  else if (myChannel) { myChannel.typing(); }
}
);

Send a message:

// Send a message to a previously created Channel
myChannel.sendMessage(msg);

We can also display messages on the chat history area. This is where you dynamically add HTML content into the ‘#chatHistory’. For better presentation, we are going to align messages from the chatting member on the left and messages received from everyone else on the right.
We will include the following information on each message:

  • An avatar
  • Message Sender ( empty if it’s the window of member itself)
  • Actual Message

function printMessage(auth,msg)
{
 
     chatHistoryDiv=$('#chatHistory') ;
 
    if (auth === memberName )
      {
        chatHistoryDiv.append(
        "<div class='chat-message bubble-left' >"+
          "<div class='avatar'>"+
               "<img src='img/minionBob.png' alt='' width='32' height='32'>"+
          "</div>"+
          "<div class='chat-message-content'>" +
            "<h5>"+msg+"</h5>"+
          "</div> <!-- end chat-message-content -->"+
        "</div> <!-- end chat-message -->"
      );
 
 
 
      }
    else
    {
      chatHistoryDiv.append(
      "<div class='chat-message bubble-right'>"+
        "<img src='img/minionKevin.png' alt='' width='32' height='32'>"+
        "<div class='chat-message-content'>" +
          "<p font-size='50%' font-style= 'italic'>"+auth+"</p>"+
          "<h5>"+msg+"</h5>"+
        "</div> <!-- end chat-message-content -->"+
      "</div> <!-- end chat-message -->"
       );
    }
    $("#chatHistory").animate({ scrollTop: $("#chatHistory")[0].scrollHeight}, 1000);
 
}

As shown in the images above, we have added a bit of CSS to make these messages look a bit better. The full CSS is available here.

That’s it really, you have your multi-party chat app ready and functional.
 
Some of the features that we have not added for the sake of simplicity of this tutorial, but you can easily add, are inviting other members and accepting invites. The code snippets for these part are shown below.

Invite Others to Your Channel

// Invite another member to your channel</em>
myChannel.invite('AM').then(function() {
 console.log('AM  has been invited!');
});
myChannel.invite('AS').then(function() {
 console.log('AS has been invited!');
});

Join Channel

Authenticated members joining existing channels:

// Join a previously created channel
invitedChannel.join().then(function(channel) {
console.log('Joined channel ' + channel.friendlyName)
});

You can also listen to invites and perform join as an action to invite:

// Listen for new invitations to your Client
messagingClient.on('channelInvited', function(channel) {
console.log('Invited to channel ' + channel.friendlyName);
// Join the channel that you were invited to
channel.join();
});

Handling Channel Events

JavaScript SDK provides events that are triggered by various actions allowing your app to perform relevant actions in the UI(example: listening for new messages on the channel appending it to the conversation).
Events available currently are

  • Member events: memberInfoUpdated, memberJoined, memberLeft, memberUpdated —> Perform UI actions to show members joining, leaving, updating status etc
  • Message events: messageAdded, messageRemoved, messageUpdated —> Show messages on the conversation window
  • Typing events: typingEnded, typingStarted —> Indicate when a member is typing
  • Channel attribute event: updated —> Update the conversation window if admin changes channel details

What’s Next?

You’ve built your basic web chat app and are ready to add some sweet new features. Here are a few more resources to help you get there:

  • Using Taskrouter to create a contact center chat experience between agents and customers
  • Add a Bot Engine to this chat application

 

Thanks for reading! If you build something cool using Programmable Chat, or if you have any questions, I’d love to hear about it. Please drop me a line on Twitter – @abhijitMehta or email me at amehta@twilio.com.