var promise = require('bluebird');
var botconfig = require('../config/botconfig');
var messagesutil = require('../utils/messagesutil');
var debugutil = require('../utils/debugutil');
var utility = require('../utils/utility');
var botutil = require('../utils/botutil');
exports = module.exports = function(userctl, botctl, scriptctl, updatectl, facebookctl)
{
return new Messagectl(userctl, botctl, scriptctl, updatectl, facebookctl);
}
function Messagectl(userctl, botctl, scriptctl, updatectl, facebookctl)
{
this.userctl = userctl || require('./user')();
this.botctl = botctl || require('./bot')();
this.scriptctl = scriptctl || require('./action')();
this.updatectl = updatectl || require('./update')();
this.facebookctl = facebookctl || require('./facebook')();
this.replyqueue = {};
this.inputqueue = {};
//callback functions
this.onFinishFacebookMessageDispatch = false;
this.onFinishAllFacebookMessageDispatch = false;
this.onFinishMessageDispatch = false;
this.onFinishAllMessageDispatch = false;
this.onHandleCustomMessageReplyItems = false;
}
/**
* Sends a message to messenger
* @param {Event} event - A NGINB event object
* @param {Object} user_data - If you have a previous loaded userdata, you can send it here
* @return {Object} A bluebird promisse response
*/
Messagectl.prototype.processMessengeEvent = function processMessengeEvent(event, user_data)
{
var self = this;
return new promise(function(resolve, reject)
{
if(isCommandFromMessage(event.type, event.text))
event.text = 'cmdimsmart';
if(!self.inputqueue.hasOwnProperty(event.sender))
self.inputqueue[event.sender] = [];
if(!self.replyqueue.hasOwnProperty(event.sender))
self.replyqueue[event.sender] = [];
if(!isInInputQueue(self, event.sender, event.text))
{
self.inputqueue[event.sender].push(event);
if(self.inputqueue[event.sender].length<=1)
{
if(event.sender!='' && event.fb_page.id!='')
{
if(user_data)
{
event.userdata = user_data;
self.dispatchEvent(event)
.then(function(dispatch_event)
{
resolve(dispatch_event);
});
}
else
{
self.userctl.getUserData(event.sender, event.fb_page, event.lang)
.then(function(user_response)
{
debugutil.log('user_response', user_response);
event.userdata = user_response;
self.dispatchEvent(event)
.then(function(dispatch_event)
{
resolve(dispatch_event);
});
});
}
}
}
}
});
}
/**
* Dispatch an event, can dispatch diretctly or using a bot
* @param {Event} event - A NGINB event object
* @return {Object} a bluebird promisse response
*/
Messagectl.prototype.dispatchEvent = function dispatchEvent(event)
{
var self = this;
return new promise(function(resolve, reject)
{
if(event.text && event.text.indexOf('<')!=-1 && event.text.indexOf('>')!=-1)
{
var messages = botutil.getVariablesObjectFromString(event.text);
if(messages[0].text==event.text)
{
if(event.userdata.status && event.userdata.status>0)
{
self.processBotEvent(event)
.then(function(response)
{
resolve(response);
});
}
}
else
{
self.replyqueue[event.sender].push.apply(self.replyqueue[event.sender], messages);
self.dispatchMessage(messages[0], event);
resolve({status:1, message:'direct message(s) dispactched', data:messages});
}
}
else
{
if(event.userdata.status && event.userdata.status>0)
{
self.processBotEvent(event)
.then(function(response)
{
resolve(response);
});
}
}
});
}
/**
* Process a bot event
* @param {Event} event - A NGINB event object
* @return {Object} a bluebird promisse response
*/
Messagectl.prototype.processBotEvent = function processBotEvent(event)
{
var self = this;
return new promise(function(resolve, reject)
{
debugutil.log('event_response', 'page:' + event.fb_page.id, 'sender:' + event.sender, 'type:' + event.type, 'text:' + event.text, 'lang:' + event.lang);
event.userdata.now = Date.now();
event.userdata.lang = event.lang;
self.botctl.setUservars(event.sender, event.userdata, event.lang);
if(event.type == "attachment")
{
if(event.id==369239263222822)
event.text = "cmdattachmentlike";
else
event.text = "cmdattachmentsent";
}
self.botctl.processEvent(event)
.then(function(bot_response)
{
self.dispatchDirectMessage(bot_response.reply, bot_response.event);
resolve({status:1, message:'bot message(s) dispactched', data:bot_response.reply});
});
});
}
/**
* Dispatch a message directly
* @param {Object} message, an object with message params, the "text" key is required
* @param {Event} event - A NGINB event object to send to next line if needed
* @return {Object} a bluebird promisse response
*/
Messagectl.prototype.dispatchDirectMessage = function dispatchDirectMessage(message, event)
{
var self = this;
message = (message.length==undefined) ? [message] : message;
var dispatch = (self.replyqueue[event.sender].length==0) ? true : false;
self.replyqueue[event.sender].push.apply(self.replyqueue[event.sender], message);
event.userdata = self.botctl.getUservars(event.sender, event.lang);
if(dispatch)
self.dispatchMessage(message[0], event);
}
/**
* Dispatch a message
* @param {Object} message, an object with message params, the text key is required
* @param {Event} event - A NGINB event object to send to next line if needed
* @return {Object} a bluebird promisse response
*/
Messagectl.prototype.dispatchMessage = function dispatchMessage(message, event)
{
var self = this;
debugutil.log('message_dispatched', message);
var dispatch_message = true;
var dispatch_next = true;
var dispatch_data = {action:false, update:false};
if(message.storage)
self.updatectl.processUpdate(event.sender, event.fb_page.id, {[botconfig.botconfig.storagetable]: {storage: message.storage}});
//if it has an if, execute a comparation and if false, skip the line
if(message.if)
dispatch_message = utility.if(message.if, event.userdata);
//if it has an ifbreak, execute a comparation and if false, break the queue
if(message.ifbreak)
{
dispatch_message = utility.if(message.ifbreak, event.userdata);
dispatch_next = false;
}
if(dispatch_message)
{
if(message.text)
message.text = botutil.replaceVariable(message.text, event.userdata);
//if it has some scripts do the scripts
if(message.script)
{
var params = message.hasOwnProperty('script_params') ? message.script_params : false;
dispatch_data.script = self.scriptctl.processFunction(event.sender, event.fb_page.id, message.script, event, params);
}
//if it has some updates, so update
if(message.update)
{
self.updatectl.processUpdate(event.sender, event.fb_page.id, message.update, event.userdata);
dispatch_data.update = true;
}
//if it has next call to bot, call bot again
if(message.next)
{
if(typeof(message.next)=='String')
{
event.text = message.next;
self.processBotEvent(event);
}
else if(typeof(message.next)=='object' && message.next.length!=undefined)
{
for(var i=0; i<message.next.length; i++)
{
event.text = message.next[i];
self.processBotEvent(event);
}
}
}
if(message.attachment)
{
if(typeof(message.attachment)=='String')
{
var message_attachment =
{
text: 'attachment',
attachment_data: message.attachment
};
if(message.text=='' && !message.hasOwnProperty('quickreply'))
message = message_attachment;
else
{
if(self.replyqueue[event.sender])
self.replyqueue[event.sender].splice(1, 0, message_attachment);
}
}
else if(typeof(message.attachment)=='object')
{
var attachments = [];
if(message.attachment.length==undefined)
attachments.push(message.attachment);
else
attachments = message.attachment;
for(var i=0; i<attachments.length; i++)
{
var message_attachment =
{
text: 'attachment',
attachment_data: attachments[i]
};
if(i==0 && message.text=='' && !message.hasOwnProperty('quickreply'))
message = message_attachment;
else
{
if(self.replyqueue[event.sender])
self.replyqueue[event.sender].splice(1 + i, 0, message_attachment);
}
}
}
}
if(self.onHandleCustomMessageReplyItems)
dispatch_data.action = self.onHandleCustomMessageReplyItems(message, event);
dispatch_data.action = (dispatch_data.action==undefined) ? false : dispatch_data.action;
if(message.text!=undefined)
{
var delay = (message.delay) ? Number(message.delay) * 1000 : 0;
delay = (isNaN(delay)) ? 0 : delay;
if(botconfig.botconfig.humanize && message.text!='attachment')
{
var chance = (event.userdata.hasOwnProperty('error_chance')) ? event.userdata.error_chance : botconfig.botconfig.typing_error_chance;
var byword = (event.userdata.hasOwnProperty('error_byword')) ? event.userdata.error_byword : false;
message.text = botutil.humanizeString(message.text, event.lang, chance, byword);
}
if(botconfig.botconfig.typing_delay)
delay += botutil.getTypingDelay(message.text);
if(botconfig.facebook.send_to)
{
var fb_page = event.fb_page;
var sender = event.sender;
self.facebookctl.getFacebookMessage(event.sender, fb_page.id, message, event.lang, event.userdata)
.then(function(facebook_message)
{
if(!message.delay && delay>botconfig.botconfig.time_for_typing_on)
self.facebookctl.sendAction(fb_page, sender, 'typing_on');
promise.delay(delay).then(function()
{
var gotonext = true;
debugutil.log('facebook_send_object', JSON.stringify(facebook_message));
if(facebook_message.text!='')
{
gotonext = false;
self.facebookctl.sendMessage(fb_page, sender, facebook_message, event)
.then(function(fb_response)
{
debugutil.log('facebook_response', fb_response.data.body);
var last = self.replyqueue[event.sender].length==1 ? true : false;
if(self.onFinishFacebookMessageDispatch)
self.onFinishFacebookMessageDispatch({fb_response:fb_response, event:event, dispatch_data:dispatch_data});
if(last && self.onFinishAllFacebookMessageDispatch)
self.onFinishAllFacebookMessageDispatch({fb_response:fb_response, event:event, dispatch_data:dispatch_data});
self.dispatchNextResponse(fb_response.callback_data, dispatch_data);
});
}
if(message.template)
{
gotonext = false;
self.facebookctl.getFacebookTemplate(event.sender, fb_page.id, message, event.lang, event.userdata)
.then(function(facebook_template)
{
debugutil.log('facebook_template_object', JSON.stringify(facebook_template));
self.facebookctl.sendMessage(fb_page, sender, facebook_template, event)
.then(function(fb_response)
{
debugutil.log('facebook_response', fb_response.data.body);
if(message.text=='')
{
var last = self.replyqueue[event.sender].length==1 ? true : false;
if(self.onFinishFacebookMessageDispatch)
self.onFinishFacebookMessageDispatch({fb_response:fb_response, event:event, dispatch_data:dispatch_data});
if(last && self.onFinishAllFacebookMessageDispatch)
self.onFinishAllFacebookMessageDispatch({fb_response:fb_response, event:event, dispatch_data:dispatch_data});
self.dispatchNextResponse(fb_response.callback_data, dispatch_data);
}
});
});
}
if(gotonext)
self.dispatchNextResponse(event, dispatch_data);
});
});
}
else
self.dispatchNextResponse(event, dispatch_data);
}
else
self.dispatchNextResponse(event, dispatch_data);
}
else
{
if(dispatch_next)
self.dispatchNextResponse(event, dispatch_data);
else
{
if(self.replyqueue.hasOwnProperty(event.sender))
self.replyqueue[event.sender] = [];
self.dispatchNextResponse(event, dispatch_data);
}
}
}
/**
* Verify if response queue has next message and dispatch
* @param {Event} event - A NGINB event object
* @param {Object} dispatch_data - A custom object data to dispatch
*/
Messagectl.prototype.dispatchNextResponse = function dispatchNextResponse(event, dispatch_data)
{
var self = this;
var last = self.replyqueue[event.sender].length==1 ? true : false;
if(self.onFinishMessageDispatch)
self.onFinishMessageDispatch({event:event, dispatch_data:dispatch_data});
if(last && self.onFinishAllMessageDispatch)
self.onFinishAllMessageDispatch({event:event, dispatch_data:dispatch_data});
if(event)
{
if(self.replyqueue.hasOwnProperty(event.sender))
{
//remove sent message from array
if(self.replyqueue[event.sender].length>0)
self.replyqueue[event.sender].splice(0, 1);
//if array has more messages to dispatch, so dispatch
if(self.replyqueue[event.sender].length>0)
self.dispatchMessage(self.replyqueue[event.sender][0], event);
else
self.dispatchNextInput(event);
}
else
self.dispatchNextInput(event);
}
}
/**
* Verify if input queue has a next message and dispatch
* @param {Event} event - A NGINB event object
*/
Messagectl.prototype.dispatchNextInput = function dispatchNextInput(event)
{
var self = this;
if(self.inputqueue.hasOwnProperty(event.sender))
{
//remove input message from array
self.inputqueue[event.sender].splice(0, 1);
//if array has more messages to dispatch, so dispatch
if(self.inputqueue[event.sender].length>0)
{
if(event.hasOwnProperty('userdata'))
self.inputqueue[event.sender][0].userdata = event.userdata;
self.dispatchEvent(self.inputqueue[event.sender][0]);
}
}
}
/**
* Private function to verify if a message is in input queue
* @private
* @param {Message_Controller} self - This instance of Message Controller
* @param {String} sender - The facebook user pid
* @param {String} text - The text message to send
*/
function isInInputQueue(self, sender, text)
{
var response = false;
if(self.inputqueue.hasOwnProperty(sender))
{
for(var i=0, len=self.inputqueue[sender].length; i<len; i++)
{
if(self.inputqueue[sender][i].text==text)
{
response = true;
break;
}
}
}
return response;
}
/**
* Private function to verify a message contains any command
* @private
* @param {String} event_type, the type of the event
* @param {String} event_text, the text of the event
*/
function isCommandFromMessage(event_type, event_text)
{
var response = false;
if(!debugutil.accept_commands_from_user && event_type!='payload')
{
var regex = /cmd\s*([^\n\r]*)/g;
var matches = regex.exec(event_text);
if(matches)
response = true;
}
return response;
}