Upload 55 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- includes/login/src/addExternalModule.js +19 -0
- includes/login/src/addUserToGroup.js +113 -0
- includes/login/src/changeAdminStatus.js +79 -0
- includes/login/src/changeArchivedStatus.js +55 -0
- includes/login/src/changeAvatar.js +126 -0
- includes/login/src/changeBio.js +77 -0
- includes/login/src/changeBlockedStatus.js +47 -0
- includes/login/src/changeGroupImage.js +132 -0
- includes/login/src/changeNickname.js +59 -0
- includes/login/src/changeThreadColor.js +65 -0
- includes/login/src/changeThreadEmoji.js +55 -0
- includes/login/src/createNewGroup.js +86 -0
- includes/login/src/createPoll.js +71 -0
- includes/login/src/deleteMessage.js +56 -0
- includes/login/src/deleteThread.js +56 -0
- includes/login/src/editMessage.js +59 -0
- includes/login/src/forwardAttachment.js +60 -0
- includes/login/src/getCurrentUserID.js +7 -0
- includes/login/src/getEmojiUrl.js +29 -0
- includes/login/src/getFriendsList.js +83 -0
- includes/login/src/getMessage.js +796 -0
- includes/login/src/getThreadHistory.js +666 -0
- includes/login/src/getThreadInfo.js +232 -0
- includes/login/src/getThreadList.js +241 -0
- includes/login/src/getThreadPictures.js +79 -0
- includes/login/src/getUserID.js +66 -0
- includes/login/src/getUserInfo.js +74 -0
- includes/login/src/handleFriendRequest.js +61 -0
- includes/login/src/handleMessageRequest.js +65 -0
- includes/login/src/httpGet.js +57 -0
- includes/login/src/httpPost.js +57 -0
- includes/login/src/httpPostFormData.js +63 -0
- includes/login/src/listenMqtt.js +853 -0
- includes/login/src/logout.js +75 -0
- includes/login/src/markAsDelivered.js +266 -0
- includes/login/src/markAsRead.js +80 -0
- includes/login/src/markAsReadAll.js +50 -0
- includes/login/src/markAsSeen.js +59 -0
- includes/login/src/muteThread.js +52 -0
- includes/login/src/refreshFb_dtsg.js +81 -0
- includes/login/src/removeUserFromGroup.js +79 -0
- includes/login/src/resolvePhotoUrl.js +45 -0
- includes/login/src/searchForThread.js +53 -0
- includes/login/src/sendComment.js +160 -0
- includes/login/src/sendMessage.js +447 -0
- includes/login/src/sendMessageMqtt.js +316 -0
- includes/login/src/sendTypingIndicator.js +103 -0
- includes/login/src/setMessageReaction.js +117 -0
- includes/login/src/setPostReaction.js +111 -0
- includes/login/src/setTitle.js +86 -0
includes/login/src/addExternalModule.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
|
| 5 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 6 |
+
return function addExternalModule(moduleObj) {
|
| 7 |
+
if (utils.getType(moduleObj) == "Object") {
|
| 8 |
+
for (const apiName in moduleObj) {
|
| 9 |
+
if (utils.getType(moduleObj[apiName]) == "Function") {
|
| 10 |
+
api[apiName] = moduleObj[apiName](defaultFuncs, api, ctx);
|
| 11 |
+
} else {
|
| 12 |
+
throw new Error(`Item "${apiName}" in moduleObj must be a function, not ${utils.getType(moduleObj[apiName])}!`);
|
| 13 |
+
}
|
| 14 |
+
}
|
| 15 |
+
} else {
|
| 16 |
+
throw new Error(`moduleObj must be an object, not ${utils.getType(moduleObj)}!`);
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
};
|
includes/login/src/addUserToGroup.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function addUserToGroup(userID, threadID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (
|
| 16 |
+
!callback &&
|
| 17 |
+
(utils.getType(threadID) === "Function" ||
|
| 18 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 19 |
+
) {
|
| 20 |
+
throw new utils.CustomError({ error: "please pass a threadID as a second argument." });
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
if (!callback) {
|
| 24 |
+
callback = function (err) {
|
| 25 |
+
if (err) {
|
| 26 |
+
return rejectFunc(err);
|
| 27 |
+
}
|
| 28 |
+
resolveFunc();
|
| 29 |
+
};
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
if (
|
| 33 |
+
utils.getType(threadID) !== "Number" &&
|
| 34 |
+
utils.getType(threadID) !== "String"
|
| 35 |
+
) {
|
| 36 |
+
throw new utils.CustomError({
|
| 37 |
+
error:
|
| 38 |
+
"ThreadID should be of type Number or String and not " +
|
| 39 |
+
utils.getType(threadID) +
|
| 40 |
+
"."
|
| 41 |
+
});
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
if (utils.getType(userID) !== "Array") {
|
| 45 |
+
userID = [userID];
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
const messageAndOTID = utils.generateOfflineThreadingID();
|
| 49 |
+
const form = {
|
| 50 |
+
client: "mercury",
|
| 51 |
+
action_type: "ma-type:log-message",
|
| 52 |
+
author: "fbid:" + (ctx.i_userID || ctx.userID),
|
| 53 |
+
thread_id: "",
|
| 54 |
+
timestamp: Date.now(),
|
| 55 |
+
timestamp_absolute: "Today",
|
| 56 |
+
timestamp_relative: utils.generateTimestampRelative(),
|
| 57 |
+
timestamp_time_passed: "0",
|
| 58 |
+
is_unread: false,
|
| 59 |
+
is_cleared: false,
|
| 60 |
+
is_forward: false,
|
| 61 |
+
is_filtered_content: false,
|
| 62 |
+
is_filtered_content_bh: false,
|
| 63 |
+
is_filtered_content_account: false,
|
| 64 |
+
is_spoof_warning: false,
|
| 65 |
+
source: "source:chat:web",
|
| 66 |
+
"source_tags[0]": "source:chat",
|
| 67 |
+
log_message_type: "log:subscribe",
|
| 68 |
+
status: "0",
|
| 69 |
+
offline_threading_id: messageAndOTID,
|
| 70 |
+
message_id: messageAndOTID,
|
| 71 |
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
| 72 |
+
manual_retry_cnt: "0",
|
| 73 |
+
thread_fbid: threadID
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
for (let i = 0; i < userID.length; i++) {
|
| 77 |
+
if (
|
| 78 |
+
utils.getType(userID[i]) !== "Number" &&
|
| 79 |
+
utils.getType(userID[i]) !== "String"
|
| 80 |
+
) {
|
| 81 |
+
throw new utils.CustomError({
|
| 82 |
+
error:
|
| 83 |
+
"Elements of userID should be of type Number or String and not " +
|
| 84 |
+
utils.getType(userID[i]) +
|
| 85 |
+
"."
|
| 86 |
+
});
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
form["log_message_data[added_participants][" + i + "]"] =
|
| 90 |
+
"fbid:" + userID[i];
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
defaultFuncs
|
| 94 |
+
.post("https://www.facebook.com/messaging/send/", ctx.jar, form)
|
| 95 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 96 |
+
.then(function (resData) {
|
| 97 |
+
if (!resData) {
|
| 98 |
+
throw new utils.CustomError({ error: "Add to group failed." });
|
| 99 |
+
}
|
| 100 |
+
if (resData.error) {
|
| 101 |
+
throw new utils.CustomError(resData);
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
return callback();
|
| 105 |
+
})
|
| 106 |
+
.catch(function (err) {
|
| 107 |
+
log.error("addUserToGroup", err);
|
| 108 |
+
return callback(err);
|
| 109 |
+
});
|
| 110 |
+
|
| 111 |
+
return returnPromise;
|
| 112 |
+
};
|
| 113 |
+
};
|
includes/login/src/changeAdminStatus.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeAdminStatus(threadID, adminIDs, adminStatus, callback) {
|
| 8 |
+
if (utils.getType(threadID) !== "String") {
|
| 9 |
+
throw new utils.CustomError({ error: "changeAdminStatus: threadID must be a string" });
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
if (utils.getType(adminIDs) === "String") {
|
| 13 |
+
adminIDs = [adminIDs];
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
if (utils.getType(adminIDs) !== "Array") {
|
| 17 |
+
throw new utils.CustomError({ error: "changeAdminStatus: adminIDs must be an array or string" });
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
if (utils.getType(adminStatus) !== "Boolean") {
|
| 21 |
+
throw new utils.CustomError({ error: "changeAdminStatus: adminStatus must be a string" });
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
let resolveFunc = function () { };
|
| 25 |
+
let rejectFunc = function () { };
|
| 26 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 27 |
+
resolveFunc = resolve;
|
| 28 |
+
rejectFunc = reject;
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
+
if (!callback) {
|
| 32 |
+
callback = function (err) {
|
| 33 |
+
if (err) {
|
| 34 |
+
return rejectFunc(err);
|
| 35 |
+
}
|
| 36 |
+
resolveFunc();
|
| 37 |
+
};
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
|
| 41 |
+
throw new utils.CustomError({ error: "changeAdminStatus: callback is not a function" });
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
const form = {
|
| 45 |
+
"thread_fbid": threadID
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
let i = 0;
|
| 49 |
+
for (const u of adminIDs) {
|
| 50 |
+
form[`admin_ids[${i++}]`] = u;
|
| 51 |
+
}
|
| 52 |
+
form["add"] = adminStatus;
|
| 53 |
+
|
| 54 |
+
defaultFuncs
|
| 55 |
+
.post("https://www.facebook.com/messaging/save_admins/?dpr=1", ctx.jar, form)
|
| 56 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 57 |
+
.then(function (resData) {
|
| 58 |
+
if (resData.error) {
|
| 59 |
+
switch (resData.error) {
|
| 60 |
+
case 1976004:
|
| 61 |
+
throw new utils.CustomError({ error: "Cannot alter admin status: you are not an admin.", rawResponse: resData });
|
| 62 |
+
case 1357031:
|
| 63 |
+
throw new utils.CustomError({ error: "Cannot alter admin status: this thread is not a group chat.", rawResponse: resData });
|
| 64 |
+
default:
|
| 65 |
+
throw new utils.CustomError({ error: "Cannot alter admin status: unknown error.", rawResponse: resData });
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
callback();
|
| 70 |
+
})
|
| 71 |
+
.catch(function (err) {
|
| 72 |
+
log.error("changeAdminStatus", err);
|
| 73 |
+
return callback(err);
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
return returnPromise;
|
| 77 |
+
};
|
| 78 |
+
};
|
| 79 |
+
|
includes/login/src/changeArchivedStatus.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeArchivedStatus(threadOrThreads, archive, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc();
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
const form = {};
|
| 25 |
+
|
| 26 |
+
if (utils.getType(threadOrThreads) === "Array") {
|
| 27 |
+
for (let i = 0; i < threadOrThreads.length; i++) {
|
| 28 |
+
form["ids[" + threadOrThreads[i] + "]"] = archive;
|
| 29 |
+
}
|
| 30 |
+
} else {
|
| 31 |
+
form["ids[" + threadOrThreads + "]"] = archive;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
defaultFuncs
|
| 35 |
+
.post(
|
| 36 |
+
"https://www.facebook.com/ajax/mercury/change_archived_status.php",
|
| 37 |
+
ctx.jar,
|
| 38 |
+
form
|
| 39 |
+
)
|
| 40 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 41 |
+
.then(function (resData) {
|
| 42 |
+
if (resData.error) {
|
| 43 |
+
throw resData;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return callback();
|
| 47 |
+
})
|
| 48 |
+
.catch(function (err) {
|
| 49 |
+
log.error("changeArchivedStatus", err);
|
| 50 |
+
return callback(err);
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
return returnPromise;
|
| 54 |
+
};
|
| 55 |
+
};
|
includes/login/src/changeAvatar.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
function handleUpload(image, callback) {
|
| 8 |
+
const uploads = [];
|
| 9 |
+
|
| 10 |
+
const form = {
|
| 11 |
+
profile_id: ctx.i_userID || ctx.userID,
|
| 12 |
+
photo_source: 57,
|
| 13 |
+
av: ctx.i_userID || ctx.userID,
|
| 14 |
+
file: image
|
| 15 |
+
};
|
| 16 |
+
|
| 17 |
+
uploads.push(
|
| 18 |
+
defaultFuncs
|
| 19 |
+
.postFormData(
|
| 20 |
+
"https://www.facebook.com/profile/picture/upload/",
|
| 21 |
+
ctx.jar,
|
| 22 |
+
form,
|
| 23 |
+
{}
|
| 24 |
+
)
|
| 25 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 26 |
+
.then(function (resData) {
|
| 27 |
+
if (resData.error) {
|
| 28 |
+
throw resData;
|
| 29 |
+
}
|
| 30 |
+
return resData;
|
| 31 |
+
})
|
| 32 |
+
);
|
| 33 |
+
|
| 34 |
+
// resolve all promises
|
| 35 |
+
Promise
|
| 36 |
+
.all(uploads)
|
| 37 |
+
.then(function (resData) {
|
| 38 |
+
callback(null, resData);
|
| 39 |
+
})
|
| 40 |
+
.catch(function (err) {
|
| 41 |
+
log.error("handleUpload", err);
|
| 42 |
+
return callback(err);
|
| 43 |
+
});
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return function changeAvatar(image, caption = "", timestamp = null, callback) {
|
| 47 |
+
let resolveFunc = function () { };
|
| 48 |
+
let rejectFunc = function () { };
|
| 49 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 50 |
+
resolveFunc = resolve;
|
| 51 |
+
rejectFunc = reject;
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
if (!timestamp && utils.getType(caption) === "Number") {
|
| 55 |
+
timestamp = caption;
|
| 56 |
+
caption = "";
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
if (!timestamp && !callback && (utils.getType(caption) == "Function" || utils.getType(caption) == "AsyncFunction")) {
|
| 60 |
+
callback = caption;
|
| 61 |
+
caption = "";
|
| 62 |
+
timestamp = null;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
if (!callback) callback = function (err, data) {
|
| 66 |
+
if (err) {
|
| 67 |
+
return rejectFunc(err);
|
| 68 |
+
}
|
| 69 |
+
resolveFunc(data);
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
if (!utils.isReadableStream(image))
|
| 73 |
+
return callback("Image is not a readable stream");
|
| 74 |
+
|
| 75 |
+
handleUpload(image, function (err, payload) {
|
| 76 |
+
if (err) {
|
| 77 |
+
return callback(err);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
const form = {
|
| 81 |
+
av: ctx.i_userID || ctx.userID,
|
| 82 |
+
fb_api_req_friendly_name: "ProfileCometProfilePictureSetMutation",
|
| 83 |
+
fb_api_caller_class: "RelayModern",
|
| 84 |
+
doc_id: "5066134240065849",
|
| 85 |
+
variables: JSON.stringify({
|
| 86 |
+
input: {
|
| 87 |
+
caption,
|
| 88 |
+
existing_photo_id: payload[0].payload.fbid,
|
| 89 |
+
expiration_time: timestamp,
|
| 90 |
+
profile_id: ctx.i_userID || ctx.userID,
|
| 91 |
+
profile_pic_method: "EXISTING",
|
| 92 |
+
profile_pic_source: "TIMELINE",
|
| 93 |
+
scaled_crop_rect: {
|
| 94 |
+
height: 1,
|
| 95 |
+
width: 1,
|
| 96 |
+
x: 0,
|
| 97 |
+
y: 0
|
| 98 |
+
},
|
| 99 |
+
skip_cropping: true,
|
| 100 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 101 |
+
client_mutation_id: Math.round(Math.random() * 19).toString()
|
| 102 |
+
},
|
| 103 |
+
isPage: false,
|
| 104 |
+
isProfile: true,
|
| 105 |
+
scale: 3
|
| 106 |
+
})
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
defaultFuncs
|
| 110 |
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
| 111 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 112 |
+
.then(function (resData) {
|
| 113 |
+
if (resData.errors) {
|
| 114 |
+
throw resData;
|
| 115 |
+
}
|
| 116 |
+
return callback(null, resData[0].data.profile_picture_set);
|
| 117 |
+
})
|
| 118 |
+
.catch(function (err) {
|
| 119 |
+
log.error("changeAvatar", err);
|
| 120 |
+
return callback(err);
|
| 121 |
+
});
|
| 122 |
+
});
|
| 123 |
+
|
| 124 |
+
return returnPromise;
|
| 125 |
+
};
|
| 126 |
+
};
|
includes/login/src/changeBio.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeBio(bio, publish, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
if (utils.getType(publish) == "Function" || utils.getType(publish) == "AsyncFunction") {
|
| 17 |
+
callback = publish;
|
| 18 |
+
} else {
|
| 19 |
+
callback = function (err) {
|
| 20 |
+
if (err) {
|
| 21 |
+
return rejectFunc(err);
|
| 22 |
+
}
|
| 23 |
+
resolveFunc();
|
| 24 |
+
};
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
if (utils.getType(publish) != "Boolean") {
|
| 29 |
+
publish = false;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
if (utils.getType(bio) != "String") {
|
| 33 |
+
bio = "";
|
| 34 |
+
publish = false;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
const form = {
|
| 38 |
+
fb_api_caller_class: "RelayModern",
|
| 39 |
+
fb_api_req_friendly_name: "ProfileCometSetBioMutation",
|
| 40 |
+
// This doc_is is valid as of May 23, 2020
|
| 41 |
+
doc_id: "2725043627607610",
|
| 42 |
+
variables: JSON.stringify({
|
| 43 |
+
input: {
|
| 44 |
+
bio: bio,
|
| 45 |
+
publish_bio_feed_story: publish,
|
| 46 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 47 |
+
client_mutation_id: Math.round(Math.random() * 1024).toString()
|
| 48 |
+
},
|
| 49 |
+
hasProfileTileViewID: false,
|
| 50 |
+
profileTileViewID: null,
|
| 51 |
+
scale: 1
|
| 52 |
+
}),
|
| 53 |
+
av: ctx.i_userID || ctx.userID
|
| 54 |
+
};
|
| 55 |
+
|
| 56 |
+
defaultFuncs
|
| 57 |
+
.post(
|
| 58 |
+
"https://www.facebook.com/api/graphql/",
|
| 59 |
+
ctx.jar,
|
| 60 |
+
form
|
| 61 |
+
)
|
| 62 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 63 |
+
.then(function (resData) {
|
| 64 |
+
if (resData.errors) {
|
| 65 |
+
throw resData;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
return callback();
|
| 69 |
+
})
|
| 70 |
+
.catch(function (err) {
|
| 71 |
+
log.error("changeBio", err);
|
| 72 |
+
return callback(err);
|
| 73 |
+
});
|
| 74 |
+
|
| 75 |
+
return returnPromise;
|
| 76 |
+
};
|
| 77 |
+
};
|
includes/login/src/changeBlockedStatus.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeBlockedStatus(userID, block, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc();
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
defaultFuncs
|
| 25 |
+
.post(
|
| 26 |
+
`https://www.facebook.com/messaging/${block ? "" : "un"}block_messages/`,
|
| 27 |
+
ctx.jar,
|
| 28 |
+
{
|
| 29 |
+
fbid: userID
|
| 30 |
+
}
|
| 31 |
+
)
|
| 32 |
+
.then(utils.saveCookies(ctx.jar))
|
| 33 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 34 |
+
.then(function (resData) {
|
| 35 |
+
if (resData.error) {
|
| 36 |
+
throw resData;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
return callback();
|
| 40 |
+
})
|
| 41 |
+
.catch(function (err) {
|
| 42 |
+
log.error("changeBlockedStatus", err);
|
| 43 |
+
return callback(err);
|
| 44 |
+
});
|
| 45 |
+
return returnPromise;
|
| 46 |
+
};
|
| 47 |
+
};
|
includes/login/src/changeGroupImage.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
function handleUpload(image, callback) {
|
| 8 |
+
const uploads = [];
|
| 9 |
+
|
| 10 |
+
const form = {
|
| 11 |
+
images_only: "true",
|
| 12 |
+
"attachment[]": image
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
uploads.push(
|
| 16 |
+
defaultFuncs
|
| 17 |
+
.postFormData(
|
| 18 |
+
"https://upload.facebook.com/ajax/mercury/upload.php",
|
| 19 |
+
ctx.jar,
|
| 20 |
+
form,
|
| 21 |
+
{}
|
| 22 |
+
)
|
| 23 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 24 |
+
.then(function (resData) {
|
| 25 |
+
if (resData.error) {
|
| 26 |
+
throw resData;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
return resData.payload.metadata[0];
|
| 30 |
+
})
|
| 31 |
+
);
|
| 32 |
+
|
| 33 |
+
// resolve all promises
|
| 34 |
+
Promise
|
| 35 |
+
.all(uploads)
|
| 36 |
+
.then(function (resData) {
|
| 37 |
+
callback(null, resData);
|
| 38 |
+
})
|
| 39 |
+
.catch(function (err) {
|
| 40 |
+
log.error("handleUpload", err);
|
| 41 |
+
return callback(err);
|
| 42 |
+
});
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
return function changeGroupImage(image, threadID, callback) {
|
| 46 |
+
if (
|
| 47 |
+
!callback &&
|
| 48 |
+
(utils.getType(threadID) === "Function" ||
|
| 49 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 50 |
+
) {
|
| 51 |
+
throw { error: "please pass a threadID as a second argument." };
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
if (!utils.isReadableStream(image)) {
|
| 55 |
+
throw { error: "please pass a readable stream as a first argument." };
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
let resolveFunc = function () { };
|
| 59 |
+
let rejectFunc = function () { };
|
| 60 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 61 |
+
resolveFunc = resolve;
|
| 62 |
+
rejectFunc = reject;
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
if (!callback) {
|
| 66 |
+
callback = function (err) {
|
| 67 |
+
if (err) {
|
| 68 |
+
return rejectFunc(err);
|
| 69 |
+
}
|
| 70 |
+
resolveFunc();
|
| 71 |
+
};
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
const messageAndOTID = utils.generateOfflineThreadingID();
|
| 75 |
+
const form = {
|
| 76 |
+
client: "mercury",
|
| 77 |
+
action_type: "ma-type:log-message",
|
| 78 |
+
author: "fbid:" + (ctx.i_userID || ctx.userID),
|
| 79 |
+
author_email: "",
|
| 80 |
+
ephemeral_ttl_mode: "0",
|
| 81 |
+
is_filtered_content: false,
|
| 82 |
+
is_filtered_content_account: false,
|
| 83 |
+
is_filtered_content_bh: false,
|
| 84 |
+
is_filtered_content_invalid_app: false,
|
| 85 |
+
is_filtered_content_quasar: false,
|
| 86 |
+
is_forward: false,
|
| 87 |
+
is_spoof_warning: false,
|
| 88 |
+
is_unread: false,
|
| 89 |
+
log_message_type: "log:thread-image",
|
| 90 |
+
manual_retry_cnt: "0",
|
| 91 |
+
message_id: messageAndOTID,
|
| 92 |
+
offline_threading_id: messageAndOTID,
|
| 93 |
+
source: "source:chat:web",
|
| 94 |
+
"source_tags[0]": "source:chat",
|
| 95 |
+
status: "0",
|
| 96 |
+
thread_fbid: threadID,
|
| 97 |
+
thread_id: "",
|
| 98 |
+
timestamp: Date.now(),
|
| 99 |
+
timestamp_absolute: "Today",
|
| 100 |
+
timestamp_relative: utils.generateTimestampRelative(),
|
| 101 |
+
timestamp_time_passed: "0"
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
handleUpload(image, function (err, payload) {
|
| 105 |
+
if (err) {
|
| 106 |
+
return callback(err);
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
form["thread_image_id"] = payload[0]["image_id"];
|
| 110 |
+
form["thread_id"] = threadID;
|
| 111 |
+
|
| 112 |
+
defaultFuncs
|
| 113 |
+
.post("https://www.facebook.com/messaging/set_thread_image/", ctx.jar, form)
|
| 114 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 115 |
+
.then(function (resData) {
|
| 116 |
+
// check for errors here
|
| 117 |
+
|
| 118 |
+
if (resData.error) {
|
| 119 |
+
throw resData;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
return callback();
|
| 123 |
+
})
|
| 124 |
+
.catch(function (err) {
|
| 125 |
+
log.error("changeGroupImage", err);
|
| 126 |
+
return callback(err);
|
| 127 |
+
});
|
| 128 |
+
});
|
| 129 |
+
|
| 130 |
+
return returnPromise;
|
| 131 |
+
};
|
| 132 |
+
};
|
includes/login/src/changeNickname.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeNickname(nickname, threadID, participantID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
if (!callback) {
|
| 15 |
+
callback = function (err) {
|
| 16 |
+
if (err) {
|
| 17 |
+
return rejectFunc(err);
|
| 18 |
+
}
|
| 19 |
+
resolveFunc();
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
const form = {
|
| 24 |
+
nickname: nickname,
|
| 25 |
+
participant_id: participantID,
|
| 26 |
+
thread_or_other_fbid: threadID
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
defaultFuncs
|
| 30 |
+
.post(
|
| 31 |
+
"https://www.facebook.com/messaging/save_thread_nickname/?source=thread_settings&dpr=1",
|
| 32 |
+
ctx.jar,
|
| 33 |
+
form
|
| 34 |
+
)
|
| 35 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 36 |
+
.then(function (resData) {
|
| 37 |
+
if (resData.error === 1545014) {
|
| 38 |
+
throw { error: "Trying to change nickname of user isn't in thread" };
|
| 39 |
+
}
|
| 40 |
+
if (resData.error === 1357031) {
|
| 41 |
+
throw {
|
| 42 |
+
error:
|
| 43 |
+
"Trying to change user nickname of a thread that doesn't exist. Have at least one message in the thread before trying to change the user nickname."
|
| 44 |
+
};
|
| 45 |
+
}
|
| 46 |
+
if (resData.error) {
|
| 47 |
+
throw resData;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
return callback();
|
| 51 |
+
})
|
| 52 |
+
.catch(function (err) {
|
| 53 |
+
log.error("changeNickname", err);
|
| 54 |
+
return callback(err);
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
return returnPromise;
|
| 58 |
+
};
|
| 59 |
+
};
|
includes/login/src/changeThreadColor.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeThreadColor(color, threadID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(err);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
if (!isNaN(color)) {
|
| 25 |
+
color = color.toString();
|
| 26 |
+
}
|
| 27 |
+
const validatedColor = color !== null ? color.toLowerCase() : color; // API only accepts lowercase letters in hex string
|
| 28 |
+
|
| 29 |
+
const form = {
|
| 30 |
+
dpr: 1,
|
| 31 |
+
queries: JSON.stringify({
|
| 32 |
+
o0: {
|
| 33 |
+
//This doc_id is valid as of January 31, 2020
|
| 34 |
+
doc_id: "1727493033983591",
|
| 35 |
+
query_params: {
|
| 36 |
+
data: {
|
| 37 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 38 |
+
client_mutation_id: "0",
|
| 39 |
+
source: "SETTINGS",
|
| 40 |
+
theme_id: validatedColor,
|
| 41 |
+
thread_id: threadID
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
})
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
defaultFuncs
|
| 49 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 50 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 51 |
+
.then(function (resData) {
|
| 52 |
+
if (resData[resData.length - 1].error_results > 0) {
|
| 53 |
+
throw new utils.CustomError(resData[0].o0.errors);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
return callback();
|
| 57 |
+
})
|
| 58 |
+
.catch(function (err) {
|
| 59 |
+
log.error("changeThreadColor", err);
|
| 60 |
+
return callback(err);
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
return returnPromise;
|
| 64 |
+
};
|
| 65 |
+
};
|
includes/login/src/changeThreadEmoji.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function changeThreadEmoji(emoji, threadID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc();
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
const form = {
|
| 24 |
+
emoji_choice: emoji,
|
| 25 |
+
thread_or_other_fbid: threadID
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
defaultFuncs
|
| 29 |
+
.post(
|
| 30 |
+
"https://www.facebook.com/messaging/save_thread_emoji/?source=thread_settings&__pc=EXP1%3Amessengerdotcom_pkg",
|
| 31 |
+
ctx.jar,
|
| 32 |
+
form
|
| 33 |
+
)
|
| 34 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 35 |
+
.then(function (resData) {
|
| 36 |
+
if (resData.error === 1357031) {
|
| 37 |
+
throw {
|
| 38 |
+
error:
|
| 39 |
+
"Trying to change emoji of a chat that doesn't exist. Have at least one message in the thread before trying to change the emoji."
|
| 40 |
+
};
|
| 41 |
+
}
|
| 42 |
+
if (resData.error) {
|
| 43 |
+
throw resData;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return callback();
|
| 47 |
+
})
|
| 48 |
+
.catch(function (err) {
|
| 49 |
+
log.error("changeThreadEmoji", err);
|
| 50 |
+
return callback(err);
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
return returnPromise;
|
| 54 |
+
};
|
| 55 |
+
};
|
includes/login/src/createNewGroup.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function createNewGroup(participantIDs, groupTitle, callback) {
|
| 8 |
+
if (utils.getType(groupTitle) == "Function") {
|
| 9 |
+
callback = groupTitle;
|
| 10 |
+
groupTitle = null;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
if (utils.getType(participantIDs) !== "Array") {
|
| 14 |
+
throw { error: "createNewGroup: participantIDs should be an array." };
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
if (participantIDs.length < 2) {
|
| 18 |
+
throw { error: "createNewGroup: participantIDs should have at least 2 IDs." };
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
let resolveFunc = function () { };
|
| 22 |
+
let rejectFunc = function () { };
|
| 23 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 24 |
+
resolveFunc = resolve;
|
| 25 |
+
rejectFunc = reject;
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
if (!callback) {
|
| 29 |
+
callback = function (err, threadID) {
|
| 30 |
+
if (err) {
|
| 31 |
+
return rejectFunc(err);
|
| 32 |
+
}
|
| 33 |
+
resolveFunc(threadID);
|
| 34 |
+
};
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
const pids = [];
|
| 38 |
+
for (const n in participantIDs) {
|
| 39 |
+
pids.push({
|
| 40 |
+
fbid: participantIDs[n]
|
| 41 |
+
});
|
| 42 |
+
}
|
| 43 |
+
pids.push({ fbid: ctx.i_userID || ctx.userID });
|
| 44 |
+
|
| 45 |
+
const form = {
|
| 46 |
+
fb_api_caller_class: "RelayModern",
|
| 47 |
+
fb_api_req_friendly_name: "MessengerGroupCreateMutation",
|
| 48 |
+
av: ctx.i_userID || ctx.userID,
|
| 49 |
+
//This doc_id is valid as of January 11th, 2020
|
| 50 |
+
doc_id: "577041672419534",
|
| 51 |
+
variables: JSON.stringify({
|
| 52 |
+
input: {
|
| 53 |
+
entry_point: "jewel_new_group",
|
| 54 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 55 |
+
participants: pids,
|
| 56 |
+
client_mutation_id: Math.round(Math.random() * 1024).toString(),
|
| 57 |
+
thread_settings: {
|
| 58 |
+
name: groupTitle,
|
| 59 |
+
joinable_mode: "PRIVATE",
|
| 60 |
+
thread_image_fbid: null
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
})
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
defaultFuncs
|
| 67 |
+
.post(
|
| 68 |
+
"https://www.facebook.com/api/graphql/",
|
| 69 |
+
ctx.jar,
|
| 70 |
+
form
|
| 71 |
+
)
|
| 72 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 73 |
+
.then(function (resData) {
|
| 74 |
+
if (resData.errors) {
|
| 75 |
+
throw resData;
|
| 76 |
+
}
|
| 77 |
+
return callback(null, resData.data.messenger_group_thread_create.thread.thread_key.thread_fbid);
|
| 78 |
+
})
|
| 79 |
+
.catch(function (err) {
|
| 80 |
+
log.error("createNewGroup", err);
|
| 81 |
+
return callback(err);
|
| 82 |
+
});
|
| 83 |
+
|
| 84 |
+
return returnPromise;
|
| 85 |
+
};
|
| 86 |
+
};
|
includes/login/src/createPoll.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function createPoll(title, threadID, options, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
if (utils.getType(options) == "Function") {
|
| 17 |
+
callback = options;
|
| 18 |
+
options = null;
|
| 19 |
+
} else {
|
| 20 |
+
callback = function (err) {
|
| 21 |
+
if (err) {
|
| 22 |
+
return rejectFunc(err);
|
| 23 |
+
}
|
| 24 |
+
resolveFunc();
|
| 25 |
+
};
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
if (!options) {
|
| 29 |
+
options = {}; // Initial poll options are optional
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
const form = {
|
| 33 |
+
target_id: threadID,
|
| 34 |
+
question_text: title
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
// Set fields for options (and whether they are selected initially by the posting user)
|
| 38 |
+
let ind = 0;
|
| 39 |
+
for (const opt in options) {
|
| 40 |
+
// eslint-disable-next-line no-prototype-builtins
|
| 41 |
+
if (options.hasOwnProperty(opt)) {
|
| 42 |
+
form["option_text_array[" + ind + "]"] = opt;
|
| 43 |
+
form["option_is_selected_array[" + ind + "]"] = options[opt]
|
| 44 |
+
? "1"
|
| 45 |
+
: "0";
|
| 46 |
+
ind++;
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
defaultFuncs
|
| 51 |
+
.post(
|
| 52 |
+
"https://www.facebook.com/messaging/group_polling/create_poll/?dpr=1",
|
| 53 |
+
ctx.jar,
|
| 54 |
+
form
|
| 55 |
+
)
|
| 56 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 57 |
+
.then(function (resData) {
|
| 58 |
+
if (resData.payload.status != "success") {
|
| 59 |
+
throw resData;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
return callback();
|
| 63 |
+
})
|
| 64 |
+
.catch(function (err) {
|
| 65 |
+
log.error("createPoll", err);
|
| 66 |
+
return callback(err);
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
return returnPromise;
|
| 70 |
+
};
|
| 71 |
+
};
|
includes/login/src/deleteMessage.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function deleteMessage(messageOrMessages, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
if (!callback) {
|
| 15 |
+
callback = function (err) {
|
| 16 |
+
if (err) {
|
| 17 |
+
return rejectFunc(err);
|
| 18 |
+
}
|
| 19 |
+
resolveFunc();
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
const form = {
|
| 24 |
+
client: "mercury"
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
if (utils.getType(messageOrMessages) !== "Array") {
|
| 28 |
+
messageOrMessages = [messageOrMessages];
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
for (let i = 0; i < messageOrMessages.length; i++) {
|
| 32 |
+
form["message_ids[" + i + "]"] = messageOrMessages[i];
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
defaultFuncs
|
| 36 |
+
.post(
|
| 37 |
+
"https://www.facebook.com/ajax/mercury/delete_messages.php",
|
| 38 |
+
ctx.jar,
|
| 39 |
+
form
|
| 40 |
+
)
|
| 41 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 42 |
+
.then(function (resData) {
|
| 43 |
+
if (resData.error) {
|
| 44 |
+
throw resData;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
return callback();
|
| 48 |
+
})
|
| 49 |
+
.catch(function (err) {
|
| 50 |
+
log.error("deleteMessage", err);
|
| 51 |
+
return callback(err);
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
return returnPromise;
|
| 55 |
+
};
|
| 56 |
+
};
|
includes/login/src/deleteThread.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function deleteThread(threadOrThreads, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
if (!callback) {
|
| 15 |
+
callback = function (err) {
|
| 16 |
+
if (err) {
|
| 17 |
+
return rejectFunc(err);
|
| 18 |
+
}
|
| 19 |
+
resolveFunc();
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
const form = {
|
| 24 |
+
client: "mercury"
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
if (utils.getType(threadOrThreads) !== "Array") {
|
| 28 |
+
threadOrThreads = [threadOrThreads];
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
for (let i = 0; i < threadOrThreads.length; i++) {
|
| 32 |
+
form["ids[" + i + "]"] = threadOrThreads[i];
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
defaultFuncs
|
| 36 |
+
.post(
|
| 37 |
+
"https://www.facebook.com/ajax/mercury/delete_thread.php",
|
| 38 |
+
ctx.jar,
|
| 39 |
+
form
|
| 40 |
+
)
|
| 41 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 42 |
+
.then(function (resData) {
|
| 43 |
+
if (resData.error) {
|
| 44 |
+
throw resData;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
return callback();
|
| 48 |
+
})
|
| 49 |
+
.catch(function (err) {
|
| 50 |
+
log.error("deleteThread", err);
|
| 51 |
+
return callback(err);
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
return returnPromise;
|
| 55 |
+
};
|
| 56 |
+
};
|
includes/login/src/editMessage.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use_strict";
|
| 2 |
+
|
| 3 |
+
const { generateOfflineThreadingID } = require('../utils');
|
| 4 |
+
|
| 5 |
+
function isCallable(func) {
|
| 6 |
+
try {
|
| 7 |
+
Reflect.apply(func, null, []);
|
| 8 |
+
return true;
|
| 9 |
+
} catch (error) {
|
| 10 |
+
return false;
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 15 |
+
|
| 16 |
+
return function editMessage(text, messageID, callback) {
|
| 17 |
+
if (!ctx.mqttClient) {
|
| 18 |
+
throw new Error('Not connected to MQTT');
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
ctx.wsReqNumber += 1;
|
| 23 |
+
ctx.wsTaskNumber += 1;
|
| 24 |
+
|
| 25 |
+
const taskPayload = {
|
| 26 |
+
message_id: messageID,
|
| 27 |
+
text: text,
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
const task = {
|
| 31 |
+
failure_count: null,
|
| 32 |
+
label: '742',
|
| 33 |
+
payload: JSON.stringify(taskPayload),
|
| 34 |
+
queue_name: 'edit_message',
|
| 35 |
+
task_id: ctx.wsTaskNumber,
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
const content = {
|
| 39 |
+
app_id: '2220391788200892',
|
| 40 |
+
payload: {
|
| 41 |
+
data_trace_id: null,
|
| 42 |
+
epoch_id: parseInt(generateOfflineThreadingID()),
|
| 43 |
+
tasks: [],
|
| 44 |
+
version_id: '6903494529735864',
|
| 45 |
+
},
|
| 46 |
+
request_id: ctx.wsReqNumber,
|
| 47 |
+
type: 3,
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
content.payload.tasks.push(task);
|
| 51 |
+
content.payload = JSON.stringify(content.payload);
|
| 52 |
+
|
| 53 |
+
if (isCallable(callback)) {
|
| 54 |
+
ctx.reqCallbacks[ctx.wsReqNumber] = callback;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
ctx.mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1, retain: false });
|
| 58 |
+
};
|
| 59 |
+
}
|
includes/login/src/forwardAttachment.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function forwardAttachment(attachmentID, userOrUsers, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
if (!callback) {
|
| 15 |
+
callback = function (err) {
|
| 16 |
+
if (err) {
|
| 17 |
+
return rejectFunc(err);
|
| 18 |
+
}
|
| 19 |
+
resolveFunc();
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
const form = {
|
| 24 |
+
attachment_id: attachmentID
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
if (utils.getType(userOrUsers) !== "Array") {
|
| 28 |
+
userOrUsers = [userOrUsers];
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
const timestamp = Math.floor(Date.now() / 1000);
|
| 32 |
+
|
| 33 |
+
for (let i = 0; i < userOrUsers.length; i++) {
|
| 34 |
+
//That's good, the key of the array is really timestmap in seconds + index
|
| 35 |
+
//Probably time when the attachment will be sent?
|
| 36 |
+
form["recipient_map[" + (timestamp + i) + "]"] = userOrUsers[i];
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
defaultFuncs
|
| 40 |
+
.post(
|
| 41 |
+
"https://www.facebook.com/mercury/attachments/forward/",
|
| 42 |
+
ctx.jar,
|
| 43 |
+
form
|
| 44 |
+
)
|
| 45 |
+
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
|
| 46 |
+
.then(function (resData) {
|
| 47 |
+
if (resData.error) {
|
| 48 |
+
throw resData;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
return callback();
|
| 52 |
+
})
|
| 53 |
+
.catch(function (err) {
|
| 54 |
+
log.error("forwardAttachment", err);
|
| 55 |
+
return callback(err);
|
| 56 |
+
});
|
| 57 |
+
|
| 58 |
+
return returnPromise;
|
| 59 |
+
};
|
| 60 |
+
};
|
includes/login/src/getCurrentUserID.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 4 |
+
return function getCurrentUserID() {
|
| 5 |
+
return ctx.i_userID || ctx.userID;
|
| 6 |
+
};
|
| 7 |
+
};
|
includes/login/src/getEmojiUrl.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const util = require("util");
|
| 4 |
+
|
| 5 |
+
module.exports = function () {
|
| 6 |
+
return function getEmojiUrl(c, size, pixelRatio) {
|
| 7 |
+
/*
|
| 8 |
+
Resolves Facebook Messenger emoji image asset URL for an emoji character.
|
| 9 |
+
Supported sizes are 32, 64, and 128.
|
| 10 |
+
Supported pixel ratios are '1.0' and '1.5' (possibly more; haven't tested)
|
| 11 |
+
*/
|
| 12 |
+
const baseUrl = "https://static.xx.fbcdn.net/images/emoji.php/v8/z%s/%s";
|
| 13 |
+
pixelRatio = pixelRatio || "1.0";
|
| 14 |
+
|
| 15 |
+
const ending = util.format(
|
| 16 |
+
"%s/%s/%s.png",
|
| 17 |
+
pixelRatio,
|
| 18 |
+
size,
|
| 19 |
+
c.codePointAt(0).toString(16)
|
| 20 |
+
);
|
| 21 |
+
let base = 317426846;
|
| 22 |
+
for (let i = 0; i < ending.length; i++) {
|
| 23 |
+
base = (base << 5) - base + ending.charCodeAt(i);
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
const hashed = (base & 255).toString(16);
|
| 27 |
+
return util.format(baseUrl, hashed, ending);
|
| 28 |
+
};
|
| 29 |
+
};
|
includes/login/src/getFriendsList.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
// [almost] copy pasted from one of FB's minified file (GenderConst)
|
| 7 |
+
const GENDERS = {
|
| 8 |
+
0: "unknown",
|
| 9 |
+
1: "female_singular",
|
| 10 |
+
2: "male_singular",
|
| 11 |
+
3: "female_singular_guess",
|
| 12 |
+
4: "male_singular_guess",
|
| 13 |
+
5: "mixed",
|
| 14 |
+
6: "neuter_singular",
|
| 15 |
+
7: "unknown_singular",
|
| 16 |
+
8: "female_plural",
|
| 17 |
+
9: "male_plural",
|
| 18 |
+
10: "neuter_plural",
|
| 19 |
+
11: "unknown_plural"
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
function formatData(obj) {
|
| 23 |
+
return Object.keys(obj).map(function (key) {
|
| 24 |
+
const user = obj[key];
|
| 25 |
+
return {
|
| 26 |
+
alternateName: user.alternateName,
|
| 27 |
+
firstName: user.firstName,
|
| 28 |
+
gender: GENDERS[user.gender],
|
| 29 |
+
userID: utils.formatID(user.id.toString()),
|
| 30 |
+
isFriend: user.is_friend != null && user.is_friend ? true : false,
|
| 31 |
+
fullName: user.name,
|
| 32 |
+
profilePicture: user.thumbSrc,
|
| 33 |
+
type: user.type,
|
| 34 |
+
profileUrl: user.uri,
|
| 35 |
+
vanity: user.vanity,
|
| 36 |
+
isBirthday: !!user.is_birthday
|
| 37 |
+
};
|
| 38 |
+
});
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 42 |
+
return function getFriendsList(callback) {
|
| 43 |
+
let resolveFunc = function () { };
|
| 44 |
+
let rejectFunc = function () { };
|
| 45 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 46 |
+
resolveFunc = resolve;
|
| 47 |
+
rejectFunc = reject;
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
if (!callback) {
|
| 51 |
+
callback = function (err, friendList) {
|
| 52 |
+
if (err) {
|
| 53 |
+
return rejectFunc(err);
|
| 54 |
+
}
|
| 55 |
+
resolveFunc(friendList);
|
| 56 |
+
};
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
defaultFuncs
|
| 60 |
+
.postFormData(
|
| 61 |
+
"https://www.facebook.com/chat/user_info_all",
|
| 62 |
+
ctx.jar,
|
| 63 |
+
{},
|
| 64 |
+
{ viewer: ctx.i_userID || ctx.userID }
|
| 65 |
+
)
|
| 66 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 67 |
+
.then(function (resData) {
|
| 68 |
+
if (!resData) {
|
| 69 |
+
throw { error: "getFriendsList returned empty object." };
|
| 70 |
+
}
|
| 71 |
+
if (resData.error) {
|
| 72 |
+
throw resData;
|
| 73 |
+
}
|
| 74 |
+
callback(null, formatData(resData.payload));
|
| 75 |
+
})
|
| 76 |
+
.catch(function (err) {
|
| 77 |
+
log.error("getFriendsList", err);
|
| 78 |
+
return callback(err);
|
| 79 |
+
});
|
| 80 |
+
|
| 81 |
+
return returnPromise;
|
| 82 |
+
};
|
| 83 |
+
};
|
includes/login/src/getMessage.js
ADDED
|
@@ -0,0 +1,796 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
// Original
|
| 4 |
+
/**
|
| 5 |
+
* @author https://github.com/Schmavery/facebook-chat-api/pull/865
|
| 6 |
+
*/
|
| 7 |
+
|
| 8 |
+
const utils = require("../utils");
|
| 9 |
+
const log = require("npmlog");
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
function formatMessage(threadID, data) {
|
| 13 |
+
switch (data.__typename) {
|
| 14 |
+
case "ThreadNameMessage":
|
| 15 |
+
return {
|
| 16 |
+
type: "event",
|
| 17 |
+
threadID: threadID,
|
| 18 |
+
messageID: data.message_id,
|
| 19 |
+
logMessageType: "log:thread-name",
|
| 20 |
+
logMessageData: {
|
| 21 |
+
name: data.thread_name
|
| 22 |
+
},
|
| 23 |
+
logMessageBody: data.snippet,
|
| 24 |
+
timestamp: data.timestamp_precise,
|
| 25 |
+
author: data.message_sender.id
|
| 26 |
+
};
|
| 27 |
+
case "ThreadImageMessage":
|
| 28 |
+
const metadata = data.image_with_metadata;
|
| 29 |
+
return {
|
| 30 |
+
type: "event",
|
| 31 |
+
threadID: threadID,
|
| 32 |
+
messageID: data.message_id,
|
| 33 |
+
logMessageType: "log:thread-image",
|
| 34 |
+
logMessageData: metadata ? {
|
| 35 |
+
attachmentID: metadata.legacy_attachment_id,
|
| 36 |
+
width: metadata.original_dimensions.x,
|
| 37 |
+
height: metadata.original_dimensions.y,
|
| 38 |
+
url: metadata.preview.uri
|
| 39 |
+
} :
|
| 40 |
+
{
|
| 41 |
+
attachmentID: null,
|
| 42 |
+
width: null,
|
| 43 |
+
height: null,
|
| 44 |
+
url: null
|
| 45 |
+
},
|
| 46 |
+
logMessageBody: data.snippet,
|
| 47 |
+
timestamp: data.timestamp_precise,
|
| 48 |
+
author: data.message_sender.id
|
| 49 |
+
};
|
| 50 |
+
case "GenericAdminTextMessage":
|
| 51 |
+
switch (data.extensible_message_admin_text_type) {
|
| 52 |
+
case "CHANGE_THREAD_THEME":
|
| 53 |
+
return {
|
| 54 |
+
type: "event",
|
| 55 |
+
threadID: threadID,
|
| 56 |
+
messageID: data.message_id,
|
| 57 |
+
logMessageType: "log:thread-color",
|
| 58 |
+
logMessageData: colors.find(color => color.theme_color === data.extensible_message_admin_text.theme_color) || {
|
| 59 |
+
theme_color: data.extensible_message_admin_text.theme_color,
|
| 60 |
+
theme_id: null,
|
| 61 |
+
theme_emoji: null,
|
| 62 |
+
gradient: null,
|
| 63 |
+
should_show_icon: null,
|
| 64 |
+
theme_name_with_subtitle: null
|
| 65 |
+
},
|
| 66 |
+
logMessageBody: data.snippet,
|
| 67 |
+
timestamp: data.timestamp_precise,
|
| 68 |
+
author: data.message_sender.id
|
| 69 |
+
};
|
| 70 |
+
case "CHANGE_THREAD_ICON":
|
| 71 |
+
const thread_icon = data.extensible_message_admin_text.thread_icon;
|
| 72 |
+
return {
|
| 73 |
+
type: "event",
|
| 74 |
+
threadID: threadID,
|
| 75 |
+
messageID: data.message_id,
|
| 76 |
+
logMessageType: "log:thread-icon",
|
| 77 |
+
logMessageData: {
|
| 78 |
+
thread_icon_url: `https://static.xx.fbcdn.net/images/emoji.php/v9/t3c/1/16/${thread_icon.codePointAt(0).toString(16)}.png`,
|
| 79 |
+
thread_icon: thread_icon
|
| 80 |
+
},
|
| 81 |
+
logMessageBody: data.snippet,
|
| 82 |
+
timestamp: data.timestamp_precise,
|
| 83 |
+
author: data.message_sender.id
|
| 84 |
+
};
|
| 85 |
+
case "CHANGE_THREAD_NICKNAME":
|
| 86 |
+
return {
|
| 87 |
+
type: "event",
|
| 88 |
+
threadID: threadID,
|
| 89 |
+
messageID: data.message_id,
|
| 90 |
+
logMessageType: "log:user-nickname",
|
| 91 |
+
logMessageData: {
|
| 92 |
+
nickname: data.extensible_message_admin_text.nickname,
|
| 93 |
+
participant_id: data.extensible_message_admin_text.participant_id
|
| 94 |
+
},
|
| 95 |
+
logMessageBody: data.snippet,
|
| 96 |
+
timestamp: data.timestamp_precise,
|
| 97 |
+
author: data.message_sender.id
|
| 98 |
+
};
|
| 99 |
+
case "GROUP_POLL":
|
| 100 |
+
const question = data.extensible_message_admin_text.question;
|
| 101 |
+
return {
|
| 102 |
+
type: "event",
|
| 103 |
+
threadID: threadID,
|
| 104 |
+
messageID: data.message_id,
|
| 105 |
+
logMessageType: "log:thread-poll",
|
| 106 |
+
logMessageData: {
|
| 107 |
+
question_json: JSON.stringify({
|
| 108 |
+
id: question.id,
|
| 109 |
+
text: question.text,
|
| 110 |
+
total_count: data.extensible_message_admin_text.total_count,
|
| 111 |
+
viewer_has_voted: question.viewer_has_voted,
|
| 112 |
+
question_type: "",
|
| 113 |
+
creator_id: data.message_sender.id,
|
| 114 |
+
options: question.options.nodes.map(option => ({
|
| 115 |
+
id: option.id,
|
| 116 |
+
text: option.text,
|
| 117 |
+
total_count: option.voters.nodes.length,
|
| 118 |
+
viewer_has_voted: option.viewer_has_voted,
|
| 119 |
+
voters: option.voters.nodes.map(voter => voter.id)
|
| 120 |
+
}))
|
| 121 |
+
}),
|
| 122 |
+
event_type: data.extensible_message_admin_text.event_type.toLowerCase(),
|
| 123 |
+
question_id: question.id
|
| 124 |
+
},
|
| 125 |
+
logMessageBody: data.snippet,
|
| 126 |
+
timestamp: data.timestamp_precise,
|
| 127 |
+
author: data.message_sender.id
|
| 128 |
+
};
|
| 129 |
+
default:
|
| 130 |
+
throw new Error(`Unknown admin text type: "${data.extensible_message_admin_text_type}", if this happens to you let me know when it happens. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.`);
|
| 131 |
+
}
|
| 132 |
+
case "UserMessage":
|
| 133 |
+
return {
|
| 134 |
+
senderID: data.message_sender.id,
|
| 135 |
+
body: data.message.text,
|
| 136 |
+
threadID: threadID,
|
| 137 |
+
messageID: data.message_id,
|
| 138 |
+
reactions: data.message_reactions.map(r => ({
|
| 139 |
+
[r.user.id]: r.reaction
|
| 140 |
+
})),
|
| 141 |
+
attachments: data.blob_attachments && data.blob_attachments.length > 0 ?
|
| 142 |
+
data.blob_attachments.length.map(att => {
|
| 143 |
+
let x;
|
| 144 |
+
try {
|
| 145 |
+
x = utils._formatAttachment(att);
|
| 146 |
+
} catch (ex) {
|
| 147 |
+
x = att;
|
| 148 |
+
x.error = ex;
|
| 149 |
+
x.type = "unknown";
|
| 150 |
+
}
|
| 151 |
+
return x;
|
| 152 |
+
}) :
|
| 153 |
+
data.extensible_attachment && Object.keys(data.extensible_attachment).length > 0 ?
|
| 154 |
+
[{
|
| 155 |
+
type: "share",
|
| 156 |
+
ID: data.extensible_attachment.legacy_attachment_id,
|
| 157 |
+
url: data.extensible_attachment.story_attachment.url,
|
| 158 |
+
|
| 159 |
+
title: data.extensible_attachment.story_attachment.title_with_entities.text,
|
| 160 |
+
description: data.extensible_attachment.story_attachment.description.text,
|
| 161 |
+
source: data.extensible_attachment.story_attachment.source,
|
| 162 |
+
|
| 163 |
+
image: ((data.extensible_attachment.story_attachment.media || {}).image || {}).uri,
|
| 164 |
+
width: ((data.extensible_attachment.story_attachment.media || {}).image || {}).width,
|
| 165 |
+
height: ((data.extensible_attachment.story_attachment.media || {}).image || {}).height,
|
| 166 |
+
playable: (data.extensible_attachment.story_attachment.media || {}).is_playable || false,
|
| 167 |
+
duration: (data.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
|
| 168 |
+
|
| 169 |
+
subattachments: data.extensible_attachment.subattachments,
|
| 170 |
+
properties: data.extensible_attachment.story_attachment.properties
|
| 171 |
+
}] :
|
| 172 |
+
[],
|
| 173 |
+
mentions: data.message.ranges.map(mention => ({
|
| 174 |
+
[mention.entity.id]: data.message.text.substring(mention.offset, mention.offset + mention.length)
|
| 175 |
+
})),
|
| 176 |
+
timestamp: data.timestamp_precise
|
| 177 |
+
};
|
| 178 |
+
default:
|
| 179 |
+
throw new Error(`Unknown message type: "${data.__typename}", if this happens to you let me know when it happens. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.`);
|
| 180 |
+
// If this happens to you let me know when it happens
|
| 181 |
+
// Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.
|
| 182 |
+
// return Object.assign({ type: "unknown", data });
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
function parseDelta(threadID, delta) {
|
| 188 |
+
if (delta.replied_to_message) {
|
| 189 |
+
return Object.assign({
|
| 190 |
+
type: "message_reply"
|
| 191 |
+
}, formatMessage(threadID, delta), {
|
| 192 |
+
messageReply: formatMessage(threadID, delta.replied_to_message.message)
|
| 193 |
+
});
|
| 194 |
+
}
|
| 195 |
+
else {
|
| 196 |
+
return formatMessage(threadID, delta);
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 202 |
+
return function getMessage(threadID, messageID, callback) {
|
| 203 |
+
let resolveFunc = function () { };
|
| 204 |
+
let rejectFunc = function () { };
|
| 205 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 206 |
+
resolveFunc = resolve;
|
| 207 |
+
rejectFunc = reject;
|
| 208 |
+
});
|
| 209 |
+
|
| 210 |
+
if (!callback) {
|
| 211 |
+
callback = function (err, info) {
|
| 212 |
+
if (err)
|
| 213 |
+
return rejectFunc(err);
|
| 214 |
+
resolveFunc(info);
|
| 215 |
+
};
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
if (!threadID || !messageID) {
|
| 219 |
+
return callback({ error: "getMessage: need threadID and messageID" });
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
const form = {
|
| 223 |
+
"av": ctx.globalOptions.pageID,
|
| 224 |
+
"queries": JSON.stringify({
|
| 225 |
+
"o0": {
|
| 226 |
+
//This doc_id is valid as of ? (prob January 18, 2020)
|
| 227 |
+
"doc_id": "1768656253222505",
|
| 228 |
+
"query_params": {
|
| 229 |
+
"thread_and_message_id": {
|
| 230 |
+
"thread_id": threadID,
|
| 231 |
+
"message_id": messageID
|
| 232 |
+
}
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
})
|
| 236 |
+
};
|
| 237 |
+
|
| 238 |
+
defaultFuncs
|
| 239 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 240 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 241 |
+
.then((resData) => {
|
| 242 |
+
if (resData[resData.length - 1].error_results > 0) {
|
| 243 |
+
throw resData[0].o0.errors;
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
if (resData[resData.length - 1].successful_results === 0) {
|
| 247 |
+
throw { error: "getMessage: there was no successful_results", res: resData };
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
const fetchData = resData[0].o0.data.message;
|
| 251 |
+
if (fetchData) {
|
| 252 |
+
callback(null, parseDelta(threadID, fetchData));
|
| 253 |
+
}
|
| 254 |
+
else {
|
| 255 |
+
throw fetchData;
|
| 256 |
+
}
|
| 257 |
+
})
|
| 258 |
+
.catch((err) => {
|
| 259 |
+
log.error("getMessage", err);
|
| 260 |
+
callback(err);
|
| 261 |
+
});
|
| 262 |
+
|
| 263 |
+
return returnPromise;
|
| 264 |
+
};
|
| 265 |
+
};
|
| 266 |
+
|
| 267 |
+
const colors = [
|
| 268 |
+
{
|
| 269 |
+
theme_color: 'FF000000',
|
| 270 |
+
theme_id: '788274591712841',
|
| 271 |
+
theme_emoji: '🖤',
|
| 272 |
+
gradient: '["FFF0F0F0"]',
|
| 273 |
+
should_show_icon: '',
|
| 274 |
+
theme_name_with_subtitle: 'Monochrome'
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
theme_color: 'FFFF5CA1',
|
| 278 |
+
theme_id: '169463077092846',
|
| 279 |
+
theme_emoji: null,
|
| 280 |
+
gradient: null,
|
| 281 |
+
should_show_icon: '1',
|
| 282 |
+
theme_name_with_subtitle: 'Hot Pink'
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
theme_color: 'FF2825B5',
|
| 286 |
+
theme_id: '271607034185782',
|
| 287 |
+
theme_emoji: null,
|
| 288 |
+
gradient: '["FF5E007E","FF331290","FF2825B5"]',
|
| 289 |
+
should_show_icon: '1',
|
| 290 |
+
theme_name_with_subtitle: 'Shadow'
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
theme_color: 'FFD9A900',
|
| 294 |
+
theme_id: '2533652183614000',
|
| 295 |
+
theme_emoji: null,
|
| 296 |
+
gradient: '["FF550029","FFAA3232","FFD9A900"]',
|
| 297 |
+
should_show_icon: '1',
|
| 298 |
+
theme_name_with_subtitle: 'Maple'
|
| 299 |
+
},
|
| 300 |
+
{
|
| 301 |
+
theme_color: 'FFFB45DE',
|
| 302 |
+
theme_id: '2873642949430623',
|
| 303 |
+
theme_emoji: null,
|
| 304 |
+
gradient: null,
|
| 305 |
+
should_show_icon: '1',
|
| 306 |
+
theme_name_with_subtitle: 'Tulip'
|
| 307 |
+
},
|
| 308 |
+
{
|
| 309 |
+
theme_color: 'FF5E007E',
|
| 310 |
+
theme_id: '193497045377796',
|
| 311 |
+
theme_emoji: null,
|
| 312 |
+
gradient: null,
|
| 313 |
+
should_show_icon: '1',
|
| 314 |
+
theme_name_with_subtitle: 'Grape'
|
| 315 |
+
},
|
| 316 |
+
{
|
| 317 |
+
theme_color: 'FF7AA286',
|
| 318 |
+
theme_id: '1455149831518874',
|
| 319 |
+
theme_emoji: '🌑',
|
| 320 |
+
gradient: '["FF25C0E1","FFCE832A"]',
|
| 321 |
+
should_show_icon: '',
|
| 322 |
+
theme_name_with_subtitle: 'Dune'
|
| 323 |
+
},
|
| 324 |
+
{
|
| 325 |
+
theme_color: 'FFFAAF00',
|
| 326 |
+
theme_id: '672058580051520',
|
| 327 |
+
theme_emoji: null,
|
| 328 |
+
gradient: null,
|
| 329 |
+
should_show_icon: '1',
|
| 330 |
+
theme_name_with_subtitle: 'Honey'
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
theme_color: 'FF0084FF',
|
| 334 |
+
theme_id: '196241301102133',
|
| 335 |
+
theme_emoji: null,
|
| 336 |
+
gradient: null,
|
| 337 |
+
should_show_icon: '1',
|
| 338 |
+
theme_name_with_subtitle: 'Default Blue'
|
| 339 |
+
},
|
| 340 |
+
{
|
| 341 |
+
theme_color: 'FFFFC300',
|
| 342 |
+
theme_id: '174636906462322',
|
| 343 |
+
theme_emoji: null,
|
| 344 |
+
gradient: null,
|
| 345 |
+
should_show_icon: '1',
|
| 346 |
+
theme_name_with_subtitle: 'Yellow'
|
| 347 |
+
},
|
| 348 |
+
{
|
| 349 |
+
theme_color: 'FF44BEC7',
|
| 350 |
+
theme_id: '1928399724138152',
|
| 351 |
+
theme_emoji: null,
|
| 352 |
+
gradient: null,
|
| 353 |
+
should_show_icon: '1',
|
| 354 |
+
theme_name_with_subtitle: 'Teal Blue'
|
| 355 |
+
},
|
| 356 |
+
{
|
| 357 |
+
theme_color: 'FF7646FF',
|
| 358 |
+
theme_id: '234137870477637',
|
| 359 |
+
theme_emoji: null,
|
| 360 |
+
gradient: null,
|
| 361 |
+
should_show_icon: '1',
|
| 362 |
+
theme_name_with_subtitle: 'Bright Purple'
|
| 363 |
+
},
|
| 364 |
+
{
|
| 365 |
+
theme_color: 'FFF25C54',
|
| 366 |
+
theme_id: '3022526817824329',
|
| 367 |
+
theme_emoji: null,
|
| 368 |
+
gradient: null,
|
| 369 |
+
should_show_icon: '1',
|
| 370 |
+
theme_name_with_subtitle: 'Peach'
|
| 371 |
+
},
|
| 372 |
+
{
|
| 373 |
+
theme_color: 'FFF01D6A',
|
| 374 |
+
theme_id: '724096885023603',
|
| 375 |
+
theme_emoji: null,
|
| 376 |
+
gradient: '["FF005FFF","FF9200FF","FFFF2E19"]',
|
| 377 |
+
should_show_icon: '1',
|
| 378 |
+
theme_name_with_subtitle: 'Berry'
|
| 379 |
+
},
|
| 380 |
+
{
|
| 381 |
+
theme_color: 'FFFF7CA8',
|
| 382 |
+
theme_id: '624266884847972',
|
| 383 |
+
theme_emoji: null,
|
| 384 |
+
gradient: '["FFFF8FB2","FFA797FF","FF00E5FF"]',
|
| 385 |
+
should_show_icon: '1',
|
| 386 |
+
theme_name_with_subtitle: 'Candy'
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
theme_color: 'FF6E5B04',
|
| 390 |
+
theme_id: '365557122117011',
|
| 391 |
+
theme_emoji: '💛',
|
| 392 |
+
gradient: '["FFED9F9A","FFED9F9A","FFED9F9A"]',
|
| 393 |
+
should_show_icon: '',
|
| 394 |
+
theme_name_with_subtitle: 'Support'
|
| 395 |
+
},
|
| 396 |
+
{
|
| 397 |
+
theme_color: 'FF0052CD',
|
| 398 |
+
theme_id: '230032715012014',
|
| 399 |
+
theme_emoji: '✌️',
|
| 400 |
+
gradient: '["FF0052CD","FF00A1E6","FF0052CD"]',
|
| 401 |
+
should_show_icon: '',
|
| 402 |
+
theme_name_with_subtitle: 'Tie-Dye'
|
| 403 |
+
},
|
| 404 |
+
{
|
| 405 |
+
theme_color: 'FF601DDD',
|
| 406 |
+
theme_id: '1060619084701625',
|
| 407 |
+
theme_emoji: '☁️',
|
| 408 |
+
gradient: '["FFCA34FF","FF302CFF","FFBA009C"]',
|
| 409 |
+
should_show_icon: '',
|
| 410 |
+
theme_name_with_subtitle: 'Lo-Fi'
|
| 411 |
+
},
|
| 412 |
+
{
|
| 413 |
+
theme_color: 'FF0099FF',
|
| 414 |
+
theme_id: '3273938616164733',
|
| 415 |
+
theme_emoji: null,
|
| 416 |
+
gradient: null,
|
| 417 |
+
should_show_icon: '1',
|
| 418 |
+
theme_name_with_subtitle: 'Classic'
|
| 419 |
+
},
|
| 420 |
+
{
|
| 421 |
+
theme_color: 'FF1ADB5B',
|
| 422 |
+
theme_id: '370940413392601',
|
| 423 |
+
theme_emoji: null,
|
| 424 |
+
gradient: '["FFFFD200","FF6EDF00","FF00DFBB"]',
|
| 425 |
+
should_show_icon: '1',
|
| 426 |
+
theme_name_with_subtitle: 'Citrus'
|
| 427 |
+
},
|
| 428 |
+
{
|
| 429 |
+
theme_color: 'FFD696BB',
|
| 430 |
+
theme_id: '2058653964378557',
|
| 431 |
+
theme_emoji: null,
|
| 432 |
+
gradient: null,
|
| 433 |
+
should_show_icon: '1',
|
| 434 |
+
theme_name_with_subtitle: 'Lavender Purple'
|
| 435 |
+
},
|
| 436 |
+
{
|
| 437 |
+
theme_color: 'FFC03232',
|
| 438 |
+
theme_id: '1059859811490132',
|
| 439 |
+
theme_emoji: '🙃',
|
| 440 |
+
gradient: '["FFDB4040","FFA32424"]',
|
| 441 |
+
should_show_icon: '',
|
| 442 |
+
theme_name_with_subtitle: 'Stranger Things'
|
| 443 |
+
},
|
| 444 |
+
{
|
| 445 |
+
theme_color: 'FFFA3C4C',
|
| 446 |
+
theme_id: '2129984390566328',
|
| 447 |
+
theme_emoji: null,
|
| 448 |
+
gradient: null,
|
| 449 |
+
should_show_icon: '1',
|
| 450 |
+
theme_name_with_subtitle: 'Red'
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
theme_color: 'FF13CF13',
|
| 454 |
+
theme_id: '2136751179887052',
|
| 455 |
+
theme_emoji: null,
|
| 456 |
+
gradient: null,
|
| 457 |
+
should_show_icon: '1',
|
| 458 |
+
theme_name_with_subtitle: 'Green'
|
| 459 |
+
},
|
| 460 |
+
{
|
| 461 |
+
theme_color: 'FFFF7E29',
|
| 462 |
+
theme_id: '175615189761153',
|
| 463 |
+
theme_emoji: null,
|
| 464 |
+
gradient: null,
|
| 465 |
+
should_show_icon: '1',
|
| 466 |
+
theme_name_with_subtitle: 'Orange'
|
| 467 |
+
},
|
| 468 |
+
{
|
| 469 |
+
theme_color: 'FFE68585',
|
| 470 |
+
theme_id: '980963458735625',
|
| 471 |
+
theme_emoji: null,
|
| 472 |
+
gradient: null,
|
| 473 |
+
should_show_icon: '1',
|
| 474 |
+
theme_name_with_subtitle: 'Coral Pink'
|
| 475 |
+
},
|
| 476 |
+
{
|
| 477 |
+
theme_color: 'FF20CEF5',
|
| 478 |
+
theme_id: '2442142322678320',
|
| 479 |
+
theme_emoji: null,
|
| 480 |
+
gradient: null,
|
| 481 |
+
should_show_icon: '1',
|
| 482 |
+
theme_name_with_subtitle: 'Aqua Blue'
|
| 483 |
+
},
|
| 484 |
+
{
|
| 485 |
+
theme_color: 'FF0EDCDE',
|
| 486 |
+
theme_id: '417639218648241',
|
| 487 |
+
theme_emoji: null,
|
| 488 |
+
gradient: '["FF19C9FF","FF00E6D2","FF0EE6B7"]',
|
| 489 |
+
should_show_icon: '1',
|
| 490 |
+
theme_name_with_subtitle: 'Aqua'
|
| 491 |
+
},
|
| 492 |
+
{
|
| 493 |
+
theme_color: 'FFFF9C19',
|
| 494 |
+
theme_id: '930060997172551',
|
| 495 |
+
theme_emoji: null,
|
| 496 |
+
gradient: '["FFFFDC2D","FFFF9616","FFFF4F00"]',
|
| 497 |
+
should_show_icon: '1',
|
| 498 |
+
theme_name_with_subtitle: 'Mango'
|
| 499 |
+
},
|
| 500 |
+
{
|
| 501 |
+
theme_color: 'FFF01D6A',
|
| 502 |
+
theme_id: '164535220883264',
|
| 503 |
+
theme_emoji: null,
|
| 504 |
+
gradient: '["FF005FFF","FF9200FF","FFFF2E19"]',
|
| 505 |
+
should_show_icon: '1',
|
| 506 |
+
theme_name_with_subtitle: 'Berry'
|
| 507 |
+
},
|
| 508 |
+
{
|
| 509 |
+
theme_color: 'FFFF7CA8',
|
| 510 |
+
theme_id: '205488546921017',
|
| 511 |
+
theme_emoji: null,
|
| 512 |
+
gradient: '["FFFF8FB2","FFA797FF","FF00E5FF"]',
|
| 513 |
+
should_show_icon: '1',
|
| 514 |
+
theme_name_with_subtitle: 'Candy'
|
| 515 |
+
},
|
| 516 |
+
{
|
| 517 |
+
theme_color: 'FFFF6F07',
|
| 518 |
+
theme_id: '1833559466821043',
|
| 519 |
+
theme_emoji: '🌎',
|
| 520 |
+
gradient: '["FFFF6F07"]',
|
| 521 |
+
should_show_icon: '',
|
| 522 |
+
theme_name_with_subtitle: 'Earth'
|
| 523 |
+
},
|
| 524 |
+
{
|
| 525 |
+
theme_color: 'FF0B0085',
|
| 526 |
+
theme_id: '339021464972092',
|
| 527 |
+
theme_emoji: '🔈',
|
| 528 |
+
gradient: '["FF2FA9E4","FF648FEB","FF9B73F2"]',
|
| 529 |
+
should_show_icon: '',
|
| 530 |
+
theme_name_with_subtitle: 'Music'
|
| 531 |
+
},
|
| 532 |
+
{
|
| 533 |
+
theme_color: 'FF8A39EF',
|
| 534 |
+
theme_id: '1652456634878319',
|
| 535 |
+
theme_emoji: '🏳️🌈',
|
| 536 |
+
gradient: '["FFFF0018","FFFF0417","FFFF310E","FFFF5D06","FFFF7A01","FFFF8701","FFFFB001","FFD9C507","FF79C718","FF01C92D","FF01BE69","FF01B3AA","FF0BA1DF","FF3F77E6","FF724CEC","FF8A39EF","FF8A39EF"]',
|
| 537 |
+
should_show_icon: '',
|
| 538 |
+
theme_name_with_subtitle: 'Pride'
|
| 539 |
+
},
|
| 540 |
+
{
|
| 541 |
+
theme_color: 'FF004D7C',
|
| 542 |
+
theme_id: '538280997628317',
|
| 543 |
+
theme_emoji: '🌀',
|
| 544 |
+
gradient: '["FF931410","FF931410","FF931410"]',
|
| 545 |
+
should_show_icon: '',
|
| 546 |
+
theme_name_with_subtitle: 'Doctor Strange'
|
| 547 |
+
},
|
| 548 |
+
{
|
| 549 |
+
theme_color: 'FF4F4DFF',
|
| 550 |
+
theme_id: '3190514984517598',
|
| 551 |
+
theme_emoji: '🌤',
|
| 552 |
+
gradient: '["FF0080FF","FF9F1AFF"]',
|
| 553 |
+
should_show_icon: '',
|
| 554 |
+
theme_name_with_subtitle: 'Sky'
|
| 555 |
+
},
|
| 556 |
+
{
|
| 557 |
+
theme_color: 'FFE84B28',
|
| 558 |
+
theme_id: '357833546030778',
|
| 559 |
+
theme_emoji: '🐯',
|
| 560 |
+
gradient: '["FFF69500","FFDA0050"]',
|
| 561 |
+
should_show_icon: '1',
|
| 562 |
+
theme_name_with_subtitle: 'Lunar New Year'
|
| 563 |
+
},
|
| 564 |
+
{
|
| 565 |
+
theme_color: 'FFB24B77',
|
| 566 |
+
theme_id: '627144732056021',
|
| 567 |
+
theme_emoji: '🥳',
|
| 568 |
+
gradient: '["FFF1614E","FF660F84"]',
|
| 569 |
+
should_show_icon: '',
|
| 570 |
+
theme_name_with_subtitle: 'Celebration!'
|
| 571 |
+
},
|
| 572 |
+
{
|
| 573 |
+
theme_color: 'FF66A9FF',
|
| 574 |
+
theme_id: '390127158985345',
|
| 575 |
+
theme_emoji: '🥶',
|
| 576 |
+
gradient: '["FF8CB3FF","FF409FFF"]',
|
| 577 |
+
should_show_icon: '',
|
| 578 |
+
theme_name_with_subtitle: 'Chill'
|
| 579 |
+
},
|
| 580 |
+
{
|
| 581 |
+
theme_color: 'FF5797FC',
|
| 582 |
+
theme_id: '275041734441112',
|
| 583 |
+
theme_emoji: '😌',
|
| 584 |
+
gradient: '["FF4AC9E4","FF5890FF","FF8C91FF"]',
|
| 585 |
+
should_show_icon: '',
|
| 586 |
+
theme_name_with_subtitle: 'Care'
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
theme_color: 'FFFF595C',
|
| 590 |
+
theme_id: '3082966625307060',
|
| 591 |
+
theme_emoji: '💫',
|
| 592 |
+
gradient: '["FFFF239A","FFFF8C21"]',
|
| 593 |
+
should_show_icon: '',
|
| 594 |
+
theme_name_with_subtitle: 'Astrology'
|
| 595 |
+
},
|
| 596 |
+
{
|
| 597 |
+
theme_color: 'FF0171FF',
|
| 598 |
+
theme_id: '184305226956268',
|
| 599 |
+
theme_emoji: '☁️',
|
| 600 |
+
gradient: '["FF0026ee","FF00b2ff"]',
|
| 601 |
+
should_show_icon: '',
|
| 602 |
+
theme_name_with_subtitle: 'J Balvin'
|
| 603 |
+
},
|
| 604 |
+
{
|
| 605 |
+
theme_color: 'FFA033FF',
|
| 606 |
+
theme_id: '621630955405500',
|
| 607 |
+
theme_emoji: '🎉',
|
| 608 |
+
gradient: '["FFFF7061","FFFF5280","FFA033FF","FF0099FF"]',
|
| 609 |
+
should_show_icon: '',
|
| 610 |
+
theme_name_with_subtitle: 'Birthday'
|
| 611 |
+
},
|
| 612 |
+
{
|
| 613 |
+
theme_color: 'FF006528',
|
| 614 |
+
theme_id: '539927563794799',
|
| 615 |
+
theme_emoji: '🍄',
|
| 616 |
+
gradient: '["FF00d52f","FF006528"]',
|
| 617 |
+
should_show_icon: '',
|
| 618 |
+
theme_name_with_subtitle: 'Cottagecore'
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
theme_color: 'FF4D3EC2',
|
| 622 |
+
theme_id: '736591620215564',
|
| 623 |
+
theme_emoji: null,
|
| 624 |
+
gradient: null,
|
| 625 |
+
should_show_icon: '1',
|
| 626 |
+
theme_name_with_subtitle: 'Ocean'
|
| 627 |
+
},
|
| 628 |
+
{
|
| 629 |
+
theme_color: 'FF4e4bf5',
|
| 630 |
+
theme_id: '3259963564026002',
|
| 631 |
+
theme_emoji: null,
|
| 632 |
+
gradient: '["FFAA00FF","FF0080FF"]',
|
| 633 |
+
should_show_icon: '1',
|
| 634 |
+
theme_name_with_subtitle: 'Default'
|
| 635 |
+
},
|
| 636 |
+
{
|
| 637 |
+
theme_color: 'FF3A12FF',
|
| 638 |
+
theme_id: '582065306070020',
|
| 639 |
+
theme_emoji: null,
|
| 640 |
+
gradient: '["FFFAAF00","FFFF2E2E","FF3A12FF"]',
|
| 641 |
+
should_show_icon: '1',
|
| 642 |
+
theme_name_with_subtitle: 'Rocket'
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
theme_color: 'FF3A1D8A',
|
| 646 |
+
theme_id: '273728810607574',
|
| 647 |
+
theme_emoji: null,
|
| 648 |
+
gradient: '["FFFB45DE","FF841DD5","FF3A1D8A"]',
|
| 649 |
+
should_show_icon: '1',
|
| 650 |
+
theme_name_with_subtitle: 'Unicorn'
|
| 651 |
+
},
|
| 652 |
+
{
|
| 653 |
+
theme_color: 'FF9FD52D',
|
| 654 |
+
theme_id: '262191918210707',
|
| 655 |
+
theme_emoji: null,
|
| 656 |
+
gradient: '["FF2A7FE3","FF00BF91","FF9FD52D"]',
|
| 657 |
+
should_show_icon: '1',
|
| 658 |
+
theme_name_with_subtitle: 'Tropical'
|
| 659 |
+
},
|
| 660 |
+
{
|
| 661 |
+
theme_color: 'FFF7B267',
|
| 662 |
+
theme_id: '909695489504566',
|
| 663 |
+
theme_emoji: null,
|
| 664 |
+
gradient: '["FFF25C54","FFF4845F","FFF7B267"]',
|
| 665 |
+
should_show_icon: '1',
|
| 666 |
+
theme_name_with_subtitle: 'Sushi'
|
| 667 |
+
},
|
| 668 |
+
{
|
| 669 |
+
theme_color: 'FF1ADB5B',
|
| 670 |
+
theme_id: '557344741607350',
|
| 671 |
+
theme_emoji: null,
|
| 672 |
+
gradient: '["FFFFD200","FF6EDF00","FF00DFBB"]',
|
| 673 |
+
should_show_icon: '1',
|
| 674 |
+
theme_name_with_subtitle: 'Citrus'
|
| 675 |
+
},
|
| 676 |
+
{
|
| 677 |
+
theme_color: 'FF4D3EC2',
|
| 678 |
+
theme_id: '280333826736184',
|
| 679 |
+
theme_emoji: null,
|
| 680 |
+
gradient: '["FFFF625B","FFC532AD","FF4D3EC2"]',
|
| 681 |
+
should_show_icon: '1',
|
| 682 |
+
theme_name_with_subtitle: 'Lollipop'
|
| 683 |
+
},
|
| 684 |
+
{
|
| 685 |
+
theme_color: 'FFFF311E',
|
| 686 |
+
theme_id: '1257453361255152',
|
| 687 |
+
theme_emoji: null,
|
| 688 |
+
gradient: null,
|
| 689 |
+
should_show_icon: '1',
|
| 690 |
+
theme_name_with_subtitle: 'Rose'
|
| 691 |
+
},
|
| 692 |
+
{
|
| 693 |
+
theme_color: 'FFA797FF',
|
| 694 |
+
theme_id: '571193503540759',
|
| 695 |
+
theme_emoji: null,
|
| 696 |
+
gradient: null,
|
| 697 |
+
should_show_icon: '1',
|
| 698 |
+
theme_name_with_subtitle: 'Lavender'
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
theme_color: 'FF6EDF00',
|
| 702 |
+
theme_id: '3151463484918004',
|
| 703 |
+
theme_emoji: null,
|
| 704 |
+
gradient: null,
|
| 705 |
+
should_show_icon: '1',
|
| 706 |
+
theme_name_with_subtitle: 'Kiwi'
|
| 707 |
+
},
|
| 708 |
+
{
|
| 709 |
+
theme_color: 'FF9D59D2',
|
| 710 |
+
theme_id: '737761000603635',
|
| 711 |
+
theme_emoji: '💛',
|
| 712 |
+
gradient: '["FFFFD600","FFFCB37B","FF9D59D2","FF282828"]',
|
| 713 |
+
should_show_icon: '',
|
| 714 |
+
theme_name_with_subtitle: 'Non-Binary'
|
| 715 |
+
},
|
| 716 |
+
{
|
| 717 |
+
theme_color: 'FF57C39C',
|
| 718 |
+
theme_id: '1288506208402340',
|
| 719 |
+
theme_emoji: '💐',
|
| 720 |
+
gradient: '["FF57C39C","FF57C39C","FF57C39C"]',
|
| 721 |
+
should_show_icon: '',
|
| 722 |
+
theme_name_with_subtitle: 'Mother\'s Day'
|
| 723 |
+
},
|
| 724 |
+
{
|
| 725 |
+
theme_color: 'FFF65900',
|
| 726 |
+
theme_id: '121771470870245',
|
| 727 |
+
theme_emoji: '💥',
|
| 728 |
+
gradient: '["FFFF8328","FFFF7014","FFFF5C00"]',
|
| 729 |
+
should_show_icon: '',
|
| 730 |
+
theme_name_with_subtitle: 'APAHM'
|
| 731 |
+
},
|
| 732 |
+
{
|
| 733 |
+
theme_color: 'FF978E21',
|
| 734 |
+
theme_id: '810978360551741',
|
| 735 |
+
theme_emoji: '🪺',
|
| 736 |
+
gradient: '["FF978E21","FF978E21","FF978E21"]',
|
| 737 |
+
should_show_icon: '',
|
| 738 |
+
theme_name_with_subtitle: 'Parenthood'
|
| 739 |
+
},
|
| 740 |
+
{
|
| 741 |
+
theme_color: 'FFDD8800',
|
| 742 |
+
theme_id: '1438011086532622',
|
| 743 |
+
theme_emoji: '✨',
|
| 744 |
+
gradient: '["FFEDAB00","FFCD6300"]',
|
| 745 |
+
should_show_icon: '',
|
| 746 |
+
theme_name_with_subtitle: 'Star Wars'
|
| 747 |
+
},
|
| 748 |
+
{
|
| 749 |
+
theme_color: 'FF7D09B9',
|
| 750 |
+
theme_id: '101275642962533',
|
| 751 |
+
theme_emoji: '🚀',
|
| 752 |
+
gradient: '["FF5C22D6","FF8F1EB5","FFC31B92"]',
|
| 753 |
+
should_show_icon: '',
|
| 754 |
+
theme_name_with_subtitle: 'Guardians of the Galaxy'
|
| 755 |
+
},
|
| 756 |
+
{
|
| 757 |
+
theme_color: 'FF61C500',
|
| 758 |
+
theme_id: '158263147151440',
|
| 759 |
+
theme_emoji: '🐝',
|
| 760 |
+
gradient: '["FF61C500","FF61C500","FF61C500"]',
|
| 761 |
+
should_show_icon: '',
|
| 762 |
+
theme_name_with_subtitle: 'Bloom'
|
| 763 |
+
},
|
| 764 |
+
{
|
| 765 |
+
theme_color: 'FF826044',
|
| 766 |
+
theme_id: '195296273246380',
|
| 767 |
+
theme_emoji: '🧋',
|
| 768 |
+
gradient: '["FF886546","FFA27F57","FFC6A26E"]',
|
| 769 |
+
should_show_icon: '',
|
| 770 |
+
theme_name_with_subtitle: 'Bubble Tea'
|
| 771 |
+
},
|
| 772 |
+
{
|
| 773 |
+
theme_color: 'FFB02501',
|
| 774 |
+
theme_id: '6026716157422736',
|
| 775 |
+
theme_emoji: '⛹️',
|
| 776 |
+
gradient: '["FFAC2503","FFB02501","FFB42400"]',
|
| 777 |
+
should_show_icon: '',
|
| 778 |
+
theme_name_with_subtitle: 'Basketball'
|
| 779 |
+
},
|
| 780 |
+
{
|
| 781 |
+
theme_color: 'FF574DC1',
|
| 782 |
+
theme_id: '693996545771691',
|
| 783 |
+
theme_emoji: '🖤',
|
| 784 |
+
gradient: '["FF4533FF","FF574AE0","FF6C64BE"]',
|
| 785 |
+
should_show_icon: '',
|
| 786 |
+
theme_name_with_subtitle: 'Elephants&Flowers'
|
| 787 |
+
},
|
| 788 |
+
{
|
| 789 |
+
theme_color: 'FFA8B8DA',
|
| 790 |
+
theme_id: '504518465021637',
|
| 791 |
+
theme_emoji: '🏳️⚧️',
|
| 792 |
+
gradient: '["FF55D0FF","FF7597D7","FFFF9FB3","FFFF9FB3"]',
|
| 793 |
+
should_show_icon: '',
|
| 794 |
+
theme_name_with_subtitle: 'Transgender'
|
| 795 |
+
}
|
| 796 |
+
];
|
includes/login/src/getThreadHistory.js
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
function getExtension(original_extension, filename = "") {
|
| 8 |
+
if (original_extension) {
|
| 9 |
+
return original_extension;
|
| 10 |
+
}
|
| 11 |
+
else {
|
| 12 |
+
const extension = filename.split(".").pop();
|
| 13 |
+
if (extension === filename) {
|
| 14 |
+
return "";
|
| 15 |
+
}
|
| 16 |
+
else {
|
| 17 |
+
return extension;
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
function formatAttachmentsGraphQLResponse(attachment) {
|
| 23 |
+
switch (attachment.__typename) {
|
| 24 |
+
case "MessageImage":
|
| 25 |
+
return {
|
| 26 |
+
type: "photo",
|
| 27 |
+
ID: attachment.legacy_attachment_id,
|
| 28 |
+
filename: attachment.filename,
|
| 29 |
+
original_extension: getExtension(attachment.original_extension, attachment.filename),
|
| 30 |
+
thumbnailUrl: attachment.thumbnail.uri,
|
| 31 |
+
|
| 32 |
+
previewUrl: attachment.preview.uri,
|
| 33 |
+
previewWidth: attachment.preview.width,
|
| 34 |
+
previewHeight: attachment.preview.height,
|
| 35 |
+
|
| 36 |
+
largePreviewUrl: attachment.large_preview.uri,
|
| 37 |
+
largePreviewHeight: attachment.large_preview.height,
|
| 38 |
+
largePreviewWidth: attachment.large_preview.width,
|
| 39 |
+
|
| 40 |
+
// You have to query for the real image. See below.
|
| 41 |
+
url: attachment.large_preview.uri, // @Legacy
|
| 42 |
+
width: attachment.large_preview.width, // @Legacy
|
| 43 |
+
height: attachment.large_preview.height, // @Legacy
|
| 44 |
+
name: attachment.filename, // @Legacy
|
| 45 |
+
|
| 46 |
+
// @Undocumented
|
| 47 |
+
attributionApp: attachment.attribution_app
|
| 48 |
+
? {
|
| 49 |
+
attributionAppID: attachment.attribution_app.id,
|
| 50 |
+
name: attachment.attribution_app.name,
|
| 51 |
+
logo: attachment.attribution_app.square_logo
|
| 52 |
+
}
|
| 53 |
+
: null
|
| 54 |
+
|
| 55 |
+
// @TODO No idea what this is, should we expose it?
|
| 56 |
+
// Ben - July 15th 2017
|
| 57 |
+
// renderAsSticker: attachment.render_as_sticker,
|
| 58 |
+
|
| 59 |
+
// This is _not_ the real URI, this is still just a large preview.
|
| 60 |
+
// To get the URL we'll need to support a POST query to
|
| 61 |
+
//
|
| 62 |
+
// https://www.facebook.com/webgraphql/query/
|
| 63 |
+
//
|
| 64 |
+
// With the following query params:
|
| 65 |
+
//
|
| 66 |
+
// query_id:728987990612546
|
| 67 |
+
// variables:{"id":"100009069356507","photoID":"10213724771692996"}
|
| 68 |
+
// dpr:1
|
| 69 |
+
//
|
| 70 |
+
// No special form though.
|
| 71 |
+
};
|
| 72 |
+
case "MessageAnimatedImage":
|
| 73 |
+
return {
|
| 74 |
+
type: "animated_image",
|
| 75 |
+
ID: attachment.legacy_attachment_id,
|
| 76 |
+
filename: attachment.filename,
|
| 77 |
+
original_extension: getExtension(attachment.original_extension, attachment.filename),
|
| 78 |
+
|
| 79 |
+
previewUrl: attachment.preview_image.uri,
|
| 80 |
+
previewWidth: attachment.preview_image.width,
|
| 81 |
+
previewHeight: attachment.preview_image.height,
|
| 82 |
+
|
| 83 |
+
url: attachment.animated_image.uri,
|
| 84 |
+
width: attachment.animated_image.width,
|
| 85 |
+
height: attachment.animated_image.height,
|
| 86 |
+
|
| 87 |
+
thumbnailUrl: attachment.preview_image.uri, // @Legacy
|
| 88 |
+
name: attachment.filename, // @Legacy
|
| 89 |
+
facebookUrl: attachment.animated_image.uri, // @Legacy
|
| 90 |
+
rawGifImage: attachment.animated_image.uri, // @Legacy
|
| 91 |
+
animatedGifUrl: attachment.animated_image.uri, // @Legacy
|
| 92 |
+
animatedGifPreviewUrl: attachment.preview_image.uri, // @Legacy
|
| 93 |
+
animatedWebpUrl: attachment.animated_image.uri, // @Legacy
|
| 94 |
+
animatedWebpPreviewUrl: attachment.preview_image.uri, // @Legacy
|
| 95 |
+
|
| 96 |
+
// @Undocumented
|
| 97 |
+
attributionApp: attachment.attribution_app
|
| 98 |
+
? {
|
| 99 |
+
attributionAppID: attachment.attribution_app.id,
|
| 100 |
+
name: attachment.attribution_app.name,
|
| 101 |
+
logo: attachment.attribution_app.square_logo
|
| 102 |
+
}
|
| 103 |
+
: null
|
| 104 |
+
};
|
| 105 |
+
case "MessageVideo":
|
| 106 |
+
return {
|
| 107 |
+
type: "video",
|
| 108 |
+
ID: attachment.legacy_attachment_id,
|
| 109 |
+
filename: attachment.filename,
|
| 110 |
+
original_extension: getExtension(attachment.original_extension, attachment.filename),
|
| 111 |
+
duration: attachment.playable_duration_in_ms,
|
| 112 |
+
|
| 113 |
+
thumbnailUrl: attachment.large_image.uri, // @Legacy
|
| 114 |
+
|
| 115 |
+
previewUrl: attachment.large_image.uri,
|
| 116 |
+
previewWidth: attachment.large_image.width,
|
| 117 |
+
previewHeight: attachment.large_image.height,
|
| 118 |
+
|
| 119 |
+
url: attachment.playable_url,
|
| 120 |
+
width: attachment.original_dimensions.x,
|
| 121 |
+
height: attachment.original_dimensions.y,
|
| 122 |
+
|
| 123 |
+
videoType: attachment.video_type.toLowerCase()
|
| 124 |
+
};
|
| 125 |
+
case "MessageFile":
|
| 126 |
+
return {
|
| 127 |
+
type: "file",
|
| 128 |
+
ID: attachment.message_file_fbid,
|
| 129 |
+
filename: attachment.filename,
|
| 130 |
+
original_extension: getExtension(attachment.original_extension, attachment.filename),
|
| 131 |
+
|
| 132 |
+
url: attachment.url,
|
| 133 |
+
isMalicious: attachment.is_malicious,
|
| 134 |
+
contentType: attachment.content_type,
|
| 135 |
+
|
| 136 |
+
name: attachment.filename, // @Legacy
|
| 137 |
+
mimeType: "", // @Legacy
|
| 138 |
+
fileSize: -1 // @Legacy
|
| 139 |
+
};
|
| 140 |
+
case "MessageAudio":
|
| 141 |
+
return {
|
| 142 |
+
type: "audio",
|
| 143 |
+
ID: attachment.url_shimhash, // Not fowardable
|
| 144 |
+
filename: attachment.filename,
|
| 145 |
+
original_extension: getExtension(attachment.original_extension, attachment.filename),
|
| 146 |
+
|
| 147 |
+
duration: attachment.playable_duration_in_ms,
|
| 148 |
+
audioType: attachment.audio_type,
|
| 149 |
+
url: attachment.playable_url,
|
| 150 |
+
|
| 151 |
+
isVoiceMail: attachment.is_voicemail
|
| 152 |
+
};
|
| 153 |
+
default:
|
| 154 |
+
return {
|
| 155 |
+
error: "Don't know about attachment type " + attachment.__typename
|
| 156 |
+
};
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function formatExtensibleAttachment(attachment) {
|
| 161 |
+
if (attachment.story_attachment) {
|
| 162 |
+
return {
|
| 163 |
+
type: "share",
|
| 164 |
+
ID: attachment.legacy_attachment_id,
|
| 165 |
+
url: attachment.story_attachment.url,
|
| 166 |
+
|
| 167 |
+
title: attachment.story_attachment.title_with_entities.text,
|
| 168 |
+
description:
|
| 169 |
+
attachment.story_attachment.description &&
|
| 170 |
+
attachment.story_attachment.description.text,
|
| 171 |
+
source:
|
| 172 |
+
attachment.story_attachment.source == null
|
| 173 |
+
? null
|
| 174 |
+
: attachment.story_attachment.source.text,
|
| 175 |
+
|
| 176 |
+
image:
|
| 177 |
+
attachment.story_attachment.media == null
|
| 178 |
+
? null
|
| 179 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 180 |
+
attachment.story_attachment.media.image == null
|
| 181 |
+
? null
|
| 182 |
+
: (
|
| 183 |
+
attachment.story_attachment.media.animated_image ||
|
| 184 |
+
attachment.story_attachment.media.image
|
| 185 |
+
).uri,
|
| 186 |
+
width:
|
| 187 |
+
attachment.story_attachment.media == null
|
| 188 |
+
? null
|
| 189 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 190 |
+
attachment.story_attachment.media.image == null
|
| 191 |
+
? null
|
| 192 |
+
: (
|
| 193 |
+
attachment.story_attachment.media.animated_image ||
|
| 194 |
+
attachment.story_attachment.media.image
|
| 195 |
+
).width,
|
| 196 |
+
height:
|
| 197 |
+
attachment.story_attachment.media == null
|
| 198 |
+
? null
|
| 199 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 200 |
+
attachment.story_attachment.media.image == null
|
| 201 |
+
? null
|
| 202 |
+
: (
|
| 203 |
+
attachment.story_attachment.media.animated_image ||
|
| 204 |
+
attachment.story_attachment.media.image
|
| 205 |
+
).height,
|
| 206 |
+
playable:
|
| 207 |
+
attachment.story_attachment.media == null
|
| 208 |
+
? null
|
| 209 |
+
: attachment.story_attachment.media.is_playable,
|
| 210 |
+
duration:
|
| 211 |
+
attachment.story_attachment.media == null
|
| 212 |
+
? null
|
| 213 |
+
: attachment.story_attachment.media.playable_duration_in_ms,
|
| 214 |
+
playableUrl:
|
| 215 |
+
attachment.story_attachment.media == null
|
| 216 |
+
? null
|
| 217 |
+
: attachment.story_attachment.media.playable_url,
|
| 218 |
+
|
| 219 |
+
subattachments: attachment.story_attachment.subattachments,
|
| 220 |
+
|
| 221 |
+
// Format example:
|
| 222 |
+
//
|
| 223 |
+
// [{
|
| 224 |
+
// key: "width",
|
| 225 |
+
// value: { text: "1280" }
|
| 226 |
+
// }]
|
| 227 |
+
//
|
| 228 |
+
// That we turn into:
|
| 229 |
+
//
|
| 230 |
+
// {
|
| 231 |
+
// width: "1280"
|
| 232 |
+
// }
|
| 233 |
+
//
|
| 234 |
+
properties: attachment.story_attachment.properties.reduce(function (
|
| 235 |
+
obj,
|
| 236 |
+
cur
|
| 237 |
+
) {
|
| 238 |
+
obj[cur.key] = cur.value.text;
|
| 239 |
+
return obj;
|
| 240 |
+
},
|
| 241 |
+
{}),
|
| 242 |
+
|
| 243 |
+
// Deprecated fields
|
| 244 |
+
animatedImageSize: "", // @Legacy
|
| 245 |
+
facebookUrl: "", // @Legacy
|
| 246 |
+
styleList: "", // @Legacy
|
| 247 |
+
target: "", // @Legacy
|
| 248 |
+
thumbnailUrl:
|
| 249 |
+
attachment.story_attachment.media == null
|
| 250 |
+
? null
|
| 251 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 252 |
+
attachment.story_attachment.media.image == null
|
| 253 |
+
? null
|
| 254 |
+
: (
|
| 255 |
+
attachment.story_attachment.media.animated_image ||
|
| 256 |
+
attachment.story_attachment.media.image
|
| 257 |
+
).uri, // @Legacy
|
| 258 |
+
thumbnailWidth:
|
| 259 |
+
attachment.story_attachment.media == null
|
| 260 |
+
? null
|
| 261 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 262 |
+
attachment.story_attachment.media.image == null
|
| 263 |
+
? null
|
| 264 |
+
: (
|
| 265 |
+
attachment.story_attachment.media.animated_image ||
|
| 266 |
+
attachment.story_attachment.media.image
|
| 267 |
+
).width, // @Legacy
|
| 268 |
+
thumbnailHeight:
|
| 269 |
+
attachment.story_attachment.media == null
|
| 270 |
+
? null
|
| 271 |
+
: attachment.story_attachment.media.animated_image == null &&
|
| 272 |
+
attachment.story_attachment.media.image == null
|
| 273 |
+
? null
|
| 274 |
+
: (
|
| 275 |
+
attachment.story_attachment.media.animated_image ||
|
| 276 |
+
attachment.story_attachment.media.image
|
| 277 |
+
).height // @Legacy
|
| 278 |
+
};
|
| 279 |
+
} else {
|
| 280 |
+
return { error: "Don't know what to do with extensible_attachment." };
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
function formatReactionsGraphQL(reaction) {
|
| 285 |
+
return {
|
| 286 |
+
reaction: reaction.reaction,
|
| 287 |
+
userID: reaction.user.id
|
| 288 |
+
};
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
function formatEventData(event) {
|
| 292 |
+
if (event == null) {
|
| 293 |
+
return {};
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
switch (event.__typename) {
|
| 297 |
+
case "ThemeColorExtensibleMessageAdminText":
|
| 298 |
+
return {
|
| 299 |
+
color: event.theme_color
|
| 300 |
+
};
|
| 301 |
+
case "ThreadNicknameExtensibleMessageAdminText":
|
| 302 |
+
return {
|
| 303 |
+
nickname: event.nickname,
|
| 304 |
+
participantID: event.participant_id
|
| 305 |
+
};
|
| 306 |
+
case "ThreadIconExtensibleMessageAdminText":
|
| 307 |
+
return {
|
| 308 |
+
threadIcon: event.thread_icon
|
| 309 |
+
};
|
| 310 |
+
case "InstantGameUpdateExtensibleMessageAdminText":
|
| 311 |
+
return {
|
| 312 |
+
gameID: (event.game == null ? null : event.game.id),
|
| 313 |
+
update_type: event.update_type,
|
| 314 |
+
collapsed_text: event.collapsed_text,
|
| 315 |
+
expanded_text: event.expanded_text,
|
| 316 |
+
instant_game_update_data: event.instant_game_update_data
|
| 317 |
+
};
|
| 318 |
+
case "GameScoreExtensibleMessageAdminText":
|
| 319 |
+
return {
|
| 320 |
+
game_type: event.game_type
|
| 321 |
+
};
|
| 322 |
+
case "RtcCallLogExtensibleMessageAdminText":
|
| 323 |
+
return {
|
| 324 |
+
event: event.event,
|
| 325 |
+
is_video_call: event.is_video_call,
|
| 326 |
+
server_info_data: event.server_info_data
|
| 327 |
+
};
|
| 328 |
+
case "GroupPollExtensibleMessageAdminText":
|
| 329 |
+
return {
|
| 330 |
+
event_type: event.event_type,
|
| 331 |
+
total_count: event.total_count,
|
| 332 |
+
question: event.question
|
| 333 |
+
};
|
| 334 |
+
case "AcceptPendingThreadExtensibleMessageAdminText":
|
| 335 |
+
return {
|
| 336 |
+
accepter_id: event.accepter_id,
|
| 337 |
+
requester_id: event.requester_id
|
| 338 |
+
};
|
| 339 |
+
case "ConfirmFriendRequestExtensibleMessageAdminText":
|
| 340 |
+
return {
|
| 341 |
+
friend_request_recipient: event.friend_request_recipient,
|
| 342 |
+
friend_request_sender: event.friend_request_sender
|
| 343 |
+
};
|
| 344 |
+
case "AddContactExtensibleMessageAdminText":
|
| 345 |
+
return {
|
| 346 |
+
contact_added_id: event.contact_added_id,
|
| 347 |
+
contact_adder_id: event.contact_adder_id
|
| 348 |
+
};
|
| 349 |
+
case "AdExtensibleMessageAdminText":
|
| 350 |
+
return {
|
| 351 |
+
ad_client_token: event.ad_client_token,
|
| 352 |
+
ad_id: event.ad_id,
|
| 353 |
+
ad_preferences_link: event.ad_preferences_link,
|
| 354 |
+
ad_properties: event.ad_properties
|
| 355 |
+
};
|
| 356 |
+
// never data
|
| 357 |
+
case "ParticipantJoinedGroupCallExtensibleMessageAdminText":
|
| 358 |
+
case "ThreadEphemeralTtlModeExtensibleMessageAdminText":
|
| 359 |
+
case "StartedSharingVideoExtensibleMessageAdminText":
|
| 360 |
+
case "LightweightEventCreateExtensibleMessageAdminText":
|
| 361 |
+
case "LightweightEventNotifyExtensibleMessageAdminText":
|
| 362 |
+
case "LightweightEventNotifyBeforeEventExtensibleMessageAdminText":
|
| 363 |
+
case "LightweightEventUpdateTitleExtensibleMessageAdminText":
|
| 364 |
+
case "LightweightEventUpdateTimeExtensibleMessageAdminText":
|
| 365 |
+
case "LightweightEventUpdateLocationExtensibleMessageAdminText":
|
| 366 |
+
case "LightweightEventDeleteExtensibleMessageAdminText":
|
| 367 |
+
return {};
|
| 368 |
+
default:
|
| 369 |
+
return {
|
| 370 |
+
error: "Don't know what to with event data type " + event.__typename
|
| 371 |
+
};
|
| 372 |
+
}
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
function formatMessagesGraphQLResponse(data) {
|
| 376 |
+
const messageThread = data.o0.data.message_thread;
|
| 377 |
+
const threadID = messageThread.thread_key.thread_fbid
|
| 378 |
+
? messageThread.thread_key.thread_fbid
|
| 379 |
+
: messageThread.thread_key.other_user_id;
|
| 380 |
+
|
| 381 |
+
const messages = messageThread.messages.nodes.map(function (d) {
|
| 382 |
+
switch (d.__typename) {
|
| 383 |
+
case "UserMessage":
|
| 384 |
+
// Give priority to stickers. They're seen as normal messages but we've
|
| 385 |
+
// been considering them as attachments.
|
| 386 |
+
var maybeStickerAttachment;
|
| 387 |
+
if (d.sticker) {
|
| 388 |
+
maybeStickerAttachment = [
|
| 389 |
+
{
|
| 390 |
+
type: "sticker",
|
| 391 |
+
ID: d.sticker.id,
|
| 392 |
+
url: d.sticker.url,
|
| 393 |
+
|
| 394 |
+
packID: d.sticker.pack ? d.sticker.pack.id : null,
|
| 395 |
+
spriteUrl: d.sticker.sprite_image,
|
| 396 |
+
spriteUrl2x: d.sticker.sprite_image_2x,
|
| 397 |
+
width: d.sticker.width,
|
| 398 |
+
height: d.sticker.height,
|
| 399 |
+
|
| 400 |
+
caption: d.snippet, // Not sure what the heck caption was.
|
| 401 |
+
description: d.sticker.label, // Not sure about this one either.
|
| 402 |
+
|
| 403 |
+
frameCount: d.sticker.frame_count,
|
| 404 |
+
frameRate: d.sticker.frame_rate,
|
| 405 |
+
framesPerRow: d.sticker.frames_per_row,
|
| 406 |
+
framesPerCol: d.sticker.frames_per_col,
|
| 407 |
+
|
| 408 |
+
stickerID: d.sticker.id, // @Legacy
|
| 409 |
+
spriteURI: d.sticker.sprite_image, // @Legacy
|
| 410 |
+
spriteURI2x: d.sticker.sprite_image_2x // @Legacy
|
| 411 |
+
}
|
| 412 |
+
];
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
var mentionsObj = {};
|
| 416 |
+
if (d.message !== null) {
|
| 417 |
+
d.message.ranges.forEach(e => {
|
| 418 |
+
mentionsObj[e.entity.id] = d.message.text.substr(e.offset, e.length);
|
| 419 |
+
});
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
return {
|
| 423 |
+
type: "message",
|
| 424 |
+
attachments: maybeStickerAttachment
|
| 425 |
+
? maybeStickerAttachment
|
| 426 |
+
: d.blob_attachments && d.blob_attachments.length > 0
|
| 427 |
+
? d.blob_attachments.map(formatAttachmentsGraphQLResponse)
|
| 428 |
+
: d.extensible_attachment
|
| 429 |
+
? [formatExtensibleAttachment(d.extensible_attachment)]
|
| 430 |
+
: [],
|
| 431 |
+
body: d.message !== null ? d.message.text : '',
|
| 432 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 433 |
+
messageID: d.message_id,
|
| 434 |
+
senderID: d.message_sender.id,
|
| 435 |
+
threadID: threadID,
|
| 436 |
+
timestamp: d.timestamp_precise,
|
| 437 |
+
|
| 438 |
+
mentions: mentionsObj,
|
| 439 |
+
isUnread: d.unread,
|
| 440 |
+
|
| 441 |
+
// New
|
| 442 |
+
messageReactions: d.message_reactions
|
| 443 |
+
? d.message_reactions.map(formatReactionsGraphQL)
|
| 444 |
+
: null,
|
| 445 |
+
isSponsored: d.is_sponsored,
|
| 446 |
+
snippet: d.snippet
|
| 447 |
+
};
|
| 448 |
+
case "ThreadNameMessage":
|
| 449 |
+
return {
|
| 450 |
+
type: "event",
|
| 451 |
+
messageID: d.message_id,
|
| 452 |
+
threadID: threadID,
|
| 453 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 454 |
+
senderID: d.message_sender.id,
|
| 455 |
+
timestamp: d.timestamp_precise,
|
| 456 |
+
eventType: "change_thread_name",
|
| 457 |
+
snippet: d.snippet,
|
| 458 |
+
eventData: {
|
| 459 |
+
threadName: d.thread_name
|
| 460 |
+
},
|
| 461 |
+
|
| 462 |
+
// @Legacy
|
| 463 |
+
author: d.message_sender.id,
|
| 464 |
+
logMessageType: "log:thread-name",
|
| 465 |
+
logMessageData: { name: d.thread_name }
|
| 466 |
+
};
|
| 467 |
+
case "ThreadImageMessage":
|
| 468 |
+
return {
|
| 469 |
+
type: "event",
|
| 470 |
+
messageID: d.message_id,
|
| 471 |
+
threadID: threadID,
|
| 472 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 473 |
+
senderID: d.message_sender.id,
|
| 474 |
+
timestamp: d.timestamp_precise,
|
| 475 |
+
eventType: "change_thread_image",
|
| 476 |
+
snippet: d.snippet,
|
| 477 |
+
eventData:
|
| 478 |
+
d.image_with_metadata == null
|
| 479 |
+
? {} /* removed image */
|
| 480 |
+
: {
|
| 481 |
+
/* image added */
|
| 482 |
+
threadImage: {
|
| 483 |
+
attachmentID: d.image_with_metadata.legacy_attachment_id,
|
| 484 |
+
width: d.image_with_metadata.original_dimensions.x,
|
| 485 |
+
height: d.image_with_metadata.original_dimensions.y,
|
| 486 |
+
url: d.image_with_metadata.preview.uri
|
| 487 |
+
}
|
| 488 |
+
},
|
| 489 |
+
|
| 490 |
+
// @Legacy
|
| 491 |
+
logMessageType: "log:thread-icon",
|
| 492 |
+
logMessageData: {
|
| 493 |
+
thread_icon: d.image_with_metadata
|
| 494 |
+
? d.image_with_metadata.preview.uri
|
| 495 |
+
: null
|
| 496 |
+
}
|
| 497 |
+
};
|
| 498 |
+
case "ParticipantLeftMessage":
|
| 499 |
+
return {
|
| 500 |
+
type: "event",
|
| 501 |
+
messageID: d.message_id,
|
| 502 |
+
threadID: threadID,
|
| 503 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 504 |
+
senderID: d.message_sender.id,
|
| 505 |
+
timestamp: d.timestamp_precise,
|
| 506 |
+
eventType: "remove_participants",
|
| 507 |
+
snippet: d.snippet,
|
| 508 |
+
eventData: {
|
| 509 |
+
// Array of IDs.
|
| 510 |
+
participantsRemoved: d.participants_removed.map(function (p) {
|
| 511 |
+
return p.id;
|
| 512 |
+
})
|
| 513 |
+
},
|
| 514 |
+
|
| 515 |
+
// @Legacy
|
| 516 |
+
logMessageType: "log:unsubscribe",
|
| 517 |
+
logMessageData: {
|
| 518 |
+
leftParticipantFbId: d.participants_removed.map(function (p) {
|
| 519 |
+
return p.id;
|
| 520 |
+
})
|
| 521 |
+
}
|
| 522 |
+
};
|
| 523 |
+
case "ParticipantsAddedMessage":
|
| 524 |
+
return {
|
| 525 |
+
type: "event",
|
| 526 |
+
messageID: d.message_id,
|
| 527 |
+
threadID: threadID,
|
| 528 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 529 |
+
senderID: d.message_sender.id,
|
| 530 |
+
timestamp: d.timestamp_precise,
|
| 531 |
+
eventType: "add_participants",
|
| 532 |
+
snippet: d.snippet,
|
| 533 |
+
eventData: {
|
| 534 |
+
// Array of IDs.
|
| 535 |
+
participantsAdded: d.participants_added.map(function (p) {
|
| 536 |
+
return p.id;
|
| 537 |
+
})
|
| 538 |
+
},
|
| 539 |
+
|
| 540 |
+
// @Legacy
|
| 541 |
+
logMessageType: "log:subscribe",
|
| 542 |
+
logMessageData: {
|
| 543 |
+
addedParticipants: d.participants_added.map(function (p) {
|
| 544 |
+
return p.id;
|
| 545 |
+
})
|
| 546 |
+
}
|
| 547 |
+
};
|
| 548 |
+
case "VideoCallMessage":
|
| 549 |
+
return {
|
| 550 |
+
type: "event",
|
| 551 |
+
messageID: d.message_id,
|
| 552 |
+
threadID: threadID,
|
| 553 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 554 |
+
senderID: d.message_sender.id,
|
| 555 |
+
timestamp: d.timestamp_precise,
|
| 556 |
+
eventType: "video_call",
|
| 557 |
+
snippet: d.snippet,
|
| 558 |
+
|
| 559 |
+
// @Legacy
|
| 560 |
+
logMessageType: "other"
|
| 561 |
+
};
|
| 562 |
+
case "VoiceCallMessage":
|
| 563 |
+
return {
|
| 564 |
+
type: "event",
|
| 565 |
+
messageID: d.message_id,
|
| 566 |
+
threadID: threadID,
|
| 567 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 568 |
+
senderID: d.message_sender.id,
|
| 569 |
+
timestamp: d.timestamp_precise,
|
| 570 |
+
eventType: "voice_call",
|
| 571 |
+
snippet: d.snippet,
|
| 572 |
+
|
| 573 |
+
// @Legacy
|
| 574 |
+
logMessageType: "other"
|
| 575 |
+
};
|
| 576 |
+
case "GenericAdminTextMessage":
|
| 577 |
+
return {
|
| 578 |
+
type: "event",
|
| 579 |
+
messageID: d.message_id,
|
| 580 |
+
threadID: threadID,
|
| 581 |
+
isGroup: messageThread.thread_type === "GROUP",
|
| 582 |
+
senderID: d.message_sender.id,
|
| 583 |
+
timestamp: d.timestamp_precise,
|
| 584 |
+
snippet: d.snippet,
|
| 585 |
+
eventType: d.extensible_message_admin_text_type.toLowerCase(),
|
| 586 |
+
eventData: formatEventData(d.extensible_message_admin_text),
|
| 587 |
+
|
| 588 |
+
// @Legacy
|
| 589 |
+
logMessageType: utils.getAdminTextMessageType(
|
| 590 |
+
d.extensible_message_admin_text_type
|
| 591 |
+
),
|
| 592 |
+
logMessageData: d.extensible_message_admin_text // Maybe different?
|
| 593 |
+
};
|
| 594 |
+
default:
|
| 595 |
+
return { error: "Don't know about message type " + d.__typename };
|
| 596 |
+
}
|
| 597 |
+
});
|
| 598 |
+
return messages;
|
| 599 |
+
}
|
| 600 |
+
|
| 601 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 602 |
+
return function getThreadHistoryGraphQL(
|
| 603 |
+
threadID,
|
| 604 |
+
amount,
|
| 605 |
+
timestamp,
|
| 606 |
+
callback
|
| 607 |
+
) {
|
| 608 |
+
let resolveFunc = function () { };
|
| 609 |
+
let rejectFunc = function () { };
|
| 610 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 611 |
+
resolveFunc = resolve;
|
| 612 |
+
rejectFunc = reject;
|
| 613 |
+
});
|
| 614 |
+
|
| 615 |
+
if (!callback) {
|
| 616 |
+
callback = function (err, data) {
|
| 617 |
+
if (err) {
|
| 618 |
+
return rejectFunc(err);
|
| 619 |
+
}
|
| 620 |
+
resolveFunc(data);
|
| 621 |
+
};
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
// `queries` has to be a string. I couldn't tell from the dev console. This
|
| 625 |
+
// took me a really long time to figure out. I deserve a cookie for this.
|
| 626 |
+
const form = {
|
| 627 |
+
"av": ctx.globalOptions.pageID,
|
| 628 |
+
queries: JSON.stringify({
|
| 629 |
+
o0: {
|
| 630 |
+
// This doc_id was valid on February 2nd 2017.
|
| 631 |
+
doc_id: "1498317363570230",
|
| 632 |
+
query_params: {
|
| 633 |
+
id: threadID,
|
| 634 |
+
message_limit: amount,
|
| 635 |
+
load_messages: 1,
|
| 636 |
+
load_read_receipts: false,
|
| 637 |
+
before: timestamp
|
| 638 |
+
}
|
| 639 |
+
}
|
| 640 |
+
})
|
| 641 |
+
};
|
| 642 |
+
|
| 643 |
+
defaultFuncs
|
| 644 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 645 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 646 |
+
.then(function (resData) {
|
| 647 |
+
if (resData.error) {
|
| 648 |
+
throw resData;
|
| 649 |
+
}
|
| 650 |
+
// This returns us an array of things. The last one is the success /
|
| 651 |
+
// failure one.
|
| 652 |
+
// @TODO What do we do in this case?
|
| 653 |
+
if (resData[resData.length - 1].error_results !== 0) {
|
| 654 |
+
throw new Error("There was an error_result.");
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
callback(null, formatMessagesGraphQLResponse(resData[0]));
|
| 658 |
+
})
|
| 659 |
+
.catch(function (err) {
|
| 660 |
+
log.error("getThreadHistoryGraphQL", err);
|
| 661 |
+
return callback(err);
|
| 662 |
+
});
|
| 663 |
+
|
| 664 |
+
return returnPromise;
|
| 665 |
+
};
|
| 666 |
+
};
|
includes/login/src/getThreadInfo.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
function formatEventReminders(reminder) {
|
| 7 |
+
return {
|
| 8 |
+
reminderID: reminder.id,
|
| 9 |
+
eventCreatorID: reminder.lightweight_event_creator.id,
|
| 10 |
+
time: reminder.time,
|
| 11 |
+
eventType: reminder.lightweight_event_type.toLowerCase(),
|
| 12 |
+
locationName: reminder.location_name,
|
| 13 |
+
// @TODO verify this
|
| 14 |
+
locationCoordinates: reminder.location_coordinates,
|
| 15 |
+
locationPage: reminder.location_page,
|
| 16 |
+
eventStatus: reminder.lightweight_event_status.toLowerCase(),
|
| 17 |
+
note: reminder.note,
|
| 18 |
+
repeatMode: reminder.repeat_mode.toLowerCase(),
|
| 19 |
+
eventTitle: reminder.event_title,
|
| 20 |
+
triggerMessage: reminder.trigger_message,
|
| 21 |
+
secondsToNotifyBefore: reminder.seconds_to_notify_before,
|
| 22 |
+
allowsRsvp: reminder.allows_rsvp,
|
| 23 |
+
relatedEvent: reminder.related_event,
|
| 24 |
+
members: reminder.event_reminder_members.edges.map(function (member) {
|
| 25 |
+
return {
|
| 26 |
+
memberID: member.node.id,
|
| 27 |
+
state: member.guest_list_state.toLowerCase()
|
| 28 |
+
};
|
| 29 |
+
})
|
| 30 |
+
};
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
function formatThreadGraphQLResponse(data) {
|
| 34 |
+
if (data.errors)
|
| 35 |
+
return data.errors;
|
| 36 |
+
const messageThread = data.message_thread;
|
| 37 |
+
if (!messageThread)
|
| 38 |
+
return null;
|
| 39 |
+
const threadID = messageThread.thread_key.thread_fbid
|
| 40 |
+
? messageThread.thread_key.thread_fbid
|
| 41 |
+
: messageThread.thread_key.other_user_id;
|
| 42 |
+
|
| 43 |
+
// Remove me
|
| 44 |
+
const lastM = messageThread.last_message;
|
| 45 |
+
const snippetID =
|
| 46 |
+
lastM &&
|
| 47 |
+
lastM.nodes &&
|
| 48 |
+
lastM.nodes[0] &&
|
| 49 |
+
lastM.nodes[0].message_sender &&
|
| 50 |
+
lastM.nodes[0].message_sender.messaging_actor
|
| 51 |
+
? lastM.nodes[0].message_sender.messaging_actor.id
|
| 52 |
+
: null;
|
| 53 |
+
const snippetText =
|
| 54 |
+
lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
|
| 55 |
+
const lastR = messageThread.last_read_receipt;
|
| 56 |
+
const lastReadTimestamp =
|
| 57 |
+
lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
|
| 58 |
+
? lastR.nodes[0].timestamp_precise
|
| 59 |
+
: null;
|
| 60 |
+
|
| 61 |
+
return {
|
| 62 |
+
threadID: threadID,
|
| 63 |
+
threadName: messageThread.name,
|
| 64 |
+
participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
|
| 65 |
+
userInfo: messageThread.all_participants.edges.map(d => ({
|
| 66 |
+
id: d.node.messaging_actor.id,
|
| 67 |
+
name: d.node.messaging_actor.name,
|
| 68 |
+
firstName: d.node.messaging_actor.short_name,
|
| 69 |
+
vanity: d.node.messaging_actor.username,
|
| 70 |
+
url: d.node.messaging_actor.url,
|
| 71 |
+
thumbSrc: d.node.messaging_actor.big_image_src.uri,
|
| 72 |
+
profileUrl: d.node.messaging_actor.big_image_src.uri,
|
| 73 |
+
gender: d.node.messaging_actor.gender,
|
| 74 |
+
type: d.node.messaging_actor.__typename,
|
| 75 |
+
isFriend: d.node.messaging_actor.is_viewer_friend,
|
| 76 |
+
isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
|
| 77 |
+
})),
|
| 78 |
+
unreadCount: messageThread.unread_count,
|
| 79 |
+
messageCount: messageThread.messages_count,
|
| 80 |
+
timestamp: messageThread.updated_time_precise,
|
| 81 |
+
muteUntil: messageThread.mute_until,
|
| 82 |
+
isGroup: messageThread.thread_type == "GROUP",
|
| 83 |
+
isSubscribed: messageThread.is_viewer_subscribed,
|
| 84 |
+
isArchived: messageThread.has_viewer_archived,
|
| 85 |
+
folder: messageThread.folder,
|
| 86 |
+
cannotReplyReason: messageThread.cannot_reply_reason,
|
| 87 |
+
eventReminders: messageThread.event_reminders
|
| 88 |
+
? messageThread.event_reminders.nodes.map(formatEventReminders)
|
| 89 |
+
: null,
|
| 90 |
+
emoji: messageThread.customization_info
|
| 91 |
+
? messageThread.customization_info.emoji
|
| 92 |
+
: null,
|
| 93 |
+
color:
|
| 94 |
+
messageThread.customization_info &&
|
| 95 |
+
messageThread.customization_info.outgoing_bubble_color
|
| 96 |
+
? messageThread.customization_info.outgoing_bubble_color.slice(2)
|
| 97 |
+
: null,
|
| 98 |
+
threadTheme: messageThread.thread_theme,
|
| 99 |
+
nicknames:
|
| 100 |
+
messageThread.customization_info &&
|
| 101 |
+
messageThread.customization_info.participant_customizations
|
| 102 |
+
? messageThread.customization_info.participant_customizations.reduce(
|
| 103 |
+
function (res, val) {
|
| 104 |
+
if (val.nickname) res[val.participant_id] = val.nickname;
|
| 105 |
+
return res;
|
| 106 |
+
},
|
| 107 |
+
{}
|
| 108 |
+
)
|
| 109 |
+
: {},
|
| 110 |
+
adminIDs: messageThread.thread_admins,
|
| 111 |
+
approvalMode: Boolean(messageThread.approval_mode),
|
| 112 |
+
approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
|
| 113 |
+
inviterID: a.inviter.id,
|
| 114 |
+
requesterID: a.requester.id,
|
| 115 |
+
timestamp: a.request_timestamp,
|
| 116 |
+
request_source: a.request_source // @Undocumented
|
| 117 |
+
})),
|
| 118 |
+
|
| 119 |
+
// @Undocumented
|
| 120 |
+
reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
|
| 121 |
+
mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
|
| 122 |
+
isPinProtected: messageThread.is_pin_protected,
|
| 123 |
+
relatedPageThread: messageThread.related_page_thread,
|
| 124 |
+
|
| 125 |
+
// @Legacy
|
| 126 |
+
name: messageThread.name,
|
| 127 |
+
snippet: snippetText,
|
| 128 |
+
snippetSender: snippetID,
|
| 129 |
+
snippetAttachments: [],
|
| 130 |
+
serverTimestamp: messageThread.updated_time_precise,
|
| 131 |
+
imageSrc: messageThread.image ? messageThread.image.uri : null,
|
| 132 |
+
isCanonicalUser: messageThread.is_canonical_neo_user,
|
| 133 |
+
isCanonical: messageThread.thread_type != "GROUP",
|
| 134 |
+
recipientsLoadable: true,
|
| 135 |
+
hasEmailParticipant: false,
|
| 136 |
+
readOnly: false,
|
| 137 |
+
canReply: messageThread.cannot_reply_reason == null,
|
| 138 |
+
lastMessageTimestamp: messageThread.last_message
|
| 139 |
+
? messageThread.last_message.timestamp_precise
|
| 140 |
+
: null,
|
| 141 |
+
lastMessageType: "message",
|
| 142 |
+
lastReadTimestamp: lastReadTimestamp,
|
| 143 |
+
threadType: messageThread.thread_type == "GROUP" ? 2 : 1,
|
| 144 |
+
|
| 145 |
+
// update in Wed, 13 Jul 2022 19:41:12 +0700
|
| 146 |
+
inviteLink: {
|
| 147 |
+
enable: messageThread.joinable_mode ? messageThread.joinable_mode.mode == 1 : false,
|
| 148 |
+
link: messageThread.joinable_mode ? messageThread.joinable_mode.link : null
|
| 149 |
+
}
|
| 150 |
+
};
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 154 |
+
return function getThreadInfoGraphQL(threadID, callback) {
|
| 155 |
+
let resolveFunc = function () { };
|
| 156 |
+
let rejectFunc = function () { };
|
| 157 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 158 |
+
resolveFunc = resolve;
|
| 159 |
+
rejectFunc = reject;
|
| 160 |
+
});
|
| 161 |
+
|
| 162 |
+
if (utils.getType(callback) != "Function" && utils.getType(callback) != "AsyncFunction") {
|
| 163 |
+
callback = function (err, data) {
|
| 164 |
+
if (err) {
|
| 165 |
+
return rejectFunc(err);
|
| 166 |
+
}
|
| 167 |
+
resolveFunc(data);
|
| 168 |
+
};
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
if (utils.getType(threadID) !== "Array") {
|
| 172 |
+
threadID = [threadID];
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
let form = {};
|
| 176 |
+
// `queries` has to be a string. I couldn't tell from the dev console. This
|
| 177 |
+
// took me a really long time to figure out. I deserve a cookie for this.
|
| 178 |
+
threadID.map(function (t, i) {
|
| 179 |
+
form["o" + i] = {
|
| 180 |
+
doc_id: "3449967031715030",
|
| 181 |
+
query_params: {
|
| 182 |
+
id: t,
|
| 183 |
+
message_limit: 0,
|
| 184 |
+
load_messages: false,
|
| 185 |
+
load_read_receipts: false,
|
| 186 |
+
before: null
|
| 187 |
+
}
|
| 188 |
+
};
|
| 189 |
+
});
|
| 190 |
+
|
| 191 |
+
form = {
|
| 192 |
+
queries: JSON.stringify(form),
|
| 193 |
+
batch_name: "MessengerGraphQLThreadFetcher"
|
| 194 |
+
};
|
| 195 |
+
|
| 196 |
+
defaultFuncs
|
| 197 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 198 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 199 |
+
.then(function (resData) {
|
| 200 |
+
|
| 201 |
+
if (resData.error) {
|
| 202 |
+
throw resData;
|
| 203 |
+
}
|
| 204 |
+
// This returns us an array of things. The last one is the success /
|
| 205 |
+
// failure one.
|
| 206 |
+
// @TODO What do we do in this case?
|
| 207 |
+
// if (resData[resData.length - 1].error_results !== 0) {
|
| 208 |
+
// throw resData[0].o0.errors[0];
|
| 209 |
+
// }
|
| 210 |
+
// if (!resData[0].o0.data.message_thread) {
|
| 211 |
+
// throw new Error("can't find this thread");
|
| 212 |
+
// }
|
| 213 |
+
const threadInfos = {};
|
| 214 |
+
for (let i = resData.length - 2; i >= 0; i--) {
|
| 215 |
+
const threadInfo = formatThreadGraphQLResponse(resData[i][Object.keys(resData[i])[0]].data);
|
| 216 |
+
threadInfos[threadInfo?.threadID || threadID[threadID.length - 1 - i]] = threadInfo;
|
| 217 |
+
}
|
| 218 |
+
if (Object.values(threadInfos).length == 1) {
|
| 219 |
+
callback(null, Object.values(threadInfos)[0]);
|
| 220 |
+
}
|
| 221 |
+
else {
|
| 222 |
+
callback(null, threadInfos);
|
| 223 |
+
}
|
| 224 |
+
})
|
| 225 |
+
.catch(function (err) {
|
| 226 |
+
log.error("getThreadInfoGraphQL", err);
|
| 227 |
+
return callback(err);
|
| 228 |
+
});
|
| 229 |
+
|
| 230 |
+
return returnPromise;
|
| 231 |
+
};
|
| 232 |
+
};
|
includes/login/src/getThreadList.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
function formatEventReminders(reminder) {
|
| 7 |
+
return {
|
| 8 |
+
reminderID: reminder.id,
|
| 9 |
+
eventCreatorID: reminder.lightweight_event_creator.id,
|
| 10 |
+
time: reminder.time,
|
| 11 |
+
eventType: reminder.lightweight_event_type.toLowerCase(),
|
| 12 |
+
locationName: reminder.location_name,
|
| 13 |
+
// @TODO verify this
|
| 14 |
+
locationCoordinates: reminder.location_coordinates,
|
| 15 |
+
locationPage: reminder.location_page,
|
| 16 |
+
eventStatus: reminder.lightweight_event_status.toLowerCase(),
|
| 17 |
+
note: reminder.note,
|
| 18 |
+
repeatMode: reminder.repeat_mode.toLowerCase(),
|
| 19 |
+
eventTitle: reminder.event_title,
|
| 20 |
+
triggerMessage: reminder.trigger_message,
|
| 21 |
+
secondsToNotifyBefore: reminder.seconds_to_notify_before,
|
| 22 |
+
allowsRsvp: reminder.allows_rsvp,
|
| 23 |
+
relatedEvent: reminder.related_event,
|
| 24 |
+
members: reminder.event_reminder_members.edges.map(function (member) {
|
| 25 |
+
return {
|
| 26 |
+
memberID: member.node.id,
|
| 27 |
+
state: member.guest_list_state.toLowerCase()
|
| 28 |
+
};
|
| 29 |
+
})
|
| 30 |
+
};
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
function formatThreadGraphQLResponse(messageThread) {
|
| 34 |
+
const threadID = messageThread.thread_key.thread_fbid
|
| 35 |
+
? messageThread.thread_key.thread_fbid
|
| 36 |
+
: messageThread.thread_key.other_user_id;
|
| 37 |
+
|
| 38 |
+
// Remove me
|
| 39 |
+
const lastM = messageThread.last_message;
|
| 40 |
+
const snippetID =
|
| 41 |
+
lastM &&
|
| 42 |
+
lastM.nodes &&
|
| 43 |
+
lastM.nodes[0] &&
|
| 44 |
+
lastM.nodes[0].message_sender &&
|
| 45 |
+
lastM.nodes[0].message_sender.messaging_actor
|
| 46 |
+
? lastM.nodes[0].message_sender.messaging_actor.id
|
| 47 |
+
: null;
|
| 48 |
+
const snippetText =
|
| 49 |
+
lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
|
| 50 |
+
const lastR = messageThread.last_read_receipt;
|
| 51 |
+
const lastReadTimestamp =
|
| 52 |
+
lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
|
| 53 |
+
? lastR.nodes[0].timestamp_precise
|
| 54 |
+
: null;
|
| 55 |
+
|
| 56 |
+
return {
|
| 57 |
+
threadID: threadID,
|
| 58 |
+
threadName: messageThread.name,
|
| 59 |
+
participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
|
| 60 |
+
userInfo: messageThread.all_participants.edges.map(d => ({
|
| 61 |
+
id: d.node.messaging_actor.id,
|
| 62 |
+
name: d.node.messaging_actor.name,
|
| 63 |
+
firstName: d.node.messaging_actor.short_name,
|
| 64 |
+
vanity: d.node.messaging_actor.username,
|
| 65 |
+
url: d.node.messaging_actor.url,
|
| 66 |
+
thumbSrc: d.node.messaging_actor.big_image_src.uri,
|
| 67 |
+
profileUrl: d.node.messaging_actor.big_image_src.uri,
|
| 68 |
+
gender: d.node.messaging_actor.gender,
|
| 69 |
+
type: d.node.messaging_actor.__typename,
|
| 70 |
+
isFriend: d.node.messaging_actor.is_viewer_friend,
|
| 71 |
+
isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
|
| 72 |
+
})),
|
| 73 |
+
unreadCount: messageThread.unread_count,
|
| 74 |
+
messageCount: messageThread.messages_count,
|
| 75 |
+
timestamp: messageThread.updated_time_precise,
|
| 76 |
+
muteUntil: messageThread.mute_until,
|
| 77 |
+
isGroup: messageThread.thread_type == "GROUP",
|
| 78 |
+
isSubscribed: messageThread.is_viewer_subscribed,
|
| 79 |
+
isArchived: messageThread.has_viewer_archived,
|
| 80 |
+
folder: messageThread.folder,
|
| 81 |
+
cannotReplyReason: messageThread.cannot_reply_reason,
|
| 82 |
+
eventReminders: messageThread.event_reminders
|
| 83 |
+
? messageThread.event_reminders.nodes.map(formatEventReminders)
|
| 84 |
+
: null,
|
| 85 |
+
emoji: messageThread.customization_info
|
| 86 |
+
? messageThread.customization_info.emoji
|
| 87 |
+
: null,
|
| 88 |
+
color:
|
| 89 |
+
messageThread.customization_info &&
|
| 90 |
+
messageThread.customization_info.outgoing_bubble_color
|
| 91 |
+
? messageThread.customization_info.outgoing_bubble_color.slice(2)
|
| 92 |
+
: null,
|
| 93 |
+
threadTheme: messageThread.thread_theme,
|
| 94 |
+
nicknames:
|
| 95 |
+
messageThread.customization_info &&
|
| 96 |
+
messageThread.customization_info.participant_customizations
|
| 97 |
+
? messageThread.customization_info.participant_customizations.reduce(
|
| 98 |
+
function (res, val) {
|
| 99 |
+
if (val.nickname) res[val.participant_id] = val.nickname;
|
| 100 |
+
return res;
|
| 101 |
+
},
|
| 102 |
+
{}
|
| 103 |
+
)
|
| 104 |
+
: {},
|
| 105 |
+
adminIDs: messageThread.thread_admins,
|
| 106 |
+
approvalMode: Boolean(messageThread.approval_mode),
|
| 107 |
+
approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
|
| 108 |
+
inviterID: a.inviter.id,
|
| 109 |
+
requesterID: a.requester.id,
|
| 110 |
+
timestamp: a.request_timestamp,
|
| 111 |
+
request_source: a.request_source // @Undocumented
|
| 112 |
+
})),
|
| 113 |
+
|
| 114 |
+
// @Undocumented
|
| 115 |
+
reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
|
| 116 |
+
mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
|
| 117 |
+
isPinProtected: messageThread.is_pin_protected,
|
| 118 |
+
relatedPageThread: messageThread.related_page_thread,
|
| 119 |
+
|
| 120 |
+
// @Legacy
|
| 121 |
+
name: messageThread.name,
|
| 122 |
+
snippet: snippetText,
|
| 123 |
+
snippetSender: snippetID,
|
| 124 |
+
snippetAttachments: [],
|
| 125 |
+
serverTimestamp: messageThread.updated_time_precise,
|
| 126 |
+
imageSrc: messageThread.image ? messageThread.image.uri : null,
|
| 127 |
+
isCanonicalUser: messageThread.is_canonical_neo_user,
|
| 128 |
+
isCanonical: messageThread.thread_type != "GROUP",
|
| 129 |
+
recipientsLoadable: true,
|
| 130 |
+
hasEmailParticipant: false,
|
| 131 |
+
readOnly: false,
|
| 132 |
+
canReply: messageThread.cannot_reply_reason == null,
|
| 133 |
+
lastMessageTimestamp: messageThread.last_message
|
| 134 |
+
? messageThread.last_message.timestamp_precise
|
| 135 |
+
: null,
|
| 136 |
+
lastMessageType: "message",
|
| 137 |
+
lastReadTimestamp: lastReadTimestamp,
|
| 138 |
+
threadType: messageThread.thread_type == "GROUP" ? 2 : 1,
|
| 139 |
+
|
| 140 |
+
// update in Wed, 13 Jul 2022 19:41:12 +0700
|
| 141 |
+
inviteLink: {
|
| 142 |
+
enable: messageThread.joinable_mode ? messageThread.joinable_mode.mode == 1 : false,
|
| 143 |
+
link: messageThread.joinable_mode ? messageThread.joinable_mode.link : null
|
| 144 |
+
}
|
| 145 |
+
};
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
function formatThreadList(data) {
|
| 149 |
+
// console.log(JSON.stringify(data.find(t => t.thread_key.thread_fbid === "5095817367161431"), null, 2));
|
| 150 |
+
return data.map(t => formatThreadGraphQLResponse(t));
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 154 |
+
return function getThreadList(limit, timestamp, tags, callback) {
|
| 155 |
+
if (!callback && (utils.getType(tags) === "Function" || utils.getType(tags) === "AsyncFunction")) {
|
| 156 |
+
callback = tags;
|
| 157 |
+
tags = [""];
|
| 158 |
+
}
|
| 159 |
+
if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) {
|
| 160 |
+
throw new utils.CustomError({ error: "getThreadList: limit must be a positive integer" });
|
| 161 |
+
}
|
| 162 |
+
if (utils.getType(timestamp) !== "Null" &&
|
| 163 |
+
(utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) {
|
| 164 |
+
throw new utils.CustomError({ error: "getThreadList: timestamp must be an integer or null" });
|
| 165 |
+
}
|
| 166 |
+
if (utils.getType(tags) === "String") {
|
| 167 |
+
tags = [tags];
|
| 168 |
+
}
|
| 169 |
+
if (utils.getType(tags) !== "Array") {
|
| 170 |
+
throw new utils.CustomError({
|
| 171 |
+
error: "getThreadList: tags must be an array",
|
| 172 |
+
message: "getThreadList: tags must be an array"
|
| 173 |
+
});
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
let resolveFunc = function () { };
|
| 177 |
+
let rejectFunc = function () { };
|
| 178 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 179 |
+
resolveFunc = resolve;
|
| 180 |
+
rejectFunc = reject;
|
| 181 |
+
});
|
| 182 |
+
|
| 183 |
+
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
|
| 184 |
+
callback = function (err, data) {
|
| 185 |
+
if (err) {
|
| 186 |
+
return rejectFunc(err);
|
| 187 |
+
}
|
| 188 |
+
resolveFunc(data);
|
| 189 |
+
};
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
const form = {
|
| 193 |
+
"av": ctx.i_userID || ctx.userID,
|
| 194 |
+
"queries": JSON.stringify({
|
| 195 |
+
"o0": {
|
| 196 |
+
// This doc_id was valid on 2020-07-20
|
| 197 |
+
// "doc_id": "3336396659757871",
|
| 198 |
+
"doc_id": "3426149104143726",
|
| 199 |
+
"query_params": {
|
| 200 |
+
"limit": limit + (timestamp ? 1 : 0),
|
| 201 |
+
"before": timestamp,
|
| 202 |
+
"tags": tags,
|
| 203 |
+
"includeDeliveryReceipts": true,
|
| 204 |
+
"includeSeqID": false
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
}),
|
| 208 |
+
"batch_name": "MessengerGraphQLThreadlistFetcher"
|
| 209 |
+
};
|
| 210 |
+
|
| 211 |
+
defaultFuncs
|
| 212 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 213 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 214 |
+
.then((resData) => {
|
| 215 |
+
if (resData[resData.length - 1].error_results > 0) {
|
| 216 |
+
throw new utils.CustomError(resData[0].o0.errors);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
if (resData[resData.length - 1].successful_results === 0) {
|
| 220 |
+
throw new utils.CustomError({ error: "getThreadList: there was no successful_results", res: resData });
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
// When we ask for threads using timestamp from the previous request,
|
| 224 |
+
// we are getting the last thread repeated as the first thread in this response.
|
| 225 |
+
// .shift() gets rid of it
|
| 226 |
+
// It is also the reason for increasing limit by 1 when timestamp is set
|
| 227 |
+
// this way user asks for 10 threads, we are asking for 11,
|
| 228 |
+
// but after removing the duplicated one, it is again 10
|
| 229 |
+
if (timestamp) {
|
| 230 |
+
resData[0].o0.data.viewer.message_threads.nodes.shift();
|
| 231 |
+
}
|
| 232 |
+
callback(null, formatThreadList(resData[0].o0.data.viewer.message_threads.nodes));
|
| 233 |
+
})
|
| 234 |
+
.catch((err) => {
|
| 235 |
+
log.error("getThreadList", err);
|
| 236 |
+
return callback(err);
|
| 237 |
+
});
|
| 238 |
+
|
| 239 |
+
return returnPromise;
|
| 240 |
+
};
|
| 241 |
+
};
|
includes/login/src/getThreadPictures.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function getThreadPictures(threadID, offset, limit, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
let form = {
|
| 25 |
+
thread_id: threadID,
|
| 26 |
+
offset: offset,
|
| 27 |
+
limit: limit
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
defaultFuncs
|
| 31 |
+
.post(
|
| 32 |
+
"https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
|
| 33 |
+
ctx.jar,
|
| 34 |
+
form
|
| 35 |
+
)
|
| 36 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 37 |
+
.then(function (resData) {
|
| 38 |
+
if (resData.error) {
|
| 39 |
+
throw resData;
|
| 40 |
+
}
|
| 41 |
+
return Promise.all(
|
| 42 |
+
resData.payload.imagesData.map(function (image) {
|
| 43 |
+
form = {
|
| 44 |
+
thread_id: threadID,
|
| 45 |
+
image_id: image.fbid
|
| 46 |
+
};
|
| 47 |
+
return defaultFuncs
|
| 48 |
+
.post(
|
| 49 |
+
"https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
|
| 50 |
+
ctx.jar,
|
| 51 |
+
form
|
| 52 |
+
)
|
| 53 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 54 |
+
.then(function (resData) {
|
| 55 |
+
if (resData.error) {
|
| 56 |
+
throw resData;
|
| 57 |
+
}
|
| 58 |
+
// the response is pretty messy
|
| 59 |
+
const queryThreadID =
|
| 60 |
+
resData.jsmods.require[0][3][1].query_metadata.query_path[0]
|
| 61 |
+
.message_thread;
|
| 62 |
+
const imageData =
|
| 63 |
+
resData.jsmods.require[0][3][1].query_results[queryThreadID]
|
| 64 |
+
.message_images.edges[0].node.image2;
|
| 65 |
+
return imageData;
|
| 66 |
+
});
|
| 67 |
+
})
|
| 68 |
+
);
|
| 69 |
+
})
|
| 70 |
+
.then(function (resData) {
|
| 71 |
+
callback(null, resData);
|
| 72 |
+
})
|
| 73 |
+
.catch(function (err) {
|
| 74 |
+
log.error("Error in getThreadPictures", err);
|
| 75 |
+
callback(err);
|
| 76 |
+
});
|
| 77 |
+
return returnPromise;
|
| 78 |
+
};
|
| 79 |
+
};
|
includes/login/src/getUserID.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
function formatData(data) {
|
| 7 |
+
return {
|
| 8 |
+
userID: utils.formatID(data.uid.toString()),
|
| 9 |
+
photoUrl: data.photo,
|
| 10 |
+
indexRank: data.index_rank,
|
| 11 |
+
name: data.text,
|
| 12 |
+
isVerified: data.is_verified,
|
| 13 |
+
profileUrl: data.path,
|
| 14 |
+
category: data.category,
|
| 15 |
+
score: data.score,
|
| 16 |
+
type: data.type
|
| 17 |
+
};
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 21 |
+
return function getUserID(name, callback) {
|
| 22 |
+
let resolveFunc = function () { };
|
| 23 |
+
let rejectFunc = function () { };
|
| 24 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 25 |
+
resolveFunc = resolve;
|
| 26 |
+
rejectFunc = reject;
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
if (!callback) {
|
| 30 |
+
callback = function (err, friendList) {
|
| 31 |
+
if (err) {
|
| 32 |
+
return rejectFunc(err);
|
| 33 |
+
}
|
| 34 |
+
resolveFunc(friendList);
|
| 35 |
+
};
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
const form = {
|
| 39 |
+
value: name.toLowerCase(),
|
| 40 |
+
viewer: ctx.i_userID || ctx.userID,
|
| 41 |
+
rsp: "search",
|
| 42 |
+
context: "search",
|
| 43 |
+
path: "/home.php",
|
| 44 |
+
request_id: utils.getGUID()
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
defaultFuncs
|
| 48 |
+
.get("https://www.facebook.com/ajax/typeahead/search.php", ctx.jar, form)
|
| 49 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 50 |
+
.then(function (resData) {
|
| 51 |
+
if (resData.error) {
|
| 52 |
+
throw resData;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const data = resData.payload.entries;
|
| 56 |
+
|
| 57 |
+
callback(null, data.map(formatData));
|
| 58 |
+
})
|
| 59 |
+
.catch(function (err) {
|
| 60 |
+
log.error("getUserID", err);
|
| 61 |
+
return callback(err);
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
return returnPromise;
|
| 65 |
+
};
|
| 66 |
+
};
|
includes/login/src/getUserInfo.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
function formatData(data) {
|
| 7 |
+
const retObj = {};
|
| 8 |
+
|
| 9 |
+
for (const prop in data) {
|
| 10 |
+
// eslint-disable-next-line no-prototype-builtins
|
| 11 |
+
if (data.hasOwnProperty(prop)) {
|
| 12 |
+
const innerObj = data[prop];
|
| 13 |
+
retObj[prop] = {
|
| 14 |
+
name: innerObj.name,
|
| 15 |
+
firstName: innerObj.firstName,
|
| 16 |
+
vanity: innerObj.vanity,
|
| 17 |
+
thumbSrc: innerObj.thumbSrc,
|
| 18 |
+
profileUrl: innerObj.uri,
|
| 19 |
+
gender: innerObj.gender,
|
| 20 |
+
type: innerObj.type,
|
| 21 |
+
isFriend: innerObj.is_friend,
|
| 22 |
+
isBirthday: !!innerObj.is_birthday,
|
| 23 |
+
searchTokens: innerObj.searchTokens,
|
| 24 |
+
alternateName: innerObj.alternateName
|
| 25 |
+
};
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
return retObj;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 33 |
+
return function getUserInfo(id, callback) {
|
| 34 |
+
let resolveFunc = function () { };
|
| 35 |
+
let rejectFunc = function () { };
|
| 36 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 37 |
+
resolveFunc = resolve;
|
| 38 |
+
rejectFunc = reject;
|
| 39 |
+
});
|
| 40 |
+
|
| 41 |
+
if (!callback) {
|
| 42 |
+
callback = function (err, friendList) {
|
| 43 |
+
if (err) {
|
| 44 |
+
return rejectFunc(err);
|
| 45 |
+
}
|
| 46 |
+
resolveFunc(friendList);
|
| 47 |
+
};
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
if (utils.getType(id) !== "Array") {
|
| 51 |
+
id = [id];
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
const form = {};
|
| 55 |
+
id.map(function (v, i) {
|
| 56 |
+
form["ids[" + i + "]"] = v;
|
| 57 |
+
});
|
| 58 |
+
defaultFuncs
|
| 59 |
+
.post("https://www.facebook.com/chat/user_info/", ctx.jar, form)
|
| 60 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 61 |
+
.then(function (resData) {
|
| 62 |
+
if (resData.error) {
|
| 63 |
+
throw resData;
|
| 64 |
+
}
|
| 65 |
+
return callback(null, formatData(resData.payload.profiles));
|
| 66 |
+
})
|
| 67 |
+
.catch(function (err) {
|
| 68 |
+
log.error("getUserInfo", err);
|
| 69 |
+
return callback(err);
|
| 70 |
+
});
|
| 71 |
+
|
| 72 |
+
return returnPromise;
|
| 73 |
+
};
|
| 74 |
+
};
|
includes/login/src/handleFriendRequest.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function handleFriendRequest(userID, accept, callback) {
|
| 8 |
+
if (utils.getType(accept) !== "Boolean") {
|
| 9 |
+
throw {
|
| 10 |
+
error: "Please pass a boolean as a second argument."
|
| 11 |
+
};
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
let resolveFunc = function () { };
|
| 15 |
+
let rejectFunc = function () { };
|
| 16 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 17 |
+
resolveFunc = resolve;
|
| 18 |
+
rejectFunc = reject;
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
if (!callback) {
|
| 22 |
+
callback = function (err, friendList) {
|
| 23 |
+
if (err) {
|
| 24 |
+
return rejectFunc(err);
|
| 25 |
+
}
|
| 26 |
+
resolveFunc(friendList);
|
| 27 |
+
};
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
const form = {
|
| 31 |
+
viewer_id: ctx.i_userID || ctx.userID,
|
| 32 |
+
"frefs[0]": "jwl",
|
| 33 |
+
floc: "friend_center_requests",
|
| 34 |
+
ref: "/reqs.php",
|
| 35 |
+
action: (accept ? "confirm" : "reject")
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
defaultFuncs
|
| 39 |
+
.post(
|
| 40 |
+
"https://www.facebook.com/requests/friends/ajax/",
|
| 41 |
+
ctx.jar,
|
| 42 |
+
form
|
| 43 |
+
)
|
| 44 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 45 |
+
.then(function (resData) {
|
| 46 |
+
if (resData.payload.err) {
|
| 47 |
+
throw {
|
| 48 |
+
err: resData.payload.err
|
| 49 |
+
};
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
return callback();
|
| 53 |
+
})
|
| 54 |
+
.catch(function (err) {
|
| 55 |
+
log.error("handleFriendRequest", err);
|
| 56 |
+
return callback(err);
|
| 57 |
+
});
|
| 58 |
+
|
| 59 |
+
return returnPromise;
|
| 60 |
+
};
|
| 61 |
+
};
|
includes/login/src/handleMessageRequest.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function handleMessageRequest(threadID, accept, callback) {
|
| 8 |
+
if (utils.getType(accept) !== "Boolean") {
|
| 9 |
+
throw {
|
| 10 |
+
error: "Please pass a boolean as a second argument."
|
| 11 |
+
};
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
let resolveFunc = function () { };
|
| 15 |
+
let rejectFunc = function () { };
|
| 16 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 17 |
+
resolveFunc = resolve;
|
| 18 |
+
rejectFunc = reject;
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
if (!callback) {
|
| 22 |
+
callback = function (err, friendList) {
|
| 23 |
+
if (err) {
|
| 24 |
+
return rejectFunc(err);
|
| 25 |
+
}
|
| 26 |
+
resolveFunc(friendList);
|
| 27 |
+
};
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
const form = {
|
| 31 |
+
client: "mercury"
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
if (utils.getType(threadID) !== "Array") {
|
| 35 |
+
threadID = [threadID];
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
const messageBox = accept ? "inbox" : "other";
|
| 39 |
+
|
| 40 |
+
for (let i = 0; i < threadID.length; i++) {
|
| 41 |
+
form[messageBox + "[" + i + "]"] = threadID[i];
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
defaultFuncs
|
| 45 |
+
.post(
|
| 46 |
+
"https://www.facebook.com/ajax/mercury/move_thread.php",
|
| 47 |
+
ctx.jar,
|
| 48 |
+
form
|
| 49 |
+
)
|
| 50 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 51 |
+
.then(function (resData) {
|
| 52 |
+
if (resData.error) {
|
| 53 |
+
throw resData;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
return callback();
|
| 57 |
+
})
|
| 58 |
+
.catch(function (err) {
|
| 59 |
+
log.error("handleMessageRequest", err);
|
| 60 |
+
return callback(err);
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
return returnPromise;
|
| 64 |
+
};
|
| 65 |
+
};
|
includes/login/src/httpGet.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function httpGet(url, form, customHeader, callback, notAPI) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
|
| 11 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 12 |
+
resolveFunc = resolve;
|
| 13 |
+
rejectFunc = reject;
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
if (utils.getType(form) == "Function" || utils.getType(form) == "AsyncFunction") {
|
| 17 |
+
callback = form;
|
| 18 |
+
form = {};
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
if (utils.getType(customHeader) == "Function" || utils.getType(customHeader) == "AsyncFunction") {
|
| 22 |
+
callback = customHeader;
|
| 23 |
+
customHeader = {};
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
customHeader = customHeader || {};
|
| 27 |
+
|
| 28 |
+
callback = callback || function (err, data) {
|
| 29 |
+
if (err) return rejectFunc(err);
|
| 30 |
+
resolveFunc(data);
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
if (notAPI) {
|
| 34 |
+
utils
|
| 35 |
+
.get(url, ctx.jar, form, ctx.globalOptions, ctx, customHeader)
|
| 36 |
+
.then(function (resData) {
|
| 37 |
+
callback(null, resData.body.toString());
|
| 38 |
+
})
|
| 39 |
+
.catch(function (err) {
|
| 40 |
+
log.error("httpGet", err);
|
| 41 |
+
return callback(err);
|
| 42 |
+
});
|
| 43 |
+
} else {
|
| 44 |
+
defaultFuncs
|
| 45 |
+
.get(url, ctx.jar, form, null, customHeader)
|
| 46 |
+
.then(function (resData) {
|
| 47 |
+
callback(null, resData.body.toString());
|
| 48 |
+
})
|
| 49 |
+
.catch(function (err) {
|
| 50 |
+
log.error("httpGet", err);
|
| 51 |
+
return callback(err);
|
| 52 |
+
});
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
return returnPromise;
|
| 56 |
+
};
|
| 57 |
+
};
|
includes/login/src/httpPost.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function httpPost(url, form, customHeader, callback, notAPI) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
|
| 11 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 12 |
+
resolveFunc = resolve;
|
| 13 |
+
rejectFunc = reject;
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
if (utils.getType(form) == "Function" || utils.getType(form) == "AsyncFunction") {
|
| 17 |
+
callback = form;
|
| 18 |
+
form = {};
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
if (utils.getType(customHeader) == "Function" || utils.getType(customHeader) == "AsyncFunction") {
|
| 22 |
+
callback = customHeader;
|
| 23 |
+
customHeader = {};
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
customHeader = customHeader || {};
|
| 27 |
+
|
| 28 |
+
callback = callback || function (err, data) {
|
| 29 |
+
if (err) return rejectFunc(err);
|
| 30 |
+
resolveFunc(data);
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
if (notAPI) {
|
| 34 |
+
utils
|
| 35 |
+
.post(url, ctx.jar, form, ctx.globalOptions, ctx, customHeader)
|
| 36 |
+
.then(function (resData) {
|
| 37 |
+
callback(null, resData.body.toString());
|
| 38 |
+
})
|
| 39 |
+
.catch(function (err) {
|
| 40 |
+
log.error("httpPost", err);
|
| 41 |
+
return callback(err);
|
| 42 |
+
});
|
| 43 |
+
} else {
|
| 44 |
+
defaultFuncs
|
| 45 |
+
.post(url, ctx.jar, form, {}, customHeader)
|
| 46 |
+
.then(function (resData) {
|
| 47 |
+
callback(null, resData.body.toString());
|
| 48 |
+
})
|
| 49 |
+
.catch(function (err) {
|
| 50 |
+
log.error("httpPost", err);
|
| 51 |
+
return callback(err);
|
| 52 |
+
});
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
return returnPromise;
|
| 56 |
+
};
|
| 57 |
+
};
|
includes/login/src/httpPostFormData.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 8 |
+
return function httpPostFormData(url, form, customHeader, callback, notAPI) {
|
| 9 |
+
let resolveFunc = function () { };
|
| 10 |
+
let rejectFunc = function () { };
|
| 11 |
+
|
| 12 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 13 |
+
resolveFunc = resolve;
|
| 14 |
+
rejectFunc = reject;
|
| 15 |
+
});
|
| 16 |
+
|
| 17 |
+
if (utils.getType(form) == "Function" || utils.getType(form) == "AsyncFunction") {
|
| 18 |
+
callback = form;
|
| 19 |
+
form = {};
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
if (utils.getType(customHeader) == "Function" || utils.getType(customHeader) == "AsyncFunction") {
|
| 23 |
+
callback = customHeader;
|
| 24 |
+
customHeader = {};
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
customHeader = customHeader || {};
|
| 28 |
+
|
| 29 |
+
if (utils.getType(callback) == "Boolean") {
|
| 30 |
+
notAPI = callback;
|
| 31 |
+
callback = null;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
callback = callback || function (err, data) {
|
| 35 |
+
if (err) return rejectFunc(err);
|
| 36 |
+
resolveFunc(data);
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
if (notAPI) {
|
| 40 |
+
utils
|
| 41 |
+
.postFormData(url, ctx.jar, form, ctx.globalOptions, ctx, customHeader)
|
| 42 |
+
.then(function (resData) {
|
| 43 |
+
callback(null, resData.body.toString());
|
| 44 |
+
})
|
| 45 |
+
.catch(function (err) {
|
| 46 |
+
log.error("httpPostFormData", err);
|
| 47 |
+
return callback(err);
|
| 48 |
+
});
|
| 49 |
+
} else {
|
| 50 |
+
defaultFuncs
|
| 51 |
+
.postFormData(url, ctx.jar, form, null, customHeader)
|
| 52 |
+
.then(function (resData) {
|
| 53 |
+
callback(null, resData.body.toString());
|
| 54 |
+
})
|
| 55 |
+
.catch(function (err) {
|
| 56 |
+
log.error("httpPostFormData", err);
|
| 57 |
+
return callback(err);
|
| 58 |
+
});
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
return returnPromise;
|
| 62 |
+
};
|
| 63 |
+
};
|
includes/login/src/listenMqtt.js
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* eslint-disable no-redeclare */
|
| 2 |
+
"use strict";
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
const mqtt = require('mqtt');
|
| 6 |
+
const websocket = require('websocket-stream');
|
| 7 |
+
const HttpsProxyAgent = require('https-proxy-agent');
|
| 8 |
+
const EventEmitter = require('events');
|
| 9 |
+
|
| 10 |
+
const identity = function () { };
|
| 11 |
+
|
| 12 |
+
const topics = [
|
| 13 |
+
"/legacy_web",
|
| 14 |
+
"/webrtc",
|
| 15 |
+
"/rtc_multi",
|
| 16 |
+
"/onevc",
|
| 17 |
+
"/br_sr", //Notification
|
| 18 |
+
//Need to publish /br_sr right after this
|
| 19 |
+
"/sr_res",
|
| 20 |
+
"/t_ms",
|
| 21 |
+
"/thread_typing",
|
| 22 |
+
"/orca_typing_notifications",
|
| 23 |
+
"/notify_disconnect",
|
| 24 |
+
//Need to publish /messenger_sync_create_queue right after this
|
| 25 |
+
"/orca_presence",
|
| 26 |
+
//Will receive /sr_res right here.
|
| 27 |
+
|
| 28 |
+
"/legacy_web_mtouch"
|
| 29 |
+
// "/inbox",
|
| 30 |
+
// "/mercury",
|
| 31 |
+
// "/messaging_events",
|
| 32 |
+
// "/orca_message_notifications",
|
| 33 |
+
// "/pp",
|
| 34 |
+
// "/webrtc_response",
|
| 35 |
+
];
|
| 36 |
+
|
| 37 |
+
function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
| 38 |
+
//Don't really know what this does but I think it's for the active state?
|
| 39 |
+
//TODO: Move to ctx when implemented
|
| 40 |
+
const chatOn = ctx.globalOptions.online;
|
| 41 |
+
const foreground = false;
|
| 42 |
+
|
| 43 |
+
const sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
|
| 44 |
+
const username = {
|
| 45 |
+
u: ctx.i_userID || ctx.userID,
|
| 46 |
+
s: sessionID,
|
| 47 |
+
chat_on: chatOn,
|
| 48 |
+
fg: foreground,
|
| 49 |
+
d: utils.getGUID(),
|
| 50 |
+
ct: "websocket",
|
| 51 |
+
//App id from facebook
|
| 52 |
+
aid: "219994525426954",
|
| 53 |
+
mqtt_sid: "",
|
| 54 |
+
cp: 3,
|
| 55 |
+
ecp: 10,
|
| 56 |
+
st: [],
|
| 57 |
+
pm: [],
|
| 58 |
+
dc: "",
|
| 59 |
+
no_auto_fg: true,
|
| 60 |
+
gas: null,
|
| 61 |
+
pack: [],
|
| 62 |
+
a: ctx.globalOptions.userAgent,
|
| 63 |
+
aids: null
|
| 64 |
+
};
|
| 65 |
+
const cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
|
| 66 |
+
|
| 67 |
+
let host;
|
| 68 |
+
if (ctx.mqttEndpoint) {
|
| 69 |
+
host = `${ctx.mqttEndpoint}&sid=${sessionID}`;
|
| 70 |
+
} else if (ctx.region) {
|
| 71 |
+
host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLocaleLowerCase()}&sid=${sessionID}`;
|
| 72 |
+
} else {
|
| 73 |
+
host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}`;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
const options = {
|
| 77 |
+
clientId: "mqttwsclient",
|
| 78 |
+
protocolId: 'MQIsdp',
|
| 79 |
+
protocolVersion: 3,
|
| 80 |
+
username: JSON.stringify(username),
|
| 81 |
+
clean: true,
|
| 82 |
+
wsOptions: {
|
| 83 |
+
headers: {
|
| 84 |
+
'Cookie': cookies,
|
| 85 |
+
'Origin': 'https://www.facebook.com',
|
| 86 |
+
'User-Agent': ctx.globalOptions.userAgent,
|
| 87 |
+
'Referer': 'https://www.facebook.com/',
|
| 88 |
+
'Host': new URL(host).hostname //'edge-chat.facebook.com'
|
| 89 |
+
},
|
| 90 |
+
origin: 'https://www.facebook.com',
|
| 91 |
+
protocolVersion: 13
|
| 92 |
+
},
|
| 93 |
+
keepalive: 10,
|
| 94 |
+
reschedulePings: false
|
| 95 |
+
};
|
| 96 |
+
|
| 97 |
+
if (typeof ctx.globalOptions.proxy != "undefined") {
|
| 98 |
+
const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
|
| 99 |
+
options.wsOptions.agent = agent;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
ctx.mqttClient = new mqtt.Client(_ => websocket(host, options.wsOptions), options);
|
| 103 |
+
|
| 104 |
+
const mqttClient = ctx.mqttClient;
|
| 105 |
+
|
| 106 |
+
mqttClient.on('error', function (err) {
|
| 107 |
+
log.error("listenMqtt", err);
|
| 108 |
+
mqttClient.end();
|
| 109 |
+
if (ctx.globalOptions.autoReconnect) {
|
| 110 |
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
| 111 |
+
} else {
|
| 112 |
+
utils.checkLiveCookie(ctx, defaultFuncs)
|
| 113 |
+
.then(res => {
|
| 114 |
+
globalCallback({
|
| 115 |
+
type: "stop_listen",
|
| 116 |
+
error: "Connection refused: Server unavailable"
|
| 117 |
+
}, null);
|
| 118 |
+
})
|
| 119 |
+
.catch(err => {
|
| 120 |
+
globalCallback({
|
| 121 |
+
type: "account_inactive",
|
| 122 |
+
error: "Maybe your account is blocked by facebook, please login and check at https://facebook.com"
|
| 123 |
+
}, null);
|
| 124 |
+
});
|
| 125 |
+
}
|
| 126 |
+
});
|
| 127 |
+
|
| 128 |
+
mqttClient.on('close', function () {
|
| 129 |
+
|
| 130 |
+
});
|
| 131 |
+
|
| 132 |
+
mqttClient.on('connect', function () {
|
| 133 |
+
topics.forEach(function (topicsub) {
|
| 134 |
+
mqttClient.subscribe(topicsub);
|
| 135 |
+
});
|
| 136 |
+
|
| 137 |
+
let topic;
|
| 138 |
+
const queue = {
|
| 139 |
+
sync_api_version: 10,
|
| 140 |
+
max_deltas_able_to_process: 1000,
|
| 141 |
+
delta_batch_size: 500,
|
| 142 |
+
encoding: "JSON",
|
| 143 |
+
entity_fbid: ctx.i_userID || ctx.userID
|
| 144 |
+
};
|
| 145 |
+
|
| 146 |
+
if (ctx.syncToken) {
|
| 147 |
+
topic = "/messenger_sync_get_diffs";
|
| 148 |
+
queue.last_seq_id = ctx.lastSeqId;
|
| 149 |
+
queue.sync_token = ctx.syncToken;
|
| 150 |
+
} else {
|
| 151 |
+
topic = "/messenger_sync_create_queue";
|
| 152 |
+
queue.initial_titan_sequence_id = ctx.lastSeqId;
|
| 153 |
+
queue.device_params = null;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
|
| 157 |
+
// set status online
|
| 158 |
+
// fix by NTKhang
|
| 159 |
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
|
| 160 |
+
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
|
| 161 |
+
|
| 162 |
+
const rTimeout = setTimeout(function () {
|
| 163 |
+
mqttClient.end();
|
| 164 |
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
| 165 |
+
}, 5000);
|
| 166 |
+
|
| 167 |
+
ctx.tmsWait = function () {
|
| 168 |
+
clearTimeout(rTimeout);
|
| 169 |
+
ctx.globalOptions.emitReady ? globalCallback({
|
| 170 |
+
type: "ready",
|
| 171 |
+
error: null
|
| 172 |
+
}) : "";
|
| 173 |
+
delete ctx.tmsWait;
|
| 174 |
+
};
|
| 175 |
+
|
| 176 |
+
});
|
| 177 |
+
|
| 178 |
+
mqttClient.on('message', function (topic, message, _packet) {
|
| 179 |
+
let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
|
| 180 |
+
try {
|
| 181 |
+
jsonMessage = JSON.parse(jsonMessage);
|
| 182 |
+
}
|
| 183 |
+
catch (e) {
|
| 184 |
+
jsonMessage = {};
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
if (jsonMessage.type === "jewel_requests_add") {
|
| 188 |
+
globalCallback(null, {
|
| 189 |
+
type: "friend_request_received",
|
| 190 |
+
actorFbId: jsonMessage.from.toString(),
|
| 191 |
+
timestamp: Date.now().toString()
|
| 192 |
+
});
|
| 193 |
+
}
|
| 194 |
+
else if (jsonMessage.type === "jewel_requests_remove_old") {
|
| 195 |
+
globalCallback(null, {
|
| 196 |
+
type: "friend_request_cancel",
|
| 197 |
+
actorFbId: jsonMessage.from.toString(),
|
| 198 |
+
timestamp: Date.now().toString()
|
| 199 |
+
});
|
| 200 |
+
}
|
| 201 |
+
else if (topic === "/t_ms") {
|
| 202 |
+
if (ctx.tmsWait && typeof ctx.tmsWait == "function") {
|
| 203 |
+
ctx.tmsWait();
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
|
| 207 |
+
ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
|
| 208 |
+
ctx.syncToken = jsonMessage.syncToken;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
if (jsonMessage.lastIssuedSeqId) {
|
| 212 |
+
ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
//If it contains more than 1 delta
|
| 216 |
+
for (const i in jsonMessage.deltas) {
|
| 217 |
+
const delta = jsonMessage.deltas[i];
|
| 218 |
+
parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
|
| 219 |
+
}
|
| 220 |
+
} else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
|
| 221 |
+
const typ = {
|
| 222 |
+
type: "typ",
|
| 223 |
+
isTyping: !!jsonMessage.state,
|
| 224 |
+
from: jsonMessage.sender_fbid.toString(),
|
| 225 |
+
threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
|
| 226 |
+
};
|
| 227 |
+
(function () { globalCallback(null, typ); })();
|
| 228 |
+
} else if (topic === "/orca_presence") {
|
| 229 |
+
if (!ctx.globalOptions.updatePresence) {
|
| 230 |
+
for (const i in jsonMessage.list) {
|
| 231 |
+
const data = jsonMessage.list[i];
|
| 232 |
+
const userID = data["u"];
|
| 233 |
+
|
| 234 |
+
const presence = {
|
| 235 |
+
type: "presence",
|
| 236 |
+
userID: userID.toString(),
|
| 237 |
+
//Convert to ms
|
| 238 |
+
timestamp: data["l"] * 1000,
|
| 239 |
+
statuses: data["p"]
|
| 240 |
+
};
|
| 241 |
+
(function () { globalCallback(null, presence); })();
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
});
|
| 247 |
+
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
| 251 |
+
if (v.delta.class == "NewMessage") {
|
| 252 |
+
//Not tested for pages
|
| 253 |
+
if (ctx.globalOptions.pageID &&
|
| 254 |
+
ctx.globalOptions.pageID != v.queue
|
| 255 |
+
)
|
| 256 |
+
return;
|
| 257 |
+
|
| 258 |
+
(function resolveAttachmentUrl(i) {
|
| 259 |
+
if (i == (v.delta.attachments || []).length) {
|
| 260 |
+
let fmtMsg;
|
| 261 |
+
try {
|
| 262 |
+
fmtMsg = utils.formatDeltaMessage(v);
|
| 263 |
+
} catch (err) {
|
| 264 |
+
return globalCallback({
|
| 265 |
+
error: "Problem parsing message object. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.",
|
| 266 |
+
detail: err,
|
| 267 |
+
res: v,
|
| 268 |
+
type: "parse_error"
|
| 269 |
+
});
|
| 270 |
+
}
|
| 271 |
+
if (fmtMsg) {
|
| 272 |
+
if (ctx.globalOptions.autoMarkDelivery) {
|
| 273 |
+
markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
return !ctx.globalOptions.selfListen &&
|
| 277 |
+
(fmtMsg.senderID === ctx.i_userID || fmtMsg.senderID === ctx.userID) ?
|
| 278 |
+
undefined :
|
| 279 |
+
(function () { globalCallback(null, fmtMsg); })();
|
| 280 |
+
} else {
|
| 281 |
+
if (v.delta.attachments[i].mercury.attach_type == "photo") {
|
| 282 |
+
api.resolvePhotoUrl(
|
| 283 |
+
v.delta.attachments[i].fbid,
|
| 284 |
+
(err, url) => {
|
| 285 |
+
if (!err)
|
| 286 |
+
v.delta.attachments[
|
| 287 |
+
i
|
| 288 |
+
].mercury.metadata.url = url;
|
| 289 |
+
return resolveAttachmentUrl(i + 1);
|
| 290 |
+
}
|
| 291 |
+
);
|
| 292 |
+
} else {
|
| 293 |
+
return resolveAttachmentUrl(i + 1);
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
})(0);
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
if (v.delta.class == "ClientPayload") {
|
| 300 |
+
const clientPayload = utils.decodeClientPayload(
|
| 301 |
+
v.delta.payload
|
| 302 |
+
);
|
| 303 |
+
|
| 304 |
+
if (clientPayload && clientPayload.deltas) {
|
| 305 |
+
for (const i in clientPayload.deltas) {
|
| 306 |
+
const delta = clientPayload.deltas[i];
|
| 307 |
+
if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
|
| 308 |
+
(function () {
|
| 309 |
+
globalCallback(null, {
|
| 310 |
+
type: "message_reaction",
|
| 311 |
+
threadID: (delta.deltaMessageReaction.threadKey
|
| 312 |
+
.threadFbId ?
|
| 313 |
+
delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey
|
| 314 |
+
.otherUserFbId).toString(),
|
| 315 |
+
messageID: delta.deltaMessageReaction.messageId,
|
| 316 |
+
reaction: delta.deltaMessageReaction.reaction,
|
| 317 |
+
senderID: delta.deltaMessageReaction.senderId == 0 ? delta.deltaMessageReaction.userId.toString() : delta.deltaMessageReaction.senderId.toString(),
|
| 318 |
+
userID: (delta.deltaMessageReaction.userId || delta.deltaMessageReaction.senderId).toString()
|
| 319 |
+
});
|
| 320 |
+
})();
|
| 321 |
+
} else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
|
| 322 |
+
(function () {
|
| 323 |
+
globalCallback(null, {
|
| 324 |
+
type: "message_unsend",
|
| 325 |
+
threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ?
|
| 326 |
+
delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey
|
| 327 |
+
.otherUserFbId).toString(),
|
| 328 |
+
messageID: delta.deltaRecallMessageData.messageID,
|
| 329 |
+
senderID: delta.deltaRecallMessageData.senderID.toString(),
|
| 330 |
+
deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
|
| 331 |
+
timestamp: delta.deltaRecallMessageData.timestamp
|
| 332 |
+
});
|
| 333 |
+
})();
|
| 334 |
+
} else if (delta.deltaRemoveMessage && !!ctx.globalOptions.listenEvents) {
|
| 335 |
+
(function () {
|
| 336 |
+
globalCallback(null, {
|
| 337 |
+
type: "message_self_delete",
|
| 338 |
+
threadID: (delta.deltaRemoveMessage.threadKey.threadFbId ?
|
| 339 |
+
delta.deltaRemoveMessage.threadKey.threadFbId : delta.deltaRemoveMessage.threadKey
|
| 340 |
+
.otherUserFbId).toString(),
|
| 341 |
+
messageID: delta.deltaRemoveMessage.messageIds.length == 1 ? delta.deltaRemoveMessage.messageIds[0] : delta.deltaRemoveMessage.messageIds,
|
| 342 |
+
senderID: api.getCurrentUserID(),
|
| 343 |
+
deletionTimestamp: delta.deltaRemoveMessage.deletionTimestamp,
|
| 344 |
+
timestamp: delta.deltaRemoveMessage.timestamp
|
| 345 |
+
});
|
| 346 |
+
})();
|
| 347 |
+
}
|
| 348 |
+
else if (delta.deltaMessageReply) {
|
| 349 |
+
//Mention block - #1
|
| 350 |
+
let mdata =
|
| 351 |
+
delta.deltaMessageReply.message === undefined ? [] :
|
| 352 |
+
delta.deltaMessageReply.message.data === undefined ? [] :
|
| 353 |
+
delta.deltaMessageReply.message.data.prng === undefined ? [] :
|
| 354 |
+
JSON.parse(delta.deltaMessageReply.message.data.prng);
|
| 355 |
+
let m_id = mdata.map(u => u.i);
|
| 356 |
+
let m_offset = mdata.map(u => u.o);
|
| 357 |
+
let m_length = mdata.map(u => u.l);
|
| 358 |
+
|
| 359 |
+
const mentions = {};
|
| 360 |
+
|
| 361 |
+
for (let i = 0; i < m_id.length; i++) {
|
| 362 |
+
mentions[m_id[i]] = (delta.deltaMessageReply.message.body || "").substring(
|
| 363 |
+
m_offset[i],
|
| 364 |
+
m_offset[i] + m_length[i]
|
| 365 |
+
);
|
| 366 |
+
}
|
| 367 |
+
//Mention block - 1#
|
| 368 |
+
const callbackToReturn = {
|
| 369 |
+
type: "message_reply",
|
| 370 |
+
threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ?
|
| 371 |
+
delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey
|
| 372 |
+
.otherUserFbId).toString(),
|
| 373 |
+
messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
|
| 374 |
+
senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
|
| 375 |
+
attachments: (delta.deltaMessageReply.message.attachments || []).map(function (att) {
|
| 376 |
+
const mercury = JSON.parse(att.mercuryJSON);
|
| 377 |
+
Object.assign(att, mercury);
|
| 378 |
+
return att;
|
| 379 |
+
}).map(att => {
|
| 380 |
+
let x;
|
| 381 |
+
try {
|
| 382 |
+
x = utils._formatAttachment(att);
|
| 383 |
+
} catch (ex) {
|
| 384 |
+
x = att;
|
| 385 |
+
x.error = ex;
|
| 386 |
+
x.type = "unknown";
|
| 387 |
+
}
|
| 388 |
+
return x;
|
| 389 |
+
}),
|
| 390 |
+
body: delta.deltaMessageReply.message.body || "",
|
| 391 |
+
isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
|
| 392 |
+
mentions: mentions,
|
| 393 |
+
timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
|
| 394 |
+
participantIDs: (delta.deltaMessageReply.message.messageMetadata.cid.canonicalParticipantFbids || delta.deltaMessageReply.message.participants || []).map(e => e.toString())
|
| 395 |
+
};
|
| 396 |
+
|
| 397 |
+
if (delta.deltaMessageReply.repliedToMessage) {
|
| 398 |
+
//Mention block - #2
|
| 399 |
+
mdata =
|
| 400 |
+
delta.deltaMessageReply.repliedToMessage === undefined ? [] :
|
| 401 |
+
delta.deltaMessageReply.repliedToMessage.data === undefined ? [] :
|
| 402 |
+
delta.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] :
|
| 403 |
+
JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
|
| 404 |
+
m_id = mdata.map(u => u.i);
|
| 405 |
+
m_offset = mdata.map(u => u.o);
|
| 406 |
+
m_length = mdata.map(u => u.l);
|
| 407 |
+
|
| 408 |
+
const rmentions = {};
|
| 409 |
+
|
| 410 |
+
for (let i = 0; i < m_id.length; i++) {
|
| 411 |
+
rmentions[m_id[i]] = (delta.deltaMessageReply.repliedToMessage.body || "").substring(
|
| 412 |
+
m_offset[i],
|
| 413 |
+
m_offset[i] + m_length[i]
|
| 414 |
+
);
|
| 415 |
+
}
|
| 416 |
+
//Mention block - 2#
|
| 417 |
+
callbackToReturn.messageReply = {
|
| 418 |
+
threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ?
|
| 419 |
+
delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey
|
| 420 |
+
.otherUserFbId).toString(),
|
| 421 |
+
messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
|
| 422 |
+
senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
|
| 423 |
+
attachments: delta.deltaMessageReply.repliedToMessage.attachments.map(function (att) {
|
| 424 |
+
const mercury = JSON.parse(att.mercuryJSON);
|
| 425 |
+
Object.assign(att, mercury);
|
| 426 |
+
return att;
|
| 427 |
+
}).map(att => {
|
| 428 |
+
let x;
|
| 429 |
+
try {
|
| 430 |
+
x = utils._formatAttachment(att);
|
| 431 |
+
} catch (ex) {
|
| 432 |
+
x = att;
|
| 433 |
+
x.error = ex;
|
| 434 |
+
x.type = "unknown";
|
| 435 |
+
}
|
| 436 |
+
return x;
|
| 437 |
+
}),
|
| 438 |
+
body: delta.deltaMessageReply.repliedToMessage.body || "",
|
| 439 |
+
isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
|
| 440 |
+
mentions: rmentions,
|
| 441 |
+
timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp
|
| 442 |
+
};
|
| 443 |
+
} else if (delta.deltaMessageReply.replyToMessageId) {
|
| 444 |
+
return defaultFuncs
|
| 445 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
|
| 446 |
+
"av": ctx.globalOptions.pageID,
|
| 447 |
+
"queries": JSON.stringify({
|
| 448 |
+
"o0": {
|
| 449 |
+
//Using the same doc_id as forcedFetch
|
| 450 |
+
"doc_id": "2848441488556444",
|
| 451 |
+
"query_params": {
|
| 452 |
+
"thread_and_message_id": {
|
| 453 |
+
"thread_id": callbackToReturn.threadID,
|
| 454 |
+
"message_id": delta.deltaMessageReply.replyToMessageId.id
|
| 455 |
+
}
|
| 456 |
+
}
|
| 457 |
+
}
|
| 458 |
+
})
|
| 459 |
+
})
|
| 460 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 461 |
+
.then((resData) => {
|
| 462 |
+
if (resData[resData.length - 1].error_results > 0) {
|
| 463 |
+
throw resData[0].o0.errors;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
if (resData[resData.length - 1].successful_results === 0) {
|
| 467 |
+
throw { error: "forcedFetch: there was no successful_results", res: resData };
|
| 468 |
+
}
|
| 469 |
+
|
| 470 |
+
const fetchData = resData[0].o0.data.message;
|
| 471 |
+
|
| 472 |
+
const mobj = {};
|
| 473 |
+
for (const n in fetchData.message.ranges) {
|
| 474 |
+
mobj[fetchData.message.ranges[n].entity.id] = (fetchData.message.text || "").substr(fetchData.message.ranges[n].offset, fetchData.message.ranges[n].length);
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
callbackToReturn.messageReply = {
|
| 478 |
+
threadID: callbackToReturn.threadID,
|
| 479 |
+
messageID: fetchData.message_id,
|
| 480 |
+
senderID: fetchData.message_sender.id.toString(),
|
| 481 |
+
attachments: fetchData.message.blob_attachment.map(att => {
|
| 482 |
+
let x;
|
| 483 |
+
try {
|
| 484 |
+
x = utils._formatAttachment({
|
| 485 |
+
blob_attachment: att
|
| 486 |
+
});
|
| 487 |
+
} catch (ex) {
|
| 488 |
+
x = att;
|
| 489 |
+
x.error = ex;
|
| 490 |
+
x.type = "unknown";
|
| 491 |
+
}
|
| 492 |
+
return x;
|
| 493 |
+
}),
|
| 494 |
+
body: fetchData.message.text || "",
|
| 495 |
+
isGroup: callbackToReturn.isGroup,
|
| 496 |
+
mentions: mobj,
|
| 497 |
+
timestamp: parseInt(fetchData.timestamp_precise)
|
| 498 |
+
};
|
| 499 |
+
})
|
| 500 |
+
.catch((err) => {
|
| 501 |
+
log.error("forcedFetch", err);
|
| 502 |
+
})
|
| 503 |
+
.finally(function () {
|
| 504 |
+
if (ctx.globalOptions.autoMarkDelivery) {
|
| 505 |
+
markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
|
| 506 |
+
}
|
| 507 |
+
!ctx.globalOptions.selfListen &&
|
| 508 |
+
(callbackToReturn.senderID === ctx.i_userID || callbackToReturn.senderID === ctx.userID) ?
|
| 509 |
+
undefined :
|
| 510 |
+
(function () { globalCallback(null, callbackToReturn); })();
|
| 511 |
+
});
|
| 512 |
+
} else {
|
| 513 |
+
callbackToReturn.delta = delta;
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
if (ctx.globalOptions.autoMarkDelivery) {
|
| 517 |
+
markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
return !ctx.globalOptions.selfListen &&
|
| 521 |
+
(callbackToReturn.senderID === ctx.i_userID || callbackToReturn.senderID === ctx.userID) ?
|
| 522 |
+
undefined :
|
| 523 |
+
(function () { globalCallback(null, callbackToReturn); })();
|
| 524 |
+
}
|
| 525 |
+
}
|
| 526 |
+
return;
|
| 527 |
+
}
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
if (v.delta.class !== "NewMessage" &&
|
| 531 |
+
!ctx.globalOptions.listenEvents
|
| 532 |
+
)
|
| 533 |
+
return;
|
| 534 |
+
|
| 535 |
+
switch (v.delta.class) {
|
| 536 |
+
case "ReadReceipt":
|
| 537 |
+
var fmtMsg;
|
| 538 |
+
try {
|
| 539 |
+
fmtMsg = utils.formatDeltaReadReceipt(v.delta);
|
| 540 |
+
}
|
| 541 |
+
catch (err) {
|
| 542 |
+
return globalCallback({
|
| 543 |
+
error: "Problem parsing message object. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.",
|
| 544 |
+
detail: err,
|
| 545 |
+
res: v.delta,
|
| 546 |
+
type: "parse_error"
|
| 547 |
+
});
|
| 548 |
+
}
|
| 549 |
+
return (function () { globalCallback(null, fmtMsg); })();
|
| 550 |
+
case "AdminTextMessage":
|
| 551 |
+
switch (v.delta.type) {
|
| 552 |
+
case "change_thread_theme":
|
| 553 |
+
case "change_thread_nickname":
|
| 554 |
+
case "change_thread_icon":
|
| 555 |
+
case "change_thread_quick_reaction":
|
| 556 |
+
case "change_thread_admins":
|
| 557 |
+
case "group_poll":
|
| 558 |
+
case "joinable_group_link_mode_change":
|
| 559 |
+
case "magic_words":
|
| 560 |
+
case "change_thread_approval_mode":
|
| 561 |
+
case "messenger_call_log":
|
| 562 |
+
case "participant_joined_group_call":
|
| 563 |
+
var fmtMsg;
|
| 564 |
+
try {
|
| 565 |
+
fmtMsg = utils.formatDeltaEvent(v.delta);
|
| 566 |
+
}
|
| 567 |
+
catch (err) {
|
| 568 |
+
return globalCallback({
|
| 569 |
+
error: "Problem parsing message object. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.",
|
| 570 |
+
detail: err,
|
| 571 |
+
res: v.delta,
|
| 572 |
+
type: "parse_error"
|
| 573 |
+
});
|
| 574 |
+
}
|
| 575 |
+
return (function () { globalCallback(null, fmtMsg); })();
|
| 576 |
+
default:
|
| 577 |
+
return;
|
| 578 |
+
}
|
| 579 |
+
//For group images
|
| 580 |
+
case "ForcedFetch":
|
| 581 |
+
if (!v.delta.threadKey) return;
|
| 582 |
+
var mid = v.delta.messageId;
|
| 583 |
+
var tid = v.delta.threadKey.threadFbId;
|
| 584 |
+
if (mid && tid) {
|
| 585 |
+
const form = {
|
| 586 |
+
"av": ctx.globalOptions.pageID,
|
| 587 |
+
"queries": JSON.stringify({
|
| 588 |
+
"o0": {
|
| 589 |
+
//This doc_id is valid as of March 25, 2020
|
| 590 |
+
"doc_id": "2848441488556444",
|
| 591 |
+
"query_params": {
|
| 592 |
+
"thread_and_message_id": {
|
| 593 |
+
"thread_id": tid.toString(),
|
| 594 |
+
"message_id": mid
|
| 595 |
+
}
|
| 596 |
+
}
|
| 597 |
+
}
|
| 598 |
+
})
|
| 599 |
+
};
|
| 600 |
+
|
| 601 |
+
defaultFuncs
|
| 602 |
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
| 603 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 604 |
+
.then((resData) => {
|
| 605 |
+
if (resData[resData.length - 1].error_results > 0) {
|
| 606 |
+
throw resData[0].o0.errors;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
if (resData[resData.length - 1].successful_results === 0) {
|
| 610 |
+
throw { error: "forcedFetch: there was no successful_results", res: resData };
|
| 611 |
+
}
|
| 612 |
+
|
| 613 |
+
const fetchData = resData[0].o0.data.message;
|
| 614 |
+
|
| 615 |
+
if (utils.getType(fetchData) == "Object") {
|
| 616 |
+
log.info("forcedFetch", fetchData);
|
| 617 |
+
switch (fetchData.__typename) {
|
| 618 |
+
case "ThreadImageMessage":
|
| 619 |
+
(!ctx.globalOptions.selfListenEvent && (fetchData.message_sender.id.toString() === ctx.i_userID || fetchData.message_sender.id.toString() === ctx.userID)) || !ctx.loggedIn ?
|
| 620 |
+
undefined :
|
| 621 |
+
(function () {
|
| 622 |
+
globalCallback(null, {
|
| 623 |
+
type: "event",
|
| 624 |
+
threadID: utils.formatID(tid.toString()),
|
| 625 |
+
messageID: fetchData.message_id,
|
| 626 |
+
logMessageType: "log:thread-image",
|
| 627 |
+
logMessageData: {
|
| 628 |
+
attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
|
| 629 |
+
width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
|
| 630 |
+
height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
|
| 631 |
+
url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri
|
| 632 |
+
},
|
| 633 |
+
logMessageBody: fetchData.snippet,
|
| 634 |
+
timestamp: fetchData.timestamp_precise,
|
| 635 |
+
author: fetchData.message_sender.id
|
| 636 |
+
});
|
| 637 |
+
})();
|
| 638 |
+
break;
|
| 639 |
+
case "UserMessage":
|
| 640 |
+
log.info("ff-Return", {
|
| 641 |
+
type: "message",
|
| 642 |
+
senderID: utils.formatID(fetchData.message_sender.id),
|
| 643 |
+
body: fetchData.message.text || "",
|
| 644 |
+
threadID: utils.formatID(tid.toString()),
|
| 645 |
+
messageID: fetchData.message_id,
|
| 646 |
+
attachments: [{
|
| 647 |
+
type: "share",
|
| 648 |
+
ID: fetchData.extensible_attachment.legacy_attachment_id,
|
| 649 |
+
url: fetchData.extensible_attachment.story_attachment.url,
|
| 650 |
+
|
| 651 |
+
title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
|
| 652 |
+
description: fetchData.extensible_attachment.story_attachment.description.text,
|
| 653 |
+
source: fetchData.extensible_attachment.story_attachment.source,
|
| 654 |
+
|
| 655 |
+
image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
|
| 656 |
+
width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
|
| 657 |
+
height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
|
| 658 |
+
playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
|
| 659 |
+
duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
|
| 660 |
+
|
| 661 |
+
subattachments: fetchData.extensible_attachment.subattachments,
|
| 662 |
+
properties: fetchData.extensible_attachment.story_attachment.properties
|
| 663 |
+
}],
|
| 664 |
+
mentions: {},
|
| 665 |
+
timestamp: parseInt(fetchData.timestamp_precise),
|
| 666 |
+
participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
|
| 667 |
+
isGroup: (fetchData.message_sender.id != tid.toString())
|
| 668 |
+
});
|
| 669 |
+
globalCallback(null, {
|
| 670 |
+
type: "message",
|
| 671 |
+
senderID: utils.formatID(fetchData.message_sender.id),
|
| 672 |
+
body: fetchData.message.text || "",
|
| 673 |
+
threadID: utils.formatID(tid.toString()),
|
| 674 |
+
messageID: fetchData.message_id,
|
| 675 |
+
attachments: [{
|
| 676 |
+
type: "share",
|
| 677 |
+
ID: fetchData.extensible_attachment.legacy_attachment_id,
|
| 678 |
+
url: fetchData.extensible_attachment.story_attachment.url,
|
| 679 |
+
|
| 680 |
+
title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
|
| 681 |
+
description: fetchData.extensible_attachment.story_attachment.description.text,
|
| 682 |
+
source: fetchData.extensible_attachment.story_attachment.source,
|
| 683 |
+
|
| 684 |
+
image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
|
| 685 |
+
width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
|
| 686 |
+
height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
|
| 687 |
+
playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
|
| 688 |
+
duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
|
| 689 |
+
|
| 690 |
+
subattachments: fetchData.extensible_attachment.subattachments,
|
| 691 |
+
properties: fetchData.extensible_attachment.story_attachment.properties
|
| 692 |
+
}],
|
| 693 |
+
mentions: {},
|
| 694 |
+
timestamp: parseInt(fetchData.timestamp_precise),
|
| 695 |
+
participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
|
| 696 |
+
isGroup: (fetchData.message_sender.id != tid.toString())
|
| 697 |
+
});
|
| 698 |
+
}
|
| 699 |
+
} else {
|
| 700 |
+
log.error("forcedFetch", fetchData);
|
| 701 |
+
}
|
| 702 |
+
})
|
| 703 |
+
.catch((err) => {
|
| 704 |
+
log.error("forcedFetch", err);
|
| 705 |
+
});
|
| 706 |
+
}
|
| 707 |
+
break;
|
| 708 |
+
case "ThreadName":
|
| 709 |
+
case "ParticipantsAddedToGroupThread":
|
| 710 |
+
case "ParticipantLeftGroupThread":
|
| 711 |
+
case "ApprovalQueue":
|
| 712 |
+
var formattedEvent;
|
| 713 |
+
try {
|
| 714 |
+
formattedEvent = utils.formatDeltaEvent(v.delta);
|
| 715 |
+
} catch (err) {
|
| 716 |
+
return globalCallback({
|
| 717 |
+
error: "Problem parsing message object. Please open an issue at https://github.com/ntkhang03/fb-chat-api/issues.",
|
| 718 |
+
detail: err,
|
| 719 |
+
res: v.delta,
|
| 720 |
+
type: "parse_error"
|
| 721 |
+
});
|
| 722 |
+
}
|
| 723 |
+
return (!ctx.globalOptions.selfListenEvent && (formattedEvent.author.toString() === ctx.i_userID || formattedEvent.author.toString() === ctx.userID)) || !ctx.loggedIn ?
|
| 724 |
+
undefined :
|
| 725 |
+
(function () { globalCallback(null, formattedEvent); })();
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
+
|
| 729 |
+
function markDelivery(ctx, api, threadID, messageID) {
|
| 730 |
+
if (threadID && messageID) {
|
| 731 |
+
api.markAsDelivered(threadID, messageID, (err) => {
|
| 732 |
+
if (err) {
|
| 733 |
+
log.error("markAsDelivered", err);
|
| 734 |
+
} else {
|
| 735 |
+
if (ctx.globalOptions.autoMarkRead) {
|
| 736 |
+
api.markAsRead(threadID, (err) => {
|
| 737 |
+
if (err) {
|
| 738 |
+
log.error("markAsDelivered", err);
|
| 739 |
+
}
|
| 740 |
+
});
|
| 741 |
+
}
|
| 742 |
+
}
|
| 743 |
+
});
|
| 744 |
+
}
|
| 745 |
+
}
|
| 746 |
+
|
| 747 |
+
function getSeqId(defaultFuncs, api, ctx, globalCallback) {
|
| 748 |
+
const jar = ctx.jar;
|
| 749 |
+
utils
|
| 750 |
+
.get('https://www.facebook.com/', jar, null, ctx.globalOptions, { noRef: true })
|
| 751 |
+
.then(utils.saveCookies(jar))
|
| 752 |
+
.then(function (resData) {
|
| 753 |
+
const html = resData.body;
|
| 754 |
+
const oldFBMQTTMatch = html.match(/irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/);
|
| 755 |
+
let mqttEndpoint = null;
|
| 756 |
+
let region = null;
|
| 757 |
+
let irisSeqID = null;
|
| 758 |
+
let noMqttData = null;
|
| 759 |
+
|
| 760 |
+
if (oldFBMQTTMatch) {
|
| 761 |
+
irisSeqID = oldFBMQTTMatch[1];
|
| 762 |
+
mqttEndpoint = oldFBMQTTMatch[2];
|
| 763 |
+
region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
|
| 764 |
+
log.info("login", `Got this account's message region: ${region}`);
|
| 765 |
+
} else {
|
| 766 |
+
const newFBMQTTMatch = html.match(/{"app_id":"219994525426954","endpoint":"(.+?)","iris_seq_id":"(.+?)"}/);
|
| 767 |
+
if (newFBMQTTMatch) {
|
| 768 |
+
irisSeqID = newFBMQTTMatch[2];
|
| 769 |
+
mqttEndpoint = newFBMQTTMatch[1].replace(/\\\//g, "/");
|
| 770 |
+
region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
|
| 771 |
+
log.info("login", `Got this account's message region: ${region}`);
|
| 772 |
+
} else {
|
| 773 |
+
const legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/);
|
| 774 |
+
if (legacyFBMQTTMatch) {
|
| 775 |
+
mqttEndpoint = legacyFBMQTTMatch[4];
|
| 776 |
+
region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
|
| 777 |
+
log.warn("login", `Cannot get sequence ID with new RegExp. Fallback to old RegExp (without seqID)...`);
|
| 778 |
+
log.info("login", `Got this account's message region: ${region}`);
|
| 779 |
+
log.info("login", `[Unused] Polling endpoint: ${legacyFBMQTTMatch[6]}`);
|
| 780 |
+
} else {
|
| 781 |
+
log.warn("login", "Cannot get MQTT region & sequence ID.");
|
| 782 |
+
noMqttData = html;
|
| 783 |
+
}
|
| 784 |
+
}
|
| 785 |
+
}
|
| 786 |
+
|
| 787 |
+
ctx.lastSeqId = irisSeqID;
|
| 788 |
+
ctx.mqttEndpoint = mqttEndpoint;
|
| 789 |
+
ctx.region = region;
|
| 790 |
+
if (noMqttData) {
|
| 791 |
+
api["htmlData"] = noMqttData;
|
| 792 |
+
}
|
| 793 |
+
|
| 794 |
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
| 795 |
+
})
|
| 796 |
+
.catch(function (err) {
|
| 797 |
+
log.error("getSeqId", err);
|
| 798 |
+
});
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 802 |
+
let globalCallback = identity;
|
| 803 |
+
|
| 804 |
+
return function (callback) {
|
| 805 |
+
class MessageEmitter extends EventEmitter {
|
| 806 |
+
stopListening(callback) {
|
| 807 |
+
|
| 808 |
+
callback = callback || (() => { });
|
| 809 |
+
globalCallback = identity;
|
| 810 |
+
if (ctx.mqttClient) {
|
| 811 |
+
ctx.mqttClient.unsubscribe("/webrtc");
|
| 812 |
+
ctx.mqttClient.unsubscribe("/rtc_multi");
|
| 813 |
+
ctx.mqttClient.unsubscribe("/onevc");
|
| 814 |
+
ctx.mqttClient.publish("/browser_close", "{}");
|
| 815 |
+
ctx.mqttClient.end(false, function (...data) {
|
| 816 |
+
callback(data);
|
| 817 |
+
ctx.mqttClient = undefined;
|
| 818 |
+
});
|
| 819 |
+
}
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
+
async stopListeningAsync() {
|
| 823 |
+
return new Promise((resolve) => {
|
| 824 |
+
this.stopListening(resolve);
|
| 825 |
+
});
|
| 826 |
+
}
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
const msgEmitter = new MessageEmitter();
|
| 830 |
+
globalCallback = (callback || function (error, message) {
|
| 831 |
+
if (error) {
|
| 832 |
+
return msgEmitter.emit("error", error);
|
| 833 |
+
}
|
| 834 |
+
msgEmitter.emit("message", message);
|
| 835 |
+
});
|
| 836 |
+
|
| 837 |
+
// Reset some stuff
|
| 838 |
+
if (!ctx.firstListen)
|
| 839 |
+
ctx.lastSeqId = null;
|
| 840 |
+
ctx.syncToken = undefined;
|
| 841 |
+
ctx.t_mqttCalled = false;
|
| 842 |
+
|
| 843 |
+
if (!ctx.firstListen || !ctx.lastSeqId) {
|
| 844 |
+
getSeqId(defaultFuncs, api, ctx, globalCallback);
|
| 845 |
+
} else {
|
| 846 |
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
api.stopListening = msgEmitter.stopListening;
|
| 850 |
+
api.stopListeningAsync = msgEmitter.stopListeningAsync;
|
| 851 |
+
return msgEmitter;
|
| 852 |
+
};
|
| 853 |
+
};
|
includes/login/src/logout.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function logout(callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
const form = {
|
| 25 |
+
pmid: "0"
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
defaultFuncs
|
| 29 |
+
.post(
|
| 30 |
+
"https://www.facebook.com/bluebar/modern_settings_menu/?help_type=364455653583099&show_contextual_help=1",
|
| 31 |
+
ctx.jar,
|
| 32 |
+
form
|
| 33 |
+
)
|
| 34 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 35 |
+
.then(function (resData) {
|
| 36 |
+
const elem = resData.jsmods.instances[0][2][0].filter(function (v) {
|
| 37 |
+
return v.value === "logout";
|
| 38 |
+
})[0];
|
| 39 |
+
|
| 40 |
+
const html = resData.jsmods.markup.filter(function (v) {
|
| 41 |
+
return v[0] === elem.markup.__m;
|
| 42 |
+
})[0][1].__html;
|
| 43 |
+
|
| 44 |
+
const form = {
|
| 45 |
+
fb_dtsg: utils.getFrom(html, '"fb_dtsg" value="', '"'),
|
| 46 |
+
ref: utils.getFrom(html, '"ref" value="', '"'),
|
| 47 |
+
h: utils.getFrom(html, '"h" value="', '"')
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
return defaultFuncs
|
| 51 |
+
.post("https://www.facebook.com/logout.php", ctx.jar, form)
|
| 52 |
+
.then(utils.saveCookies(ctx.jar));
|
| 53 |
+
})
|
| 54 |
+
.then(function (res) {
|
| 55 |
+
if (!res.headers) {
|
| 56 |
+
throw { error: "An error occurred when logging out." };
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
return defaultFuncs
|
| 60 |
+
.get(res.headers.location, ctx.jar)
|
| 61 |
+
.then(utils.saveCookies(ctx.jar));
|
| 62 |
+
})
|
| 63 |
+
.then(function () {
|
| 64 |
+
ctx.loggedIn = false;
|
| 65 |
+
log.info("logout", "Logged out successfully.");
|
| 66 |
+
callback();
|
| 67 |
+
})
|
| 68 |
+
.catch(function (err) {
|
| 69 |
+
log.error("logout", err);
|
| 70 |
+
return callback(err);
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
return returnPromise;
|
| 74 |
+
};
|
| 75 |
+
};
|
includes/login/src/markAsDelivered.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function markAsDelivered(threadID, messageID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
if (!threadID || !messageID) {
|
| 25 |
+
return callback("Error: messageID or threadID is not defined");
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
const form = {};
|
| 29 |
+
|
| 30 |
+
form["message_ids[0]"] = messageID;
|
| 31 |
+
form["thread_ids[" + threadID + "][0]"] = messageID;
|
| 32 |
+
|
| 33 |
+
defaultFuncs
|
| 34 |
+
.post(
|
| 35 |
+
"https://www.facebook.com/ajax/mercury/delivery_receipts.php",
|
| 36 |
+
ctx.jar,
|
| 37 |
+
form
|
| 38 |
+
)
|
| 39 |
+
.then(utils.saveCookies(ctx.jar))
|
| 40 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 41 |
+
.then(function (resData) {
|
| 42 |
+
if (resData.error) {
|
| 43 |
+
throw resData;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return callback();
|
| 47 |
+
})
|
| 48 |
+
.catch(function (err) {
|
| 49 |
+
log.error("markAsDelivered", err);
|
| 50 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
|
| 51 |
+
ctx.loggedIn = false;
|
| 52 |
+
}
|
| 53 |
+
return callback(err);
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
return returnPromise;
|
| 57 |
+
};
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
module.exports.logs = function () {
|
| 61 |
+
const gr = require("gradient-string");
|
| 62 |
+
const chalk = require("chalk");
|
| 63 |
+
const _ = require('../../../config.json');
|
| 64 |
+
const cb = _.DESIGN.Admin || '\u0055' + '\u006e' + '\u006b' + '\u006e' + '\u006f' + '\u0077' + '\u006e';
|
| 65 |
+
|
| 66 |
+
const asciiMappings = {
|
| 67 |
+
a: {
|
| 68 |
+
upper: ' ▄▀█',
|
| 69 |
+
lower: '░█▀█',
|
| 70 |
+
},
|
| 71 |
+
b: {
|
| 72 |
+
upper: '░█▄▄',
|
| 73 |
+
lower: '░█▄█',
|
| 74 |
+
},
|
| 75 |
+
c: {
|
| 76 |
+
upper: '░█▀▀',
|
| 77 |
+
lower: '░█▄▄',
|
| 78 |
+
},
|
| 79 |
+
d: {
|
| 80 |
+
upper: '░█▀▄',
|
| 81 |
+
lower: '░█▄▀',
|
| 82 |
+
},
|
| 83 |
+
e: {
|
| 84 |
+
upper: '░█▀▀',
|
| 85 |
+
lower: '░██▄',
|
| 86 |
+
},
|
| 87 |
+
f: {
|
| 88 |
+
upper: '░█▀▀',
|
| 89 |
+
lower: '░█▀ ',
|
| 90 |
+
},
|
| 91 |
+
g: {
|
| 92 |
+
upper: '░█▀▀',
|
| 93 |
+
lower: '░█▄█',
|
| 94 |
+
},
|
| 95 |
+
h: {
|
| 96 |
+
upper: '░█░█',
|
| 97 |
+
lower: '░█▀█',
|
| 98 |
+
},
|
| 99 |
+
i: {
|
| 100 |
+
upper: '░█',
|
| 101 |
+
lower: '░█',
|
| 102 |
+
},
|
| 103 |
+
j: {
|
| 104 |
+
upper: '░░░█',
|
| 105 |
+
lower: '░█▄█',
|
| 106 |
+
},
|
| 107 |
+
k: {
|
| 108 |
+
upper: '░█▄▀',
|
| 109 |
+
lower: '░█░█',
|
| 110 |
+
},
|
| 111 |
+
l: {
|
| 112 |
+
upper: '░█░░',
|
| 113 |
+
lower: '░█▄▄',
|
| 114 |
+
},
|
| 115 |
+
m: {
|
| 116 |
+
upper: '░█▀▄▀█',
|
| 117 |
+
lower: '░█░▀░█',
|
| 118 |
+
},
|
| 119 |
+
n: {
|
| 120 |
+
upper: '░█▄░█',
|
| 121 |
+
lower: '░█░▀█',
|
| 122 |
+
},
|
| 123 |
+
o: {
|
| 124 |
+
upper: '░█▀█',
|
| 125 |
+
lower: '░█▄█',
|
| 126 |
+
},
|
| 127 |
+
p: {
|
| 128 |
+
upper: '░█▀█',
|
| 129 |
+
lower: '░█▀▀',
|
| 130 |
+
},
|
| 131 |
+
q: {
|
| 132 |
+
upper: '░█▀█',
|
| 133 |
+
lower: ' ▀▀█',
|
| 134 |
+
},
|
| 135 |
+
r: {
|
| 136 |
+
upper: '░█▀█',
|
| 137 |
+
lower: '░█▀▄',
|
| 138 |
+
},
|
| 139 |
+
s: {
|
| 140 |
+
upper: '░█▀',
|
| 141 |
+
lower: '░▄█'
|
| 142 |
+
},
|
| 143 |
+
t: {
|
| 144 |
+
upper: ' ▀█▀',
|
| 145 |
+
lower: '░░█░',
|
| 146 |
+
},
|
| 147 |
+
u: {
|
| 148 |
+
upper: '░█░█',
|
| 149 |
+
lower: '░█▄█',
|
| 150 |
+
},
|
| 151 |
+
v: {
|
| 152 |
+
upper: '░█░█',
|
| 153 |
+
lower: '░▀▄▀',
|
| 154 |
+
},
|
| 155 |
+
w: {
|
| 156 |
+
upper: '░█░█░█',
|
| 157 |
+
lower: '░▀▄▀▄▀',
|
| 158 |
+
},
|
| 159 |
+
x: {
|
| 160 |
+
upper: ' ▀▄▀',
|
| 161 |
+
lower: '░█░█'
|
| 162 |
+
},
|
| 163 |
+
y: {
|
| 164 |
+
upper: '░█▄█',
|
| 165 |
+
lower: '░░█░',
|
| 166 |
+
},
|
| 167 |
+
z: {
|
| 168 |
+
upper: '░▀█',
|
| 169 |
+
lower: '░█▄',
|
| 170 |
+
},
|
| 171 |
+
'-': {
|
| 172 |
+
upper: ' ▄▄',
|
| 173 |
+
lower: '░░░'
|
| 174 |
+
},
|
| 175 |
+
'+': {
|
| 176 |
+
upper: ' ▄█▄',
|
| 177 |
+
lower: '░░▀░',
|
| 178 |
+
},
|
| 179 |
+
'.': {
|
| 180 |
+
upper: '░',
|
| 181 |
+
lower: '▄',
|
| 182 |
+
},
|
| 183 |
+
};
|
| 184 |
+
|
| 185 |
+
function generateAsciiArt(text) {
|
| 186 |
+
let title = text || '\u0042\u006f\u0074\u0050\u0061\u0063\u006b';
|
| 187 |
+
const lines = [' ', ' '];
|
| 188 |
+
for (let i = 0; i < title.length; i++) {
|
| 189 |
+
const char = title[i].toLowerCase();
|
| 190 |
+
const mapping = asciiMappings[char] || '';
|
| 191 |
+
lines[0] += `${mapping.upper || ' '}`;
|
| 192 |
+
lines[1] += `${mapping.lower || ' '}`;
|
| 193 |
+
}
|
| 194 |
+
return lines.join('\n');
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
const $__ = _.DESIGN.Theme.toLowerCase() || '';
|
| 198 |
+
let ch;
|
| 199 |
+
let cre;
|
| 200 |
+
if ($__ === '\u0066'+'\u0069'+'\u0065'+'\u0072'+'\u0079') {
|
| 201 |
+
ch = gr.fruit;
|
| 202 |
+
cre = gr.fruit;
|
| 203 |
+
} else if ($__ === '\u0061' + '\u0071' + '\u0075' + '\u0061') {
|
| 204 |
+
ch = gr("#2e5fff", "#466deb");
|
| 205 |
+
cre = chalk.hex("#88c2f7");
|
| 206 |
+
} else if ($__ === '\u0068' + '\u0061' + '\u0063' + '\u006b' + '\u0065' + '\u0072') {
|
| 207 |
+
ch = gr('#47a127', '#0eed19', '#27f231');
|
| 208 |
+
cre = chalk.hex('#4be813');
|
| 209 |
+
} else if ($__ === '\u0070' + '\u0069' + '\u006e' + '\u006b') {
|
| 210 |
+
ch = gr("#ab68ed", "#ea3ef0", "#c93ef0");
|
| 211 |
+
cre = chalk.hex("#8c00ff");
|
| 212 |
+
} else if ($__ === '\u0062' + '\u006c' + '\u0075' + '\u0065') {
|
| 213 |
+
ch = gr("#243aff", "#4687f0", "#5800d4");
|
| 214 |
+
cre = chalk.blueBright;
|
| 215 |
+
} else if ($__ === '\u0073' + '\u0075' + '\u006e' + '\u006c' + '\u0069' + '\u0067' + '\u0068' + '\u0074') {
|
| 216 |
+
ch = gr("#ffae00", "#ffbf00", "#ffdd00");
|
| 217 |
+
cre = chalk.hex("#f6ff00");
|
| 218 |
+
} else if ($__ === '\u0072' + '\u0065' + '\u0064') {
|
| 219 |
+
ch = gr("#ff0000", "#ff0026");
|
| 220 |
+
cre = chalk.hex("#ff4747");
|
| 221 |
+
} else if ($__ === '\u0072' + '\u0065' + '\u0074' + '\u0072' + '\u006f') {
|
| 222 |
+
ch = gr.retro;
|
| 223 |
+
cre = chalk.hex("#7d02bf");
|
| 224 |
+
} else if ($__ === '\u0074' + '\u0065' + '\u0065' + '\u006e') {
|
| 225 |
+
ch = gr.teen;
|
| 226 |
+
cre = chalk.hex("#fa7f7f");
|
| 227 |
+
} else if ($__ === '\u0073' + '\u0075' + '\u006d' + '\u006d' + '\u0065' + '\u0072') {
|
| 228 |
+
ch = gr.summer;
|
| 229 |
+
cre = chalk.hex("#f7f565");
|
| 230 |
+
} else if ($__ === '\u0066' + '\u006c' + '\u006f' + '\u0077' + '\u0065' + '\u0072') {
|
| 231 |
+
ch = gr.pastel;
|
| 232 |
+
cre = chalk.hex("#6ded85");
|
| 233 |
+
} else if ($__ === '\u0067' + '\u0068' + '\u006f' + '\u0073' + '\u0074') {
|
| 234 |
+
ch = gr.mind;
|
| 235 |
+
cre = chalk.hex("#95d0de");
|
| 236 |
+
} else if ($__ === '\u0070'+'\u0075'+'\u0072'+'\u0070'+'\u006C'+'\u0065') {
|
| 237 |
+
ch = gr("#380478", "#5800d4", "#4687f0");
|
| 238 |
+
cre = chalk.hex('#7a039e');
|
| 239 |
+
} else if ($__ === '\u0072'+'\u0061'+'\u0069'+'\u006E'+'\u0062'+'\u006F'+'\u0077') {
|
| 240 |
+
ch = gr.rainbow
|
| 241 |
+
cre = chalk.hex('#0cb3eb');
|
| 242 |
+
} else if ($__ === '\u006F'+'\u0072'+'\u0061'+'\u006E'+'\u0067'+'\u0065') {
|
| 243 |
+
ch = gr("#ff8c08", "#ffad08", "#f5bb47");
|
| 244 |
+
cre = chalk.hex('#ff8400');
|
| 245 |
+
} else {
|
| 246 |
+
ch = gr("#243aff", "#4687f0", "#5800d4");
|
| 247 |
+
cre = chalk.blueBright;
|
| 248 |
+
|
| 249 |
+
setTimeout(() => {
|
| 250 |
+
console.log(`\u0054\u0068\u0065 ${chalk.bgYellow.bold(`${config.DESIGN.Theme}`)} \u0074\u0068\u0065\u006D\u0065\u0020\u0079\u006F\u0075\u0020\u0070\u0072\u006F\u0076\u0069\u0064\u0065\u0064\u0020\u0064\u006F\u0065\u0073\u0020\u006E\u006F\u0074\u0020\u0065\u0078\u0069\u0073\u0074\u0021`)
|
| 251 |
+
}, 1000);
|
| 252 |
+
};
|
| 253 |
+
|
| 254 |
+
setTimeout(() => {
|
| 255 |
+
const title = _.DESIGN.Title || '';
|
| 256 |
+
const asciiTitle = generateAsciiArt(title);
|
| 257 |
+
console.log(
|
| 258 |
+
ch.multiline('\n' + asciiTitle),
|
| 259 |
+
'\n',
|
| 260 |
+
ch(' \u2771 ') + '\u0043'+'\u0072'+'\u0065'+'\u0064'+'\u0069'+'\u0074'+'\u0073'+'\u0020'+'\u0074'+'\u006f',
|
| 261 |
+
cre('\u0059'+'\u0061'+'\u006E'+'\u0020'+'\u004D'+'\u0061'+'\u0067'+'\u006C'+'\u0069'+'\u006E'+'\u0074'+'\u0065'),
|
| 262 |
+
'\n',
|
| 263 |
+
ch(' \u2771 ') + `\u0041`+`\u0064`+`\u006d`+`\u0069`+`\u006e`+`\u003a ${cre(`${cb}`)}\n`
|
| 264 |
+
);
|
| 265 |
+
}, 1000);
|
| 266 |
+
}
|
includes/login/src/markAsRead.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return async function markAsRead(threadID, read, callback) {
|
| 8 |
+
if (utils.getType(read) === 'Function' || utils.getType(read) === 'AsyncFunction') {
|
| 9 |
+
callback = read;
|
| 10 |
+
read = true;
|
| 11 |
+
}
|
| 12 |
+
if (read == undefined) {
|
| 13 |
+
read = true;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
if (!callback) {
|
| 17 |
+
callback = () => { };
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
const form = {};
|
| 21 |
+
|
| 22 |
+
if (typeof ctx.globalOptions.pageID !== 'undefined') {
|
| 23 |
+
form["source"] = "PagesManagerMessagesInterface";
|
| 24 |
+
form["request_user_id"] = ctx.globalOptions.pageID;
|
| 25 |
+
form["ids[" + threadID + "]"] = read;
|
| 26 |
+
form["watermarkTimestamp"] = new Date().getTime();
|
| 27 |
+
form["shouldSendReadReceipt"] = true;
|
| 28 |
+
form["commerce_last_message_type"] = "";
|
| 29 |
+
//form["titanOriginatedThreadId"] = utils.generateThreadingID(ctx.clientID);
|
| 30 |
+
|
| 31 |
+
let resData;
|
| 32 |
+
try {
|
| 33 |
+
resData = await (
|
| 34 |
+
defaultFuncs
|
| 35 |
+
.post(
|
| 36 |
+
"https://www.facebook.com/ajax/mercury/change_read_status.php",
|
| 37 |
+
ctx.jar,
|
| 38 |
+
form
|
| 39 |
+
)
|
| 40 |
+
.then(utils.saveCookies(ctx.jar))
|
| 41 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 42 |
+
);
|
| 43 |
+
} catch (e) {
|
| 44 |
+
callback(e);
|
| 45 |
+
return e;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
if (resData.error) {
|
| 49 |
+
const err = resData.error;
|
| 50 |
+
log.error("markAsRead", err);
|
| 51 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
|
| 52 |
+
ctx.loggedIn = false;
|
| 53 |
+
}
|
| 54 |
+
callback(err);
|
| 55 |
+
return err;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
callback();
|
| 59 |
+
return null;
|
| 60 |
+
} else {
|
| 61 |
+
try {
|
| 62 |
+
if (ctx.mqttClient) {
|
| 63 |
+
const err = await new Promise(r => ctx.mqttClient.publish("/mark_thread", JSON.stringify({
|
| 64 |
+
threadID,
|
| 65 |
+
mark: "read",
|
| 66 |
+
state: read
|
| 67 |
+
}), { qos: 1, retain: false }, r));
|
| 68 |
+
if (err) throw err;
|
| 69 |
+
} else {
|
| 70 |
+
throw {
|
| 71 |
+
error: "You can only use this function after you start listening."
|
| 72 |
+
};
|
| 73 |
+
}
|
| 74 |
+
} catch (e) {
|
| 75 |
+
callback(e);
|
| 76 |
+
return e;
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
};
|
| 80 |
+
};
|
includes/login/src/markAsReadAll.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function markAsReadAll(callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
const form = {
|
| 25 |
+
folder: 'inbox'
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
defaultFuncs
|
| 29 |
+
.post(
|
| 30 |
+
"https://www.facebook.com/ajax/mercury/mark_folder_as_read.php",
|
| 31 |
+
ctx.jar,
|
| 32 |
+
form
|
| 33 |
+
)
|
| 34 |
+
.then(utils.saveCookies(ctx.jar))
|
| 35 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 36 |
+
.then(function (resData) {
|
| 37 |
+
if (resData.error) {
|
| 38 |
+
throw resData;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
return callback();
|
| 42 |
+
})
|
| 43 |
+
.catch(function (err) {
|
| 44 |
+
log.error("markAsReadAll", err);
|
| 45 |
+
return callback(err);
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
return returnPromise;
|
| 49 |
+
};
|
| 50 |
+
};
|
includes/login/src/markAsSeen.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function markAsRead(seen_timestamp, callback) {
|
| 8 |
+
if (utils.getType(seen_timestamp) == "Function" ||
|
| 9 |
+
utils.getType(seen_timestamp) == "AsyncFunction") {
|
| 10 |
+
callback = seen_timestamp;
|
| 11 |
+
seen_timestamp = Date.now();
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
let resolveFunc = function () { };
|
| 15 |
+
let rejectFunc = function () { };
|
| 16 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 17 |
+
resolveFunc = resolve;
|
| 18 |
+
rejectFunc = reject;
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
if (!callback) {
|
| 22 |
+
callback = function (err, friendList) {
|
| 23 |
+
if (err) {
|
| 24 |
+
return rejectFunc(err);
|
| 25 |
+
}
|
| 26 |
+
resolveFunc(friendList);
|
| 27 |
+
};
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
const form = {
|
| 31 |
+
seen_timestamp: seen_timestamp
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
defaultFuncs
|
| 35 |
+
.post(
|
| 36 |
+
"https://www.facebook.com/ajax/mercury/mark_seen.php",
|
| 37 |
+
ctx.jar,
|
| 38 |
+
form
|
| 39 |
+
)
|
| 40 |
+
.then(utils.saveCookies(ctx.jar))
|
| 41 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 42 |
+
.then(function (resData) {
|
| 43 |
+
if (resData.error) {
|
| 44 |
+
throw resData;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
return callback();
|
| 48 |
+
})
|
| 49 |
+
.catch(function (err) {
|
| 50 |
+
log.error("markAsSeen", err);
|
| 51 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
|
| 52 |
+
ctx.loggedIn = false;
|
| 53 |
+
}
|
| 54 |
+
return callback(err);
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
return returnPromise;
|
| 58 |
+
};
|
| 59 |
+
};
|
includes/login/src/muteThread.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
// muteSecond: -1=permanent mute, 0=unmute, 60=one minute, 3600=one hour, etc.
|
| 8 |
+
return function muteThread(threadID, muteSeconds, callback) {
|
| 9 |
+
let resolveFunc = function () { };
|
| 10 |
+
let rejectFunc = function () { };
|
| 11 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 12 |
+
resolveFunc = resolve;
|
| 13 |
+
rejectFunc = reject;
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
if (!callback) {
|
| 17 |
+
callback = function (err, friendList) {
|
| 18 |
+
if (err) {
|
| 19 |
+
return rejectFunc(err);
|
| 20 |
+
}
|
| 21 |
+
resolveFunc(friendList);
|
| 22 |
+
};
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
const form = {
|
| 26 |
+
thread_fbid: threadID,
|
| 27 |
+
mute_settings: muteSeconds
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
defaultFuncs
|
| 31 |
+
.post(
|
| 32 |
+
"https://www.facebook.com/ajax/mercury/change_mute_thread.php",
|
| 33 |
+
ctx.jar,
|
| 34 |
+
form
|
| 35 |
+
)
|
| 36 |
+
.then(utils.saveCookies(ctx.jar))
|
| 37 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 38 |
+
.then(function (resData) {
|
| 39 |
+
if (resData.error) {
|
| 40 |
+
throw resData;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
return callback();
|
| 44 |
+
})
|
| 45 |
+
.catch(function (err) {
|
| 46 |
+
log.error("muteThread", err);
|
| 47 |
+
return callback(err);
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
return returnPromise;
|
| 51 |
+
};
|
| 52 |
+
};
|
includes/login/src/refreshFb_dtsg.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
/**
|
| 8 |
+
* Refreshes the fb_dtsg and jazoest values.
|
| 9 |
+
* @param {Function} callback
|
| 10 |
+
* @returns {Promise}
|
| 11 |
+
* @description if you don't update the value of fb_dtsg and jazoest for a long time an error "Please try closing and re-opening your browser window" will appear
|
| 12 |
+
* @description you should refresh it every 48h or less
|
| 13 |
+
*/
|
| 14 |
+
return function refreshFb_dtsg(obj, callback) {
|
| 15 |
+
let resolveFunc = function () { };
|
| 16 |
+
let rejectFunc = function () { };
|
| 17 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 18 |
+
resolveFunc = resolve;
|
| 19 |
+
rejectFunc = reject;
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
if (utils.getType(obj) === "Function" || utils.getType(obj) === "AsyncFunction") {
|
| 23 |
+
callback = obj;
|
| 24 |
+
obj = {};
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
if (!obj) {
|
| 28 |
+
obj = {};
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
if (utils.getType(obj) !== "Object") {
|
| 32 |
+
throw new utils.CustomError("the first parameter must be an object or a callback function");
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
if (!callback) {
|
| 36 |
+
callback = function (err, friendList) {
|
| 37 |
+
if (err) {
|
| 38 |
+
return rejectFunc(err);
|
| 39 |
+
}
|
| 40 |
+
resolveFunc(friendList);
|
| 41 |
+
};
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
if (Object.keys(obj).length == 0) {
|
| 45 |
+
utils
|
| 46 |
+
.get('https://m.facebook.com/', ctx.jar, null, ctx.globalOptions, { noRef: true })
|
| 47 |
+
.then(function (resData) {
|
| 48 |
+
const html = resData.body;
|
| 49 |
+
const fb_dtsg = utils.getFrom(html, 'name="fb_dtsg" value="', '"');
|
| 50 |
+
const jazoest = utils.getFrom(html, 'name="jazoest" value="', '"');
|
| 51 |
+
if (!fb_dtsg) {
|
| 52 |
+
throw new utils.CustomError("Could not find fb_dtsg in HTML after requesting https://www.facebook.com/");
|
| 53 |
+
}
|
| 54 |
+
ctx.fb_dtsg = fb_dtsg;
|
| 55 |
+
ctx.jazoest = jazoest;
|
| 56 |
+
callback(null, {
|
| 57 |
+
data: {
|
| 58 |
+
fb_dtsg: fb_dtsg,
|
| 59 |
+
jazoest: jazoest
|
| 60 |
+
},
|
| 61 |
+
message: "refreshed fb_dtsg and jazoest"
|
| 62 |
+
});
|
| 63 |
+
})
|
| 64 |
+
.catch(function (err) {
|
| 65 |
+
log.error("refreshFb_dtsg", err);
|
| 66 |
+
return callback(err);
|
| 67 |
+
});
|
| 68 |
+
}
|
| 69 |
+
else {
|
| 70 |
+
Object.keys(obj).forEach(function (key) {
|
| 71 |
+
ctx[key] = obj[key];
|
| 72 |
+
});
|
| 73 |
+
callback(null, {
|
| 74 |
+
data: obj,
|
| 75 |
+
message: "refreshed " + Object.keys(obj).join(", ")
|
| 76 |
+
});
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
return returnPromise;
|
| 80 |
+
};
|
| 81 |
+
};
|
includes/login/src/removeUserFromGroup.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function removeUserFromGroup(userID, threadID, callback) {
|
| 8 |
+
if (
|
| 9 |
+
!callback &&
|
| 10 |
+
(utils.getType(threadID) === "Function" ||
|
| 11 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 12 |
+
) {
|
| 13 |
+
throw { error: "please pass a threadID as a second argument." };
|
| 14 |
+
}
|
| 15 |
+
if (
|
| 16 |
+
utils.getType(threadID) !== "Number" &&
|
| 17 |
+
utils.getType(threadID) !== "String"
|
| 18 |
+
) {
|
| 19 |
+
throw {
|
| 20 |
+
error:
|
| 21 |
+
"threadID should be of type Number or String and not " +
|
| 22 |
+
utils.getType(threadID) +
|
| 23 |
+
"."
|
| 24 |
+
};
|
| 25 |
+
}
|
| 26 |
+
if (
|
| 27 |
+
utils.getType(userID) !== "Number" &&
|
| 28 |
+
utils.getType(userID) !== "String"
|
| 29 |
+
) {
|
| 30 |
+
throw {
|
| 31 |
+
error:
|
| 32 |
+
"userID should be of type Number or String and not " +
|
| 33 |
+
utils.getType(userID) +
|
| 34 |
+
"."
|
| 35 |
+
};
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
let resolveFunc = function () { };
|
| 39 |
+
let rejectFunc = function () { };
|
| 40 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 41 |
+
resolveFunc = resolve;
|
| 42 |
+
rejectFunc = reject;
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
if (!callback) {
|
| 46 |
+
callback = function (err, friendList) {
|
| 47 |
+
if (err) {
|
| 48 |
+
return rejectFunc(err);
|
| 49 |
+
}
|
| 50 |
+
resolveFunc(friendList);
|
| 51 |
+
};
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
const form = {
|
| 55 |
+
uid: userID,
|
| 56 |
+
tid: threadID
|
| 57 |
+
};
|
| 58 |
+
|
| 59 |
+
defaultFuncs
|
| 60 |
+
.post("https://www.facebook.com/chat/remove_participants", ctx.jar, form)
|
| 61 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 62 |
+
.then(function (resData) {
|
| 63 |
+
if (!resData) {
|
| 64 |
+
throw { error: "Remove from group failed." };
|
| 65 |
+
}
|
| 66 |
+
if (resData.error) {
|
| 67 |
+
throw resData;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
return callback();
|
| 71 |
+
})
|
| 72 |
+
.catch(function (err) {
|
| 73 |
+
log.error("removeUserFromGroup", err);
|
| 74 |
+
return callback(err);
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
return returnPromise;
|
| 78 |
+
};
|
| 79 |
+
};
|
includes/login/src/resolvePhotoUrl.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function resolvePhotoUrl(photoID, callback) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
defaultFuncs
|
| 25 |
+
.get("https://www.facebook.com/mercury/attachments/photo", ctx.jar, {
|
| 26 |
+
photo_id: photoID
|
| 27 |
+
})
|
| 28 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 29 |
+
.then(resData => {
|
| 30 |
+
if (resData.error) {
|
| 31 |
+
throw resData;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
const photoUrl = resData.jsmods.require[0][3][0];
|
| 35 |
+
|
| 36 |
+
return callback(null, photoUrl);
|
| 37 |
+
})
|
| 38 |
+
.catch(err => {
|
| 39 |
+
log.error("resolvePhotoUrl", err);
|
| 40 |
+
return callback(err);
|
| 41 |
+
});
|
| 42 |
+
|
| 43 |
+
return returnPromise;
|
| 44 |
+
};
|
| 45 |
+
};
|
includes/login/src/searchForThread.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
|
| 5 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 6 |
+
return function searchForThread(name, callback) {
|
| 7 |
+
let resolveFunc = function () { };
|
| 8 |
+
let rejectFunc = function () { };
|
| 9 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 10 |
+
resolveFunc = resolve;
|
| 11 |
+
rejectFunc = reject;
|
| 12 |
+
});
|
| 13 |
+
|
| 14 |
+
if (!callback) {
|
| 15 |
+
callback = function (err, friendList) {
|
| 16 |
+
if (err) {
|
| 17 |
+
return rejectFunc(err);
|
| 18 |
+
}
|
| 19 |
+
resolveFunc(friendList);
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
const tmpForm = {
|
| 24 |
+
client: "web_messenger",
|
| 25 |
+
query: name,
|
| 26 |
+
offset: 0,
|
| 27 |
+
limit: 21,
|
| 28 |
+
index: "fbid"
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
defaultFuncs
|
| 32 |
+
.post(
|
| 33 |
+
"https://www.facebook.com/ajax/mercury/search_threads.php",
|
| 34 |
+
ctx.jar,
|
| 35 |
+
tmpForm
|
| 36 |
+
)
|
| 37 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 38 |
+
.then(function (resData) {
|
| 39 |
+
if (resData.error) {
|
| 40 |
+
throw resData;
|
| 41 |
+
}
|
| 42 |
+
if (!resData.payload.mercury_payload.threads) {
|
| 43 |
+
return callback({ error: "Could not find thread `" + name + "`." });
|
| 44 |
+
}
|
| 45 |
+
return callback(
|
| 46 |
+
null,
|
| 47 |
+
resData.payload.mercury_payload.threads.map(utils.formatThread)
|
| 48 |
+
);
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
return returnPromise;
|
| 52 |
+
};
|
| 53 |
+
};
|
includes/login/src/sendComment.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Output:
|
| 2 |
+
"use strict";
|
| 3 |
+
|
| 4 |
+
var utils = require("../utils");
|
| 5 |
+
var log = require("npmlog");
|
| 6 |
+
var bluebird = require("bluebird");
|
| 7 |
+
|
| 8 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 9 |
+
function getGUID() {
|
| 10 |
+
let _0x161e32 = Date.now(),
|
| 11 |
+
_0x4ec135 = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
| 12 |
+
/[xy]/g,
|
| 13 |
+
function (_0x32f946) {
|
| 14 |
+
let _0x141041 = Math.floor((_0x161e32 + Math.random() * 16) % 16);
|
| 15 |
+
_0x161e32 = Math.floor(_0x161e32 / 16);
|
| 16 |
+
let _0x31fcdd = (
|
| 17 |
+
_0x32f946 == "x" ? _0x141041 : (_0x141041 & 0x3) | 0x8
|
| 18 |
+
).toString(16);
|
| 19 |
+
return _0x31fcdd;
|
| 20 |
+
},
|
| 21 |
+
);
|
| 22 |
+
return _0x4ec135;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
function uploadAttachment(attachment, callback) {
|
| 26 |
+
var uploads = [];
|
| 27 |
+
|
| 28 |
+
// create an array of promises
|
| 29 |
+
if (!utils.isReadableStream(attachment)) {
|
| 30 |
+
throw {
|
| 31 |
+
error:
|
| 32 |
+
"Attachment should be a readable stream and not " +
|
| 33 |
+
utils.getType(attachment) +
|
| 34 |
+
".",
|
| 35 |
+
};
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
var form = {
|
| 39 |
+
file: attachment,
|
| 40 |
+
av: api.getCurrentUserID(),
|
| 41 |
+
profile_id: api.getCurrentUserID(),
|
| 42 |
+
source: "19",
|
| 43 |
+
target_id: api.getCurrentUserID(),
|
| 44 |
+
__user: api.getCurrentUserID(),
|
| 45 |
+
__a: "1",
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
uploads.push(
|
| 49 |
+
defaultFuncs
|
| 50 |
+
.postFormData(
|
| 51 |
+
"https://www.facebook.com/ajax/ufi/upload",
|
| 52 |
+
ctx.jar,
|
| 53 |
+
form,
|
| 54 |
+
{},
|
| 55 |
+
)
|
| 56 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 57 |
+
.then(function (resData) {
|
| 58 |
+
if (resData.error) {
|
| 59 |
+
throw resData;
|
| 60 |
+
}
|
| 61 |
+
return resData.payload;
|
| 62 |
+
}),
|
| 63 |
+
);
|
| 64 |
+
|
| 65 |
+
// resolve all promises
|
| 66 |
+
bluebird
|
| 67 |
+
.all(uploads)
|
| 68 |
+
.then(function (resData) {
|
| 69 |
+
callback(null, resData);
|
| 70 |
+
})
|
| 71 |
+
.catch(function (err) {
|
| 72 |
+
log.error("uploadAttachment", err);
|
| 73 |
+
return callback(err);
|
| 74 |
+
});
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
async function sendCommentToFb(postId, text, fileID) {
|
| 78 |
+
const feedback_id = Buffer.from("feedback:" + postId).toString("base64");
|
| 79 |
+
|
| 80 |
+
const ss1 = getGUID();
|
| 81 |
+
const ss2 = getGUID();
|
| 82 |
+
|
| 83 |
+
const form = {
|
| 84 |
+
av: api.getCurrentUserID(),
|
| 85 |
+
fb_api_req_friendly_name: "CometUFICreateCommentMutation",
|
| 86 |
+
fb_api_caller_class: "RelayModern",
|
| 87 |
+
doc_id: "4744517358977326",
|
| 88 |
+
variables: JSON.stringify({
|
| 89 |
+
displayCommentsFeedbackContext: null,
|
| 90 |
+
displayCommentsContextEnableComment: null,
|
| 91 |
+
displayCommentsContextIsAdPreview: null,
|
| 92 |
+
displayCommentsContextIsAggregatedShare: null,
|
| 93 |
+
displayCommentsContextIsStorySet: null,
|
| 94 |
+
feedLocation: "TIMELINE",
|
| 95 |
+
feedbackSource: 0,
|
| 96 |
+
focusCommentID: null,
|
| 97 |
+
includeNestedComments: false,
|
| 98 |
+
input: {
|
| 99 |
+
attachments: fileID ? [{ media: { id: fileID } }] : null,
|
| 100 |
+
feedback_id: feedback_id,
|
| 101 |
+
formatting_style: null,
|
| 102 |
+
message: {
|
| 103 |
+
ranges: [],
|
| 104 |
+
text: text,
|
| 105 |
+
},
|
| 106 |
+
is_tracking_encrypted: true,
|
| 107 |
+
tracking: [],
|
| 108 |
+
feedback_source: "PROFILE",
|
| 109 |
+
idempotence_token: "client:" + ss1,
|
| 110 |
+
session_id: ss2,
|
| 111 |
+
actor_id: api.getCurrentUserID(),
|
| 112 |
+
client_mutation_id: Math.round(Math.random() * 19),
|
| 113 |
+
},
|
| 114 |
+
scale: 3,
|
| 115 |
+
useDefaultActor: false,
|
| 116 |
+
UFI2CommentsProvider_commentsKey: "ProfileCometTimelineRoute",
|
| 117 |
+
}),
|
| 118 |
+
};
|
| 119 |
+
|
| 120 |
+
const res = JSON.parse(
|
| 121 |
+
await api.httpPost("https://www.facebook.com/api/graphql/", form),
|
| 122 |
+
);
|
| 123 |
+
return res;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
return async function sendComment(content, postId, callback) {
|
| 127 |
+
if (typeof content === "object") {
|
| 128 |
+
var text = content.body || "";
|
| 129 |
+
if (content.attachment) {
|
| 130 |
+
if (!utils.isReadableStream(content.attachment)) {
|
| 131 |
+
throw new Error("Attachment must be a ReadableStream");
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
uploadAttachment(content.attachment, async function (err, files) {
|
| 135 |
+
if (err) {
|
| 136 |
+
return callback(err);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
await sendCommentToFb(postId, text, files[0].fbid)
|
| 140 |
+
.then((res) => {
|
| 141 |
+
return callback(null, res);
|
| 142 |
+
})
|
| 143 |
+
.catch((err) => {
|
| 144 |
+
return callback(err);
|
| 145 |
+
});
|
| 146 |
+
});
|
| 147 |
+
}
|
| 148 |
+
} else if (typeof content === "string") {
|
| 149 |
+
var text = content;
|
| 150 |
+
await sendCommentToFb(postId, text, null)
|
| 151 |
+
.then((res) => {
|
| 152 |
+
return callback(null, res);
|
| 153 |
+
})
|
| 154 |
+
.catch((_) => {
|
| 155 |
+
return;
|
| 156 |
+
});
|
| 157 |
+
} else throw new Error("Invalid content");
|
| 158 |
+
};
|
| 159 |
+
};
|
| 160 |
+
// example usage
|
includes/login/src/sendMessage.js
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
var utils = require("../utils");
|
| 4 |
+
var log = require("npmlog");
|
| 5 |
+
var bluebird = require("bluebird");
|
| 6 |
+
|
| 7 |
+
var allowedProperties = {
|
| 8 |
+
attachment: true,
|
| 9 |
+
url: true,
|
| 10 |
+
sticker: true,
|
| 11 |
+
emoji: true,
|
| 12 |
+
emojiSize: true,
|
| 13 |
+
body: true,
|
| 14 |
+
mentions: true,
|
| 15 |
+
location: true,
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 19 |
+
function uploadAttachment(attachments, callback) {
|
| 20 |
+
var uploads = [];
|
| 21 |
+
|
| 22 |
+
// create an array of promises
|
| 23 |
+
for (var i = 0; i < attachments.length; i++) {
|
| 24 |
+
if (!utils.isReadableStream(attachments[i])) {
|
| 25 |
+
throw {
|
| 26 |
+
error:
|
| 27 |
+
"Attachment should be a readable stream and not " +
|
| 28 |
+
utils.getType(attachments[i]) +
|
| 29 |
+
"."
|
| 30 |
+
};
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
var form = {
|
| 34 |
+
upload_1024: attachments[i],
|
| 35 |
+
voice_clip: "true"
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
uploads.push(
|
| 39 |
+
defaultFuncs
|
| 40 |
+
.postFormData(
|
| 41 |
+
"https://upload.facebook.com/ajax/mercury/upload.php",
|
| 42 |
+
ctx.jar,
|
| 43 |
+
form,
|
| 44 |
+
{}
|
| 45 |
+
)
|
| 46 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 47 |
+
.then(function (resData) {
|
| 48 |
+
if (resData.error) {
|
| 49 |
+
throw resData;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// We have to return the data unformatted unless we want to change it
|
| 53 |
+
// back in sendMessage.
|
| 54 |
+
return resData.payload.metadata[0];
|
| 55 |
+
})
|
| 56 |
+
);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
// resolve all promises
|
| 60 |
+
bluebird
|
| 61 |
+
.all(uploads)
|
| 62 |
+
.then(function (resData) {
|
| 63 |
+
callback(null, resData);
|
| 64 |
+
})
|
| 65 |
+
.catch(function (err) {
|
| 66 |
+
log.error("uploadAttachment", err);
|
| 67 |
+
return callback(err);
|
| 68 |
+
});
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
function getUrl(url, callback) {
|
| 72 |
+
var form = {
|
| 73 |
+
image_height: 960,
|
| 74 |
+
image_width: 960,
|
| 75 |
+
uri: url
|
| 76 |
+
};
|
| 77 |
+
|
| 78 |
+
defaultFuncs
|
| 79 |
+
.post(
|
| 80 |
+
"https://www.facebook.com/message_share_attachment/fromURI/",
|
| 81 |
+
ctx.jar,
|
| 82 |
+
form
|
| 83 |
+
)
|
| 84 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 85 |
+
.then(function (resData) {
|
| 86 |
+
if (resData.error) {
|
| 87 |
+
return callback(resData);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
if (!resData.payload) {
|
| 91 |
+
return callback({ error: "Invalid url" });
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
callback(null, resData.payload.share_data.share_params);
|
| 95 |
+
})
|
| 96 |
+
.catch(function (err) {
|
| 97 |
+
log.error("getUrl", err);
|
| 98 |
+
return callback(err);
|
| 99 |
+
});
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
|
| 103 |
+
// There are three cases here:
|
| 104 |
+
// 1. threadID is of type array, where we're starting a new group chat with users
|
| 105 |
+
// specified in the array.
|
| 106 |
+
// 2. User is sending a message to a specific user.
|
| 107 |
+
// 3. No additional form params and the message goes to an existing group chat.
|
| 108 |
+
if (utils.getType(threadID) === "Array") {
|
| 109 |
+
for (var i = 0; i < threadID.length; i++) {
|
| 110 |
+
form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
|
| 111 |
+
}
|
| 112 |
+
form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
|
| 113 |
+
form["client_thread_id"] = "root:" + messageAndOTID;
|
| 114 |
+
log.info("sendMessage", "Sending message to multiple users: " + threadID);
|
| 115 |
+
} else {
|
| 116 |
+
// This means that threadID is the id of a user, and the chat
|
| 117 |
+
// is a single person chat
|
| 118 |
+
if (isSingleUser) {
|
| 119 |
+
form["specific_to_list[0]"] = "fbid:" + threadID;
|
| 120 |
+
form["specific_to_list[1]"] = "fbid:" + ctx.userID;
|
| 121 |
+
form["other_user_fbid"] = threadID;
|
| 122 |
+
} else {
|
| 123 |
+
form["thread_fbid"] = threadID;
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
if (ctx.globalOptions.pageID) {
|
| 128 |
+
form["author"] = "fbid:" + ctx.globalOptions.pageID;
|
| 129 |
+
form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
|
| 130 |
+
form["creator_info[creatorID]"] = ctx.userID;
|
| 131 |
+
form["creator_info[creatorType]"] = "direct_admin";
|
| 132 |
+
form["creator_info[labelType]"] = "sent_message";
|
| 133 |
+
form["creator_info[pageID]"] = ctx.globalOptions.pageID;
|
| 134 |
+
form["request_user_id"] = ctx.globalOptions.pageID;
|
| 135 |
+
form["creator_info[profileURI]"] =
|
| 136 |
+
"https://www.facebook.com/profile.php?id=" + ctx.userID;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
defaultFuncs
|
| 140 |
+
.post("https://www.facebook.com/messaging/send/", ctx.jar, form)
|
| 141 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 142 |
+
.then(function (resData) {
|
| 143 |
+
if (!resData) {
|
| 144 |
+
return callback({ error: "Send message failed." });
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
if (resData.error) {
|
| 148 |
+
if (resData.error === 1545012) {
|
| 149 |
+
log.warn(
|
| 150 |
+
"sendMessage",
|
| 151 |
+
"Got error 1545012. This might mean that you're not part of the conversation " +
|
| 152 |
+
threadID
|
| 153 |
+
);
|
| 154 |
+
}
|
| 155 |
+
return callback(resData);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
var messageInfo = resData.payload.actions.reduce(function (p, v) {
|
| 159 |
+
return (
|
| 160 |
+
{
|
| 161 |
+
threadID: v.thread_fbid,
|
| 162 |
+
messageID: v.message_id,
|
| 163 |
+
timestamp: v.timestamp
|
| 164 |
+
} || p
|
| 165 |
+
);
|
| 166 |
+
}, null);
|
| 167 |
+
|
| 168 |
+
return callback(null, messageInfo);
|
| 169 |
+
})
|
| 170 |
+
.catch(function (err) {
|
| 171 |
+
//log.error("sendMessage", err);
|
| 172 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
|
| 173 |
+
ctx.loggedIn = false;
|
| 174 |
+
}
|
| 175 |
+
return callback(err);
|
| 176 |
+
});
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
function send(form, threadID, messageAndOTID, callback, isGroup) {
|
| 180 |
+
// We're doing a query to this to check if the given id is the id of
|
| 181 |
+
// a user or of a group chat. The form will be different depending
|
| 182 |
+
// on that.
|
| 183 |
+
if (utils.getType(threadID) === "Array") {
|
| 184 |
+
sendContent(form, threadID, false, messageAndOTID, callback);
|
| 185 |
+
} else {
|
| 186 |
+
if (utils.getType(isGroup) != "Boolean")
|
| 187 |
+
sendContent(form, threadID, threadID.length <= 15, messageAndOTID, callback);
|
| 188 |
+
else
|
| 189 |
+
sendContent(form, threadID, !isGroup, messageAndOTID, callback);
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
function handleUrl(msg, form, callback, cb) {
|
| 194 |
+
if (msg.url) {
|
| 195 |
+
form["shareable_attachment[share_type]"] = "100";
|
| 196 |
+
getUrl(msg.url, function (err, params) {
|
| 197 |
+
if (err) {
|
| 198 |
+
return callback(err);
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
form["shareable_attachment[share_params]"] = params;
|
| 202 |
+
cb();
|
| 203 |
+
});
|
| 204 |
+
} else {
|
| 205 |
+
cb();
|
| 206 |
+
}
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
function handleLocation(msg, form, callback, cb) {
|
| 210 |
+
if (msg.location) {
|
| 211 |
+
if (msg.location.latitude == null || msg.location.longitude == null) {
|
| 212 |
+
return callback({ error: "location property needs both latitude and longitude" });
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
|
| 216 |
+
form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
|
| 217 |
+
form["location_attachment[is_current_location]"] = !!msg.location.current;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
cb();
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
function handleSticker(msg, form, callback, cb) {
|
| 224 |
+
if (msg.sticker) {
|
| 225 |
+
form["sticker_id"] = msg.sticker;
|
| 226 |
+
}
|
| 227 |
+
cb();
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
function handleEmoji(msg, form, callback, cb) {
|
| 231 |
+
if (msg.emojiSize != null && msg.emoji == null) {
|
| 232 |
+
return callback({ error: "emoji property is empty" });
|
| 233 |
+
}
|
| 234 |
+
if (msg.emoji) {
|
| 235 |
+
if (msg.emojiSize == null) {
|
| 236 |
+
msg.emojiSize = "medium";
|
| 237 |
+
}
|
| 238 |
+
if (
|
| 239 |
+
msg.emojiSize != "small" &&
|
| 240 |
+
msg.emojiSize != "medium" &&
|
| 241 |
+
msg.emojiSize != "large"
|
| 242 |
+
) {
|
| 243 |
+
return callback({ error: "emojiSize property is invalid" });
|
| 244 |
+
}
|
| 245 |
+
if (form["body"] != null && form["body"] != "") {
|
| 246 |
+
return callback({ error: "body is not empty" });
|
| 247 |
+
}
|
| 248 |
+
form["body"] = msg.emoji;
|
| 249 |
+
form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
|
| 250 |
+
}
|
| 251 |
+
cb();
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
function handleAttachment(msg, form, callback, cb) {
|
| 255 |
+
if (msg.attachment) {
|
| 256 |
+
form["image_ids"] = [];
|
| 257 |
+
form["gif_ids"] = [];
|
| 258 |
+
form["file_ids"] = [];
|
| 259 |
+
form["video_ids"] = [];
|
| 260 |
+
form["audio_ids"] = [];
|
| 261 |
+
|
| 262 |
+
if (utils.getType(msg.attachment) !== "Array") {
|
| 263 |
+
msg.attachment = [msg.attachment];
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
uploadAttachment(msg.attachment, function (err, files) {
|
| 267 |
+
if (err) {
|
| 268 |
+
return callback(err);
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
files.forEach(function (file) {
|
| 272 |
+
var key = Object.keys(file);
|
| 273 |
+
var type = key[0]; // image_id, file_id, etc
|
| 274 |
+
form["" + type + "s"].push(file[type]); // push the id
|
| 275 |
+
});
|
| 276 |
+
cb();
|
| 277 |
+
});
|
| 278 |
+
} else {
|
| 279 |
+
cb();
|
| 280 |
+
}
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
function handleMention(msg, form, callback, cb) {
|
| 284 |
+
if (msg.mentions) {
|
| 285 |
+
for (let i = 0; i < msg.mentions.length; i++) {
|
| 286 |
+
const mention = msg.mentions[i];
|
| 287 |
+
|
| 288 |
+
const tag = mention.tag;
|
| 289 |
+
if (typeof tag !== "string") {
|
| 290 |
+
return callback({ error: "Mention tags must be strings." });
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
|
| 294 |
+
|
| 295 |
+
if (offset < 0) {
|
| 296 |
+
log.warn(
|
| 297 |
+
"handleMention",
|
| 298 |
+
'Mention for "' + tag + '" not found in message string.'
|
| 299 |
+
);
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
if (mention.id == null) {
|
| 303 |
+
log.warn("handleMention", "Mention id should be non-null.");
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
const id = mention.id || 0;
|
| 307 |
+
const emptyChar = '\u200E';
|
| 308 |
+
form["body"] = emptyChar + msg.body;
|
| 309 |
+
form["profile_xmd[" + i + "][offset]"] = offset + 1;
|
| 310 |
+
form["profile_xmd[" + i + "][length]"] = tag.length;
|
| 311 |
+
form["profile_xmd[" + i + "][id]"] = id;
|
| 312 |
+
form["profile_xmd[" + i + "][type]"] = "p";
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
cb();
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
|
| 319 |
+
typeof isGroup == "undefined" ? isGroup = null : "";
|
| 320 |
+
if (
|
| 321 |
+
!callback &&
|
| 322 |
+
(utils.getType(threadID) === "Function" ||
|
| 323 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 324 |
+
) {
|
| 325 |
+
return threadID({ error: "Pass a threadID as a second argument." });
|
| 326 |
+
}
|
| 327 |
+
if (
|
| 328 |
+
!replyToMessage &&
|
| 329 |
+
utils.getType(callback) === "String"
|
| 330 |
+
) {
|
| 331 |
+
replyToMessage = callback;
|
| 332 |
+
callback = undefined;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
var resolveFunc = function () { };
|
| 336 |
+
var rejectFunc = function () { };
|
| 337 |
+
var returnPromise = new Promise(function (resolve, reject) {
|
| 338 |
+
resolveFunc = resolve;
|
| 339 |
+
rejectFunc = reject;
|
| 340 |
+
});
|
| 341 |
+
|
| 342 |
+
if (!callback) {
|
| 343 |
+
callback = function (err, data) {
|
| 344 |
+
if (err) return rejectFunc(err);
|
| 345 |
+
resolveFunc(data);
|
| 346 |
+
};
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
var msgType = utils.getType(msg);
|
| 350 |
+
var threadIDType = utils.getType(threadID);
|
| 351 |
+
var messageIDType = utils.getType(replyToMessage);
|
| 352 |
+
|
| 353 |
+
if (msgType !== "String" && msgType !== "Object") {
|
| 354 |
+
return callback({
|
| 355 |
+
error:
|
| 356 |
+
"Message should be of type string or object and not " + msgType + "."
|
| 357 |
+
});
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
// Changing this to accomodate an array of users
|
| 361 |
+
if (
|
| 362 |
+
threadIDType !== "Array" &&
|
| 363 |
+
threadIDType !== "Number" &&
|
| 364 |
+
threadIDType !== "String"
|
| 365 |
+
) {
|
| 366 |
+
return callback({
|
| 367 |
+
error:
|
| 368 |
+
"ThreadID should be of type number, string, or array and not " +
|
| 369 |
+
threadIDType +
|
| 370 |
+
"."
|
| 371 |
+
});
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
if (replyToMessage && messageIDType !== 'String') {
|
| 375 |
+
return callback({
|
| 376 |
+
error:
|
| 377 |
+
"MessageID should be of type string and not " +
|
| 378 |
+
threadIDType +
|
| 379 |
+
"."
|
| 380 |
+
});
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
if (msgType === "String") {
|
| 384 |
+
msg = { body: msg };
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
var disallowedProperties = Object.keys(msg).filter(
|
| 388 |
+
prop => !allowedProperties[prop]
|
| 389 |
+
);
|
| 390 |
+
if (disallowedProperties.length > 0) {
|
| 391 |
+
return callback({
|
| 392 |
+
error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`"
|
| 393 |
+
});
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
var messageAndOTID = utils.generateOfflineThreadingID();
|
| 397 |
+
|
| 398 |
+
var form = {
|
| 399 |
+
client: "mercury",
|
| 400 |
+
action_type: "ma-type:user-generated-message",
|
| 401 |
+
author: "fbid:" + ctx.userID,
|
| 402 |
+
timestamp: Date.now(),
|
| 403 |
+
timestamp_absolute: "Today",
|
| 404 |
+
timestamp_relative: utils.generateTimestampRelative(),
|
| 405 |
+
timestamp_time_passed: "0",
|
| 406 |
+
is_unread: false,
|
| 407 |
+
is_cleared: false,
|
| 408 |
+
is_forward: false,
|
| 409 |
+
is_filtered_content: false,
|
| 410 |
+
is_filtered_content_bh: false,
|
| 411 |
+
is_filtered_content_account: false,
|
| 412 |
+
is_filtered_content_quasar: false,
|
| 413 |
+
is_filtered_content_invalid_app: false,
|
| 414 |
+
is_spoof_warning: false,
|
| 415 |
+
source: "source:chat:web",
|
| 416 |
+
"source_tags[0]": "source:chat",
|
| 417 |
+
body: msg.body ? msg.body.toString() : "",
|
| 418 |
+
html_body: false,
|
| 419 |
+
ui_push_phase: "V3",
|
| 420 |
+
status: "0",
|
| 421 |
+
offline_threading_id: messageAndOTID,
|
| 422 |
+
message_id: messageAndOTID,
|
| 423 |
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
| 424 |
+
"ephemeral_ttl_mode:": "0",
|
| 425 |
+
manual_retry_cnt: "0",
|
| 426 |
+
has_attachment: !!(msg.attachment || msg.url || msg.sticker),
|
| 427 |
+
signatureID: utils.getSignatureID(),
|
| 428 |
+
replied_to_message_id: replyToMessage
|
| 429 |
+
};
|
| 430 |
+
|
| 431 |
+
handleLocation(msg, form, callback, () =>
|
| 432 |
+
handleSticker(msg, form, callback, () =>
|
| 433 |
+
handleAttachment(msg, form, callback, () =>
|
| 434 |
+
handleUrl(msg, form, callback, () =>
|
| 435 |
+
handleEmoji(msg, form, callback, () =>
|
| 436 |
+
handleMention(msg, form, callback, () =>
|
| 437 |
+
send(form, threadID, messageAndOTID, callback, isGroup)
|
| 438 |
+
)
|
| 439 |
+
)
|
| 440 |
+
)
|
| 441 |
+
)
|
| 442 |
+
)
|
| 443 |
+
);
|
| 444 |
+
|
| 445 |
+
return returnPromise;
|
| 446 |
+
};
|
| 447 |
+
};
|
includes/login/src/sendMessageMqtt.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var utils = require("../utils");
|
| 2 |
+
var log = require("npmlog");
|
| 3 |
+
var bluebird = require("bluebird");
|
| 4 |
+
|
| 5 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 6 |
+
function uploadAttachment(attachments, callback) {
|
| 7 |
+
callback = callback || function () { };
|
| 8 |
+
var uploads = [];
|
| 9 |
+
|
| 10 |
+
// create an array of promises
|
| 11 |
+
for (var i = 0; i < attachments.length; i++) {
|
| 12 |
+
if (!utils.isReadableStream(attachments[i])) {
|
| 13 |
+
throw {
|
| 14 |
+
error:
|
| 15 |
+
"Attachment should be a readable stream and not " +
|
| 16 |
+
utils.getType(attachments[i]) +
|
| 17 |
+
"."
|
| 18 |
+
};
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
var form = {
|
| 22 |
+
upload_1024: attachments[i],
|
| 23 |
+
voice_clip: "true"
|
| 24 |
+
};
|
| 25 |
+
|
| 26 |
+
uploads.push(
|
| 27 |
+
defaultFuncs
|
| 28 |
+
.postFormData(
|
| 29 |
+
"https://upload.facebook.com/ajax/mercury/upload.php",
|
| 30 |
+
ctx.jar,
|
| 31 |
+
form,
|
| 32 |
+
{}
|
| 33 |
+
)
|
| 34 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 35 |
+
.then(function (resData) {
|
| 36 |
+
if (resData.error) {
|
| 37 |
+
throw resData;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
// We have to return the data unformatted unless we want to change it
|
| 41 |
+
// back in sendMessage.
|
| 42 |
+
return resData.payload.metadata[0];
|
| 43 |
+
})
|
| 44 |
+
);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// resolve all promises
|
| 48 |
+
bluebird
|
| 49 |
+
.all(uploads)
|
| 50 |
+
.then(function (resData) {
|
| 51 |
+
callback(null, resData);
|
| 52 |
+
})
|
| 53 |
+
.catch(function (err) {
|
| 54 |
+
log.error("uploadAttachment", err);
|
| 55 |
+
return callback(err);
|
| 56 |
+
});
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
let variance = 0;
|
| 60 |
+
const epoch_id = () => Math.floor(Date.now() * (4194304 + (variance = (variance + 0.1) % 5)));
|
| 61 |
+
const emojiSizes = {
|
| 62 |
+
small: 1,
|
| 63 |
+
medium: 2,
|
| 64 |
+
large: 3
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
function handleEmoji(msg, form, callback, cb) {
|
| 68 |
+
if (msg.emojiSize != null && msg.emoji == null) {
|
| 69 |
+
return callback({ error: "emoji property is empty" });
|
| 70 |
+
}
|
| 71 |
+
if (msg.emoji) {
|
| 72 |
+
if (!msg.emojiSize) {
|
| 73 |
+
msg.emojiSize = "small";
|
| 74 |
+
}
|
| 75 |
+
if (
|
| 76 |
+
msg.emojiSize !== "small" &&
|
| 77 |
+
msg.emojiSize !== "medium" &&
|
| 78 |
+
msg.emojiSize !== "large" &&
|
| 79 |
+
(isNaN(msg.emojiSize) || msg.emojiSize < 1 || msg.emojiSize > 3)
|
| 80 |
+
) {
|
| 81 |
+
return callback({ error: "emojiSize property is invalid" });
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
form.payload.tasks[0].payload.send_type = 1;
|
| 85 |
+
form.payload.tasks[0].payload.text = msg.emoji;
|
| 86 |
+
form.payload.tasks[0].payload.hot_emoji_size = !isNaN(msg.emojiSize) ? msg.emojiSize : emojiSizes[msg.emojiSize];
|
| 87 |
+
}
|
| 88 |
+
cb();
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function handleSticker(msg, form, callback, cb) {
|
| 92 |
+
if (msg.sticker) {
|
| 93 |
+
form.payload.tasks[0].payload.send_type = 2;
|
| 94 |
+
form.payload.tasks[0].payload.sticker_id = msg.sticker;
|
| 95 |
+
}
|
| 96 |
+
cb();
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
function handleAttachment(msg, form, callback, cb) {
|
| 100 |
+
if (msg.attachment) {
|
| 101 |
+
form.payload.tasks[0].payload.send_type = 3;
|
| 102 |
+
form.payload.tasks[0].payload.attachment_fbids = [];
|
| 103 |
+
if (form.payload.tasks[0].payload.text == "")
|
| 104 |
+
form.payload.tasks[0].payload.text = null;
|
| 105 |
+
if (utils.getType(msg.attachment) !== "Array") {
|
| 106 |
+
msg.attachment = [msg.attachment];
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
uploadAttachment(msg.attachment, function (err, files) {
|
| 110 |
+
if (err) {
|
| 111 |
+
return callback(err);
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
files.forEach(function (file) {
|
| 115 |
+
var key = Object.keys(file);
|
| 116 |
+
var type = key[0]; // image_id, file_id, etc
|
| 117 |
+
form.payload.tasks[0].payload.attachment_fbids.push(file[type]); // push the id
|
| 118 |
+
});
|
| 119 |
+
cb();
|
| 120 |
+
});
|
| 121 |
+
} else {
|
| 122 |
+
cb();
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
function handleMention(msg, form, callback, cb) {
|
| 128 |
+
if (msg.mentions) {
|
| 129 |
+
form.payload.tasks[0].payload.send_type = 1;
|
| 130 |
+
|
| 131 |
+
const arrayIds = [];
|
| 132 |
+
const arrayOffsets = [];
|
| 133 |
+
const arrayLengths = [];
|
| 134 |
+
const mention_types = [];
|
| 135 |
+
|
| 136 |
+
for (let i = 0; i < msg.mentions.length; i++) {
|
| 137 |
+
const mention = msg.mentions[i];
|
| 138 |
+
|
| 139 |
+
const tag = mention.tag;
|
| 140 |
+
if (typeof tag !== "string") {
|
| 141 |
+
return callback({ error: "Mention tags must be strings." });
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
|
| 145 |
+
|
| 146 |
+
if (offset < 0) {
|
| 147 |
+
log.warn(
|
| 148 |
+
"handleMention",
|
| 149 |
+
'Mention for "' + tag + '" not found in message string.'
|
| 150 |
+
);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
if (mention.id == null) {
|
| 154 |
+
log.warn("handleMention", "Mention id should be non-null.");
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
const id = mention.id || 0;
|
| 158 |
+
arrayIds.push(id);
|
| 159 |
+
arrayOffsets.push(offset);
|
| 160 |
+
arrayLengths.push(tag.length);
|
| 161 |
+
mention_types.push("p");
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
form.payload.tasks[0].payload.mention_data = {
|
| 165 |
+
mention_ids: arrayIds.join(","),
|
| 166 |
+
mention_offsets: arrayOffsets.join(","),
|
| 167 |
+
mention_lengths: arrayLengths.join(","),
|
| 168 |
+
mention_types: mention_types.join(",")
|
| 169 |
+
};
|
| 170 |
+
}
|
| 171 |
+
cb();
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
function handleLocation(msg, form, callback, cb) {
|
| 175 |
+
// this is not working yet
|
| 176 |
+
if (msg.location) {
|
| 177 |
+
if (msg.location.latitude == null || msg.location.longitude == null) {
|
| 178 |
+
return callback({ error: "location property needs both latitude and longitude" });
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
form.payload.tasks[0].payload.send_type = 1;
|
| 182 |
+
form.payload.tasks[0].payload.location_data = {
|
| 183 |
+
coordinates: {
|
| 184 |
+
latitude: msg.location.latitude,
|
| 185 |
+
longitude: msg.location.longitude
|
| 186 |
+
},
|
| 187 |
+
is_current_location: !!msg.location.current,
|
| 188 |
+
is_live_location: !!msg.location.live
|
| 189 |
+
};
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
cb();
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
function send(form, threadID, callback, replyToMessage) {
|
| 196 |
+
if (replyToMessage) {
|
| 197 |
+
form.payload.tasks[0].payload.reply_metadata = {
|
| 198 |
+
reply_source_id: replyToMessage,
|
| 199 |
+
reply_source_type: 1,
|
| 200 |
+
reply_type: 0
|
| 201 |
+
};
|
| 202 |
+
}
|
| 203 |
+
const mqttClient = ctx.mqttClient;
|
| 204 |
+
form.payload.tasks.forEach((task) => {
|
| 205 |
+
task.payload = JSON.stringify(task.payload);
|
| 206 |
+
});
|
| 207 |
+
form.payload = JSON.stringify(form.payload);
|
| 208 |
+
console.log(global.jsonStringifyColor(form, null, 2));
|
| 209 |
+
|
| 210 |
+
return mqttClient.publish("/ls_req", JSON.stringify(form), function (err, data) {
|
| 211 |
+
if (err) {
|
| 212 |
+
console.error('Error publishing message: ', err);
|
| 213 |
+
callback(err);
|
| 214 |
+
} else {
|
| 215 |
+
console.log('Message published successfully with data: ', data);
|
| 216 |
+
callback(null, data);
|
| 217 |
+
}
|
| 218 |
+
});
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
return function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
|
| 222 |
+
if (
|
| 223 |
+
!callback &&
|
| 224 |
+
(utils.getType(threadID) === "Function" ||
|
| 225 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 226 |
+
) {
|
| 227 |
+
return threadID({ error: "Pass a threadID as a second argument." });
|
| 228 |
+
}
|
| 229 |
+
if (
|
| 230 |
+
!replyToMessage &&
|
| 231 |
+
utils.getType(callback) === "String"
|
| 232 |
+
) {
|
| 233 |
+
replyToMessage = callback;
|
| 234 |
+
callback = function () { };
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
if (!callback) {
|
| 239 |
+
callback = function (err, friendList) {
|
| 240 |
+
};
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
var msgType = utils.getType(msg);
|
| 244 |
+
var threadIDType = utils.getType(threadID);
|
| 245 |
+
var messageIDType = utils.getType(replyToMessage);
|
| 246 |
+
|
| 247 |
+
if (msgType !== "String" && msgType !== "Object") {
|
| 248 |
+
return callback({
|
| 249 |
+
error:
|
| 250 |
+
"Message should be of type string or object and not " + msgType + "."
|
| 251 |
+
});
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
if (msgType === "String") {
|
| 255 |
+
msg = { body: msg };
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
const timestamp = Date.now();
|
| 259 |
+
// get full date time
|
| 260 |
+
const epoch = timestamp << 22;
|
| 261 |
+
//const otid = epoch + 0; // TODO replace with randomInt(0, 2**22)
|
| 262 |
+
const otid = epoch + Math.floor(Math.random() * 4194304);
|
| 263 |
+
|
| 264 |
+
const form = {
|
| 265 |
+
app_id: "2220391788200892",
|
| 266 |
+
payload: {
|
| 267 |
+
tasks: [
|
| 268 |
+
{
|
| 269 |
+
label: "46",
|
| 270 |
+
payload: {
|
| 271 |
+
thread_id: threadID.toString(),
|
| 272 |
+
otid: otid.toString(),
|
| 273 |
+
source: 0,
|
| 274 |
+
send_type: 1,
|
| 275 |
+
sync_group: 1,
|
| 276 |
+
text: msg.body != null && msg.body != undefined ? msg.body.toString() : "",
|
| 277 |
+
initiating_source: 1,
|
| 278 |
+
skip_url_preview_gen: 0
|
| 279 |
+
},
|
| 280 |
+
queue_name: threadID.toString(),
|
| 281 |
+
task_id: 0,
|
| 282 |
+
failure_count: null
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
label: "21",
|
| 286 |
+
payload: {
|
| 287 |
+
thread_id: threadID.toString(),
|
| 288 |
+
last_read_watermark_ts: Date.now(),
|
| 289 |
+
sync_group: 1
|
| 290 |
+
},
|
| 291 |
+
queue_name: threadID.toString(),
|
| 292 |
+
task_id: 1,
|
| 293 |
+
failure_count: null
|
| 294 |
+
}
|
| 295 |
+
],
|
| 296 |
+
epoch_id: epoch_id(),
|
| 297 |
+
version_id: "6120284488008082",
|
| 298 |
+
data_trace_id: null
|
| 299 |
+
},
|
| 300 |
+
request_id: 1,
|
| 301 |
+
type: 3
|
| 302 |
+
};
|
| 303 |
+
|
| 304 |
+
handleEmoji(msg, form, callback, function () {
|
| 305 |
+
handleLocation(msg, form, callback, function () {
|
| 306 |
+
handleMention(msg, form, callback, function () {
|
| 307 |
+
handleSticker(msg, form, callback, function () {
|
| 308 |
+
handleAttachment(msg, form, callback, function () {
|
| 309 |
+
send(form, threadID, callback, replyToMessage);
|
| 310 |
+
});
|
| 311 |
+
});
|
| 312 |
+
});
|
| 313 |
+
});
|
| 314 |
+
});
|
| 315 |
+
};
|
| 316 |
+
};
|
includes/login/src/sendTypingIndicator.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
function makeTypingIndicator(typ, threadID, callback, isGroup) {
|
| 8 |
+
const form = {
|
| 9 |
+
typ: +typ,
|
| 10 |
+
to: "",
|
| 11 |
+
source: "mercury-chat",
|
| 12 |
+
thread: threadID
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
// Check if thread is a single person chat or a group chat
|
| 16 |
+
// More info on this is in api.sendMessage
|
| 17 |
+
if (utils.getType(isGroup) == "Boolean") {
|
| 18 |
+
if (!isGroup) {
|
| 19 |
+
form.to = threadID;
|
| 20 |
+
}
|
| 21 |
+
defaultFuncs
|
| 22 |
+
.post("https://www.facebook.com/ajax/messaging/typ.php", ctx.jar, form)
|
| 23 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 24 |
+
.then(function (resData) {
|
| 25 |
+
if (resData.error) {
|
| 26 |
+
throw resData;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
return callback();
|
| 30 |
+
})
|
| 31 |
+
.catch(function (err) {
|
| 32 |
+
log.error("sendTypingIndicator", err);
|
| 33 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in") {
|
| 34 |
+
ctx.loggedIn = false;
|
| 35 |
+
}
|
| 36 |
+
return callback(err);
|
| 37 |
+
});
|
| 38 |
+
} else {
|
| 39 |
+
api.getUserInfo(threadID, function (err, res) {
|
| 40 |
+
if (err) {
|
| 41 |
+
return callback(err);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
// If id is single person chat
|
| 45 |
+
if (Object.keys(res).length > 0) {
|
| 46 |
+
form.to = threadID;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
defaultFuncs
|
| 50 |
+
.post("https://www.facebook.com/ajax/messaging/typ.php", ctx.jar, form)
|
| 51 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 52 |
+
.then(function (resData) {
|
| 53 |
+
if (resData.error) {
|
| 54 |
+
throw resData;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
return callback();
|
| 58 |
+
})
|
| 59 |
+
.catch(function (err) {
|
| 60 |
+
log.error("sendTypingIndicator", err);
|
| 61 |
+
if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
|
| 62 |
+
ctx.loggedIn = false;
|
| 63 |
+
}
|
| 64 |
+
return callback(err);
|
| 65 |
+
});
|
| 66 |
+
});
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
return function sendTypingIndicator(threadID, callback, isGroup) {
|
| 71 |
+
if (
|
| 72 |
+
utils.getType(callback) !== "Function" &&
|
| 73 |
+
utils.getType(callback) !== "AsyncFunction"
|
| 74 |
+
) {
|
| 75 |
+
if (callback) {
|
| 76 |
+
log.warn(
|
| 77 |
+
"sendTypingIndicator",
|
| 78 |
+
"callback is not a function - ignoring."
|
| 79 |
+
);
|
| 80 |
+
}
|
| 81 |
+
callback = () => { };
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
makeTypingIndicator(true, threadID, callback, isGroup);
|
| 85 |
+
|
| 86 |
+
return function end(cb) {
|
| 87 |
+
if (
|
| 88 |
+
utils.getType(cb) !== "Function" &&
|
| 89 |
+
utils.getType(cb) !== "AsyncFunction"
|
| 90 |
+
) {
|
| 91 |
+
if (cb) {
|
| 92 |
+
log.warn(
|
| 93 |
+
"sendTypingIndicator",
|
| 94 |
+
"callback is not a function - ignoring."
|
| 95 |
+
);
|
| 96 |
+
}
|
| 97 |
+
cb = () => { };
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
makeTypingIndicator(false, threadID, cb, isGroup);
|
| 101 |
+
};
|
| 102 |
+
};
|
| 103 |
+
};
|
includes/login/src/setMessageReaction.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function setMessageReaction(reaction, messageID, callback, forceCustomReaction) {
|
| 8 |
+
let resolveFunc = function () { };
|
| 9 |
+
let rejectFunc = function () { };
|
| 10 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 11 |
+
resolveFunc = resolve;
|
| 12 |
+
rejectFunc = reject;
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
if (!callback) {
|
| 16 |
+
callback = function (err, friendList) {
|
| 17 |
+
if (err) {
|
| 18 |
+
return rejectFunc(err);
|
| 19 |
+
}
|
| 20 |
+
resolveFunc(friendList);
|
| 21 |
+
};
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
switch (reaction) {
|
| 25 |
+
case "\uD83D\uDE0D": //:heart_eyes:
|
| 26 |
+
case "\uD83D\uDE06": //:laughing:
|
| 27 |
+
case "\uD83D\uDE2E": //:open_mouth:
|
| 28 |
+
case "\uD83D\uDE22": //:cry:
|
| 29 |
+
case "\uD83D\uDE20": //:angry:
|
| 30 |
+
case "\uD83D\uDC4D": //:thumbsup:
|
| 31 |
+
case "\uD83D\uDC4E": //:thumbsdown:
|
| 32 |
+
case "\u2764": //:heart:
|
| 33 |
+
case "\uD83D\uDC97": //:glowingheart:
|
| 34 |
+
case "":
|
| 35 |
+
//valid
|
| 36 |
+
break;
|
| 37 |
+
case ":heart_eyes:":
|
| 38 |
+
case ":love:":
|
| 39 |
+
reaction = "\uD83D\uDE0D";
|
| 40 |
+
break;
|
| 41 |
+
case ":laughing:":
|
| 42 |
+
case ":haha:":
|
| 43 |
+
reaction = "\uD83D\uDE06";
|
| 44 |
+
break;
|
| 45 |
+
case ":open_mouth:":
|
| 46 |
+
case ":wow:":
|
| 47 |
+
reaction = "\uD83D\uDE2E";
|
| 48 |
+
break;
|
| 49 |
+
case ":cry:":
|
| 50 |
+
case ":sad:":
|
| 51 |
+
reaction = "\uD83D\uDE22";
|
| 52 |
+
break;
|
| 53 |
+
case ":angry:":
|
| 54 |
+
reaction = "\uD83D\uDE20";
|
| 55 |
+
break;
|
| 56 |
+
case ":thumbsup:":
|
| 57 |
+
case ":like:":
|
| 58 |
+
reaction = "\uD83D\uDC4D";
|
| 59 |
+
break;
|
| 60 |
+
case ":thumbsdown:":
|
| 61 |
+
case ":dislike:":
|
| 62 |
+
reaction = "\uD83D\uDC4E";
|
| 63 |
+
break;
|
| 64 |
+
case ":heart:":
|
| 65 |
+
reaction = "\u2764";
|
| 66 |
+
break;
|
| 67 |
+
case ":glowingheart:":
|
| 68 |
+
reaction = "\uD83D\uDC97";
|
| 69 |
+
break;
|
| 70 |
+
default:
|
| 71 |
+
if (forceCustomReaction) {
|
| 72 |
+
break;
|
| 73 |
+
}
|
| 74 |
+
return callback({ error: "Reaction is not a valid emoji." });
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
const variables = {
|
| 78 |
+
data: {
|
| 79 |
+
client_mutation_id: ctx.clientMutationId++,
|
| 80 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 81 |
+
action: reaction == "" ? "REMOVE_REACTION" : "ADD_REACTION",
|
| 82 |
+
message_id: messageID,
|
| 83 |
+
reaction: reaction
|
| 84 |
+
}
|
| 85 |
+
};
|
| 86 |
+
|
| 87 |
+
const qs = {
|
| 88 |
+
doc_id: "1491398900900362",
|
| 89 |
+
variables: JSON.stringify(variables),
|
| 90 |
+
dpr: 1
|
| 91 |
+
};
|
| 92 |
+
|
| 93 |
+
defaultFuncs
|
| 94 |
+
.postFormData(
|
| 95 |
+
"https://www.facebook.com/webgraphql/mutation/",
|
| 96 |
+
ctx.jar,
|
| 97 |
+
{},
|
| 98 |
+
qs
|
| 99 |
+
)
|
| 100 |
+
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
|
| 101 |
+
.then(function (resData) {
|
| 102 |
+
if (!resData) {
|
| 103 |
+
throw { error: "setReaction returned empty object." };
|
| 104 |
+
}
|
| 105 |
+
if (resData.error) {
|
| 106 |
+
throw resData;
|
| 107 |
+
}
|
| 108 |
+
callback(null);
|
| 109 |
+
})
|
| 110 |
+
.catch(function (err) {
|
| 111 |
+
log.error("setReaction", err);
|
| 112 |
+
return callback(err);
|
| 113 |
+
});
|
| 114 |
+
|
| 115 |
+
return returnPromise;
|
| 116 |
+
};
|
| 117 |
+
};
|
includes/login/src/setPostReaction.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* @Updated by @YanMaglinte
|
| 3 |
+
* updated on Sunday, 3 March 2024
|
| 4 |
+
* changed Buffer => Buffer.from()
|
| 5 |
+
* modified it since it was outdated
|
| 6 |
+
* Do not remove the author's name to get notified to the latest updates.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
"use strict";
|
| 10 |
+
|
| 11 |
+
const utils = require("../utils");
|
| 12 |
+
const log = require("npmlog");
|
| 13 |
+
|
| 14 |
+
function formatData(resData) {
|
| 15 |
+
return {
|
| 16 |
+
viewer_feedback_reaction_info: resData.feedback_react.feedback.viewer_feedback_reaction_info,
|
| 17 |
+
supported_reactions: resData.feedback_react.feedback.supported_reactions,
|
| 18 |
+
top_reactions: resData.feedback_react.feedback.top_reactions.edges,
|
| 19 |
+
reaction_count: resData.feedback_react.feedback.reaction_count
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 24 |
+
return function setPostReaction(postID, type, callback) {
|
| 25 |
+
let resolveFunc = function () { };
|
| 26 |
+
let rejectFunc = function () { };
|
| 27 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 28 |
+
resolveFunc = resolve;
|
| 29 |
+
rejectFunc = reject;
|
| 30 |
+
});
|
| 31 |
+
|
| 32 |
+
if (!callback) {
|
| 33 |
+
if (utils.getType(type) === "Function" || utils.getType(type) === "AsyncFunction") {
|
| 34 |
+
callback = type;
|
| 35 |
+
type = 0;
|
| 36 |
+
}
|
| 37 |
+
else {
|
| 38 |
+
callback = function (err, data) {
|
| 39 |
+
if (err) {
|
| 40 |
+
return rejectFunc(err);
|
| 41 |
+
}
|
| 42 |
+
resolveFunc(data);
|
| 43 |
+
};
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
const map = {
|
| 48 |
+
unlike: 0,
|
| 49 |
+
like: 1,
|
| 50 |
+
heart: 2,
|
| 51 |
+
love: 16,
|
| 52 |
+
haha: 4,
|
| 53 |
+
wow: 3,
|
| 54 |
+
sad: 7,
|
| 55 |
+
angry: 8
|
| 56 |
+
};
|
| 57 |
+
|
| 58 |
+
if (utils.getType(type) !== "Number" && utils.getType(type) === "String") {
|
| 59 |
+
type = map[type.toLowerCase()];
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
if (utils.getType(type) !== "Number" && utils.getType(type) !== "String") {
|
| 63 |
+
throw {
|
| 64 |
+
error: "setPostReaction: Invalid reaction type"
|
| 65 |
+
};
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
if (type != 0 && !type) {
|
| 69 |
+
throw {
|
| 70 |
+
error: "setPostReaction: Invalid reaction type"
|
| 71 |
+
};
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
const form = {
|
| 75 |
+
av: ctx.i_userID || ctx.userID,
|
| 76 |
+
fb_api_caller_class: "RelayModern",
|
| 77 |
+
fb_api_req_friendly_name: "CometUFIFeedbackReactMutation",
|
| 78 |
+
doc_id: "4769042373179384",
|
| 79 |
+
variables: JSON.stringify({
|
| 80 |
+
input: {
|
| 81 |
+
actor_id: ctx.i_userID || ctx.userID,
|
| 82 |
+
feedback_id: (new Buffer.from("feedback:" + postID)).toString("base64"),
|
| 83 |
+
feedback_reaction: type,
|
| 84 |
+
feedback_source: "OBJECT",
|
| 85 |
+
is_tracking_encrypted: true,
|
| 86 |
+
tracking: [],
|
| 87 |
+
session_id: "f7dd50dd-db6e-4598-8cd9-561d5002b423",
|
| 88 |
+
client_mutation_id: Math.round(Math.random() * 19).toString()
|
| 89 |
+
},
|
| 90 |
+
useDefaultActor: false,
|
| 91 |
+
scale: 3
|
| 92 |
+
})
|
| 93 |
+
};
|
| 94 |
+
|
| 95 |
+
defaultFuncs
|
| 96 |
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
| 97 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 98 |
+
.then(function (resData) {
|
| 99 |
+
if (resData.errors) {
|
| 100 |
+
throw resData;
|
| 101 |
+
}
|
| 102 |
+
return callback(null, formatData(resData.data));
|
| 103 |
+
})
|
| 104 |
+
.catch(function (err) {
|
| 105 |
+
log.error("setPostReaction", err);
|
| 106 |
+
return callback(err);
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
return returnPromise;
|
| 110 |
+
};
|
| 111 |
+
};
|
includes/login/src/setTitle.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
const utils = require("../utils");
|
| 4 |
+
const log = require("npmlog");
|
| 5 |
+
|
| 6 |
+
module.exports = function (defaultFuncs, api, ctx) {
|
| 7 |
+
return function setTitle(newTitle, threadID, callback) {
|
| 8 |
+
if (
|
| 9 |
+
!callback &&
|
| 10 |
+
(utils.getType(threadID) === "Function" ||
|
| 11 |
+
utils.getType(threadID) === "AsyncFunction")
|
| 12 |
+
) {
|
| 13 |
+
throw { error: "please pass a threadID as a second argument." };
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
let resolveFunc = function () { };
|
| 17 |
+
let rejectFunc = function () { };
|
| 18 |
+
const returnPromise = new Promise(function (resolve, reject) {
|
| 19 |
+
resolveFunc = resolve;
|
| 20 |
+
rejectFunc = reject;
|
| 21 |
+
});
|
| 22 |
+
|
| 23 |
+
if (!callback) {
|
| 24 |
+
callback = function (err, friendList) {
|
| 25 |
+
if (err) {
|
| 26 |
+
return rejectFunc(err);
|
| 27 |
+
}
|
| 28 |
+
resolveFunc(friendList);
|
| 29 |
+
};
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
const messageAndOTID = utils.generateOfflineThreadingID();
|
| 33 |
+
const form = {
|
| 34 |
+
client: "mercury",
|
| 35 |
+
action_type: "ma-type:log-message",
|
| 36 |
+
author: "fbid:" + (ctx.i_userID || ctx.userID),
|
| 37 |
+
author_email: "",
|
| 38 |
+
coordinates: "",
|
| 39 |
+
timestamp: Date.now(),
|
| 40 |
+
timestamp_absolute: "Today",
|
| 41 |
+
timestamp_relative: utils.generateTimestampRelative(),
|
| 42 |
+
timestamp_time_passed: "0",
|
| 43 |
+
is_unread: false,
|
| 44 |
+
is_cleared: false,
|
| 45 |
+
is_forward: false,
|
| 46 |
+
is_filtered_content: false,
|
| 47 |
+
is_spoof_warning: false,
|
| 48 |
+
source: "source:chat:web",
|
| 49 |
+
"source_tags[0]": "source:chat",
|
| 50 |
+
status: "0",
|
| 51 |
+
offline_threading_id: messageAndOTID,
|
| 52 |
+
message_id: messageAndOTID,
|
| 53 |
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
| 54 |
+
manual_retry_cnt: "0",
|
| 55 |
+
thread_fbid: threadID,
|
| 56 |
+
thread_name: newTitle,
|
| 57 |
+
thread_id: threadID,
|
| 58 |
+
log_message_type: "log:thread-name"
|
| 59 |
+
};
|
| 60 |
+
|
| 61 |
+
defaultFuncs
|
| 62 |
+
.post("https://www.facebook.com/messaging/set_thread_name/", ctx.jar, form)
|
| 63 |
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
| 64 |
+
.then(function (resData) {
|
| 65 |
+
if (resData.error && resData.error === 1545012) {
|
| 66 |
+
throw { error: "Cannot change chat title: Not member of chat." };
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
if (resData.error && resData.error === 1545003) {
|
| 70 |
+
throw { error: "Cannot set title of single-user chat." };
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
if (resData.error) {
|
| 74 |
+
throw resData;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
return callback();
|
| 78 |
+
})
|
| 79 |
+
.catch(function (err) {
|
| 80 |
+
log.error("setTitle", err);
|
| 81 |
+
return callback(err);
|
| 82 |
+
});
|
| 83 |
+
|
| 84 |
+
return returnPromise;
|
| 85 |
+
};
|
| 86 |
+
};
|