How to Use Lightning Web Components in Einstein Bot.

Posted on May 23, 2021 Post Thumbnail

Hi Trailblazers, In this blog post we are going learn, How to Use Lightning Web Components in Einstein Bot.

Sometimes, We have a requirement to show a Lightning Component inside Einstein Bot's Chat window.

In this blog, I'm going to Show a simple Calculator Component inside Einstein Bot.

Before we start, Let's have a look at the demo itself.

For a quick demo,  All you need to do.

Click on the right bottom button "Chat with an Expert"

Welcome

After Greeting Messages, answer quick questions.

And Type: Calculator

Query

Now It'll Show you Calculator Component.

Showing Calculator.

Let's Start :)

Some days ago, I posted a Blog.

Use Images And Videos In Einstein Bot

In this Blog, I was showing you, How to show Images and Videos in Einstein Bot.

Before reading this blog, I'd like you to visit to Use Images And Videos In Einstein Bot

Walkthrow :)

1. First we'll create a Calculator Component to use in Einstein Bot.

2. We'll create a Component for Embedded Service Deployments.

3. After that, We'll add component in Embedded Chat.

4. In last, We'll update and create some dialogue in Einstein Bot.

Let's create :)

MyCalculator.html

<template>
<div class="slds-box">
    <img src={SfLogo} width="50%">
    <div class="slds-box">
<div>LWC Calculator </div>
<lightning-input type="number" name="input1" label="Enter 1st number" onchange={inputChange}></lightning-input>
<lightning-input type="number" name="input2" label="Enter 2nd number" onchange={inputChange}></lightning-input>
<div>
<lightning-button-group>
<lightning-button name="add" label="Add" onclick={calculate}></lightning-button>
<lightning-button name="subtract" label="Subtract" onclick={calculate}></lightning-button>
<lightning-button name="multiply" label="Multiply" onclick={calculate}></lightning-button>
<lightning-button name="divide" label="Divide" onclick={calculate}></lightning-button>
</lightning-button-group>
</div>
<br/>
{outputNumber}
</div>
<div class="slds-box">
<lightning-input type="checkbox" label="Show Previous Result" name="checkbox1" onchange={calculate}>
</lightning-input>
<template if:true={showOldResult}>
<ul>
<template for:each={allOutputResult} for:item="item">
<li key={item}>{item}</li>
</template>
</ul>
</template>
</div>
</div>
</template>

myCalculator.js

import { LightningElement, track } from 'lwc';
import heySfLogo from '@salesforce/resourceUrl/heySfLogo';
export default class MyCalculator extends LightningElement {
@track outputNumber;
@track allOutputResult = [];
@track showOldResult = false;
input1;
input2;
@track SfLogo;
SfLogo = heySfLogo ;
inputChange(event)
{

if(event.target.name === 'input1')
{
// eslint-disable-next-line radix
this.input1 = parseInt(event.target.value );
}
else if(event.target.name === 'input2')
{
// eslint-disable-next-line radix
this.input2 = parseInt(event.target.value) ;
}
}
calculate(event)
{
if(event.target.name === 'add')
{
this.outputNumber = `${this.input1} + ${this.input2} = ${(this.input1 + this.input2)}` ;
this.allOutputResult.push(this.outputNumber);
}
if(event.target.name === 'subtract')
{
this.outputNumber = `${this.input1} - ${this.input2} = ${(this.input1 - this.input2)}` ;
this.allOutputResult.push(this.outputNumber);
}
if(event.target.name === 'multiply')
{
this.outputNumber = `${this.input1} x ${this.input2} = ${(this.input1 * this.input2)}` ;
this.allOutputResult.push(this.outputNumber);
}
if(event.target.name === 'divide')
{
this.outputNumber = `${this.input1} / ${this.input2} = ${(this.input1 / this.input2)}` ;
this.allOutputResult.push(this.outputNumber);
}
if(event.target.name === 'checkbox1')
{
    if(this.showOldResult === false)
    {
        this.showOldResult = true;
    }
    else if(this.showOldResult === true)
    {
        this.showOldResult = false;
    }
   
        
}
}
}

