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
package/DOCS.md ADDED
@@ -0,0 +1,1686 @@
1
+ # Documentation
2
+
3
+ * [`login`](#login)
4
+ * [`api.addUserToGroup`](#addUserToGroup)
5
+ * [`api.changeAdminStatus`](#changeAdminStatus)
6
+ * [`api.changeArchivedStatus`](#changeArchivedStatus)
7
+ * [`api.changeBlockedStatus`](#changeBlockedStatus)
8
+ * [`api.changeGroupImage`](#changeGroupImage)
9
+ * [`api.changeNickname`](#changeNickname)
10
+ * [`api.changeThreadColor`](#changeThreadColor)
11
+ * [`api.changeThreadEmoji`](#changeThreadEmoji)
12
+ * [`api.createPoll`](#createPoll)
13
+ * [`api.deleteMessage`](#deleteMessage)
14
+ * [`api.deleteThread`](#deleteThread)
15
+ * [`api.forwardAttachment`](#forwardAttachment)
16
+ * [`api.getAppState`](#getAppState)
17
+ * [`api.getCurrentUserID`](#getCurrentUserID)
18
+ * [`api.getEmojiUrl`](#getEmojiUrl)
19
+ * [`api.getFriendsList`](#getFriendsList)
20
+ * [`api.getThreadHistory`](#getThreadHistory)
21
+ * [`api.getThreadInfo`](#getThreadInfo)
22
+ * [`api.getThreadList`](#getThreadList)
23
+ * [`api.getThreadPictures`](#getThreadPictures)
24
+ * [`api.getUserID`](#getUserID)
25
+ * [`api.getUserInfo`](#getUserInfo)
26
+ * [`api.handleMessageRequest`](#handleMessageRequest)
27
+ * [`api.listen`](#listen)
28
+ * [`api.listenMqtt`](#listenMqtt)
29
+ * [`api.logout`](#logout)
30
+ * [`api.markAsDelivered`](#markAsDelivered)
31
+ * [`api.markAsRead`](#markAsRead)
32
+ * [`api.markAsReadAll`](#markAsReadAll)
33
+ * [`api.muteThread`](#muteThread)
34
+ * [`api.removeUserFromGroup`](#removeUserFromGroup)
35
+ * [`api.resolvePhotoUrl`](#resolvePhotoUrl)
36
+ * [`api.searchForThread`](#searchForThread)
37
+ * [`api.sendMessage`](#sendMessage)
38
+ * [`api.sendTypingIndicator`](#sendTypingIndicator)
39
+ * [`api.setMessageReaction`](#setMessageReaction)
40
+ * [`api.setOptions`](#setOptions)
41
+ * [`api.setTitle`](#setTitle)
42
+ * [`api.threadColors`](#threadColors)
43
+ * [`api.unsendMessage`](#unsendMessage)
44
+
45
+ ---------------------------------------
46
+
47
+ ### Password safety
48
+
49
+ **Read this** before you _copy+paste_ examples from below.
50
+
51
+ You should not store Facebook password in your scripts.
52
+ There are few good reasons:
53
+ * People who are standing behind you may look at your "code" and get your password if it is on the screen
54
+ * Backups of source files may be readable by someone else. "_There is nothing secret in my code, why should I ever password protect my backups_"
55
+ * You can't push your code to Github (or any onther service) without removing your password from the file. Remember: Even if you undo your accidential commit with password, Git doesn't delete it, that commit is just not used but is still readable by everybody.
56
+ * If you change your password in the future (maybe it leaked because _someone_ stored password in source file… oh… well…) you will have to change every occurrence in your scripts
57
+
58
+ Preferred method is to have `login.js` that saves `AppState` to a file and then use that file from all your scripts.
59
+ This way you can put password in your code for a minute, login to facebook and then remove it.
60
+
61
+ If you want to be even more safe: _login.js_ can get password with `require("readline")` or with environment variables like this:
62
+ ```js
63
+ var credentials = {
64
+ email: process.env.FB_EMAIL,
65
+ password: process.env.FB_PASSWORD
66
+ }
67
+ ```
68
+ ```bash
69
+ FB_EMAIL="john.doe@example.com"
70
+ FB_PASSWORD="MySuperHardP@ssw0rd"
71
+ nodejs login.js
72
+ ```
73
+
74
+ ---------------------------------------
75
+
76
+ <a name="login"></a>
77
+ ### login(credentials[, options], callback)
78
+
79
+ This function is returned by `require(...)` and is the main entry point to the API.
80
+
81
+ It allows the user to log into facebook given the right credentials.
82
+
83
+ If it succeeds, `callback` will be called with a `null` object (for potential errors) and with an object containing all the available functions.
84
+
85
+ If it fails, `callback` will be called with an error object.
86
+
87
+ __Arguments__
88
+
89
+ * `credentials`: An object containing the fields `email` and `password` used to login, __*or*__ an object containing the field `appState`.
90
+ * `options`: An object representing options to use when logging in (as described in [api.setOptions](#setOptions)).
91
+ * `callback(err, api)`: A callback called when login is done (successful or not). `err` is an object containing a field `error`.
92
+
93
+ __Example (Email & Password)__
94
+
95
+ ```js
96
+ const login = require("fca-luxury");
97
+
98
+ login({email: "FB_EMAIL", password: "FB_PASSWORD"}, (err, api) => {
99
+ if(err) return console.error(err);
100
+ // Here you can use the api
101
+ });
102
+ ```
103
+
104
+ __Example (Email & Password then save appState to file)__
105
+
106
+ ```js
107
+ const fs = require("fs");
108
+ const login = require("fca-luxury");
109
+
110
+ login({email: "FB_EMAIL", password: "FB_PASSWORD"}, (err, api) => {
111
+ if(err) return console.error(err);
112
+
113
+ fs.writeFileSync('appstate.json', JSON.stringify(api.getAppState()));
114
+ });
115
+ ```
116
+
117
+ __Example (AppState loaded from file)__
118
+
119
+ ```js
120
+ const fs = require("fs");
121
+ const login = require(fca-luxury);
122
+
123
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
124
+ if(err) return console.error(err);
125
+ // Here you can use the api
126
+ });
127
+ ```
128
+
129
+ __Login Approvals (2-Factor Auth)__: When you try to login with Login Approvals enabled, your callback will be called with an error `'login-approval'` that has a `continue` function that accepts the approval code as a `string` or a `number`.
130
+
131
+ __Example__:
132
+
133
+ ```js
134
+ const fs = require("fs");
135
+ const login = require(fca-luxury);
136
+ const readline = require("readline");
137
+
138
+ var rl = readline.createInterface({
139
+ input: process.stdin,
140
+ output: process.stdout
141
+ });
142
+
143
+ const obj = {email: "FB_EMAIL", password: "FB_PASSWORD"};
144
+ login(obj, (err, api) => {
145
+ if(err) {
146
+ switch (err.error) {
147
+ case 'login-approval':
148
+ console.log('Enter code > ');
149
+ rl.on('line', (line) => {
150
+ err.continue(line);
151
+ rl.close();
152
+ });
153
+ break;
154
+ default:
155
+ console.error(err);
156
+ }
157
+ return;
158
+ }
159
+
160
+ // Logged in!
161
+ });
162
+ ```
163
+
164
+ __Review Recent Login__: Sometimes Facebook will ask you to review your recent logins. This means you've recently logged in from a unrecognized location. This will will result in the callback being called with an error `'review-recent-login'` by default. If you wish to automatically approve all recent logins, you can set the option `forceLogin` to `true` in the `loginOptions`.
165
+
166
+
167
+ ---------------------------------------
168
+
169
+ <a name="addUserToGroup"></a>
170
+ ### api.addUserToGroup(userID, threadID[, callback])
171
+
172
+ Adds a user (or array of users) to a group chat.
173
+
174
+ __Arguments__
175
+
176
+ * `userID`: User ID or array of user IDs.
177
+ * `threadID`: Group chat ID.
178
+ * `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
179
+
180
+ ---------------------------------------
181
+
182
+ <a name="changeAdminStatus"></a>
183
+ ### api.changeAdminStatus(threadID, adminIDs, adminStatus[, callback])
184
+
185
+ Given a adminID, or an array of adminIDs, will set the admin status of the user(s) to `adminStatus`.
186
+
187
+ __Arguments__
188
+ * `threadID`: ID of a group chat (can't use in one-to-one conversations)
189
+ * `adminIDs`: The id(s) of users you wish to admin/unadmin (string or an array).
190
+ * `adminStatus`: Boolean indicating whether the user(s) should be promoted to admin (`true`) or demoted to a regular user (`false`).
191
+ * `callback(err)`: A callback called when the query is done (either with an error or null).
192
+
193
+ __Example__
194
+
195
+ ```js
196
+ const fs = require("fs");
197
+ const login = require("fca-luxury");
198
+
199
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
200
+ if (err) return console.error(err);
201
+
202
+ let threadID = "0000000000000000";
203
+ let newAdmins = ["111111111111111", "222222222222222"];
204
+ api.changeAdminStatus(threadID, newAdmins, true, editAdminsCallback);
205
+
206
+ let adminToRemove = "333333333333333";
207
+ api.changeAdminStatus(threadID, adminToRemove, false, editAdminsCallback);
208
+
209
+ });
210
+
211
+ function editAdminsCallback(err) {
212
+ if (err) return console.error(err);
213
+ }
214
+
215
+ ```
216
+
217
+ ---------------------------------------
218
+
219
+ <a name="changeArchivedStatus"></a>
220
+ ### api.changeArchivedStatus(threadOrThreads, archive[, callback])
221
+
222
+ Given a threadID, or an array of threadIDs, will set the archive status of the threads to `archive`. Archiving a thread will hide it from the logged-in user's inbox until the next time a message is sent or received.
223
+
224
+ __Arguments__
225
+ * `threadOrThreads`: The id(s) of the threads you wish to archive/unarchive.
226
+ * `archive`: Boolean indicating the new archive status to assign to the thread(s).
227
+ * `callback(err)`: A callback called when the query is done (either with an error or null).
228
+
229
+ __Example__
230
+
231
+ ```js
232
+ const fs = require("fs");
233
+ const login = require("fca-luxury");
234
+
235
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
236
+ if(err) return console.error(err);
237
+
238
+ api.changeArchivedStatus("000000000000000", true, (err) => {
239
+ if(err) return console.error(err);
240
+ });
241
+ });
242
+ ```
243
+
244
+ ---------------------------------------
245
+
246
+ <a name="changeBlockedStatus"></a>
247
+ ### api.changeBlockedStatus(userID, block[, callback])
248
+
249
+ Prevents a user from privately contacting you. (Messages in a group chat will still be seen by both parties).
250
+
251
+ __Arguments__
252
+
253
+ * `userID`: User ID.
254
+ * `block`: Boolean indicating whether to block or unblock the user (true for block).
255
+ * `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
256
+
257
+ ---------------------------------------
258
+
259
+ <a name="changeGroupImage"></a>
260
+ ### api.changeGroupImage(image, threadID[, callback])
261
+
262
+ Will change the group chat's image to the given image.
263
+
264
+ __Arguments__
265
+ * `image`: File stream of image.
266
+ * `threadID`: String representing the ID of the thread.
267
+ * `callback(err)`: A callback called when the change is done (either with an error or null).
268
+
269
+ __Example__
270
+
271
+ ```js
272
+ const fs = require("fs");
273
+ const login = require("fca-luxury");
274
+
275
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
276
+ if(err) return console.error(err);
277
+
278
+ api.changeGroupImage(fs.createReadStream("./avatar.png"), "000000000000000", (err) => {
279
+ if(err) return console.error(err);
280
+ });
281
+ });
282
+ ```
283
+
284
+ ---------------------------------------
285
+
286
+ <a name="changeNickname"></a>
287
+ ### api.changeNickname(nickname, threadID, participantID[, callback])
288
+
289
+ Will change the thread user nickname to the one provided.
290
+
291
+ __Arguments__
292
+ * `nickname`: String containing a nickname. Leave empty to reset nickname.
293
+ * `threadID`: String representing the ID of the thread.
294
+ * `participantID`: String representing the ID of the user.
295
+ * `callback(err)`: An optional callback called when the change is done (either with an error or null).
296
+
297
+ __Example__
298
+
299
+ ```js
300
+ const fs = require("fs");
301
+ const login = require("fca-luxury");
302
+
303
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
304
+ if(err) return console.error(err);
305
+
306
+ api.changeNickname("Example", "000000000000000", "000000000000000", (err) => {
307
+ if(err) return console.error(err);
308
+ });
309
+ });
310
+ ```
311
+
312
+ ---------------------------------------
313
+
314
+ <a name="changeThreadColor"></a>
315
+ ### api.changeThreadColor(color, threadID[, callback])
316
+
317
+ Will change the thread color to the given hex string color ("#0000ff"). Set it
318
+ to empty string if you want the default.
319
+
320
+ Note: the color needs to start with a "#".
321
+
322
+ __Arguments__
323
+ * `color`: String representing a hex color code (eg: "#0000ff") preceded by "#".
324
+ * `threadID`: String representing the ID of the thread.
325
+ * `callback(err)`: A callback called when the change is done (either with an error or null).
326
+
327
+ __Example__
328
+
329
+ ```js
330
+ const fs = require("fs");
331
+ const login = require("fca-luxury");
332
+
333
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
334
+ if(err) return console.error(err);
335
+
336
+ api.changeThreadColor("#0000ff", "000000000000000", (err) => {
337
+ if(err) return console.error(err);
338
+ });
339
+ });
340
+ ```
341
+
342
+ ---------------------------------------
343
+
344
+ <a name="changeThreadEmoji"></a>
345
+ ### api.changeThreadEmoji(emoji, threadID[, callback])
346
+
347
+ Will change the thread emoji to the one provided.
348
+
349
+ Note: The UI doesn't play nice with all emoji.
350
+
351
+ __Arguments__
352
+ * `emoji`: String containing a single emoji character.
353
+ * `threadID`: String representing the ID of the thread.
354
+ * `callback(err)`: A callback called when the change is done (either with an error or null).
355
+
356
+ __Example__
357
+
358
+ ```js
359
+ const fs = require("fs");
360
+ const login = require(fca-luxury);
361
+
362
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
363
+ if(err) return console.error(err);
364
+
365
+ api.changeThreadEmoji("💯", "000000000000000", (err) => {
366
+ if(err) return console.error(err);
367
+ });
368
+ });
369
+ ```
370
+
371
+ ---------------------------------------
372
+
373
+ <a name="createPoll"></a>
374
+ ### api.createPoll(title, threadID[, options][, callback])
375
+
376
+ Creates a poll with the specified title and optional poll options, which can also be initially selected by the logged-in user.
377
+
378
+ __Arguments__
379
+ * `title`: String containing a title for the poll.
380
+ * `threadID`: String representing the ID of the thread.
381
+ * `options`: An optional `string : bool` dictionary to specify initial poll options and their initial states (selected/not selected), respectively.
382
+ * `callback(err)`: An optional callback called when the poll is posted (either with an error or null) - can omit the `options` parameter and use this as the third parameter if desired.
383
+
384
+ __Example__
385
+
386
+ ```js
387
+ const fs = require("fs");
388
+ const login = require(fca-luxury);
389
+
390
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
391
+ if(err) return console.error(err);
392
+
393
+ api.createPoll("Example Poll", "000000000000000", {
394
+ "Option 1": false,
395
+ "Option 2": true
396
+ }, (err) => {
397
+ if(err) return console.error(err);
398
+ });
399
+ });
400
+ ```
401
+
402
+ ---------------------------------------
403
+
404
+ <a name="deleteMessage"></a>
405
+ ### api.deleteMessage(messageOrMessages[, callback])
406
+
407
+ Takes a messageID or an array of messageIDs and deletes the corresponding message.
408
+
409
+ __Arguments__
410
+ * `messageOrMessages`: A messageID string or messageID string array
411
+ * `callback(err)`: A callback called when the query is done (either with an error or null).
412
+
413
+ __Example__
414
+ ```js
415
+ const fs = require("fs");
416
+ const login = require(fca-luxury);
417
+
418
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
419
+ if(err) return console.error(err);
420
+
421
+ api.listen((err, message) => {
422
+ if(message.body) {
423
+ api.sendMessage(message.body, message.threadID, (err, messageInfo) => {
424
+ if(err) return console.error(err);
425
+
426
+ api.deleteMessage(messageInfo.messageID);
427
+ });
428
+ }
429
+ });
430
+ });
431
+ ```
432
+
433
+ ---------------------------------------
434
+
435
+ <a name="deleteThread"></a>
436
+ ### api.deleteThread(threadOrThreads[, callback])
437
+
438
+ Given a threadID, or an array of threadIDs, will delete the threads from your account. Note that this does *not* remove the messages from Facebook's servers - anyone who hasn't deleted the thread can still view all of the messages.
439
+
440
+ __Arguments__
441
+
442
+ * `threadOrThreads` - The id(s) of the threads you wish to remove from your account.
443
+ * `callback(err)` - A callback called when the operation is done, maybe with an object representing an error.
444
+
445
+ __Example__
446
+
447
+ ```js
448
+ const fs = require("fs");
449
+ const login = require(fca-luxury);
450
+
451
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
452
+ if(err) return console.error(err);
453
+
454
+ api.deleteThread("000000000000000", (err) => {
455
+ if(err) return console.error(err);
456
+ });
457
+ });
458
+ ```
459
+
460
+ ---------------------------------------
461
+
462
+ <a name="forwardAttachment"></a>
463
+ ### api.forwardAttachment(attachmentID, userOrUsers[, callback])
464
+
465
+ Forwards corresponding attachment to given userID or to every user from an array of userIDs
466
+
467
+ __Arguments__
468
+ * `attachmentID`: The ID field in the attachment object. Recorded audio cannot be forwarded.
469
+ * `userOrUsers`: A userID string or usersID string array
470
+ * `callback(err)`: A callback called when the query is done (either with an error or null).
471
+
472
+ ---------------------------------------
473
+
474
+ <a name="getAppState"></a>
475
+ ### api.getAppState()
476
+
477
+ Returns current appState which can be saved to a file or stored in a variable.
478
+
479
+ ---------------------------------------
480
+
481
+ <a name="getCurrentUserID"></a>
482
+ ### api.getCurrentUserID()
483
+
484
+ Returns the currently logged-in user's Facebook user ID.
485
+
486
+ ---------------------------------------
487
+
488
+ <a name="getEmojiUrl"></a>
489
+ ### api.getEmojiUrl(c, size[, pixelRatio])
490
+
491
+ Returns the URL to a Facebook Messenger-style emoji image asset.
492
+
493
+ __note__: This function will return a URL regardless of whether the image at the URL actually exists.
494
+ This can happen if, for example, Messenger does not have an image asset for the requested emoji.
495
+
496
+ __Arguments__
497
+
498
+ * `c` - The emoji character
499
+ * `size` - The width and height of the emoji image; supported sizes are 32, 64, and 128
500
+ * `pixelRatio` - The pixel ratio of the emoji image; supported ratios are '1.0' and '1.5' (default is '1.0')
501
+
502
+ __Example__
503
+
504
+ ```js
505
+ const fs = require("fs");
506
+ const login = require(fca-luxury);
507
+
508
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
509
+ if(err) return console.error(err);
510
+
511
+ // Prints https://static.xx.fbcdn.net/images/emoji.php/v8/z9c/1.0/128/1f40d.png
512
+ console.log('Snake emoji, 128px (128x128 with pixel ratio of 1.0');
513
+ console.log(api.getEmojiUrl('\ud83d\udc0d', 128));
514
+
515
+ // Prints https://static.xx.fbcdn.net/images/emoji.php/v8/ze1/1.5/128/1f40d.png
516
+ console.log('Snake emoji, 192px (128x128 with pixel ratio of 1.5');
517
+ console.log(api.getEmojiUrl('\ud83d\udc0d', 128, '1.5'));
518
+ });
519
+ ```
520
+
521
+ ---------------------------------------
522
+
523
+ <a name="getFriendsList"></a>
524
+ ### api.getFriendsList(callback)
525
+
526
+ Returns an array of objects with some information about your friends.
527
+
528
+ __Arguments__
529
+
530
+ * `callback(err, arr)` - A callback called when the query is done (either with an error or with an confirmation object). `arr` is an array of objects with the following fields: `alternateName`, `firstName`, `gender`, `userID`, `isFriend`, `fullName`, `profilePicture`, `type`, `profileUrl`, `vanity`, `isBirthday`.
531
+
532
+ __Example__
533
+
534
+ ```js
535
+ const fs = require("fs");
536
+ const login = require(fca-luxury);
537
+
538
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
539
+ if(err) return console.error(err);
540
+
541
+ api.getFriendsList((err, data) => {
542
+ if(err) return console.error(err);
543
+
544
+ console.log(data.length);
545
+ });
546
+ });
547
+ ```
548
+
549
+ ---------------------------------------
550
+
551
+ <a name="getThreadHistory"></a>
552
+ ### api.getThreadHistory(threadID, amount, timestamp, callback)
553
+
554
+ Takes a threadID, number of messages, a timestamp, and a callback.
555
+
556
+ __note__: if you're getting a 500 error, it's possible that you're requesting too many messages. Try reducing that number and see if that works.
557
+
558
+ __Arguments__
559
+ * `threadID`: A threadID corresponding to the target chat
560
+ * `amount`: The amount of messages to *request*
561
+ * `timestamp`: Used to described the time of the most recent message to load. If timestamp is `undefined`, facebook will load the most recent messages.
562
+ * `callback(error, history)`: If error is null, history will contain an array of message objects.
563
+
564
+ __Example__
565
+
566
+ To load 50 messages at a time, we can use `undefined` as the timestamp to retrieve the most recent messages and use the timestamp of the earliest message to load the next 50.
567
+
568
+ ```js
569
+ var timestamp = undefined;
570
+
571
+ function loadNextThreadHistory(api){
572
+ api.getThreadHistory(threadID, 50, timestamp, (err, history) => {
573
+ if(err) return console.error(err);
574
+
575
+ /*
576
+ Since the timestamp is from a previous loaded message,
577
+ that message will be included in this history so we can discard it unless it is the first load.
578
+ */
579
+ if(timestamp != undefined) history.pop();
580
+
581
+ /*
582
+ Handle message history
583
+ */
584
+
585
+ timestamp = history[0].timestamp;
586
+ });
587
+ }
588
+ ```
589
+
590
+ ---------------------------------------
591
+
592
+ <a name="getThreadInfo"></a>
593
+ ### api.getThreadInfo(threadID[, callback])
594
+
595
+ Takes a threadID and a callback. Works for both single-user and group threads.
596
+
597
+ __Arguments__
598
+ * `threadID`: A threadID corresponding to the target thread.
599
+ * `callback(err, info)`: If `err` is `null`, `info` will contain the following properties: * `callback(err, arr)`: A callback called when the query is done (either with an error or with an confirmation object). `arr` is an array of thread object containing the following properties:
600
+
601
+ | Key | Description |
602
+ |----------|:-------------:|
603
+ | threadID | ID of the thread |
604
+ | participantIDs | Array of user IDs in the thread |
605
+ | name | Name of the thread. Usually the name of the user. In group chats, this will be empty if the name of the group chat is unset. |
606
+ | nicknames | Map of nicknames for members of the thread. If there are no nicknames set, this will be null. |
607
+ | unreadCount | Number of unread messages |
608
+ | messageCount | Number of messages |
609
+ | imageSrc | URL to the group chat photo. Null if unset or a 1-1 thread. |
610
+ | timestamp | |
611
+ | muteUntil | Timestamp at which the thread will no longer be muted. The timestamp will be -1 if the thread is muted indefinitely or null if the thread is not muted. |
612
+ | isGroup | boolean, true if this thread is a group thread (more than 2 participants). |
613
+ | isSubscribed | |
614
+ | folder | The folder that the thread is in. Can be one of: <ul><li>'inbox'</li><li>'archive'</li></ul> |
615
+ | isArchived | True if the thread is archived, false if not |
616
+ | cannotReplyReason | If you cannot reply to this thread, this will be a string stating why. Otherwise it will be null. |
617
+ | lastReadTimestamp | Timestamp of the last message that is marked as 'read' by the current user. |
618
+ | emoji | Object with key 'emoji' whose value is the emoji unicode character. Null if unset. |
619
+ | color | String form of the custom color in hexadecimal form. |
620
+ | adminIDs | Array of user IDs of the admins of the thread. Empty array if unset. |
621
+
622
+ ---------------------------------------
623
+
624
+ <a name="getThreadList"></a>
625
+ ### api.getThreadList(limit, timestamp, tags, callback)
626
+
627
+ Returns information about the user's threads.
628
+
629
+ __Arguments__
630
+
631
+ * `limit`: Limit the number of threads to fetch.
632
+ * `timestamp`: Request threads *before* this date. `null` means *now*
633
+ * `tags`: An array describing which folder to fetch. It should be one of these:
634
+ - `["INBOX"]` *(same as `[]`)*
635
+ - `["ARCHIVED"]`
636
+ - `["PENDING"]`
637
+ - `["OTHER"]`
638
+ - `["INBOX", "unread"]`
639
+ - `["ARCHIVED", "unread"]`
640
+ - `["PENDING", "unread"]`
641
+ - `["OTHER", "unread"]`
642
+
643
+ *if you find something new, let us know*
644
+
645
+ * `callback(err, list)`: Callback called when the query is done (either with an error or with a proper result). `list` is an *array* with objects with the following properties:
646
+
647
+ __Thread list__
648
+
649
+ | Key | Description |
650
+ |----------------------|-------------------------------------------------------------|
651
+ | threadID | ID of the thread |
652
+ | name | The name of the thread |
653
+ | unreadCount | Amount of unread messages in thread |
654
+ | messageCount | Amount of messages in thread |
655
+ | imageSrc | Link to the thread's image or `null` |
656
+ | emoji | The default emoji in thread (classic like is `null`) |
657
+ | color | Thread's message color in `RRGGBB` (default blue is `null`) |
658
+ | nicknames | An array of `{"userid": "1234", "nickname": "John Doe"}` |
659
+ | muteUntil | Timestamp until the mute expires or `null` |
660
+ | participants | An array of participants. See below |
661
+ | adminIDs | An array of thread admin IDs |
662
+ | folder | `INBOX`, `ARCHIVED`, `PENDING` or `OTHER` |
663
+ | isGroup | `true` or `false` |
664
+ | customizationEnabled | `false` in one-to-one conversations with `Page` or `ReducedMessagingActor` |
665
+ | participantAddMode | currently `"ADD"` for groups and `null` otherwise |
666
+ | reactionsMuteMode | `REACTIONS_NOT_MUTED` or `REACTIONS_MUTED` |
667
+ | mentionsMuteMode | `MENTIONS_NOT_MUTED` or `MENTIONS_MUTED` |
668
+ | isArchived | `true` or `false` |
669
+ | isSubscribed | `true` or `false` |
670
+ | timestamp | timestamp in miliseconds |
671
+ | snippet | Snippet's text message |
672
+ | snippetAttachments | Attachments in snippet |
673
+ | snippetSender | ID of snippet sender |
674
+ | lastMessageTimestamp | timestamp in milliseconds |
675
+ | lastReadTimestamp | timestamp in milliseconds or `null` |
676
+ | cannotReplyReason | `null`, `"RECIPIENTS_NOT_LOADABLE"` or `"BLOCKED"` |
677
+
678
+ __`participants` format__
679
+
680
+ `accountType` is one of the following:
681
+ - `"User"`
682
+ - `"Page"`
683
+ - `"UnavailableMessagingActor"`
684
+ - `"ReducedMessagingActor"`
685
+
686
+ (*there might be more*)
687
+
688
+ <table>
689
+ <tr>
690
+ <th>Account type</th>
691
+ <th>Key</th>
692
+ <th>Description</th>
693
+ </tr>
694
+ <tr>
695
+ <td rowspan="12"><code>"User"</code></td>
696
+ <td>userID</td>
697
+ <td>ID of user</td>
698
+ </tr>
699
+ <tr>
700
+ <td>name</td>
701
+ <td>Full name of user</td>
702
+ </tr>
703
+ <tr>
704
+ <td>shortName</td>
705
+ <td>Short name of user (most likely first name)</td>
706
+ </tr>
707
+ <tr>
708
+ <td>gender</td>
709
+ <td>Either
710
+ <code>"MALE"</code>,
711
+ <code>"FEMALE"</code>,
712
+ <code>"NEUTER"</code> or
713
+ <code>"UNKNOWN"</code>
714
+ </td>
715
+ </tr>
716
+ <tr>
717
+ <td>url</td>
718
+ <td>URL of the user's Facebook profile</td>
719
+ </tr>
720
+ <tr>
721
+ <td>profilePicture</td>
722
+ <td>URL of the profile picture</td>
723
+ </tr>
724
+ <tr>
725
+ <td>username</td>
726
+ <td>Username of user or
727
+ <code>null</code>
728
+ </td>
729
+ </tr>
730
+ <tr>
731
+ <td>isViewerFriend</td>
732
+ <td>Is the user a friend of you?</td>
733
+ </tr>
734
+ <tr>
735
+ <td>isMessengerUser</td>
736
+ <td>Does the user use Messenger?</td>
737
+ </tr>
738
+ <tr>
739
+ <td>isVerified</td>
740
+ <td>Is the user verified? (Little blue tick mark)</td>
741
+ </tr>
742
+ <tr>
743
+ <td>isMessageBlockedByViewer</td>
744
+ <td>Is the user blocking messages from you?</td>
745
+ </tr>
746
+ <tr>
747
+ <td>isViewerCoworker</td>
748
+ <td>Is the user your coworker?
749
+ </td>
750
+ </tr>
751
+
752
+ <tr>
753
+ <td rowspan="10"><code>"Page"</code></td>
754
+ <td>userID</td>
755
+ <td>ID of the page</td>
756
+ </tr>
757
+ <tr>
758
+ <td>name</td>
759
+ <td>Name of the fanpage</td>
760
+ </tr>
761
+ <tr>
762
+ <td>url</td>
763
+ <td>URL of the fanpage</td>
764
+ </tr>
765
+ <tr>
766
+ <td>profilePicture</td>
767
+ <td>URL of the profile picture</td>
768
+ </tr>
769
+ <tr>
770
+ <td>username</td>
771
+ <td>Username of user or
772
+ <code>null</code>
773
+ </td>
774
+ </tr>
775
+ <tr>
776
+ <td>acceptsMessengerUserFeedback</td>
777
+ <td></td>
778
+ </tr>
779
+ <tr>
780
+ <td>isMessengerUser</td>
781
+ <td>Does the fanpage use Messenger?</td>
782
+ </tr>
783
+ <tr>
784
+ <td>isVerified</td>
785
+ <td>Is the fanpage verified? (Little blue tick mark)</td>
786
+ </tr>
787
+ <tr>
788
+ <td>isMessengerPlatformBot</td>
789
+ <td>Is the fanpage a bot</td>
790
+ </tr>
791
+ <tr>
792
+ <td>isMessageBlockedByViewer</td>
793
+ <td>Is the fanpage blocking messages from you?</td>
794
+ </tr>
795
+
796
+ <tr>
797
+ <td rowspan="7"><code>"ReducedMessagingActor"</code><br />(account requres verification,<br />messages are hidden)</td>
798
+ <td>userID</td>
799
+ <td>ID of the user</td>
800
+ </tr>
801
+ <tr>
802
+ <td>name</td>
803
+ <td>Name of the user</td>
804
+ </tr>
805
+ <tr>
806
+ <td>url</td>
807
+ <td>
808
+ <code>null</code>
809
+ </td>
810
+ </tr>
811
+ <tr>
812
+ <td>profilePicture</td>
813
+ <td>URL of the default Facebook profile picture</td>
814
+ </tr>
815
+ <tr>
816
+ <td>username</td>
817
+ <td>Username of user</td>
818
+ </td>
819
+ </tr>
820
+ <tr>
821
+ <td>acceptsMessengerUserFeedback</td>
822
+ <td></td>
823
+ </tr>
824
+ <tr>
825
+ <td>isMessageBlockedByViewer</td>
826
+ <td>Is the user blocking messages from you?</td>
827
+ </tr>
828
+ <tr>
829
+ <td rowspan="7"><code>"UnavailableMessagingActor"</code><br />(account disabled/removed)</td>
830
+ <td>userID</td>
831
+ <td>ID of the user</td>
832
+ </tr>
833
+ <tr>
834
+ <td>name</td>
835
+ <td><em>Facebook User</em> in user's language</td>
836
+ </tr>
837
+ <tr>
838
+ <td>url</td>
839
+ <td><code>null</code></td>
840
+ </tr>
841
+ <tr>
842
+ <td>profilePicture</td>
843
+ <td>URL of the default **male** Facebook profile picture</td>
844
+ </tr>
845
+ <tr>
846
+ <td>username</td>
847
+ <td><code>null</code></td>
848
+ </tr>
849
+ <tr>
850
+ <td>acceptsMessengerUserFeedback</td>
851
+ <td></td>
852
+ </tr>
853
+ <tr>
854
+ <td>isMessageBlockedByViewer</td>
855
+ <td>Is the user blocking messages from you?</td>
856
+ </tr>
857
+ </table>
858
+
859
+
860
+ In a case that some account type is not supported, we return just this *(but you can't rely on it)* and log a warning to the console:
861
+
862
+ | Key | Description |
863
+ |--------------|-------------------------|
864
+ | accountType | type, can be anything |
865
+ | userID | ID of the account |
866
+ | name | Name of the account |
867
+
868
+
869
+ ---------------------------------------
870
+
871
+ <a name="getThreadPictures"></a>
872
+ ### api.getThreadPictures(threadID, offset, limit, callback)
873
+
874
+ Returns pictures sent in the thread.
875
+
876
+ __Arguments__
877
+
878
+ * `threadID`: A threadID corresponding to the target chat
879
+ * `offset`: Start index of picture to retrieve, where 0 is the most recent picture
880
+ * `limit`: Number of pictures to get, incrementing from the offset index
881
+ * `callback(err, arr)`: A callback called when the query is done (either with an error or with an confirmation object). `arr` is an array of objects with `uri`, `width`, and `height`.
882
+
883
+ ---------------------------------------
884
+
885
+ <a name="getUserID"></a>
886
+ ### api.getUserID(name, callback)
887
+
888
+ Given the full name or vanity name of a Facebook user, event, page, group or app, the call will perform a Facebook Graph search and return all corresponding IDs (order determined by Facebook).
889
+
890
+ __Arguments__
891
+
892
+ * `name` - A string being the name of the item you're looking for.
893
+ * `callback(err, obj)` - A callback called when the search is done (either with an error or with the resulting object). `obj` is an array which contains all of the items that facebook graph search found, ordered by "importance". Each item in the array has the following properties: `userID`,`photoUrl`,`indexRank`, `name`, `isVerified`, `profileUrl`, `category`, `score`, `type` (type is generally user, group, page, event or app).
894
+
895
+ __Example__
896
+
897
+ ```js
898
+ const fs = require("fs");
899
+ const login = require(fca-luxury);
900
+
901
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
902
+ if(err) return console.error(err);
903
+
904
+ api.getUserID("Marc Zuckerbot", (err, data) => {
905
+ if(err) return console.error(err);
906
+
907
+ // Send the message to the best match (best by Facebook's criteria)
908
+ var msg = "Hello!"
909
+ var threadID = data[0].userID;
910
+ api.sendMessage(msg, threadID);
911
+ });
912
+ });
913
+ ```
914
+
915
+ ---------------------------------------
916
+
917
+ <a name="getUserInfo"></a>
918
+ ### api.getUserInfo(ids, callback)
919
+
920
+ Will get some information about the given users.
921
+
922
+ __Arguments__
923
+
924
+ * `ids` - Either a string/number for one ID or an array of strings/numbers for a batched query.
925
+ * `callback(err, obj)` - A callback called when the query is done (either with an error or with an confirmation object). `obj` is a mapping from userId to another object containing the following properties: `name`, `firstName`, `vanity` (user's chosen facebook handle, if any), `thumbSrc`, `profileUrl`, `gender`, `type` (type is generally user, group, page, event or app), `isFriend`, `isBirthday`, `searchTokens`, `alternateName`.
926
+
927
+ __Example__
928
+
929
+ ```js
930
+ const fs = require("fs");
931
+ const login = require(fca-luxury);
932
+
933
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
934
+ if(err) return console.error(err);
935
+
936
+ api.getUserInfo([1, 2, 3, 4], (err, ret) => {
937
+ if(err) return console.error(err);
938
+
939
+ for(var prop in ret) {
940
+ if(ret.hasOwnProperty(prop) && ret[prop].isBirthday) {
941
+ api.sendMessage("Happy birthday :)", prop);
942
+ }
943
+ }
944
+ });
945
+ });
946
+ ```
947
+
948
+ ---------------------------------------
949
+
950
+ <a name="threadColors"></a>
951
+ ### api.threadColors
952
+
953
+ A dictionary mapping names of all currently valid thread colors to their hexadecimal values that are accepted by [`api.changeThreadColor`](#changeThreadColor). These colors, listed below, are the ones present in the palette UI used for selecting thread colors on the Messenger client.
954
+
955
+ - MessengerBlue: `null`
956
+ - Viking: `#44bec7`
957
+ - GoldenPoppy: `#ffc300`
958
+ - RadicalRed: `#fa3c4c`
959
+ - Shocking: `#d696bb`
960
+ - PictonBlue: `#6699cc`
961
+ - FreeSpeechGreen: `#13cf13`
962
+ - Pumpkin: `#ff7e29`
963
+ - LightCoral: `#e68585`
964
+ - MediumSlateBlue: `#7646ff`
965
+ - DeepSkyBlue: `#20cef5`
966
+ - Fern: `#67b868`
967
+ - Cameo: `#d4a88c`
968
+ - BrilliantRose: `#ff5ca1`
969
+ - BilobaFlower: `#a695c7`
970
+
971
+ ---------------------------------------
972
+
973
+ <a name="handleMessageRequest"></a>
974
+ ### api.handleMessageRequest(threadID, accept[, callback])
975
+
976
+ Accept or ignore message request(s) with thread id `threadID`.
977
+
978
+ __Arguments__
979
+
980
+ * `threadID`: A threadID or array of threadIDs corresponding to the target thread(s). Can be numbers or strings.
981
+ * `accept`: Boolean indicating the new status to assign to the message request(s); true for inbox, false to others.
982
+ * `callback(err)`: A callback called when the query is done (with an error or with null).
983
+
984
+ ---------------------------------------
985
+
986
+ <a name="listen"></a>
987
+ ### api.listen(callback)
988
+
989
+ Will call `callback` when a new message is received on this account.
990
+ By default this won't receive events (joining/leaving a chat, title change etc...) but it can be activated with `api.setOptions({listenEvents: true})`. This will by default ignore messages sent by the current account, you can enable listening to your own messages with `api.setOptions({selfListen: true})`. This returns `stopListening` that will stop the `listen` loop and is guaranteed to prevent any future calls to the callback given to `listen`. An immediate call to `stopListening` when an error occurs will prevent the listen function to continue.
991
+
992
+ __Arguments__
993
+
994
+ - `callback(error, message)`: A callback called every time the logged-in account receives a new message.
995
+
996
+ <a name="message"></a>
997
+ __Message__
998
+
999
+ The message object will contain different fields based on its type (as determined by its `type` field). By default, the only type that will be listened for is `message`. If enabled through [setOptions](#setOptions), the message object may alternatively represent an event e.g. a read receipt. The available event types are as follows:
1000
+
1001
+ <table>
1002
+ <tr>
1003
+ <th>Event Type</th>
1004
+ <th>Field</th>
1005
+ <th>Description</th>
1006
+ </tr>
1007
+ <tr>
1008
+ <td rowspan="9">
1009
+ <code>"message"</code><br />
1010
+ A message was sent to a thread.
1011
+ </td>
1012
+ <td><code>attachments</code></td>
1013
+ <td>An array of attachments to the message. Attachments vary in type, see the attachments table below.</td>
1014
+ </tr>
1015
+ <tr>
1016
+ <td><code>body</code></td>
1017
+ <td>The string corresponding to the message that was just received.</td>
1018
+ </tr>
1019
+ <tr>
1020
+ <td><code>isGroup</code></td>
1021
+ <td>boolean, true if this thread is a group thread (more than 2 participants).</td>
1022
+ </tr>
1023
+ <tr>
1024
+ <td><code>mentions</code></td>
1025
+ <td>An object containing people mentioned/tagged in the message in the format { id: name }</td>
1026
+ </tr>
1027
+ <tr>
1028
+ <td><code>messageID</code></td>
1029
+ <td>A string representing the message ID.</td>
1030
+ </tr>
1031
+ <tr>
1032
+ <td><code>senderID</code></td>
1033
+ <td>The id of the person who sent the message in the chat with threadID.</td>
1034
+ </tr>
1035
+ <tr>
1036
+ <td><code>threadID</code></td>
1037
+ <td>The threadID representing the thread in which the message was sent.</td>
1038
+ </tr>
1039
+ <tr>
1040
+ <td><code>isUnread</code></td>
1041
+ <td>Boolean representing whether or not the message was read.</td>
1042
+ </tr>
1043
+ <tr>
1044
+ <td><code>type</code></td>
1045
+ <td>For this event type, this will always be the string <code>"message"</code>.</td>
1046
+ </tr>
1047
+ <tr>
1048
+ <td rowspan="6">
1049
+ <code>"event"</code><br />
1050
+ An event occurred within a thread.
1051
+ </td>
1052
+ <td><code>author</code></td>
1053
+ <td>The person who performed the event.</td>
1054
+ </tr>
1055
+ <tr>
1056
+ <td><code>logMessageBody</code></td>
1057
+ <td>String printed in the chat.</td>
1058
+ </tr>
1059
+ <tr>
1060
+ <td><code>logMessageData</code></td>
1061
+ <td>Data relevant to the event.</td>
1062
+ </tr>
1063
+ <tr>
1064
+ <td><code>logMessageType</code></td>
1065
+ <td>String representing the type of event (<code>log:subscribe</code>, <code>log:unsubscribe</code>, <code>log:thread-name</code>, <code>log:thread-color</code>, <code>log:thread-icon</code>, <code>log:user-nickname</code>)</td>
1066
+ </tr>
1067
+ <tr>
1068
+ <td><code>threadID</code></td>
1069
+ <td>The threadID representing the thread in which the message was sent.</td>
1070
+ </tr>
1071
+ <tr>
1072
+ <td><code>type</code></td>
1073
+ <td>For this event type, this will always be the string <code>"event"</code>.</td>
1074
+ </tr>
1075
+ <tr>
1076
+ <td rowspan="5">
1077
+ <code>"typ"</code><br />
1078
+ A user in a thread is typing.
1079
+ </td>
1080
+ <td><code>from</code></td>
1081
+ <td>ID of the user who started/stopped typing.</td>
1082
+ </tr>
1083
+ <tr>
1084
+ <td><code>fromMobile</code></td>
1085
+ <td>Boolean representing whether or not the person's using a mobile device to type.</td>
1086
+ </tr>
1087
+ <tr>
1088
+ <td><code>isTyping</code></td>
1089
+ <td>Boolean representing whether or not a person started typing.</td>
1090
+ </tr>
1091
+ <tr>
1092
+ <td><code>threadID</code></td>
1093
+ <td>The threadID representing the thread in which a user is typing.</td>
1094
+ </tr>
1095
+ <tr>
1096
+ <td><code>type</code></td>
1097
+ <td>For this event type, this will always be the string <code>"typ"</code>.</td>
1098
+ </tr>
1099
+ <tr>
1100
+ <td rowspan="3">
1101
+ <code>"read"</code><br />
1102
+ The current API user has read a message.
1103
+ </td>
1104
+ <td><code>threadID</code></td>
1105
+ <td>The threadID representing the thread in which the message was sent.</td>
1106
+ </tr>
1107
+ <tr>
1108
+ <td><code>time</code></td>
1109
+ <td>The time at which the user read the message.</td>
1110
+ </tr>
1111
+ <tr>
1112
+ <td><code>type</code></td>
1113
+ <td>For this event type, this will always be the string <code>"read"</code>.</td>
1114
+ </tr>
1115
+ <tr>
1116
+ <td rowspan="4">
1117
+ <code>"read_receipt"</code><br />
1118
+ A user within a thread has seen a message sent by the API user.
1119
+ </td>
1120
+ <td><code>reader</code></td>
1121
+ <td>ID of the user who just read the message.</td>
1122
+ </tr>
1123
+ <tr>
1124
+ <td><code>threadID</code></td>
1125
+ <td>The thread in which the message was read.</td>
1126
+ </tr>
1127
+ <tr>
1128
+ <td><code>time</code></td>
1129
+ <td>The time at which the reader read the message.</td>
1130
+ </tr>
1131
+ <tr>
1132
+ <td><code>type</code></td>
1133
+ <td>For this event type, this will always be the string <code>"read_receipt"</code>.</td>
1134
+ </tr>
1135
+ <tr>
1136
+ <td rowspan="8">
1137
+ <code>"message_reaction"</code><br />
1138
+ A user has sent a reaction to a message.
1139
+ </td>
1140
+ <td><code>messageID</code></td>
1141
+ <td>The ID of the message</td>
1142
+ </tr>
1143
+ <tr>
1144
+ <td><code>offlineThreadingID</code></td>
1145
+ <td>The offline message ID</td>
1146
+ </tr>
1147
+ <tr>
1148
+ <td><code>reaction</code></td>
1149
+ <td>Contains reaction emoji</td>
1150
+ </tr>
1151
+ <tr>
1152
+ <td><code>senderID</code></td>
1153
+ <td>ID of the author the message, where has been reaction added</td>
1154
+ </tr>
1155
+ <tr>
1156
+ <td><code>threadID</code></td>
1157
+ <td>ID of the thread where the message has been sent</td>
1158
+ </tr>
1159
+ <tr>
1160
+ <td><code>timestamp</code></td>
1161
+ <td>Unix Timestamp (in miliseconds) when the reaction was sent</td>
1162
+ </tr>
1163
+ <tr>
1164
+ <td><code>type</code></td>
1165
+ <td>For this event type, this will always be the string <code>"message_reaction"</code>.</td>
1166
+ </tr>
1167
+ <tr>
1168
+ <td><code>userID</code></td>
1169
+ <td>ID of the reaction sender</td>
1170
+ </tr>
1171
+ <tr>
1172
+ <td rowspan="4"><a name="presence"></a>
1173
+ <code>"presence"</code><br />
1174
+ The online status of the user's friends. Note that receiving this event type needs to be enabled with <code>api.setOptions({ updatePresence: true })</code>
1175
+ </td>
1176
+ <td><code>statuses</code></td>
1177
+ <td>The online status of the user. <code>0</code> means the user is idle (away for 2 minutes) and <code>2</code> means the user is online (we don't know what 1 or above 2 means...).</td>
1178
+ </tr>
1179
+ <tr>
1180
+ <td><code>timestamp</code></td>
1181
+ <td>The time when the user was last online.</td>
1182
+ </tr>
1183
+ <tr>
1184
+ <td><code>type</code></td>
1185
+ <td>For this event type, this will always be the string <code>"presence"</code>.</td>
1186
+ </tr>
1187
+ <tr>
1188
+ <td><code>userID</code></td>
1189
+ <td>The ID of the user whose status this packet is describing.</td>
1190
+ </tr>
1191
+ <tr>
1192
+ <td rowspan="5">
1193
+ <code>"message_unsend"</code><br />
1194
+ A revoke message request for a message from a thread was received.
1195
+ </td>
1196
+ <td><code>threadID</code></td>
1197
+ <td>The threadID representing the thread in which the revoke message request was received.</td>
1198
+ </tr>
1199
+ <tr>
1200
+ <td><code>senderID</code></td>
1201
+ <td>The id of the person who request to revoke message on threadID.</td>
1202
+ </tr>
1203
+ <tr>
1204
+ <td><code>messageID</code></td>
1205
+ <td>A string representing the message ID that the person request to revoke message want to.</td>
1206
+ </tr>
1207
+ <tr>
1208
+ <td><code>deletionTimestamp</code></td>
1209
+ <td>The time when the request was sent.</td>
1210
+ </tr>
1211
+ <tr>
1212
+ <td><code>type</code></td>
1213
+ <td>For this event type, this will always be the string <code>"message_unsend"</code>.</td>
1214
+ </tr>
1215
+ <tr>
1216
+ <td rowspan="10">
1217
+ <code>"message_reply"</code><br />
1218
+ A reply message was sent to a thread.
1219
+ </td>
1220
+ <td><code>attachments</code></td>
1221
+ <td>An array of attachments to the message. Attachments vary in type, see the attachments table below.</td>
1222
+ </tr>
1223
+ <tr>
1224
+ <td><code>body</code></td>
1225
+ <td>The string corresponding to the message that was just received.</td>
1226
+ </tr>
1227
+ <tr>
1228
+ <td><code>isGroup</code></td>
1229
+ <td>boolean, true if this thread is a group thread (more than 2 participants).</td>
1230
+ </tr>
1231
+ <tr>
1232
+ <td><code>mentions</code></td>
1233
+ <td>An object containing people mentioned/tagged in the message in the format { id: name }</td>
1234
+ </tr>
1235
+ <tr>
1236
+ <td><code>messageID</code></td>
1237
+ <td>A string representing the message ID.</td>
1238
+ </tr>
1239
+ <tr>
1240
+ <td><code>senderID</code></td>
1241
+ <td>The id of the person who sent the message in the chat with threadID.</td>
1242
+ </tr>
1243
+ <tr>
1244
+ <td><code>threadID</code></td>
1245
+ <td>The threadID representing the thread in which the message was sent.</td>
1246
+ </tr>
1247
+ <tr>
1248
+ <td><code>isUnread</code></td>
1249
+ <td>Boolean representing whether or not the message was read.</td>
1250
+ </tr>
1251
+ <tr>
1252
+ <td><code>type</code></td>
1253
+ <td>For this event type, this will always be the string <code>"message_reply"</code>.</td>
1254
+ </tr>
1255
+ <tr>
1256
+ <td><code>messageReply</code></td>
1257
+ <td>An object represent a message being replied. Content inside is the same like a normal <code>"message"</code> event.</td>
1258
+ </tr>
1259
+ </table>
1260
+
1261
+ __Attachments__
1262
+
1263
+ Similar to how messages can vary based on their `type`, so too can the `attachments` within `"message"` events. Each attachment will consist of an object of one of the following types:
1264
+
1265
+ | Attachment Type | Fields |
1266
+ | --------------- | ------ |
1267
+ | `"sticker"` | `ID`, `url`, `packID`, `spriteUrl`, `spriteUrl2x`, `width`, `height`, `caption`, `description`, `frameCount`, `frameRate`, `framesPerRow`, `framesPerCol` |
1268
+ | `"file"` | `ID`, `filename`, `url`, `isMalicious`, `contentType` |
1269
+ | `"photo"` | `ID`, `filename`, `thumbnailUrl`, `previewUrl`, `previewWidth`, `previewHeight`, `largePreviewUrl`, `largePreviewWidth`, `largePreviewHeight` |
1270
+ | `"animated_image"` | `ID`, `filename`, `previewUrl`, `previewWidth`, `previewHeight`, `url`, `width`, `height` |
1271
+ | `"video"` | `ID`, `filename`, `previewUrl`, `previewWidth`, `previewHeight`, `url`, `width`, `height`, `duration`, `videoType` |
1272
+ | `"audio"` | `ID`, `filename`, `audioType`, `duration`, `url`, `isVoiceMail` |
1273
+ | `"share"` | `ID`, `url`, `title`, `description`, `source`, `image`, `width`, `height`, `playable`, `duration`, `playableUrl`, `subattachments`, `properties` |
1274
+
1275
+ __Example__
1276
+
1277
+ ```js
1278
+ const fs = require("fs");
1279
+ const login = require(fca-luxury);
1280
+
1281
+ // Simple echo bot. He'll repeat anything that you say.
1282
+ // Will stop when you say '/stop'
1283
+
1284
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1285
+ if(err) return console.error(err);
1286
+
1287
+ api.setOptions({listenEvents: true});
1288
+
1289
+ var stopListening = api.listen((err, event) => {
1290
+ if(err) return console.error(err);
1291
+
1292
+ switch(event.type) {
1293
+ case "message":
1294
+ if(event.body === '/stop') {
1295
+ api.sendMessage("Goodbye...", event.threadID);
1296
+ return stopListening();
1297
+ }
1298
+ api.markAsRead(event.threadID, (err) => {
1299
+ if(err) console.log(err);
1300
+ });
1301
+ api.sendMessage("TEST BOT: " + event.body, event.threadID);
1302
+ break;
1303
+ case "event":
1304
+ console.log(event);
1305
+ break;
1306
+ }
1307
+ });
1308
+ });
1309
+ ```
1310
+
1311
+ ---------------------------------------
1312
+
1313
+ <a name="listenMqtt"></a>
1314
+ ### api.listenMqtt(callback) (Experimental)
1315
+ Same as [`api.listen`](#listen) but uses MQTT to recieve data.
1316
+
1317
+ Will call `callback` when a new message is received on this account.
1318
+ By default this won't receive events (joining/leaving a chat, title change etc...) but it can be activated with `api.setOptions({listenEvents: true})`. This will by default ignore messages sent by the current account, you can enable listening to your own messages with `api.setOptions({selfListen: true})`. This returns `stopListening` that will stop the `listen` loop and is guaranteed to prevent any future calls to the callback given to `listenMqtt`. An immediate call to `stopListening` when an error occurs will prevent the listen function to continue.
1319
+
1320
+
1321
+ __Arguments__
1322
+
1323
+ - `callback(error, message)`: A callback called every time the logged-in account receives a new message.
1324
+
1325
+ Messages and Events are the same as [`api.listen`](#listen)
1326
+
1327
+ ---------------------------------------
1328
+
1329
+ <a name="logout"></a>
1330
+ ### api.logout([callback])
1331
+
1332
+ Logs out the current user.
1333
+
1334
+ __Arguments__
1335
+
1336
+ * `callback(err)`: A callback called when the query is done (either with an error or with null).
1337
+
1338
+ ---------------------------------------
1339
+
1340
+ <a name="markAsDelivered"></a>
1341
+ ### api.markAsDelivered(threadID, messageID[, callback]])
1342
+
1343
+ Given a threadID and a messageID will mark that message as delivered. If a message is marked as delivered that tells facebook servers that it was recieved.
1344
+
1345
+ You can also mark new messages as delivered automatically. This is enabled by default. See [api.setOptions](#setOptions).
1346
+
1347
+ __Arguments__
1348
+
1349
+ * `threadID` - The id of the thread in which you want to mark the message as delivered.
1350
+ * `messageID` - The id of the message want to mark as delivered.
1351
+ * `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1352
+
1353
+ __Example__
1354
+
1355
+ ```js
1356
+ const fs = require("fs");
1357
+ const login = require(fca-luxury);
1358
+
1359
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1360
+ if(err) return console.error(err);
1361
+
1362
+ api.listen((err, message) => {
1363
+ if(err) return console.error(err);
1364
+
1365
+ // Marks messages as delivered immediately after they're received
1366
+ api.markAsDelivered(message.threadID, message.messageID);
1367
+ });
1368
+ });
1369
+ ```
1370
+
1371
+ ---------------------------------------
1372
+
1373
+ <a name="markAsRead"></a>
1374
+ ### api.markAsRead(threadID, [read[, callback]])
1375
+
1376
+ Given a threadID will mark all the unread messages as read. Facebook will take a couple of seconds to show that you've read the messages.
1377
+
1378
+ You can also mark new messages as read automatically. See [api.setOptions](#setOptions).
1379
+
1380
+ __Arguments__
1381
+
1382
+ * `threadID` - The id of the thread in which you want to mark the messages as read.
1383
+ * `read` - An optional boolean where `true` means to mark the message as being "read" and `false` means to mark the message as being "unread".
1384
+ * `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1385
+
1386
+ __Example__
1387
+
1388
+ ```js
1389
+ const fs = require("fs");
1390
+ const login = require(fca-luxury);
1391
+
1392
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1393
+ if(err) return console.error(err);
1394
+
1395
+ api.listen((err, message) => {
1396
+ if(err) return console.error(err);
1397
+
1398
+ // Marks messages as read immediately after they're received
1399
+ api.markAsRead(message.threadID);
1400
+ });
1401
+ });
1402
+ ```
1403
+
1404
+ ---------------------------------------
1405
+
1406
+ <a name="markAsReadAll"></a>
1407
+ ### api.markAsReadAll([callback]])
1408
+
1409
+ This function will mark all of messages in your inbox readed.
1410
+
1411
+ ---------------------------------------
1412
+
1413
+ <a name="muteThread"></a>
1414
+ ### api.muteThread(threadID, muteSeconds[, callback])
1415
+
1416
+ Mute a chat for a period of time, or unmute a chat.
1417
+
1418
+ __Arguments__
1419
+
1420
+ * `threadID` - The ID of the chat you want to mute.
1421
+ * `muteSeconds` - Mute the chat for this amount of seconds. Use `0` to unmute a chat. Use '-1' to mute a chat indefinitely.
1422
+ * `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1423
+
1424
+ __Example__
1425
+
1426
+ ```js
1427
+ const fs = require("fs");
1428
+ const login = require(fca-luxury);
1429
+
1430
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1431
+ if(err) return console.error(err);
1432
+
1433
+ api.listen((err, message) => {
1434
+ if(err) return console.error(err);
1435
+
1436
+ // Mute all incoming chats for one minute
1437
+ api.muteThread(message.threadID, 60);
1438
+ });
1439
+ });
1440
+ ```
1441
+
1442
+ ---------------------------------------
1443
+
1444
+ <a name="removeUserFromGroup"></a>
1445
+ ### api.removeUserFromGroup(userID, threadID[, callback])
1446
+
1447
+ Removes a user from a group chat.
1448
+
1449
+ __Arguments__
1450
+
1451
+ * `userID`: User ID.
1452
+ * `threadID`: Group chat ID.
1453
+ * `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
1454
+
1455
+ ---------------------------------------
1456
+
1457
+ <a name="resolvePhotoUrl"></a>
1458
+ ### api.resolvePhotoUrl(photoID, callback)
1459
+
1460
+ Resolves the URL to the full-size photo, given its ID. This function is useful for retrieving the full-size photo URL
1461
+ of image attachments in messages, returned by [`api.getThreadHistory`](#getThreadHistory).
1462
+
1463
+ __Arguments__
1464
+
1465
+ * `photoID`: Photo ID.
1466
+ * `callback(err, url)`: A callback called when the query is done (either with an error or with the photo's URL). `url` is a string with the photo's URL.
1467
+
1468
+ ---------------------------------------
1469
+
1470
+ <a name="searchForThread"></a>
1471
+ ### api.searchForThread(name, callback)
1472
+
1473
+ > This part is outdated.
1474
+ > see #396
1475
+
1476
+ Takes a chat title (thread name) and returns matching results as a formatted threads array (ordered according to Facebook).
1477
+
1478
+ __Arguments__
1479
+ * `name`: A messageID string or messageID string array
1480
+ * `callback(err, obj)`: A callback called when the query is done (either with an error or a thread object). The object passed in the callback has the following shape: `threadID`, <del>`participants`</del>, `participantIDs`, `formerParticipants`, `name`, `nicknames`, `snippet`, `snippetHasAttachment`, `snippetAttachments`, `snippetSender`, `unreadCount`, `messageCount`, `imageSrc`, `timestamp`, `serverTimestamp`, `muteSettings`, `isCanonicalUser`, `isCanonical`, `canonicalFbid`, `isSubscribed`, `rootMessageThreadingID`, `folder`, `isArchived`, `recipientsLoadable`, `hasEmailParticipant`, `readOnly`, `canReply`, `composerEnabled`, `blockedParticipants`, `lastMessageID`
1481
+
1482
+ ---------------------------------------
1483
+
1484
+ <a name="sendMessage"></a>
1485
+ ### api.sendMessage(message, threadID[, callback][, messageID])
1486
+
1487
+ Sends the given message to the threadID.
1488
+
1489
+ __Arguments__
1490
+
1491
+ * `message`: A string (for backward compatibility) or a message object as described below.
1492
+ * `threadID`: A string, number, or array representing a thread. It happens to be someone's userID in the case of a one to one conversation or an array of userIDs when starting a new group chat.
1493
+ * `callback(err, messageInfo)`: (Optional) A callback called when sending the message is done (either with an error or with an confirmation object). `messageInfo` contains the `threadID` where the message was sent and a `messageID`, as well as the `timestamp` of the message.
1494
+ * `messageID`: (Optional) A string representing a message you want to reply.
1495
+
1496
+ __Message Object__:
1497
+
1498
+ Various types of message can be sent:
1499
+ * *Regular:* set field `body` to the desired message as a string.
1500
+ * *Sticker:* set a field `sticker` to the desired sticker ID.
1501
+ * *File or image:* Set field `attachment` to a readable stream or an array of readable streams.
1502
+ * *URL:* set a field `url` to the desired URL.
1503
+ * *Emoji:* set field `emoji` to the desired emoji as a string and set field `emojiSize` with size of the emoji (`small`, `medium`, `large`)
1504
+ * *Mentions:* set field `mentions` to an array of objects. Objects should have the `tag` field set to the text that should be highlighted in the mention. The object should have an `id` field, where the `id` is the user id of the person being mentioned. The instance of `tag` that is highlighted is determined through indexOf, an optional `fromIndex`
1505
+ can be passed in to specify the start index to start searching for the `tag` text
1506
+ in `body` (default=0). (See below for an example.)
1507
+
1508
+ Note that a message can only be a regular message (which can be empty) and optionally one of the following: a sticker, an attachment or a url.
1509
+
1510
+ __Tip__: to find your own ID, you can look inside the cookies. The `userID` is under the name `c_user`.
1511
+
1512
+ __Example (Basic Message)__
1513
+ ```js
1514
+ const fs = require("fs");
1515
+ const login = require(fca-luxury);
1516
+
1517
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1518
+ if(err) return console.error(err);
1519
+
1520
+ var yourID = "000000000000000";
1521
+ var msg = {body: "Hey!"};
1522
+ api.sendMessage(msg, yourID);
1523
+ });
1524
+ ```
1525
+
1526
+ __Example (File upload)__
1527
+ ```js
1528
+ const fs = require("fs");
1529
+ const login = require(fca-luxury);
1530
+
1531
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1532
+ if(err) return console.error(err);
1533
+
1534
+ // This example uploads an image called image.jpg
1535
+ var yourID = "000000000000000";
1536
+ var msg = {
1537
+ body: "Hey!",
1538
+ attachment: fs.createReadStream(__dirname + '/image.jpg')
1539
+ }
1540
+ api.sendMessage(msg, yourID);
1541
+ });
1542
+ ```
1543
+
1544
+ __Example (Mention)__
1545
+ ```js
1546
+ const login = require(fca-luxury);
1547
+
1548
+ login({email: "EMAIL", password: "PASSWORD"}, (err, api) => {
1549
+ if(err) return console.error(err);
1550
+
1551
+ api.listen((err, message) => {
1552
+ if (message && message.body) {
1553
+ // Getting the actual sender name from ID involves calling
1554
+ // `api.getThreadInfo` and `api.getUserInfo`
1555
+ api.sendMessage({
1556
+ body: 'Hello @Sender! @Sender!',
1557
+ mentions: [{
1558
+ tag: '@Sender',
1559
+ id: message.senderID,
1560
+ fromIndex: 9, // Highlight the second occurrence of @Sender
1561
+ }],
1562
+ }, message.threadID);
1563
+ }
1564
+ });
1565
+ });
1566
+ ```
1567
+
1568
+ ---------------------------------------
1569
+
1570
+ <a name="sendTypingIndicator"></a>
1571
+ ### api.sendTypingIndicator(threadID[, callback])
1572
+
1573
+ Sends a "USERNAME is typing" indicator to other members of the thread indicated by `threadID`. This indication will disappear after 30 second or when the `end` function is called. The `end` function is returned by `api.sendTypingIndicator`.
1574
+
1575
+ __Arguments__
1576
+
1577
+ * `threadID`: Group chat ID.
1578
+ * `callback(err)`: A callback called when the query is done (with an error or with null).
1579
+
1580
+ ---------------------------------------
1581
+
1582
+ <a name="setMessageReaction"></a>
1583
+ ### api.setMessageReaction(reaction, messageID[, callback])
1584
+
1585
+ Sets reaction on message
1586
+
1587
+ __Arguments__
1588
+
1589
+ * `reaction`: A string containing either an emoji, an emoji in unicode, or an emoji shortcut (see list of supported emojis below). The string can be left empty ("") in order to remove a reaction.
1590
+ * `messageID`: A string representing the message ID.
1591
+ * `callback(err)` - A callback called when sending the reaction is done.
1592
+
1593
+ __Supported Emojis__
1594
+
1595
+ |Emoji|Text|Unicode|Shortcuts|
1596
+ |---|---|---|---|
1597
+ |😍|`😍`|`\uD83D\uDE0D`|`:love:`, `:heart_eyes:`|
1598
+ |😆|`😆`|`\uD83D\uDE06`|`:haha:`, `:laughing:`|
1599
+ |😮|`😮`|`\uD83D\uDE2E`|`:wow:`, `:open_mouth:`|
1600
+ |😢|`😢`|`\uD83D\uDE22`|`:sad:`, `:cry:`|
1601
+ |😠|`😠`|`\uD83D\uDE20`|`:angry:`|
1602
+ |👍|`👍`|`\uD83D\uDC4D`|`:like:`, `:thumbsup:`|
1603
+ |👎|`👎`|`\uD83D\uDC4E`|`:dislike:`, `:thumbsdown:`|
1604
+
1605
+ ---------------------------------------
1606
+
1607
+ <a name="setOptions"></a>
1608
+ ### api.setOptions(options)
1609
+
1610
+ Sets various configurable options for the api.
1611
+
1612
+ __Arguments__
1613
+
1614
+ * `options` - An object containing the new values for the options that you want
1615
+ to set. If the value for an option is unspecified, it is unchanged. The following options are possible.
1616
+ - `logLevel`: The desired logging level as determined by npmlog. Choose
1617
+ from either `"silly"`, `"verbose"`, `"info"`, `"http"`, `"warn"`, `"error"`, or `"silent"`.
1618
+ - `selfListen`: (Default `false`) Set this to `true` if you want your api
1619
+ to receive messages from its own account. This is to be used with
1620
+ caution, as it can result in loops (a simple echo bot will send messages
1621
+ forever).
1622
+ - `listenEvents`: (Default `false`) Will make [api.listen](#listen) also handle events (look at api.listen for more details).
1623
+ - `pageID`: (Default empty) Makes [api.listen](#listen) only receive messages through the page specified by that ID. Also makes `sendMessage` and `sendSticker` send from the page.
1624
+ - `updatePresence`: (Default `false`) Will make [api.listen](#listen) also return `presence` ([api.listen](#presence) for more details).
1625
+ - `forceLogin`: (Default `false`) Will automatically approve of any recent logins and continue with the login process.
1626
+ - `userAgent`: (Default `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18`) The desired simulated User Agent.
1627
+ - `autoMarkDelivery`: (Default `true`) Will automatically mark new messages as delivered. See [api.markAsDelivered](#markAsDelivered).
1628
+ - `autoMarkRead`: (Default `false`) Will automatically mark new messages as read/seen. See [api.markAsRead](#markAsRead).
1629
+
1630
+ __Example__
1631
+
1632
+ ```js
1633
+ const fs = require("fs");
1634
+ const login = require(fca-luxury);
1635
+
1636
+ // Simple echo bot. This will send messages forever.
1637
+
1638
+ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
1639
+ if(err) return console.error(err);
1640
+
1641
+ api.setOptions({
1642
+ selfListen: true,
1643
+ logLevel: "silent"
1644
+ });
1645
+
1646
+ api.listen((err, message) => {
1647
+ if(err) return console.error(err);
1648
+
1649
+ // Ignore empty messages (photos etc.)
1650
+ if (typeof message.body === "string") {
1651
+ api.sendMessage(message.body, message.threadID);
1652
+ }
1653
+ });
1654
+ });
1655
+ ```
1656
+
1657
+ ---------------------------------------
1658
+
1659
+ <a name="setTitle"></a>
1660
+ ### api.setTitle(newTitle, threadID[, callback])
1661
+
1662
+ Sets the title of the group chat with thread id `threadID` to `newTitle`.
1663
+
1664
+ Note: This will not work if the thread id corresponds to a single-user chat or if the bot is not in the group chat.
1665
+
1666
+ __Arguments__
1667
+
1668
+ * `newTitle`: A string representing the new title.
1669
+ * `threadID`: A string or number representing a thread. It happens to be someone's userID in the case of a one to one conversation.
1670
+ * `callback(err, obj)` - A callback called when sending the message is done (either with an error or with an confirmation object). `obj` contains only the threadID where the message was sent.
1671
+
1672
+ ---------------------------------------
1673
+
1674
+ <a name="unsendMessage"></a>
1675
+ ### api.unsendMessage(messageID[, callback])
1676
+
1677
+ Revokes a message from anyone that could see that message with `messageID`
1678
+
1679
+ Note: This will only work if the message is sent by you and was sent less than 10 minutes ago.
1680
+
1681
+ __Arguments__
1682
+
1683
+ * `messageID`: Message ID you want to unsend.
1684
+ * `callback(err)`: A callback called when the query is done (with an error or with null).
1685
+
1686
+ ---------------------------------------