@dongdev/fca-unofficial 2.0.7 → 2.0.8
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/CHANGELOG.md +3 -0
- package/func/checkUpdate.js +9 -9
- package/func/logger.js +40 -104
- package/module/login.js +4 -4
- package/module/loginHelper.js +3 -3
- package/module/options.js +5 -9
- package/package.json +5 -5
- package/src/api/action/addExternalModule.js +5 -5
- package/src/api/action/changeAvatar.js +11 -10
- package/src/api/action/changeBio.js +7 -8
- package/src/api/action/getCurrentUserID.js +1 -1
- package/src/api/action/handleFriendRequest.js +5 -5
- package/src/api/action/logout.js +9 -8
- package/src/api/action/refreshFb_dtsg.js +17 -12
- package/src/api/action/setPostReaction.js +10 -11
- package/src/api/action/unfriend.js +3 -4
- package/src/api/http/httpGet.js +7 -8
- package/src/api/http/httpPost.js +7 -8
- package/src/api/http/postFormData.js +6 -5
- package/src/api/messaging/addUserToGroup.js +0 -1
- package/src/api/messaging/changeAdminStatus.js +108 -89
- package/src/api/messaging/changeArchivedStatus.js +6 -6
- package/src/api/messaging/changeBlockedStatus.js +3 -4
- package/src/api/messaging/changeGroupImage.js +72 -117
- package/src/api/messaging/changeNickname.js +59 -48
- package/src/api/messaging/changeThreadColor.js +61 -47
- package/src/api/messaging/changeThreadEmoji.js +106 -0
- package/src/api/messaging/createNewGroup.js +5 -5
- package/src/api/messaging/createPoll.js +36 -63
- package/src/api/messaging/deleteMessage.js +4 -4
- package/src/api/messaging/deleteThread.js +4 -4
- package/src/api/messaging/forwardAttachment.js +38 -47
- package/src/api/messaging/getFriendsList.js +5 -6
- package/src/api/messaging/getMessage.js +4 -9
- package/src/api/messaging/handleMessageRequest.js +5 -5
- package/src/api/messaging/markAsDelivered.js +5 -5
- package/src/api/messaging/markAsRead.js +7 -7
- package/src/api/messaging/markAsReadAll.js +3 -4
- package/src/api/messaging/markAsSeen.js +7 -7
- package/src/api/messaging/muteThread.js +3 -4
- package/src/api/messaging/removeUserFromGroup.js +82 -56
- package/src/api/messaging/resolvePhotoUrl.js +2 -3
- package/src/api/messaging/searchForThread.js +2 -3
- package/src/api/messaging/sendMessage.js +171 -101
- package/src/api/messaging/sendMessageMqtt.js +14 -12
- package/src/api/messaging/sendTypingIndicator.js +11 -11
- package/src/api/messaging/setMessageReaction.js +68 -82
- package/src/api/messaging/setTitle.js +77 -48
- package/src/api/messaging/shareContact.js +2 -4
- package/src/api/messaging/threadColors.js +0 -3
- package/src/api/messaging/unsendMessage.js +74 -37
- package/src/api/messaging/uploadAttachment.js +11 -9
- package/src/api/socket/core/connectMqtt.js +180 -0
- package/src/api/socket/core/getSeqID.js +25 -0
- package/src/api/socket/core/getTaskResponseData.js +22 -0
- package/src/api/socket/core/markDelivery.js +12 -0
- package/src/api/socket/core/parseDelta.js +351 -0
- package/src/api/socket/detail/buildStream.js +176 -68
- package/src/api/socket/detail/constants.js +24 -0
- package/src/api/socket/listenMqtt.js +80 -1005
- package/src/api/{messaging → threads}/getThreadHistory.js +5 -22
- package/src/api/threads/getThreadInfo.js +35 -248
- package/src/api/threads/getThreadList.js +20 -20
- package/src/api/threads/getThreadPictures.js +3 -4
- package/src/api/users/getUserID.js +5 -6
- package/src/api/users/getUserInfo.js +305 -73
- package/src/api/users/getUserInfoV2.js +134 -0
- package/src/database/models/user.js +32 -0
- package/src/database/userData.js +89 -0
- package/src/utils/constants.js +12 -2
- package/src/utils/format.js +1051 -0
- package/src/utils/request.js +75 -7
- package/src/api/threads/changeThreadEmoji.js +0 -55
- package/src/utils/index.js +0 -1497
@@ -1,79 +1,105 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
-
const
|
3
|
+
const { parseAndCheckLogin, getType } = require("../../utils/format");
|
4
4
|
const log = require("npmlog");
|
5
5
|
|
6
|
-
module.exports = function(defaultFuncs, api, ctx) {
|
7
|
-
|
8
|
-
if (
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
) {
|
13
|
-
|
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) {
|
6
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
7
|
+
function removeUserFromGroupNoMqtt(userID, threadID, callback) {
|
8
|
+
if (!callback && (getType(threadID) === "Function" || getType(threadID) === "AsyncFunction")) throw { error: "please pass a threadID as a second argument." };
|
9
|
+
if (getType(threadID) !== "Number" && getType(threadID) !== "String") throw { error: "threadID should be of type Number or String and not " + getType(threadID) + "." };
|
10
|
+
if (getType(userID) !== "Number" && getType(userID) !== "String") throw { error: "userID should be of type Number or String and not " + getType(userID) + "." };
|
11
|
+
var resolveFunc = function () { };
|
12
|
+
var rejectFunc = function () { };
|
13
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
41
14
|
resolveFunc = resolve;
|
42
15
|
rejectFunc = reject;
|
43
16
|
});
|
44
|
-
|
45
17
|
if (!callback) {
|
46
|
-
callback = function(err,
|
47
|
-
if (err)
|
48
|
-
|
49
|
-
}
|
50
|
-
resolveFunc(friendList);
|
18
|
+
callback = function (err, data) {
|
19
|
+
if (err) return rejectFunc(err);
|
20
|
+
resolveFunc(data);
|
51
21
|
};
|
52
22
|
}
|
53
|
-
|
54
|
-
const form = {
|
23
|
+
var form = {
|
55
24
|
uid: userID,
|
56
25
|
tid: threadID
|
57
26
|
};
|
58
|
-
|
59
27
|
defaultFuncs
|
60
28
|
.post("https://www.facebook.com/chat/remove_participants", ctx.jar, form)
|
61
|
-
.then(
|
62
|
-
.then(function(resData) {
|
63
|
-
if (!resData) {
|
64
|
-
|
65
|
-
}
|
66
|
-
if (resData.error) {
|
67
|
-
throw resData;
|
68
|
-
}
|
69
|
-
|
29
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
30
|
+
.then(function (resData) {
|
31
|
+
if (!resData) throw { error: "Remove from group failed." };
|
32
|
+
if (resData.error) throw resData;
|
70
33
|
return callback();
|
71
34
|
})
|
72
|
-
.catch(function(err) {
|
35
|
+
.catch(function (err) {
|
73
36
|
log.error("removeUserFromGroup", err);
|
74
37
|
return callback(err);
|
75
38
|
});
|
76
|
-
|
77
39
|
return returnPromise;
|
78
40
|
};
|
41
|
+
function removeUserFromGroupMqtt(userID, threadID, callback) {
|
42
|
+
if (!ctx.mqttClient) {
|
43
|
+
throw new Error("Not connected to MQTT");
|
44
|
+
}
|
45
|
+
if (!callback && (getType(threadID) === "Function" || getType(threadID) === "AsyncFunction")) throw { error: "please pass a threadID as a second argument." };
|
46
|
+
if (getType(threadID) !== "Number" && getType(threadID) !== "String") throw { error: "threadID should be of type Number or String and not " + getType(threadID) + "." };
|
47
|
+
if (getType(userID) !== "Number" && getType(userID) !== "String") throw { error: "userID should be of type Number or String and not " + getType(userID) + "." };
|
48
|
+
var resolveFunc = function () { };
|
49
|
+
var rejectFunc = function () { };
|
50
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
51
|
+
resolveFunc = resolve;
|
52
|
+
rejectFunc = reject;
|
53
|
+
});
|
54
|
+
if (!callback) {
|
55
|
+
callback = function (err, data) {
|
56
|
+
if (err) return rejectFunc(err);
|
57
|
+
resolveFunc(data);
|
58
|
+
};
|
59
|
+
}
|
60
|
+
let count_req = 0;
|
61
|
+
var form = JSON.stringify({
|
62
|
+
"app_id": "2220391788200892",
|
63
|
+
"payload": JSON.stringify({
|
64
|
+
epoch_id: generateOfflineThreadingID(),
|
65
|
+
tasks: [
|
66
|
+
{
|
67
|
+
failure_count: null,
|
68
|
+
label: '140',
|
69
|
+
payload: JSON.stringify({
|
70
|
+
"thread_id": threadID,
|
71
|
+
"contact_id": userID,
|
72
|
+
"sync_group": 1
|
73
|
+
}),
|
74
|
+
queue_name: 'remove_participant_v2',
|
75
|
+
task_id: Math.random() * 1001 << 0
|
76
|
+
}
|
77
|
+
],
|
78
|
+
version_id: '8798795233522156'
|
79
|
+
}),
|
80
|
+
"request_id": ++count_req,
|
81
|
+
"type": 3
|
82
|
+
});
|
83
|
+
mqttClient.publish('/ls_req', form, (err, data) => {
|
84
|
+
if (err) {
|
85
|
+
callback(err, null);
|
86
|
+
rejectFunc(false);
|
87
|
+
} else {
|
88
|
+
callback(null, true);
|
89
|
+
resolveFunc(true);
|
90
|
+
}
|
91
|
+
});
|
92
|
+
return returnPromise;
|
93
|
+
};
|
94
|
+
return function removeUserFromGroup(userID, threadID, callback) {
|
95
|
+
if (ctx.mqttClient) {
|
96
|
+
try {
|
97
|
+
removeUserFromGroupMqtt(userID, threadID, callback);
|
98
|
+
} catch (e) {
|
99
|
+
removeUserFromGroupNoMqtt(userID, threadID, callback);
|
100
|
+
}
|
101
|
+
} else {
|
102
|
+
removeUserFromGroupNoMqtt(userID, threadID, callback);
|
103
|
+
}
|
104
|
+
};
|
79
105
|
};
|
@@ -1,7 +1,6 @@
|
|
1
1
|
"use strict";
|
2
|
-
const utils = require("../../utils");
|
3
2
|
const log = require("npmlog");
|
4
|
-
|
3
|
+
const { parseAndCheckLogin, saveCookies } = require("../../utils/client");
|
5
4
|
module.exports = function(defaultFuncs, api, ctx) {
|
6
5
|
return function resolvePhotoUrl(photoID, callback) {
|
7
6
|
let resolveFunc = function() {};
|
@@ -24,7 +23,7 @@ module.exports = function(defaultFuncs, api, ctx) {
|
|
24
23
|
.get("https://www.facebook.com/mercury/attachments/photo", ctx.jar, {
|
25
24
|
photo_id: photoID
|
26
25
|
})
|
27
|
-
.then(
|
26
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
28
27
|
.then(resData => {
|
29
28
|
if (resData.error) {
|
30
29
|
throw resData;
|
@@ -1,7 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
-
const
|
4
|
-
|
3
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
5
4
|
module.exports = function(defaultFuncs, api, ctx) {
|
6
5
|
return function searchForThread(name, callback) {
|
7
6
|
let resolveFunc = function() {};
|
@@ -34,7 +33,7 @@ module.exports = function(defaultFuncs, api, ctx) {
|
|
34
33
|
ctx.jar,
|
35
34
|
tmpForm
|
36
35
|
)
|
37
|
-
.then(
|
36
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
38
37
|
.then(function(resData) {
|
39
38
|
if (resData.error) {
|
40
39
|
throw resData;
|
@@ -1,9 +1,6 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
1
|
const fs = require("fs");
|
4
2
|
const path = require("path");
|
5
3
|
const { Readable } = require("stream");
|
6
|
-
const utils = require("../../utils");
|
7
4
|
const log = require("npmlog");
|
8
5
|
const allowedProperties = {
|
9
6
|
attachment: true,
|
@@ -16,38 +13,27 @@ const allowedProperties = {
|
|
16
13
|
location: true,
|
17
14
|
asPage: true
|
18
15
|
};
|
16
|
+
const { isReadableStream } = require("../../utils/constants");
|
17
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
18
|
+
const { getType, generateThreadingID, generateTimestampRelative, generateOfflineThreadingID, getSignatureID } = require("../../utils/format");
|
19
|
+
|
19
20
|
module.exports = function (defaultFuncs, api, ctx) {
|
20
21
|
function toReadable(input) {
|
21
|
-
if (
|
22
|
+
if (isReadableStream(input)) return input;
|
22
23
|
if (Buffer.isBuffer(input)) return Readable.from(input);
|
23
24
|
if (typeof input === "string" && fs.existsSync(input) && fs.statSync(input).isFile()) return fs.createReadStream(path.resolve(input));
|
24
25
|
throw { error: "Unsupported attachment input. Use stream/buffer/filepath." };
|
25
26
|
}
|
26
|
-
|
27
|
-
if (!Array.isArray(list)) list = [list];
|
28
|
-
return list.map(a => {
|
29
|
-
if (Array.isArray(a) && /_id$/.test(a[0])) return a;
|
30
|
-
if (a && typeof a === "object") {
|
31
|
-
if (a.id && a.type && /_id$/.test(a.type)) return [a.type, a.id];
|
32
|
-
if (a.image_id || a.file_id || a.video_id || a.audio_id || a.gif_id) {
|
33
|
-
const k = Object.keys(a).find(x => /_id$/.test(x));
|
34
|
-
return [k, a[k]];
|
35
|
-
}
|
36
|
-
}
|
37
|
-
return toReadable(a);
|
38
|
-
});
|
39
|
-
}
|
27
|
+
|
40
28
|
function uploadAttachment(attachments, callback) {
|
41
29
|
const uploads = [];
|
42
30
|
for (let i = 0; i < attachments.length; i++) {
|
43
|
-
if (!
|
44
|
-
throw { error: "Attachment should be a readable stream and not " + utils.getType(attachments[i]) + "." };
|
45
|
-
}
|
31
|
+
if (!isReadableStream(attachments[i])) throw { error: "Attachment should be a readable stream and not " + getType(attachments[i]) + "." };
|
46
32
|
const form = { upload_1024: attachments[i], voice_clip: "true" };
|
47
33
|
uploads.push(
|
48
34
|
defaultFuncs
|
49
35
|
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, {})
|
50
|
-
.then(
|
36
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
51
37
|
.then(resData => {
|
52
38
|
if (resData.error) throw resData;
|
53
39
|
return resData.payload.metadata[0];
|
@@ -61,11 +47,12 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
61
47
|
callback(err);
|
62
48
|
});
|
63
49
|
}
|
50
|
+
|
64
51
|
function getUrl(url, callback) {
|
65
52
|
const form = { image_height: 960, image_width: 960, uri: url };
|
66
53
|
defaultFuncs
|
67
54
|
.post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
|
68
|
-
.then(
|
55
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
69
56
|
.then(resData => {
|
70
57
|
if (resData.error) return callback(resData);
|
71
58
|
if (!resData.payload) return callback({ error: "Invalid url" });
|
@@ -76,12 +63,16 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
76
63
|
callback(err);
|
77
64
|
});
|
78
65
|
}
|
79
|
-
|
66
|
+
|
67
|
+
function sleep(ms) {
|
68
|
+
return new Promise(r => setTimeout(r, ms));
|
69
|
+
}
|
70
|
+
|
80
71
|
async function postWithRetry(url, jar, form, tries = 3) {
|
81
72
|
let lastErr;
|
82
73
|
for (let i = 0; i < tries; i++) {
|
83
74
|
try {
|
84
|
-
const res = await defaultFuncs.post(url, jar, form).then(
|
75
|
+
const res = await defaultFuncs.post(url, jar, form).then(parseAndCheckLogin(ctx, defaultFuncs));
|
85
76
|
if (res && !res.error) return res;
|
86
77
|
lastErr = res;
|
87
78
|
if (res && (res.error === 1545003 || res.error === 368)) await sleep(500 * (i + 1));
|
@@ -93,6 +84,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
93
84
|
}
|
94
85
|
throw lastErr || { error: "Send failed" };
|
95
86
|
}
|
87
|
+
|
96
88
|
function applyPageAuthor(form, msg) {
|
97
89
|
const pageID = msg && msg.asPage ? msg.asPage : ctx.globalOptions.pageID;
|
98
90
|
if (!pageID) return;
|
@@ -105,9 +97,16 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
105
97
|
form["request_user_id"] = pageID;
|
106
98
|
form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
|
107
99
|
}
|
100
|
+
|
108
101
|
function applyMentions(msg, form) {
|
109
102
|
if (!msg.mentions || !msg.mentions.length) return;
|
110
|
-
|
103
|
+
let body = typeof msg.body === "string" ? msg.body : "";
|
104
|
+
const need = [];
|
105
|
+
for (const m of msg.mentions) {
|
106
|
+
const tag = String(m.tag || "");
|
107
|
+
if (tag && !body.includes(tag)) need.push(tag);
|
108
|
+
}
|
109
|
+
if (need.length) body = (body ? body + " " : "") + need.join(" ");
|
111
110
|
const emptyChar = "\u200E";
|
112
111
|
form["body"] = emptyChar + body;
|
113
112
|
let searchFrom = 0;
|
@@ -122,16 +121,35 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
122
121
|
searchFrom = off + tag.length;
|
123
122
|
});
|
124
123
|
}
|
124
|
+
|
125
125
|
function finalizeHasAttachment(form) {
|
126
126
|
const keys = ["image_ids", "gif_ids", "file_ids", "video_ids", "audio_ids", "sticker_id", "shareable_attachment[share_params]"];
|
127
127
|
form.has_attachment = keys.some(k => k in form && (Array.isArray(form[k]) ? form[k].length > 0 : !!form[k]));
|
128
128
|
}
|
129
|
+
|
130
|
+
function extractMessageInfo(resData, fallbackThreadID) {
|
131
|
+
let messageID = null;
|
132
|
+
let threadFBID = null;
|
133
|
+
let timestamp = null;
|
134
|
+
const actions = resData && resData.payload && Array.isArray(resData.payload.actions) ? resData.payload.actions : null;
|
135
|
+
if (actions && actions.length) {
|
136
|
+
const v = actions.find(x => x && x.message_id) || actions[0];
|
137
|
+
messageID = v && v.message_id ? v.message_id : null;
|
138
|
+
threadFBID = (v && (v.thread_fbid || v.thread_id)) || fallbackThreadID || null;
|
139
|
+
timestamp = v && v.timestamp ? v.timestamp : null;
|
140
|
+
}
|
141
|
+
if (!messageID) messageID = (resData && resData.payload && resData.payload.message_id) || resData.message_id || null;
|
142
|
+
if (!threadFBID) threadFBID = (resData && resData.payload && resData.payload.thread_id) || fallbackThreadID || null;
|
143
|
+
if (!timestamp) timestamp = (resData && resData.timestamp) || Date.now();
|
144
|
+
if (!messageID) return null;
|
145
|
+
return { threadID: threadFBID, messageID, timestamp };
|
146
|
+
}
|
147
|
+
|
129
148
|
function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
|
130
|
-
if (
|
149
|
+
if (getType(threadID) === "Array") {
|
131
150
|
for (let i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
|
132
151
|
form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
|
133
152
|
form["client_thread_id"] = "root:" + messageAndOTID;
|
134
|
-
log.verbose("sendMessage", "Sending message to multiple users: " + threadID);
|
135
153
|
} else {
|
136
154
|
if (isSingleUser) {
|
137
155
|
form["specific_to_list[0]"] = "fbid:" + threadID;
|
@@ -149,25 +167,23 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
149
167
|
else log.error("sendMessage", resData);
|
150
168
|
return callback(resData);
|
151
169
|
}
|
152
|
-
|
153
|
-
if (
|
154
|
-
|
155
|
-
if (v && v.message_id) messageInfo = { threadID: v.thread_fbid, messageID: v.message_id, timestamp: v.timestamp };
|
156
|
-
}
|
157
|
-
}
|
158
|
-
callback(null, messageInfo);
|
170
|
+
const info = extractMessageInfo(resData, getType(threadID) === "Array" ? null : String(threadID));
|
171
|
+
if (!info) return callback({ error: "Cannot parse message info." });
|
172
|
+
callback(null, info);
|
159
173
|
})
|
160
174
|
.catch(err => {
|
161
175
|
log.error("sendMessage", err);
|
162
|
-
if (
|
176
|
+
if (getType(err) === "Object" && err.error === "Not logged in.") ctx.loggedIn = false;
|
163
177
|
callback(err);
|
164
178
|
});
|
165
179
|
}
|
180
|
+
|
166
181
|
function send(form, threadID, messageAndOTID, callback, isGroup) {
|
167
|
-
if (
|
168
|
-
if (
|
182
|
+
if (getType(threadID) === "Array") return sendContent(form, threadID, false, messageAndOTID, callback);
|
183
|
+
if (getType(isGroup) !== "Boolean") return sendContent(form, threadID, String(threadID).length === 15, messageAndOTID, callback);
|
169
184
|
return sendContent(form, threadID, !isGroup, messageAndOTID, callback);
|
170
185
|
}
|
186
|
+
|
171
187
|
function handleUrl(msg, form, callback, cb) {
|
172
188
|
if (msg.url) {
|
173
189
|
form["shareable_attachment[share_type]"] = "100";
|
@@ -178,6 +194,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
178
194
|
});
|
179
195
|
} else cb();
|
180
196
|
}
|
197
|
+
|
181
198
|
function handleLocation(msg, form, callback, cb) {
|
182
199
|
if (msg.location) {
|
183
200
|
if (msg.location.latitude == null || msg.location.longitude == null) return callback({ error: "location property needs both latitude and longitude" });
|
@@ -187,10 +204,12 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
187
204
|
}
|
188
205
|
cb();
|
189
206
|
}
|
207
|
+
|
190
208
|
function handleSticker(msg, form, callback, cb) {
|
191
209
|
if (msg.sticker) form["sticker_id"] = msg.sticker;
|
192
210
|
cb();
|
193
211
|
}
|
212
|
+
|
194
213
|
function handleEmoji(msg, form, callback, cb) {
|
195
214
|
if (msg.emojiSize != null && msg.emoji == null) return callback({ error: "emoji property is empty" });
|
196
215
|
if (msg.emoji) {
|
@@ -202,6 +221,32 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
202
221
|
}
|
203
222
|
cb();
|
204
223
|
}
|
224
|
+
|
225
|
+
function splitAttachments(list) {
|
226
|
+
if (!Array.isArray(list)) list = [list];
|
227
|
+
const ids = [];
|
228
|
+
const streams = [];
|
229
|
+
for (const a of list) {
|
230
|
+
if (Array.isArray(a) && /_id$/.test(a[0])) {
|
231
|
+
ids.push([a[0], String(a[1])]);
|
232
|
+
continue;
|
233
|
+
}
|
234
|
+
if (a && typeof a === "object") {
|
235
|
+
if (a.id && a.type && /_id$/.test(a.type)) {
|
236
|
+
ids.push([a.type, String(a.id)]);
|
237
|
+
continue;
|
238
|
+
}
|
239
|
+
const k = Object.keys(a || {}).find(x => /_id$/.test(x));
|
240
|
+
if (k) {
|
241
|
+
ids.push([k, String(a[k])]);
|
242
|
+
continue;
|
243
|
+
}
|
244
|
+
}
|
245
|
+
streams.push(toReadable(a));
|
246
|
+
}
|
247
|
+
return { ids, streams };
|
248
|
+
}
|
249
|
+
|
205
250
|
function handleAttachment(msg, form, callback, cb) {
|
206
251
|
if (!msg.attachment) return cb();
|
207
252
|
form["image_ids"] = [];
|
@@ -209,12 +254,10 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
209
254
|
form["file_ids"] = [];
|
210
255
|
form["video_ids"] = [];
|
211
256
|
form["audio_ids"] = [];
|
212
|
-
const
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
}
|
217
|
-
uploadAttachment(items, function (err, files) {
|
257
|
+
const { ids, streams } = splitAttachments(msg.attachment);
|
258
|
+
for (const [type, id] of ids) form[`${type}s`].push(id);
|
259
|
+
if (!streams.length) return cb();
|
260
|
+
uploadAttachment(streams, function (err, files) {
|
218
261
|
if (err) return callback(err);
|
219
262
|
files.forEach(function (file) {
|
220
263
|
const type = Object.keys(file)[0];
|
@@ -223,16 +266,32 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
223
266
|
cb();
|
224
267
|
});
|
225
268
|
}
|
269
|
+
|
226
270
|
function handleMention(msg, form, callback, cb) {
|
227
|
-
try {
|
271
|
+
try {
|
272
|
+
applyMentions(msg, form);
|
273
|
+
cb();
|
274
|
+
} catch (e) {
|
275
|
+
callback(e);
|
276
|
+
}
|
228
277
|
}
|
278
|
+
|
229
279
|
return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
|
280
|
+
const isFn = v => typeof v === "function";
|
281
|
+
const isStr = v => typeof v === "string";
|
282
|
+
|
230
283
|
if (typeof isGroup === "undefined") isGroup = null;
|
231
|
-
if (!callback && (
|
232
|
-
|
284
|
+
if (!callback && (getType(threadID) === "Function" || getType(threadID) === "AsyncFunction")) return threadID({ error: "Pass a threadID as a second argument." });
|
285
|
+
|
286
|
+
if (isStr(callback) && isFn(replyToMessage)) {
|
287
|
+
const t = callback;
|
288
|
+
callback = replyToMessage;
|
289
|
+
replyToMessage = t;
|
290
|
+
} else if (!replyToMessage && isStr(callback)) {
|
233
291
|
replyToMessage = callback;
|
234
|
-
callback =
|
292
|
+
callback = null;
|
235
293
|
}
|
294
|
+
|
236
295
|
let resolveFunc = function () { };
|
237
296
|
let rejectFunc = function () { };
|
238
297
|
const returnPromise = new Promise(function (resolve, reject) {
|
@@ -245,62 +304,73 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
245
304
|
resolveFunc(data);
|
246
305
|
};
|
247
306
|
}
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
307
|
+
|
308
|
+
try {
|
309
|
+
const msgType = getType(msg);
|
310
|
+
const threadIDType = getType(threadID);
|
311
|
+
const messageIDType = getType(replyToMessage);
|
312
|
+
if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
|
313
|
+
if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
|
314
|
+
if (replyToMessage && messageIDType !== "String") return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
|
315
|
+
if (msgType === "String") msg = { body: msg };
|
316
|
+
|
317
|
+
const disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
|
318
|
+
if (disallowedProperties.length > 0) return callback({ error: "Disallowed props: `" + disallowedProperties.join(", ") + "`" });
|
319
|
+
|
320
|
+
const messageAndOTID = generateOfflineThreadingID();
|
321
|
+
const form = {
|
322
|
+
client: "mercury",
|
323
|
+
action_type: "ma-type:user-generated-message",
|
324
|
+
author: "fbid:" + ctx.userID,
|
325
|
+
timestamp: Date.now(),
|
326
|
+
timestamp_absolute: "Today",
|
327
|
+
timestamp_relative: generateTimestampRelative(),
|
328
|
+
timestamp_time_passed: "0",
|
329
|
+
is_unread: false,
|
330
|
+
is_cleared: false,
|
331
|
+
is_forward: false,
|
332
|
+
is_filtered_content: false,
|
333
|
+
is_filtered_content_bh: false,
|
334
|
+
is_filtered_content_account: false,
|
335
|
+
is_filtered_content_quasar: false,
|
336
|
+
is_filtered_content_invalid_app: false,
|
337
|
+
is_spoof_warning: false,
|
338
|
+
source: "source:chat:web",
|
339
|
+
"source_tags[0]": "source:chat",
|
340
|
+
body: msg.body ? msg.body.toString() : "",
|
341
|
+
html_body: false,
|
342
|
+
ui_push_phase: "V3",
|
343
|
+
status: "0",
|
344
|
+
offline_threading_id: messageAndOTID,
|
345
|
+
message_id: messageAndOTID,
|
346
|
+
threading_id: generateThreadingID(ctx.clientID),
|
347
|
+
ephemeral_ttl_mode: "0",
|
348
|
+
manual_retry_cnt: "0",
|
349
|
+
signatureID: getSignatureID(),
|
350
|
+
replied_to_message_id: replyToMessage ? replyToMessage.toString() : ""
|
351
|
+
};
|
352
|
+
applyPageAuthor(form, msg);
|
353
|
+
handleLocation(msg, form, callback, () =>
|
354
|
+
handleSticker(msg, form, callback, () =>
|
355
|
+
handleAttachment(msg, form, callback, () =>
|
356
|
+
handleUrl(msg, form, callback, () =>
|
357
|
+
handleEmoji(msg, form, callback, () =>
|
358
|
+
handleMention(msg, form, callback, () => {
|
359
|
+
finalizeHasAttachment(form);
|
360
|
+
send(form, threadID, messageAndOTID, callback, isGroup);
|
361
|
+
})
|
362
|
+
)
|
299
363
|
)
|
300
364
|
)
|
301
365
|
)
|
302
|
-
)
|
303
|
-
)
|
366
|
+
);
|
367
|
+
} catch (e) {
|
368
|
+
log.error("sendMessage", e);
|
369
|
+
if (getType(e) === "Object" && e.error === "Not logged in.") ctx.loggedIn = false;
|
370
|
+
return callback(e);
|
371
|
+
}
|
372
|
+
|
304
373
|
return returnPromise;
|
305
374
|
};
|
375
|
+
|
306
376
|
};
|