Implementing a Chat Bubble in SUSI.AI

SUSI.AI now has a chat bubble on the bottom right of every page to assist you. Chat Bubble allows you to connect with susi.ai on just a click. You can now directly play test examples of skill on chatbot. It can also be viewed on full screen mode.

Redux Code

Redux Action associated with chat bubble is handleChatBubble. Redux state chatBubble can have 3 states:

  • minimised – chat bubble is not visible. This set state is set when the chat is viewed in full screen mode.
  • bubble – only the chat bubble is visible. This state is set when close icon is clicked and on toggle.
  • full – the chat bubble along with message section is visible. This state is set when minimize icon is clicked on full screen mode and on toggle.
const defaultState = {
chatBubble: 'bubble',
};
export default handleActions(
 {
  [actionTypes.HANDLE_CHAT_BUBBLE](state, { payload }) {
     return {
       ...state,
       chatBubble: payload.chatBubble,
     };
   },
 },
 defaultState,
);

Speech box for skill example

The user can click on the speech box for skill example and immediately view the answer for the skill on the chatbot. When a speech bubble is clicked a query parameter testExample is added to the URL. The value of this query parameter is resolved and answered by Message Composer. To be able to generate an answer bubble again and again for the same query, we have a reducer state testSkillExampleKey which is updated when the user clicks on the speech box. This is passed as key parameter to messageSection.

Chat Bubble Code

The functions involved in the working of chatBubble code are:

  • openFullScreen – This function is called when the full screen icon is clicked in the tab and laptop view and also when chat bubble is clicked in mobile view. This opens up a full screen dialog with message section. It dispatches handleChatBubble action which sets the chatBubble reducer state as minimised.
  • closeFullScreen – This function is called when the exit full screen icon is clicked. It dispatches a handleChatBubble action which sets the chatBubble reducer state as full.
  • toggleChat –  This function is called when the user clicks on the chat bubble. It dispatches handleChatBubble action which toggles the chatBubble reducer state between full and bubble.
  • handleClose – This function is called when the user clicks on the close icon. It dispatches handleChatBubble action which sets the chatBubble reducer state to bubble.
openFullScreen = () => {
   const { actions } = this.props;
   actions.handleChatBubble({
     chatBubble: 'minimised',
   });
   actions.openModal({
     modalType: 'chatBubble',
     fullScreenChat: true,
   });
 };

 closeFullScreen = () => {
   const { actions } = this.props;
   actions.handleChatBubble({
     chatBubble: 'full',
   });
   actions.closeModal();
 };

 toggleChat = () => {
   const { actions, chatBubble } = this.props;
   actions.handleChatBubble({
     chatBubble: chatBubble === 'bubble' ? 'full' : 'bubble',
   });
 };

 handleClose = () => {
   const { actions } = this.props;
   actions.handleChatBubble({ chatBubble: 'bubble' });
   actions.closeModal();
 };

Message Section Code (Reduced)

The message section comprises of three parts the actionBar, messageSection and the message Composer.

Action Bar

The actionBar consists of the action buttons – search, full screen, exit full screen and close button. Clicking on the search button expands and opens up a search bar. On clicking the full screen icon openFullScreen function is called which open up the chat dialog. On clicking the exit icon the handleClose function is called, which set chatBubble reducer state to bubble. On full screen view, clicking on the exit full screen icon calls the closeFullScreen functions which sets the reducer state chatBubble to full.

   const actionBar = (
     <ActionBar fullScreenChat={fullScreenChat}>
       {fullScreenChat !== undefined ? (
         <FullScreenExit onClick={this.closeFullScreen} width={width} />
       ) : (
         <FullScreen onClick={this.openFullScreen} width={width} />
       )}
       <Close onClick={fullScreenChat ? this.handleClose : this.toggleChat}/>
     </ActionBar>
   );

Message Section

