var promise = require('bluebird');
var request = require('request');
var JSONbig = require('json-bigint');
var botconfig = require('../config/botconfig');
var fbmessageutil = require('../utils/fbmessageutil');
var messagesutil = require('../utils/messagesutil');
var debugutil = require('../utils/debugutil');
var pagectl = false;
var menuctl = false;
var attachmentctl = false;
exports = module.exports = function(pagectl, attachmentctl, menuctl)
{
return new Facebookctl(pagectl, attachmentctl, menuctl);
}
/**
* @constructs Facebook_Controller
* @public
* @param {Page_Controller} page_controller - A NGINB page controller
* @param {Attachment_Controller} attachment_controller - A NGINB attachment controller
* @param {Menu_Controller} menu_controller - A NGINB menu controller
*/
function Facebookctl(page_controller, attachment_controller, menu_controller)
{
pagectl = page_controller || require('./page')();
attachmentctl = attachment_controller || require('./attachment')();
menuctl = menu_controller || require('./menu')();
}
/**
* Sets the greetings text of the facebook messenger bot
* @param {PageConfig} page - A PageConfig object
* @param {Array|String} greetings - A String or an Array with greetings messages and locations
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.setGreetingText = function setGreetingText(page, greetings)
{
var self = this;
return new promise(function(resolve, reject)
{
greetings = (greetings.length!=undefined) ? greetings : [greetings];
var json =
{
greeting: greetings
}
self.setMessengerProfile(page, json)
.then(function(result)
{
resolve(result);
});
});
}
/**
* Enables the start button for facebook bot
* @param {PageConfig} page - A PageConfig object
* @param {String} payload - A String with the paylod of the start button
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.setStartButton = function setStartButton(page, payload)
{
var self = this;
return new promise(function(resolve, reject)
{
var json =
{
get_started: {"payload": payload}
}
self.setMessengerProfile(page, json)
.then(function(result)
{
resolve(result);
});
});
}
/**
* Enables the facebook persistent menu (sandwich)
* @param {PageConfig} page - A PageConfig object
* @param {String} menu - The menu name to gets from menus object file, if configured
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.setPersistentMenu = function setPersistentMenu(page, menu)
{
var self = this;
return new promise(function(resolve, reject)
{
var menu_data = (menu.data!=undefined && menu.data.length!=undefined && menu.data.length>0) ? menu.data : [];
var json = {persistent_menu:[]};
if(menu.type!=undefined)
{
if(menu.type=='fixed')
{
var menu_buttons = (menu_data.hasOwnProperty('buttons') && menu_data.buttons.length>0) ? menu_data.buttons : [{"type":"postback", "title":"OK", "payload":"ok"}];
var buttons = menuctl.getFacebookButtons(menu_buttons);
json = {persistent_menu: buttons}
}
else if(menu.type=='persistent_menu')
{
json = {persistent_menu: menu_data}
}
self.setMessengerProfile(page, json)
.then(function(result)
{
resolve(result);
});
}
else
{
resolve(false);
}
});
}
/**
* Adds a domain or an array of domains to the facebooks domain whitelist
* @param {PageConfig} page - A PageConfig object
* @param {String} domainArray - The array of domais do add (can be only one item)
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.domainWhitelisting = function domainWhitelisting(page, domainArray)
{
var self = this;
return new promise(function(resolve, reject)
{
domainArray = domainArray.length!=undefined ? domainArray : [domainArray];
var json = {whitelisted_domains: domainArray};
self.setMessengerProfile(page, json)
.then(function(result)
{
resolve(result);
});
});
}
/**
* Sets a messenger profile from a given json object - See {@link https://developers.facebook.com/docs/messenger-platform/messenger-profile|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {Object} json - The json object to set
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.setMessengerProfile = function setMessengerProfile(page, json)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
method: 'POST',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/messenger_profile?access_token=" + page.token,
json: json
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Set Thread Settings Error');
resolve(message);
});
});
}
/**
* Deletes a messenger profile from a given field - See {@link https://developers.facebook.com/docs/messenger-platform/messenger-profile|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {String} fields - The fields to delete
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.deleteMessengerProfile = function setMessengerProfile(page, fields)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
method: 'DELETE',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/messenger_profile?access_token=" + page.token,
json: {fields:fields}
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Set Thread Settings Error');
resolve(message);
});
});
}
/**
* Gets a messenger profile from a given field - See {@link https://developers.facebook.com/docs/messenger-platform/messenger-profile|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {String} fields - The fields to get info from
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.getMessengerProfile = function setMessengerProfile(page, fields)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
method: 'GET',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/messenger_profile?fields="+fields+"&access_token=" + page.token,
json: json
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Set Thread Settings Error');
resolve(message);
});
});
}
/**
* Subscribes a messenger page to receive bot messages - See {@link https://developers.facebook.com/docs/messenger-platform/webhook-reference#subscribe|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.doSubscribeRequest = function doSubscribeRequest(page)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
method: 'POST',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/subscribed_apps?access_token=" + page.token
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Do Subscribe Error');
resolve(message);
});
});
}
/**
* Does a facebook login - See {@link https://developers.facebook.com/docs/facebook-login|Facebook documentation}
* @param {Object} req - A request object from express
* @param {Object} res - A response object from express
* @param {String} redirect_url - The redirect url to be used by facebook API
* @param {String} scope - The scope of facebook auth request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.facebookLogin = function functionName(req, res, redirect_url, scope)
{
var self = this;
var facebookUrl = "https://www.facebook.com";
if (req.device.type!='desktop')
facebookUrl = "https://m.facebook.com";
var url = facebookUrl+'/'+botconfig.facebook.version+'/dialog/oauth?';
var params = 'client_id='+botconfig.facebook.login_app_id+'&redirect_uri='+encodeURIComponent(redirect_url)+'&scope='+scope;
res.redirect(url+params);
}
/**
* Gets the facebook token by a given code - See {@link https://developers.facebook.com/docs/facebook-login|Facebook documentation}
* @param {String} code - A code returned by facebook login page
* @param {String} redirect_url - The redirect url to be used by facebook API
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.getFacebookToken = function getFacebookToken(code, redirect_url, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
var params = 'client_id='+botconfig.facebook.login_app_id+'&redirect_uri='+encodeURIComponent(redirect_url)+'&client_secret='+botconfig.facebook.login_app_secret+'&code='+code;
request(
{
method: 'GET',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/oauth/access_token?"+params
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Get Token Error');
message.callback_data = callback_data;
resolve(message);
});
});
}
/**
* Gets the facebook user data by a given code - See {@link https://developers.facebook.com/docs/facebook-login|Facebook documentation}
* @param {String} code - A code returned by facebook login page
* @param {String} redirect_url - The redirect url to be used by facebook API
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.getFacebookUserDataByCode = function requestUserData(code, redirect_url, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
self.getFacebookToken(code, redirect_url, callback_data)
.then(function(token_response)
{
var data = token_response.data.body;
if(typeof(data)=='string')
data = JSONbig.parse(data);
self.getFacebookUserData(data.access_token, token_response.callback_data)
.then(function(fb_response)
{
var fb_data = fb_response.data.body;
if(typeof(fb_data)=='string')
fb_data = JSONbig.parse(fb_data);
var picture = botconfig.facebook.graph_url + "/" + fb_data.id + "/picture?type=large";
fb_data.profile_pic = picture;
fb_data.facebook_id = fb_data.id;
fb_data.sender_id = fb_response.callback_data.pid;
fb_data.page_id = fb_response.callback_data.page_id;
delete fb_data.id;
delete fb_data.name;
resolve({data:fb_data, callback_data:callback_data, access_token:data.access_token});
});
});
});
}
/**
* Gets a facebook user data by a valid access_token - See {@link https://developers.facebook.com/docs/facebook-login|Facebook documentation}
* @param {String} access_token - A code returned by facebook login page
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.getFacebookUserData = function requestUserData(access_token, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
var fields = 'name,first_name,last_name,locale,gender,email,timezone';
request(
{
method: 'GET',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me?fields=" + fields + "&access_token=" + access_token
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Set Thread Settings Error');
message.callback_data = callback_data;
resolve(message);
});
});
}
/**
* Gets the facebook user data by a given page/sender_id - See {@link https://developers.facebook.com/docs/facebook-login|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {String} sender_id - The facebook sender id
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.requestUserData = function requestUserData(page, sender_id, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
var fields = 'first_name,last_name,profile_pic,locale,timezone,gender';
request(
{
method: 'GET',
uri: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/"+sender_id+"?fields="+fields+"&access_token=" + page.token
},
function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Set Thread Settings Error');
message.callback_data = callback_data;
resolve(message);
});
});
}
/**
* Sends a facebook messenger message - See {@link https://developers.facebook.com/docs/messenger-platform/send-api-reference|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {String} sender - The facebook sender id
* @param {Object} message_data - A facebook message formated data
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.sendMessage = function sendMessage(page, sender, message_data, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
url: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/messages",
qs: {access_token: page.token},
method: 'POST',
json:
{
recipient: {id: sender},
message: message_data
}
}, function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Error sending message');
message.callback_data = callback_data;
resolve(message);
});
});
}
/**
* Sends a facebook messenger action - See {@link https://developers.facebook.com/docs/messenger-platform/send-api-reference/sender-actions|Facebook documentation}
* @param {PageConfig} page - A PageConfig object
* @param {String} sender - The facebook sender id
* @param {Object} action - A facebook action (mark_seen|typing_on|typing_off)
* @param {Object} callback_data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook response object
*/
Facebookctl.prototype.sendAction = function sendAction(page, sender, action, callback_data)
{
var self = this;
return new promise(function(resolve, reject)
{
request(
{
url: botconfig.facebook.graph_url + "/" + botconfig.facebook.version + "/me/messages",
qs: {access_token: page.token},
method: 'POST',
json: {
recipient: {id: sender},
sender_action: action
}
}, function (error, response, body)
{
var message = messagesutil.getMessage(error, response, body, 'Error sending action');
message.callback_data = callback_data;
resolve(message);
});
});
}
/**
* Formats a bot message to a facebook template object
* @param {String} page_id - the facebook page id
* @param {String} sender - The facebook sender id
* @param {Object} message - A message object returned by bot controller
* @param {String} lang - The language of the template to be passed to menu configuration
* @param {Object} data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook template object
*/
Facebookctl.prototype.getFacebookTemplate = function getFacebookTemplate(sender, page_id, message, lang, data)
{
var self = this;
return new promise(function(resolve, reject)
{
var facebook_message = false;
var menu_params = false;
var params = (message.button_params) ? message.button_params : [];
if(message.template)
{
if(typeof(message.template)=='string' || (typeof(message.template)=='object' && message.template.length==undefined))
menu_params = message.template;
if(menu_params)
{
menuctl.getMenu(menu_params, lang)
.then(function(menu)
{
facebook_message = menuctl.getFacebookMenu(menu, sender, page_id, data, params);
resolve(facebook_message);
});
}
else if(message.template.length!=undefined)
{
var elements = [];
for (var i = 0; i < message.template.length; i++)
{
var element = message.template[i];
var buttons = [];
if(element.hasOwnProperty('buttons'))
buttons = menuctl.getFacebookButtons(element.buttons, sender, page_id, data, params);
element.buttons = buttons;
elements.push(element);
}
facebook_message = fbmessageutil.genericTemplate(elements);
resolve(facebook_message);
}
else
resolve(false);
}
else
resolve(false);
});
}
/**
* Formats a bot message to a facebook message
* @param {String} page_id - the facebook page id
* @param {String} sender - The facebook sender id
* @param {Object} message - A message object returned by bot controller
* @param {String} lang - The language of the template to be passed to menu configuration
* @param {Object} data - A callback object data to be passed after the request
* @return {Object} A bluebird promise facebook message object
*/
Facebookctl.prototype.getFacebookMessage = function getFacebookMessage(sender, page_id, message, lang, data)
{
var self = this;
return new promise(function(resolve, reject)
{
var facebook_message = {text: ''};
var menu_params = false;
if(message.quickreply)
{
var rpl_items = message.quickreply;
var msg = {text: ''};
var params = (message.quickreply_params) ? message.quickreply_params : [];
var menu = false;
if(typeof(message.quickreply)=='string' || (typeof(message.quickreply)=='object' && message.quickreply.length==undefined))
menu_params = message.quickreply;
menuctl.getMenu(menu_params, lang)
.then(function(menu)
{
if(menu)
{
if(menu.data[0].hasOwnProperty('quick_replies'))
rpl_items = menu.data[0].quick_replies;
else
rpl_items = [];
msg = menu.data[0].hasOwnProperty('text') ? {text: menu.data[0].text} : msg;
}
rpl_items = menuctl.getFacebookButtons(rpl_items, sender, page_id, data, params);
if(message.text!=undefined && message.text!='')
msg = {text: message.text};
var items = fbmessageutil.quickReplyItems(rpl_items);
facebook_message = fbmessageutil.quickReply(msg, items);
resolve(facebook_message);
});
}
else if(message.button)
{
var bt_items = message.button;
var msg = '';
var params = (message.button_params) ? message.button_params : [];
var menu = false;
if(typeof(message.button)=='string' || (typeof(message.button)=='object' && message.button.length==undefined))
menu_params = message.button;
menuctl.getMenu(menu_params, lang)
.then(function(menu)
{
if(menu)
{
if(menu.data[0].hasOwnProperty('buttons'))
bt_items = menu.data[0].buttons;
else
bt_items = [];
msg = menu.data[0].hasOwnProperty('title') ? menu.data[0].title : msg;
}
if(message.text!=undefined && message.text!='')
msg = message.text;
var buttons = menuctl.getFacebookButtons(bt_items, sender, page_id, data, params);
facebook_message = fbmessageutil.messageButtons(msg, buttons);
resolve(facebook_message);
});
}
else if(message.text!=undefined)
{
if(!debugutil.attachment_debug && message.hasOwnProperty('attachment_data') && message.text=='attachment')
{
var attachement = message.attachment_data;
if(typeof(attachement)=='string')
attachement = attachmentctl.getAttachment(attachement);
facebook_message = fbmessageutil.attachment(attachement.type, attachmentctl.getAttachmentUrl(attachement.url));
}
else
facebook_message.text = message.text;
resolve(facebook_message);
}
});
}
/**
* Takes a facebook message and transform into a NGINB event formated
* @param {Object} data - A facebook messenger data object
* @return {Event} A bluebird promise NGINB event object
*/
Facebookctl.prototype.messengerEvent = function messengerEvent(data)
{
var self = this;
return new promise(function(resolve, reject)
{
if(data.type!=undefined && data.type=='website')
{
var response_event =
{
fb_page: false,
sender: data.sender,
type: 'message',
text: data.text,
id: '',
lang: false
}
resolve(response_event);
}
else
{
if (data.entry)
{
var entries = data.entry;
var entries_length = entries.length;
for (var i = 0; i < entries_length; i++)
{
var messaging_events = entries[i].messaging;
if (messaging_events)
{
var msg_events_count = messaging_events.length;
var response_events = [];
var promises = [];
for (var j = 0; j < msg_events_count; j++)
{
var messaging_event = messaging_events[j];
if((messaging_event.message && !messaging_event.message.is_echo) || messaging_event.postback)
{
var sender = messaging_event.sender.id.toString();
var recipient = messaging_event.recipient.id.toString();
promises.push
(
pagectl.getFacebookPageInfo(recipient, messaging_event)
.then(function(response)
{
var messaging_event = response.data;
if(response.page)
{
var lang = response.page.language;
var attachmentType = '';
var attachmentPayload = '';
var id = '';
var type = '';
var text = '';
if(messaging_event.message && messaging_event.message.attachments)
{
for (var i = 0; i < messaging_event.message.attachments.length; i++)
{
var attachment = messaging_event.message.attachments[i];
attachmentType = attachment.type;
attachmentPayload = attachment.payload.url;
if(attachment.payload.hasOwnProperty('sticker_id'))
id = attachment.payload.sticker_id;
}
}
if(messaging_event.message && messaging_event.message.text)
{
type = 'message';
text = messaging_event.message.text;
}
else if(messaging_event.postback && messaging_event.postback.payload)
{
type = 'payload';
text = messaging_event.postback.payload;
}
else if(attachmentType!='')
{
type = 'attachment';
text = attachmentPayload;
}
if(messaging_event.message && messaging_event.message.quick_reply!=undefined)
{
//type = 'quick_reply';
type = 'payload';
text = messaging_event.message.quick_reply.payload;
}
var response_event =
{
fb_page: response.page,
sender: sender,
type: type,
text: text,
id: id,
lang: lang
}
response_events.push(response_event);
}
})
);
}
}
promise.all(promises)
.then(function()
{
resolve(response_events);
});
}
}
}
else
resolve(false);
}
});
}