README
xurei Messenger Chatbot
Simple Messenger chatbot framework for simple Q&A conversations based on Finite State Machine.
https://github.com/xurei/x-chatbot
This is a work in progress ! Expect things to be broken.
Usage
Setting up
Before using the chatbot, you need to register a messenger app on the Facebook for Developpers Page.
See Facebook's Guide for more infos (Don't bother too much about the code in the guide. Most of it is handled by the framework :-) )
Take note of the access token and the validation token of you app.
npm install x-chatbot
const http = require('http');
const xchatbot = require('../../xchatbot/xchatbot');
const chatbot = xchatbot({
validation_token: "this_is_y_validation_token",
access_token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
});
const app = chatbot.express;
const httpServer = http.createServer(app);
httpServer.listen(80, function () {
console.log('Server listening on '+this.address().address+' port '+ this.address().port);
});
Creating your chat flow
The framework works by setting the different questions and the answers that it expects. Questions are linked together via a simple FSM, i.e. by changing state of the current session.
Here is an example of question :
chatbot.registerQuestion({
name: "QUESTION_NAME", //unique identifier
execute: function(api, session) {
return api.sendTextMessage(session.senderId(), "When is the meeting taking place (dd/mm/yyyy) ?");
},
answers: {
"INPUT": function (api, session, payload) {
if (is_date_valid(payload.text)) {
//Store information in the user session
session.store.date = payload.text;
//Change the state to the next question
session.setState("REQUEST_BUDGET");
}
else {
//Send the invalid data message
api.sendTextMessage(session.senderId(), "Sorry, I didn't get it.")
//Change the state to itself so it can ask the question again
.then(() => session.setState("REQUEST_DATE"));
}
}
}
});
You can also use chatbot.registerQuestions
:
chatbot.registerQuestions({
{
name: "QUESTION_1",
execute: /* ... */,
answers: /* ... */
},
{
name: "QUESTION_2",
execute: /* ... */,
answers: /* ... */
}
});
Session persistence
By default, the sessions are not persisted. If you want persistence, use chatbot.readSession
and chatbot.writeSession
.
API Doc
chatbot.express
Instance of express framework running the chatbot.
chatbot.api
This is the wrapper of the Facebook API. All the methods return ES6 promises to deal with the asynchronicity.
chatbot.api.sendTextMessage : function(idSender, text)
Sends a basic text message to a user.
Too send multiple messages in a specific order, use then()
:
chatbot.api.sendTextMessage(session.senderId(), "Hello")
.then(() => chatbot.api.sendTextMessage(session.senderId(), "World"));
chatbot.api.sendPicMessage : function(idSender, url)
Sends an image to a user.
chatbot.api.send : function(idSender, messageData)
Sends raw data to the facebook messaging endpoint (https://graph.facebook.com/v2.6/me/messages). You can basically send any king of message with this method.
idSender
id of the user to send the messagemessageData
json "message" field as requested by Facebook API
Example :
chatbot.api.send(session.senderId(), {
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"Welcome to Peter\'s Hats",
"item_url":"https://petersfancybrownhats.com",
"image_url":"https://petersfancybrownhats.com/company_image.png",
"subtitle":"We\'ve got the right hat for everyone.",
"buttons":[
{
"type":"web_url",
"url":"https://petersfancybrownhats.com",
"title":"View Website"
},
{
"type":"postback",
"title":"Start Chatting",
"payload":"DEVELOPER_DEFINED_PAYLOAD"
}
]
}
]
}
}
}
chatbot.api.setMenu : function(data)
Define the persistant menu, as defined by Facebook docs Example :
chatbot.api.setMenu({
"setting_type" : "call_to_actions",
"thread_state" : "existing_thread",
"call_to_actions":[
{
"type":"postback",
"title":"Help",
"payload": JSON.stringify({action: "HELP"})
},
{
"type":"postback",
"title":"Create new request",
"payload": JSON.stringify({action: "CREATE_REQUEST"})
},
{
"type":"web_url",
"title":"View Website",
"url":"http://www.google.com/"
}
]
});
chatbot.readSession : function(senderId, api, questions)
Defines the way you read a session from your persistent data store. By default, it returns null.
senderId
id of the userapi
instance of chatbot.apiquestions
questions array The method should return a Session object. Example :
var myDatabaseObject = /* ... */
chatbot.readSession = function(senderId, api, questions) {
var dbEntry = myDatabaseObject.read(senderId);
if (typeof (dbEntry) !== "undefined" && dbEntry !== null) {
return new Session(senderId, api, questions, dbEntry.state, dbEntry.store);
}
else {
return null;
}
}
chatbot.writeSession : function(session)
Writes the session to your persistent database store. By default, it does nothing.
session
The method should return a Session object. Example :
var myDatabaseObject = /* ... */
chatbot.writeSession = function(session) {
myDatabaseObject.write(session.senderId(), {state: session.getQuestion().name, store: session.store});
}
chatbot.registerAction : function(actionName, callback)
Register an action that the user can do at any time, typically via the menu.
actionName
the action trigerred. See chatbot.api.setMenu() example.callback: function(api, session, payload)
method to be called when the action is trigerredapi
instance of chatbot.apisession
Session object, see belowpayload
additionnal information if required
chatbot.registerQuestion : function(question_name, question_data)
chatbot.registerQuestions : function(questions)
Register a question along with the expected answer and the functions to handle them. It is the key element to create flows using the FSM.
Example :
chatbot.registerQuestion({
name: "ARE_YOU_SURE",
execute: function(api, session) {
return
api.sendTextMessage(session.senderId(), "Are you sure ?")
.then(() => api.send(session.senderId(), {
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"elements": [
{
"title": "Please confirm",
"buttons": [{
"type": "postback",
"title": "Yes",
"payload": JSON.stringify({action: "CONFIRM_REQUEST"})
},
{
"type": "postback",
"title": "No",
"payload": JSON.stringify({action: "DENY_REQUEST"})
}]
}
]
}
}
}));
},
answers: {
"CONFIRM_REQUEST": function (api, session, payload) {
session.store.confirmed = true;
session.setQuestion("REQUEST_CONFIRMED");
},
"DENY_REQUEST": function (api, session, payload) {
session.store.confirmed = false;
session.setQuestion("REQUEST_DENIED");
},
"INPUT": function (api, session, payload) {
api.sendTextMessage(session.senderId(), "Please use the buttons")
.then(() => session.setQuestion("ARE_YOU_SURE")); //Ask the question again
}
}
});
The execute
function is executed when session.setQuestion()
is called. The answers
are the expected answers. There
can be multiple possible answers.
The name of the answer is defined by the actions you provided in the execute()
method. The INPUT
answer is when the
user types in text directly.
The Session object
The session object handles the sessions like a traditionnal HTTP session. The only difference is that it uses the Facebook user id as session key. It also contains the current question asked to a user and provides a store for extra information.
Session.setQuestion: function (question_name)
Set the current question to a registered question and ask it to the user.
Session.getQuestion: function ()
Returns the current question object.
Session.senderId: function ()
Returns the id of the user.
Session.store: {}
Storage space where you can put anything you need to keep the state of your bot.
Full Example
Coming soon...