How to build a chatbot with Preact and Wit.ai
You will need a recent version of Node and npm installed on your machine. A basic understanding of React or Preact will be helpful.
In this tutorial, we will consider how to build a realtime chatbot that incorporates NLP using Preact, Wit.ai and Pusher Channels. You can find the entire source code of the application in this GitHub repository.
Chatbots have become more and more prevalent over the past few years, with several businesses taking advantage of them to serve their customers better.
Many chatbots integrate natural language processing (NLP) which adds a more human touch to conversations, and helps them understand a wider variety of inputs.
Prerequisites
Before you continue, make sure you have Node.js, npm and curl
installed on your computer. You can find out how to install Node.js and npm here.
The versions I used while creating this tutorial are as follows:
- Node.js v10.4.1
- npm v6.3.0
You can view the version of Node and npm
you have installed by running the following commands in your terminal:
node -v
npm -v
I believe the code will still work even if you’re on an older version of Node, but if you have any trouble completing the tutorial, try upgrading to the versions I used to see if it fixes your problem.
Also investigate how to install curl
on your favorite operating system, or use this website.
Finally, you need to have a basic understanding of JavaScript and Preact or React, but no prior experience with Pusher or Wit.ai is required.
Getting started
Let’s bootstrap our project using the preact-cli tool which allows us to quickly get a Preact application up and running.
Open up your terminal, and run the following command to install preact-cli
on your machine:
npm install -g preact-cli
Once the installation completes, you’ll have access to the preact
command that will be used to setup the project. Run the following command in the terminal to create your Preact app:
preact create simple preact-chatbot
The above command will create a new directory called preact-chatbot
and install preact
as well as its accompanying dependencies. It may take a while to complete, so sit tight and wait. Once it’s done, you should see a some information in the terminal informing you of what you can do next.
Next, change into the newly created directory and run npm run start
to start the development server.
Once the application compiles, you will be able to view it at http://localhost:8080. When you open up that URL in your browser, you should see a page on your screen that looks like this:
Create your application frontend with Preact
Open up index.js
in your text editor, and change its contents to look like this:
// index.js
import './style';
import { Component } from 'preact';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
userMessage: '',
conversation: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ userMessage: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const msg = {
text: this.state.userMessage,
user: 'user',
};
this.setState({
conversation: [...this.state.conversation, msg],
});
fetch('http://localhost:7777/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: this.state.userMessage,
}),
});
this.setState({ userMessage: '' });
}
render() {
const ChatBubble = (text, i, className) => {
const classes = `${className} chat-bubble`;
return (
<div key={`${className}-${i}`} class={`${className} chat-bubble`}>
<span class="chat-content">{text}</span>
</div>
);
};
const chat = this.state.conversation.map((e, index) =>
ChatBubble(e.text, index, e.user)
);
return (
<div>
<h1>Realtime Preact Chatbot</h1>
<div class="chat-window">
<div class="conversation-view">{chat}</div>
<div class="message-box">
<form onSubmit={this.handleSubmit}>
<input
value={this.state.userMessage}
onInput={this.handleChange}
class="text-input"
type="text"
autofocus
placeholder="Type your message and hit Enter to send"
/>
</form>
</div>
</div>
</div>
);
}
}
If you have some experience with Preact or React, the above code should be straightforward to understand. The state of the application is initialized with two values: userMessage
which contains the value of whatever the user types into the input field, and conversation
which is an array that will hold each message in the conversation.
The handleChange
function runs on every keystroke to update userMessage
which allows the displayed value to update as the user types. When the user hits the Enter
button the form will be submitted and handleSubmit
will be invoked.
handleSubmit
updates the conversation
state with the contents of the user’s message and sends the message in a POST
request to the /chat
endpoint which we will soon setup in our app’s server component, before clearing the input field by setting userMessage
to an empty string.
Add the styles for the application
Let’s add the styles for the app’s frontend. Open up style.css
in your editor and replace its contents with the following styles:
// style.css
html, body {
font: 14px/1.21 'Helvetica Neue', arial, sans-serif;
font-weight: 400;
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
margin-bottom: 40px;
}
.chat-window {
width: 750px;
margin: auto;
border: 1px solid #eee;
}
.conversation-view {
width: 100%;
min-height: 300px;
padding: 20px 40px;
}
.message-box {
width: 100%;
background-color: #d5d5d5;
padding: 10px 20px;
}
.text-input {
width: 100%;
border-radius: 4px;
border: 1px solid #999;
padding: 5px;
}
.chat-bubble {
font-size: 20px;
margin-bottom: 20px;
width: 100%;
display: flex;
}
.chat-bubble.ai {
justify-content: flex-end;
}
.chat-bubble.ai .chat-content {
background-color: #eec799;
}
.chat-content {
display: inline-block;
padding: 8px 15px;
background-color: #bada55;
border-radius: 10px;
}
Now, the application should look like this:
Setup your Wit.ai application
Head over to the Wit.ai website and create a free account.
Once you are signed in, hit the + icon at the top right of the page to create a new application. Enter your app name and click the +Create App button at the bottom.
You should see the following page once your app has been created.
Create your first entity
Wit.ai uses entities to help you understand user queries and extract meaningful information from them. Let’s setup an entity that will enable our bot to understand common greetings like “Hi” or “Hello”.
Type the word “Hello” in the “User says…” input field, then select the “wit/greetings” entity in the Add a new entity field. Use the dropdown on the right to set the value of the entity to true.
Once done, hit the Validate button to add the entity to your application. You can repeat the steps for other greetings such as “Hi”, “Hey”, “Good morning” etc.
If you click on the wit/greetings entry at the bottom, you will be directed to the entity page that contains all the expressions under that entity.
Create a custom entity
wit/greetings
is an example of a built-in entity. These built-in entities are prefixed by wit/
, and are defined to extract common expressions from messages. Things like age, money, email address, location and the likes are all covered by Wit.ai’s built-in entities.
You can train our bot to understand other things that the built-in entities do not cover. For example, let’s add an entity that allows our bot to understand a request for a joke.
Type “Tell me a joke” in the User says… input field, and add a new entity called “getJoke”. As before, use the dropdown on the right to set the value of the entity to true and hit the Validate button.
Test your Wit.ai chatbot with curl
Go to the settings page, and type “Hello” in the input field that says Type something to cURL, then copy the command to your clipboard using the copy icon on the right.
Open a terminal window and paste in the command, then press Enter. This would produce some output in your terminal that shows the entity that your query matches.
Set up the server component
We need to setup a server component so that we can pass messages sent through the frontend of the app to Wit.ai for processing.
Run the following command to install the dependencies we’ll be needing on the server side:
npm install --save express body-parser cors node-wit dotenv
Next, create a new file called server.js
in the root of your project directory and paste in the following code to set up a simple express server:
// server.js
require('dotenv').config({ path: 'variables.env' });
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const { Wit } = require('node-wit');
const client = new Wit({
accessToken: process.env.WIT_ACCESS_TOKEN,
});
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/chat', (req, res) => {
const { message } = req.body;
client
.message(message)
.then(data => {
console.log(data);
})
.catch(error => console.log(error));
});
app.set('port', process.env.PORT || 7777);
const server = app.listen(app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
});
We’ve also set up a /chat
endpoint that receives messages from the frontend of our app and sends it off to the Wit message API. Whatever response is received is then logged to the console.
Before you start the server, create a variables.env
file in the root of your project directory. You should add this file to your .gitignore
so that you do not commit it into your repository by accident.
Here’s how your variables.env
file should look like:
// variables.env
PORT=7777
WIT_ACCESS_TOKEN=<your server access token>
You can grab your Wit.ai server access token by heading to the settings under API Details.
Save the file and run node server.js
from the root of your project directory to start the server.
Now, send a few messages in the chat window, and watch the terminal where your Node server is running. You should see some output in your terminal that shows the entity that your query matches.
Set up responses for your chatbot
Now that user messages are being passed on to Wit.ai successfully, we need to add a way to detect which entity was matched and send an appropriate response to the user.
We’ll achieve that by setting up a responses
object that contains a variety of responses for each entity that we defined, and then send a random message when the appropriate entity is matched.
Inside the /chat
route and under the message
variable, paste in the following code:
// server.js
const responses = {
greetings: ["Hey, how's it going?", "What's good with you?"],
jokes: [
'Do I lose when the police officer says papers and I say scissors?',
'I have clean conscience. I haven’t used it once till now.',
'Did you hear about the crook who stole a calendar? He got twelve months.',
],
};
const firstEntityValue = (entities, entity) => {
const val =
entities &&
entities[entity] &&
Array.isArray(entities[entity]) &&
entities[entity].length > 0 &&
entities[entity][0].value;
if (!val) {
return null;
}
return val;
};
const handleMessage = ({ entities }) => {
const greetings = firstEntityValue(entities, 'greetings');
const jokes = firstEntityValue(entities, 'getJoke');
if (greetings) {
return console.log(responses.greetings[
Math.floor(Math.random() * responses.greetings.length)
]);
}
if (jokes) {
return console.log(responses.jokes[
Math.floor(Math.random() * responses.jokes.length)
]);
}
return console.log('I can tell jokes! Say \'tell me a joke\'')
};
Then change the line that says console.log(data)
to handleMessage(data)
:
// server.js
client
.message(message)
.then(data => {
handleMessage(data);
})
.catch(error => console.log(error));
Once we find an entity that matches, a random message from the appropriate property in the responses
object is logged to the console. Otherwise the default response is logged.
Set up Pusher Channels for realtime responses
Now, let’s integrate Pusher into the app so that our bot can respond to the user in realtime. Head over to the Pusher website and sign up for a free account. Select Channels apps on the sidebar, and hit Create Channels app to create a new app.
Once your app is created, retrieve your credentials from the API Keys tab, then add the following to your variables.env
file:
PUSHER_APP_ID=<your app id>
PUSHER_APP_KEY=<your app key>
PUSHER_APP_SECRET=<your app secret>
PUSHER_APP_CLUSTER=<your app cluster>
Integrate Pusher Channels into your Preact application
First, install the Pusher Channels client library by running the command below:
npm install pusher-js
Then import it at the top of index.js
:
import Pusher from 'pusher-js';
Next, we’ll open a connection to Channels and use the subscribe()
method from Pusher to subscribe to a new channel called bot
. Finally, we’ll listen for the bot-response
on the bot
channel using the bind
method and update the application state once we receive a message.
Don’t forget to replace the <your app key>
and <your app cluster>
placeholder with the appropriate details from your Pusher account dashboard.
// index.js
componentDidMount() {
const pusher = new Pusher('<your app key>', {
cluster: '<your app cluster>',
encrypted: true,
});
const channel = pusher.subscribe('bot');
channel.bind('bot-response', data => {
const msg = {
text: data.message,
user: 'ai',
};
this.setState({
conversation: [...this.state.conversation, msg],
});
});
}
Trigger events from the server
Add the Pusher server library though npm:
npm install pusher
Then import it at the top of server.js
:
// server.js
const Pusher = require('pusher');
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_APP_KEY,
secret: process.env.PUSHER_APP_SECRET,
cluster: process.env.PUSHER_APP_CLUSTER,
encrypted: true,
});
Change the handleMessage
function to look like this:
// server.js
onst handleMessage = ({ entities }) => {
const greetings = firstEntityValue(entities, 'greetings');
const jokes = firstEntityValue(entities, 'getJoke');
if (greetings) {
return pusher.trigger('bot', 'bot-response', {
message:
responses.greetings[
Math.floor(Math.random() * responses.greetings.length)
],
});
}
if (jokes) {
return pusher.trigger('bot', 'bot-response', {
message:
responses.jokes[
Math.floor(Math.random() * responses.jokes.length)
],
});
}
return pusher.trigger('bot', 'bot-response', {
message: 'I can tell jokes! Say \'tell me a joke\'',
});
};
Stop the node server if it is currently running by pressing Ctrl + C
in the terminal and restart it with node server.js
. Now you can go ahead and test your bot! Send messages like “hey”, or “Tell me a joke” and you will get replies from the bot.
Conclusion
You have now learned how easy it is to create a chatbot that incorporates natural language processing with Wit.ai and how to respond in realtime with Pusher Channels.
Thanks for reading! Remember that you can find the source code of this app in this GitHub repository.
16 August 2018
by Ayooluwa Isaiah