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 Reading

Making API for Fetching Private Skill Bot Settings from Server

SUSI Server needs to provide an API which would return the private skill’s metadata. This metadata will include the chatbot’s design and configuration settings. The skill’s meta data is being stored in the chatbot.json file in the server. This API will be called from the websites where the chatbot has been deployed. The skill’s metadata will be used for customizing the design of the chatbot. This blog explains how the API for fetching a private skill bot’s metadata is made.

Understanding the API

The API used to fetch public skill’s metadata is /cms/getSkillMetadata.json, with the following parameters:

  • Model
  • Group
  • Language
  • Skill

These parameters help to uniquely identify the skill.

The same API is used to fetch private skill’s metadata too. But the parameters changes to:

  • userid
  • Group
  • Language
  • Skill

The userid also helps to secure the identity of the user. Unlike email id, the user can be easily identified using the userid. Also it prevents from accessing other user’s chatbots without knowing their userid.

Understanding the data stored

In the chatbot.json file, the skill’s metadata are stored in the following format:

{"17a70987d09c33e6f56fe05dca6e3d27": {"Knowledge": {"en": {"test2new1": {
  "design": {
    "botIconColor": "#000000",
    "userMessageTextColor": "#ffffff",
    "botMessageBoxBackground": "#f8f8f8",
    "userMessageBoxBackground": "#0077e5",
    "botMessageTextColor": "#455a64",
    "bodyBackground": "#ffffff"
  },
  "configure": {
    "sites_enabled": "website1.com, website2.com",
    "sites_disabled": "website3.com"
  },
  "timestamp": "2018-07-20 01:04:39.571"
}}}}}

 

Thus, each entry is stored as a key-value pair. This makes the retrieval of record very efficient.

Making API to fetch private skill bot’s settings

In GetSkillMetadataService.java file, we detect if the client is sending the “userid” parameter or not. For fetching public skill’s metadata, the client will send the “model” parameter and for fetching private skill bot’ settings, the client will send the “userid” parameter. To fetch the private skill bot’s settings, we need to extract it from the chatbot.json file. We fetch the entire object for the particular skill and return it:

// fetch private skill (chatbot) meta data
JsonTray chatbot = DAO.chatbot;
JSONObject json = new JSONObject(true);
JSONObject userObject = chatbot.getJSONObject(userid);
JSONObject groupObject = userObject.getJSONObject(group);
JSONObject languageObject = groupObject.getJSONObject(language);
JSONObject skillObject = languageObject.getJSONObject(skillname);
json.put("skill_metadata", skillObject);
json.put("accepted", true);
json.put("message", "Success: Fetched Skill's Metadata");
return new ServiceResponse(json);

 

Result

Example API request: http://127.0.0.1:4000/cms/getSkillMetadata.json?userid=17a70987d09c33e6f56fe05dca6e3d27&group=Knowledge&language=en&skill=testnew

This gives the following output:

{
  "skill_metadata": {
    "design": {
      "botIconColor": "#000000",
      "userMessageTextColor": "#ffffff",
      "botMessageBoxBackground": "#f8f8f8",
      "userMessageBoxBackground": "#0077e5",
      "botMessageTextColor": "#455a64",
      "bodyBackground": "#ffffff"
    },
    "configure": {
      "sites_enabled": "website1.com, website2.com",
      "sites_disabled": "website3.com"
    },
    "timestamp": "2018-07-20 01:39:55.205"
  },
  "accepted": true,
  "message": "Success: Fetched Skill's Metadata",
  "session": {"identity": {
    "type": "host",
    "name": "127.0.0.1_af2c1fe3",
    "anonymous": true
  }}
}

 

Resources

Continue Reading

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 Reading

Creating API to Retrieve Images in SUSI.AI Server

SUSI Server needed to have an API where users can upload and store images. This images will be useful in setting custom theme for the botbuilder or for chat.susi.ai. User can upload image from their systems instead of pasting the link of an image stored in cloud. Also we need an API to retrieve the stored image given its full path. This blog explains how the SUSI Server returns the image stored upon requesting.

Understanding where the image is stored

The images uploaded to the SUSI Server is stored in its local storage. The local storage is where all the json files and other data are stored. We store the images inside “image_uploads” folder. In the get image API, the client will provide the full path of the image i.e the user UUID and the image’s full name. Then we need to fetch this image from the local storage and return it.

Retrieving the image from local storage

