Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The LogicDialog SDK is a Node JS project that allows you to construct replies to a conversation programmatically. By providing an SDK and using the Webhook functionality in the product your conversations can become much more dynamic and meaningful to your users.
The SDK is simple to use and so you'll be up and running in no time. If you are keen to dive in then take a look at our quick start guide.
Dive a little deeper and start exploring our API reference to get an idea of everything that's possible with the API:
On this page we'll get you setup using the SDK to provide a simple "Hello World" reply to a users message.
In this guide we are going to setup a simple web server that can respond to requests. This web server will be triggered using the "webhook" capability within logicdialog. There are just a few steps we'll need to take to get you all setup. These are as follows :
Setting the Webhook URL settings
Building some content that will trigger the webhook
Setting up a project and creating an instance of the Webhook Client
Adding functionality to the project to handle specific types of request
These steps are all described below.
In this example we are going to use ngrok
to provide an internet accessible hostname. You will need to install ngrok
first. Once installed open a command terminal and type
ngrok http <server port to expose>
In this case the <server port to expose>
is going to be 8080
.
The command will start and provide to URLs, one on http and the other on https. Make a note of the https
URL as you will need this shortly.
Once you have the URL we'll need to add this to the settings in IA. To do this :
Login to logicdialog
Go to Settings -> Webhook
Set Endpoint to the forwarding URI from ngrok
Please make a note of the JWT secret that is shown on the Webhook settings page. We'll use this to authenticate our requests later.
Using Visual Bot Builder in logicdialog, add new Choice using plus buttons to the far left or right of the existing choices, or add to existing choice by using the plus button on said choice
Choose New Block and then Function Block
Give block a name, and pick module if applicable
If function has not been implemented, leave Todo field ticked, and add Temporary Text, usually a description of function implementation, and add button to simulate response. If you want your webhook to receive an event when this block is hit by a user, "Todo" must be unchecked
Pick Form from dropdown if applicable
Pick Handler Name, note this as it is used when adding handlers to the WebhookClient
We will also need to...
In logicdialog go to the Forms page
Click add new form, and give form a descriptive title
Add new question, or pick one from the question bank
Give descriptive title to question
Write question text, to be displayed when question is asked
Pick input type given to the user, followed by data type
A question can also be added to the question bank from the Question Bank tab
The best way to interact with our API is to use one of our official libraries:
# Install via NPM
npm install --save @webuildbots/webuildbots-sdk
The webhook from IA will make an HTTP request to a webserver containing your application logic. This could be a number of things, including a serverless function however in this example we are going to use express
to host our API. We can setup express
using the following simple bits of code.
import { createServer } from 'http';
import express, { Request, Response } from 'express';
import { divisionHandler } from './handler';
import { WebhookClient } from '@webuildbots/webuildbots-sdk/lib/util/webhook-client';
import dotenv from 'dotenv';
// Load Webhook Secret, this can be found in IntelAgent settings
dotenv.config();
const secret = process.env['WEBHOOK_SECRET'] || '';
// WebhookClient used to run handlers, intialised with JWT token copied previously
const whClient: WebhookClient = new WebhookClient(secret);
// Add handler to the list of handlers in the Client, name must match the functions handler-name
whClient.addHandler('division-handler', divisionHandler);
// Function to getToken from request, used to authorise request and secret
function getToken(request: Request): string {
const token = request.headers.authorization?.split('Bearer ')[1];
if (!token) {
throw Error('No token found');
}
return token;
}
const app: express.Application = express();
// Text body-parser, as handleRequest function takes body from request as a string
// Content-type of request is plaintext, which is parsed by the handleRequest function
app.use(express.text());
// This should match the endpoint set in IntelAgent
app.post('/', async (req: Request, res: Response) => {
// handleRequest returns a JSON object and a statusCode
const { status, body } = await whClient.handleRequest(
req.body,
getToken(req)
);
res.status(status).send(body);
});
const server = createServer(app);
server.listen(8080);
This example will allow us to POST
data to the /
endpoint running on port 8080
. First we create an instance of the WebhookClient
which will allow us to handle the request and return various values back to the user. You'll notice on line 13
we construct a handler
which will handle the division-handler
requests. By adding this handler we can build an application that can handle different requests from different parts of the application, each one using a different name that is set in IA on a block by block level. On line 36
the webhook client will handle the request - specifically looking for a handler that matches the incoming requests handle name.
A webhook handler is a simple function that will be called any time a block is triggered from IA. Its passed the webhookReq
object which represents the information in the conversation, and a responseBuilder
that helps you construct a reply to the user.
The following example shows a simple case where one number is divided by another, and an error is thrown if this combination of numbers provides a divide by zero error. A plain text message will be sent back to the user containing the answer.
export const divisionHandler: WebhookHandler = async (
webhookReq: WebhookRequest,
responseBuilder: ResponseBuilder
): Promise<void> => {
// read values from the form completed by the user before the call over the webhook was made
const { value: baseNumber } = webhookReq.formValue.baseNumber;
const { value: divisionNumber } = webhookReq.formValue.divisionNumber;
if (divisionNumber.value === 0) {
responseBuilder.functionFailure(failureParams);
return;
}
const resultNumber = baseNumber / divisionNumber;
responseBuilder.pushBlockPointer(resultBlock);
const basicBB = new BasicBB()
.addText(Languages.ENGLISH, `${resultNumber}!`)
.pushChoice(backToMenu());
responseBuilder
// Add the block to the response
.pushBlock(basicBB)
// Reset the form values after the response is sent to the user
.unsetFunctionForm();
};
For more information please see the following pages :
Using a BlockPointer you can route the conversation to different blocks, potentially to handle different questions or continue the next steps.
A BlockPointer
points to an already existing Block using the Block Id, found in logicdialog.
Where possible this method should be used as it allows for block content to be edited easily without having to deploy changes to your code.
A block pointer is a simple object containing the ID of the block you wish to link to. In the example below, the ID of this block would be 5c62ead3ab358c3780bb60dc
.
To use a BlockPointer
, the pushBlockPointer
function in the ResponseBuilder
is used, with a BlockPointer
object, containing the id of a block. This block can be a basic block, or a function block.
responseBuilder.pushBlockPointer({
id: "60db2d02d19a13bc109c5bd4"
});
For further reference on the ResponseBuilder
, please see the API page.
Initialise a new instance of BasicBB
to start building a block.
Add text with the addText function, specifying a Language with the Languages Enum, if the text needs to be translated to different languages, do addText for each language. Here the text added is the result of the calculation done earlier
Errors can happen, and so by including an error handler in your response processing we can ensure that users are always informed.
If an input is invalid, or another error is caught, the responseBuilder
can return a functionFailure
,
The failureParams object is a particular type used to indicate how to handle the error. It contains an error BlockPointer, pointing to a block with an error message, as well as two optional fields, resetForm
and tryAgain
, defaulting to true.
resetForm
indicates that the form should be unset, similarly to the unsetFunctionForm
function
tryAgain
offers a button to try again if true
For example, in the division handler, one such case of invalid input, is a divide by zero error. This is a number that cannot be divided by as of yet, so that number is checked early in the function, before further logic.
A number of content types are available when using the SDK. These are as follows :
if (divisionNumber.value === 0) {
responseBuilder.functionFailure(failureParams);
return;
}
The UrlCB
works similarly to the BlockCB as a choice, however rather than point to a Block, it contains a URL.
The GalleryBB is used to show a list of elements, such as an array of products, or when info should be displayed in a particular way Each item in the GalleryBB is built using the ItemBuilder.
The ItemBuilder is used similarly to the BasicBB, in that a title, text, and Choices can be added They differ in the additional features, such as being able to contain an image, from a URL, and adding a subtitle.
If multiple items are added, each of these Items are be displayed in a Carousel, letting the user flip through them like a book, to find what they are looking for.
A block can also contain buttons, or Choices. These can be added using the pushChoice function to push a ChoiceBuilder Block, of which there are several, depending on need. UrlCB links to a URL, BlockCB points to a BlockId. Here a button, that leads points back to the Menu, is added to the BasicBB with the result string
basicBB.pushChoice(
new BlockCB("60e2b7f2ba7625f3fb73dd56")
.addTitle(Languages.ENGLISH, "Back to Menu")
);
Some Choices that are used quite often, including the one above, has been pre-built so there is no need to build it repeatedly.
const basicBB = new BasicBB();
basicBB.addText(Languages.ENGLISH, `${resultNumber}!`);
/**
* BlockPointer containing block id found in IntelAgent
*/
const errorBlock: BlockPointer = {
id: '60db2d02d19a13bc109c5bd4'
};
/**
* Function failure params used to handle errors, for example in the case of bad data
*/
const failureParams: FunctionFailureParams = {
errorBP: errorBlock,
resetForm : true,
tryAgain : true
};
responseBuilder.functionFailure(failureParams);
basicBB.pushChoice(new UrlCB()
.addTitle(Languages.ENGLISH, "Google")
.addUrl(Languages.ENGLISH, "https://www.google.com/"));
const complexBB = new ComplexBB();
const ib = new ItemBuilder();
ib.image("<url>")
ib.addSubtitle(Languages.ENGLISH, "subtitle text")
complexBB.pushItem(ib)
The WebhookClient is the entrypoint into the SDK. Here we can setup handlers that will respond to various events from the bot.
A WebhookClient
is instantiated using the JWT Secret found in logicdialog. This is used to call the handler function.
There are two main methods on the Webhook client - handleRequest
and addHandler
The handleRequest
function is called from within a code that has been setup to handle HTTP requests made to your application. This might be part of an express
route, or some other process. The function is passed two arguments. The first of these arguments is the body of the HTTP request which has originated from logicdialog. The second argument provides the authorization token used on the incoming request. This token is used to verify the payload that has been sent has not been modified in transit.
Examples of these function calls are provided below.
The addHandler
function allows you to specify a function that can be run to process the data coming in from logicdialog. Much like event subscriptions, this handler is associated with a specific name and as such you can add multiple handlers to a webhook client so that they can handle different types of events from your bot.
The handler that you create is an asynchronous function that accepts two parameters - a WebhookRequest
object and a link to a ResponseBuilder
.
A new WebhookHandler
is initiated as follows :
Once you have created a handler, you can add it to your webhook client using the addHandler
method.
In the example above, only forms in logicdialog that have a handler specified as division-handler
will trigger the function.
Inside the function you can find the parameters that have been set in the conversation within the webhookReq
object. Each form will differ depending on the number and type of questions that are asked to the user however the following example shows how you might access two values of a form.
The formValues
in the example are structured as the following interface:
For more information about constructing a reply to the user, please see the ResponseBuilder
section.
export const divisionHandler: WebhookHandler = async (
webhookReq: WebhookRequest,
responseBuilder: ResponseBuilder
): Promise<void> => {
// Logic goes here
}
whClient.addHandler('division-handler', divisionHandler);
const {value: baseNumber} = webhookReq.formValue.baseNumber;
const {value: divisionNumber} = webhookReq.formValue.divisionNumber;
{
baseNumber: {
title: string,
value: string
},
divisionNumber: {
title: string,
value: string
}
}
export const whClient: WebhookClient = new WebhookClient('secret');
// This should match the endpoint set in IntelAgent
app.post('/', async (req: Request, res: Response) => {
// handleRequest returns a JSON object and a statusCode
const { status, body } = await whClient.handleRequest(
req.body,
getToken(req)
);
res.status(status).send(body);
});
function getToken(request: Request): string {
const token = request.headers.authorization?.split('Bearer ')[1];
if (!token) {
throw Error('No token found');
}
return token;
}
The ResponseBuilder
can store multiple Blocks
and BlockPointers
, to be displayed in the Chat Bot, in the order they were added to the response. If a Form was used, it may be necessary to unset it once finished, letting the user trigger the function again, with different input
responseBuilder.unsetFunctionForm();
Along with the Quick Start pages, we've also provided a sample project via gitlab which can help you get up and running. This project is designed as as Google Cloud Function meaning that it'll only be running for the times that it needs to be used. You can find out more about cloud functions here.
Within the project you'll find the following folders :
src
- this is the main folder for the project. All of your application logic should go in here.
src/config
- here we've provided a way of allowing you to swap configuration variables between environments.
src/const
- Any constants that might be used in your code. As an example we provide const
variables for environment.
src/handlers
- This contains the logic for your function and will be the entry point for the function.
src/model
- this provides a utility function to help manage the configuration between environments.
src/util
- a few other utility functions that you might find useful.
scripts
- add any build scripts you need in here. We've provided one script in this folder to help get your environment variables into you project.
package.json
- As a NodeJS project, this file determines the tasks that you can run, as well as any dependancies you might need for your application.
tsconfig.json
- This is the configuration for the TypeScript
files.
webpack.config.js
- This is the configuration for Webpack
To get the project running you will first need a NodeJS environment on your computer. We'd recommend using Node Version Manager
or nvm
for short to help you manage the various different versions of NodeJS to install.
The page above provides instructions for installing nvm
. Once installed, we recommend a version 12.x
of NodeJS. You can install this on your computer by running nvm install 12
.
The file contains all the various scripts and commands you will need to build the source code into an executable format.
To start with you'll need to run npm install
from within the project folder to install all the external dependancies listed in the package.json
. These dependancies will include TypeScript
.
The project source code is written in TypeScript
however as part of the build process we compile the typescript into normal JavaScript. This is done using the tsc
command. As defined in the package.json
file, we have provided a watch
command which will run this command with the -w
flag so that it automatically builds any modified files in the source directory. Depending on how you are working you can either run tsc
once, or npm run watch
to build the files as they change.
The result of the tsc
command will be a series of files in the .build
folder which mirror that of the src
folder, but are .js
files rather than .ts
files. It is these files that are used to run the cloud function.
To start the cloud function, use npm start
. This will use the functions-framework
tool that emulates what it will be like running the function via Google Cloud. This process will start the webserver and expose the application on typically port 8080
. This means that once running you should be able to send network requests to your application via http://localhost:8080
As mentioned in the documentation, to test this when running locally you will need something like ngrok
to make your local application available to the internet. You can read more about that here. Once you have done this, or if the application is deployed to a public URL then you can modify the settings of your digital assistant to point to it.
Also provided within the package.json
file is a build
task that will help you package the files in an appropriate format for a live environment. This process will combine all the files into one, and "minimise" them so that they can be transferred and loaded as quick as possible. To run this process use npm run build
. The output of this task will be in the .webpack
folder and will be essentially an index.js
file, along with a package.json
file.
As each environment is different we have not provided samples to help you deploy your application to something like Google Cloud, however if you have followed the steps above then you should have everything you need. For reference, the documentation for Google Cloud is below.
In this context, we would use the following command to deploy this project, however please adapt this to suit your needs.
gcloud functions deploy sample_project --source=.webpack --entry-point handler --runtime nodejs12 --trigger-http --allow-unauthenticated --region europe-west2
If you have used environment variables with your application then we'd recommend the use of a env.yaml file to provide the variables to the Cloud environment. More information is available below.
To make use of this file, simply add --env-vars-file env.yaml
to the end of the deployment command line options.