fca-luxury 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. package/DOCS.md +1686 -0
  2. package/README.md +222 -0
  3. package/index.js +544 -0
  4. package/package.json +73 -0
  5. package/src/Screenshot.js +83 -0
  6. package/src/addExternalModule.js +15 -0
  7. package/src/addUserToGroup.js +77 -0
  8. package/src/changeAdminStatus.js +47 -0
  9. package/src/changeArchivedStatus.js +41 -0
  10. package/src/changeAvt.js +85 -0
  11. package/src/changeBio.js +65 -0
  12. package/src/changeBlockedStatus.js +36 -0
  13. package/src/changeGroupImage.js +106 -0
  14. package/src/changeNickname.js +45 -0
  15. package/src/changeThreadColor.js +61 -0
  16. package/src/changeThreadEmoji.js +41 -0
  17. package/src/createNewGroup.js +70 -0
  18. package/src/createPoll.js +59 -0
  19. package/src/deleteMessage.js +44 -0
  20. package/src/deleteThread.js +42 -0
  21. package/src/editMessage.js +55 -0
  22. package/src/forwardAttachment.js +47 -0
  23. package/src/getCurrentUserID.js +7 -0
  24. package/src/getEmojiUrl.js +27 -0
  25. package/src/getFriendsList.js +73 -0
  26. package/src/getThreadHistory.js +537 -0
  27. package/src/getThreadHistoryDeprecated.js +71 -0
  28. package/src/getThreadInfo.js +171 -0
  29. package/src/getThreadInfoDeprecated.js +56 -0
  30. package/src/getThreadList.js +213 -0
  31. package/src/getThreadListDeprecated.js +46 -0
  32. package/src/getThreadPictures.js +59 -0
  33. package/src/getUserID.js +61 -0
  34. package/src/getUserInfo.js +66 -0
  35. package/src/handleFriendRequest.js +46 -0
  36. package/src/handleMessageRequest.js +47 -0
  37. package/src/httpGet.js +49 -0
  38. package/src/httpPost.js +48 -0
  39. package/src/listenMqtt.js +701 -0
  40. package/src/logout.js +68 -0
  41. package/src/markAsDelivered.js +47 -0
  42. package/src/markAsRead.js +70 -0
  43. package/src/markAsReadAll.js +40 -0
  44. package/src/markAsSeen.js +48 -0
  45. package/src/muteThread.js +45 -0
  46. package/src/removeUserFromGroup.js +45 -0
  47. package/src/resolvePhotoUrl.js +36 -0
  48. package/src/searchForThread.js +42 -0
  49. package/src/sendMessage.js +461 -0
  50. package/src/sendTypingIndicator.js +70 -0
  51. package/src/setMessageReaction.js +109 -0
  52. package/src/setPostReaction.js +102 -0
  53. package/src/setTitle.js +70 -0
  54. package/src/shareContact.js +46 -0
  55. package/src/shareLink.js +55 -0
  56. package/src/threadColors.js +41 -0
  57. package/src/unfriend.js +42 -0
  58. package/src/unsendMessage.js +39 -0
  59. package/test/data/shareAttach.js +146 -0
  60. package/test/data/something.mov +0 -0
  61. package/test/data/test.png +0 -0
  62. package/test/data/test.txt +7 -0
  63. package/test/example-config.json +18 -0
  64. package/test/test-page.js +140 -0
  65. package/test/test.js +385 -0
  66. package/utils.js +1196 -0
