@cexy/hoonfca 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.travis.yml +6 -0
- package/LICENSE-MIT +21 -0
- package/index.d.ts +620 -0
- package/index.js +745 -433
- package/package.json +45 -6
- package/src/addExternalModule.js +14 -16
- package/src/addUserToGroup.js +45 -30
- package/src/changeAdminStatus.js +39 -42
- package/src/changeApprovalMode.js +79 -0
- package/src/changeArchivedStatus.js +19 -15
- package/src/changeBio.js +18 -16
- package/src/changeBlockedStatus.js +14 -12
- package/src/changeBlockedStatusMqtt.js +38 -17
- package/src/changeGroupImage.js +100 -77
- package/src/changeNickname.js +12 -11
- package/src/changeThreadColor.js +25 -16
- package/src/changeThreadEmoji.js +11 -10
- package/src/createNewGroup.js +21 -14
- package/src/createPoll.js +87 -21
- package/src/deleteMessage.js +18 -17
- package/src/deleteThread.js +18 -17
- package/src/editMessage.js +28 -31
- package/src/forwardAttachment.js +22 -19
- package/src/forwardMessage.js +63 -0
- package/src/getCurrentUserID.js +4 -2
- package/src/getEmojiUrl.js +20 -9
- package/src/getFriendsList.js +21 -18
- package/src/getGroupsList.js +83 -0
- package/src/getMarketplace.js +84 -0
- package/src/getNotifications.js +86 -0
- package/src/getPagesList.js +83 -0
- package/src/getStories.js +88 -0
- package/src/getThreadHistory.js +277 -126
- package/src/getThreadInfo.js +143 -115
- package/src/getThreadList.js +216 -143
- package/src/getThreadPictures.js +26 -32
- package/src/getUserID.js +13 -14
- package/src/getUserInfo.js +107 -68
- package/src/handleFriendRequest.js +16 -15
- package/src/handleMessageRequest.js +15 -15
- package/src/httpGet.js +36 -31
- package/src/httpPost.js +36 -31
- package/src/listenMqtt.js +705 -370
- package/src/logout.js +26 -28
- package/src/markAsDelivered.js +22 -17
- package/src/markAsRead.js +63 -49
- package/src/markAsReadAll.js +17 -13
- package/src/markAsSeen.js +23 -19
- package/src/muteThread.js +17 -14
- package/src/pinMessage.js +23 -22
- package/src/removeUserFromGroup.js +42 -22
- package/src/resolvePhotoUrl.js +15 -17
- package/src/searchForThread.js +18 -20
- package/src/searchUsers.js +89 -0
- package/src/sendComment.js +161 -0
- package/src/sendMessage.js +65 -52
- package/src/sendTypingIndicator.js +173 -48
- package/src/setBio.js +86 -0
- package/src/setMessageReaction.js +76 -16
- package/src/setPostReaction.js +29 -61
- package/src/setTheme.js +313 -0
- package/src/setTitle.js +27 -37
- package/src/shareContact.js +34 -82
- package/src/shareLink.js +56 -99
- package/src/threadColors.js +35 -97
- package/src/unfriend.js +15 -20
- package/src/unsendMessage.js +71 -18
- package/utils.js +1000 -472
- package/README.md +0 -83
- package/replit.nix +0 -3
- package/src/changeAvatar.js +0 -89
- package/src/changeCover.js +0 -92
- package/src/changeName.js +0 -106
- package/src/changeUsername.js +0 -80
- package/src/createCommentPost.js +0 -196
- package/src/createPost.js +0 -285
- package/src/data/getThreadInfo.json +0 -1
- package/src/follow.js +0 -71
- package/src/getAccess.js +0 -130
- package/src/getAvatarUser.js +0 -88
- package/src/getBotInitialData.js +0 -59
- package/src/getCtx.js +0 -5
- package/src/getMessage.js +0 -827
- package/src/getOptions.js +0 -5
- package/src/getRegion.js +0 -7
- package/src/getThreadHistoryDeprecated.js +0 -95
- package/src/getThreadInfoDeprecated.js +0 -77
- package/src/getThreadListDeprecated.js +0 -86
- package/src/httpPostFormData.js +0 -58
- package/src/listenNotification.js +0 -96
- package/src/refreshFb_dtsg.js +0 -75
- package/src/searchStickers.js +0 -59
- package/src/sendMessageMqtt.js +0 -326
- package/src/setMessageReactionMqtt.js +0 -74
- package/src/setProfileGuard.js +0 -66
- package/src/setStoryReaction.js +0 -97
- package/src/stopListenMqtt.js +0 -47
- package/src/uploadAttachment.js +0 -98
package/utils.js
CHANGED
|
@@ -1,165 +1,151 @@
|
|
|
1
|
+
/* eslint-disable no-prototype-builtins */
|
|
1
2
|
"use strict";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
var bluebird = require("bluebird");
|
|
5
|
+
var request = bluebird.promisify(require("request").defaults({ jar: true }));
|
|
6
|
+
var stream = require("stream");
|
|
7
|
+
var log = require("npmlog");
|
|
8
|
+
var querystring = require("querystring");
|
|
9
|
+
var url = require("url");
|
|
10
|
+
const { Container } = require("nosql-json-database");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const { existsSync, mkdirSync } = require("fs");
|
|
13
|
+
|
|
14
|
+
if (!existsSync(path.join(process.cwd(), "dinovn-fca")))
|
|
15
|
+
mkdirSync(path.join(process.cwd(), "dinovn-fca"));
|
|
16
|
+
const database = new Container(path.join(process.cwd(), "dinovn-fca"));
|
|
17
|
+
const usersCache = database.createCollection("users");
|
|
18
|
+
const groupsCache = database.createCollection("groups");
|
|
19
|
+
|
|
20
|
+
function setProxy(url) {
|
|
21
|
+
if (typeof url == undefined)
|
|
22
|
+
return (request = bluebird.promisify(
|
|
23
|
+
require("request").defaults({
|
|
24
|
+
jar: true,
|
|
25
|
+
}),
|
|
26
|
+
));
|
|
27
|
+
return (request = bluebird.promisify(
|
|
28
|
+
require("request").defaults({
|
|
29
|
+
jar: true,
|
|
30
|
+
proxy: url,
|
|
31
|
+
}),
|
|
32
|
+
));
|
|
15
33
|
}
|
|
16
34
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
chrome: ['122.0.0.0', '121.0.0.0'],
|
|
26
|
-
firefox: ['123.0', '122.0'],
|
|
27
|
-
edge: ['122.0.2365.92']
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
const browserName = getRandom(Object.keys(platform.browsers));
|
|
31
|
-
const version = getRandom(platform.browsers[browserName]);
|
|
32
|
-
const plat = getRandom(platform.platform);
|
|
33
|
-
const userAgentArray = [
|
|
34
|
-
defaultUserAgent,
|
|
35
|
-
windowsUserAgent,
|
|
36
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
|
|
37
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:45.0) Gecko/20100101 Firefox/45.0",
|
|
38
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0",
|
|
39
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.7 (KHTML, like Gecko) Version/9.1.2 Safari/601.7.7",
|
|
40
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8",
|
|
41
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.3",
|
|
42
|
-
];
|
|
43
|
-
return getRandom([
|
|
44
|
-
browserName === 'firefox' ? `Mozilla/5.0 (${plat}) Gecko/20100101 Firefox/${version}` : `Mozilla/5.0 (${plat}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`,
|
|
45
|
-
getRandom(userAgentArray)
|
|
46
|
-
]);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const headers = {
|
|
50
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
51
|
-
"referer": "https://www.facebook.com/",
|
|
52
|
-
"origin": "https://www.facebook.com",
|
|
53
|
-
"connection": "keep-alive",
|
|
54
|
-
"Sec-Fetch-Site": "same-origin",
|
|
55
|
-
"Sec-Fetch-User": "?1"
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
let request = require("request").defaults({
|
|
59
|
-
jar: true
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
function setProxy(proxy) {
|
|
63
|
-
request = require("request").defaults({
|
|
64
|
-
jar: true,
|
|
65
|
-
...(proxy && { proxy })
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getHeaders(urlStr, options, ctx, customHeader) {
|
|
70
|
-
const headers1 = {
|
|
71
|
-
"host": new URL(urlStr).hostname,
|
|
72
|
-
...headers,
|
|
73
|
-
"User-Agent": customHeader?.customUserAgent ?? options?.userAgent ?? defaultUserAgent
|
|
35
|
+
function getHeaders(url, options, ctx, customHeader) {
|
|
36
|
+
var headers = {
|
|
37
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
38
|
+
Referer: "https://www.facebook.com/",
|
|
39
|
+
Host: url.replace("https://", "").split("/")[0],
|
|
40
|
+
Origin: "https://www.facebook.com",
|
|
41
|
+
"User-Agent": options.userAgent,
|
|
42
|
+
Connection: "keep-alive",
|
|
74
43
|
};
|
|
75
|
-
if (ctx && ctx.region) headers1["X-MSGR-Region"] = ctx.region;
|
|
76
44
|
if (customHeader) {
|
|
77
|
-
Object.assign(
|
|
78
|
-
|
|
45
|
+
Object.assign(headers, customHeader);
|
|
46
|
+
}
|
|
47
|
+
if (ctx && ctx.region) {
|
|
48
|
+
headers["X-MSGR-Region"] = ctx.region;
|
|
79
49
|
}
|
|
80
|
-
return headers1;
|
|
81
|
-
}
|
|
82
50
|
|
|
83
|
-
|
|
84
|
-
return obj instanceof stream.Stream && typeof obj._read === "function" && getType(obj._readableState) === "Object";
|
|
51
|
+
return headers;
|
|
85
52
|
}
|
|
86
53
|
|
|
87
|
-
function
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
|
|
54
|
+
function isReadableStream(obj) {
|
|
55
|
+
return (
|
|
56
|
+
obj instanceof stream.Stream &&
|
|
57
|
+
(getType(obj._read) === "Function" ||
|
|
58
|
+
getType(obj._read) === "AsyncFunction") &&
|
|
59
|
+
getType(obj._readableState) === "Object"
|
|
60
|
+
);
|
|
91
61
|
}
|
|
92
62
|
|
|
93
|
-
function get(url, jar, qs, options, ctx
|
|
63
|
+
function get(url, jar, qs, options, ctx) {
|
|
64
|
+
// I'm still confused about this
|
|
94
65
|
if (getType(qs) === "Object") {
|
|
95
|
-
|
|
96
|
-
if (getType(qs[prop]) ===
|
|
66
|
+
for (var prop in qs) {
|
|
67
|
+
if (qs.hasOwnProperty(prop) && getType(qs[prop]) === "Object") {
|
|
97
68
|
qs[prop] = JSON.stringify(qs[prop]);
|
|
98
69
|
}
|
|
99
|
-
}
|
|
70
|
+
}
|
|
100
71
|
}
|
|
101
|
-
|
|
102
|
-
headers: getHeaders(url, options, ctx
|
|
72
|
+
var op = {
|
|
73
|
+
headers: getHeaders(url, options, ctx),
|
|
103
74
|
timeout: 60000,
|
|
104
|
-
qs,
|
|
105
|
-
|
|
106
|
-
|
|
75
|
+
qs: qs,
|
|
76
|
+
url: url,
|
|
77
|
+
method: "GET",
|
|
78
|
+
jar: jar,
|
|
79
|
+
gzip: true,
|
|
107
80
|
};
|
|
108
|
-
|
|
109
|
-
|
|
81
|
+
|
|
82
|
+
return request(op).then(function (res) {
|
|
83
|
+
return res[0];
|
|
110
84
|
});
|
|
111
85
|
}
|
|
112
86
|
|
|
113
87
|
function post(url, jar, form, options, ctx, customHeader) {
|
|
114
|
-
|
|
88
|
+
var op = {
|
|
115
89
|
headers: getHeaders(url, options, ctx, customHeader),
|
|
116
90
|
timeout: 60000,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
91
|
+
url: url,
|
|
92
|
+
method: "POST",
|
|
93
|
+
form: form,
|
|
94
|
+
jar: jar,
|
|
95
|
+
gzip: true,
|
|
120
96
|
};
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
|
|
98
|
+
return request(op).then(function (res) {
|
|
99
|
+
return res[0];
|
|
123
100
|
});
|
|
124
101
|
}
|
|
125
102
|
|
|
126
103
|
function postFormData(url, jar, form, qs, options, ctx) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
const op = {
|
|
135
|
-
headers: getHeaders(url, options, ctx, { 'content-type': 'multipart/form-data' }),
|
|
104
|
+
var headers = getHeaders(url, options, ctx);
|
|
105
|
+
headers["Content-Type"] = "multipart/form-data";
|
|
106
|
+
var op = {
|
|
107
|
+
headers: headers,
|
|
136
108
|
timeout: 60000,
|
|
109
|
+
url: url,
|
|
110
|
+
method: "POST",
|
|
137
111
|
formData: form,
|
|
138
|
-
qs,
|
|
139
|
-
jar,
|
|
140
|
-
gzip: true
|
|
112
|
+
qs: qs,
|
|
113
|
+
jar: jar,
|
|
114
|
+
gzip: true,
|
|
141
115
|
};
|
|
142
|
-
|
|
143
|
-
|
|
116
|
+
|
|
117
|
+
return request(op).then(function (res) {
|
|
118
|
+
return res[0];
|
|
144
119
|
});
|
|
145
120
|
}
|
|
146
121
|
|
|
147
|
-
function padZeros(val, len
|
|
148
|
-
|
|
122
|
+
function padZeros(val, len) {
|
|
123
|
+
val = String(val);
|
|
124
|
+
len = len || 2;
|
|
125
|
+
while (val.length < len) val = "0" + val;
|
|
126
|
+
return val;
|
|
149
127
|
}
|
|
150
128
|
|
|
151
129
|
function generateThreadingID(clientID) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
130
|
+
var k = Date.now();
|
|
131
|
+
var l = Math.floor(Math.random() * 4294967295);
|
|
132
|
+
var m = clientID;
|
|
133
|
+
return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getCurrentTimestamp() {
|
|
137
|
+
const date = new Date();
|
|
138
|
+
const unixTime = date.getTime();
|
|
139
|
+
return unixTime;
|
|
155
140
|
}
|
|
156
141
|
|
|
157
142
|
function binaryToDecimal(data) {
|
|
158
|
-
|
|
143
|
+
var ret = "";
|
|
159
144
|
while (data !== "0") {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
145
|
+
var end = 0;
|
|
146
|
+
var fullName = "";
|
|
147
|
+
var i = 0;
|
|
148
|
+
for (; i < data.length; i++) {
|
|
163
149
|
end = 2 * end + parseInt(data[i], 10);
|
|
164
150
|
if (end >= 10) {
|
|
165
151
|
fullName += "1";
|
|
@@ -169,36 +155,53 @@ function binaryToDecimal(data) {
|
|
|
169
155
|
}
|
|
170
156
|
}
|
|
171
157
|
ret = end.toString() + ret;
|
|
172
|
-
|
|
173
|
-
data = firstOne === -1 ? "0" : fullName.slice(firstOne);
|
|
158
|
+
data = fullName.slice(fullName.indexOf("1"));
|
|
174
159
|
}
|
|
175
160
|
return ret;
|
|
176
161
|
}
|
|
177
162
|
|
|
178
163
|
function generateOfflineThreadingID() {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
164
|
+
var ret = Date.now();
|
|
165
|
+
var value = Math.floor(Math.random() * 4294967295);
|
|
166
|
+
var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
|
|
167
|
+
var msgs = ret.toString(2) + str;
|
|
168
|
+
return binaryToDecimal(msgs);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
var h;
|
|
172
|
+
var i = {};
|
|
173
|
+
var j = {
|
|
174
|
+
_: "%",
|
|
175
|
+
A: "%2",
|
|
176
|
+
B: "000",
|
|
177
|
+
C: "%7d",
|
|
178
|
+
D: "%7b%22",
|
|
179
|
+
E: "%2c%22",
|
|
180
|
+
F: "%22%3a",
|
|
181
|
+
G: "%2c%22ut%22%3a1",
|
|
182
|
+
H: "%2c%22bls%22%3a",
|
|
183
|
+
I: "%2c%22n%22%3a%22%",
|
|
184
|
+
J: "%22%3a%7b%22i%22%3a0%7d",
|
|
185
|
+
K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
|
|
186
|
+
L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
|
|
187
|
+
M: "%7b%22v%22%3a2%2c%22time%22%3a1",
|
|
188
|
+
N: ".channel%22%2c%22sub%22%3a%5b",
|
|
189
|
+
O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
|
|
190
|
+
P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
|
|
191
|
+
Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
|
|
192
|
+
R: ".channel%22%2c%22sub%22%3a%5b1%5d",
|
|
193
|
+
S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
|
|
194
|
+
T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
|
|
195
|
+
U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
|
|
196
|
+
V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
|
|
197
|
+
W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
|
|
198
|
+
X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
|
|
195
199
|
Y: "%2c%22pt%22%3a0%2c%22vis%22%3a1%2c%22bls%22%3a0%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
|
|
196
|
-
Z: "%2c%22sb%22%3a1%2c%22t%22%3a%5b%5d%2c%22f%22%3anull%2c%22uct%22%3a0%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a"
|
|
200
|
+
Z: "%2c%22sb%22%3a1%2c%22t%22%3a%5b%5d%2c%22f%22%3anull%2c%22uct%22%3a0%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
|
|
197
201
|
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
for (const m in j) {
|
|
202
|
+
(function () {
|
|
203
|
+
var l = [];
|
|
204
|
+
for (var m in j) {
|
|
202
205
|
i[j[m]] = m;
|
|
203
206
|
l.push(j[m]);
|
|
204
207
|
}
|
|
@@ -208,286 +211,593 @@ const j = {
|
|
|
208
211
|
|
|
209
212
|
function presenceEncode(str) {
|
|
210
213
|
return encodeURIComponent(str)
|
|
211
|
-
.replace(/([_A-Z])|%../g, (m, n)
|
|
214
|
+
.replace(/([_A-Z])|%../g, function (m, n) {
|
|
215
|
+
return n ? "%" + n.charCodeAt(0).toString(16) : m;
|
|
216
|
+
})
|
|
212
217
|
.toLowerCase()
|
|
213
|
-
.replace(h,
|
|
218
|
+
.replace(h, function (m) {
|
|
219
|
+
return i[m];
|
|
220
|
+
});
|
|
214
221
|
}
|
|
215
222
|
|
|
223
|
+
// eslint-disable-next-line no-unused-vars
|
|
216
224
|
function presenceDecode(str) {
|
|
217
|
-
return decodeURIComponent(
|
|
225
|
+
return decodeURIComponent(
|
|
226
|
+
str.replace(/[_A-Z]/g, function (m) {
|
|
227
|
+
return j[m];
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
218
230
|
}
|
|
219
231
|
|
|
220
232
|
function generatePresence(userID) {
|
|
221
|
-
|
|
222
|
-
return
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
var time = Date.now();
|
|
234
|
+
return (
|
|
235
|
+
"E" +
|
|
236
|
+
presenceEncode(
|
|
237
|
+
JSON.stringify({
|
|
238
|
+
v: 3,
|
|
239
|
+
time: parseInt(time / 1000, 10),
|
|
240
|
+
user: userID,
|
|
241
|
+
state: {
|
|
242
|
+
ut: 0,
|
|
243
|
+
t2: [],
|
|
244
|
+
lm2: null,
|
|
245
|
+
uct2: time,
|
|
246
|
+
tr: null,
|
|
247
|
+
tw: Math.floor(Math.random() * 4294967295) + 1,
|
|
248
|
+
at: time,
|
|
249
|
+
},
|
|
250
|
+
ch: {
|
|
251
|
+
["p_" + userID]: 0,
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
)
|
|
255
|
+
);
|
|
232
256
|
}
|
|
233
257
|
|
|
234
258
|
function generateAccessiblityCookie() {
|
|
235
|
-
|
|
236
|
-
return encodeURIComponent(
|
|
259
|
+
var time = Date.now();
|
|
260
|
+
return encodeURIComponent(
|
|
261
|
+
JSON.stringify({
|
|
262
|
+
sr: 0,
|
|
263
|
+
"sr-ts": time,
|
|
264
|
+
jk: 0,
|
|
265
|
+
"jk-ts": time,
|
|
266
|
+
kb: 0,
|
|
267
|
+
"kb-ts": time,
|
|
268
|
+
hcm: 0,
|
|
269
|
+
"hcm-ts": time,
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
237
272
|
}
|
|
238
273
|
|
|
239
274
|
function getGUID() {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
275
|
+
/** @type {number} */
|
|
276
|
+
var sectionLength = Date.now();
|
|
277
|
+
/** @type {string} */
|
|
278
|
+
var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
|
279
|
+
/[xy]/g,
|
|
280
|
+
function (c) {
|
|
281
|
+
/** @type {number} */
|
|
282
|
+
var r = Math.floor((sectionLength + Math.random() * 16) % 16);
|
|
283
|
+
/** @type {number} */
|
|
284
|
+
sectionLength = Math.floor(sectionLength / 16);
|
|
285
|
+
/** @type {string} */
|
|
286
|
+
var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
|
|
287
|
+
return _guid;
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
return id;
|
|
252
291
|
}
|
|
253
292
|
|
|
254
293
|
function _formatAttachment(attachment1, attachment2) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
294
|
+
// TODO: THIS IS REALLY BAD
|
|
295
|
+
// This is an attempt at fixing Facebook's inconsistencies. Sometimes they give us
|
|
296
|
+
// two attachment objects, but sometimes only one. They each contain part of the
|
|
297
|
+
// data that you'd want so we merge them for convenience.
|
|
298
|
+
// Instead of having a bunch of if statements guarding every access to image_data,
|
|
299
|
+
// we set it to empty object and use the fact that it'll return undefined.
|
|
261
300
|
attachment2 = attachment2 || { id: "", image_data: {} };
|
|
262
|
-
attachment1 = attachment1.mercury
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
301
|
+
attachment1 = attachment1.mercury ? attachment1.mercury : attachment1;
|
|
302
|
+
var blob = attachment1.blob_attachment;
|
|
303
|
+
var type =
|
|
304
|
+
blob && blob.__typename ? blob.__typename : attachment1.attach_type;
|
|
266
305
|
if (!type && attachment1.sticker_attachment) {
|
|
267
306
|
type = "StickerAttachment";
|
|
268
307
|
blob = attachment1.sticker_attachment;
|
|
269
308
|
} else if (!type && attachment1.extensible_attachment) {
|
|
270
|
-
if (
|
|
309
|
+
if (
|
|
310
|
+
attachment1.extensible_attachment.story_attachment &&
|
|
311
|
+
attachment1.extensible_attachment.story_attachment.target &&
|
|
312
|
+
attachment1.extensible_attachment.story_attachment.target.__typename &&
|
|
313
|
+
attachment1.extensible_attachment.story_attachment.target.__typename ===
|
|
314
|
+
"MessageLocation"
|
|
315
|
+
) {
|
|
271
316
|
type = "MessageLocation";
|
|
272
317
|
} else {
|
|
273
318
|
type = "ExtensibleAttachment";
|
|
274
319
|
}
|
|
320
|
+
|
|
275
321
|
blob = attachment1.extensible_attachment;
|
|
276
322
|
}
|
|
277
|
-
|
|
323
|
+
// TODO: Determine whether "sticker", "photo", "file" etc are still used
|
|
324
|
+
// KEEP IN SYNC WITH getThreadHistory
|
|
278
325
|
switch (type) {
|
|
279
326
|
case "sticker":
|
|
280
327
|
return {
|
|
281
|
-
type: "sticker",
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
328
|
+
type: "sticker",
|
|
329
|
+
ID: attachment1.metadata.stickerID.toString(),
|
|
330
|
+
url: attachment1.url,
|
|
331
|
+
|
|
332
|
+
packID: attachment1.metadata.packID.toString(),
|
|
333
|
+
spriteUrl: attachment1.metadata.spriteURI,
|
|
334
|
+
spriteUrl2x: attachment1.metadata.spriteURI2x,
|
|
335
|
+
width: attachment1.metadata.width,
|
|
336
|
+
height: attachment1.metadata.height,
|
|
337
|
+
|
|
338
|
+
caption: attachment2.caption,
|
|
339
|
+
description: attachment2.description,
|
|
340
|
+
|
|
341
|
+
frameCount: attachment1.metadata.frameCount,
|
|
342
|
+
frameRate: attachment1.metadata.frameRate,
|
|
343
|
+
framesPerRow: attachment1.metadata.framesPerRow,
|
|
344
|
+
framesPerCol: attachment1.metadata.framesPerCol,
|
|
345
|
+
|
|
346
|
+
stickerID: attachment1.metadata.stickerID.toString(), // @Legacy
|
|
347
|
+
spriteURI: attachment1.metadata.spriteURI, // @Legacy
|
|
348
|
+
spriteURI2x: attachment1.metadata.spriteURI2x, // @Legacy
|
|
287
349
|
};
|
|
288
350
|
case "file":
|
|
289
351
|
return {
|
|
290
|
-
type: "file",
|
|
291
|
-
|
|
292
|
-
|
|
352
|
+
type: "file",
|
|
353
|
+
filename: attachment1.name,
|
|
354
|
+
ID: attachment2.id.toString(),
|
|
355
|
+
url: attachment1.url,
|
|
356
|
+
|
|
357
|
+
isMalicious: attachment2.is_malicious,
|
|
358
|
+
contentType: attachment2.mime_type,
|
|
359
|
+
|
|
360
|
+
name: attachment1.name, // @Legacy
|
|
361
|
+
mimeType: attachment2.mime_type, // @Legacy
|
|
362
|
+
fileSize: attachment2.file_size, // @Legacy
|
|
293
363
|
};
|
|
294
364
|
case "photo":
|
|
295
365
|
return {
|
|
296
|
-
type: "photo",
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
366
|
+
type: "photo",
|
|
367
|
+
ID: attachment1.metadata.fbid.toString(),
|
|
368
|
+
filename: attachment1.fileName,
|
|
369
|
+
thumbnailUrl: attachment1.thumbnail_url,
|
|
370
|
+
|
|
371
|
+
previewUrl: attachment1.preview_url,
|
|
372
|
+
previewWidth: attachment1.preview_width,
|
|
373
|
+
previewHeight: attachment1.preview_height,
|
|
374
|
+
|
|
375
|
+
largePreviewUrl: attachment1.large_preview_url,
|
|
376
|
+
largePreviewWidth: attachment1.large_preview_width,
|
|
377
|
+
largePreviewHeight: attachment1.large_preview_height,
|
|
378
|
+
|
|
379
|
+
url: attachment1.metadata.url, // @Legacy
|
|
380
|
+
width: attachment1.metadata.dimensions.split(",")[0], // @Legacy
|
|
381
|
+
height: attachment1.metadata.dimensions.split(",")[1], // @Legacy
|
|
382
|
+
name: attachment1.fileName, // @Legacy
|
|
302
383
|
};
|
|
303
384
|
case "animated_image":
|
|
304
385
|
return {
|
|
305
|
-
type: "animated_image",
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
386
|
+
type: "animated_image",
|
|
387
|
+
ID: attachment2.id.toString(),
|
|
388
|
+
filename: attachment2.filename,
|
|
389
|
+
|
|
390
|
+
previewUrl: attachment1.preview_url,
|
|
391
|
+
previewWidth: attachment1.preview_width,
|
|
392
|
+
previewHeight: attachment1.preview_height,
|
|
393
|
+
|
|
394
|
+
url: attachment2.image_data.url,
|
|
395
|
+
width: attachment2.image_data.width,
|
|
396
|
+
height: attachment2.image_data.height,
|
|
397
|
+
|
|
398
|
+
name: attachment1.name, // @Legacy
|
|
399
|
+
facebookUrl: attachment1.url, // @Legacy
|
|
400
|
+
thumbnailUrl: attachment1.thumbnail_url, // @Legacy
|
|
401
|
+
mimeType: attachment2.mime_type, // @Legacy
|
|
402
|
+
rawGifImage: attachment2.image_data.raw_gif_image, // @Legacy
|
|
403
|
+
rawWebpImage: attachment2.image_data.raw_webp_image, // @Legacy
|
|
404
|
+
animatedGifUrl: attachment2.image_data.animated_gif_url, // @Legacy
|
|
405
|
+
animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url, // @Legacy
|
|
406
|
+
animatedWebpUrl: attachment2.image_data.animated_webp_url, // @Legacy
|
|
407
|
+
animatedWebpPreviewUrl:
|
|
408
|
+
attachment2.image_data.animated_webp_preview_url, // @Legacy
|
|
312
409
|
};
|
|
313
410
|
case "share":
|
|
314
411
|
return {
|
|
315
|
-
type: "share",
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
412
|
+
type: "share",
|
|
413
|
+
ID: attachment1.share.share_id.toString(),
|
|
414
|
+
url: attachment2.href,
|
|
415
|
+
|
|
416
|
+
title: attachment1.share.title,
|
|
417
|
+
description: attachment1.share.description,
|
|
418
|
+
source: attachment1.share.source,
|
|
419
|
+
|
|
420
|
+
image: attachment1.share.media.image,
|
|
421
|
+
width: attachment1.share.media.image_size.width,
|
|
422
|
+
height: attachment1.share.media.image_size.height,
|
|
423
|
+
playable: attachment1.share.media.playable,
|
|
424
|
+
duration: attachment1.share.media.duration,
|
|
425
|
+
|
|
426
|
+
subattachments: attachment1.share.subattachments,
|
|
427
|
+
properties: {},
|
|
428
|
+
|
|
429
|
+
animatedImageSize: attachment1.share.media.animated_image_size, // @Legacy
|
|
430
|
+
facebookUrl: attachment1.share.uri, // @Legacy
|
|
431
|
+
target: attachment1.share.target, // @Legacy
|
|
432
|
+
styleList: attachment1.share.style_list, // @Legacy
|
|
320
433
|
};
|
|
321
434
|
case "video":
|
|
322
435
|
return {
|
|
323
|
-
type: "video",
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
436
|
+
type: "video",
|
|
437
|
+
ID: attachment1.metadata.fbid.toString(),
|
|
438
|
+
filename: attachment1.name,
|
|
439
|
+
|
|
440
|
+
previewUrl: attachment1.preview_url,
|
|
441
|
+
previewWidth: attachment1.preview_width,
|
|
442
|
+
previewHeight: attachment1.preview_height,
|
|
443
|
+
|
|
444
|
+
url: attachment1.url,
|
|
445
|
+
width: attachment1.metadata.dimensions.width,
|
|
446
|
+
height: attachment1.metadata.dimensions.height,
|
|
447
|
+
|
|
448
|
+
duration: attachment1.metadata.duration,
|
|
449
|
+
videoType: "unknown",
|
|
450
|
+
|
|
451
|
+
thumbnailUrl: attachment1.thumbnail_url, // @Legacy
|
|
328
452
|
};
|
|
329
453
|
case "error":
|
|
330
|
-
return {
|
|
454
|
+
return {
|
|
455
|
+
type: "error",
|
|
456
|
+
|
|
457
|
+
// Save error attachments because we're unsure of their format,
|
|
458
|
+
// and whether there are cases they contain something useful for debugging.
|
|
459
|
+
attachment1: attachment1,
|
|
460
|
+
attachment2: attachment2,
|
|
461
|
+
};
|
|
331
462
|
case "MessageImage":
|
|
332
463
|
return {
|
|
333
|
-
type: "photo",
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
464
|
+
type: "photo",
|
|
465
|
+
ID: blob.legacy_attachment_id,
|
|
466
|
+
filename: blob.filename,
|
|
467
|
+
thumbnailUrl: blob.thumbnail.uri,
|
|
468
|
+
|
|
469
|
+
previewUrl: blob.preview.uri,
|
|
470
|
+
previewWidth: blob.preview.width,
|
|
471
|
+
previewHeight: blob.preview.height,
|
|
472
|
+
|
|
473
|
+
largePreviewUrl: blob.large_preview.uri,
|
|
474
|
+
largePreviewWidth: blob.large_preview.width,
|
|
475
|
+
largePreviewHeight: blob.large_preview.height,
|
|
476
|
+
|
|
477
|
+
url: blob.large_preview.uri, // @Legacy
|
|
478
|
+
width: blob.original_dimensions.x, // @Legacy
|
|
479
|
+
height: blob.original_dimensions.y, // @Legacy
|
|
480
|
+
name: blob.filename, // @Legacy
|
|
338
481
|
};
|
|
339
482
|
case "MessageAnimatedImage":
|
|
340
483
|
return {
|
|
341
|
-
type: "animated_image",
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
484
|
+
type: "animated_image",
|
|
485
|
+
ID: blob.legacy_attachment_id,
|
|
486
|
+
filename: blob.filename,
|
|
487
|
+
|
|
488
|
+
previewUrl: blob.preview_image.uri,
|
|
489
|
+
previewWidth: blob.preview_image.width,
|
|
490
|
+
previewHeight: blob.preview_image.height,
|
|
491
|
+
|
|
492
|
+
url: blob.animated_image.uri,
|
|
493
|
+
width: blob.animated_image.width,
|
|
494
|
+
height: blob.animated_image.height,
|
|
495
|
+
|
|
496
|
+
thumbnailUrl: blob.preview_image.uri, // @Legacy
|
|
497
|
+
name: blob.filename, // @Legacy
|
|
498
|
+
facebookUrl: blob.animated_image.uri, // @Legacy
|
|
499
|
+
rawGifImage: blob.animated_image.uri, // @Legacy
|
|
500
|
+
animatedGifUrl: blob.animated_image.uri, // @Legacy
|
|
501
|
+
animatedGifPreviewUrl: blob.preview_image.uri, // @Legacy
|
|
502
|
+
animatedWebpUrl: blob.animated_image.uri, // @Legacy
|
|
503
|
+
animatedWebpPreviewUrl: blob.preview_image.uri, // @Legacy
|
|
347
504
|
};
|
|
348
505
|
case "MessageVideo":
|
|
349
506
|
return {
|
|
350
|
-
type: "video",
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
507
|
+
type: "video",
|
|
508
|
+
filename: blob.filename,
|
|
509
|
+
ID: blob.legacy_attachment_id,
|
|
510
|
+
|
|
511
|
+
previewUrl: blob.large_image.uri,
|
|
512
|
+
previewWidth: blob.large_image.width,
|
|
513
|
+
previewHeight: blob.large_image.height,
|
|
514
|
+
|
|
515
|
+
url: blob.playable_url,
|
|
516
|
+
width: blob.original_dimensions.x,
|
|
517
|
+
height: blob.original_dimensions.y,
|
|
518
|
+
|
|
519
|
+
duration: blob.playable_duration_in_ms,
|
|
520
|
+
videoType: blob.video_type.toLowerCase(),
|
|
521
|
+
|
|
522
|
+
thumbnailUrl: blob.large_image.uri, // @Legacy
|
|
355
523
|
};
|
|
356
524
|
case "MessageAudio":
|
|
357
525
|
return {
|
|
358
|
-
type: "audio",
|
|
359
|
-
|
|
360
|
-
|
|
526
|
+
type: "audio",
|
|
527
|
+
filename: blob.filename,
|
|
528
|
+
ID: blob.url_shimhash,
|
|
529
|
+
|
|
530
|
+
audioType: blob.audio_type,
|
|
531
|
+
duration: blob.playable_duration_in_ms,
|
|
532
|
+
url: blob.playable_url,
|
|
533
|
+
|
|
534
|
+
isVoiceMail: blob.is_voicemail,
|
|
361
535
|
};
|
|
362
536
|
case "StickerAttachment":
|
|
363
|
-
case "Sticker":
|
|
364
537
|
return {
|
|
365
|
-
type: "sticker",
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
538
|
+
type: "sticker",
|
|
539
|
+
ID: blob.id,
|
|
540
|
+
url: blob.url,
|
|
541
|
+
|
|
542
|
+
packID: blob.pack ? blob.pack.id : null,
|
|
543
|
+
spriteUrl: blob.sprite_image,
|
|
544
|
+
spriteUrl2x: blob.sprite_image_2x,
|
|
545
|
+
width: blob.width,
|
|
546
|
+
height: blob.height,
|
|
547
|
+
|
|
548
|
+
caption: blob.label,
|
|
549
|
+
description: blob.label,
|
|
550
|
+
|
|
551
|
+
frameCount: blob.frame_count,
|
|
552
|
+
frameRate: blob.frame_rate,
|
|
553
|
+
framesPerRow: blob.frames_per_row,
|
|
554
|
+
framesPerCol: blob.frames_per_column,
|
|
555
|
+
|
|
556
|
+
stickerID: blob.id, // @Legacy
|
|
557
|
+
spriteURI: blob.sprite_image, // @Legacy
|
|
558
|
+
spriteURI2x: blob.sprite_image_2x, // @Legacy
|
|
369
559
|
};
|
|
370
560
|
case "MessageLocation":
|
|
561
|
+
var urlAttach = blob.story_attachment.url;
|
|
562
|
+
var mediaAttach = blob.story_attachment.media;
|
|
563
|
+
|
|
564
|
+
var u = querystring.parse(url.parse(urlAttach).query).u;
|
|
565
|
+
var where1 = querystring.parse(url.parse(u).query).where1;
|
|
566
|
+
var address = where1.split(", ");
|
|
567
|
+
|
|
568
|
+
var latitude;
|
|
569
|
+
var longitude;
|
|
570
|
+
|
|
371
571
|
try {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const u = querystring.parse(url.parse(urlAttach).query).u;
|
|
375
|
-
const where1 = querystring.parse(url.parse(u).query).where1;
|
|
376
|
-
const address = where1.split(", ");
|
|
377
|
-
let latitude = Number.parseFloat(address[0]);
|
|
378
|
-
let longitude = Number.parseFloat(address[1]);
|
|
379
|
-
let imageUrl, width, height;
|
|
380
|
-
if (mediaAttach?.image) {
|
|
381
|
-
imageUrl = mediaAttach.image.uri;
|
|
382
|
-
width = mediaAttach.image.width;
|
|
383
|
-
height = mediaAttach.image.height;
|
|
384
|
-
}
|
|
385
|
-
return {
|
|
386
|
-
type: "location", ID: blob.legacy_attachment_id, latitude, longitude, image: imageUrl, width, height,
|
|
387
|
-
url: u || urlAttach, address: where1, facebookUrl: blob.story_attachment.url, target: blob.story_attachment.target,
|
|
388
|
-
styleList: blob.story_attachment.style_list
|
|
389
|
-
};
|
|
572
|
+
latitude = Number.parseFloat(address[0]);
|
|
573
|
+
longitude = Number.parseFloat(address[1]);
|
|
390
574
|
} catch (err) {
|
|
391
|
-
|
|
575
|
+
/* empty */
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
var imageUrl;
|
|
579
|
+
var width;
|
|
580
|
+
var height;
|
|
581
|
+
|
|
582
|
+
if (mediaAttach && mediaAttach.image) {
|
|
583
|
+
imageUrl = mediaAttach.image.uri;
|
|
584
|
+
width = mediaAttach.image.width;
|
|
585
|
+
height = mediaAttach.image.height;
|
|
392
586
|
}
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
type: "location",
|
|
590
|
+
ID: blob.legacy_attachment_id,
|
|
591
|
+
latitude: latitude,
|
|
592
|
+
longitude: longitude,
|
|
593
|
+
image: imageUrl,
|
|
594
|
+
width: width,
|
|
595
|
+
height: height,
|
|
596
|
+
url: u || urlAttach,
|
|
597
|
+
address: where1,
|
|
598
|
+
|
|
599
|
+
facebookUrl: blob.story_attachment.url, // @Legacy
|
|
600
|
+
target: blob.story_attachment.target, // @Legacy
|
|
601
|
+
styleList: blob.story_attachment.style_list, // @Legacy
|
|
602
|
+
};
|
|
393
603
|
case "ExtensibleAttachment":
|
|
394
604
|
return {
|
|
395
|
-
type: "share",
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
605
|
+
type: "share",
|
|
606
|
+
ID: blob.legacy_attachment_id,
|
|
607
|
+
url: blob.story_attachment.url,
|
|
608
|
+
|
|
609
|
+
title: blob.story_attachment.title_with_entities.text,
|
|
610
|
+
description:
|
|
611
|
+
blob.story_attachment.description &&
|
|
612
|
+
blob.story_attachment.description.text,
|
|
613
|
+
source: blob.story_attachment.source
|
|
614
|
+
? blob.story_attachment.source.text
|
|
615
|
+
: null,
|
|
616
|
+
|
|
617
|
+
image:
|
|
618
|
+
blob.story_attachment.media &&
|
|
619
|
+
blob.story_attachment.media.image &&
|
|
620
|
+
blob.story_attachment.media.image.uri,
|
|
621
|
+
width:
|
|
622
|
+
blob.story_attachment.media &&
|
|
623
|
+
blob.story_attachment.media.image &&
|
|
624
|
+
blob.story_attachment.media.image.width,
|
|
625
|
+
height:
|
|
626
|
+
blob.story_attachment.media &&
|
|
627
|
+
blob.story_attachment.media.image &&
|
|
628
|
+
blob.story_attachment.media.image.height,
|
|
629
|
+
playable:
|
|
630
|
+
blob.story_attachment.media &&
|
|
631
|
+
blob.story_attachment.media.is_playable,
|
|
632
|
+
duration:
|
|
633
|
+
blob.story_attachment.media &&
|
|
634
|
+
blob.story_attachment.media.playable_duration_in_ms,
|
|
635
|
+
playableUrl:
|
|
636
|
+
blob.story_attachment.media == null
|
|
637
|
+
? null
|
|
638
|
+
: blob.story_attachment.media.playable_url,
|
|
639
|
+
|
|
640
|
+
subattachments: blob.story_attachment.subattachments,
|
|
641
|
+
properties: blob.story_attachment.properties.reduce(function (
|
|
642
|
+
obj,
|
|
643
|
+
cur,
|
|
644
|
+
) {
|
|
401
645
|
obj[cur.key] = cur.value.text;
|
|
402
646
|
return obj;
|
|
403
647
|
}, {}),
|
|
404
|
-
|
|
648
|
+
|
|
649
|
+
facebookUrl: blob.story_attachment.url, // @Legacy
|
|
650
|
+
target: blob.story_attachment.target, // @Legacy
|
|
651
|
+
styleList: blob.story_attachment.style_list, // @Legacy
|
|
405
652
|
};
|
|
406
653
|
case "MessageFile":
|
|
407
654
|
return {
|
|
408
|
-
type: "file",
|
|
409
|
-
|
|
410
|
-
|
|
655
|
+
type: "file",
|
|
656
|
+
filename: blob.filename,
|
|
657
|
+
ID: blob.message_file_fbid,
|
|
658
|
+
|
|
659
|
+
url: blob.url,
|
|
660
|
+
isMalicious: blob.is_malicious,
|
|
661
|
+
contentType: blob.content_type,
|
|
662
|
+
|
|
663
|
+
name: blob.filename,
|
|
664
|
+
mimeType: "",
|
|
665
|
+
fileSize: -1,
|
|
411
666
|
};
|
|
412
667
|
default:
|
|
413
|
-
throw new Error(
|
|
668
|
+
throw new Error(
|
|
669
|
+
"unrecognized attach_file of type " +
|
|
670
|
+
type +
|
|
671
|
+
"`" +
|
|
672
|
+
JSON.stringify(attachment1, null, 4) +
|
|
673
|
+
" attachment2: " +
|
|
674
|
+
JSON.stringify(attachment2, null, 4) +
|
|
675
|
+
"`",
|
|
676
|
+
);
|
|
414
677
|
}
|
|
415
678
|
}
|
|
416
679
|
|
|
417
680
|
function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
|
|
418
|
-
|
|
419
|
-
return attachments
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
681
|
+
attachmentMap = shareMap || attachmentMap;
|
|
682
|
+
return attachments
|
|
683
|
+
? attachments.map(function (val, i) {
|
|
684
|
+
if (
|
|
685
|
+
!attachmentMap ||
|
|
686
|
+
!attachmentIds ||
|
|
687
|
+
!attachmentMap[attachmentIds[i]]
|
|
688
|
+
) {
|
|
689
|
+
return _formatAttachment(val);
|
|
690
|
+
}
|
|
691
|
+
return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
|
|
692
|
+
})
|
|
693
|
+
: [];
|
|
425
694
|
}
|
|
426
695
|
|
|
427
696
|
function formatDeltaMessage(m) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
697
|
+
var md = m.delta.messageMetadata;
|
|
698
|
+
|
|
699
|
+
var mdata =
|
|
700
|
+
m.delta.data === undefined
|
|
701
|
+
? []
|
|
702
|
+
: m.delta.data.prng === undefined
|
|
703
|
+
? []
|
|
704
|
+
: JSON.parse(m.delta.data.prng);
|
|
705
|
+
var m_id = mdata.map((u) => u.i);
|
|
706
|
+
var m_offset = mdata.map((u) => u.o);
|
|
707
|
+
var m_length = mdata.map((u) => u.l);
|
|
708
|
+
var mentions = {};
|
|
709
|
+
var body = m.delta.body || "";
|
|
710
|
+
var args = body == "" ? [] : body.trim().split(/\s+/);
|
|
711
|
+
for (var i = 0; i < m_id.length; i++) {
|
|
712
|
+
mentions[m_id[i]] = m.delta.body.substring(
|
|
713
|
+
m_offset[i],
|
|
714
|
+
m_offset[i] + m_length[i],
|
|
715
|
+
);
|
|
436
716
|
}
|
|
437
717
|
|
|
438
718
|
return {
|
|
439
719
|
type: "message",
|
|
440
720
|
senderID: formatID(md.actorFbId.toString()),
|
|
441
|
-
|
|
442
|
-
|
|
721
|
+
threadID: formatID(
|
|
722
|
+
(md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString(),
|
|
723
|
+
),
|
|
724
|
+
args: args,
|
|
725
|
+
body: body,
|
|
443
726
|
messageID: md.messageId,
|
|
444
|
-
attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
|
|
727
|
+
attachments: (m.delta.attachments || []).map((v) => _formatAttachment(v)),
|
|
445
728
|
mentions: mentions,
|
|
446
729
|
timestamp: md.timestamp,
|
|
447
730
|
isGroup: !!md.threadKey.threadFbId,
|
|
448
|
-
participantIDs: m.delta.participants
|
|
731
|
+
participantIDs: m.delta.participants || [],
|
|
449
732
|
};
|
|
450
733
|
}
|
|
451
734
|
|
|
452
735
|
function formatID(id) {
|
|
453
|
-
|
|
736
|
+
if (id != undefined && id != null) {
|
|
737
|
+
return id.replace(/(fb)?id[:.]/, "");
|
|
738
|
+
} else {
|
|
739
|
+
return id;
|
|
740
|
+
}
|
|
454
741
|
}
|
|
455
742
|
|
|
456
743
|
function formatMessage(m) {
|
|
457
|
-
|
|
458
|
-
|
|
744
|
+
var originalMessage = m.message ? m.message : m;
|
|
745
|
+
var obj = {
|
|
459
746
|
type: "message",
|
|
460
747
|
senderName: originalMessage.sender_name,
|
|
461
748
|
senderID: formatID(originalMessage.sender_fbid.toString()),
|
|
462
|
-
participantNames: originalMessage.group_thread_info
|
|
463
|
-
|
|
749
|
+
participantNames: originalMessage.group_thread_info
|
|
750
|
+
? originalMessage.group_thread_info.participant_names
|
|
751
|
+
: [originalMessage.sender_name.split(" ")[0]],
|
|
752
|
+
participantIDs: originalMessage.group_thread_info
|
|
753
|
+
? originalMessage.group_thread_info.participant_ids.map(function (v) {
|
|
754
|
+
return formatID(v.toString());
|
|
755
|
+
})
|
|
756
|
+
: [formatID(originalMessage.sender_fbid)],
|
|
464
757
|
body: originalMessage.body || "",
|
|
465
|
-
threadID: formatID(
|
|
466
|
-
|
|
758
|
+
threadID: formatID(
|
|
759
|
+
(
|
|
760
|
+
originalMessage.thread_fbid || originalMessage.other_user_fbid
|
|
761
|
+
).toString(),
|
|
762
|
+
),
|
|
763
|
+
threadName: originalMessage.group_thread_info
|
|
764
|
+
? originalMessage.group_thread_info.name
|
|
765
|
+
: originalMessage.sender_name,
|
|
467
766
|
location: originalMessage.coordinates ? originalMessage.coordinates : null,
|
|
468
|
-
messageID: originalMessage.mid
|
|
469
|
-
|
|
767
|
+
messageID: originalMessage.mid
|
|
768
|
+
? originalMessage.mid.toString()
|
|
769
|
+
: originalMessage.message_id,
|
|
770
|
+
attachments: formatAttachment(
|
|
771
|
+
originalMessage.attachments,
|
|
772
|
+
originalMessage.attachmentIds,
|
|
773
|
+
originalMessage.attachment_map,
|
|
774
|
+
originalMessage.share_map,
|
|
775
|
+
),
|
|
470
776
|
timestamp: originalMessage.timestamp,
|
|
471
777
|
timestampAbsolute: originalMessage.timestamp_absolute,
|
|
472
778
|
timestampRelative: originalMessage.timestamp_relative,
|
|
473
779
|
timestampDatetime: originalMessage.timestamp_datetime,
|
|
474
780
|
tags: originalMessage.tags,
|
|
475
781
|
reactions: originalMessage.reactions ? originalMessage.reactions : [],
|
|
476
|
-
isUnread: originalMessage.is_unread
|
|
782
|
+
isUnread: originalMessage.is_unread,
|
|
477
783
|
};
|
|
478
784
|
|
|
479
|
-
if (m.type === "pages_messaging")
|
|
785
|
+
if (m.type === "pages_messaging")
|
|
786
|
+
obj.pageID = m.realtime_viewer_fbid.toString();
|
|
480
787
|
obj.isGroup = obj.participantIDs.length > 2;
|
|
788
|
+
|
|
481
789
|
return obj;
|
|
482
790
|
}
|
|
483
791
|
|
|
484
792
|
function formatEvent(m) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
793
|
+
var originalMessage = m.message ? m.message : m;
|
|
794
|
+
var logMessageType = originalMessage.log_message_type;
|
|
795
|
+
var logMessageData;
|
|
488
796
|
if (logMessageType === "log:generic-admin-text") {
|
|
489
797
|
logMessageData = originalMessage.log_message_data.untypedData;
|
|
490
|
-
logMessageType = getAdminTextMessageType(
|
|
798
|
+
logMessageType = getAdminTextMessageType(
|
|
799
|
+
originalMessage.log_message_data.message_type,
|
|
800
|
+
);
|
|
491
801
|
} else {
|
|
492
802
|
logMessageData = originalMessage.log_message_data;
|
|
493
803
|
}
|
|
@@ -496,39 +806,58 @@ function formatEvent(m) {
|
|
|
496
806
|
type: "event",
|
|
497
807
|
logMessageType: logMessageType,
|
|
498
808
|
logMessageData: logMessageData,
|
|
499
|
-
logMessageBody: originalMessage.log_message_body
|
|
809
|
+
logMessageBody: originalMessage.log_message_body,
|
|
500
810
|
});
|
|
501
811
|
}
|
|
502
812
|
|
|
503
813
|
function formatHistoryMessage(m) {
|
|
504
|
-
|
|
814
|
+
switch (m.action_type) {
|
|
815
|
+
case "ma-type:log-message":
|
|
816
|
+
return formatEvent(m);
|
|
817
|
+
default:
|
|
818
|
+
return formatMessage(m);
|
|
819
|
+
}
|
|
505
820
|
}
|
|
506
821
|
|
|
822
|
+
// Get a more readable message type for AdminTextMessages
|
|
507
823
|
function getAdminTextMessageType(type) {
|
|
508
824
|
switch (type) {
|
|
509
|
-
case
|
|
510
|
-
|
|
511
|
-
case "
|
|
512
|
-
case "
|
|
513
|
-
|
|
514
|
-
case "change_thread_nickname":
|
|
515
|
-
|
|
516
|
-
case "
|
|
517
|
-
|
|
825
|
+
case "change_thread_theme":
|
|
826
|
+
return "log:thread-color";
|
|
827
|
+
// case "change_thread_icon": deprecated
|
|
828
|
+
case "change_thread_quick_reaction":
|
|
829
|
+
return "log:thread-icon";
|
|
830
|
+
case "change_thread_nickname":
|
|
831
|
+
return "log:user-nickname";
|
|
832
|
+
case "change_thread_admins":
|
|
833
|
+
return "log:thread-admins";
|
|
834
|
+
case "group_poll":
|
|
835
|
+
return "log:thread-poll";
|
|
836
|
+
case "change_thread_approval_mode":
|
|
837
|
+
return "log:thread-approval-mode";
|
|
518
838
|
case "messenger_call_log":
|
|
519
|
-
case "participant_joined_group_call":
|
|
520
|
-
|
|
839
|
+
case "participant_joined_group_call":
|
|
840
|
+
return "log:thread-call";
|
|
841
|
+
default:
|
|
842
|
+
return type;
|
|
521
843
|
}
|
|
522
844
|
}
|
|
523
845
|
|
|
524
846
|
function formatDeltaEvent(m) {
|
|
525
|
-
|
|
526
|
-
|
|
847
|
+
var logMessageType;
|
|
848
|
+
var logMessageData;
|
|
849
|
+
|
|
850
|
+
// log:thread-color => {theme_color}
|
|
851
|
+
// log:user-nickname => {participant_id, nickname}
|
|
852
|
+
// log:thread-icon => {thread_icon}
|
|
853
|
+
// log:thread-name => {name}
|
|
854
|
+
// log:subscribe => {addedParticipants - [Array]}
|
|
855
|
+
// log:unsubscribe => {leftParticipantFbId}
|
|
527
856
|
|
|
528
857
|
switch (m.class) {
|
|
529
858
|
case "AdminTextMessage":
|
|
530
|
-
logMessageData = m.untypedData;
|
|
531
859
|
logMessageType = getAdminTextMessageType(m.type);
|
|
860
|
+
logMessageData = m.untypedData;
|
|
532
861
|
break;
|
|
533
862
|
case "ThreadName":
|
|
534
863
|
logMessageType = "log:thread-name";
|
|
@@ -542,28 +871,21 @@ function formatDeltaEvent(m) {
|
|
|
542
871
|
logMessageType = "log:unsubscribe";
|
|
543
872
|
logMessageData = { leftParticipantFbId: m.leftParticipantFbId };
|
|
544
873
|
break;
|
|
545
|
-
case "ApprovalQueue":
|
|
546
|
-
logMessageType = "log:approval-queue";
|
|
547
|
-
logMessageData = {
|
|
548
|
-
approvalQueue: {
|
|
549
|
-
action: m.action,
|
|
550
|
-
recipientFbId: m.recipientFbId,
|
|
551
|
-
requestSource: m.requestSource,
|
|
552
|
-
...m.messageMetadata
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
break;
|
|
556
874
|
}
|
|
875
|
+
|
|
557
876
|
return {
|
|
558
877
|
type: "event",
|
|
559
|
-
threadID: formatID(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
878
|
+
threadID: formatID(
|
|
879
|
+
(
|
|
880
|
+
m.messageMetadata.threadKey.threadFbId ||
|
|
881
|
+
m.messageMetadata.threadKey.otherUserFbId
|
|
882
|
+
).toString(),
|
|
883
|
+
),
|
|
884
|
+
logMessageType: logMessageType,
|
|
885
|
+
logMessageData: logMessageData,
|
|
563
886
|
logMessageBody: m.messageMetadata.adminText,
|
|
564
|
-
timestamp: m.messageMetadata.timestamp,
|
|
565
887
|
author: m.messageMetadata.actorFbId,
|
|
566
|
-
participantIDs: m.participants
|
|
888
|
+
participantIDs: m.participants || [],
|
|
567
889
|
};
|
|
568
890
|
}
|
|
569
891
|
|
|
@@ -571,19 +893,27 @@ function formatTyp(event) {
|
|
|
571
893
|
return {
|
|
572
894
|
isTyping: !!event.st,
|
|
573
895
|
from: event.from.toString(),
|
|
574
|
-
threadID: formatID(
|
|
896
|
+
threadID: formatID(
|
|
897
|
+
(event.to || event.thread_fbid || event.from).toString(),
|
|
898
|
+
),
|
|
899
|
+
// When receiving typ indication from mobile, `from_mobile` isn't set.
|
|
900
|
+
// If it is, we just use that value.
|
|
575
901
|
fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
|
|
576
902
|
userID: (event.realtime_viewer_fbid || event.from).toString(),
|
|
577
|
-
type: "typ"
|
|
903
|
+
type: "typ",
|
|
578
904
|
};
|
|
579
905
|
}
|
|
580
906
|
|
|
581
907
|
function formatDeltaReadReceipt(delta) {
|
|
908
|
+
// otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
|
|
909
|
+
// In a group chat actorFbId is used for the reader and threadFbId for the thread.
|
|
582
910
|
return {
|
|
583
911
|
reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
|
|
584
912
|
time: delta.actionTimestampMs,
|
|
585
|
-
threadID: formatID(
|
|
586
|
-
|
|
913
|
+
threadID: formatID(
|
|
914
|
+
(delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString(),
|
|
915
|
+
),
|
|
916
|
+
type: "read_receipt",
|
|
587
917
|
};
|
|
588
918
|
}
|
|
589
919
|
|
|
@@ -592,42 +922,69 @@ function formatReadReceipt(event) {
|
|
|
592
922
|
reader: event.reader.toString(),
|
|
593
923
|
time: event.time,
|
|
594
924
|
threadID: formatID((event.thread_fbid || event.reader).toString()),
|
|
595
|
-
type: "read_receipt"
|
|
925
|
+
type: "read_receipt",
|
|
596
926
|
};
|
|
597
927
|
}
|
|
598
928
|
|
|
599
929
|
function formatRead(event) {
|
|
600
930
|
return {
|
|
601
|
-
threadID: formatID(
|
|
931
|
+
threadID: formatID(
|
|
932
|
+
(
|
|
933
|
+
(event.chat_ids && event.chat_ids[0]) ||
|
|
934
|
+
(event.thread_fbids && event.thread_fbids[0])
|
|
935
|
+
).toString(),
|
|
936
|
+
),
|
|
602
937
|
time: event.timestamp,
|
|
603
|
-
type: "read"
|
|
938
|
+
type: "read",
|
|
604
939
|
};
|
|
605
940
|
}
|
|
606
941
|
|
|
607
942
|
function getFrom(str, startToken, endToken) {
|
|
608
|
-
|
|
943
|
+
var start = str.indexOf(startToken) + startToken.length;
|
|
609
944
|
if (start < startToken.length) return "";
|
|
610
|
-
|
|
611
|
-
|
|
945
|
+
|
|
946
|
+
var lastHalf = str.substring(start);
|
|
947
|
+
var end = lastHalf.indexOf(endToken);
|
|
612
948
|
if (end === -1) {
|
|
613
|
-
throw Error(
|
|
949
|
+
throw Error(
|
|
950
|
+
"Could not find endTime `" + endToken + "` in the given string.",
|
|
951
|
+
);
|
|
614
952
|
}
|
|
615
953
|
return lastHalf.substring(0, end);
|
|
616
954
|
}
|
|
617
955
|
|
|
618
956
|
function makeParsable(html) {
|
|
619
|
-
|
|
620
|
-
|
|
957
|
+
let withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
|
|
958
|
+
|
|
959
|
+
// (What the fuck FB, why windows style newlines?)
|
|
960
|
+
// So sometimes FB will send us base multiple objects in the same response.
|
|
961
|
+
// They're all valid JSON, one after the other, at the top level. We detect
|
|
962
|
+
// that and make it parse-able by JSON.parse.
|
|
963
|
+
// Ben - July 15th 2017
|
|
964
|
+
//
|
|
965
|
+
// It turns out that Facebook may insert random number of spaces before
|
|
966
|
+
// next object begins (issue #616)
|
|
967
|
+
// rav_kr - 2018-03-19
|
|
968
|
+
let maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
|
|
621
969
|
if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
|
|
970
|
+
|
|
622
971
|
return "[" + maybeMultipleObjects.join("},{") + "]";
|
|
623
972
|
}
|
|
624
973
|
|
|
625
974
|
function arrToForm(form) {
|
|
626
|
-
return arrayToObject(
|
|
975
|
+
return arrayToObject(
|
|
976
|
+
form,
|
|
977
|
+
function (v) {
|
|
978
|
+
return v.name;
|
|
979
|
+
},
|
|
980
|
+
function (v) {
|
|
981
|
+
return v.val;
|
|
982
|
+
},
|
|
983
|
+
);
|
|
627
984
|
}
|
|
628
985
|
|
|
629
986
|
function arrayToObject(arr, getKey, getValue) {
|
|
630
|
-
return arr.reduce(function(acc, val) {
|
|
987
|
+
return arr.reduce(function (acc, val) {
|
|
631
988
|
acc[getKey(val)] = getValue(val);
|
|
632
989
|
return acc;
|
|
633
990
|
}, {});
|
|
@@ -638,113 +995,223 @@ function getSignatureID() {
|
|
|
638
995
|
}
|
|
639
996
|
|
|
640
997
|
function generateTimestampRelative() {
|
|
641
|
-
|
|
998
|
+
var d = new Date();
|
|
642
999
|
return d.getHours() + ":" + padZeros(d.getMinutes());
|
|
643
1000
|
}
|
|
644
1001
|
|
|
645
1002
|
function makeDefaults(html, userID, ctx) {
|
|
646
|
-
|
|
647
|
-
|
|
1003
|
+
var reqCounter = 1;
|
|
1004
|
+
var fb_dtsg = getFrom(html, 'name="fb_dtsg" value="', '"');
|
|
1005
|
+
|
|
1006
|
+
// @Hack Ok we've done hacky things, this is definitely on top 5.
|
|
1007
|
+
// We totally assume the object is flat and try parsing until a }.
|
|
1008
|
+
// If it works though it's cool because we get a bunch of extra data things.
|
|
1009
|
+
//
|
|
1010
|
+
// Update: we don't need this. Leaving it in in case we ever do.
|
|
1011
|
+
// Ben - July 15th 2017
|
|
1012
|
+
|
|
1013
|
+
// var siteData = getFrom(html, "[\"SiteData\",[],", "},");
|
|
1014
|
+
// try {
|
|
1015
|
+
// siteData = JSON.parse(siteData + "}");
|
|
1016
|
+
// } catch(e) {
|
|
1017
|
+
// log.warn("makeDefaults", "Couldn't parse SiteData. Won't have access to some variables.");
|
|
1018
|
+
// siteData = {};
|
|
1019
|
+
// }
|
|
1020
|
+
|
|
1021
|
+
var ttstamp = "2";
|
|
1022
|
+
for (var i = 0; i < fb_dtsg.length; i++) {
|
|
1023
|
+
ttstamp += fb_dtsg.charCodeAt(i);
|
|
1024
|
+
}
|
|
1025
|
+
var revision = getFrom(html, 'revision":', ",");
|
|
1026
|
+
|
|
648
1027
|
function mergeWithDefaults(obj) {
|
|
649
|
-
|
|
650
|
-
|
|
1028
|
+
// @TODO This is missing a key called __dyn.
|
|
1029
|
+
// After some investigation it seems like __dyn is some sort of set that FB
|
|
1030
|
+
// calls BitMap. It seems like certain responses have a "define" key in the
|
|
1031
|
+
// res.jsmods arrays. I think the code iterates over those and calls `set`
|
|
1032
|
+
// on the bitmap for each of those keys. Then it calls
|
|
1033
|
+
// bitmap.toCompressedString() which returns what __dyn is.
|
|
1034
|
+
//
|
|
1035
|
+
// So far the API has been working without this.
|
|
1036
|
+
//
|
|
1037
|
+
// Ben - July 15th 2017
|
|
1038
|
+
var newObj = {
|
|
651
1039
|
__user: userID,
|
|
652
1040
|
__req: (reqCounter++).toString(36),
|
|
653
1041
|
__rev: revision,
|
|
654
1042
|
__a: 1,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1043
|
+
// __af: siteData.features,
|
|
1044
|
+
fb_dtsg: ctx.fb_dtsg ? ctx.fb_dtsg : fb_dtsg,
|
|
1045
|
+
jazoest: ctx.ttstamp ? ctx.ttstamp : ttstamp,
|
|
1046
|
+
// __spin_r: siteData.__spin_r,
|
|
1047
|
+
// __spin_b: siteData.__spin_b,
|
|
1048
|
+
// __spin_t: siteData.__spin_t,
|
|
659
1049
|
};
|
|
660
1050
|
|
|
1051
|
+
// @TODO this is probably not needed.
|
|
1052
|
+
// Ben - July 15th 2017
|
|
1053
|
+
// if (siteData.be_key) {
|
|
1054
|
+
// newObj[siteData.be_key] = siteData.be_mode;
|
|
1055
|
+
// }
|
|
1056
|
+
// if (siteData.pkg_cohort_key) {
|
|
1057
|
+
// newObj[siteData.pkg_cohort_key] = siteData.pkg_cohort;
|
|
1058
|
+
// }
|
|
1059
|
+
|
|
661
1060
|
if (!obj) return newObj;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (obj.hasOwnProperty(prop)
|
|
665
|
-
newObj[prop]
|
|
1061
|
+
|
|
1062
|
+
for (var prop in obj) {
|
|
1063
|
+
if (obj.hasOwnProperty(prop)) {
|
|
1064
|
+
if (!newObj[prop]) {
|
|
1065
|
+
newObj[prop] = obj[prop];
|
|
1066
|
+
}
|
|
666
1067
|
}
|
|
667
|
-
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
668
1070
|
return newObj;
|
|
669
1071
|
}
|
|
670
1072
|
|
|
1073
|
+
function postWithDefaults(url, jar, form, ctxx) {
|
|
1074
|
+
return post(
|
|
1075
|
+
url,
|
|
1076
|
+
jar,
|
|
1077
|
+
mergeWithDefaults(form),
|
|
1078
|
+
ctx.globalOptions,
|
|
1079
|
+
ctxx || ctx,
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function getWithDefaults(url, jar, qs, ctxx) {
|
|
1084
|
+
return get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function postFormDataWithDefault(url, jar, form, qs, ctxx) {
|
|
1088
|
+
return postFormData(
|
|
1089
|
+
url,
|
|
1090
|
+
jar,
|
|
1091
|
+
mergeWithDefaults(form),
|
|
1092
|
+
mergeWithDefaults(qs),
|
|
1093
|
+
ctx.globalOptions,
|
|
1094
|
+
ctxx || ctx,
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
671
1098
|
return {
|
|
672
|
-
get:
|
|
673
|
-
post:
|
|
674
|
-
postFormData:
|
|
1099
|
+
get: getWithDefaults,
|
|
1100
|
+
post: postWithDefaults,
|
|
1101
|
+
postFormData: postFormDataWithDefault,
|
|
675
1102
|
};
|
|
676
1103
|
}
|
|
677
1104
|
|
|
678
|
-
function parseAndCheckLogin(ctx,
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
return function(data) {
|
|
689
|
-
function any() {
|
|
1105
|
+
function parseAndCheckLogin(ctx, defaultFuncs, retryCount) {
|
|
1106
|
+
if (retryCount == undefined) {
|
|
1107
|
+
retryCount = 0;
|
|
1108
|
+
}
|
|
1109
|
+
return function (data) {
|
|
1110
|
+
return bluebird.try(function () {
|
|
1111
|
+
log.verbose("parseAndCheckLogin", data.body);
|
|
690
1112
|
if (data.statusCode >= 500 && data.statusCode < 600) {
|
|
691
1113
|
if (retryCount >= 5) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1114
|
+
throw {
|
|
1115
|
+
error:
|
|
1116
|
+
"Request retry failed. Check the `res` and `statusCode` property on this error.",
|
|
1117
|
+
statusCode: data.statusCode,
|
|
1118
|
+
res: data.body,
|
|
1119
|
+
};
|
|
697
1120
|
}
|
|
698
1121
|
retryCount++;
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
.
|
|
707
|
-
|
|
1122
|
+
var retryTime = Math.floor(Math.random() * 5000);
|
|
1123
|
+
log.warn(
|
|
1124
|
+
"parseAndCheckLogin",
|
|
1125
|
+
"Got status code " +
|
|
1126
|
+
data.statusCode +
|
|
1127
|
+
" - " +
|
|
1128
|
+
retryCount +
|
|
1129
|
+
". attempt to retry in " +
|
|
1130
|
+
retryTime +
|
|
1131
|
+
" milliseconds...",
|
|
1132
|
+
);
|
|
1133
|
+
var url =
|
|
1134
|
+
data.request.uri.protocol +
|
|
1135
|
+
"//" +
|
|
1136
|
+
data.request.uri.hostname +
|
|
1137
|
+
data.request.uri.pathname;
|
|
1138
|
+
if (
|
|
1139
|
+
data.request.headers["Content-Type"].split(";")[0] ===
|
|
1140
|
+
"multipart/form-data"
|
|
1141
|
+
) {
|
|
1142
|
+
return bluebird
|
|
1143
|
+
.delay(retryTime)
|
|
1144
|
+
.then(function () {
|
|
1145
|
+
return defaultFuncs.postFormData(
|
|
1146
|
+
url,
|
|
1147
|
+
ctx.jar,
|
|
1148
|
+
data.request.formData,
|
|
1149
|
+
{},
|
|
1150
|
+
);
|
|
1151
|
+
})
|
|
1152
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
|
|
708
1153
|
} else {
|
|
709
|
-
return
|
|
710
|
-
.
|
|
711
|
-
.then(
|
|
1154
|
+
return bluebird
|
|
1155
|
+
.delay(retryTime)
|
|
1156
|
+
.then(function () {
|
|
1157
|
+
return defaultFuncs.post(url, ctx.jar, data.request.formData);
|
|
1158
|
+
})
|
|
1159
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
|
|
712
1160
|
}
|
|
713
1161
|
}
|
|
714
|
-
|
|
715
|
-
if (data.statusCode === 404) return;
|
|
716
1162
|
if (data.statusCode !== 200)
|
|
717
|
-
throw new Error(
|
|
1163
|
+
throw new Error(
|
|
1164
|
+
"parseAndCheckLogin got status code: " +
|
|
1165
|
+
data.statusCode +
|
|
1166
|
+
". Bailing out of trying to parse response.",
|
|
1167
|
+
);
|
|
718
1168
|
|
|
719
|
-
|
|
1169
|
+
var res = null;
|
|
720
1170
|
try {
|
|
721
1171
|
res = JSON.parse(makeParsable(data.body));
|
|
722
1172
|
} catch (e) {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1173
|
+
throw {
|
|
1174
|
+
error: "JSON.parse error. Check the `detail` property on this error.",
|
|
1175
|
+
detail: e,
|
|
1176
|
+
res: data.body,
|
|
1177
|
+
};
|
|
728
1178
|
}
|
|
729
1179
|
|
|
1180
|
+
// In some cases the response contains only a redirect URL which should be followed
|
|
730
1181
|
if (res.redirect && data.request.method === "GET") {
|
|
731
|
-
return
|
|
1182
|
+
return defaultFuncs
|
|
1183
|
+
.get(res.redirect, ctx.jar)
|
|
1184
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs));
|
|
732
1185
|
}
|
|
733
1186
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1187
|
+
// TODO: handle multiple cookies?
|
|
1188
|
+
if (
|
|
1189
|
+
res.jsmods &&
|
|
1190
|
+
res.jsmods.require &&
|
|
1191
|
+
Array.isArray(res.jsmods.require[0]) &&
|
|
1192
|
+
res.jsmods.require[0][0] === "Cookie"
|
|
1193
|
+
) {
|
|
1194
|
+
res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace(
|
|
1195
|
+
"_js_",
|
|
1196
|
+
"",
|
|
1197
|
+
);
|
|
1198
|
+
var cookie = formatCookie(res.jsmods.require[0][3], "facebook");
|
|
1199
|
+
var cookie2 = formatCookie(res.jsmods.require[0][3], "messenger");
|
|
1200
|
+
ctx.jar.setCookie(cookie, "https://www.facebook.com");
|
|
1201
|
+
ctx.jar.setCookie(cookie2, "https://www.messenger.com");
|
|
739
1202
|
}
|
|
740
1203
|
|
|
1204
|
+
// On every request we check if we got a DTSG and we mutate the context so that we use the latest
|
|
1205
|
+
// one for the next requests.
|
|
741
1206
|
if (res.jsmods && Array.isArray(res.jsmods.require)) {
|
|
742
|
-
|
|
743
|
-
for (
|
|
1207
|
+
var arr = res.jsmods.require;
|
|
1208
|
+
for (var i in arr) {
|
|
744
1209
|
if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
|
|
745
1210
|
ctx.fb_dtsg = arr[i][3][0];
|
|
1211
|
+
|
|
1212
|
+
// Update ttstamp since that depends on fb_dtsg
|
|
746
1213
|
ctx.ttstamp = "2";
|
|
747
|
-
for (
|
|
1214
|
+
for (var j = 0; j < ctx.fb_dtsg.length; j++) {
|
|
748
1215
|
ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
|
|
749
1216
|
}
|
|
750
1217
|
}
|
|
@@ -752,43 +1219,73 @@ function parseAndCheckLogin(ctx, http, retryCount = 0) {
|
|
|
752
1219
|
}
|
|
753
1220
|
|
|
754
1221
|
if (res.error === 1357001) {
|
|
755
|
-
|
|
756
|
-
err.error = "Not logged in.";
|
|
757
|
-
throw err;
|
|
1222
|
+
throw { error: "Not logged in." };
|
|
758
1223
|
}
|
|
759
1224
|
return res;
|
|
760
|
-
}
|
|
761
|
-
return _try(any);
|
|
1225
|
+
});
|
|
762
1226
|
};
|
|
763
1227
|
}
|
|
764
1228
|
|
|
765
1229
|
function saveCookies(jar) {
|
|
766
|
-
return function(res) {
|
|
767
|
-
|
|
768
|
-
cookies.forEach(function(c) {
|
|
1230
|
+
return function (res) {
|
|
1231
|
+
var cookies = res.headers["set-cookie"] || [];
|
|
1232
|
+
cookies.forEach(function (c) {
|
|
769
1233
|
if (c.indexOf(".facebook.com") > -1) {
|
|
770
1234
|
jar.setCookie(c, "https://www.facebook.com");
|
|
771
1235
|
}
|
|
772
|
-
|
|
1236
|
+
var c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
|
|
773
1237
|
jar.setCookie(c2, "https://www.messenger.com");
|
|
774
1238
|
});
|
|
775
1239
|
return res;
|
|
776
1240
|
};
|
|
777
1241
|
}
|
|
778
1242
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1243
|
+
var NUM_TO_MONTH = [
|
|
1244
|
+
"Jan",
|
|
1245
|
+
"Feb",
|
|
1246
|
+
"Mar",
|
|
1247
|
+
"Apr",
|
|
1248
|
+
"May",
|
|
1249
|
+
"Jun",
|
|
1250
|
+
"Jul",
|
|
1251
|
+
"Aug",
|
|
1252
|
+
"Sep",
|
|
1253
|
+
"Oct",
|
|
1254
|
+
"Nov",
|
|
1255
|
+
"Dec",
|
|
1256
|
+
];
|
|
1257
|
+
var NUM_TO_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
782
1258
|
function formatDate(date) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1259
|
+
var d = date.getUTCDate();
|
|
1260
|
+
d = d >= 10 ? d : "0" + d;
|
|
1261
|
+
var h = date.getUTCHours();
|
|
1262
|
+
h = h >= 10 ? h : "0" + h;
|
|
1263
|
+
var m = date.getUTCMinutes();
|
|
1264
|
+
m = m >= 10 ? m : "0" + m;
|
|
1265
|
+
var s = date.getUTCSeconds();
|
|
1266
|
+
s = s >= 10 ? s : "0" + s;
|
|
1267
|
+
return (
|
|
1268
|
+
NUM_TO_DAY[date.getUTCDay()] +
|
|
1269
|
+
", " +
|
|
1270
|
+
d +
|
|
1271
|
+
" " +
|
|
1272
|
+
NUM_TO_MONTH[date.getUTCMonth()] +
|
|
1273
|
+
" " +
|
|
1274
|
+
date.getUTCFullYear() +
|
|
1275
|
+
" " +
|
|
1276
|
+
h +
|
|
1277
|
+
":" +
|
|
1278
|
+
m +
|
|
1279
|
+
":" +
|
|
1280
|
+
s +
|
|
1281
|
+
" GMT"
|
|
1282
|
+
);
|
|
788
1283
|
}
|
|
789
1284
|
|
|
790
|
-
function formatCookie(arr,
|
|
791
|
-
return
|
|
1285
|
+
function formatCookie(arr, url) {
|
|
1286
|
+
return (
|
|
1287
|
+
arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com"
|
|
1288
|
+
);
|
|
792
1289
|
}
|
|
793
1290
|
|
|
794
1291
|
function formatThread(data) {
|
|
@@ -805,7 +1302,7 @@ function formatThread(data) {
|
|
|
805
1302
|
messageCount: data.message_count,
|
|
806
1303
|
imageSrc: data.image_src,
|
|
807
1304
|
timestamp: data.timestamp,
|
|
808
|
-
serverTimestamp: data.server_timestamp,
|
|
1305
|
+
serverTimestamp: data.server_timestamp, // what is this?
|
|
809
1306
|
muteUntil: data.mute_until,
|
|
810
1307
|
isCanonicalUser: data.is_canonical_user,
|
|
811
1308
|
isCanonical: data.is_canonical,
|
|
@@ -823,54 +1320,88 @@ function formatThread(data) {
|
|
|
823
1320
|
emoji: data.custom_like_icon,
|
|
824
1321
|
color: data.custom_color,
|
|
825
1322
|
adminIDs: data.admin_ids,
|
|
826
|
-
threadType: data.thread_type
|
|
1323
|
+
threadType: data.thread_type,
|
|
827
1324
|
};
|
|
828
1325
|
}
|
|
829
1326
|
|
|
1327
|
+
function getType(obj) {
|
|
1328
|
+
return Object.prototype.toString.call(obj).slice(8, -1);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
830
1331
|
function formatProxyPresence(presence, userID) {
|
|
831
1332
|
if (presence.lat === undefined || presence.p === undefined) return null;
|
|
832
|
-
return {
|
|
1333
|
+
return {
|
|
1334
|
+
type: "presence",
|
|
1335
|
+
timestamp: presence.lat * 1000,
|
|
1336
|
+
userID: userID || "",
|
|
1337
|
+
statuses: presence.p,
|
|
1338
|
+
};
|
|
833
1339
|
}
|
|
834
1340
|
|
|
835
1341
|
function formatPresence(presence, userID) {
|
|
836
|
-
return {
|
|
1342
|
+
return {
|
|
1343
|
+
type: "presence",
|
|
1344
|
+
timestamp: presence.la * 1000,
|
|
1345
|
+
userID: userID || "",
|
|
1346
|
+
statuses: presence.a,
|
|
1347
|
+
};
|
|
837
1348
|
}
|
|
838
1349
|
|
|
839
1350
|
function decodeClientPayload(payload) {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1351
|
+
/*
|
|
1352
|
+
Special function which Client using to "encode" clients JSON payload
|
|
1353
|
+
*/
|
|
1354
|
+
function Utf8ArrayToStr(array) {
|
|
1355
|
+
var out, i, len, c;
|
|
1356
|
+
var char2, char3;
|
|
1357
|
+
out = "";
|
|
1358
|
+
len = array.length;
|
|
1359
|
+
i = 0;
|
|
1360
|
+
while (i < len) {
|
|
1361
|
+
c = array[i++];
|
|
1362
|
+
switch (c >> 4) {
|
|
1363
|
+
case 0:
|
|
1364
|
+
case 1:
|
|
1365
|
+
case 2:
|
|
1366
|
+
case 3:
|
|
1367
|
+
case 4:
|
|
1368
|
+
case 5:
|
|
1369
|
+
case 6:
|
|
1370
|
+
case 7:
|
|
1371
|
+
out += String.fromCharCode(c);
|
|
1372
|
+
break;
|
|
1373
|
+
case 12:
|
|
1374
|
+
case 13:
|
|
1375
|
+
char2 = array[i++];
|
|
1376
|
+
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
|
|
1377
|
+
break;
|
|
1378
|
+
case 14:
|
|
1379
|
+
char2 = array[i++];
|
|
1380
|
+
char3 = array[i++];
|
|
1381
|
+
out += String.fromCharCode(
|
|
1382
|
+
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0),
|
|
1383
|
+
);
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return out;
|
|
844
1388
|
}
|
|
845
|
-
}
|
|
846
1389
|
|
|
847
|
-
|
|
848
|
-
return jar.getCookies("https://www.facebook.com").concat(jar.getCookies("https://www.messenger.com"));
|
|
1390
|
+
return JSON.parse(Utf8ArrayToStr(payload));
|
|
849
1391
|
}
|
|
850
1392
|
|
|
851
|
-
function
|
|
852
|
-
return
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const match = /"accessToken":"([^.]+)","clientID":/g.exec(resData.body);
|
|
857
|
-
const token = match ? match[1] : null;
|
|
858
|
-
return [html, token];
|
|
859
|
-
})
|
|
860
|
-
.catch(function() {
|
|
861
|
-
return [html, null];
|
|
862
|
-
});
|
|
863
|
-
};
|
|
1393
|
+
function getAppState(jar) {
|
|
1394
|
+
return jar
|
|
1395
|
+
.getCookies("https://www.facebook.com")
|
|
1396
|
+
.concat(jar.getCookies("https://facebook.com"))
|
|
1397
|
+
.concat(jar.getCookies("https://www.messenger.com"));
|
|
864
1398
|
}
|
|
865
1399
|
|
|
866
|
-
const
|
|
1400
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
867
1401
|
|
|
868
1402
|
module.exports = {
|
|
869
|
-
|
|
870
|
-
error(...args) { console.error(ws, chalk.red.bold("[ERROR]"), ...args); },
|
|
871
|
-
warn(...args) { console.warn(ws, chalk.yellow.bold("[WARNING]"), ...args); },
|
|
1403
|
+
delay,
|
|
872
1404
|
isReadableStream,
|
|
873
|
-
cleanGet,
|
|
874
1405
|
get,
|
|
875
1406
|
post,
|
|
876
1407
|
postFormData,
|
|
@@ -908,12 +1439,9 @@ module.exports = {
|
|
|
908
1439
|
getAppState,
|
|
909
1440
|
getAdminTextMessageType,
|
|
910
1441
|
setProxy,
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
windowsUserAgent,
|
|
917
|
-
randomUserAgent,
|
|
918
|
-
meta
|
|
1442
|
+
getCurrentTimestamp,
|
|
1443
|
+
// Database / cache
|
|
1444
|
+
database,
|
|
1445
|
+
usersCache,
|
|
1446
|
+
groupsCache,
|
|
919
1447
|
};
|