cyber-fca
Version:
A Facebook chat api for messenger bots.
323 lines (287 loc) • 9.06 kB
JavaScript
var utils = require("../utils");
// @NethWs3Dev
var bluebird = require("bluebird");
module.exports = function (defaultFuncs, api, ctx) {
function uploadAttachment(attachments, callback) {
callback = callback || function () {};
var uploads = [];
// create an array of promises
for (var i = 0; i < attachments.length; i++) {
if (!utils.isReadableStream(attachments[i])) {
throw {
error:
"Attachment should be a readable stream and not " +
utils.getType(attachments[i]) +
".",
};
}
var form = {
upload_1024: attachments[i],
voice_clip: "true",
};
uploads.push(
defaultFuncs
.postFormData(
"https://upload.facebook.com/ajax/mercury/upload.php",
ctx.jar,
form,
{},
)
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
.then(function (resData) {
if (resData.error) {
throw resData;
}
// We have to return the data unformatted unless we want to change it
// back in sendMessage.
return resData.payload.metadata[0];
}),
);
}
// resolve all promises
bluebird
.all(uploads)
.then(function (resData) {
callback(null, resData);
})
.catch(function (err) {
console.error("uploadAttachment", err);
return callback(err);
});
}
let variance = 0;
const epoch_id = () =>
Math.floor(Date.now() * (4194304 + (variance = (variance + 0.1) % 5)));
const emojiSizes = {
small: 1,
medium: 2,
large: 3,
};
function handleEmoji(msg, form, callback, cb) {
if (msg.emojiSize != null && msg.emoji == null) {
return callback({ error: "emoji property is empty" });
}
if (msg.emoji) {
if (!msg.emojiSize) {
msg.emojiSize = "small";
}
if (
msg.emojiSize !== "small" &&
msg.emojiSize !== "medium" &&
msg.emojiSize !== "large" &&
(isNaN(msg.emojiSize) || msg.emojiSize < 1 || msg.emojiSize > 3)
) {
return callback({ error: "emojiSize property is invalid" });
}
form.payload.tasks[0].payload.send_type = 1;
form.payload.tasks[0].payload.text = msg.emoji;
form.payload.tasks[0].payload.hot_emoji_size = !isNaN(msg.emojiSize)
? msg.emojiSize
: emojiSizes[msg.emojiSize];
}
cb();
}
function handleSticker(msg, form, callback, cb) {
if (msg.sticker) {
form.payload.tasks[0].payload.send_type = 2;
form.payload.tasks[0].payload.sticker_id = msg.sticker;
}
cb();
}
function handleAttachment(msg, form, callback, cb) {
if (msg.attachment) {
form.payload.tasks[0].payload.send_type = 3;
form.payload.tasks[0].payload.attachment_fbids = [];
if (form.payload.tasks[0].payload.text == "")
form.payload.tasks[0].payload.text = null;
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.payload.tasks[0].payload.attachment_fbids.push(file[type]); // push the id
});
cb();
});
} else {
cb();
}
}
function handleMention(msg, form, callback, cb) {
if (msg.mentions) {
form.payload.tasks[0].payload.send_type = 1;
const arrayIds = [];
const arrayOffsets = [];
const arrayLengths = [];
const mention_types = [];
for (let i = 0; i < msg.mentions.length; i++) {
const mention = msg.mentions[i];
const tag = mention.tag;
if (typeof tag !== "string") {
return callback({ error: "Mention tags must be strings." });
}
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
if (offset < 0) {
console.warn(
"handleMention",
'Mention for "' + tag + '" not found in message string.',
);
}
if (mention.id == null) {
console.warn("handleMention", "Mention id should be non-null.");
}
const id = mention.id || 0;
arrayIds.push(id);
arrayOffsets.push(offset);
arrayLengths.push(tag.length);
mention_types.push("p");
}
form.payload.tasks[0].payload.mention_data = {
mention_ids: arrayIds.join(","),
mention_offsets: arrayOffsets.join(","),
mention_lengths: arrayLengths.join(","),
mention_types: mention_types.join(","),
};
}
cb();
}
function handleLocation(msg, form, callback, cb) {
// this is not working yet
if (msg.location) {
if (msg.location.latitude == null || msg.location.longitude == null) {
return callback({
error: "location property needs both latitude and longitude",
});
}
form.payload.tasks[0].payload.send_type = 1;
form.payload.tasks[0].payload.location_data = {
coordinates: {
latitude: msg.location.latitude,
longitude: msg.location.longitude,
},
is_current_location: !!msg.location.current,
is_live_location: !!msg.location.live,
};
}
cb();
}
function send(form, threadID, callback, replyToMessage) {
if (replyToMessage) {
form.payload.tasks[0].payload.reply_metadata = {
reply_source_id: replyToMessage,
reply_source_type: 1,
reply_type: 0,
};
}
const mqttClient = ctx.mqttClient;
form.payload.tasks.forEach((task) => {
task.payload = JSON.stringify(task.payload);
});
form.payload = JSON.stringify(form.payload);
console.log(global.jsonStringifyColor(form, null, 2));
return mqttClient.publish(
"/ls_req",
JSON.stringify(form),
function (err, data) {
if (err) {
console.error("Error publishing message: ", err);
callback(err);
} else {
console.log("Message published successfully with data: ", data);
callback(null, data);
}
},
);
}
return function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
if (
!callback &&
(utils.getType(threadID) === "Function" ||
utils.getType(threadID) === "AsyncFunction")
) {
return threadID({ error: "Pass a threadID as a second argument." });
}
if (!replyToMessage && utils.getType(callback) === "String") {
replyToMessage = callback;
callback = function () {};
}
if (!callback) {
callback = function (err, friendList) {};
}
var msgType = utils.getType(msg);
var threadIDType = utils.getType(threadID);
var messageIDType = utils.getType(replyToMessage);
if (msgType !== "String" && msgType !== "Object") {
return callback({
error:
"Message should be of type string or object and not " + msgType + ".",
});
}
if (msgType === "String") {
msg = { body: msg };
}
const timestamp = Date.now();
// get full date time
const epoch = timestamp << 22;
//const otid = epoch + 0; // TODO replace with randomInt(0, 2**22)
const otid = epoch + Math.floor(Math.random() * 4194304);
const form = {
app_id: "2220391788200892",
payload: {
tasks: [
{
label: "46",
payload: {
thread_id: threadID.toString(),
otid: otid.toString(),
source: 0,
send_type: 1,
sync_group: 1,
text:
msg.body != null && msg.body != undefined
? msg.body.toString()
: "",
initiating_source: 1,
skip_url_preview_gen: 0,
},
queue_name: threadID.toString(),
task_id: 0,
failure_count: null,
},
{
label: "21",
payload: {
thread_id: threadID.toString(),
last_read_watermark_ts: Date.now(),
sync_group: 1,
},
queue_name: threadID.toString(),
task_id: 1,
failure_count: null,
},
],
epoch_id: epoch_id(),
version_id: "6120284488008082",
data_trace_id: null,
},
request_id: 1,
type: 3,
};
handleEmoji(msg, form, callback, function () {
handleLocation(msg, form, callback, function () {
handleMention(msg, form, callback, function () {
handleSticker(msg, form, callback, function () {
handleAttachment(msg, form, callback, function () {
send(form, threadID, callback, replyToMessage);
});
});
});
});
});
};
};