To skip the tutorial, feel free to download the source code from my Github repo here.
In very little time, there are quite a few very good tutorials out there for beginners about how to develop a Facebook Messenger bot. However, these tutorials describe a stateless bot. What that means is, that for every user who send a message to your bot, there is no info saved regarding his current state in the conversations, and other basic info. This is why I've decided to write this tutorial, which consists of a basic implementation of a Facebook Messenger bot, in addition to a functional working MongoDB library.
Saving end users specific states dynamically within a conversation, is crucial for the UX of any basic chat bot. Saving states allows the bot to communicate with the end users in a flow which follows some pattern, that otherwise would not be possible.
Getting started
For starters, you'll need a Facebook developers account which can be found here.
Secondly, follow the beginning of the process for creating a Facebook page and set up a 'Webhook' (up until step 5) by clicking here. Note: You should write down the verification code which you've provided to the web hook in the tutorial. Secondly, once you've got a Facebook page up and running, look up the page token, and send a POST request with the following:
https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=<TOKEN_GOES_HERE>
You should get a response 'true' which means you've synced your Facebook page with the provided API.
Lastly, please get familiar with the basics of Node.js and MongoDB. It is recommended to learn the basics of MongoDB. In addition, you should understand the basics of writing in Node.js and ES6.
Now let's create you very first Facebook messenger chat bot!
Facebook API Structured messages
First things first. Understand and learn the basic concepts of the Facebook API - click here. Let's look at an example:
"welcome_message": { "attachment": { "type": "template", "payload": { "template_type": "button", "text": "Hello and welcome to your first bot. Would you like to get see our products?", "buttons": [ { "type": "postback", "title": "Yes", "payload": "get_options" }, { "type": "postback", "title": "No", "payload": "no_options" } ] } } }
In the example above, you can see that for every message sent to Facebook, we need to declare the type of message, which is in this case a template (for basic text, text is enough). In addition, we declare the template type which is buttons in this case, and the buttons themselves. For every button, we need to declare the button title, type and payload. Type is so we'll know how the button click is handled, and payload is so we can identify which button the user clicked (a further example is described in the source code).
Server side
The basic and required implementation for the server side, is to set up a GET handler for the url/webhook/, and a POST for the same url/webhook/. The GET handler is for Facebook verification when applying your url webhook and should be as follows:
function facebookVerification(req, res) { if (req.query['hub.verify_token'] === WEBHOOK_TOKEN) { res.status(200).send(req.query['hub.challenge']); } else { console.error("Failed validation. Make sure the validation tokens match."); } }
Note: the WEBHOOK_TOKEN above is to be stated as you've declared when initializing the webhook. Facebook shows an example with 'my_voice_is_my_password_verify_me'. You can leave it as is and update the source code.
The second and most important method is the POST. Facebook Messenger sends every ICM (Incoming message) sent to your bot page, via POST to the url you've declared in the developers portal. The method should handle all ICM either those which arrived by user clicks, or by free text. I will describe three methods which are used in this case:
// 0 MongoDB info const mongoose = require('mongoose'); const User = mongoose.model('User', {_id: String, name: String, profile_image_url: String, phone_number: String, current_state: String}); // 1 app.post('/webhook/', facebook_parser.facebookWebhookListener); // 2 function facebookWebhookListener(req, res) { if (!req.body || !req.body.entry[0] || !req.body.entry[0].messaging) { return console.error("no entries on received body"); } let messaging_events = req.body.entry[0].messaging; for (let messagingItem of messaging_events) { let user_id = messagingItem.sender.id; db_utils.getUserById(user_id, messagingItem, parseIncomingMSGSession); } res.sendStatus(200); } // 3 function getUserById(user_id, incomingMessage, callback) { var result = null; //Lets try to Find a user User.findById(user_id, function (err, userObj) { if (err) { console.log(err); } else if (userObj) { result = userObj; console.log('User ' + user_id + ' exists. Getting current user object:', userObj); } else { console.log('User not found!'); } // After getting user object, forward to callback method. callback(user_id, incomingMessage, userObj); }); } // 4 function parseIncomingMSGSession(user_id, messageItem, userObj) { var current_state = "welcome_message"; if (userObj != null) { current_state = userObj.current_state; } // If we recieve any text message, parse and respond accordingly if (messageItem.message && messageItem.message.text) { // Currently support a static welcome message only sendFacebookGenericMsg(user_id, message_templates.templates["welcome_message"]); } // If the user sends us a button click if (messageItem.postback && messageItem.postback.payload) { var button_payload_state = messageItem.postback.payload; switch (button_payload_state) { case "get_options": sendFacebookGenericMsg(user_id, message_templates.templates["options_message"]); break; case "no_options": sendFacbookTextMsg(user_id, "Ok. There is so much you can do with stateful bots!"); break; } } // Save new user state. If user does not exist in DB, will create a new user. db_utils.setUserFieldById(user_id, "current_state", ""); }
The first step (commented) is for listening to POST requests and forwarding them to a method called facebookWebhookListener (method 2). This method then retrieves from the POST body the relevant info such as the message item (consists of user unique id, message text, etc) and forwards the content to a method called getUserById (method 3).
The method getUserById (method 3), uses the info set at the top (comment 0), and tries to retrieve a user with the given id in the DB. If the user is not found, a null will be returned, and the info is passed to a callback function which is in our case, parseIncomingMSGSession (method 4).
The method parseIncomingMSGSession (method 4), is in charge of sending an OGM (Outgoing message) based on the user info. In the case above, the default state is "welcome_message". Secondly, the method obtains the type of the ICM, which could either be a text message, or a clicked message (when user clicks on buttons the bot provided). Based on the ICM and users state, a relevant message is sent. There are additional methods declared in the code above, which I will not explain, since they are pretty much self explanatory and can be found in full in the source code provided at the top of this post (or at the end). Feel free to ask me any questions regarding any of the methods and general flow of the server side.
Finally, in order to send back a response to the end user, you'll need to send a POST request with the message template as described above and with the following structure:
// Send generic template msg (could be options, images, etc.) function sendFacebookGenericMsg(user_id, message_template) { request({ url: 'https://graph.facebook.com/v2.6/me/messages', qs: {access_token: TOKEN}, method: 'POST', json: { recipient: { id: user_id }, message: message_template } }, facebookCallbackResponse); } function facebookCallbackResponse(error, response, body) { if (error) { console.log('Error sending messages: ', error) } else if (response.body.error) { console.log('Error: ', response.body.error) } }
The TOKEN shown above is the page token you've received via the Facebook developers portal page. Congratulations! You've completed your very first Facebook messenger bot. The source code is built in such a way, that it'll be very easy for you to scale it up to a fully functional chatbot.
To view the full project source code, click the button below. Feel free to ask any questions you might have, and I'll answer you ASAP!