shadowx-fca 8.0.0
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/README.md +1066 -0
- package/build/messagix.dll +0 -0
- package/build/messagix.so +0 -0
- package/checkUpdate.js +393 -0
- package/config.json +17 -0
- package/e2ee.js +563 -0
- package/e2eetest.js +356 -0
- package/index.js +611 -0
- package/lib/index.mjs +1412 -0
- package/logger.js +500 -0
- package/package.json +65 -0
- package/src/GetBotInfo.js +66 -0
- package/src/OldMessage.js +182 -0
- package/src/Screenshot.js +83 -0
- package/src/addExternalModule.js +13 -0
- package/src/addUserToGroup.js +33 -0
- package/src/approveGroupJoinRequests.js +18 -0
- package/src/changeAdminStatus.js +16 -0
- package/src/changeArchivedStatus.js +17 -0
- package/src/changeAvatar.js +136 -0
- package/src/changeAvatarV2.js +86 -0
- package/src/changeAvt.js +85 -0
- package/src/changeBio.js +76 -0
- package/src/changeBlockedStatus.js +20 -0
- package/src/changeBlockedStatusMqtt.js +80 -0
- package/src/changeCover.js +72 -0
- package/src/changeGroupImage.js +16 -0
- package/src/changeName.js +79 -0
- package/src/changeNickname.js +16 -0
- package/src/changeThreadColor.js +15 -0
- package/src/changeThreadEmoji.js +15 -0
- package/src/changeThreadMemberNickname.js +6 -0
- package/src/changeUsername.js +59 -0
- package/src/createCommentPost.js +230 -0
- package/src/createNewGroup.js +38 -0
- package/src/createNote.js +35 -0
- package/src/createPoll.js +27 -0
- package/src/createPost.js +276 -0
- package/src/createThemeAI.js +129 -0
- package/src/data/cache/system/data.json +4 -0
- package/src/data/cache/system/datahandle.js +21 -0
- package/src/data/getThreadInfo.json +1 -0
- package/src/deleteComment.js +23 -0
- package/src/deleteMessage.js +15 -0
- package/src/deleteThread.js +15 -0
- package/src/denyGroupJoinRequests.js +18 -0
- package/src/e2ee/crypto.js +173 -0
- package/src/e2ee/index.js +144 -0
- package/src/e2ee/proto/ArmadilloApplication.proto +281 -0
- package/src/e2ee/proto/ArmadilloICDC.proto +14 -0
- package/src/e2ee/proto/ConsumerApplication.proto +232 -0
- package/src/e2ee/proto/MessageApplication.proto +82 -0
- package/src/e2ee/proto/MessageTransport.proto +77 -0
- package/src/e2ee/proto/WACommon.proto +66 -0
- package/src/e2ee/proto/WAMediaTransport.proto +176 -0
- package/src/e2ee/proto/proto-writer.ts +76 -0
- package/src/e2ee/protocol.js +196 -0
- package/src/e2ee/ratchet.js +219 -0
- package/src/e2ee/store.js +182 -0
- package/src/e2ee.js +8 -0
- package/src/editMessage.js +56 -0
- package/src/editMessageOld.js +67 -0
- package/src/enableReactions.js +24 -0
- package/src/follow.js +74 -0
- package/src/followUser.js +23 -0
- package/src/forwardAttachment.js +16 -0
- package/src/friendList.js +98 -0
- package/src/getAccess.js +112 -0
- package/src/getAppState.js +13 -0
- package/src/getAvatarUser.js +11 -0
- package/src/getBio.js +24 -0
- package/src/getBotInitialData.js +42 -0
- package/src/getCtx.js +5 -0
- package/src/getCurrentUserID.js +6 -0
- package/src/getEmojiUrl.js +29 -0
- package/src/getFriendsList.js +36 -0
- package/src/getMessage.js +37 -0
- package/src/getNotes.js +17 -0
- package/src/getOptions.js +5 -0
- package/src/getPinnedMessages.js +33 -0
- package/src/getPostInfo.js +17 -0
- package/src/getProfileInfo.js +17 -0
- package/src/getPublicData.js +25 -0
- package/src/getRegion.js +7 -0
- package/src/getRepInfo.js +17 -0
- package/src/getStickerPacks.js +25 -0
- package/src/getStickers.js +39 -0
- package/src/getStoryReactions.js +18 -0
- package/src/getThreadHistory.js +45 -0
- package/src/getThreadHistoryDeprecated.js +71 -0
- package/src/getThreadInfo.js +73 -0
- package/src/getThreadInfoDeprecated.js +56 -0
- package/src/getThreadList.js +76 -0
- package/src/getThreadListDeprecated.js +46 -0
- package/src/getThreadPictures.js +59 -0
- package/src/getThreadTheme.js +77 -0
- package/src/getUID.js +17 -0
- package/src/getUserID.js +17 -0
- package/src/getUserInfo.js +28 -0
- package/src/handleFriendRequest.js +21 -0
- package/src/handleMessageRequest.js +15 -0
- package/src/httpGet.js +13 -0
- package/src/httpPost.js +12 -0
- package/src/httpPostFormData.js +12 -0
- package/src/listenE2EE.js +75 -0
- package/src/listenMqtt.js +802 -0
- package/src/listenNotification.js +85 -0
- package/src/logout.js +22 -0
- package/src/markAsDelivered.js +17 -0
- package/src/markAsRead.js +14 -0
- package/src/markAsReadAll.js +15 -0
- package/src/markAsSeen.js +15 -0
- package/src/metaTheme.js +185 -0
- package/src/muteThread.js +52 -0
- package/src/note.js +228 -0
- package/src/pin.js +53 -0
- package/src/pinMessage.js +6 -0
- package/src/postComment.js +29 -0
- package/src/postFormData.js +46 -0
- package/src/reactToComment.js +31 -0
- package/src/reactToPost.js +32 -0
- package/src/refreshFb_dtsg.js +31 -0
- package/src/removeSuspiciousAccount.js +74 -0
- package/src/removeUserFromGroup.js +15 -0
- package/src/reply.js +442 -0
- package/src/resolvePhotoUrl.js +15 -0
- package/src/searchForThread.js +20 -0
- package/src/searchFriends.js +28 -0
- package/src/searchStickers.js +53 -0
- package/src/send.js +46 -0
- package/src/sendAudio.js +8 -0
- package/src/sendBroadcast.js +93 -0
- package/src/sendButtons.js +161 -0
- package/src/sendComment.js +159 -0
- package/src/sendEmoji.js +10 -0
- package/src/sendFile.js +9 -0
- package/src/sendFriendRequest.js +33 -0
- package/src/sendGif.js +24 -0
- package/src/sendImage.js +9 -0
- package/src/sendLocation.js +9 -0
- package/src/sendMessage.js +487 -0
- package/src/sendMessage1.js +309 -0
- package/src/sendMessageMqtt.js +68 -0
- package/src/sendSticker.js +8 -0
- package/src/sendTypingIndicator.js +36 -0
- package/src/sendTypingIndicatorV2.js +28 -0
- package/src/sendVideo.js +9 -0
- package/src/sessionGuard.js +130 -0
- package/src/setActiveStatus.js +16 -0
- package/src/setMessageReaction.js +61 -0
- package/src/setMessageReactionMqtt.js +62 -0
- package/src/setOptions.js +22 -0
- package/src/setPollVote.js +17 -0
- package/src/setPostReaction.js +112 -0
- package/src/setProfileGuard.js +44 -0
- package/src/setProfileLock.js +93 -0
- package/src/setStoryReaction.js +129 -0
- package/src/setStorySeen.js +99 -0
- package/src/setThreadTheme.js +17 -0
- package/src/setTitle.js +15 -0
- package/src/shareContact.js +33 -0
- package/src/shareLink.js +8 -0
- package/src/sharePost.js +31 -0
- package/src/stopListenMqtt.js +23 -0
- package/src/storyManager.js +353 -0
- package/src/suggestFriend.js +128 -0
- package/src/threadColors.js +131 -0
- package/src/unfollowUser.js +23 -0
- package/src/unfriend.js +15 -0
- package/src/unpinMessage.js +6 -0
- package/src/unsendMessage.js +14 -0
- package/src/uploadAttachment.js +58 -0
- package/src/uploadImageToImgbb.js +29 -0
- package/utils.js +2945 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var utils = require("../utils");
|
|
4
|
+
var logger = require("../logger");
|
|
5
|
+
|
|
6
|
+
var ALLOWED = {
|
|
7
|
+
attachment: true, url: true, sticker: true, emoji: true,
|
|
8
|
+
emojiSize: true, body: true, mentions: true, location: true,
|
|
9
|
+
replyToMessage: true, forwardAttachmentIds: true
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
var EMOJI_SIZES = { small: 1, medium: 2, large: 3 };
|
|
13
|
+
|
|
14
|
+
function toEmojiSize(size) {
|
|
15
|
+
if (typeof size === "number" && !isNaN(size)) return Math.min(3, Math.max(1, size));
|
|
16
|
+
if (typeof size === "string" && size in EMOJI_SIZES) return EMOJI_SIZES[size];
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hasLinks(text) {
|
|
21
|
+
return /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildMentionData(msg, baseBody) {
|
|
25
|
+
if (!Array.isArray(msg.mentions) || !msg.mentions.length) return null;
|
|
26
|
+
var ids = [], offsets = [], lengths = [], types = [];
|
|
27
|
+
for (var i = 0; i < msg.mentions.length; i++) {
|
|
28
|
+
var mention = msg.mentions[i];
|
|
29
|
+
var tag = String(mention.tag || "");
|
|
30
|
+
if (tag && !tag.startsWith("@")) tag = "@" + tag;
|
|
31
|
+
var fromIndex = Number.isInteger(mention.fromIndex) ? mention.fromIndex : 0;
|
|
32
|
+
var offset = baseBody.indexOf(tag, fromIndex);
|
|
33
|
+
if (offset === -1) {
|
|
34
|
+
offset = baseBody.indexOf(tag.slice(1), fromIndex);
|
|
35
|
+
}
|
|
36
|
+
if (offset < 0) offset = 0;
|
|
37
|
+
ids.push(String(mention.id || 0));
|
|
38
|
+
offsets.push(offset);
|
|
39
|
+
lengths.push(tag.length);
|
|
40
|
+
types.push("p");
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
mention_ids: ids.join(","),
|
|
44
|
+
mention_offsets: offsets.join(","),
|
|
45
|
+
mention_lengths: lengths.join(","),
|
|
46
|
+
mention_types: types.join(",")
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractIdsFromPayload(payload) {
|
|
51
|
+
var messageID = null, threadID = null;
|
|
52
|
+
function walk(node) {
|
|
53
|
+
if (!Array.isArray(node)) return;
|
|
54
|
+
if (node[0] === 5 && (node[1] === "replaceOptimsiticMessage" || node[1] === "replaceOptimisticMessage")) {
|
|
55
|
+
messageID = String(node[3]);
|
|
56
|
+
}
|
|
57
|
+
if (node[0] === 5 && node[1] === "writeCTAIdToThreadsTable") {
|
|
58
|
+
var candidate = node[2];
|
|
59
|
+
if (Array.isArray(candidate) && candidate[0] === 19) threadID = String(candidate[1]);
|
|
60
|
+
}
|
|
61
|
+
for (var i = 0; i < node.length; i++) walk(node[i]);
|
|
62
|
+
}
|
|
63
|
+
try { walk(payload && payload.step); } catch (_) { }
|
|
64
|
+
return { threadID, messageID };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function publishLsRequestWithAck(mqttClient, content, requestId, timeout) {
|
|
68
|
+
timeout = timeout || 15000;
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
var timer = setTimeout(() => {
|
|
71
|
+
mqttClient.removeListener('message', onMessage);
|
|
72
|
+
reject(new Error('MQTT sendMessage timed out after ' + timeout + 'ms'));
|
|
73
|
+
}, timeout);
|
|
74
|
+
|
|
75
|
+
function onMessage(topic, message) {
|
|
76
|
+
if (topic !== '/ls_resp') return;
|
|
77
|
+
try {
|
|
78
|
+
var data = JSON.parse(message.toString());
|
|
79
|
+
if (String(data.request_id) === String(requestId)) {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
mqttClient.removeListener('message', onMessage);
|
|
82
|
+
var extracted = extractIdsFromPayload(data.payload ? JSON.parse(data.payload) : {});
|
|
83
|
+
resolve({ threadID: extracted.threadID, messageID: extracted.messageID });
|
|
84
|
+
}
|
|
85
|
+
} catch (_) { }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
mqttClient.on('message', onMessage);
|
|
89
|
+
mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1 }, err => {
|
|
90
|
+
if (err) {
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
mqttClient.removeListener('message', onMessage);
|
|
93
|
+
reject(err);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
100
|
+
// HTTP-based upload for OldMessage fallback
|
|
101
|
+
async function uploadAttachmentHTTP(attachments) {
|
|
102
|
+
var uploads = [];
|
|
103
|
+
for (var i = 0; i < attachments.length; i++) {
|
|
104
|
+
if (!utils.isReadableStream(attachments[i])) {
|
|
105
|
+
throw new Error("Attachment should be a readable stream and not " + utils.getType(attachments[i]) + ".");
|
|
106
|
+
}
|
|
107
|
+
var formData = { upload_1024: attachments[i], voice_clip: "true" };
|
|
108
|
+
var resData = await defaultFuncs
|
|
109
|
+
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, formData, {})
|
|
110
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
111
|
+
if (!resData || resData.error) {
|
|
112
|
+
throw new Error(resData ? resData.error : "Upload failed");
|
|
113
|
+
}
|
|
114
|
+
uploads.push(resData.payload.metadata[0]);
|
|
115
|
+
}
|
|
116
|
+
return uploads;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// MQTT-based upload using uploadAttachment module
|
|
120
|
+
var uploadAttachmentMQTT = require('./uploadAttachment')(defaultFuncs, api, ctx);
|
|
121
|
+
|
|
122
|
+
async function uploadAttachmentsMQTT(attachments) {
|
|
123
|
+
if (!Array.isArray(attachments)) attachments = [attachments];
|
|
124
|
+
return uploadAttachmentMQTT(attachments);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// URL sharing helper for HTTP path
|
|
128
|
+
async function getShareUrl(url) {
|
|
129
|
+
var resData = await defaultFuncs
|
|
130
|
+
.post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, {
|
|
131
|
+
image_height: 960,
|
|
132
|
+
image_width: 960,
|
|
133
|
+
uri: url
|
|
134
|
+
})
|
|
135
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
136
|
+
if (!resData || resData.error || !resData.payload) {
|
|
137
|
+
throw new Error(resData ? resData.error : "Failed to get share URL");
|
|
138
|
+
}
|
|
139
|
+
return resData.payload.share_params;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// MQTT send path
|
|
143
|
+
async function sendViaMqtt(msg, threadID, replyToMessage) {
|
|
144
|
+
var mqttClient = ctx.mqttClient || global.mqttClient;
|
|
145
|
+
if (!mqttClient) throw new Error('MQTT client not available');
|
|
146
|
+
|
|
147
|
+
var baseBody = msg.body != null ? String(msg.body) : "";
|
|
148
|
+
var requestId = Math.floor(100 + Math.random() * 900);
|
|
149
|
+
var epoch = (BigInt(Date.now()) << 22n).toString();
|
|
150
|
+
|
|
151
|
+
var payload0 = {
|
|
152
|
+
thread_id: String(threadID),
|
|
153
|
+
otid: utils.generateOfflineThreadingID(),
|
|
154
|
+
source: 2097153,
|
|
155
|
+
send_type: 1,
|
|
156
|
+
sync_group: 1,
|
|
157
|
+
mark_thread_read: 1,
|
|
158
|
+
text: baseBody === "" ? null : baseBody,
|
|
159
|
+
initiating_source: 0,
|
|
160
|
+
skip_url_preview_gen: 0,
|
|
161
|
+
text_has_links: hasLinks(baseBody) ? 1 : 0,
|
|
162
|
+
multitab_env: 0,
|
|
163
|
+
metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } })
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
var mentionData = buildMentionData(msg, baseBody);
|
|
167
|
+
if (mentionData) payload0.mention_data = mentionData;
|
|
168
|
+
|
|
169
|
+
if (msg.sticker) {
|
|
170
|
+
payload0.send_type = 2;
|
|
171
|
+
payload0.sticker_id = msg.sticker;
|
|
172
|
+
}
|
|
173
|
+
if (msg.emoji) {
|
|
174
|
+
payload0.send_type = 1;
|
|
175
|
+
payload0.text = msg.emoji;
|
|
176
|
+
payload0.hot_emoji_size = toEmojiSize(msg.emojiSize);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (msg.location && msg.location.latitude != null && msg.location.longitude != null) {
|
|
180
|
+
payload0.send_type = 1;
|
|
181
|
+
payload0.location_data = {
|
|
182
|
+
coordinates: { latitude: msg.location.latitude, longitude: msg.location.longitude },
|
|
183
|
+
is_current_location: Boolean(msg.location.current),
|
|
184
|
+
is_live_location: Boolean(msg.location.live)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
var effectiveReplyTo = replyToMessage || msg.replyToMessage;
|
|
189
|
+
if (effectiveReplyTo) {
|
|
190
|
+
payload0.reply_metadata = {
|
|
191
|
+
reply_source_id: effectiveReplyTo,
|
|
192
|
+
reply_source_type: 1,
|
|
193
|
+
reply_type: 0
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (msg.url) {
|
|
198
|
+
// URL sharing via MQTT
|
|
199
|
+
payload0.send_type = 1;
|
|
200
|
+
payload0.link_share = {
|
|
201
|
+
share_type: 100,
|
|
202
|
+
share_params: await getShareUrl(msg.url)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (msg.attachment) {
|
|
207
|
+
payload0.send_type = 3;
|
|
208
|
+
if (payload0.text === "") payload0.text = null;
|
|
209
|
+
payload0.attachment_fbids = [];
|
|
210
|
+
var list = Array.isArray(msg.attachment) ? msg.attachment : [msg.attachment];
|
|
211
|
+
var preuploaded = [], toUpload = [];
|
|
212
|
+
for (var item of list) {
|
|
213
|
+
if (Array.isArray(item) && item.length >= 2 && typeof item[0] === "string") {
|
|
214
|
+
preuploaded.push(String(item[1]));
|
|
215
|
+
} else if (utils.isReadableStream(item)) {
|
|
216
|
+
toUpload.push(item);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (preuploaded.length) payload0.attachment_fbids = payload0.attachment_fbids.concat(preuploaded);
|
|
220
|
+
if (msg.forwardAttachmentIds && msg.forwardAttachmentIds.length) {
|
|
221
|
+
payload0.attachment_fbids = payload0.attachment_fbids.concat(msg.forwardAttachmentIds.map(String));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (toUpload.length) {
|
|
225
|
+
var uploaded = await uploadAttachmentsMQTT(toUpload);
|
|
226
|
+
for (var u of uploaded) {
|
|
227
|
+
if (!u) continue;
|
|
228
|
+
var fbid = u.image_id || u.video_id || u.audio_id || u.file_id || u.sticker_id || u.gif_id;
|
|
229
|
+
if (!fbid) {
|
|
230
|
+
var firstKey = Object.keys(u).find(function(k) { return u[k] && /^\d+$/.test(String(u[k])); });
|
|
231
|
+
if (firstKey) fbid = u[firstKey];
|
|
232
|
+
}
|
|
233
|
+
if (fbid) payload0.attachment_fbids.push(String(fbid));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (!payload0.attachment_fbids.length) delete payload0.attachment_fbids;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
var tasks = [
|
|
240
|
+
{
|
|
241
|
+
label: '46',
|
|
242
|
+
payload: JSON.stringify(payload0),
|
|
243
|
+
queue_name: String(threadID),
|
|
244
|
+
task_id: 400,
|
|
245
|
+
failure_count: null
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
label: '21',
|
|
249
|
+
payload: JSON.stringify({
|
|
250
|
+
thread_id: String(threadID),
|
|
251
|
+
last_read_watermark_ts: Date.now(),
|
|
252
|
+
sync_group: 1
|
|
253
|
+
}),
|
|
254
|
+
queue_name: String(threadID),
|
|
255
|
+
task_id: 401,
|
|
256
|
+
failure_count: null
|
|
257
|
+
}
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
var content = {
|
|
261
|
+
app_id: '2220391788200892',
|
|
262
|
+
payload: JSON.stringify({
|
|
263
|
+
tasks: tasks,
|
|
264
|
+
epoch_id: epoch,
|
|
265
|
+
version_id: '24804310205905615',
|
|
266
|
+
data_trace_id: '#' + Buffer.from(String(Math.random())).toString('base64').replace(/=+$/, '')
|
|
267
|
+
}),
|
|
268
|
+
request_id: requestId,
|
|
269
|
+
type: 3
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
return publishLsRequestWithAck(mqttClient, content, requestId, 15000);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// HTTP send path (OldMessage style)
|
|
276
|
+
async function sendViaHTTP(msg, threadID, isSingleUser, replyToMessage) {
|
|
277
|
+
var messageAndOTID = utils.generateOfflineThreadingID();
|
|
278
|
+
var form = {
|
|
279
|
+
client: "mercury",
|
|
280
|
+
action_type: "ma-type:user-generated-message",
|
|
281
|
+
author: "fbid:" + ctx.userID,
|
|
282
|
+
timestamp: Date.now(),
|
|
283
|
+
timestamp_absolute: "Today",
|
|
284
|
+
timestamp_relative: utils.generateTimestampRelative(),
|
|
285
|
+
timestamp_time_passed: "0",
|
|
286
|
+
is_unread: false,
|
|
287
|
+
is_cleared: false,
|
|
288
|
+
is_forward: false,
|
|
289
|
+
is_filtered_content: false,
|
|
290
|
+
is_filtered_content_bh: false,
|
|
291
|
+
is_filtered_content_account: false,
|
|
292
|
+
is_filtered_content_quasar: false,
|
|
293
|
+
is_filtered_content_invalid_app: false,
|
|
294
|
+
is_spoof_warning: false,
|
|
295
|
+
source: "source:chat:web",
|
|
296
|
+
"source_tags[0]": "source:chat",
|
|
297
|
+
body: msg.body || "",
|
|
298
|
+
html_body: false,
|
|
299
|
+
ui_push_phase: "V3",
|
|
300
|
+
status: "0",
|
|
301
|
+
offline_threading_id: messageAndOTID,
|
|
302
|
+
message_id: messageAndOTID,
|
|
303
|
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
|
304
|
+
"ephemeral_ttl_mode:": "0",
|
|
305
|
+
manual_retry_cnt: "0",
|
|
306
|
+
has_attachment: !!(msg.attachment || msg.url || msg.sticker),
|
|
307
|
+
signatureID: utils.getSignatureID(),
|
|
308
|
+
replied_to_message_id: replyToMessage || ""
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
var threadIDStr = threadID.toString();
|
|
312
|
+
if (utils.getType(threadID) === "Array") {
|
|
313
|
+
for (var i = 0; i < threadID.length; i++) {
|
|
314
|
+
form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
|
|
315
|
+
}
|
|
316
|
+
form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
|
|
317
|
+
form["client_thread_id"] = "root:" + messageAndOTID;
|
|
318
|
+
} else {
|
|
319
|
+
if (isSingleUser) {
|
|
320
|
+
form["specific_to_list[0]"] = "fbid:" + threadIDStr;
|
|
321
|
+
form["specific_to_list[1]"] = "fbid:" + ctx.userID;
|
|
322
|
+
form["other_user_fbid"] = threadIDStr;
|
|
323
|
+
} else {
|
|
324
|
+
form["thread_fbid"] = threadIDStr;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (ctx.globalOptions.pageID) {
|
|
329
|
+
form["author"] = "fbid:" + ctx.globalOptions.pageID;
|
|
330
|
+
form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
|
|
331
|
+
form["creator_info[creatorID]"] = ctx.userID;
|
|
332
|
+
form["creator_info[creatorType]"] = "direct_admin";
|
|
333
|
+
form["creator_info[labelType]"] = "sent_message";
|
|
334
|
+
form["creator_info[pageID]"] = ctx.globalOptions.pageID;
|
|
335
|
+
form["request_user_id"] = ctx.globalOptions.pageID;
|
|
336
|
+
form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (msg.location) {
|
|
340
|
+
form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
|
|
341
|
+
form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
|
|
342
|
+
form["location_attachment[is_current_location]"] = !!msg.location.current;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (msg.sticker) {
|
|
346
|
+
form["sticker_id"] = msg.sticker;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (msg.emoji) {
|
|
350
|
+
form.body = msg.emoji;
|
|
351
|
+
form["tags[0]"] = "hot_emoji_size:" + (msg.emojiSize || "medium");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (msg.mentions) {
|
|
355
|
+
for (var j = 0; j < msg.mentions.length; j++) {
|
|
356
|
+
var mention = msg.mentions[j];
|
|
357
|
+
var tag = mention.tag;
|
|
358
|
+
var offset = (msg.body || "").indexOf(tag, mention.fromIndex || 0);
|
|
359
|
+
if (offset < 0) {
|
|
360
|
+
logger.warn("sendMessage", 'Mention for "' + tag + '" not found in message string.');
|
|
361
|
+
}
|
|
362
|
+
form["profile_xmd[" + j + "][offset]"] = offset + 1;
|
|
363
|
+
form["profile_xmd[" + j + "][length]"] = tag.length;
|
|
364
|
+
form["profile_xmd[" + j + "][id]"] = mention.id || 0;
|
|
365
|
+
form["profile_xmd[" + j + "][type]"] = "p";
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (msg.attachment) {
|
|
370
|
+
form.image_ids = [];
|
|
371
|
+
form.gif_ids = [];
|
|
372
|
+
form.file_ids = [];
|
|
373
|
+
form.video_ids = [];
|
|
374
|
+
form.audio_ids = [];
|
|
375
|
+
var attachments = utils.getType(msg.attachment) !== "Array" ? [msg.attachment] : msg.attachment;
|
|
376
|
+
var files = await uploadAttachmentHTTP(attachments);
|
|
377
|
+
files.forEach(function(file) {
|
|
378
|
+
var type = Object.keys(file)[0];
|
|
379
|
+
form[type + "s"].push(file[type]);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (msg.url) {
|
|
384
|
+
form["shareable_attachment[share_type]"] = "100";
|
|
385
|
+
form["shareable_attachment[share_params]"] = await getShareUrl(msg.url);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
var resData = await defaultFuncs
|
|
389
|
+
.post("https://www.facebook.com/messaging/send/", ctx.jar, form)
|
|
390
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
391
|
+
|
|
392
|
+
if (!resData) throw new Error("Send message failed.");
|
|
393
|
+
if (resData.error) {
|
|
394
|
+
if (resData.error === 1545012) {
|
|
395
|
+
throw new Error("Cannot send message to thread " + threadID + ": Bot is not part of this conversation (Error 1545012)");
|
|
396
|
+
}
|
|
397
|
+
throw new Error(resData.error);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return resData.payload.actions.reduce(function(p, v) {
|
|
401
|
+
return { threadID: v.thread_fbid, messageID: v.message_id, timestamp: v.timestamp } || p;
|
|
402
|
+
}, null);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return async function sendMessage(msg, threadID, callback, replyToMessage, isSingleUser) {
|
|
406
|
+
// Normalize parameters
|
|
407
|
+
if (typeof callback === "string" || (callback && typeof callback === "object")) {
|
|
408
|
+
isSingleUser = replyToMessage;
|
|
409
|
+
replyToMessage = callback;
|
|
410
|
+
callback = function() {};
|
|
411
|
+
}
|
|
412
|
+
if (typeof msg === "string") msg = { body: msg };
|
|
413
|
+
if (typeof callback !== "function") callback = null;
|
|
414
|
+
|
|
415
|
+
var resolve, reject;
|
|
416
|
+
var promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
|
417
|
+
|
|
418
|
+
// Validate properties
|
|
419
|
+
for (var key in msg) {
|
|
420
|
+
if (!ALLOWED[key]) {
|
|
421
|
+
var err = { error: "sendMessage: Unknown property '" + key + "'" };
|
|
422
|
+
if (callback) callback(err);
|
|
423
|
+
else reject(err);
|
|
424
|
+
return promise;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Auto-detect isSingleUser from ctx.threadTypes if not explicitly provided
|
|
429
|
+
if (isSingleUser === undefined && ctx.threadTypes) {
|
|
430
|
+
isSingleUser = ctx.threadTypes[String(threadID)] === 'dm';
|
|
431
|
+
}
|
|
432
|
+
if (isSingleUser === undefined) {
|
|
433
|
+
var threadIDStr = threadID.toString();
|
|
434
|
+
isSingleUser = threadIDStr.length === 15 || !threadIDStr.match(/^\d{16,}$/);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// DM attachment sends — route appropriately
|
|
438
|
+
if (isSingleUser && msg.attachment) {
|
|
439
|
+
var useE2EE = api.e2ee && typeof api.e2ee.isConnected === "function" && api.e2ee.isConnected();
|
|
440
|
+
if (useE2EE) {
|
|
441
|
+
try {
|
|
442
|
+
var e2eeResult = await api.e2ee.sendMessage(String(threadID), msg, replyToMessage);
|
|
443
|
+
var wrapped = e2eeResult && e2eeResult.messageId
|
|
444
|
+
? { threadID: String(threadID), messageID: String(e2eeResult.messageId) }
|
|
445
|
+
: e2eeResult;
|
|
446
|
+
if (callback) callback(null, wrapped);
|
|
447
|
+
else resolve(wrapped);
|
|
448
|
+
} catch (e2eeErr) {
|
|
449
|
+
logger.error("sendMessage", "E2EE DM attachment send failed: " + (e2eeErr.message || e2eeErr));
|
|
450
|
+
if (callback) callback(e2eeErr);
|
|
451
|
+
else reject(e2eeErr);
|
|
452
|
+
}
|
|
453
|
+
} else {
|
|
454
|
+
try {
|
|
455
|
+
var httpResult = await sendViaHTTP(msg, threadID, true, replyToMessage);
|
|
456
|
+
if (callback) callback(null, httpResult);
|
|
457
|
+
else resolve(httpResult);
|
|
458
|
+
} catch (httpErr) {
|
|
459
|
+
logger.error("sendMessage", "DM attachment via HTTP failed: " + (httpErr.error || httpErr.message || httpErr));
|
|
460
|
+
if (callback) callback(httpErr);
|
|
461
|
+
else reject(httpErr);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return promise;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Try MQTT first, fallback to HTTP
|
|
468
|
+
try {
|
|
469
|
+
var mqttResult = await sendViaMqtt(msg, threadID, replyToMessage);
|
|
470
|
+
if (callback) callback(null, mqttResult);
|
|
471
|
+
else resolve(mqttResult);
|
|
472
|
+
} catch (mqttErr) {
|
|
473
|
+
logger.warn("sendMessage", "MQTT failed, falling back to HTTP: " + (mqttErr.message || mqttErr));
|
|
474
|
+
try {
|
|
475
|
+
var httpResult = await sendViaHTTP(msg, threadID, isSingleUser, replyToMessage);
|
|
476
|
+
if (callback) callback(null, httpResult);
|
|
477
|
+
else resolve(httpResult);
|
|
478
|
+
} catch (httpErr) {
|
|
479
|
+
logger.error("sendMessage", "HTTP fallback also failed: " + (httpErr.error || httpErr.message || httpErr));
|
|
480
|
+
if (callback) callback(httpErr);
|
|
481
|
+
else reject(httpErr);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return promise;
|
|
486
|
+
};
|
|
487
|
+
};
|