The message section has two parts MessageList and Message Composer. Message List is where the messages are viewed and the Message composer allows you to interact with the bot through text and speech. ScrollBar is imported from the npm library react-custom-scrollbars. When the scroll bar is moved it sets the state of showScrollTop and showScrollBottom in the chat. messageListItems consists of all the messages between the user and the bot.


   const messageSection = (
     <MessageSectionContainer showChatBubble={showChatBubble} height={height}>
       {loadingHistory ? (
         <CircularLoader height={38} />
       ) : (
         <Fragment>
           {fullScreenChat ? null : actionBar}
           <MessageList
             ref={c => {
               this.messageList = c;
             }}
             pane={pane}
             messageBackgroundImage={messageBackgroundImage}
             showChatBubble={showChatBubble}
             height={height}>
             <Scrollbars
               renderThumbHorizontal={this.renderThumb}
               renderThumbVertical={this.renderThumb}
               ref={ref => {
                 this.scrollarea = ref;
               }}
               onScroll={this.onScroll}
               autoHide={false}>
               {messageListItems}
               {!search && loadingReply && this.getLoadingGIF()}
             </Scrollbars>
           </MessageList>
           {showScrollTop && (
             <ScrollTopFab
               size="small"
               backgroundcolor={body}
               color={theme === 'light' ? 'inherit' : 'secondary'}
               onClick={this.scrollToTop}
             >
               <NavigateUp />
             </ScrollTopFab>
           )}
           {showScrollBottom && (
             <ScrollBottomContainer>
               <ScrollBottomFab
                 size="small"
                 backgroundcolor={body}
                 color={theme === 'light' ? 'inherit' : 'secondary'}
                 onClick={this.scrollToBottom}>
                 <NavigateDown />
               </ScrollBottomFab>
             </ScrollBottomContainer>
           )}
         </Fragment>
       )}
       <MessageComposeContainer
         backgroundColor={composer}
         theme={theme}
         showChatBubble={showChatBubble}>
         <MessageComposer
           focus={!search}
           dream={dream}
           speechOutput={speechOutput}
           speechOutputAlways={speechOutputAlways}
           micColor={button}
           textarea={textarea}
           exitSearch={this.exitSearch}
           showChatBubble={showChatBubble}
         />
       </MessageComposeContainer>
     </MessageSectionContainer>
   );

 const Chat = (
     <ChatBubbleContainer className="chatbubble" height={height} width={width}>
       {chatBubble === 'full' ? messageSection : null}
       {chatBubble !== 'minimised' ? (
         <SUSILauncherContainer>
           <SUSILauncherWrapper
             onClick={width < 500 ? this.openFullscreen : this.toggleChat}>
             <SUSILauncherButton data-tip="Toggle Launcher" />
           </SUSILauncherWrapper>
         </SUSILauncherContainer>
       ) : null}
     </ChatBubbleContainer>
   );

Resources

Continue ReadingImplementing a Chat Bubble in SUSI.AI

Limit the Chatbots to Run on Specified Domains

SUSI botbuilder enables users to make their own private skill and deploy a chatbot widget in their websites. Users can copy paste a javascript code into their website’s source code to activate the bot. But what if someone copies that code from your website and put it in their own website? You won’t want the chat bot to work for such users in some cases. Thus we have a feature through which the bot creator can restrict the usage of the chatbot to only certain domains. The chat bot will not work from other domains.

Understanding the APIs Used

In working of the chatbot, there are mainly two APIs used from the server which play a mainstream role. The first API is the cms/getSkillMetaData.json API. It is used to get the design and configurations of the chatbot. The second API is the susi/chat.json API. It is used to get responses from the server applying the private skill. By restricting the chatbot usage we try to restrict the usage of these two APIs. Also, on the client side we display the chatbot only if the server sends a valid response indicating that the chatbot is legitimate. However, this can be circumvented if the person modifies the javascript of the chatbot. Hence, we need to secure the above two APIs. We check the domain from where the request is coming by checking the referer field in the request’s header.

Securing the APIs

In each of the above two APIs, we check if the bot owner has checked “allow bot only on own site”. If no, then the APIs can be accessed from any domain we need not check. If selected yes, then we need to check if the current site’s domain is allowed in the allowed sites list. For this, we extract the current domain from the request’s referer field. The allowed sites list is fetched from the configure object of that skill.

public static boolean allowDomainForChatbot(JSONObject configureObject, String referer) {
    Boolean allowed_site = true;
    if (configureObject.getBoolean("allow_bot_only_on_own_sites") && configureObject.has("allowed_sites") && configureObject.getString("allowed_sites").length() > 0) {
        allowed_site = false;
        if (referer != null && referer.length() > 0) {
            String[] sites = configureObject.getString("allowed_sites").split(",");
            for (int i = 0; i < sites.length; i++) {
                String site = sites[i].trim();
                int referer_index = referer.indexOf("://");
                String host = referer;
                if (referer.indexOf('/',referer_index+3) > -1) {
                    host = referer.substring(0,referer.indexOf('/',referer_index+3));
                }
                if (host.equalsIgnoreCase(site)) {
                    allowed_site = true;
                    break;
                }
            }
        }
    }
    return allowed_site; 
}

 

Result

Not allowed from other domains:
(For getSkillMetaData.json API)

Allowed on approved domains:
(For getSkillMetaData.json API)

Resources

 

Continue ReadingLimit the Chatbots to Run on Specified Domains

Applying Private Skill as Web Bots in SUSI Chat

Along with public skills, we now have private skills as web bots! Users can create their own private skills which can be used only by them and in the chatbots deployed by them. The SUSI Server accepts parameters to identify a valid private skill and applies that private skill for a particular chat. It then executes the query and sends the response to the client. This blog explains how private skill is applied in the SUSI Chat.