@@ -0,0 +1,461 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+ var bluebird = require("bluebird");
6
+
7
+ var allowedProperties = {
8
+ attachment: true,
9
+ url: true,
10
+ sticker: true,
11
+ emoji: true,
12
+ emojiSize: true,
13
+ body: true,
14
+ mentions: true,
15
+ location: true,
16
+ };
17
+
18
+ module.exports = function (defaultFuncs, api, ctx) {
19
+ function uploadAttachment(attachments, callback) {
20
+ var uploads = [];
21
+
22
+ // create an array of promises
23
+ for (var i = 0; i < attachments.length; i++) {
24
+ if (!utils.isReadableStream(attachments[i])) {
25
+ throw {
26
+ error:
27
+ "Attachment should be a readable stream and not " +
28
+ utils.getType(attachments[i]) +
29
+ ".",
30
+ };
31
+ }
32
+
33
+ var form = {
34
+ upload_1024: attachments[i],
35
+ voice_clip: "true",
36
+ };
37
+
38
+ uploads.push(
39
+ defaultFuncs
40
+ .postFormData(
41
+ "https://upload.facebook.com/ajax/mercury/upload.php",
42
+ ctx.jar,
43
+ form,
44
+ {}
45
+ )
46
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
47
+ .then(function (resData) {
48
+ if (resData.error) {
49
+ throw resData;
50
+ }
51
+
52
+ // We have to return the data unformatted unless we want to change it
53
+ // back in sendMessage.
54
+ return resData.payload.metadata[0];
55
+ })
56
+ );
57
+ }
58
+
59
+ // resolve all promises
60
+ bluebird
61
+ .all(uploads)
62
+ .then(function (resData) {
63
+ callback(null, resData);
64
+ })
65
+ .catch(function (err) {
66
+ log.error("uploadAttachment", err);
67
+ return callback(err);
68
+ });
69
+ }
70
+
71
+ function getUrl(url, callback) {
72
+ var form = {
73
+ image_height: 960,
74
+ image_width: 960,
75
+ uri: url,
76
+ };
77
+
78
+ defaultFuncs
79
+ .post(
80
+ "https://www.facebook.com/message_share_attachment/fromURI/",
81
+ ctx.jar,
82
+ form
83
+ )
84
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
85
+ .then(function (resData) {
86
+ if (resData.error) {
87
+ return callback(resData);
88
+ }
89
+
90
+ if (!resData.payload) {
91
+ return callback({ error: "Invalid url" });
92
+ }
93
+
94
+ callback(null, resData.payload.share_data.share_params);
95
+ })
96
+ .catch(function (err) {
97
+ log.error("getUrl", err);
98
+ return callback(err);
99
+ });
100
+ }
101
+
102
+ function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
103
+ // There are three cases here:
104
+ // 1. threadID is of type array, where we're starting a new group chat with users
105
+ // specified in the array.
106
+ // 2. User is sending a message to a specific user.
107
+ // 3. No additional form params and the message goes to an existing group chat.
108
+ if (utils.getType(threadID) === "Array") {
109
+ for (var i = 0; i < threadID.length; i++) {
110
+ form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
111
+ }
112
+ form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
113
+ form["client_thread_id"] = "root:" + messageAndOTID;
114
+ log.info("sendMessage", "Sending message to multiple users: " + threadID);
115
+ } else {
116
+ // This means that threadID is the id of a user, and the chat
117
+ // is a single person chat
118
+ if (isSingleUser) {
119
+ form["specific_to_list[0]"] = "fbid:" + threadID;
120
+ form["specific_to_list[1]"] = "fbid:" + ctx.userID;
121
+ form["other_user_fbid"] = threadID;
122
+ } else {
123
+ form["thread_fbid"] = threadID;
124
+ }
125
+ }
126
+
127
+ if (ctx.globalOptions.pageID) {
128
+ form["author"] = "fbid:" + ctx.globalOptions.pageID;
129
+ form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
130
+ form["creator_info[creatorID]"] = ctx.userID;
131
+ form["creator_info[creatorType]"] = "direct_admin";
132
+ form["creator_info[labelType]"] = "sent_message";
133
+ form["creator_info[pageID]"] = ctx.globalOptions.pageID;
134
+ form["request_user_id"] = ctx.globalOptions.pageID;
135
+ form["creator_info[profileURI]"] =
136
+ "https://www.facebook.com/profile.php?id=" + ctx.userID;
137
+ }
138
+
139
+ defaultFuncs
140
+ .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
141
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
142
+ .then(function (resData) {
143
+ if (!resData) {
144
+ return callback({ error: "Send message failed." });
145
+ }
146
+
147
+ if (resData.error) {
148
+ if (resData.error === 1545012) {
149
+ log.warn(
150
+ "sendMessage",
151
+ "Got error 1545012. This might mean that you're not part of the conversation " +
152
+ threadID
153
+ );
154
+ }
155
+ return callback(resData);
156
+ }
157
+
158
+ var messageInfo = resData.payload.actions.reduce(function (p, v) {
159
+ return (
160
+ {
161
+ threadID: v.thread_fbid,
162
+ messageID: v.message_id,
163
+ timestamp: v.timestamp,
164
+ } || p
165
+ );
166
+ }, null);
167
+
168
+ return callback(null, messageInfo);
169
+ })
170
+ .catch(function (err) {
171
+ log.error("sendMessage", err);
172
+ if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
173
+ ctx.loggedIn = false;
174
+ }
175
+ return callback(err);
176
+ });
177
+ }
178
+
179
+ function send(form, threadID, messageAndOTID, callback, isGroup) {
180
+ // We're doing a query to this to check if the given id is the id of
181
+ // a user or of a group chat. The form will be different depending
182
+ // on that.
183
+ if (utils.getType(threadID) === "Array") {
184
+ sendContent(form, threadID, false, messageAndOTID, callback);
185
+ } else {
186
+ if (utils.getType(isGroup) != "Boolean")
187
+ sendContent(
188
+ form,
189
+ threadID,
190
+ threadID.length <= 15,
191
+ messageAndOTID,
192
+ callback
193
+ );
194
+ else sendContent(form, threadID, !isGroup, messageAndOTID, callback);
195
+ }
196
+ }
197
+
198
+ function handleUrl(msg, form, callback, cb) {
199
+ if (msg.url) {
200
+ form["shareable_attachment[share_type]"] = "100";
201
+ getUrl(msg.url, function (err, params) {
202
+ if (err) {
203
+ return callback(err);
204
+ }
205
+
206
+ form["shareable_attachment[share_params]"] = params;
207
+ cb();
208
+ });
209
+ } else {
210
+ cb();
211
+ }
212
+ }
213
+
214
+ function handleLocation(msg, form, callback, cb) {
215
+ if (msg.location) {
216
+ if (msg.location.latitude == null || msg.location.longitude == null) {
217
+ return callback({
218
+ error: "location property needs both latitude and longitude",
219
+ });
220
+ }
221
+
222
+ form["location_attachment[coordinates][latitude]"] =
223
+ msg.location.latitude;
224
+ form["location_attachment[coordinates][longitude]"] =
225
+ msg.location.longitude;
226
+ form["location_attachment[is_current_location]"] = !!msg.location.current;
227
+ }
228
+
229
+ cb();
230
+ }
231
+
232
+ function handleSticker(msg, form, callback, cb) {
233
+ if (msg.sticker) {
234
+ form["sticker_id"] = msg.sticker;
235
+ }
236
+ cb();
237
+ }
238
+
239
+ function handleEmoji(msg, form, callback, cb) {
240
+ if (msg.emojiSize != null && msg.emoji == null) {
241
+ return callback({ error: "emoji property is empty" });
242
+ }
243
+ if (msg.emoji) {
244
+ if (msg.emojiSize == null) {
245
+ msg.emojiSize = "medium";
246
+ }
247
+ if (
248
+ msg.emojiSize != "small" &&
249
+ msg.emojiSize != "medium" &&
250
+ msg.emojiSize != "large"
251
+ ) {
252
+ return callback({ error: "emojiSize property is invalid" });
253
+ }
254
+ if (form["body"] != null && form["body"] != "") {
255
+ return callback({ error: "body is not empty" });
256
+ }
257
+ form["body"] = msg.emoji;
258
+ form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
259
+ }
260
+ cb();
261
+ }
262
+
263
+ function handleAttachment(msg, form, callback, cb) {
264
+ if (msg.attachment) {
265
+ form["image_ids"] = [];
266
+ form["gif_ids"] = [];
267
+ form["file_ids"] = [];
268
+ form["video_ids"] = [];
269
+ form["audio_ids"] = [];
270
+
271
+ if (utils.getType(msg.attachment) !== "Array") {
272
+ msg.attachment = [msg.attachment];
273
+ }
274
+
275
+ if (utils.getType(msg.attachment) !== "Array")
276
+ msg.attachment = [msg.attachment];
277
+ if (msg.attachment.every((e) => /_id$/.test(e[0]))) {
278
+ //console.log(msg.attachment)
279
+ msg.attachment.map((e) => form[`${e[0]}s`].push(e[1]));
280
+ return cb();
281
+ }
282
+ uploadAttachment(msg.attachment, function (err, files) {
283
+ if (err) return callback(err);
284
+ files.forEach(function (file) {
285
+ var key = Object.keys(file);
286
+ var type = key[0]; // image_id, file_id, etc
287
+ form["" + type + "s"].push(file[type]); // push the id
288
+ });
289
+ cb();
290
+ });
291
+ } else {
292
+ cb();
293
+ }
294
+ }
295
+
296
+ function handleMention(msg, form, callback, cb) {
297
+ if (msg.mentions) {
298
+ for (let i = 0; i < msg.mentions.length; i++) {
299
+ const mention = msg.mentions[i];
300
+
301
+ const tag = mention.tag;
302
+ if (typeof tag !== "string") {
303
+ return callback({ error: "Mention tags must be strings." });
304
+ }
305
+
306
+ const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
307
+
308
+ if (offset < 0) {
309
+ log.warn(
310
+ "handleMention",
311
+ 'Mention for "' + tag + '" not found in message string.'
312
+ );
313
+ }
314
+
315
+ if (mention.id == null) {
316
+ log.warn("handleMention", "Mention id should be non-null.");
317
+ }
318
+
319
+ const id = mention.id || 0;
320
+ const emptyChar = "\u200E";
321
+ form["body"] = emptyChar + msg.body;
322
+ form["profile_xmd[" + i + "][offset]"] = offset + 1;
323
+ form["profile_xmd[" + i + "][length]"] = tag.length;
324
+ form["profile_xmd[" + i + "][id]"] = id;
325
+ form["profile_xmd[" + i + "][type]"] = "p";
326
+ }
327
+ }
328
+ cb();
329
+ }
330
+
331
+ return function sendMessage(
332
+ msg,
333
+ threadID,
334
+ callback,
335
+ replyToMessage,
336
+ isGroup
337
+ ) {
338
+ typeof isGroup == "undefined" ? (isGroup = null) : "";
339
+ if (
340
+ !callback &&
341
+ (utils.getType(threadID) === "Function" ||
342
+ utils.getType(threadID) === "AsyncFunction")
343
+ ) {
344
+ return threadID({ error: "Pass a threadID as a second argument." });
345
+ }
346
+ if (!replyToMessage && utils.getType(callback) === "String") {
347
+ replyToMessage = callback;
348
+ callback = undefined;
349
+ }
350
+
351
+ var resolveFunc = function () {};
352
+ var rejectFunc = function () {};
353
+ var returnPromise = new Promise(function (resolve, reject) {
354
+ resolveFunc = resolve;
355
+ rejectFunc = reject;
356
+ });
357
+
358
+ if (!callback) {
359
+ callback = function (err, data) {
360
+ if (err) return rejectFunc(err);
361
+ resolveFunc(data);
362
+ };
363
+ }
364
+
365
+ var msgType = utils.getType(msg);
366
+ var threadIDType = utils.getType(threadID);
367
+ var messageIDType = utils.getType(replyToMessage);
368
+
369
+ if (msgType !== "String" && msgType !== "Object") {
370
+ return callback({
371
+ error:
372
+ "Message should be of type string or object and not " + msgType + ".",
373
+ });
374
+ }
375
+
376
+ // Changing this to accomodate an array of users
377
+ if (
378
+ threadIDType !== "Array" &&
379
+ threadIDType !== "Number" &&
380
+ threadIDType !== "String"
381
+ ) {
382
+ return callback({
383
+ error:
384
+ "ThreadID should be of type number, string, or array and not " +
385
+ threadIDType +
386
+ ".",
387
+ });
388
+ }
389
+
390
+ if (replyToMessage && messageIDType !== "String") {
391
+ return callback({
392
+ error:
393
+ "MessageID should be of type string and not " + threadIDType + ".",
394
+ });
395
+ }
396
+
397
+ if (msgType === "String") {
398
+ msg = { body: msg };
399
+ }
400
+
401
+ var disallowedProperties = Object.keys(msg).filter(
402
+ (prop) => !allowedProperties[prop]
403
+ );
404
+ if (disallowedProperties.length > 0) {
405
+ return callback({
406
+ error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`",
407
+ });
408
+ }
409
+
410
+ var messageAndOTID = utils.generateOfflineThreadingID();
411
+
412
+ var form = {
413
+ client: "mercury",
414
+ action_type: "ma-type:user-generated-message",
415
+ author: "fbid:" + ctx.userID,
416
+ timestamp: Date.now(),
417
+ timestamp_absolute: "Today",
418
+ timestamp_relative: utils.generateTimestampRelative(),
419
+ timestamp_time_passed: "0",
420
+ is_unread: false,
421
+ is_cleared: false,
422
+ is_forward: false,
423
+ is_filtered_content: false,
424
+ is_filtered_content_bh: false,
425
+ is_filtered_content_account: false,
426
+ is_filtered_content_quasar: false,
427
+ is_filtered_content_invalid_app: false,
428
+ is_spoof_warning: false,
429
+ source: "source:chat:web",
430
+ "source_tags[0]": "source:chat",
431
+ body: msg.body ? msg.body.toString() : "",
432
+ html_body: false,
433
+ ui_push_phase: "V3",
434
+ status: "0",
435
+ offline_threading_id: messageAndOTID,
436
+ message_id: messageAndOTID,
437
+ threading_id: utils.generateThreadingID(ctx.clientID),
438
+ "ephemeral_ttl_mode:": "0",
439
+ manual_retry_cnt: "0",
440
+ has_attachment: !!(msg.attachment || msg.url || msg.sticker),
441
+ signatureID: utils.getSignatureID(),
442
+ replied_to_message_id: replyToMessage,
443
+ };
444
+
445
+ handleLocation(msg, form, callback, () =>
446
+ handleSticker(msg, form, callback, () =>
447
+ handleAttachment(msg, form, callback, () =>
448
+ handleUrl(msg, form, callback, () =>
449
+ handleEmoji(msg, form, callback, () =>
450
+ handleMention(msg, form, callback, () =>
451
+ send(form, threadID, messageAndOTID, callback, isGroup)
452
+ )
453
+ )
454
+ )
455
+ )
456
+ )
457
+ );
458
+
459
+ return returnPromise;
460
+ };
461
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ function makeTypingIndicator(typ, threadID, callback, isGroup) {
8
+ var form = {
9
+ typ: +typ,
10
+ to: "",
11
+ source: "mercury-chat",
12
+ thread: threadID
13
+ };
14
+
15
+ // Check if thread is a single person chat or a group chat
16
+ // More info on this is in api.sendMessage
17
+ if (utils.getType(isGroup) == "Boolean") {
18
+ if (!isGroup) form.to = threadID;
19
+
20
+ defaultFuncs
21
+ .post("https://www.facebook.com/ajax/messaging/typ.php", ctx.jar, form)
22
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
23
+ .then(function (resData) {
24
+ if (resData.error) throw resData;
25
+ return callback();
26
+ })
27
+ .catch(function (err) {
28
+ log.error("sendTypingIndicator", err);
29
+ if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
30
+ return callback(err);
31
+ });
32
+ }
33
+ else {
34
+ api.getUserInfo(threadID, function (err, res) {
35
+ if (err) return callback(err);
36
+ // If id is single person chat
37
+ if (Object.keys(res).length > 0) form.to = threadID;
38
+ defaultFuncs
39
+ .post("https://www.facebook.com/ajax/messaging/typ.php", ctx.jar, form)
40
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
41
+ .then(function (resData) {
42
+ if (resData.error) throw resData;
43
+ return callback();
44
+ })
45
+ .catch(function (err) {
46
+ log.error("sendTypingIndicator", err);
47
+ if (utils.getType(err) == "Object" && err.error === "Not logged in.") ctx.loggedIn = false;
48
+ return callback(err);
49
+ });
50
+ });
51
+ }
52
+ }
53
+
54
+ return function sendTypingIndicator(threadID, callback, isGroup) {
55
+ if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
56
+ if (callback) log.warn("sendTypingIndicator", "callback is not a function - ignoring.");
57
+ callback = () => { };
58
+ }
59
+
60
+ makeTypingIndicator(true, threadID, callback, isGroup);
61
+
62
+ return function end(cb) {
63
+ if (utils.getType(cb) !== "Function" && utils.getType(cb) !== "AsyncFunction") {
64
+ if (cb) log.warn("sendTypingIndicator", "callback is not a function - ignoring.");
65
+ cb = () => { };
66
+ }
67
+ makeTypingIndicator(false, threadID, cb, isGroup);
68
+ };
69
+ };
70
+ };
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function setMessageReaction(reaction, messageID, callback, forceCustomReaction) {
8
+ var resolveFunc = function () { };
9
+ var rejectFunc = function () { };
10
+ var returnPromise = new Promise(function (resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = function (err, data) {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc(data);
19
+ };
20
+ }
21
+
22
+ switch (reaction) {
23
+ case "\uD83D\uDE0D": //:heart_eyes:
24
+ case "\uD83D\uDE06": //:laughing:
25
+ case "\uD83D\uDE2E": //:open_mouth:
26
+ case "\uD83D\uDE22": //:cry:
27
+ case "\uD83D\uDE20": //:angry:
28
+ case "\uD83D\uDC4D": //:thumbsup:
29
+ case "\uD83D\uDC4E": //:thumbsdown:
30
+ case "\u2764": //:heart:
31
+ case "\uD83D\uDC97": //:glowingheart:
32
+ case "":
33
+ //valid
34
+ break;
35
+ case ":heart_eyes:":
36
+ case ":love:":
37
+ reaction = "\uD83D\uDE0D";
38
+ break;
39
+ case ":laughing:":
40
+ case ":haha:":
41
+ reaction = "\uD83D\uDE06";
42
+ break;
43
+ case ":open_mouth:":
44
+ case ":wow:":
45
+ reaction = "\uD83D\uDE2E";
46
+ break;
47
+ case ":cry:":
48
+ case ":sad:":
49
+ reaction = "\uD83D\uDE22";
50
+ break;
51
+ case ":angry:":
52
+ reaction = "\uD83D\uDE20";
53
+ break;
54
+ case ":thumbsup:":
55
+ case ":like:":
56
+ reaction = "\uD83D\uDC4D";
57
+ break;
58
+ case ":thumbsdown:":
59
+ case ":dislike:":
60
+ reaction = "\uD83D\uDC4E";
61
+ break;
62
+ case ":heart:":
63
+ reaction = "\u2764";
64
+ break;
65
+ case ":glowingheart:":
66
+ reaction = "\uD83D\uDC97";
67
+ break;
68
+ default:
69
+ if (forceCustomReaction) break;
70
+ return callback({ error: "Reaction is not a valid emoji." });
71
+ }
72
+
73
+ var variables = {
74
+ data: {
75
+ client_mutation_id: ctx.clientMutationId++,
76
+ actor_id: ctx.userID,
77
+ action: reaction == "" ? "REMOVE_REACTION" : "ADD_REACTION",
78
+ message_id: messageID,
79
+ reaction: reaction
80
+ }
81
+ };
82
+
83
+ var qs = {
84
+ doc_id: "1491398900900362",
85
+ variables: JSON.stringify(variables),
86
+ dpr: 1
87
+ };
88
+
89
+ defaultFuncs
90
+ .postFormData(
91
+ "https://www.facebook.com/webgraphql/mutation/",
92
+ ctx.jar,
93
+ {},
94
+ qs
95
+ )
96
+ .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
97
+ .then(function (resData) {
98
+ if (!resData) throw { error: "setReaction returned empty object." };
99
+ if (resData.error) throw resData;
100
+ callback(null);
101
+ })
102
+ .catch(function (err) {
103
+ log.error("setReaction", err);
104
+ return callback(err);
105
+ });
106
+
107
+ return returnPromise;
108
+ };
109
+ };