The bot builder provides you with commonly used functions for your daily operations. Learn more about the default functions in Freshchat here.
Apart from these default functions in Freshchat, we are opening up the bot builder for you to define and create multiple custom JavaScript (JS) functions that you can declare, define, and call from within the bot builder.
There are two ways to use custom JS functions for your bots in Freshchat:
Defining your functions within the bot builder
- You can define JS functions within the bot builder and call them as part of an action inside your bot flows.
- This is particularly useful when you need to call functions in your bot that are deployed on a channel that doesn't support JS functions like WhatsApp, Insta, or Facebook Messenger, etc.
Configuration inside the bot builder
- The first step is to define the custom JS function. This can be done in two places inside the bot builder:
- Define the function directly inside a dialog:
Navigate to the dialog in the bot builder in which you need to call a function and use the + icon on the bottom right. - Or you can directly navigate to Flows > Configure > Custom functions > New function. Either way, you will end up on this screen:
- Define the function directly inside a dialog:
- Name the JS Function and a description for your reference.
- Your output type will show up as a JSON file. This is particularly useful when you need to transmit or store data in a structured format, such as when sending data to a web server or storing data in a database.
- Use the code editor to define the function per your business use case. You can use dynamic values such as Dialogs, APIs, Properties, or other Functions. You can also pass properties from any dialog to the function.
You can add an entire response body to Custom function and incorporate a logic to return a string or an array. Below is an example to highlight the capabilties.
Sample API Payload
Here is a sample API payload that you have received from your API. Let's say you need to filter these order IDs to show only those orders having value greater than $160.
{
"customer": {
"id": "cust_123456789",
"name": "John Doe",
"phone": "+1234567890",
"email": "[email protected]"
},
"orders": [{
"order_id": "123456789",
"created_at": "2024-06-18T12:00:00Z",
"order_status": "shipped",
"payment_status": "paid",
"total_price": "300.00",
"currency": "USD",
"order_tracking_url": "https://app.orders.com/track/123456789",
"items": [{
"item_id": "item_2001",
"name": "Bluetooth Headphones",
"quantity": 1,
"price_per_unit": "150.00",
"total_item_price": "150.00",
"currency": "USD"
},
{
"item_id": "item_2002",
"name": "Air Pods",
"quantity": 1,
"price_per_unit": "150.00",
"total_item_price": "150.00",
"currency": "USD"
}
],
"payment_info": {
"payment_id": 10101,
"payment_type": "Gift Voucher",
"payment_gateway": "GV",
"amount": "300.00",
"currency": "USD"
}
},
{
"order_id": "987654321",
"created_at": "2024-06-19T15:30:00Z",
"order_status": "processing",
"payment_status": "pending",
"total_price": "200.00",
"currency": "USD",
"order_tracking_url": "https://app.orders.com/track/987654321",
"items": [{
"item_id": "item_3001",
"name": "Smartwatch",
"quantity": 1,
"price_per_unit": "200.00",
"total_item_price": "200.00",
"currency": "USD"
}],
"payment_info": {
"payment_id": 20202,
"payment_type": "Cards",
"payment_gateway": "AmEx",
"amount": "200.00",
"currency": "USD"
}
}
]
}
- Below is the JS code that can be added to your custom function library to achieve this use-case.
function isJSONString(str) { try { JSON.parse(str); return true; } catch (error) { return false; } } function getOrderIdsAndItemsWithHighTotalPrice(jsonData, priceThreshold) { let inputData; if (typeof jsonData === "string" && isJSONString(jsonData)) { inputData = JSON.parse(jsonData); } else { inputData = jsonData; } const orderIds = []; const items = []; if (inputData && inputData.orders && Array.isArray(inputData.orders)) { inputData.orders .filter(order => parseFloat(order.total_price) > priceThreshold) .forEach(order => { orderIds.push(order.order_id); const itemNames = order.items.map(item => item.name).join(", "); items.push(itemNames); }); } return { orderIds, items }; } const inputValue = data['Response_body']; const highPriceOrders = getOrderIdsAndItemsWithHighTotalPrice(inputValue, 160); console.log("Order IDs with total price greater than 160:", highPriceOrders.orderIds); console.log("Items for each order:", highPriceOrders.items); return { orderIds: highPriceOrders.orderIds, items: highPriceOrders.items };
You can save the OrderIds[] as a response from the custom function to be added to the button input option for the bot.Ensure that you have first added the API and saved the Response Body to be used in the custom function before writing the function.
The following is a screenshot of the configuration to help you further.
- You can also run tests on response parameters and check responses and the respective console logs from this page.
Configuring the dialogs
Now that the function has been defined, we can call the function from anywhere in the bot flow. To call the function, you'll need to set up an action in the dialog for the bot to trigger the function.
- Navigate to the flow where you want the function to run > new Action > Trigger custom function > select the custom function you just configured.
Limitations
The maximum output size supported is 5 KB and the maximum size of code supported is 10 KB.
The maximum number of custom functions that can be created for a single bot is 20.
The maximum size of all input parameters (from Dialogs, APIs, Properties, Functions) that can be inserted into the code should be less than 200 KB.
The following keywords and libraries are not yet supported within custom functions. We are working on supporting them as soon as possible.
eval exec export import execfile evalfile open os.system dir path child_process new Function require rimraf shelljs mv spawn fork dns promisify pickle process.* subprocess input context.* fs.* util.* vm.* .prototype eval, exec, export, import, execfile, evalfile, open, os.system, dir, path, child_process, new Function, require, rimraf, shelljs, mv, spawn, fork, dns, promisify, pickle, process.*, subprocess, input, context.*, fs.*, util.*, vm.*, .prototype, setTimeout, setInterval, new Error, runinthiscontext, constructor.constructor, sub.constructor
Defining your functions on your website
- You can define JS functions on your website and trigger them using the execute JS action inside your bot flows.
- This is particularly useful when you need to call functions in your bot that are deployed on your website. The action will pass any parameters to that script function and return the response to the bot.
Let's walk you through how this is done with the example of a simple JS function:
<script> print: function (a, b, c) { console.log("print called: a:" + a + " b: " + b + " c: " +c); } </script>
Note: You can write any function as a logic into this script. This is just one example.
Configuration inside the bot builder
The first step is to define the custom JS function to work as part of the APIs in the bot builder. Navigate to Flows > Configure > API Libary > Add new.
- Name the API. This is just for your reference.
- Choose JS Function for the Method and JSON for the Payload type.
- Name the JS Function. This is just for your reference.
- Use the URL / JS function name field, and enter the function's name. This should be the same name you will use to refer to the custom JS function elsewhere, such as the widget embed script. In our example, it is "print". Remember that the function name is case-sensitive.
- Enter the argument values you need to pass in the Payload content as single-line JSON values. These argument values can also be dynamic. Use the + button to include dynamic values.
- Use the Required response parameters field to pass your success and failure response parameters.
The bot will pass these inputs to the script after calling the custom JS function. When the function executes, it will expect a response parameter. You can pass these response parameters from the script to the bot based on the action's result. You can set up the bot to collect the values that must be passed in the payload content. Here's what it looks like:
You can call the response value in other parts of the bot flow. For example, suppose you're setting up the customer input as a message or a question. In that case, you can insert content from APIs, or in this case, insert the result of the JS function (success or failure response parameter).
Configuring the dialogs
Now that the function has been defined, we can call the function from anywhere in the bot flow. To call the function, you'll need to set up an action in the dialog for the bot to trigger the function.
- Navigate to the flow where you want the function to run > new Action > Trigger API > select the API you just configured.
- This will trigger the bot to run this action, which will call the API, which will trigger the configured JS function.
Configuration in the widget script
- Open your bot and ensure it's on the latest version > Publish > Navigate to the Deploy menu > select Self-service widget from the dropdown > Save.
- Copy the widget deployment script from the right and paste this code onto a script editor so that you can include your script inside this code
- Include your custom JS function after the getClientParams function is defined.
- Let's take the print function we showed earlier for this example. We shall define it within our widget bot script as seen below:
< script >
function initFreshChat() {
window.fcWidget.init({
token: "c72a7c04-e792-4516-8a74-54a5b958571a",
host: "https://dcx1.freshchat.com",
jsFunctions: {
sampleFun: function () {
console.log("JS Function ran successfully");
return "returnValue";
}
}
});
}
function initialize(i, t) {
var e;
i.getElementById(t) ?
initFreshChat() : ((e = i.createElement("script")).id = t, e.async = !0,
e.src = "https://dcx1.freshchat.com/js/widget.js", e.onload = initFreshChat, i.head.appendChild(e))
}
function initiateCall() {
initialize(document, "Freshchat-js-sdk")
}
window.addEventListener ? window.addEventListener("load", initiateCall, !1) :
window.attachEvent("load", initiateCall, !1); <
/script>
Note:
To add JS Function to widget code on the latest version of Freshchat, you can follow the SDK documentation here
< script src = '//fw-cdn.com/1875422/2681344.js'
chat = 'true' >
<
/script> <
script >
window.fcWidgetMessengerConfig = {
jsFunctions: {
sampleFun: function () {
console.log("JS Function ran successfully");
return "returnValue";
}
}
}; <
/script>
- Once you define this function in the bot script, you can call it anywhere in the bot flow.
Note: Keep in mind that if you're using a client-side action to call the JS function, you'll have to get the user input as a text field. This is to denote that the function is returning a response.
Limitations
- This is suitable for use on websites but may not be applicable for third-party channels such as Facebook Messenger, WhatsApp, and Instagram.
- Another difficulty in using custom functions directly on the website is that multiple teams (web developers, web designers, and back-end teams) need to collaborate to get started.
Some use cases
- If you have a phone number that your customer needs to call, for example, a toll-free phone number, you can have a button that says "call us", which your customer can tap or click. This can trigger the JS function, which in turn will populate the default phone dialler with your toll-free number. In this case, WebView should have permission to access the dialler for Android devices. The phone dialler permission should be allowed at the manifest file level.
<script>
// initiate Freshbots.
(function (d, w, c) {
if (!d.getElementById("spd-busns-spt")) {
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script");
var loaded = false;
s.id = "spd-busns-spt";
s.async = "async";
s.setAttribute("data-self-init", "false");
s.setAttribute("data-init-type", "opt");
s.src = "https://cdn.freshbots.ai/assets/share/js/freshbots.min.js";
s.setAttribute("data-client", "xxxxxxxxxx");
s.setAttribute("data-bot-hash", "xxxxxxxxxxx");
s.setAttribute("data-env", "prod");
s.setAttribute("data-region", "us");
if (c) {
s.onreadystatechange = s.onload = function () {
if (!loaded) {
c();
}
loaded = true;
};
}
n.parentNode.insertBefore(s, n);
}
})(document, window, function () {
Freshbots.initiateWidget(
{
autoInitChat: false,
getClientParams: function () {
return;
},
// openPhone JS custom function. pass the number to the function.
openPhone: function(number){
document.location.href = "tel:" + number;
return {
message: "success"
}
},
},
function (successResponse) {},
function (errorResponse) {}
);
});
</script> - If you've deployed the bot on your mobile app and want to take your customer to a specific page on your app, you can do so using a deep link in the app.
<script>
function getMobileOperatingSystem() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if( userAgent.match( /iPad/i ) || userAgent.match( /iPhone/i ) || userAgent.match( /iPod/i ) )
{
return 'iOS';
}
else if( userAgent.match( /Android/i ) )
{
return 'Android';
}
else
{
return 'unknown';
}
}
(function (d, w, c) {
if (!d.getElementById("spd-busns-spt")) {
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script");
var loaded = false;
s.id = "spd-busns-spt";
s.async = "async";
s.setAttribute("data-self-init", "false");
s.setAttribute("data-init-type", "opt");
s.src = "https://cdn.freshbots.ai/assets/share/js/freshbots.min.js";
s.setAttribute("data-client", "xxxxxx");
s.setAttribute("data-bot-hash", "xxxxxxx");
s.setAttribute("data-env", "prod");
s.setAttribute("data-region", "us");
if (c) {
s.onreadystatechange = s.onload = function () {
if (!loaded) {
c();
}
loaded = true;
};
}
n.parentNode.insertBefore(s, n);
}
})(document, window, function () {
Freshbots.initiateWidget(
{
autoInitChat: false,
getClientParams: function () {
return;
},
// load the apps based on the username and platform sent to this app.
loadDeepLink: function(userName, platform){
const deepLinkInstagramDomain = "instagram://";
const deepLinkTwitterDomain = "twitter://";
const deepLinkFacebookDomain = "facebook://";
var urls = {
instagram: `http://www.instagram.com/${userName}`,
twitter: `http://www.twitter.com/${userName}`,
facebook: `http://www.facebook.com/${userName}`
}
switch(getMobileOperatingSystem()){
case 'Android':
case 'iOS':
urls["instagram"] = `${deepLinkInstagramDomain}user?username=${userName}`;
urls["twitter"] = `${deepLinkTwitterDomain}user?username=${userName}`;
urls["facebook"] = `${deepLinkFacebookDomain}user?username=${userName}`;
break;
default:
break;
}
document.location.href = urls[platform]
return {
message: "success"
}
}
},
function (successResponse) {},
function (errorResponse) {}
);
});
</script> - Let's say your business is location-based, so you want to display the nearest store to your customers or you want to check if you deliver to their location, you can get the location based on their browser data to personalize the customer experience.
<script>
// get location during page load.
var lat = 0;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
function showPosition(position) {
long = position.coords.longitude;
lat = position.coords.latitude;
}
}
// initiate Freshbots.
(function (d, w, c) {
if (!d.getElementById("spd-busns-spt")) {
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script");
var loaded = false;
s.id = "spd-busns-spt";
s.async = "async";
s.setAttribute("data-self-init", "false");
s.setAttribute("data-init-type", "opt");
s.src = "https://cdn.freshbots.ai/assets/share/js/freshbots.min.js";
s.setAttribute("data-client", "xxxxxxxxxx");
s.setAttribute("data-bot-hash", "xxxxxxxxxxx");
s.setAttribute("data-env", "prod");
s.setAttribute("data-region", "us");
if (c) {
s.onreadystatechange = s.onload = function () {
if (!loaded) {
c();
}
loaded = true;
};
}
n.parentNode.insertBefore(s, n);
}
})(document, window, function () {
Freshbots.initiateWidget(
{
autoInitChat: false,
getClientParams: function () {
return;
},
// getLocation JS custom function.
getLocation: function () {
if (navigator.geolocation) {
return { message: "success", lat: lat, long: long };
} else {
return { message: "not supported", lat: "NA", long: "NA" };
}
},
},
function (successResponse) {},
function (errorResponse) {}
);
});
</script>{ "customer": { "id": "cust_123456789", "name": "John Doe", "phone": "+1234567890", "email": "[email protected]" }, "orders": [{ "order_id": "123456789", "created_at": "2024-06-18T12:00:00Z", "order_status": "shipped", "payment_status": "paid", "total_price": "300.00", "currency": "USD", "order_tracking_url": "https://app.orders.com/track/123456789", "items": [{ "item_id": "item_2001", "name": "Bluetooth Headphones", "quantity": 1, "price_per_unit": "150.00", "total_item_price": "150.00", "currency": "USD" }, { "item_id": "item_2002", "name": "Air Pods", "quantity": 1, "price_per_unit": "150.00", "total_item_price": "150.00", "currency": "USD" } ], "payment_info": { "payment_id": 10101, "payment_type": "Gift Voucher", "payment_gateway": "GV", "amount": "300.00", "currency": "USD" } }, { "order_id": "987654321", "created_at": "2024-06-19T15:30:00Z", "order_status": "processing", "payment_status": "pending", "total_price": "200.00", "currency": "USD", "order_tracking_url": "https://app.orders.com/track/987654321", "items": [{ "item_id": "item_3001", "name": "Smartwatch", "quantity": 1, "price_per_unit": "200.00", "total_item_price": "200.00", "currency": "USD" }], "payment_info": { "payment_id": 20202, "payment_type": "Cards", "payment_gateway": "AmEx", "amount": "200.00", "currency": "USD" } } ] }
Below is the JS code that can be added to your custom function library to achieve this use-case.
function isJSONString(str) { try { JSON.parse(str); return true; } catch (error) { return false; } } function getOrderIdsAndItemsWithHighTotalPrice(jsonData, priceThreshold) { let inputData; if (typeof jsonData === "string" && isJSONString(jsonData)) { inputData = JSON.parse(jsonData); } else { inputData = jsonData; } const orderIds = []; const items = []; if (inputData && inputData.orders && Array.isArray(inputData.orders)) { inputData.orders .filter(order => parseFloat(order.total_price) > priceThreshold) .forEach(order => { orderIds.push(order.order_id); const itemNames = order.items.map(item => item.name).join(", "); items.push(itemNames); }); } return { orderIds, items }; } const inputValue = data['Response_body']; const highPriceOrders = getOrderIdsAndItemsWithHighTotalPrice(inputValue, 160); console.log("Order IDs with total price greater than 160:", highPriceOrders.orderIds); console.log("Items for each order:", highPriceOrders.items); return { orderIds: highPriceOrders.orderIds, items: highPriceOrders.items };
You can add an entire response body to Custom function and incorporate a logic to return a string or an array. Below is an example to highlight the capabilties.