Understanding the API

The API to receive response from SUSI is /susi/chat.json. For applying only the public skills, we can send a request like /susi/chat.json?q=hello. Here only one parameter “q” is involved in which we send the query. However, for a private skill, the following parameters are involved:

  • privateskill – when the client sends this parameter, it indicates to use a private skill.
  • userid – the userid of the user who has created the private skill
  • group – the group name of the private skill
  • language – the language of the private skill
  • skill – the skill name of the private skill

Thus, the four parameters userid, group, language, skill serves to uniquely identify a private skill.

Fetching the private skill

After the client sends the appropriate parameters to apply a private skill, the server must actually apply the private skill. This is done in a similar manner how persona and dream skills are applied. The First step is the fetch the private skill from the susi_private_skill_data folder.

// read the private skill
File private_skill_dir = new File(DAO.private_skill_watch_dir,userId);
File group_file = new File(private_skill_dir, group_name);
File language_file = new File(group_file, language);
skillfile = SusiSkill.getSkillFileInLanguage(language_file, skill_name, false);
String text = new String(Files.readAllBytes(skillfile.toPath()), StandardCharsets.UTF_8);

Applying the private skill

After we have fetched the private skill, the next step is to apply it. To do this, we create a SusiMind object and add it to the general minds variable. Thus the skill will be applied to the particular chat with the highest priority, since it will be the first skill to be added to the minds variable. Later, the public skills can be added to the minds variable, thus their priority will be lower than the private skill.

String text = new String(Files.readAllBytes(skillfile.toPath()), StandardCharsets.UTF_8);
// fill an empty mind with the private skill
SusiMind awakeMind = new SusiMind(DAO.susi_chatlog_dir, DAO.susi_skilllog_dir); // we need the memory directory here to get a share on the memory of previous dialoges, otherwise we cannot test call-back questions
JSONObject rules = SusiSkill.readLoTSkill(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)), SusiLanguage.unknown, dream);
awakeMind.learn(rules, skillfile);
SusiSkill.ID skillid = new SusiSkill.ID(skillfile);
SusiSkill activeskill = awakeMind.getSkillMetadata().get(skillid);
awakeMind.setActiveSkill(activeskill);
// we are awake!
minds.add(awakeMind);

 

Result

Example API: http://localhost:4000/susi/chat.json?q=hi&privateskill=1&userid=17a70987d09c33e6f56fe05dca6e3d27&group=Knowledge&language=en&skill=knowprides

The skill exists in the correct location:

The skill file content is:

Thus, on sending the query “tell me” with the other parameters, we get the correct reply i.e “yes sure” from the server:

Resources

 

 

Continue ReadingApplying Private Skill as Web Bots in SUSI Chat

Store User’s Chatbots in SUSI Server

Users can create their own private skill which corresponds to their susi bot in SUSI Server. We store these private skills inside the susi_private_skill_data directory. We also store the information of the chatbots of each user in chatbot.json file inside the local storage of the server. It contains the list of chatbots created by each user against their UUIDs. This makes it easier and more organised to retrieve the chatbots of the users and their associated settings. This blog explains how the chatbots are being saved in the chatbot.json file in the SUSI server.

Receiving Private Skill from client