myCalculator.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

Deploy this Component to Salesforce Org.

For calling Calculator Component:

        <template if:true={isLwc}>
            <c-my-calculator></c-my-calculator>
        </template>

Complete code for Chat Component:

chatMessage.html

<template>
    <template if:false={isAgent}>
        <div class="chatMessage chasitor">
            <lightning-formatted-rich-text value={messageContent.value}>   
            </lightning-formatted-rich-text>
        </div>
    </template>
    <template if:true={isAgent}>
        <template if:true={isPlainText}>
            <div class="chatMessage agent plainText">
                <lightning-formatted-rich-text value={content}>
                </lightning-formatted-rich-text>
            </div>
        </template>

        <template if:true={isRichText}>
            <div class="chatMessage agent richText">
                <lightning-formatted-rich-text value={content}>
                </lightning-formatted-rich-text>
            </div>
        </template>

        <template if:true={isYoutube}>
            <div class="chatMessage agent youtube">
                <iframe src={content} allowfullscreen>
                </iframe>
            </div>
        </template>

        <template if:true={isImage}>
            <div class="chatMessage agent image">
                <img src={content} />
            </div>
        </template>

        <template if:true={isLwc}>
            <c-my-calculator></c-my-calculator>
        </template>

        <template if:true={isNavigate}>
            <div class="chatMessage agent plainText">
                <lightning-formatted-rich-text value={content}>
                </lightning-formatted-rich-text>
            </div>
        </template>
        
        <template if:true={isUrl}>
            <template if:true={hasOGPInfo}>
                <div class="chatMessage agent url">
                    <a href={content} target="_blank"></a>
                    <img src={ogpMeta.image} onerror={fallback} />
                    <div class="ogpInfo">
                        <div class="title">{ogpMeta.title}</div>
                        <div class="description">{ogpMeta.description}</div>
                        <div class="site_name">{ogpMeta.site_name}</div>
                    </div>
                </div>
            </template>
            <template if:false={hasOGPInfo}>
                <div class="chatMessage agent plainText">
                    <lightning-formatted-rich-text value={content}>
                    </lightning-formatted-rich-text>
                </div>
            </template>
        </template>
    </template>
</template>

chatMessage.js

import BaseChatMessage from 'lightningsnapin/baseChatMessage';
import { track } from 'lwc';
import { loadStyle } from 'lightning/platformResourceLoader';
import chatMessageStyle from '@salesforce/resourceUrl/chatMessageStyle';

const DEFAULT_MESSAGE_PREFIX = 'PLAIN_TEXT';
const RICHTEXT_MESSAGE_PREFIX = 'RICH_TEXT';
const YOUTUBE_MESSAGE_PREFIX = 'YOUTUBE';
const IMAGE_MESSAGE_PREFIX = 'IMAGE';
const LWC_MESSAGE_PREFIX = 'COMPONENT';
const URL_MESSAGE_PREFIX = 'URL';
const NAVIGATE_MESSAGE_PREFIX = 'NAVIGATE';
const SUPPORTED_MESSAGE_PREFIX = [DEFAULT_MESSAGE_PREFIX, RICHTEXT_MESSAGE_PREFIX, YOUTUBE_MESSAGE_PREFIX, IMAGE_MESSAGE_PREFIX, LWC_MESSAGE_PREFIX, URL_MESSAGE_PREFIX, NAVIGATE_MESSAGE_PREFIX];
const OPENGRAPH_API_KEY = 'YOUR_OPENGRAPH_API_KEY';

/**
 * Displays a chat message using the inherited api messageContent and is styled based on the inherited api userType and messageContent api objects passed in from BaseChatMessage.
 */
