@lazyneoaz/metachat 1.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/LICENSE +3 -0
- package/README.md +199 -0
- package/index.js +2 -0
- package/package.json +86 -0
- package/src/apis/addExternalModule.js +24 -0
- package/src/apis/addUserToGroup.js +108 -0
- package/src/apis/changeAdminStatus.js +148 -0
- package/src/apis/changeArchivedStatus.js +61 -0
- package/src/apis/changeAvatar.js +103 -0
- package/src/apis/changeBio.js +69 -0
- package/src/apis/changeBlockedStatus.js +54 -0
- package/src/apis/changeGroupImage.js +136 -0
- package/src/apis/changeThreadColor.js +116 -0
- package/src/apis/changeThreadEmoji.js +53 -0
- package/src/apis/comment.js +207 -0
- package/src/apis/createAITheme.js +129 -0
- package/src/apis/createNewGroup.js +79 -0
- package/src/apis/createPoll.js +73 -0
- package/src/apis/deleteMessage.js +44 -0
- package/src/apis/deleteThread.js +52 -0
- package/src/apis/editMessage.js +70 -0
- package/src/apis/emoji.js +124 -0
- package/src/apis/enableAutoSaveAppState.js +69 -0
- package/src/apis/fetchThemeData.js +113 -0
- package/src/apis/follow.js +81 -0
- package/src/apis/forwardAttachment.js +195 -0
- package/src/apis/forwardMessage.js +52 -0
- package/src/apis/friend.js +243 -0
- package/src/apis/gcmember.js +122 -0
- package/src/apis/gcname.js +123 -0
- package/src/apis/gcrule.js +119 -0
- package/src/apis/getAccess.js +111 -0
- package/src/apis/getBotInfo.js +88 -0
- package/src/apis/getBotInitialData.js +43 -0
- package/src/apis/getEmojiUrl.js +40 -0
- package/src/apis/getFriendsList.js +79 -0
- package/src/apis/getMessage.js +423 -0
- package/src/apis/getTheme.js +123 -0
- package/src/apis/getThemeInfo.js +116 -0
- package/src/apis/getThemePictures.js +87 -0
- package/src/apis/getThreadColors.js +119 -0
- package/src/apis/getThreadHistory.js +239 -0
- package/src/apis/getThreadInfo.js +271 -0
- package/src/apis/getThreadList.js +236 -0
- package/src/apis/getThreadPictures.js +58 -0
- package/src/apis/getUserID.js +117 -0
- package/src/apis/getUserInfo.js +513 -0
- package/src/apis/getUserInfoV2.js +146 -0
- package/src/apis/handleFriendRequest.js +66 -0
- package/src/apis/handleMessageRequest.js +50 -0
- package/src/apis/httpGet.js +63 -0
- package/src/apis/httpPost.js +89 -0
- package/src/apis/httpPostFormData.js +69 -0
- package/src/apis/listenMqtt.js +1081 -0
- package/src/apis/listenSpeed.js +178 -0
- package/src/apis/logout.js +63 -0
- package/src/apis/markAsDelivered.js +47 -0
- package/src/apis/markAsRead.js +82 -0
- package/src/apis/markAsReadAll.js +40 -0
- package/src/apis/markAsSeen.js +70 -0
- package/src/apis/mqttDeltaValue.js +252 -0
- package/src/apis/muteThread.js +45 -0
- package/src/apis/nickname.js +132 -0
- package/src/apis/notes.js +163 -0
- package/src/apis/pinMessage.js +150 -0
- package/src/apis/produceMetaTheme.js +160 -0
- package/src/apis/realtime.js +182 -0
- package/src/apis/refreshFb_dtsg.js +94 -0
- package/src/apis/removeUserFromGroup.js +117 -0
- package/src/apis/resolvePhotoUrl.js +58 -0
- package/src/apis/scheduler.js +129 -0
- package/src/apis/searchForThread.js +154 -0
- package/src/apis/sendEffect.js +311 -0
- package/src/apis/sendMessage.js +341 -0
- package/src/apis/sendMessageMqtt.js +271 -0
- package/src/apis/sendTypingIndicator.js +74 -0
- package/src/apis/setMessageReaction.js +27 -0
- package/src/apis/setMessageReactionMqtt.js +61 -0
- package/src/apis/setPostReaction.js +118 -0
- package/src/apis/setThreadTheme.js +210 -0
- package/src/apis/setThreadThemeMqtt.js +94 -0
- package/src/apis/setTitle.js +26 -0
- package/src/apis/share.js +106 -0
- package/src/apis/shareContact.js +66 -0
- package/src/apis/stickers.js +257 -0
- package/src/apis/story.js +181 -0
- package/src/apis/theme.js +233 -0
- package/src/apis/unfriend.js +47 -0
- package/src/apis/unsendMessage.js +17 -0
- package/src/apis/uploadAttachment.js +87 -0
- package/src/database/appStateBackup.js +189 -0
- package/src/database/models/index.js +56 -0
- package/src/database/models/thread.js +31 -0
- package/src/database/models/user.js +32 -0
- package/src/database/threadData.js +101 -0
- package/src/database/userData.js +90 -0
- package/src/engine/client.js +92 -0
- package/src/engine/models/buildAPI.js +118 -0
- package/src/engine/models/loginHelper.js +492 -0
- package/src/engine/models/setOptions.js +88 -0
- package/src/types/index.d.ts +498 -0
- package/src/utils/antiSuspension.js +516 -0
- package/src/utils/auth-helpers.js +149 -0
- package/src/utils/autoReLogin.js +239 -0
- package/src/utils/axios.js +368 -0
- package/src/utils/cache.js +54 -0
- package/src/utils/clients.js +279 -0
- package/src/utils/constants.js +525 -0
- package/src/utils/formatters/data/formatAttachment.js +370 -0
- package/src/utils/formatters/data/formatDelta.js +109 -0
- package/src/utils/formatters/index.js +159 -0
- package/src/utils/formatters/value/formatCookie.js +91 -0
- package/src/utils/formatters/value/formatDate.js +36 -0
- package/src/utils/formatters/value/formatID.js +16 -0
- package/src/utils/formatters.js +1369 -0
- package/src/utils/headers.js +235 -0
- package/src/utils/index.js +153 -0
- package/src/utils/monitoring.js +333 -0
- package/src/utils/rateLimiter.js +251 -0
- package/src/utils/tokenRefresh.js +285 -0
- package/src/utils/user-agents.js +238 -0
- package/src/utils/validation.js +157 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
const { globalAntiSuspension } = require('../utils/antiSuspension');
|
|
5
|
+
|
|
6
|
+
module.exports = (defaultFuncs, api, ctx) => {
|
|
7
|
+
function detectAttachmentType(attachment) {
|
|
8
|
+
const p = attachment.path || "";
|
|
9
|
+
const ext = p.toLowerCase().split(".").pop();
|
|
10
|
+
const audio = ["mp3", "wav", "aac", "m4a", "ogg", "opus", "flac"];
|
|
11
|
+
const video = ["mp4", "mov", "avi", "mkv", "webm", "wmv", "flv"];
|
|
12
|
+
const image = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"];
|
|
13
|
+
if (audio.includes(ext)) return { voice_clip: "true" };
|
|
14
|
+
if (video.includes(ext)) return { video: "true" };
|
|
15
|
+
if (image.includes(ext)) return { image: "true" };
|
|
16
|
+
return { file: "true" };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function uploadAttachments(attachments) {
|
|
20
|
+
const uploads = [];
|
|
21
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
22
|
+
if (!utils.isReadableStream(attachments[i])) {
|
|
23
|
+
throw new Error("Attachment should be a readable stream and not " + utils.getType(attachments[i]) + ".");
|
|
24
|
+
}
|
|
25
|
+
if (i > 0) {
|
|
26
|
+
await globalAntiSuspension.addSmartDelay();
|
|
27
|
+
}
|
|
28
|
+
const form = {
|
|
29
|
+
upload_1024: attachments[i],
|
|
30
|
+
...detectAttachmentType(attachments[i]),
|
|
31
|
+
};
|
|
32
|
+
const upload = await defaultFuncs
|
|
33
|
+
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, { ...ctx, requestThreadID: String(ctx._lastThreadHint || "") })
|
|
34
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
35
|
+
.then(resData => {
|
|
36
|
+
if (resData.error) throw resData;
|
|
37
|
+
return resData.payload.metadata[0];
|
|
38
|
+
});
|
|
39
|
+
uploads.push(upload);
|
|
40
|
+
}
|
|
41
|
+
return uploads;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildMentionData(msg, baseBody) {
|
|
45
|
+
if (!Array.isArray(msg.mentions) || msg.mentions.length === 0) return null;
|
|
46
|
+
const ids = [], offsets = [], lengths = [], types = [];
|
|
47
|
+
let cursor = 0;
|
|
48
|
+
for (const mention of msg.mentions) {
|
|
49
|
+
const rawTag = String(mention.tag || "");
|
|
50
|
+
const displayName = rawTag.replace(/^@+/, "");
|
|
51
|
+
const start = Number.isInteger(mention.fromIndex) ? mention.fromIndex : cursor;
|
|
52
|
+
let index = baseBody.indexOf(rawTag, start);
|
|
53
|
+
let adjustment = 0;
|
|
54
|
+
if (index === -1) {
|
|
55
|
+
index = baseBody.indexOf(displayName, start);
|
|
56
|
+
} else {
|
|
57
|
+
adjustment = rawTag.length - displayName.length;
|
|
58
|
+
}
|
|
59
|
+
if (index < 0) { index = 0; adjustment = 0; }
|
|
60
|
+
const offset = index + adjustment;
|
|
61
|
+
ids.push(String(mention.id || 0));
|
|
62
|
+
offsets.push(offset);
|
|
63
|
+
lengths.push(displayName.length);
|
|
64
|
+
types.push("p");
|
|
65
|
+
cursor = offset + displayName.length;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mention_ids: ids.join(","),
|
|
69
|
+
mention_offsets: offsets.join(","),
|
|
70
|
+
mention_lengths: lengths.join(","),
|
|
71
|
+
mention_types: types.join(","),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hasLinks(text) {
|
|
76
|
+
return /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function extractIdsFromPayload(payload) {
|
|
80
|
+
let messageID = null;
|
|
81
|
+
let threadID = null;
|
|
82
|
+
function walk(n) {
|
|
83
|
+
if (!Array.isArray(n)) return;
|
|
84
|
+
if (n[0] === 5 && (n[1] === "replaceOptimsiticMessage" || n[1] === "replaceOptimisticMessage")) {
|
|
85
|
+
messageID = String(n[3]);
|
|
86
|
+
}
|
|
87
|
+
if (n[0] === 5 && n[1] === "writeCTAIdToThreadsTable") {
|
|
88
|
+
const a = n[2];
|
|
89
|
+
if (Array.isArray(a) && a[0] === 19) threadID = String(a[1]);
|
|
90
|
+
}
|
|
91
|
+
for (const x of n) walk(x);
|
|
92
|
+
}
|
|
93
|
+
walk(payload?.step);
|
|
94
|
+
return { threadID, messageID };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function publishWithAck(content, reqID) {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
if (!ctx.mqttClient || typeof ctx.mqttClient.on !== "function" || typeof ctx.mqttClient.publish !== "function") {
|
|
100
|
+
return reject(new Error("MQTT client is not initialized"));
|
|
101
|
+
}
|
|
102
|
+
if (typeof ctx.mqttClient.setMaxListeners === "function") {
|
|
103
|
+
ctx.mqttClient.setMaxListeners(0);
|
|
104
|
+
}
|
|
105
|
+
let settled = false;
|
|
106
|
+
let timer;
|
|
107
|
+
const cleanup = () => {
|
|
108
|
+
if (settled) return;
|
|
109
|
+
settled = true;
|
|
110
|
+
if (timer) clearTimeout(timer);
|
|
111
|
+
ctx.mqttClient.removeListener("message", onMessage);
|
|
112
|
+
};
|
|
113
|
+
const onMessage = (topic, message) => {
|
|
114
|
+
if (topic !== "/ls_resp") return;
|
|
115
|
+
let parsed;
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(message.toString());
|
|
118
|
+
if (typeof parsed.payload === "string") parsed.payload = JSON.parse(parsed.payload);
|
|
119
|
+
} catch { return; }
|
|
120
|
+
if (parsed.request_id !== reqID) return;
|
|
121
|
+
const { threadID, messageID } = extractIdsFromPayload(parsed.payload);
|
|
122
|
+
cleanup();
|
|
123
|
+
resolve({ messageID, threadID });
|
|
124
|
+
};
|
|
125
|
+
ctx.mqttClient.on("message", onMessage);
|
|
126
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false }, (err) => {
|
|
127
|
+
if (err) { cleanup(); reject(err); }
|
|
128
|
+
});
|
|
129
|
+
timer = setTimeout(() => {
|
|
130
|
+
if (settled) return;
|
|
131
|
+
cleanup();
|
|
132
|
+
reject({ error: "Timeout waiting for ACK" });
|
|
133
|
+
}, 15000);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return async (msg, threadID, replyToMessage, callback) => {
|
|
138
|
+
if (typeof msg !== "string" && typeof msg !== "object") {
|
|
139
|
+
throw new Error("Message should be of type string or object, not " + utils.getType(msg) + ".");
|
|
140
|
+
}
|
|
141
|
+
if (typeof threadID !== "string" && typeof threadID !== "number") {
|
|
142
|
+
throw new Error("threadID must be a string or number.");
|
|
143
|
+
}
|
|
144
|
+
if (!callback && typeof replyToMessage === "function") {
|
|
145
|
+
callback = replyToMessage;
|
|
146
|
+
replyToMessage = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await globalAntiSuspension.prepareBeforeMessage(String(threadID), typeof msg === "string" ? msg : (msg.body || ""));
|
|
151
|
+
} catch (_) {}
|
|
152
|
+
|
|
153
|
+
const normalized = typeof msg === "string" ? { body: msg } : msg;
|
|
154
|
+
const baseBody = normalized.body != null ? String(normalized.body) : "";
|
|
155
|
+
const epoch = (BigInt(Date.now()) << 22n).toString();
|
|
156
|
+
const requestId = Math.floor(100 + Math.random() * 900);
|
|
157
|
+
const otid = utils.generateOfflineThreadingID();
|
|
158
|
+
|
|
159
|
+
const payload0 = {
|
|
160
|
+
thread_id: String(threadID),
|
|
161
|
+
otid: otid.toString(),
|
|
162
|
+
source: 2097153,
|
|
163
|
+
send_type: 1,
|
|
164
|
+
sync_group: 1,
|
|
165
|
+
mark_thread_read: 1,
|
|
166
|
+
text: baseBody === "" ? null : baseBody,
|
|
167
|
+
initiating_source: 0,
|
|
168
|
+
skip_url_preview_gen: 0,
|
|
169
|
+
text_has_links: hasLinks(baseBody) ? 1 : 0,
|
|
170
|
+
multitab_env: 0,
|
|
171
|
+
metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } }),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (normalized.mentions && Array.isArray(normalized.mentions) && normalized.mentions.length > 0) {
|
|
175
|
+
const mentionData = buildMentionData(normalized, baseBody);
|
|
176
|
+
if (mentionData) payload0.mention_data = mentionData;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (normalized.sticker) {
|
|
180
|
+
payload0.send_type = 2;
|
|
181
|
+
payload0.sticker_id = normalized.sticker;
|
|
182
|
+
payload0.text = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (normalized.emoji) {
|
|
186
|
+
payload0.send_type = 1;
|
|
187
|
+
payload0.text = normalized.emoji;
|
|
188
|
+
const sizeMap = { small: 1, medium: 2, large: 3 };
|
|
189
|
+
const emojiSize = normalized.emojiSize;
|
|
190
|
+
payload0.hot_emoji_size = (typeof emojiSize === "number" ? Math.min(3, Math.max(1, emojiSize)) : sizeMap[emojiSize]) || 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (normalized.location && normalized.location.latitude != null && normalized.location.longitude != null) {
|
|
194
|
+
payload0.send_type = 1;
|
|
195
|
+
payload0.location_data = {
|
|
196
|
+
coordinates: { latitude: normalized.location.latitude, longitude: normalized.location.longitude },
|
|
197
|
+
is_current_location: Boolean(normalized.location.current),
|
|
198
|
+
is_live_location: Boolean(normalized.location.live),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (normalized.effect) {
|
|
203
|
+
const effectTag = String(normalized.effect).toUpperCase().replace(/[\s\-]+/g, "_");
|
|
204
|
+
payload0.lightweight_action_attached = {
|
|
205
|
+
message_id: otid.toString(),
|
|
206
|
+
messaging_tag: "fb.messaging.effects." + effectTag,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (replyToMessage || normalized.replyToMessage) {
|
|
211
|
+
const replyId = replyToMessage || normalized.replyToMessage;
|
|
212
|
+
payload0.reply_metadata = {
|
|
213
|
+
reply_source_id: replyId,
|
|
214
|
+
reply_source_type: 1,
|
|
215
|
+
reply_type: 0,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (normalized.attachment) {
|
|
220
|
+
payload0.send_type = 3;
|
|
221
|
+
if (payload0.text === "") payload0.text = null;
|
|
222
|
+
const list = Array.isArray(normalized.attachment) ? normalized.attachment : [normalized.attachment];
|
|
223
|
+
ctx._lastThreadHint = threadID;
|
|
224
|
+
const files = await uploadAttachments(list.filter(a => utils.isReadableStream(a)));
|
|
225
|
+
payload0.attachment_fbids = files.map(f => String(Object.values(f)[0]));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const content = {
|
|
229
|
+
app_id: "2220391788200892",
|
|
230
|
+
payload: {
|
|
231
|
+
tasks: [
|
|
232
|
+
{
|
|
233
|
+
label: "46",
|
|
234
|
+
payload: payload0,
|
|
235
|
+
queue_name: String(threadID),
|
|
236
|
+
task_id: 400,
|
|
237
|
+
failure_count: null,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
label: "21",
|
|
241
|
+
payload: {
|
|
242
|
+
thread_id: String(threadID),
|
|
243
|
+
last_read_watermark_ts: Date.now(),
|
|
244
|
+
sync_group: 1,
|
|
245
|
+
},
|
|
246
|
+
queue_name: String(threadID),
|
|
247
|
+
task_id: 401,
|
|
248
|
+
failure_count: null,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
epoch_id: epoch,
|
|
252
|
+
version_id: "24804310205905615",
|
|
253
|
+
data_trace_id: `#${Buffer.from(String(Math.random())).toString("base64").replace(/=+$/g, "")}`,
|
|
254
|
+
},
|
|
255
|
+
request_id: requestId,
|
|
256
|
+
type: 3,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
content.payload.tasks = content.payload.tasks.map(t => ({ ...t, payload: JSON.stringify(t.payload) }));
|
|
260
|
+
content.payload = JSON.stringify(content.payload);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const result = await publishWithAck(content, requestId);
|
|
264
|
+
if (callback) callback(undefined, result);
|
|
265
|
+
return result;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (callback) callback(err);
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
6
|
+
return function sendTypingIndicator(sendTyping, threadID, callback) {
|
|
7
|
+
let resolveFunc, rejectFunc;
|
|
8
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
9
|
+
resolveFunc = resolve;
|
|
10
|
+
rejectFunc = reject;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!callback) {
|
|
14
|
+
callback = (err) => {
|
|
15
|
+
if (err) return rejectFunc(err);
|
|
16
|
+
resolveFunc(true);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!ctx.mqttClient || typeof ctx.mqttClient.publish !== "function") {
|
|
21
|
+
const err = new Error("You can only use sendTypingIndicator after you start listening.");
|
|
22
|
+
callback(err);
|
|
23
|
+
return returnPromise;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const threadIDs = Array.isArray(threadID) ? threadID : [threadID];
|
|
27
|
+
if (!threadIDs.length) {
|
|
28
|
+
const err = new Error("threadID is required");
|
|
29
|
+
callback(err);
|
|
30
|
+
return returnPromise;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
|
|
34
|
+
|
|
35
|
+
function buildPayload(tid) {
|
|
36
|
+
const isGroup = String(tid).length >= 16 ? 1 : 0;
|
|
37
|
+
return {
|
|
38
|
+
app_id: "772021112871879",
|
|
39
|
+
payload: JSON.stringify({
|
|
40
|
+
label: "3",
|
|
41
|
+
payload: JSON.stringify({
|
|
42
|
+
thread_key: Number.parseInt(String(tid), 10),
|
|
43
|
+
is_group_thread: isGroup,
|
|
44
|
+
is_typing: sendTyping ? 1 : 0,
|
|
45
|
+
attribution: 0,
|
|
46
|
+
sync_group: 1,
|
|
47
|
+
thread_type: isGroup ? 2 : 1,
|
|
48
|
+
}),
|
|
49
|
+
version: "8965252033599983",
|
|
50
|
+
}),
|
|
51
|
+
request_id: ++ctx.wsReqNumber,
|
|
52
|
+
type: 4,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const publishes = threadIDs.map(tid =>
|
|
57
|
+
new Promise((resolve, reject) => {
|
|
58
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(buildPayload(tid)), { qos: 1 }, (err) => {
|
|
59
|
+
if (err) return reject(err);
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
Promise.all(publishes)
|
|
66
|
+
.then(() => callback(null, true))
|
|
67
|
+
.catch(err => {
|
|
68
|
+
utils.error("sendTypingIndicator", err);
|
|
69
|
+
callback(err);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return returnPromise;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
7
|
+
return async (reaction, messageID) => {
|
|
8
|
+
if (!reaction) throw new Error("Please enter a valid emoji.");
|
|
9
|
+
const defData = await defaultFuncs.postFormData("https://www.facebook.com/webgraphql/mutation/", ctx.jar, {}, {
|
|
10
|
+
doc_id: "1491398900900362",
|
|
11
|
+
variables: JSON.stringify({
|
|
12
|
+
data: {
|
|
13
|
+
client_mutation_id: ctx.clientMutationId++,
|
|
14
|
+
actor_id: ctx.userID,
|
|
15
|
+
action: reaction == "" ? "REMOVE_REACTION" : "ADD_REACTION",
|
|
16
|
+
message_id: messageID,
|
|
17
|
+
reaction
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
dpr: 1
|
|
21
|
+
});
|
|
22
|
+
const resData = await utils.parseAndCheckLogin(ctx, defaultFuncs)(defData);
|
|
23
|
+
if (!resData) {
|
|
24
|
+
throw new Error("setMessageReactionLegacy returned empty object.");
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const utils = require('../utils');
|
|
5
|
+
|
|
6
|
+
function isCallable(func) {
|
|
7
|
+
try {
|
|
8
|
+
Reflect.apply(func, null, []);
|
|
9
|
+
return true;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
16
|
+
return function setMessageReactionMqtt(reaction, messageID, threadID) {
|
|
17
|
+
if (!ctx.mqttClient) {
|
|
18
|
+
throw new Error('Not connected to MQTT');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ctx.wsReqNumber += 1;
|
|
22
|
+
ctx.wsTaskNumber += 1;
|
|
23
|
+
|
|
24
|
+
const taskPayload = {
|
|
25
|
+
thread_key: threadID,
|
|
26
|
+
timestamp_ms: Date.now(),
|
|
27
|
+
message_id: messageID,
|
|
28
|
+
reaction,
|
|
29
|
+
actor_id: ctx.userID,
|
|
30
|
+
reaction_style: null,
|
|
31
|
+
sync_group: 1,
|
|
32
|
+
send_attribution: Math.random() < 0.5 ? 65537 : 524289
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const task = {
|
|
36
|
+
failure_count: null,
|
|
37
|
+
label: '29',
|
|
38
|
+
payload: JSON.stringify(taskPayload),
|
|
39
|
+
queue_name: JSON.stringify(['reaction', messageID]),
|
|
40
|
+
task_id: ctx.wsTaskNumber
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const content = {
|
|
44
|
+
app_id: '2220391788200892',
|
|
45
|
+
payload: JSON.stringify({
|
|
46
|
+
data_trace_id: null,
|
|
47
|
+
epoch_id: parseInt(utils.generateOfflineThreadingID()),
|
|
48
|
+
tasks: [task],
|
|
49
|
+
version_id: '7158486590867448',
|
|
50
|
+
}),
|
|
51
|
+
request_id: ctx.wsReqNumber,
|
|
52
|
+
type: 3,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/*if (isCallable(callback)) {
|
|
56
|
+
ctx.reqCallbacks[ctx.wsReqNumber] = callback;
|
|
57
|
+
}*/
|
|
58
|
+
|
|
59
|
+
ctx.mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1, retain: false });
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require("../utils");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds or removes a reaction on a Facebook post (not a Messenger message).
|
|
7
|
+
*
|
|
8
|
+
* For reacting to Messenger messages use api.setMessageReaction() instead.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} postID The Facebook post/object ID.
|
|
11
|
+
* @param {string|number} type Reaction type:
|
|
12
|
+
* - Named: "like" | "heart" | "love" | "haha" | "wow" | "sad" | "angry" | "unlike"
|
|
13
|
+
* - Numeric: 0=unlike, 1=like, 2=heart, 4=haha, 3=wow, 7=sad, 8=angry, 16=love
|
|
14
|
+
* @param {Function} [callback]
|
|
15
|
+
* @returns {Promise}
|
|
16
|
+
*/
|
|
17
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
18
|
+
const REACTION_MAP = {
|
|
19
|
+
unlike: 0,
|
|
20
|
+
like: 1,
|
|
21
|
+
heart: 2,
|
|
22
|
+
love: 16,
|
|
23
|
+
haha: 4,
|
|
24
|
+
wow: 3,
|
|
25
|
+
sad: 7,
|
|
26
|
+
angry: 8,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return function setPostReaction(postID, type, callback) {
|
|
30
|
+
let reactionType = type;
|
|
31
|
+
let cb = callback;
|
|
32
|
+
|
|
33
|
+
// Allow omitting type to default to "unlike"
|
|
34
|
+
if (!cb && (utils.getType(type) === "Function" || utils.getType(type) === "AsyncFunction")) {
|
|
35
|
+
cb = type;
|
|
36
|
+
reactionType = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let resolveFunc, rejectFunc;
|
|
40
|
+
const promise = new Promise((resolve, reject) => {
|
|
41
|
+
resolveFunc = resolve;
|
|
42
|
+
rejectFunc = reject;
|
|
43
|
+
});
|
|
44
|
+
if (!cb) {
|
|
45
|
+
cb = (err, data) => { if (err) return rejectFunc(err); resolveFunc(data); };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!postID) return cb(new Error("postID is required"));
|
|
49
|
+
|
|
50
|
+
// Resolve named type
|
|
51
|
+
if (utils.getType(reactionType) === "String") {
|
|
52
|
+
const key = String(reactionType).toLowerCase();
|
|
53
|
+
if (key in REACTION_MAP) {
|
|
54
|
+
reactionType = REACTION_MAP[key];
|
|
55
|
+
} else {
|
|
56
|
+
return cb(new Error(`Unknown reaction type "${type}". Valid types: ${Object.keys(REACTION_MAP).join(", ")}`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof reactionType !== "number") {
|
|
61
|
+
return cb(new Error("setPostReaction: reaction type must be a string or number"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const feedbackID = Buffer.from(`feedback:${postID}`).toString("base64");
|
|
65
|
+
|
|
66
|
+
const form = {
|
|
67
|
+
av: ctx.userID,
|
|
68
|
+
__user: ctx.userID,
|
|
69
|
+
__a: 1,
|
|
70
|
+
__req: utils.getSignatureID(),
|
|
71
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
72
|
+
lsd: ctx.lsd || ctx.fb_dtsg,
|
|
73
|
+
jazoest: ctx.jazoest,
|
|
74
|
+
fb_api_caller_class: "RelayModern",
|
|
75
|
+
fb_api_req_friendly_name: "CometUFIFeedbackReactMutation",
|
|
76
|
+
doc_id: "4769042373179384",
|
|
77
|
+
variables: JSON.stringify({
|
|
78
|
+
input: {
|
|
79
|
+
actor_id: ctx.userID,
|
|
80
|
+
feedback_id: feedbackID,
|
|
81
|
+
feedback_reaction: reactionType,
|
|
82
|
+
feedback_source: "OBJECT",
|
|
83
|
+
is_tracking_encrypted: true,
|
|
84
|
+
tracking: [],
|
|
85
|
+
session_id: utils.getSignatureID() + "-" + Date.now(),
|
|
86
|
+
client_mutation_id: Math.floor(Math.random() * 20).toString(),
|
|
87
|
+
},
|
|
88
|
+
useDefaultActor: false,
|
|
89
|
+
scale: 3,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
defaultFuncs
|
|
94
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
95
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
96
|
+
.then((resData) => {
|
|
97
|
+
if (resData && resData.errors) throw resData;
|
|
98
|
+
|
|
99
|
+
const fbReact = resData && resData.data && resData.data.feedback_react;
|
|
100
|
+
const result = {
|
|
101
|
+
postID,
|
|
102
|
+
reactionType,
|
|
103
|
+
success: true,
|
|
104
|
+
reaction_count: fbReact && fbReact.feedback
|
|
105
|
+
? fbReact.feedback.reaction_count
|
|
106
|
+
: null,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
cb(null, result);
|
|
110
|
+
})
|
|
111
|
+
.catch((err) => {
|
|
112
|
+
utils.error("setPostReaction", err.message || err);
|
|
113
|
+
cb(err instanceof Error ? err : new Error(String(err.message || err)));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return promise;
|
|
117
|
+
};
|
|
118
|
+
};
|