Skip to content

Commit

Permalink
Refactor in sendMessage. Fix Schmavery#184 and Schmavery#83.
Browse files Browse the repository at this point in the history
Deleted undocumented API `getUrl`.
  • Loading branch information
bsansouci committed Mar 2, 2016
1 parent f47d591 commit e1550e7
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 169 deletions.
1 change: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ function buildAPI(globalOptions, html, jar) {
'getOnlineUsers',
'getThreadHistory',
'getThreadList',
'getUrl',
'getUserID',
'getUserInfo',
'listen',
Expand Down
37 changes: 0 additions & 37 deletions src/getUrl.js

This file was deleted.

311 changes: 180 additions & 131 deletions src/sendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ var utils = require("../utils");
var log = require("npmlog");
var bluebird = require("bluebird");

var allowedProperties = {
attachment: true,
url: true,
sticker: true,
body: true,
};

module.exports = function(defaultFuncs, api, ctx) {
function uploadAttachment(attachments, callback) {
var uploads = [];
Expand Down Expand Up @@ -44,9 +51,169 @@ module.exports = function(defaultFuncs, api, ctx) {
});
}

function getUrl(url, callback) {
var form = {
image_height: 960,
image_width: 960,
uri: url
};

defaultFuncs
.post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
.then(function(resData) {
if (resData.error) {
return callback(resData);
}

if (!resData.payload) {
return callback({error: 'Invalid url'});
}

callback(null, resData.payload.share_data.share_params);
})
.catch(function(err) {
log.error("Error in getUrl", err);
return callback(err);
});
}

function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
// There are three cases here:
// 1. threadID is of type array, where we're starting a new group chat with users
// specified in the array.
// 2. User is sending a message to a specific user.
// 3. No additional form params and the message goes to an existing group chat.
if(utils.getType(threadID) === "Array") {
for (var i = 0; i < threadID.length; i++) {
form['message_batch[0][specific_to_list][' + i + ']'] = "fbid:" + threadID[i];
}
form['message_batch[0][specific_to_list][' + (threadID.length) + ']'] = "fbid:" + ctx.userID;
form['message_batch[0][client_thread_id]'] = "root:" + messageAndOTID;
log.info("Sending message to multiple users: " + threadID);
} else {
// This means that threadID is the id of a user, and the chat
// is a single person chat
if(isSingleUser) {
form['message_batch[0][specific_to_list][0]'] = "fbid:" + threadID;
form['message_batch[0][specific_to_list][1]'] = "fbid:" + ctx.userID;
form['message_batch[0][other_user_fbid]'] = threadID;
} else {
form['message_batch[0][thread_fbid]'] = threadID;
}
}

if(ctx.globalOptions.pageID) {
form['message_batch[0][author]'] = "fbid:" + ctx.globalOptions.pageID;
form['message_batch[0][specific_to_list][1]'] = "fbid:" + ctx.globalOptions.pageID;
form['message_batch[0][creator_info][creatorID]'] = ctx.userID;
form['message_batch[0][creator_info][creatorType]'] = "direct_admin";
form['message_batch[0][creator_info][labelType]'] = "sent_message";
form['message_batch[0][creator_info][pageID]'] = ctx.globalOptions.pageID;
form['request_user_id'] = ctx.globalOptions.pageID;
form['message_batch[0][creator_info][profileURI]'] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
}

defaultFuncs
.post("https://www.facebook.com/ajax/mercury/send_messages.php", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
.then(function(resData) {
if (!resData) {
return callback({error: "Send message failed."});
}

if (resData.error) {
if (resData.error === 1545012) {
log.warn("Got error 1545012. This might mean that you're not part of the conversation " + threadID);
}
return callback(resData);
}

var messageInfo = resData.payload.actions.reduce(function(p, v) {
return {
threadID: v.thread_fbid,
messageID: v.message_id,
timestamp: v.timestamp
} || p; }, null);

return callback(null, messageInfo);
})
.catch(function(err) {
log.error("ERROR in sendMessage --> ", err);
return callback(err);
});
}

function send(form, threadID, messageAndOTID, callback) {
// We're doing a query to this to check if the given id is the id of
// a user or of a group chat. The form will be different depending
// on that.
if(utils.getType(threadID) === "Array") {
sendContent(form, threadID, false, messageAndOTID, callback);
} else {
api.getUserInfo(threadID, function(err, res) {
if(err) {
return callback(err);
}
sendContent(form, threadID, Object.keys(res).length > 0, messageAndOTID, callback);
});
}
}

function handleUrl(msg, form, callback, cb) {
if (msg.url) {
form['message_batch[0][shareable_attachment][share_type]'] = '100';
getUrl(msg.url, function (err, params) {
if (err) {
return callback(err);
}

form['message_batch[0][shareable_attachment][share_params]'] = params;
cb();
});
} else {
cb();
}
}

function handleSticker(msg, form, callback, cb) {
if (msg.sticker) {
form['message_batch[0][sticker_id]'] = msg.sticker;
}
cb();
}

function handleAttachment(msg, form, callback, cb) {
if (msg.attachment) {
form['message_batch[0][image_ids]'] = [];
form['message_batch[0][gif_ids]'] = [];
form['message_batch[0][file_ids]'] = [];
form['message_batch[0][video_ids]'] = [];

if (utils.getType(msg.attachment) !== 'Array') {
msg.attachment = [msg.attachment];
}

uploadAttachment(msg.attachment, function (err, files) {
if (err) {
return callback(err);
}

files.forEach(function (file) {
var key = Object.keys(file);
var type = key[0]; // image_id, file_id, etc
form['message_batch[0][' + type + 's]'].push(file[type]); // push the id
});
cb();
});
} else {
cb();
}
}

return function sendMessage(msg, threadID, callback) {
if(!callback && utils.getType(threadID) === 'Function') {
throw {error: "please pass a threadID as a second argument."};
return callback({error: "Pass a threadID as a second argument."});
}
if(!callback) {
callback = function() {};
Expand All @@ -56,18 +223,23 @@ module.exports = function(defaultFuncs, api, ctx) {
var threadIDType = utils.getType(threadID);

if(msgType !== "String" && msgType !== "Object") {
throw {error: "Message should be of type string or object and not " + msgType + "."};
return callback({error: "Message should be of type string or object and not " + msgType + "."});
}

// Changing this to accomodate an array of users
if(threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") {
throw {error: "ThreadID should be of type number, string, or array and not " + threadIDType + "."};
return callback({error: "ThreadID should be of type number, string, or array and not " + threadIDType + "."});
}

if (msgType === "String") {
msg = { body: msg };
}

var disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
if (disallowedProperties.length > 0) {
return callback({error: "Dissallowed props: `" + disallowedProperties.join(', ') + "`"});
}

var messageAndOTID = utils.generateOfflineThreadingID();

var form = {
Expand Down Expand Up @@ -98,136 +270,13 @@ module.exports = function(defaultFuncs, api, ctx) {
'message_batch[0][threading_id]': utils.generateThreadingID(ctx.clientID),
'message_batch[0][ephemeral_ttl_mode]:': '0',
'message_batch[0][manual_retry_cnt]' : '0',
'message_batch[0][has_attachment]' : false,
'message_batch[0][has_attachment]' : !!(msg.attachment || msg.url || msg.sticker),
'message_batch[0][signatureID]' : utils.getSignatureID(),
};

if (msg.attachment) {
form['message_batch[0][has_attachment]'] = true;
form['message_batch[0][image_ids]'] = [];
form['message_batch[0][gif_ids]'] = [];
form['message_batch[0][file_ids]'] = [];
form['message_batch[0][video_ids]'] = [];

if (utils.getType(msg.attachment) !== 'Array') {
msg.attachment = [msg.attachment];
}

uploadAttachment(msg.attachment, function (err, files) {
if (err) {
return callback(err);
}

files.forEach(function (file) {
var key = Object.keys(file);
var type = key[0]; // image_id, file_id, etc
form['message_batch[0][' + type + 's]'].push(file[type]); // push the id
});

send();
});
} else if (msg.url) {
form['message_batch[0][has_attachment]'] = true;
form['message_batch[0][shareable_attachment][share_type]'] = 100;
api.getUrl(msg.url, function (err, params) {
if (err) {
return callback(err);
}

form['message_batch[0][shareable_attachment][share_params]'] = params;
send();
});
} else if (msg.sticker) {
form['message_batch[0][has_attachment]'] = true;
form['message_batch[0][sticker_id]'] = msg.sticker;

// Sticker can't be combined with body
delete form['message_batch[0][body]']
send();
} else {
send();
}

function sendContent(isSingleUser) {
// There are three cases here:
// 1. threadID is of type array, where we're starting a new group chat with users
// specified in the array.
// 2. User is sending a message to a specific user.
// 3. No additional form params and the message goes to an existing group chat.
if(threadIDType === "Array") {
for (var i = 0; i < threadID.length; i++) {
form['message_batch[0][specific_to_list][' + i + ']'] = "fbid:" + threadID[i];
}
form['message_batch[0][specific_to_list][' + (threadID.length) + ']'] = "fbid:" + ctx.userID;
form['message_batch[0][client_thread_id]'] = "root:" + messageAndOTID;
log.info("Sending message to multiple users: " + threadID);
} else {
// This means that threadID is the id of a user, and the chat
// is a single person chat
if(isSingleUser) {
form['message_batch[0][specific_to_list][0]'] = "fbid:" + threadID;
form['message_batch[0][specific_to_list][1]'] = "fbid:" + ctx.userID;
form['message_batch[0][other_user_fbid]'] = threadID;
} else {
form['message_batch[0][thread_fbid]'] = threadID;
}
}

if(ctx.globalOptions.pageID) {
form['message_batch[0][author]'] = "fbid:" + ctx.globalOptions.pageID;
form['message_batch[0][specific_to_list][1]'] = "fbid:" + ctx.globalOptions.pageID;
form['message_batch[0][creator_info][creatorID]'] = ctx.userID;
form['message_batch[0][creator_info][creatorType]'] = "direct_admin";
form['message_batch[0][creator_info][labelType]'] = "sent_message";
form['message_batch[0][creator_info][pageID]'] = ctx.globalOptions.pageID;
form['request_user_id'] = ctx.globalOptions.pageID;
form['message_batch[0][creator_info][profileURI]'] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
}

defaultFuncs
.post("https://www.facebook.com/ajax/mercury/send_messages.php", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
.then(function(resData) {
if (!resData) {
throw {error: "Send message failed."};
}

if (resData.error) {
if (resData.error === 1545012) {
log.warn("Got error 1545012. This might mean that you're not part of the conversation " + threadID);
}
throw resData;
}

var messageInfo = resData.payload.actions.reduce(function(p, v) {
return {
threadID: v.thread_fbid,
messageID: v.message_id,
timestamp: v.timestamp
} || p; }, null);

return callback(null, messageInfo);
})
.catch(function(err) {
log.error("ERROR in sendMessage --> ", err);
return callback(err);
});
}

function send() {
// We're doing a query to this to check if the given id is the id of
// a user or of a group chat. The form will be different depending
// on that.
if(threadIDType === "Array") {
sendContent(false);
} else {
api.getUserInfo(threadID, function(err, res) {
if(err) {
return callback(err);
}
sendContent(Object.keys(res).length > 0);
});
}
}
handleSticker(msg, form, callback,
() => handleAttachment(msg, form, callback,
() => handleUrl(msg, form, callback,
() => send(form, threadID, messageAndOTID, callback))));
};
};

0 comments on commit e1550e7

Please sign in to comment.