export default class ChatMessageDefaultUI extends BaseChatMessage {
    messageType = DEFAULT_MESSAGE_PREFIX;
    @track content = '';
    @track ogpMeta = {};
    connectedCallback() {
        if (!this.isAgent) {
            return;
        }
        const messageTypePrefixPosition = SUPPORTED_MESSAGE_PREFIX.indexOf(this.messageContent.value.split(':')[0]);
        if (messageTypePrefixPosition > -1) {
            this.messageType = SUPPORTED_MESSAGE_PREFIX[messageTypePrefixPosition];
            console.log('Message Type:'+this.messageType);
        }
        const contentValue = (this.messageContent.value.split(this.messageType + ':').length === 1) ? this.messageContent.value : this.messageContent.value.split(this.messageType + ':')[1];
        console.log('Message contentValue:'+contentValue);
        Promise.all([
            loadStyle(this, chatMessageStyle + '/style.css')
        ]);
        if (this.isPlainText) {
            this.content = contentValue;
        } else if (this.isYoutube) {
            this.content = 'https://www.youtube.com/embed/' + contentValue
        } else if (this.isNavigate) {
            const url = this.extractOriginalUrl(contentValue);
            window.open(url);
            this.content = `Opening ${url}`;
        } else if (this.isImage) {
            this.content = this.extractOriginalUrl(contentValue);
        } else if(this.isLwc) {
            console.log('In LWC');
            //this.content = this.extractOriginalUrl(contentValue);
        } else if (this.isUrl) {
            this.content = this.extractOriginalUrl(contentValue);
            const urlEncoded = encodeURIComponent(this.content);
            const requestURL = 'https://opengraph.io/api/1.1/site/' + urlEncoded + '?app_id=' + OPENGRAPH_API_KEY;
            fetch(requestURL, { method: "GET" })
                .then(response => {
                    return response.json();
                })
                .then(jsonResponse => {
                    if(jsonResponse.hybridGraph) {
                        this.ogpMeta.title = jsonResponse.hybridGraph.title;
                        this.ogpMeta.description = jsonResponse.hybridGraph.description;
                        this.ogpMeta.image = jsonResponse.hybridGraph.image;
                        this.ogpMeta.site_name = jsonResponse.hybridGraph.site_name;
                    }
                })
        } else {
            this.content = contentValue
                .replace(/&lt;/g, '<')
                .replace(/&gt;/g, '>')
                .replace(/&quot;/g, '\"')
                .replace(/<a href='mailto:.*?' target='_blank'>(.*?)<\/a>/g, '$1')
                .replace(/<a href='/g, '')
                .replace(/' target='_blank'.*?<\/a>( +)/g, '$1')
                .replace(/' target='_blank'.*?<\/a>.*?<\/a>/g, '');
        }
    }

    extractOriginalUrl(generatedString) {
        const matched = generatedString.match(/<a href.+>(.*?)<\/a>/);
        if (matched.length > 1) {
            return matched[1];
        }
        return generatedString;
    }

    fallback(event) {
        event.target.onerror = null;
        event.target.style.display = 'none';
        event.target.style.height = 0;
    }

    get isAgent() {
        return this.userType === 'agent';
    }

    get isPlainText() {
        return this.messageType === DEFAULT_MESSAGE_PREFIX;
    }

    get isRichText() {
        return this.messageType === RICHTEXT_MESSAGE_PREFIX;
    }

    get isYoutube() {
        return this.messageType === YOUTUBE_MESSAGE_PREFIX;
    }

    get isImage() {
        return this.messageType === IMAGE_MESSAGE_PREFIX;
    }

    get isLwc() {
        return this.messageType === LWC_MESSAGE_PREFIX;
    }

    get isUrl() {
        return this.messageType === URL_MESSAGE_PREFIX;
    }

    get isNavigate() {
        return this.messageType === NAVIGATE_MESSAGE_PREFIX;
    }

    get hasOGPInfo() {
        return this.ogpMeta.title !== undefined;
    }
}

chatMessage.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
      <target>lightningSnapin__ChatMessage</target>
    </targets>
</LightningComponentBundle>

chatMessage.css

.chatMessage {
    font-size: 0.875em;
    max-width: 70%;
    padding: 10px;
    white-space: pre-wrap;
    text-align: left;
}

.chasitor {
    color: #FFF;
    background: #005290;
    margin-left: auto;
    border-radius: 10px 10px 0;
    float: right;
}

.agent {
    margin-left: 40px;
    border-radius: 10px 10px 10px 0;
    float: left;
}

.agent.plainText {
    color: #333;
    background: #f4f4f4;
}

.richText {
    border: 1px solid #AAA;
}

.youtube, .image, .url {
    max-width: 100%;
}

.youtube iframe, .image img, .url img {
    max-width: 100%;
    border: 0;
}

.url {
    position: relative;
    padding: 0;
    margin-right: 40px;
    border-radius: 10px;
    border: 1px solid #DDD;
}

.url a {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
}

.url img {
    border-radius: 10px 10px 0 0;
    width: 100%;
    height: 150px;
    object-fit: cover;
}

.url .ogpInfo {
    padding: 5px;
    color: #333;
}

.url .ogpInfo .title {
    word-break: break-all;
    display: -webkit-box;
    overflow: hidden;
    -webkit-line-clamp: 1;
}

.url .ogpInfo .description {
    margin-top: 10px;
    font-size: 0.8rem;
    color: #3e3e3c;
    word-break: break-all;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
}

.url .ogpInfo .site_name {
    margin-top: 10px;
    font-size: .75rem;
    color: #3e3e3c;
}

Deploy this component to Salesforce Org.

Now, Got to Embedded Service Deployments.

img

Click on Edit.

img

Scroll down to Customize with Lightning Components

And choose chatMessage.

img

Now, We need to update Dialogs in Einstein Bot.

Go to Einstein Bot Builder.

Go to : Setup > Einstein Bots > yourBot

After clicking on your Bot, You'll go to Einstein Bot Builder.

img

Now, need to update Dialogs.

If a message in Einstein Bot starts with

COMPONENT:<COMPONENT NAME>

Shows the Component in Einstein Bot.

RICH_TEXT:<RICH_TEXT_CONTENT_INCLUDING_SUPPORTED_HTML_TAGS>

Shows the message in rich text format.

YOUTUBE:<YOUTUBE_VIDEO_ID>

Shows the youtube video in chat window.

IMAGE:<IMAGE_URL>

Shows the image.

NAVIGATE:<TARGET_URL> 

Opens the url with a new browser window (tab).

URL:<TARGET_URL> 

Shows the OGP info.

PLAIN_TEXT:<MESSAGE>

Shows the message in plain text format.

In my case

For Component :

COMPONENT:myCalculator

After updating all the Dialogs

Activate the Einstein Bot and Test it.

Click on right bottom button "Chat with an Expert"

img

After Greeting Messages, answer quick questions.

And Type: Calculator

img

Now It'll Show you Calculator Component.

img

As you can see, It's working now.

This is how we can use Lightning Web Components in Einstein Bot.

I took reference from this repository and Updated it for Lightning Web Components.

I hope It'll help you somehow.

If you have any question Ask Me

Thanks for Reading :)

Write a comment for suggestions and hit the heart icon.


5040
4

Tags: #lwc #EinsteinBot

Comments

  • Madhu Javvaji Avatar
    Madhu Javvaji - 6 months ago
    Hi, Thank you its a good article. Can you please share chatMessageStyle resource file. Regards, Madhu

  • Hardika Avatar
    Hardika - 6 months ago
    Great article. One question - if I want to send data from the component back to the bot, how can I do so?

  • Sandeep Avatar
    Sandeep - 2 months ago
    How can we send a response from the LWC component back to bot

  • Mukesh Tiwari Avatar
    Mukesh Tiwari - 1 week ago
    The same question as Sandeep asked: How can we send a response from the LWC component back to the bot, Also bot is asking the next question without completing the first LWC question.

Your message is required.

Get notified of new posts