The client can create a private skill by accessing the /cms/createSkill.txt API. Along with the other parameters used to create a skill, it also has to send private=1 so that the server can recognise that this is a private skill. The private skills are stored in the folder susi_private_skill_data. The API is made by the CreateSkillService.java file.

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setHeader("Access-Control-Allow-Origin", "*"); // enable CORS
    String userEmail = null;
    JSONObject json = new JSONObject();
    Part imagePart = req.getPart("image");
    if (req.getParameter("access_token") != null) {
        if (imagePart == null) {
            json.put("accepted", false);
            json.put("message", "Image not given");
        } 

 

Getting skill parameters and Saving skill locally

The client sends various parameters related to the skill. Such as the group name, language, skill name, image, etc. Also to identify the skill as a private skill, it needs to send private=1 parameter. If it is a private skill, then we call the function storePrivateSkillBot().

if(privateSkill != null){
    this.storePrivateSkillBot(userId, skill_name, group_name, language_name);
    try (Git git = DAO.getPrivateGit()) {
        git.add().addFilepattern(".").call();
        // commit the changes
        DAO.pushCommit(git, "Created " + skill_name, userEmail);
        json.put("accepted", true);

    } catch (IOException | GitAPIException e) {
        e.printStackTrace();
        json.put("message", "error: " + e.getMessage());

    }
}

 

Creating the chatbot.json file and saving user’s bot

Inside the function storePrivateSkillBot(), we create the json file in the local storage and save the user’s bot along with their user ids. If the bot with the same name already exists, then we update it or else create a new one.

private static void storePrivateSkillBot(String userId, String skillName, String group, String language) {
    JsonTray chatbot = DAO.chatbot;
    JSONArray userBots = new JSONArray();
    JSONObject botObject = new JSONObject();
    JSONObject userChatBotsObject = new JSONObject();
    if (chatbot.has(userId)) {
        userBots = chatbot.getJSONObject(userId).getJSONArray("chatbots");
    }
        // save a new bot
    botObject.put("name",skillName);
    botObject.put("group",group);
    botObject.put("language",language);
    userBots.put(botObject);
    userChatBotsObject.put("chatbots",userBots);
    chatbot.put(userId,userChatBotsObject,true);
} 

 

In chatbot.json file, on creating private skills, the following json gets stored:

Resources

 

Continue ReadingStore User’s Chatbots in SUSI Server

Protected Skills for SUSI.AI Bot Wizard

The first version of SUSI AI web bot plugin is working like a protected skill. A SUSI.AI skill can be created by someone with minimum base user role USER, or in other words, anyone who is logged in.  Anyone can see this skill or edit it. This is not the case with a protected skill or bot. If a skill is protected it becomes a personal bot and then it can only be used by the SUSI AI web client (chatbot) created by that user. Also, only the person who created this skill will be able to edit it or delete it. This skill won’t be listed with all the other public skills on skills.susi.ai.

How is a skill made private?

To make a skill private, a new parameter is added to SusiSkill.java file. This is a boolean called protected. If this parameter is true (Yes) then the skill is protected else if the parameter is false (No) then the skill is not protected. To add protected parameter, we add the following code to SusiSkill.java:

private Boolean protectedSkill;
boolean protectedSkill = false;

You can see that protectedSkill is a boolean with initial value false.
We need to read the value of this parameter in skill code. This is done by the following code:

if (line.startsWith("::protected"&& (thenpos = line.indexOf(' ')) > 0) {
  if (line.substring(thenpos+ 1).trim().equalsIgnoreCase("yes")) protectedSkill=true;
  json.put("protected",protectedSkill);
}

As you can see that the value of protected is read from the skill code. If it’s ‘Yes’ then protectedSkill is set as true and if it’s ‘No’ then protectedSkill is set as false.
If no protected parameter is given in the skill code, its value remains false.

How to add only protected skills from bot wizard?

Now that protected parameter has been added to the server, we need to add this parameter in the skill code but it should be ‘Yes’ if the user is creating a bot and ‘No’ if user is creating a skill using skill creator. In order to do this, we simply check if the user is creating a bot wizard. This can be done by passing a props in the CreateSkill.js file when the skill creator is being used to create a protected skill. Next, we can determine whether the user is using bot wizard or not simply by an if else statement. The following code will demonstrate it:

if (this.props.botBuilder) {
 code = '::protected Yes\n' + code;
else {
 code = '::protected No\n' + code;
}

References:

Continue ReadingProtected Skills for SUSI.AI Bot Wizard

Making Tree View in SUSI botbuilder

SUSI botbuilder enables you to create your own bot which you can integrate in your own website. While creating the skill for the bot, you often need to visualize the dialog flow. The Tree View helps you to visualize the conversation flow. The queries from the user and the related responses from the bot can easily be understood by seeing the tree.

Installing package

We will be using react-orgchart to make the tree. React-orgchart offers minimum basic implementation of the tree and we can easily customise the nodes. To install the package, type the following command in your terminal:

npm i react-orgchart --save

 

Using the Orgchart

In the TreeView.js file, we import the orgchart and render it. The tree data and node component is passed to the OrgChart component. Tree data stores the format and conversations in the tree and the node component represents the rendering of the individual node. We can customise the node according to our needs.

const MyNodeComponent = ({node}) => {
      return (
‘initechNode’>{node.type===‘bot’ && ‘#4285f5’ style={styles.icon}/>}{node.type===‘user’ && } { node.name }

); }; <OrgChart tree={this.props.treeData} NodeComponent={MyNodeComponent} />

 

Generating the Tree Data

The data for the tree is generated by the code view. As user adds or edit skills in the code view, the skill language is interpreted and converted to a tree data structure. The generateTreeData() function in Build.js file does this job.

//generation of skills from the code view
for (let i = 0; i < lines.length; i++) {
  let bot_response = null;
  let line = lines[i];
  if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) {
    let user_query = line;
  while (true) {
    i++;
    if (i >= lines.length) {
      break;
    }
    line = lines[i];
    if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) {
      bot_response = lines[i];
    break;
  }
}
let obj = {
  user_query,
  bot_response,
};
skills.push(obj);
}
}

 

Thus, we are separating the skills in the code view and separating the bot and user response. A sample Tree data looks like:

const treeData = {
  name: 'Welcome!',
  children: [
    {
      name: 'User query 1',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    },
    {
      name: 'User query 2',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    },
    {
      name: 'User query 3',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    }
  ]
}

 

Result:

Resources

Continue ReadingMaking Tree View in SUSI botbuilder

Adding feature to preview chatbot

You can access all features of SUSI.AI on chat.susi.ai. But this restricts the usage of SUSI.AI to this site only. To solve this issue, we can use a Chatbot which can be embedded on any website and then can be used by the people who visit that website, hence generalising the usage of SUSI.AI.

How to embed SUSI.AI Chatbot on any website?

To embed SUSI.AI Chatbot on any website, go to skills.susi.ai and login and then check out the Botbuilder section. There you can find a deploy button which will give you a code which looks something like this :

<script type="text/javascript" id="susi-bot-script" data-userid="" data-group="" data-language="" data-skill="" src="https://skills.susi.ai/susi-chatbot.js" > </script>

 

As can be seen from the code above, it is simply a script tag with an id and a custom attribute data-token (which is the unique access token of the User for that session). The source file for this script is susi-chatbot.js. This is the file that is responsible for displaying the Chatbot on the screen.

But, let’s say that you configured your Chatbot in the entire Botbuilder process. So, we need to have a Preview button which could show us a preview of the Chatbot with the chosen configurations, otherwise we would have to test it out by embedding it on our website, which would be tiresome.

How does the Preview Button work?

The code for Botbuilder page is in the Botbuilder.js file.

The code for the Preview button is as follows :

  {!this.state.chatbotOpen?(<RaisedButton
        label='Preview'
        style={{ width: '148px' }}
        onClick={this.injectJS}
    />):(<RaisedButton
        label='Close Preview'
        style={{ width: '148px' }}
        onClick={this.handleChatbotClose}
  />)}

 

It can be seen that on clicking the ‘Preview’ button, injectJS() function is called, and on clicking the ‘Close Preview’ button, handleChatbotClose() function is called.

     injectJS = () => {
        const myscript = document.createElement('script');
        myscript.type = 'text/javascript';
        myscript.id = 'susi-bot-script';
        myscript.setAttribute('data-token',cookies.get('loggedIn'));
        myscript.src = '/susi-chatbot.js';
        myscript.async = true;
        document.body.appendChild(myscript);
        // some code
    };

     handleChatbotClose = () =>{
      // some code
      $('#susi-launcher-container').remove();
      $('#susi-frame-container').remove();
      $('#susi-launcher-close').remove();
      $('#susi-bot-script').remove();
    }

 

The above 2 functions are the key functions in understanding how the Preview button is previewing the Chatbot on the screen.

Let us see the functioning of injectJS() function first. Firstly, the createElement() method creates an element node of type ‘script’. We store this element node in a variable myscript. We then add attribute values to the script tag by using myscript.attribute_name = attribute_value. We can add ‘type’, ‘id’, ‘src’ and ‘async’ attributes in this way because they are predefined attributes.

However, we need to also add a custom attribute to store the access_token of the User, because we would need to pass down this information to detect the owner of the Chatbot. To add a custom attribute, we use the following code :

myscript.setAttribute('data-token',cookies.get('loggedIn'));

 

This add a custom attribute ‘data-token’ and sets its value to the access token of the session, which would later be used to identify the User.

This script is then appended to the body of the document. As soon as the script is appended to the page, 3 new <div> tags are created and added to the page’s HTML. These 3 <div> tags contain the code for the UI and functionality of the Chatbot itself.

Next, let us look at the functioning of handleChatbotClose() function. This function’s basic objective is to close the Chatbot and remove the 3 components that were rendered as a result of the injectJS() function, and also remove the script itself. As can be seen from the code of the function, we are removing the 4 components which can be seen in the above image of the developer console.

So, this is how the Preview functionality has been added to the Chatbot. Visit https://skills.susi.ai and login to check out the Botbuilder section to try it out.

Resources

Continue ReadingAdding feature to preview chatbot

How to make SUSI AI Twitch Bot

In this blog post, we’ll learn how to make SUSI AI Twitch botSUSI.AI is an intelligent Open Source personal assistant. SUSI AI Bots are built to enable users to chat with SUSI on different clients. Twitch is a live streaming video platform. We can integrate SUSI AI to the live chat on any of its channels.

Pre-requisites:

  1. A Main Twitch account (This is the account on whose channel users would be able to talk to SUSI)
  2. A separate Twitch account for SUSI.
  3. GitHub Account

Create a GitHub from here – Sign up on GitHub

  1. Heroku Account

To create Heroku account, go to Heroku and click on Sign Up.

  1. Node.js

Install Node.js from https://nodejs.org/en/ if it isn’t installed already on your computer.

To check if node is already installed or not, open terminal and type the command:

node -v

If you see something like this – (version can be different)

v9.4.0

Then Node.js is installed on your computer and you can follow along.

Procedure:

You can either fork susi_twitchbot repository and then simply deploy it on Heroku to create SUSI AI Twitch bot or you can create a new repository on your account then deploy it. The following section will describe how to create a new repository for SUSI AI Twitch bot. If you want to deploy susi_twitchbot directly then skip this section and directly move on to the deployment section.

Creating the SUSI AI Twitch codebase:

1. Create a folder on your computer with any name. Open terminal and change your current directory to the new folder that you just created.

2. Type npm init command and enter details like name, version etc. (preferably just keep pressing enter key and let the values stay default)

3. Create a file with the same name that you wrote in entry point (index.js by default). NOTE – It should be in the same folder that you created earlier.

4. In the same folder, type the following commands in command line –

npm i -s tmi.js

We need tmi.js module to easily interact with Twitch messaging interface. For more details, visit https://www.tmijs.org/.

npm i -s request

We need request module to make a GET request to the SUSI API and retrieve JSON data for a particular query.

npm i -s express

We need express module to create successful connection with Heroku through the port provided by it.

5. Open package.json file. It should look like this:

(Adding “start” script is necessary for deploying the app on Heroku)

{
  "name": "susi_twitchbot",
  "version": "1.0.0",
  "description": "SUSI.AI Twitchbot",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.3",
    "request": "^2.86.0",
    "tmi.js": "^1.2.1"
  }
}

6. Copy the following code into the file that you created i.e. index.js

var tmi = require('tmi.js');
var request = require('request');
const express = require('express');
const app = express();
const userChannel = process.env.CHANNEL;

var ans;
var options = {
        options: {
                debug: true
        },
        connection: {
                reconnect: true
        },
        identity: {
                username: process.env.USERNAME,
                password: process.env.OAUTH_TOKEN
        },
        channels: [userChannel]
};

var client = new tmi.client(options);
// Connect the client to the server
client.connect();
client.on('chat', function(channel, userstate, message, self){
        if(message.includes("@"+process.env.USERNAME)){ // checking if SUSI is tagged
                var u = message.split("@" + process.env.USERNAME + " ");
                // Setting options to make a successful call to SUSI API
                var options1 = {
                        method: 'GET',
                        url: 'http://api.susi.ai/susi/chat.json',
                        qs:
                        {
                                timezoneOffset: '-300',
                                q: u[1]
                        }
                };
                request(options1, function(error, response, body) {
                        if (error) throw new Error(error);
                        if((JSON.parse(body)).answers[0])
                                ans = userstate['display-name'] + " " + (JSON.parse(body)).answers[0].actions[0].expression;
                        else
                                ans = userstate['display-name'] + " Sorry, I could not understand what you just said."
                
                        client.action(userChannel, ans);
                });
        }
});

client.on('connected', function(address, port){
        client.action(userChannel, `Hi, I'm SUSI. Mention me using @${process.env.USERNAME} to chat with me.`);
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
   console.log(`Listening on ${port}`);
});

7. Before we can deploy this bot to Heroku, we have to make a GitHub repository for it. For making a github repository for the chatbot, follow these steps:

In the command line, change current directory to the folder we created above for the bot and type in the following commands.

git init
git add .
git commit -"initial"

Now, you have to create a Github repository, follow these steps to do that –

  1. Go to https://github.com/ and login.
  2. Create a new repository. Choose any name.
  3. Get the URL for remote repository and copy it.

Again go to the command line, change current directory to the folder we created above for the bot and type in the following commands.

git remote add origin <URL for remote repository that you just copied>
git remote -v
git push -u origin master

Setting up SUSI bot account on Twitch:

In order to set up SUSI AI bot on Twitch, go to http://twitchapps.com/tmiclick on “Connect with Twitch” and login to the account that will be used as SUSI AI bot. This will generate an OAuth tokenSave it for later use.

Deploying the Bot on Heroku:

  1. Got to Heroku and login.
  2. Go to dashboard and create a new app.
  3. After creating an app, go to Deploy and choose “GitHub” for Deployment method.

  1. Search for the repository that you created on GitHub or select susi_twitchbot after forking it. Connect to it in “App connected to GitHub”.
  2. Enable Automatic deployment.

  1. Now go to Settings and add the following config vars “CHANNEL”, “OAUTH_TOKEN”, “USERNAME” and “HEROKU_URL”.
  • The key of “CHANNEL” is the channel name on which you’d like to talk to SUSI.
  • The key of “OAUTH_TOKEN” is the OAuth key that you generated earlier. (Remember to include “oauth:” in the key)
  • The key of “USERNAME” is the username of the Twitch account you created for SUSI AI.
  • The key of “HEROKU_URL” is “http://<your_app_name>.herokuapp.com” (write ‘http’ and not ‘https’).

  1. Go to Deploy, Press “Deploy Branch” under “Manual Deployment”.

Now your SUSI AI Twitch bot is successfully deployed on Heroku. Go to your Twitch channel and mention your SUSI account using ‘@’ and start talking to SUSI!

Here, ‘dns4044′ is the owner of channel, ‘testdns’ is some user on the channel and ‘susiaibot’ is SUSI AI Twitch bot.

References:

Continue ReadingHow to make SUSI AI Twitch Bot

Showing “Get started” button in SUSI Viber bot

When we start a chat with SUSI.AI on Viber i.e. SUSI Viberbot, there should be an option on how to get started with the bot. The response to it are some options like “Visit repository”, “How to contribute” which direct the user to check how SUSI.AI bot is made and prompts him/her to contribute to it. Along with that an option of “start chatting” can be shown to add up some sample queries for the user to try.

To accomplish the task at hand, we will accomplish these sub tasks:

  1. To show the “Get started” button.
  2. To show the reply to “Get started” query.
  3. To respond to the queries, nested in the response of “Get started”

Showing “Get started”:

The Viber developers platform notifies us when a user starts a conversation with our bot. To be exact, a conversation_started event is sent to our webhook and can be handled accordingly. The Viberbot shows a welcome message to the user along with a Get started button to help him/her start.

To send just the welcome message:

if (req.body.event === 'conversation_started') {
       // Welcome Message
       var options = {
           method: 'POST',
           url: 'https://chatapi.viber.com/pa/send_message',
           headers: headerBody,
           body: {
               // some required body properties here
               text: 'Welcome to SUSI.AI!, ' + req.body.user.name + '.',
               // code for showing the get started button here.
        }
           json: true
       };
 
       request(options, function(error, res, body) {
           // handle error
       });
   }

The next step is to show the “Get started” button. To show that we use a keyboard tool, provided by Viber developers platform. So after the “text” key we have the “keyboard” key and a value for it:

keyboard: {
             "Type": "keyboard",
             "DefaultHeight": true,
             "Buttons": [{
                 "ActionType": "reply",
                 "ActionBody": "Get started",
             }]
         }

The action type as shown in the code, can be “reply” or “open-url”. The “reply” action type, triggers an automatic query sent back with “Get started” (i.e. the value of “ActionBody” key), when that button gets clicked.

Hence, this code helps us tackle our first sub task:

Reply to “Get started”:

We target to make each SUSI.AI bot generic. The SUSI FBbot and SUSI Tweetbot shows some options like “Visit repository”, “Start chatting” and “How to contribute?” for the “Get started” query. We render the same answer structure in Viberbot.

The “rich_media” type helps us send buttons in our reply message. As we ought to use three buttons in our message, the button rows are three in the body object:

if(message === "Get started"){
                   var options = {
                       method: 'POST',
                       url: 'https://chatapi.viber.com/pa/send_message',
                       headers: headerBody,
                       body: {
                           // some body object properties here
                           type: 'rich_media',
                           rich_media: {
                               Type: "rich_media",
                               ButtonsGroupColumns: 6,
                               ButtonsGroupRows: 3,
                               BgColor: "#FFFFFF",
                               Buttons: buttons
                           }
                       },
                       json: true
                   };
 
                   request(options, function(error, res, body) {
                       if (error) throw new Error(error);
                       console.log(body);
                   });

As said before, 2 type of Action types are available – “open-url” and “reply”. “Visit repository” button has an “open-url” action type and “How to contribute?” or “start chatting” has a “reply” action type.

Example of “Visit repository” button:

var buttons = [{
                Columns: 6,
                Rows: 1,
                Text: "Visit repository",
                "ActionType": "open-url",
                "ActionBody": "https://www.github.com/fossasia/susi_server",
                // some text styling properties here
             }];

To respond to the “reply” action type queries:

When the “reply” action type button gets clicked, it triggers an automatic query sent back to the bot with the value same as that of the “ActionBody” key. So we just need to apply a check if the message string recieved is “Start chatting” or “How to contribute?”

For the response to “Start chatting”, we plan to show sample queries for the user to try. This can be shown by using buttons with the action type as “reply”.

Code snippet to show a button with the text as “What is FOSSASIA?”:

var buttons = [{
                        Columns: 6,
                        Rows: 1,
                        Text: "What is FOSSASIA? ",
                        "ActionType": "reply",
                        "ActionBody": "What is FOSSASIA?",
                        // text styling here
                    }];

For the response to “How to contribute”, we show some messages to help the user contribute to SUSI.AI. These messages also just need buttons with it, to be able to apply the necessary action.

We respond with 2 messages to the user, both the messages have a button.

For example, a button to visit the SUSI.AI Gitter channel:

var buttons = [{
                    Columns: 6,
                    Rows: 1,
                       Text: "<font color=#323232><b>Chat on Gitter</b></font>",
                      ActionType: "open-url",
                      ActionBody: "https://www.gitter.im/fossasia/susi_server",
                      // text styling here
            }];

This way we have successfully added the “Get started” option to our Viberbot and handled all the subsequent steps.

Resources:

  1. Viber video managing chat extensions by Ingrid Lunden from Tech crunch.
  2. Develop a chat bot with node js by Slobodan Stojanović from smashing magazine.
Continue ReadingShowing “Get started” button in SUSI Viber bot

Making SUSI Alexa skill as an express app

Previously SUSI Alexa skill was deployed using AWS Lambda service (Refer to this blog). Each SUSI.AI Bot should be deployed on Google cloud using Kubernetes. To accomplish that, we need to remove the dependency of the SUSI Alexa skill from AWS Lambda service. We need to make it an express app, to be able to deploy it to Google cloud. Let’s start with on how to achieve it:

SUSI Alexa skill:

We require three files to make the skill as an express app. The main entry point for the skill would be server.js file, which will serve the incoming request using two helper files alexa.js and handlers.js.

Server.js:

This file acts as the main entry point for the incoming request. We handle two type of requests using it, that are:

  1. Launch request
  2. Intent request

Launch request is triggered when a person utters “Alexa, open susi chat” , “Alexa, start susi chat”, “Alexa, launch susi chat” etc. This request is responded with an introductory phrase about SUSI.AI. To catch this request:

if (type === "LaunchRequest") {
        var endpoint = "http://api.susi.ai/susi/chat.json?q="+"Welcome"; // ENDPOINT GOES HERE
        
        http.get(endpoint, (response1) => {
            var body = "";
            response1.on("data", (chunk) => { body += chunk; });
            response1.on("end", () => {
                var viewCount;
                viewCount = JSON.parse(body).answers[0].actions[0].expression;
                endpoint = "http://api.susi.ai/susi/chat.json?q="+"Get+started"; // ENDPOINT GOES HERE
                body = "";
                http.get(endpoint, (response2) => {
                    response2.on("data", (chunk) => { body += chunk; });
                    response2.on("end", () => {
                        viewCount += JSON.parse(body);.answers[0].actions[0].expression;
                        response.say(viewCount,false);
                    });
                });
            });
        });
    }

Intent request gets triggered, when any other phrase is uttered by the user except Launch related phrases. We check if the intent triggered has a corresponding handler to handle the request. If the handler is found in handlers.js file, we call it passing the required arguments to the handler function. Let’s see how handlers make this step possible.

Handler.js:

This file decides on what function to run when a particular type of intent is triggered. As we have just one intent for our SUSI Alexa skill i.e. callSusiApi, we have just one function in our handlers.js file. During its execution, the first step we do is extract the query value:

let query = slots.query.value;

Depending upon the query value, we run its corresponding code. For example, in case of a generic query (i.e. any query except stop, cancel and help):

var endpoint = "http://api.susi.ai/susi/chat.json?q="+query; // ENDPOINT GOES HERE

http.get(endpoint, (response1) => {
    var body = "";
    response1.on("data", (chunk) => { body += chunk; });
    response1.on("end", () => {
        var data = JSON.parse(body);
        if(data.answers[0].actions[1]){
            // handle rss and table type results
        }
        else
        {
            viewCount = data.answers[0].actions[0].expression;
        }
        response.say(viewCount,true);
    });
});

At the end of the function we respond to the user with an answer to his/her query using:

response.say(viewCount,true);

Alexa.js:

When we get a request from the user, we pass that request and response object to this file. This file helps us wrap the required request properties into an object and return that back to the server file, which was the entry point for the request. Now, we can easily extract the properties in server file and work with those:

We extract the properties like this:

let session = req.body.session,
        intent,
        slots;
session.attributes = session.attributes || {};

if (req.body.request.intent) {
    intent = req.body.request.intent.name;
    slots = req.body.request.intent.slots;
}

Then we return the object back at the end:

return {
        type: req.body.request.type,
        intent: intent,
        slots: slots,
        session: session,
        response: {
            say: (text, shouldEndSession) => say(text, shouldEndSession),
            ask: (text, shouldEndSession) => say(text, shouldEndSession)
        }
    };

Great, we have made the SUSI Alexa skill as an express app. The next step is to do some changes in the configuration tab of our skill:

  1. Instead of Amazon resource number, we fill our webhook address here: 

  2. A new property shows up that is SSL certificate. As we are using Heroku for webhook services, we select the second option as shown below: 

  3. It’s time to test the skill: 

    This repository by Salesforce helped me a lot in making the SUSI skill as an express app.

    Resources:

    1. Developing Alexa Skills Locally with Node.js by Josh Skeen from Bignerdranch.
    2. Amazon Alexa Skills: Create a Custom Skill by Simon Coope from SJCNET.
Continue ReadingMaking SUSI Alexa skill as an express app