The Retrieving image API has the endpoint “/cms/getImage.png”. The servlet file is “GetImageServlet.java”. The client has to send the full path of the image in the parameter “image”. A sample GET request is “http://127.0.0.1:4000/cms/getImage.png?image=963e84467b92c0916b27d157b1d45328/1529692996805_susi icon.png”.

We need to retrieve the given image from the correct path.

Query post = RemoteAccess.evaluate(request);
String image_path = post.get("image","");
File imageFile = new File(DAO.data_dir  + File.separator + "image_uploads" + File.separator+ image_path);
if (!imageFile.exists()) {response.sendError(503, "image does not exist"); return;}
String file = image_path.substring(image_path.indexOf("/")+1);

 

Writing the image in Byte Stream

Now that we have the image file, we need to write that image in a Byte array output stream. We read the pixels data from the image using “FileInputStream” method.

ByteArrayOutputStream data = new ByteArrayOutputStream();
byte[] b = new byte[2048];
InputStream is = new BufferedInputStream(new FileInputStream(imageFile));
int c;
try {
while ((c = is.read(b)) >  0) {data.write(b, 0, c);}
} catch (IOException e) {}

 

Returning the image to the client

Now that we have the image data in the Byte array, we can send that data to the client. Before that we have to set the response type. We set the response type accordingly if the image is png, gif, or jpg. For other types, we the response type as “octet-stream”. Finally we attach the image in the response object and send it.

if (file.endsWith(".png") || (file.length() == 0 && request.getServletPath().endsWith(".png"))) post.setResponse(response, "image/png");
else if (file.endsWith(".gif") || (file.length() == 0 && request.getServletPath().endsWith(".gif"))) post.setResponse(response, "image/gif");
else if (file.endsWith(".jpg") || file.endsWith(".jpeg") || (file.length() == 0 && request.getServletPath().endsWith(".jpg"))) post.setResponse(response, "image/jpeg");
else post.setResponse(response, "application/octet-stream");

ServletOutputStream sos = response.getOutputStream();
sos.write(data.toByteArray());
post.finalize();

 

Result:

Resources

 

Continue Reading

Creating API to Upload Images in SUSI.AI Server

SUSI Server needed to have an API where users can upload and store images. This images will be useful in setting custom theme for the botbuilder or for chat.susi.ai. User can upload image from their systems instead of pasting the link of an image stored in cloud. This blog explains how the SUSI Server stores the images in its local storage.

Creating Upload Image Service

UploadImageService.java is the file which creates the API to upload image. After creating the UploadImageService class in this file, we need to include the class in SusiServer.java to enable the API:

