In this tutorial you are going to learn how to implement a Facebook Messenger bot on your Facebook page using the Nexmo Messages API and the Google Dialogflow service.
This example is going to take inbound messages sent either via the ‘Send Message’ button on a Facebook page, or via the Facebook Messenger app. Both will work just fine.
Messages will be routed through our app to the Google Dialogflow service which will generate responses to questions and then send these back via the Nexmo Messages API.
In this example, we’re using the prebuilt Small Talk agent in Dialogflow that will respond with chirpy answers to inbound questions and is great for development purposes.
You can download and run this code for yourself from the nexmo-community/nexmo-messages-facebook-dialogflow respository on GitHub.
Prerequisites
You’ll need to create accounts to run this for yourself, so make sure you have the following set up:
- A Nexmo account
- A Facebook account with a brand/business page you can use for testing
- A Google Dialogflow account
- Ngrok (so the outside world can access the app on your local machine)
- The Nexmo Command Line Interface
The code for this example is built using Node.js and the Koa framework. It will work on any version of Node.js 7.6.0 or above.
You can check your version of Node by running node -v
on your command line. If the number is 7.6.0 or higher then you’re good to go. If it isn’t then there are some extra steps that Koa would like you to take (see the installation section of this page).
Create the Skeleton Application
In order to receive messages coming in from Facebook, you need to have two webhooks set up that allow the app to receive data about messages and delivery statuses.
We’ll start by building the basic application with these two routes.
In a new folder, initalise a new Node.js application by running
1 2 |
npm init -y |
Next, install the dependencies for the project:
1 2 |
npm i koa koa-route koa-bodyparser nexmo@beta dialogflow |
Additionally, we’ll be using the excellent Nodemon and DotEnv packages to keep our app up and running whilst changes are made so you don’t have to keep restarting it. Install these as devDependencies
.
1 2 |
npm i -D nodemon dotenv |
Finally, add a little folder structure by creating routes
and controllers
folders in the root of your directory.
1 2 |
mkdir routes controllers |
Create the Koa Server
Koa is a framework for Node.js written by the creators of Express. It’s lightweight and comes with only a basic set of features out of the box which makes it perfect for creating a small webhook server like the one we need here.
Create the main file for the server by adding a new file in your root directory called server.js
.
In this new file, add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const Koa = require('koa'); const router = require('koa-route'); const bodyParser = require('koa-bodyparser'); const routes = require('./routes'); const port = process.env.PORT || 3000; // Set up a new Koa app and tell it to use // the bodyParser middleware for inbound requests const app = new Koa(); app.use(bodyParser()); // Routes app.use(router.post('/webhooks/status', routes.status)); app.use(router.post('/webhooks/inbound', routes.inbound)); // Have the app listen on a default port or 3000 app.listen(port, () => console.log(`App is waiting on port ${port}`)); |
The routes
constant is used to store and access the routes in the application:
1 2 |
const routes = require('./routes'); |
We’re also going to need a new file in the routes
folder called index.js
. Go ahead and create that, and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const routes = { inbound: async ctx => { // Get the detail of who sent the message, and the message itself const { from, message } = ctx.request.body; console.log(from, message); ctx.status = 200; }, status: async ctx => { const status = await ctx.request.body; console.log(status); ctx.status = 200; } }; module.exports = routes; |
With that in place, run the following command to start up the server:
1 2 |
nodemon server.js |
The app will launch on port 3000
.
Use Ngrok to open up this port to the world and make note of the URLs it produces for you.
Here is a handy guide to working with Ngrok if you haven’t used it before.
TLDR? You can start up Ngrok (if installed) by running this command:
1 2 |
ngrok http 3000 |
Create a Messages & Dispatch Application
Set up a new Messages & Dispatch application via the Nexmo Dashboard.
Make sure that you append /webhooks/inbound
and /webhooks/status
to the URL you get from Ngrok when you paste them into the form (like in the image below).
Remember to also click the Generate public/private key pair link. This will download a file called private.key
.
Locate the private.key
file on your system and move it to the root folder for your application.
Finalise the app set up by clicking the Create Application button and you’re done with config.
Make a note of your Application ID, you’ll need it in the next step.
Connect the Messages API to Facebook
In order for Facebook to be aware of your newly created app, you need to connect them together.
First, you’ll need to create a JSON Web Token to authorise Facebook to use your application, you can do this with the Nexmo CLI.
Open your terminal and ensure that you are in the root of your application folder.
Using the Nexmo CLI run the following command:
1 2 |
nexmo jwt:generate private.key exp=$(($(date +%s)+86400)) application_id=NEXMO_APPLICATION_ID |
Be sure to replace NEXMO_APPLICATION_ID
with the ID of the application you just created.
Running this command will result in a big string of letters and numbers – this is your JSON Web Token. Copy the whole thing.
To connect your Facebook page to your app, we’ve created a handy page:
https://static.nexmo.com/messenger/
Complete the following steps:
- Log in with your Facebook credentials
- Select the Facebook page you want to connect to your Nexmo app
- Click Subscribe
If all is well, you’ll see a green dialog pop up congratulating you on your success, and letting you know the ID of your Facebook page.
Make a note of this ID.
You can verify the content of your JWT by using jwt.io.
Note: If any element of this wasn’t clear, there’s a guide to creating JWTs for use in this context in our Facebook Messenger tutorial.
Test the Connection
Your application is now connected to your Facebook page.
With your server still running, and Ngrok still exposing it to the world, head over to your Facebook page and locate the messaging button.
Click it to open the messaging window and start sending some wonderful missives. Alternatively, just start with ‘Hello’.
Any message you send will be passed along to the Inbound Webhook you specified in your application set up. This maps directly to the inbound
function in the routes.js
file.
Currently, the inbound
function is set to log the from
and message
portion of what is being sent from Facebook out to the console.
You should see your messages appearing in the console.
Send the Messages to Dialogflow
Now that your stunning word play is being received by the application, it’s time to send it over to Dialogflow to get some equally pithy responses back.
In the controllers
folder, create a new file called dialogflow.js
and add the the contents of this javascript file.
The exported function in the file achieves the following:
- An async function called
dialogflowHandler
is instantiated and it accepts a param calledquery
. - An object called
request
is created, containing all the keys that Dialogflow expects. - The request object is sent to Dialogflow.
- The reply from the Small Talk agent, contained within
result[0].queryResult.fulfillmentText
is returned.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
const dialogflowHandler = async query => { // Create a text query request object const request = { session: sessionPath, queryInput: { text: { text: query, languageCode: languageCode } } }; // Send the text query over to Dialogflow and await the result // using .catch to throw any errors const result = await sessionClient .detectIntent(request) .catch(err => console.error('ERROR:', err)); // Pick out the response text from the returned array of objects const reply = await result[0].queryResult.fulfillmentText; // Return the reply return reply; }; module.exports = dialogflowHandler; |
To make use of this dialogflowHandler
function, open the routes/index.js
file and require it at the top:
1 2 |
const dialogflowHandler = require('../controllers/dialogflow'); |
Modify the inbound
function so it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
inbound: async ctx => { const { from, message } = await ctx.request.body; console.log(from, message); const dialogflowResponse = await dialogflowHandler(message.content.text); console.log(dialogflowResponse); ctx.status = 200; }; |
Send a new message (something akin to ‘Hello!’) from your Facebook page (or via your Facebook Messenger app).
This time you’ll see the incoming message being logged to the console, as well as the response coming back from Dialogflow.
Note: If you need help setting up Dialogflow, follow the SmallTalk Prebuilt Agent guide.
Send the Reply Using the Messages API
You’re almost close to completion. Here is what has been achieved so far.
- Set up the Koa server ✔️
- Set up a new Nexmo App ✔️
- Connected the app to a Facebook Page ✔️
- Test for incoming messages ✔️
- Send incoming messages to Dialogflow and get a response ✔️
The final piece in this puzzle is to take the response that Dialogflow returns and send it back to the user as a reply to their message.
The Nexmo Messages API will handle all of this for us.
Create a new file in the controllers
folder called nexmo.js
and populate it with the contents of this file.
The main function being exported in this file is called messageResponder
.
This function uses the Nexmo Node JS Client Library to send a message back to the user.
The function is passed an object called message
that will contain the id
of the user to send the reply to, and the dialogflowResponse
(the text to send in the message).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const messageResponder = async message => { nexmo.channel.send( { type: 'messenger', id: message.id }, // Who the message goes to { type: 'messenger', id: FBID }, // Your FBID - who the message comes from { content: { type: 'text', text: message.dialogflowResponse } }, (err, data) => { console.log(data.message_uuid); } ) }; |
To make use of this messageResponder
function import it in the routes/index.js
file:
At the top of the file, underneath the require
statement for the dialogflow.js
file created earlier, add the following:
1 2 |
const messageResponder = require('../controllers/nexmo'); |
Then, in the inbound
function, add the following code just above the ctx.status = 200
line:
1 2 3 |
messageResponder({ ...from, dialogflowResponse }); ctx.status = 200; |
As you can see, we’re passing an object into messageResponder
and using the spread operator to combine the contents of the from
object with the response we got from Dialogflow.
This will make the object being passed to the function look like this:
1 2 3 4 5 6 |
{ type: 'messenger', id: '111111111111', dialogflowResponse: 'Greetings!' } |
The id
in this instance is the ID of the user on Facebook that sent the message, so it will be different to the one above.
The Moment of Truth
The stage is set. With that final file the loop has been closed and any incoming message should now receive a reply straight back.
Once again, send a message from your Facebook page. The response from Dialogflow should now pop up in the same window!
Conclusion
The prebuilt Small Talk agent is great for testing but the next step here would be to actually build an agent of your own that can relay some knowledge that is of some worth to the user.
For more information on getting up and running with building Dialogflow agents, check out some of these articles:
- Build an agent from scratch using best practices
- Bitesize videos from Google on building Dialogflow agents
- Building Your Own Chatbot Using Dialogflow
With the code from this tutorial, you have the link between Facebook and Dialoglow already in place so you can go ahead and build the mightiest agent of all time.