// add services
  services = new Class[]{
...
UploadImageService.class

Restricting access rights and giving API endpoint name

The Upload Image API should be available only to logged in users and not to anonymous users. Therefore we need to set the base user role to “USER”.  Also we need to set the name of the API endpoint. Lets set it to “/cms/uploadImage.json”.

@Override
public UserRole getMinimalUserRole() {
return UserRole.USER;
}

@Override
public String getAPIPath() {
return "/cms/uploadImage.json";
}

 

Creating API to accept the Post request

We need to accept the image uploaded by the client in a post request. The post request will contain the following parameters:

  • access_token – used to verify identity of the user and get their UUID
  • image_name – the name of the image
  • image (the image file) – the actual image file

To accept the post request on this route, we define the following function:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Access-Control-Allow-Origin", "*"); // enable CORS
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");
} else {
// save image

 

Getting the correct storage location

We are saving the image in the local storage of the server, i.e inside the “data” folder. Inside it, we store inside the “image_uploads” folder. The image path and name has three parts:

  • User’s UUID as the sub folder’s name
  • System time in milliseconds as the first part of the image’s name
  • The image’s name given by the user as the second part of the image’s name

String image_name = req.getParameter("image_name");
ClientCredential credential = new ClientCredential(ClientCredential.Type.access_token, req.getParameter("access_token"));
Authentication authentication = DAO.getAuthentication(credential);
ClientIdentity identity = authentication.getIdentity();
String userId = identity.getUuid();
String imagePath = DAO.data_dir  + File.separator + "image_uploads" + File.separator + userId;

 

Saving the image

After we have formed the location of the image to be stored, we need to actually write that file to the local storage. Here is the code snippet which does that:

// Reading content for image
Image image = ImageIO.read(imagePartContent);
BufferedImage bi = this.toBufferedImage(image);
// Checks if images directory exists or not. If not then create one
if (!Files.exists(Paths.get(imagePath))) new File(imagePath).mkdirs();
String new_image_name = System.currentTimeMillis() + "_" + image_name;
File p = new File(imagePath + File.separator + new_image_name);
if (p.exists()) p.delete();
ImageIO.write(bi, "jpg", new File(imagePath + File.separator + new_image_name));

 

Thus the image gets stored in the local storage inside of the folder named by user’s UUID

Here, the number 963e84467b92c0916b27d157b1d45328 is the user UUID of the user and 1529692996805 is generated by getting the System time in milliseconds. This helps to differentiate between images of same name uploaded by the same user.

Resources

Continue Reading

Code view and UI View in design tab of bot wizard

Design tab is for designing your SUSI AI chatbot. You can change the background colour, add an image as background color, change colour of user text box, bot text box etc. On SUSI AI bot wizard design tab, we have two views. These views show the same thing but in a different way for different people. The code view is mainly for the developers while the UI (User Interface) view is for a non-developer user because we can not expect them to know how to code even if it is easy.

Code View

The current code view of design tab looks like this:

::bodyBackground #ffffff
::bodyBackgroundImage
::userMessageBoxBackground #0077e5
::userMessageTextColor #ffffff
::botMessageBoxBackground #f8f8f8
::botMessageTextColor #455a64
::botIconColor #000000
::botIconImage

For changing colours, you simply have to change the hex codes and for adding images as background, you need to add the url of image. You can also upload an image but that function is provided in the UI View.

UI View

The current UI view of design tab looks like this:

Here, we have colour picker for user to choose a colour for various components of the chatbot. There’s also a small box with the same colour as specified in hex code for previewing.

Switching and Synchronising between Code view and UI view

We have different components for code view and UI view. In order to synchronise both the views, we need to have the same code in state of both components. To do that, we add the code in state of the parent component i.e. design.js and then pass it to code view and UI view as states.

The following code snippet will demonstrate this:

<CodeView
    design={{
        sendInfoToProps: this.sendInfoToProps,
        updateSettings: this.updateSettings,
        code: this.state.code,
    }}
/>

You can see that we pass a whole object as props.
Using this approach, same code is passed to both the views. This solves the first problem. The next problem is that if we do a change in code view then it should happen in the code state of parent file as well. Same goes for UI view.
To do this, whenever we change the code in code view or UI view, we call a function in the parent file (design.js) which was passed as props and we pass the state of code view or UI view as arguments of this function. This function then updates the state accordingly. The name of the function is sendInfoToProps and it is as follows:

sendInfoToProps = values => {
    this.setState({ ...values });
};

You can see that it simply updates the state value as per the parameters passed to it.
So this is how synchronisation works between different views.

References:

Continue Reading

Code view in Configure tab of SUSI.AI Bot Wizard

The purpose of configure tab in SUSI.AI bot wizard is to provide the bot creator various options on how the bot will interact with different websites. It currently provides an option of enabling or disabling the chatbot on different websites.
The configure tab has two parts. One is the code view which allows the users to write websites on which they want to enable/disable the chatbot and a table below it which lists those websites.

The default code in code view is passed in a state in the Configure.js file. The following code demonstrates that:

this.state = {
  code: this.props.code,
};

Fetching data from code view:

After writing the websites on which the user wants the chatbot to be enabled/disabled and pressing on the ‘Save’ button, a functiongenerateConfigData is called.
This function takes the code in code view and stores in inside of a variable. Then it splits the code and makes two arrays:

  • enabledSites: This array contains all the websites that are written in  enabled field.
  • disabledSites: This array contains all the websites that are written in disabled field.

This process can be easily understood from the following code snippet:

let newCode = this.state.code;
let websiteData = newCode
 .split('::sites_enabled')[1]
 .split('::sites_disabled');
let enabledSites = websiteData[0].split(',');
let disabledSites = websiteData[1].split(',');

This data is stored inside an object.

Displaying the data:

The data fetched from the code view now has to be displayed in form of a table. This data is looks like this:

let configData = [
 {
   id: '1',
   name: 'website1.com',
   last: 'Jan 12, 2018 20:08 hrs',
   status: 1,
 },
 {
   id: '2',
   name: 'website2.com',
   last: 'Feb 19, 2018 13:00 hrs',
   status: 1,
 },
 {
   id: '3',
   name: 'website3.com',
   last: 'Mar 14, 2018 10:15 hrs',
   Status: 2,
 }
];

Status is 1 if the website is in enabled column and 2 if the website is in disabled column.
To display this data on the screen, we simply map through the data and display the rows of the table. The following code demonstrates it:

{configData.map((item, index) => {
 if (item.name) {
   return (
    <TableRow key={index}>
      <TableRowColumn style={{ fontSize: '16px' }}>
         {item.name}
     </TableRowColumn>
      <TableRowColumn style={{ fontSize: '16px' }}>
         {item.last}
     </TableRowColumn>
     <TableRowColumn>
       <SelectField
         floatingLabelText="Status"
         fullWidth={true}
         value={item.status}
       >
         <MenuItem value={1} primaryText="Enable" />
         <MenuItem value={2} primaryText="Disable" />
       </SelectField>
     </TableRowColumn>
    </TableRow>
   );
 }
})}

References:

Continue Reading

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 Reading

Add auto copy code feature in botbuilder

SUSI botbuilder lets you create your own skill bot and deploy on your website. After you have customised your bot, you will get a javascript code that you need to paste in your website’s source code. To make the process of copying the code easy, we have developed a feature for auto copying of the code to your clipboard. You just need to click on a button “copy” and the code will be copied to your clipboard.

Installing package

We use react-copy-to-clipboard package to enable auto copy feature. Install it in your project using the following command.

npm i react-copy-to-clipboard --save

 

Adding code inside render function

Inside the render() function in react file, paste the following code where you want the copy button to be displayed. Here we need to provide the text to be copied to the CopyToClipboard component via the text props. We pass the script code. We get the access token for the bot via the saved cookie. Inside the children of CopyToClipboard we need to pass the copy button which we want to show.

<CopyToClipboard
  text={
    " +cookies.get('uuid') +"' data-group='" +
    group + "' data-language='" + language + "' data-skill='" + skill + "' src='" + api + "/susi-chatbot.js'>"
  }
  onCopy={() => this.setState({ copied: true })}
>
  <span className="copy-button">copy</span>
</CopyToClipboard>

 

Thus, when the user clicks on the “copy” button, the code will be automatically copied to the user’s clipboard.

Showing snackbar message

After the code has been copied to the user’s clipboard, we can show a snackbar message to inform the user. First we pass a function onCopy to the CopyToClipboard component. This sets the state variable copied to true. Then we have a snackbar component which displays the message.

<Snackbar
   open={this.state.copied}
   message="Copied to clipboard!"
   autoHideDuration={2000}
   onRequestClose={() => {
      this.setState({ copied: false });
   }}
/>

 

Result:

 

Resources

 

Continue Reading

Create custom theme for SUSI chatbot web plugin

SUSI web plugin bot provides the features of SUSI as a chat window which users can plugin to their websites. They just need to copy the generated javascript code into their website and the SUSI bot plugin will be enabled. The user can customize the theme of the chatbot. This blog explains how the feature of applying custom theme is implemented.

Accepting user’s input

The react component of our concern is BotBuilderPages/Design.js. We use material-ui-color-picker component to accept user’s choice of color.

<ColorPicker
  className='color-picker'
  style={{display:'inline-block',float:'left'}}
  name='color'
  defaultValue={ this.state[component.component] }
  onChange={(color)=>
   this.handleChangeColor(component.component,color) }
/>

 

Similarly, there is a text field which accepts the url of an image. The values are stored in the component’s state variables.

Storing settings in server

The design settings are stored in the text format:

This forms part of the skill’s text file. When the user clicks on “save and deploy” button, the complete skill gets send to the server through the following API:

let settings = {
      async: true,
      crossDomain: true,
      url:
        urls.API_URL +
        '/cms/' +
        (this.state.updateSkillNow ? 'modifySkill.json' : 'createSkill.json'),
      method: 'POST',
      processData: false,
      contentType: false,
      mimeType: 'multipart/form-data',
      data: form,
    };

    $.ajax(settings)
      .done(function(response) {
        // successfully saved in server
      });

 

In the server, the skill is saved in susi_private_skill_data directory. Also, the design and configuration settings are stored in chatbot.json file.

Applying the settings to the bot

Now, in the susi-chatbot.js file, the custom theme settings are applied to the bot. The function getTheme() fetches the theme settings from the server via an ajax request. Then the function applyTheme() is executed which applies the theme to the chatbot.

$(".susi-sheet-content-container").css("background-color",botbuilderBackgroundBody);
if(botbuilderBodyBackgroundImg){
$(".susi-sheet-content-container").css("background-image","url("+botbuilderBodyBackgroundImg+")");
}

 

Similarly, other theme variables are applied as well. Thus we have customised the theme of the SUSI chatbot plugin.

Result:

Resources

 

Continue Reading
Close Menu