@dongdev/fca-unofficial 2.0.8 → 2.0.10

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/DOCS.md CHANGED
@@ -1,1873 +1,2138 @@
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.createNewGroup`](#createNewGroup)
13
- - [`api.createPoll`](#createPoll)
14
- - [`api.deleteMessage`](#deleteMessage)
15
- - [`api.deleteThread`](#deleteThread)
16
- - [`api.forwardAttachment`](#forwardAttachment)
17
- - [`api.getAppState`](#getAppState)
18
- - [`api.getCurrentUserID`](#getCurrentUserID)
19
- - [`api.getEmojiUrl`](#getEmojiUrl)
20
- - [`api.getFriendsList`](#getFriendsList)
21
- - [`api.getThreadHistory`](#getThreadHistory)
22
- - [`api.getThreadInfo`](#getThreadInfo)
23
- - [`api.getThreadList`](#getThreadList)
24
- - [`api.getThreadPictures`](#getThreadPictures)
25
- - [`api.getUserID`](#getUserID)
26
- - [`api.getUserInfo`](#getUserInfo)
27
- - [`api.handleMessageRequest`](#handleMessageRequest)
28
- - [`api.listenMqtt`](#listenMqtt)
29
- - [`api.logout`](#logout)
30
- - [`api.markAsDelivered`](#markAsDelivered)
31
- - [`api.markAsRead`](#markAsRead)
32
- - [`api.markAsReadAll`](#markAsReadAll)
33
- - [`api.markAsSeen`](#markAsSeen)
34
- - [`api.muteThread`](#muteThread)
35
- - [`api.removeUserFromGroup`](#removeUserFromGroup)
36
- - [`api.resolvePhotoUrl`](#resolvePhotoUrl)
37
- - [`api.searchForThread`](#searchForThread)
38
- - [`api.sendMessage`](#sendMessage)
39
- - [`api.sendTypingIndicator`](#sendTypingIndicator)
40
- - [`api.setMessageReaction`](#setMessageReaction)
41
- - [`api.setOptions`](#setOptions)
42
- - [`api.setTitle`](#setTitle)
43
- - [`api.threadColors`](#threadColors)
44
- - [`api.unsendMessage`](#unsendMessage)
1
+ # FCA-Unofficial - Complete API Documentation
45
2
 
46
- ---
47
-
48
- ### Password safety
49
-
50
- **Read this** before you _copy+paste_ examples from below.
51
-
52
- You should not store Facebook password in your scripts.
53
- There are few good reasons:
3
+ ## Introduction
54
4
 
55
- - People who are standing behind you may look at your "code" and get your password if it is on the screen
56
- - 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_"
57
- - 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.
58
- - 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
5
+ **@dongdev/fca-unofficial** is an unofficial Node.js library for interacting with Facebook Messenger by emulating browser behavior. This library allows you to create chat bots and automate tasks on Facebook Messenger.
59
6
 
60
- Preferred method is to have `login.js` that saves `AppState` to a file and then use that file from all your scripts.
61
- This way you can put password in your code for a minute, login to facebook and then remove it.
62
-
63
- If you want to be even more safe: _login.js_ can get password with `require("readline")` or with environment variables like this:
64
-
65
- ```js
66
- var credentials = {
67
- email: process.env.FB_EMAIL,
68
- password: process.env.FB_PASSWORD
69
- };
70
- ```
7
+ ## Installation
71
8
 
72
9
  ```bash
73
- FB_EMAIL="john.doe@example.com"
74
- FB_PASSWORD="MySuperHardP@ssw0rd"
75
- nodejs login.js
10
+ npm install @dongdev/fca-unofficial@latest
76
11
  ```
77
12
 
78
13
  ---
79
14
 
80
- <a name="login"></a>
15
+ ## 1. LOGIN
81
16
 
82
- ### login(credentials[, options][, callback])
17
+ ### 1.1. Login with Email & Password
83
18
 
84
- This function is returned by `require(...)` and is the main entry point to the API.
19
+ ```javascript
20
+ const login = require("@dongdev/fca-unofficial");
85
21
 
86
- It allows the user to log into facebook given the right credentials.
87
-
88
- Return a Promise that will resolve if logged in successfully, or reject if failed to login. (will not resolve or reject if callback is supplied!)
89
-
90
- If `callback` is supplied:
91
-
92
- - `callback` will be called with a `null` object (for potential errors) and with an object containing all the available functions if logged in successfully.
93
-
94
- - `callback` will be called with an error object if failed to login.
22
+ const credentials = {
23
+ email: "your_email@example.com",
24
+ password: "your_password"
25
+ };
95
26
 
96
- If `login-approval` error was thrown: Inside error object is `continue` function, you can call that function with 2FA code. The behaviour of this function depends on how you call `login` with:
27
+ login(credentials, (err, api) => {
28
+ if (err) {
29
+ console.error("Login error:", err);
30
+ return;
31
+ }
32
+ console.log("Login successful!");
33
+ });
34
+ ```
97
35
 
98
- - If `callback` is not supplied (using `Promise`), this function will return a `Promise` that behaves like `Promise` received from `login`.
36
+ ### 1.2. Login with 2FA (Two-Factor Authentication)
99
37
 
100
- - If `callback` is supplied, this function will still return a `Promise`, but it will not resolve. Instead, the result is called to `callback`.
38
+ When your account has 2FA enabled, you need to provide the 2FA code:
101
39
 
102
- **Arguments**
40
+ ```javascript
41
+ const login = require("@dongdev/fca-unofficial");
42
+ const readline = require("readline");
103
43
 
104
- - `credentials`: An object containing the fields `email` and `password` used to login, **_or_** an object containing the field `appState`.
105
- - `options`: An object representing options to use when logging in (as described in [api.setOptions](#setOptions)).
106
- - `callback(err, api)`: A callback called when login is done (successful or not). `err` is an object containing a field `error`.
44
+ const rl = readline.createInterface({
45
+ input: process.stdin,
46
+ output: process.stdout
47
+ });
107
48
 
108
- **Example (Email & Password)**
49
+ const credentials = {
50
+ email: "your_email@example.com",
51
+ password: "your_password"
52
+ };
109
53
 
110
- ```js
111
- const login = require("fca-unofficial");
54
+ login(credentials, (err, api) => {
55
+ if (err) {
56
+ // If 2FA is required
57
+ if (err.error === 'login-approval') {
58
+ console.log("2FA code required!");
59
+
60
+ rl.question("Enter 2FA code: ", (code) => {
61
+ err.continue(code);
62
+ rl.close();
63
+ });
64
+ } else {
65
+ console.error("Login error:", err);
66
+ }
67
+ return;
68
+ }
112
69
 
113
- login({ email: "FB_EMAIL", password: "FB_PASSWORD" }, (err, api) => {
114
- if (err) return console.error(err);
115
- // Here you can use the api
70
+ console.log("Login successful!");
116
71
  });
117
72
  ```
118
73
 
119
- **Example (Email & Password then save appState to file)**
74
+ ### 1.3. Login with AppState (Recommended)
120
75
 
121
- ```js
122
- const fs = require("fs");
123
- const login = require("fca-unofficial");
76
+ AppState is saved cookies and session data. Login with AppState helps avoid entering password each time and reduces checkpoint risk.
124
77
 
125
- login({ email: "FB_EMAIL", password: "FB_PASSWORD" }, (err, api) => {
126
- if (err) return console.error(err);
78
+ #### Get and Save AppState:
127
79
 
128
- fs.writeFileSync("appstate.json", JSON.stringify(api.getAppState()));
129
- });
130
- ```
80
+ ```javascript
81
+ const fs = require("fs");
82
+ const login = require("@dongdev/fca-unofficial");
131
83
 
132
- **Example (AppState loaded from file)**
84
+ const credentials = {
85
+ email: "your_email@example.com",
86
+ password: "your_password"
87
+ };
133
88
 
134
- ```js
135
- const fs = require("fs");
136
- const login = require("fca-unofficial");
89
+ login(credentials, (err, api) => {
90
+ if (err) {
91
+ console.error("Login error:", err);
92
+ return;
93
+ }
137
94
 
138
- login(
139
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
140
- (err, api) => {
141
- if (err) return console.error(err);
142
- // Here you can use the api
143
- }
144
- );
95
+ // Save AppState to file
96
+ try {
97
+ const appState = api.getAppState();
98
+ fs.writeFileSync("appstate.json", JSON.stringify(appState, null, 2));
99
+ console.log("✅ AppState saved!");
100
+ } catch (error) {
101
+ console.error("Error saving AppState:", error);
102
+ }
103
+ });
145
104
  ```
146
105
 
147
- **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`.
148
-
149
- **Example**:
106
+ #### Use Saved AppState:
150
107
 
151
- ```js
108
+ ```javascript
152
109
  const fs = require("fs");
153
- const login = require("fca-unofficial");
154
- const readline = require("readline");
110
+ const login = require("@dongdev/fca-unofficial");
155
111
 
156
- var rl = readline.createInterface({
157
- input: process.stdin,
158
- output: process.stdout
159
- });
112
+ const credentials = {
113
+ appState: JSON.parse(fs.readFileSync("appstate.json", "utf8"))
114
+ };
160
115
 
161
- const obj = { email: "FB_EMAIL", password: "FB_PASSWORD" };
162
- login(obj, (err, api) => {
163
- if (err) {
164
- switch (err.error) {
165
- case "login-approval":
166
- console.log("Enter code > ");
167
- rl.on("line", line => {
168
- err.continue(line);
169
- rl.close();
170
- });
171
- break;
172
- default:
173
- console.error(err);
116
+ login(credentials, (err, api) => {
117
+ if (err) {
118
+ console.error("Login error:", err);
119
+ return;
174
120
  }
175
- return;
176
- }
177
121
 
178
- // Logged in!
122
+ console.log("Login successful with AppState!");
179
123
  });
180
124
  ```
181
125
 
182
- **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`.
126
+ **Note:** You can use [c3c-fbstate](https://github.com/c3cbot/c3c-fbstate) tool to get AppState from browser.
183
127
 
184
128
  ---
185
129
 
186
- <a name="addUserToGroup"></a>
187
-
188
- ### api.addUserToGroup(userID, threadID[, callback])
130
+ ## 2. CONFIGURATION (Options)
189
131
 
190
- Adds a user (or array of users) to a group chat.
132
+ After login, you can configure API options:
191
133
 
192
- **Arguments**
134
+ ```javascript
135
+ api.setOptions({
136
+ // Listen to events (add/remove members, change group name, etc.)
137
+ listenEvents: true,
193
138
 
194
- - `userID`: User ID or array of user IDs.
195
- - `threadID`: Group chat ID.
196
- - `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
139
+ // Listen to your own messages
140
+ selfListen: false,
197
141
 
198
- ---
199
-
200
- <a name="changeAdminStatus"></a>
142
+ // Auto mark messages as read
143
+ autoMarkRead: false,
201
144
 
202
- ### api.changeAdminStatus(threadID, adminIDs, adminStatus)
145
+ // Auto mark as delivered
146
+ autoMarkDelivery: false,
203
147
 
204
- Given a adminID, or an array of adminIDs, will set the admin status of the user(s) to `adminStatus`.
148
+ // Online status (true/false)
149
+ online: true,
205
150
 
206
- **Arguments**
151
+ // Log level (silent/error/warn/info/verbose)
152
+ logLevel: "info",
207
153
 
208
- - `threadID`: ID of a group chat (can't use in one-to-one conversations)
209
- - `adminIDs`: The id(s) of users you wish to admin/unadmin (string or an array).
210
- - `adminStatus`: Boolean indicating whether the user(s) should be promoted to admin (`true`) or demoted to a regular user (`false`).
154
+ // Custom user agent
155
+ userAgent: "Mozilla/5.0..."
156
+ });
157
+ ```
211
158
 
212
- **Example**
159
+ ---
213
160
 
214
- ```js
215
- const fs = require("fs");
216
- const login = require("fca-unofficial");
161
+ ## 3. DETAILED API METHODS
217
162
 
218
- login(
219
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
220
- async function(err, api) {
221
- if (err) return console.error(err);
163
+ ### 3.1. sendMessage - Send Message
222
164
 
223
- let threadID = "0000000000000000";
224
- let newAdmins = ["111111111111111", "222222222222222"];
225
- await api.changeAdminStatus(threadID, newAdmins, true);
165
+ Send message to user or group chat.
226
166
 
227
- let adminToRemove = "333333333333333";
228
- await api.changeAdminStatus(threadID, adminToRemove, false);
229
- }
230
- );
167
+ #### Syntax:
168
+ ```javascript
169
+ api.sendMessage(message, threadID, [messageID], [callback])
231
170
  ```
232
171
 
233
- ---
172
+ #### Parameters:
173
+ - `message`: Message content (string or object)
174
+ - `threadID`: Conversation ID (user ID or group ID)
175
+ - `messageID`: (Optional) Message ID to reply to
176
+ - `callback`: (Optional) Callback function `(err, messageInfo)`
234
177
 
235
- <a name="changeArchivedStatus"></a>
178
+ #### Basic Example:
236
179
 
237
- ### api.changeArchivedStatus(threadOrThreads, archive[, callback])
180
+ ```javascript
181
+ api.sendMessage("Hello!", "100012345678901", (err, messageInfo) => {
182
+ if (err) {
183
+ console.error("Send message error:", err);
184
+ return;
185
+ }
186
+ console.log("Message sent, ID:", messageInfo.messageID);
187
+ });
188
+ ```
238
189
 
239
- 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.
190
+ #### Send Messages with Various Content Types:
240
191
 
241
- **Arguments**
192
+ ```javascript
193
+ // 1. Simple text message
194
+ api.sendMessage("Hello World", threadID);
242
195
 
243
- - `threadOrThreads`: The id(s) of the threads you wish to archive/unarchive.
244
- - `archive`: Boolean indicating the new archive status to assign to the thread(s).
245
- - `callback(err)`: A callback called when the query is done (either with an error or null).
196
+ // 2. Send sticker
197
+ api.sendMessage({
198
+ sticker: "767334476655547" // Sticker ID
199
+ }, threadID);
246
200
 
247
- **Example**
201
+ // 3. Send emoji with size
202
+ api.sendMessage({
203
+ body: "Awesome!",
204
+ emoji: "👍",
205
+ emojiSize: "large" // small, medium, large
206
+ }, threadID);
248
207
 
249
- ```js
208
+ // 4. Send file/image
250
209
  const fs = require("fs");
251
- const login = require("fca-unofficial");
252
-
253
- login(
254
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
255
- (err, api) => {
256
- if (err) return console.error(err);
257
-
258
- api.changeArchivedStatus("000000000000000", true, err => {
259
- if (err) return console.error(err);
260
- });
261
- }
262
- );
210
+ api.sendMessage({
211
+ body: "Here is an image",
212
+ attachment: fs.createReadStream("./image.jpg")
213
+ }, threadID);
214
+
215
+ // 5. Send multiple files
216
+ api.sendMessage({
217
+ body: "Multiple attachments",
218
+ attachment: [
219
+ fs.createReadStream("./image1.jpg"),
220
+ fs.createReadStream("./image2.jpg"),
221
+ fs.createReadStream("./document.pdf")
222
+ ]
223
+ }, threadID);
224
+
225
+ // 6. Send URL
226
+ api.sendMessage({
227
+ body: "Check this link",
228
+ url: "https://example.com"
229
+ }, threadID);
230
+
231
+ // 7. Reply to message
232
+ api.sendMessage({
233
+ body: "This is a reply"
234
+ }, threadID, messageID);
235
+
236
+ // 8. Mention users
237
+ api.sendMessage({
238
+ body: "Hello @User1 and @User2!",
239
+ mentions: [
240
+ {
241
+ tag: "@User1",
242
+ id: "100012345678901",
243
+ fromIndex: 6 // Starting position of @User1
244
+ },
245
+ {
246
+ tag: "@User2",
247
+ id: "100012345678902",
248
+ fromIndex: 17
249
+ }
250
+ ]
251
+ }, threadID);
263
252
  ```
264
253
 
265
254
  ---
266
255
 
267
- <a name="changeBlockedStatus"></a>
268
-
269
- ### api.changeBlockedStatus(userID, block[, callback])
256
+ ### 3.2. listenMqtt - Listen for Messages
270
257
 
271
- Prevents a user from privately contacting you. (Messages in a group chat will still be seen by both parties).
258
+ Listen for messages and events from Facebook Messenger (using MQTT).
272
259
 
273
- **Arguments**
260
+ #### Syntax:
261
+ ```javascript
262
+ const stopListening = api.listenMqtt(callback);
263
+ ```
274
264
 
275
- - `userID`: User ID.
276
- - `block`: Boolean indicating whether to block or unblock the user (true for block).
277
- - `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
265
+ #### Parameters:
266
+ - `callback`: Function `(err, event)` called when new message/event arrives
278
267
 
279
- ---
268
+ #### Example:
280
269
 
281
- <a name="changeGroupImage"></a>
270
+ ```javascript
271
+ const stopListening = api.listenMqtt((err, event) => {
272
+ if (err) {
273
+ console.error("Listen error:", err);
274
+ return;
275
+ }
282
276
 
283
- ### api.changeGroupImage(image, threadID[, callback])
277
+ // Handle events
278
+ switch (event.type) {
279
+ case "message":
280
+ console.log("New message:", event.body);
281
+ console.log("From user:", event.senderID);
282
+ console.log("In conversation:", event.threadID);
284
283
 
285
- Will change the group chat's image to the given image.
284
+ // Reply to message
285
+ if (event.body === "Hi") {
286
+ api.sendMessage("Hello!", event.threadID);
287
+ }
288
+ break;
286
289
 
287
- **Arguments**
290
+ case "event":
291
+ console.log("Event:", event.logMessageType);
292
+ // log_message_type can be:
293
+ // - log:subscribe (member added)
294
+ // - log:unsubscribe (member removed)
295
+ // - log:thread-name (group name changed)
296
+ // - log:thread-icon (group icon changed)
297
+ // - log:thread-color (chat color changed)
298
+ break;
299
+
300
+ case "typ":
301
+ console.log(event.from, "is typing...");
302
+ break;
303
+
304
+ case "read_receipt":
305
+ console.log("Message read by:", event.reader);
306
+ break;
307
+ }
308
+ });
288
309
 
289
- - `image`: File stream of image.
290
- - `threadID`: String representing the ID of the thread.
291
- - `callback(err)`: A callback called when the change is done (either with an error or null).
310
+ // Stop listening
311
+ // stopListening();
312
+ ```
292
313
 
293
- **Example**
314
+ #### Event Object Details:
315
+
316
+ ```javascript
317
+ // Event type: "message"
318
+ {
319
+ type: "message",
320
+ threadID: "1234567890",
321
+ messageID: "mid.xxx",
322
+ senderID: "100012345678901",
323
+ body: "Message content",
324
+ attachments: [], // Array of attachments
325
+ mentions: {}, // Object of mentions
326
+ timestamp: 1234567890000,
327
+ isGroup: false // true if group chat
328
+ }
294
329
 
295
- ```js
296
- const fs = require("fs");
297
- const login = require("fca-unofficial");
330
+ // Event type: "event"
331
+ {
332
+ type: "event",
333
+ threadID: "1234567890",
334
+ logMessageType: "log:subscribe",
335
+ logMessageData: {...},
336
+ author: "100012345678901"
337
+ }
298
338
 
299
- login(
300
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
301
- (err, api) => {
302
- if (err) return console.error(err);
339
+ // Event type: "typ" (typing)
340
+ {
341
+ type: "typ",
342
+ threadID: "1234567890",
343
+ from: "100012345678901",
344
+ isTyping: true
345
+ }
303
346
 
304
- api.changeGroupImage(
305
- fs.createReadStream("./avatar.png"),
306
- "000000000000000",
307
- err => {
308
- if (err) return console.error(err);
309
- }
310
- );
311
- }
312
- );
347
+ // Event type: "read_receipt" (read)
348
+ {
349
+ type: "read_receipt",
350
+ threadID: "1234567890",
351
+ reader: "100012345678901",
352
+ time: 1234567890000
353
+ }
313
354
  ```
314
355
 
315
356
  ---
316
357
 
317
- <a name="changeNickname"></a>
358
+ ### 3.3. getUserInfo - Get User Information
318
359
 
319
- ### api.changeNickname(nickname, threadID, participantID[, callback])
360
+ Get detailed information about one or more users.
320
361
 
321
- Will change the thread user nickname to the one provided.
322
-
323
- **Arguments**
362
+ #### Syntax:
363
+ ```javascript
364
+ api.getUserInfo(userID, callback);
365
+ ```
324
366
 
325
- - `nickname`: String containing a nickname. Leave empty to reset nickname.
326
- - `threadID`: String representing the ID of the thread.
327
- - `participantID`: String representing the ID of the user.
328
- - `callback(err)`: An optional callback called when the change is done (either with an error or null).
367
+ #### Example:
329
368
 
330
- **Example**
369
+ ```javascript
370
+ // Get info for 1 user
371
+ api.getUserInfo("100012345678901", (err, userInfo) => {
372
+ if (err) {
373
+ console.error(err);
374
+ return;
375
+ }
331
376
 
332
- ```js
333
- const fs = require("fs");
334
- const login = require("fca-unofficial");
377
+ console.log(userInfo);
378
+ // {
379
+ // "100012345678901": {
380
+ // name: "John Doe",
381
+ // firstName: "John",
382
+ // vanity: "john.doe",
383
+ // thumbSrc: "avatar_url",
384
+ // profileUrl: "https://facebook.com/john.doe",
385
+ // gender: "MALE", // MALE/FEMALE
386
+ // type: "user",
387
+ // isFriend: true,
388
+ // isBirthday: false
389
+ // }
390
+ // }
391
+ });
335
392
 
336
- login(
337
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
338
- (err, api) => {
393
+ // Get info for multiple users
394
+ api.getUserInfo(["100012345678901", "100012345678902"], (err, userInfo) => {
339
395
  if (err) return console.error(err);
340
396
 
341
- api.changeNickname("Example", "000000000000000", "000000000000000", err => {
342
- if (err) return console.error(err);
343
- });
344
- }
345
- );
397
+ for (let id in userInfo) {
398
+ console.log(userInfo[id].name);
399
+ }
400
+ });
346
401
  ```
347
402
 
348
403
  ---
349
404
 
350
- <a name="changeThreadColor"></a>
351
-
352
- ### api.changeThreadColor(color, threadID[, callback])
353
-
354
- Will change the thread color to the given hex string color ("#0000ff"). Set it
355
- to empty string if you want the default.
356
-
357
- Note: the color needs to start with a "#".
405
+ ### 3.4. getThreadInfo - Get Thread Information
358
406
 
359
- **Arguments**
407
+ Get information about conversation/group chat.
360
408
 
361
- - `color`: String representing a theme ID (a list of theme ID can be found at `api.threadColors`).
362
- - `threadID`: String representing the ID of the thread.
363
- - `callback(err)`: A callback called when the change is done (either with an error or null).
364
-
365
- **Example**
409
+ #### Syntax:
410
+ ```javascript
411
+ api.getThreadInfo(threadID, callback);
412
+ ```
366
413
 
367
- ```js
368
- const fs = require("fs");
369
- const login = require("fca-unofficial");
414
+ #### Example:
370
415
 
371
- login(
372
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
373
- (err, api) => {
374
- if (err) return console.error(err);
416
+ ```javascript
417
+ api.getThreadInfo("1234567890", (err, threadInfo) => {
418
+ if (err) {
419
+ console.error(err);
420
+ return;
421
+ }
375
422
 
376
- api.changeThreadColor("#0000ff", "000000000000000", err => {
377
- if (err) return console.error(err);
378
- });
379
- }
380
- );
423
+ console.log("Group name:", threadInfo.threadName);
424
+ console.log("Member count:", threadInfo.participantIDs.length);
425
+ console.log("Members list:", threadInfo.participantIDs);
426
+ console.log("Admins:", threadInfo.adminIDs);
427
+ console.log("Nicknames:", threadInfo.nicknames);
428
+ console.log("Chat color:", threadInfo.color);
429
+ console.log("Emoji:", threadInfo.emoji);
430
+ });
381
431
  ```
382
432
 
383
433
  ---
384
434
 
385
- <a name="changeThreadEmoji"></a>
435
+ ### 3.5. changeThreadColor - Change Chat Color
386
436
 
387
- ### api.changeThreadEmoji(emoji, threadID[, callback])
437
+ Change the color of conversation.
388
438
 
389
- Will change the thread emoji to the one provided.
439
+ #### Syntax:
440
+ ```javascript
441
+ api.changeThreadColor(color, threadID, callback);
442
+ ```
390
443
 
391
- Note: The UI doesn't play nice with all emoji.
444
+ #### Example:
445
+
446
+ ```javascript
447
+ // Color can be:
448
+ // "#0084ff" (Messenger Blue)
449
+ // "#44bec7" (Teal Blue)
450
+ // "#ffc300" (Yellow)
451
+ // "#fa3c4c" (Red)
452
+ // "#d696bb" (Pink)
453
+ // "#6699cc" (Sky Blue)
454
+ // "#13cf13" (Green)
455
+ // "#ff7e29" (Orange)
456
+ // "#e68585" (Light Red)
457
+ // "#7646ff" (Purple)
458
+ // "#20cef5" (Cyan)
459
+ // or any hex color code
460
+
461
+ api.changeThreadColor("#ffc300", "1234567890", (err) => {
462
+ if (err) {
463
+ console.error("Change color error:", err);
464
+ return;
465
+ }
466
+ console.log("Chat color changed successfully!");
467
+ });
468
+ ```
392
469
 
393
- **Arguments**
470
+ ---
394
471
 
395
- - `emoji`: String containing a single emoji character.
396
- - `threadID`: String representing the ID of the thread.
397
- - `callback(err)`: A callback called when the change is done (either with an error or null).
472
+ ### 3.6. changeThreadEmoji - Change Group Emoji
398
473
 
399
- **Example**
474
+ Change the default emoji of conversation.
400
475
 
401
- ```js
402
- const fs = require("fs");
403
- const login = require("fca-unofficial");
476
+ #### Syntax:
477
+ ```javascript
478
+ api.changeThreadEmoji(emoji, threadID, callback);
479
+ ```
404
480
 
405
- login(
406
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
407
- (err, api) => {
408
- if (err) return console.error(err);
481
+ #### Example:
409
482
 
410
- api.changeThreadEmoji("💯", "000000000000000", err => {
411
- if (err) return console.error(err);
412
- });
413
- }
414
- );
483
+ ```javascript
484
+ api.changeThreadEmoji("👍", "1234567890", (err) => {
485
+ if (err) {
486
+ console.error("Change emoji error:", err);
487
+ return;
488
+ }
489
+ console.log("Emoji changed successfully!");
490
+ });
415
491
  ```
416
492
 
417
493
  ---
418
494
 
419
- <a name="createNewGroup"></a>
495
+ ### 3.7. setTitle - Change Group Name
420
496
 
421
- ### api.createNewGroup(participantIDs[, groupTitle][, callback])
497
+ Change the name of group chat.
422
498
 
423
- Create a new group chat.
499
+ #### Syntax:
500
+ ```javascript
501
+ api.setTitle(newTitle, threadID, callback);
502
+ ```
424
503
 
425
- **Arguments**
504
+ #### Example:
426
505
 
427
- - `participantIDs`: An array containing participant IDs. (_Length must be >= 2_)
428
- - `groupTitle`: The title of the new group chat.
429
- - `callback(err, threadID)`: A callback called when created.
506
+ ```javascript
507
+ api.setTitle("New Chat Group", "1234567890", (err) => {
508
+ if (err) {
509
+ console.error("Change name error:", err);
510
+ return;
511
+ }
512
+ console.log("Group name changed successfully!");
513
+ });
514
+ ```
430
515
 
431
516
  ---
432
517
 
433
- <a name="createPoll"></a>
434
-
435
- ### api.createPoll(title, threadID[, options][, callback]) (_temporary deprecated because Facebook is updating this feature_)
518
+ ### 3.8. addUserToGroup - Add Member to Group
436
519
 
437
- Creates a poll with the specified title and optional poll options, which can also be initially selected by the logged-in user.
520
+ Add user to group chat.
438
521
 
439
- **Arguments**
440
-
441
- - `title`: String containing a title for the poll.
442
- - `threadID`: String representing the ID of the thread.
443
- - `options`: An optional `string : bool` dictionary to specify initial poll options and their initial states (selected/not selected), respectively.
444
- - `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.
522
+ #### Syntax:
523
+ ```javascript
524
+ api.addUserToGroup(userID, threadID, callback);
525
+ ```
445
526
 
446
- **Example**
527
+ #### Example:
447
528
 
448
- ```js
449
- const fs = require("fs");
450
- const login = require("fca-unofficial");
529
+ ```javascript
530
+ // Add 1 person
531
+ api.addUserToGroup("100012345678901", "1234567890", (err) => {
532
+ if (err) {
533
+ console.error("Add user error:", err);
534
+ return;
535
+ }
536
+ console.log("Member added successfully!");
537
+ });
451
538
 
452
- login(
453
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
454
- (err, api) => {
539
+ // Add multiple people
540
+ api.addUserToGroup(["100012345678901", "100012345678902"], "1234567890", (err) => {
455
541
  if (err) return console.error(err);
456
-
457
- api.createPoll(
458
- "Example Poll",
459
- "000000000000000",
460
- {
461
- "Option 1": false,
462
- "Option 2": true
463
- },
464
- err => {
465
- if (err) return console.error(err);
466
- }
467
- );
468
- }
469
- );
542
+ console.log("Multiple members added!");
543
+ });
470
544
  ```
471
545
 
472
546
  ---
473
547
 
474
- <a name="deleteMessage"></a>
548
+ ### 3.9. removeUserFromGroup - Remove Member from Group
475
549
 
476
- ### api.deleteMessage(messageOrMessages[, callback])
550
+ Remove user from group chat.
477
551
 
478
- Takes a messageID or an array of messageIDs and deletes the corresponding message.
479
-
480
- **Arguments**
552
+ #### Syntax:
553
+ ```javascript
554
+ api.removeUserFromGroup(userID, threadID, callback);
555
+ ```
481
556
 
482
- - `messageOrMessages`: A messageID string or messageID string array
483
- - `callback(err)`: A callback called when the query is done (either with an error or null).
557
+ #### Example:
484
558
 
485
- **Example**
559
+ ```javascript
560
+ api.removeUserFromGroup("100012345678901", "1234567890", (err) => {
561
+ if (err) {
562
+ console.error("Remove user error:", err);
563
+ return;
564
+ }
565
+ console.log("Member removed successfully!");
566
+ });
567
+ ```
486
568
 
487
- ```js
488
- const fs = require("fs");
489
- const login = require("fca-unofficial");
569
+ ---
490
570
 
491
- login(
492
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
493
- (err, api) => {
494
- if (err) return console.error(err);
571
+ ### 3.10. changeNickname - Change Nickname
495
572
 
496
- api.listen((err, message) => {
497
- if (message.body) {
498
- api.sendMessage(message.body, message.threadID, (err, messageInfo) => {
499
- if (err) return console.error(err);
573
+ Change user's nickname in group chat.
500
574
 
501
- api.deleteMessage(messageInfo.messageID);
502
- });
503
- }
504
- });
505
- }
506
- );
575
+ #### Syntax:
576
+ ```javascript
577
+ api.changeNickname(nickname, threadID, userID, callback);
507
578
  ```
508
579
 
509
- ---
580
+ #### Example:
510
581
 
511
- <a name="deleteThread"></a>
582
+ ```javascript
583
+ api.changeNickname("Admin Bot", "1234567890", "100012345678901", (err) => {
584
+ if (err) {
585
+ console.error("Change nickname error:", err);
586
+ return;
587
+ }
588
+ console.log("Nickname changed successfully!");
589
+ });
512
590
 
513
- ### api.deleteThread(threadOrThreads[, callback])
591
+ // Remove nickname (set to original name)
592
+ api.changeNickname("", "1234567890", "100012345678901", (err) => {
593
+ if (err) return console.error(err);
594
+ console.log("Nickname removed!");
595
+ });
596
+ ```
514
597
 
515
- 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.
598
+ ---
516
599
 
517
- **Arguments**
600
+ ### 3.11. markAsRead - Mark as Read
518
601
 
519
- - `threadOrThreads` - The id(s) of the threads you wish to remove from your account.
520
- - `callback(err)` - A callback called when the operation is done, maybe with an object representing an error.
602
+ Mark message as read.
521
603
 
522
- **Example**
604
+ #### Syntax:
605
+ ```javascript
606
+ api.markAsRead(threadID, callback);
607
+ ```
523
608
 
524
- ```js
525
- const fs = require("fs");
526
- const login = require("fca-unofficial");
609
+ #### Example:
527
610
 
528
- login(
529
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
530
- (err, api) => {
611
+ ```javascript
612
+ api.listenMqtt((err, event) => {
531
613
  if (err) return console.error(err);
532
614
 
533
- api.deleteThread("000000000000000", err => {
534
- if (err) return console.error(err);
535
- });
536
- }
537
- );
615
+ if (event.type === "message") {
616
+ // Auto mark as read
617
+ api.markAsRead(event.threadID, (err) => {
618
+ if (err) console.error("Mark as read error:", err);
619
+ });
620
+ }
621
+ });
538
622
  ```
539
623
 
540
624
  ---
541
625
 
542
- <a name="forwardAttachment"></a>
626
+ ### 3.12. markAsDelivered - Mark as Delivered
543
627
 
544
- ### api.forwardAttachment(attachmentID, userOrUsers[, callback])
628
+ Mark message as delivered.
545
629
 
546
- Forwards corresponding attachment to given userID or to every user from an array of userIDs
630
+ #### Syntax:
631
+ ```javascript
632
+ api.markAsDelivered(threadID, messageID, callback);
633
+ ```
547
634
 
548
- **Arguments**
635
+ #### Example:
549
636
 
550
- - `attachmentID`: The ID field in the attachment object. Recorded audio cannot be forwarded.
551
- - `userOrUsers`: A userID string or usersID string array
552
- - `callback(err)`: A callback called when the query is done (either with an error or null).
637
+ ```javascript
638
+ api.markAsDelivered("1234567890", "mid.xxx", (err) => {
639
+ if (err) {
640
+ console.error("Mark as delivered error:", err);
641
+ return;
642
+ }
643
+ console.log("Marked as delivered!");
644
+ });
645
+ ```
553
646
 
554
647
  ---
555
648
 
556
- <a name="getAppState"></a>
649
+ ### 3.13. markAsReadAll - Mark All as Read
650
+
651
+ Mark all messages as read.
652
+
653
+ #### Syntax:
654
+ ```javascript
655
+ api.markAsReadAll(callback);
656
+ ```
557
657
 
558
- ### api.getAppState()
658
+ #### Example:
559
659
 
560
- Returns current appState which can be saved to a file or stored in a variable.
660
+ ```javascript
661
+ api.markAsReadAll((err) => {
662
+ if (err) {
663
+ console.error("Error:", err);
664
+ return;
665
+ }
666
+ console.log("All messages marked as read!");
667
+ });
668
+ ```
561
669
 
562
670
  ---
563
671
 
564
- <a name="getCurrentUserID"></a>
672
+ ### 3.14. sendTypingIndicator - Show Typing Indicator
565
673
 
566
- ### api.getCurrentUserID()
674
+ Display "typing..." status in chat.
567
675
 
568
- Returns the currently logged-in user's Facebook user ID.
676
+ #### Syntax:
677
+ ```javascript
678
+ api.sendTypingIndicator(threadID, callback);
679
+ ```
569
680
 
570
- ---
681
+ #### Example:
571
682
 
572
- <a name="getEmojiUrl"></a>
683
+ ```javascript
684
+ // Show typing
685
+ api.sendTypingIndicator("1234567890", (err) => {
686
+ if (err) return console.error(err);
573
687
 
574
- ### api.getEmojiUrl(c, size[, pixelRatio])
688
+ // After 3 seconds, send message
689
+ setTimeout(() => {
690
+ api.sendMessage("Hello!", "1234567890");
691
+ }, 3000);
692
+ });
575
693
 
576
- Returns the URL to a Facebook Messenger-style emoji image asset.
694
+ // Or stop typing indicator
695
+ api.sendTypingIndicator("1234567890", (err) => {
696
+ if (err) return console.error(err);
697
+ }, true); // 3rd parameter is true to turn off typing
698
+ ```
577
699
 
578
- **note**: This function will return a URL regardless of whether the image at the URL actually exists.
579
- This can happen if, for example, Messenger does not have an image asset for the requested emoji.
700
+ ---
580
701
 
581
- **Arguments**
702
+ ### 3.15. unsendMessage - Unsend Message
582
703
 
583
- - `c` - The emoji character
584
- - `size` - The width and height of the emoji image; supported sizes are 32, 64, and 128
585
- - `pixelRatio` - The pixel ratio of the emoji image; supported ratios are '1.0' and '1.5' (default is '1.0')
704
+ Unsend/recall a sent message.
586
705
 
587
- **Example**
706
+ #### Syntax:
707
+ ```javascript
708
+ api.unsendMessage(messageID, callback);
709
+ ```
588
710
 
589
- ```js
590
- const fs = require("fs");
591
- const login = require("fca-unofficial");
711
+ #### Example:
592
712
 
593
- login(
594
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
595
- (err, api) => {
713
+ ```javascript
714
+ api.sendMessage("This message will be deleted", "1234567890", (err, messageInfo) => {
596
715
  if (err) return console.error(err);
597
716
 
598
- // Prints https://static.xx.fbcdn.net/images/emoji.php/v8/z9c/1.0/128/1f40d.png
599
- console.log("Snake emoji, 128px (128x128 with pixel ratio of 1.0");
600
- console.log(api.getEmojiUrl("\ud83d\udc0d", 128));
601
-
602
- // Prints https://static.xx.fbcdn.net/images/emoji.php/v8/ze1/1.5/128/1f40d.png
603
- console.log("Snake emoji, 192px (128x128 with pixel ratio of 1.5");
604
- console.log(api.getEmojiUrl("\ud83d\udc0d", 128, "1.5"));
605
- }
606
- );
717
+ // Unsend after 5 seconds
718
+ setTimeout(() => {
719
+ api.unsendMessage(messageInfo.messageID, (err) => {
720
+ if (err) {
721
+ console.error("Unsend error:", err);
722
+ return;
723
+ }
724
+ console.log("Message unsent!");
725
+ });
726
+ }, 5000);
727
+ });
607
728
  ```
608
729
 
609
730
  ---
610
731
 
611
- <a name="getFriendsList"></a>
732
+ ### 3.16. createPoll - Create Poll
612
733
 
613
- ### api.getFriendsList(callback)
734
+ Create poll in group chat.
614
735
 
615
- Returns an array of objects with some information about your friends.
736
+ #### Syntax:
737
+ ```javascript
738
+ api.createPoll(title, threadID, options, callback);
739
+ ```
616
740
 
617
- **Arguments**
741
+ #### Example:
618
742
 
619
- - `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`.
743
+ ```javascript
744
+ const title = "Choose travel destination?";
745
+ const options = {
746
+ "Da Lat": false, // false = allow multiple choices
747
+ "Nha Trang": false,
748
+ "Phu Quoc": false
749
+ };
620
750
 
621
- **Example**
751
+ api.createPoll(title, "1234567890", options, (err, pollInfo) => {
752
+ if (err) {
753
+ console.error("Create poll error:", err);
754
+ return;
755
+ }
756
+ console.log("Poll created successfully!");
757
+ });
758
+ ```
622
759
 
623
- ```js
624
- const fs = require("fs");
625
- const login = require("fca-unofficial");
760
+ ---
626
761
 
627
- login(
628
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
629
- (err, api) => {
630
- if (err) return console.error(err);
762
+ ### 3.17. handleMessageRequest - Handle Message Request
631
763
 
632
- api.getFriendsList((err, data) => {
633
- if (err) return console.error(err);
764
+ Accept or decline message from stranger.
634
765
 
635
- console.log(data.length);
636
- });
637
- }
638
- );
766
+ #### Syntax:
767
+ ```javascript
768
+ api.handleMessageRequest(threadID, accept, callback);
639
769
  ```
640
770
 
641
- ---
771
+ #### Example:
642
772
 
643
- <a name="getThreadHistory"></a>
773
+ ```javascript
774
+ // Accept message
775
+ api.handleMessageRequest("1234567890", true, (err) => {
776
+ if (err) {
777
+ console.error("Error:", err);
778
+ return;
779
+ }
780
+ console.log("Message accepted!");
781
+ });
644
782
 
645
- ### api.getThreadHistory(threadID, amount, timestamp, callback)
783
+ // Decline message
784
+ api.handleMessageRequest("1234567890", false, (err) => {
785
+ if (err) return console.error(err);
786
+ console.log("Message declined!");
787
+ });
788
+ ```
646
789
 
647
- Takes a threadID, number of messages, a timestamp, and a callback.
790
+ ---
648
791
 
649
- **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.
792
+ ### 3.18. muteThread - Mute Notifications
650
793
 
651
- **Arguments**
794
+ Mute or unmute notifications for conversation.
652
795
 
653
- - `threadID`: A threadID corresponding to the target chat
654
- - `amount`: The amount of messages to _request_
655
- - `timestamp`: Used to described the time of the most recent message to load. If timestamp is `undefined`, facebook will load the most recent messages.
656
- - `callback(error, history)`: If error is null, history will contain an array of message objects.
796
+ #### Syntax:
797
+ ```javascript
798
+ api.muteThread(threadID, muteSeconds, callback);
799
+ ```
657
800
 
658
- **Example**
801
+ #### Example:
659
802
 
660
- 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.
803
+ ```javascript
804
+ // Mute for 1 hour (3600 seconds)
805
+ api.muteThread("1234567890", 3600, (err) => {
806
+ if (err) {
807
+ console.error("Error:", err);
808
+ return;
809
+ }
810
+ console.log("Muted for 1 hour!");
811
+ });
661
812
 
662
- ```js
663
- var timestamp = undefined;
813
+ // Mute permanently
814
+ api.muteThread("1234567890", -1, (err) => {
815
+ if (err) return console.error(err);
816
+ console.log("Muted permanently!");
817
+ });
664
818
 
665
- function loadNextThreadHistory(api) {
666
- api.getThreadHistory(threadID, 50, timestamp, (err, history) => {
819
+ // Unmute
820
+ api.muteThread("1234567890", 0, (err) => {
667
821
  if (err) return console.error(err);
822
+ console.log("Unmuted!");
823
+ });
824
+ ```
825
+
826
+ ---
668
827
 
669
- /*
670
- Since the timestamp is from a previous loaded message,
671
- that message will be included in this history so we can discard it unless it is the first load.
672
- */
673
- if (timestamp != undefined) history.pop();
828
+ ### 3.19. getThreadList - Get Thread List
674
829
 
675
- /*
676
- Handle message history
677
- */
830
+ Get list of conversations.
678
831
 
679
- timestamp = history[0].timestamp;
680
- });
681
- }
832
+ #### Syntax:
833
+ ```javascript
834
+ api.getThreadList(limit, timestamp, tags, callback);
682
835
  ```
683
836
 
684
- ---
837
+ #### Example:
685
838
 
686
- <a name="getThreadInfo"></a>
687
-
688
- ### api.getThreadInfo(threadID[, callback])
689
-
690
- Takes a threadID and a callback. Works for both single-user and group threads.
691
-
692
- **Arguments**
693
-
694
- - `threadID`: A threadID corresponding to the target thread.
695
- - `callback(err, info)`: If `err` is `null`, `info` will contain the following properties:
696
-
697
- | Key | Description |
698
- | ----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
699
- | threadID | ID of the thread |
700
- | participantIDs | Array of user IDs in the thread |
701
- | threadName | 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. |
702
- | userInfo | An array contains info of members, which has the same structure as [`getUserInfo`](#getUserInfo), but add a key `id`, contain ID of member currently at. |
703
- | nicknames | Map of nicknames for members of the thread. If there are no nicknames set, this will be null. |
704
- | unreadCount | Number of unread messages |
705
- | messageCount | Number of messages |
706
- | imageSrc | URL to the group chat photo. Null if unset or a 1-1 thread. |
707
- | timestamp | Timestamp of last activity |
708
- | 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. |
709
- | isGroup | boolean, true if this thread is a group thread (more than 2 participants). |
710
- | isSubscribed | |
711
- | folder | The folder that the thread is in. Can be one of: <ul><li>'inbox'</li><li>'archive'</li></ul> |
712
- | isArchived | True if the thread is archived, false if not |
713
- | cannotReplyReason | If you cannot reply to this thread, this will be a string stating why. Otherwise it will be null. |
714
- | lastReadTimestamp | Timestamp of the last message that is marked as 'read' by the current user. |
715
- | emoji | Object with key 'emoji' whose value is the emoji unicode character. Null if unset. |
716
- | color | String form of the custom color in hexadecimal form. |
717
- | adminIDs | Array of user IDs of the admins of the thread. Empty array if unset. |
718
- | approvalMode | `true` or `false`, used to check if this group requires admin approval to add users |
719
- | approvalQueue | Array of object that has the following keys: <ul><li>`inviterID`: ID of the user invited the person to the group</li><li>`requesterID`: ID of the person waiting to be approved</li><li>`timestamp`: Request timestamp</li></ul> |
839
+ ```javascript
840
+ // Get 20 most recent conversations
841
+ api.getThreadList(20, null, ["INBOX"], (err, threads) => {
842
+ if (err) {
843
+ console.error("Error:", err);
844
+ return;
845
+ }
720
846
 
721
- ---
847
+ threads.forEach(thread => {
848
+ console.log("Thread ID:", thread.threadID);
849
+ console.log("Name:", thread.name);
850
+ console.log("Unread count:", thread.unreadCount);
851
+ console.log("Last message:", thread.snippet);
852
+ console.log("---");
853
+ });
854
+ });
722
855
 
723
- <a name="getThreadList"></a>
724
-
725
- ### api.getThreadList(limit, timestamp, tags, callback)
726
-
727
- Returns information about the user's threads.
728
-
729
- **Arguments**
730
-
731
- - `limit`: Limit the number of threads to fetch.
732
- - `timestamp`: Request threads _before_ this date. `null` means _now_
733
- - `tags`: An array describing which folder to fetch. It should be one of these:
734
- - `["INBOX"]` _(same as `[]`)_
735
- - `["ARCHIVED"]`
736
- - `["PENDING"]`
737
- - `["OTHER"]`
738
- - `["INBOX", "unread"]`
739
- - `["ARCHIVED", "unread"]`
740
- - `["PENDING", "unread"]`
741
- - `["OTHER", "unread"]`
742
-
743
- _if you find something new, let us know_
744
-
745
- - `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:
746
-
747
- **Thread list**
748
-
749
- | Key | Description |
750
- | -------------------- | ----------------------------------------------------------------------------------- |
751
- | threadID | ID of the thread |
752
- | name | The name of the thread |
753
- | unreadCount | Amount of unread messages in thread |
754
- | messageCount | Amount of messages in thread |
755
- | imageSrc | Link to the thread's image or `null` |
756
- | emoji | The default emoji in thread (classic like is `null`) |
757
- | color | Thread's message color in `RRGGBB` (default blue is `null`) |
758
- | nicknames | An array of `{"userid": "1234", "nickname": "John Doe"}` |
759
- | muteUntil | Timestamp until the mute expires or `null` |
760
- | participants | An array of participants. See below |
761
- | adminIDs | An array of thread admin IDs |
762
- | folder | `INBOX`, `ARCHIVED`, `PENDING` or `OTHER` |
763
- | isGroup | `true` or `false` |
764
- | customizationEnabled | `false` in one-to-one conversations with `Page` or `ReducedMessagingActor` |
765
- | participantAddMode | currently `"ADD"` for groups and `null` otherwise |
766
- | reactionsMuteMode | `REACTIONS_NOT_MUTED` or `REACTIONS_MUTED` |
767
- | mentionsMuteMode | `MENTIONS_NOT_MUTED` or `MENTIONS_MUTED` |
768
- | isArchived | `true` or `false` |
769
- | isSubscribed | `true` or `false` |
770
- | timestamp | timestamp in miliseconds |
771
- | snippet | Snippet's text message |
772
- | snippetAttachments | Attachments in snippet |
773
- | snippetSender | ID of snippet sender |
774
- | lastMessageTimestamp | timestamp in milliseconds |
775
- | lastReadTimestamp | timestamp in milliseconds or `null` |
776
- | cannotReplyReason | `null`, `"RECIPIENTS_NOT_LOADABLE"` or `"BLOCKED"` |
777
- | approvalMode | `true` or `false`, used to check if this group requires admin approval to add users |
778
-
779
- **`participants` format**
780
-
781
- `accountType` is one of the following:
782
-
783
- - `"User"`
784
- - `"Page"`
785
- - `"UnavailableMessagingActor"`
786
- - `"ReducedMessagingActor"`
787
-
788
- (_there might be more_)
789
-
790
- <table>
791
- <tr>
792
- <th>Account type</th>
793
- <th>Key</th>
794
- <th>Description</th>
795
- </tr>
796
- <tr>
797
- <td rowspan="12"><code>"User"</code></td>
798
- <td>userID</td>
799
- <td>ID of user</td>
800
- </tr>
801
- <tr>
802
- <td>name</td>
803
- <td>Full name of user</td>
804
- </tr>
805
- <tr>
806
- <td>shortName</td>
807
- <td>Short name of user (most likely first name)</td>
808
- </tr>
809
- <tr>
810
- <td>gender</td>
811
- <td>Either
812
- <code>"MALE"</code>,
813
- <code>"FEMALE"</code>,
814
- <code>"NEUTER"</code> or
815
- <code>"UNKNOWN"</code>
816
- </td>
817
- </tr>
818
- <tr>
819
- <td>url</td>
820
- <td>URL of the user's Facebook profile</td>
821
- </tr>
822
- <tr>
823
- <td>profilePicture</td>
824
- <td>URL of the profile picture</td>
825
- </tr>
826
- <tr>
827
- <td>username</td>
828
- <td>Username of user or
829
- <code>null</code>
830
- </td>
831
- </tr>
832
- <tr>
833
- <td>isViewerFriend</td>
834
- <td>Is the user a friend of you?</td>
835
- </tr>
836
- <tr>
837
- <td>isMessengerUser</td>
838
- <td>Does the user use Messenger?</td>
839
- </tr>
840
- <tr>
841
- <td>isVerified</td>
842
- <td>Is the user verified? (Little blue tick mark)</td>
843
- </tr>
844
- <tr>
845
- <td>isMessageBlockedByViewer</td>
846
- <td>Is the user blocking messages from you?</td>
847
- </tr>
848
- <tr>
849
- <td>isViewerCoworker</td>
850
- <td>Is the user your coworker?
851
- </td>
852
- </tr>
853
-
854
- <tr>
855
- <td rowspan="10"><code>"Page"</code></td>
856
- <td>userID</td>
857
- <td>ID of the page</td>
858
- </tr>
859
- <tr>
860
- <td>name</td>
861
- <td>Name of the fanpage</td>
862
- </tr>
863
- <tr>
864
- <td>url</td>
865
- <td>URL of the fanpage</td>
866
- </tr>
867
- <tr>
868
- <td>profilePicture</td>
869
- <td>URL of the profile picture</td>
870
- </tr>
871
- <tr>
872
- <td>username</td>
873
- <td>Username of user or
874
- <code>null</code>
875
- </td>
876
- </tr>
877
- <tr>
878
- <td>acceptsMessengerUserFeedback</td>
879
- <td></td>
880
- </tr>
881
- <tr>
882
- <td>isMessengerUser</td>
883
- <td>Does the fanpage use Messenger?</td>
884
- </tr>
885
- <tr>
886
- <td>isVerified</td>
887
- <td>Is the fanpage verified? (Little blue tick mark)</td>
888
- </tr>
889
- <tr>
890
- <td>isMessengerPlatformBot</td>
891
- <td>Is the fanpage a bot</td>
892
- </tr>
893
- <tr>
894
- <td>isMessageBlockedByViewer</td>
895
- <td>Is the fanpage blocking messages from you?</td>
896
- </tr>
897
-
898
- <tr>
899
- <td rowspan="7"><code>"ReducedMessagingActor"</code><br />(account requres verification,<br />messages are hidden)</td>
900
- <td>userID</td>
901
- <td>ID of the user</td>
902
- </tr>
903
- <tr>
904
- <td>name</td>
905
- <td>Name of the user</td>
906
- </tr>
907
- <tr>
908
- <td>url</td>
909
- <td>
910
- <code>null</code>
911
- </td>
912
- </tr>
913
- <tr>
914
- <td>profilePicture</td>
915
- <td>URL of the default Facebook profile picture</td>
916
- </tr>
917
- <tr>
918
- <td>username</td>
919
- <td>Username of user</td>
920
- </td>
921
- </tr>
922
- <tr>
923
- <td>acceptsMessengerUserFeedback</td>
924
- <td></td>
925
- </tr>
926
- <tr>
927
- <td>isMessageBlockedByViewer</td>
928
- <td>Is the user blocking messages from you?</td>
929
- </tr>
930
- <tr>
931
- <td rowspan="7"><code>"UnavailableMessagingActor"</code><br />(account disabled/removed)</td>
932
- <td>userID</td>
933
- <td>ID of the user</td>
934
- </tr>
935
- <tr>
936
- <td>name</td>
937
- <td><em>Facebook User</em> in user's language</td>
938
- </tr>
939
- <tr>
940
- <td>url</td>
941
- <td><code>null</code></td>
942
- </tr>
943
- <tr>
944
- <td>profilePicture</td>
945
- <td>URL of the default **male** Facebook profile picture</td>
946
- </tr>
947
- <tr>
948
- <td>username</td>
949
- <td><code>null</code></td>
950
- </tr>
951
- <tr>
952
- <td>acceptsMessengerUserFeedback</td>
953
- <td></td>
954
- </tr>
955
- <tr>
956
- <td>isMessageBlockedByViewer</td>
957
- <td>Is the user blocking messages from you?</td>
958
- </tr>
959
- </table>
960
-
961
- 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:
962
-
963
- | Key | Description |
964
- | ----------- | --------------------- |
965
- | accountType | type, can be anything |
966
- | userID | ID of the account |
967
- | name | Name of the account |
856
+ // Tags can be:
857
+ // - "INBOX" : Inbox
858
+ // - "ARCHIVED" : Archived
859
+ // - "PENDING" : Pending messages
860
+ // - "OTHER" : Other
861
+ ```
968
862
 
969
863
  ---
970
864
 
971
- <a name="getThreadPictures"></a>
865
+ ### 3.20. getThreadHistory - Get Message History
972
866
 
973
- ### api.getThreadPictures(threadID, offset, limit, callback)
867
+ Get message history of conversation.
974
868
 
975
- Returns pictures sent in the thread.
976
-
977
- **Arguments**
978
-
979
- - `threadID`: A threadID corresponding to the target chat
980
- - `offset`: Start index of picture to retrieve, where 0 is the most recent picture
981
- - `limit`: Number of pictures to get, incrementing from the offset index
982
- - `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`.
869
+ #### Syntax:
870
+ ```javascript
871
+ api.getThreadHistory(threadID, amount, timestamp, callback);
872
+ ```
983
873
 
984
- ---
874
+ #### Example:
985
875
 
986
- <a name="getUserID"></a>
876
+ ```javascript
877
+ // Get 50 most recent messages
878
+ api.getThreadHistory("1234567890", 50, null, (err, history) => {
879
+ if (err) {
880
+ console.error("Error:", err);
881
+ return;
882
+ }
987
883
 
988
- ### api.getUserID(name, callback)
884
+ history.forEach(msg => {
885
+ console.log("From:", msg.senderName);
886
+ console.log("Content:", msg.body);
887
+ console.log("Time:", new Date(msg.timestamp));
888
+ console.log("---");
889
+ });
890
+ });
989
891
 
990
- 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).
892
+ // Get older messages (pagination)
893
+ const oldestTimestamp = history[history.length - 1].timestamp;
894
+ api.getThreadHistory("1234567890", 50, oldestTimestamp, (err, olderHistory) => {
895
+ if (err) return console.error(err);
896
+ console.log("Retrieved 50 older messages!");
897
+ });
898
+ ```
991
899
 
992
- **Arguments**
900
+ ---
993
901
 
994
- - `name` - A string being the name of the item you're looking for.
995
- - `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).
902
+ ### 3.21. getThreadPictures - Get Thread Pictures
996
903
 
997
- **Example**
904
+ Get conversation/group avatar URL.
998
905
 
999
- ```js
1000
- const fs = require("fs");
1001
- const login = require("fca-unofficial");
906
+ #### Syntax:
907
+ ```javascript
908
+ api.getThreadPictures(threadID, offset, limit, callback);
909
+ ```
1002
910
 
1003
- login(
1004
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1005
- (err, api) => {
1006
- if (err) return console.error(err);
911
+ #### Example:
1007
912
 
1008
- api.getUserID("Marc Zuckerbot", (err, data) => {
1009
- if (err) return console.error(err);
913
+ ```javascript
914
+ api.getThreadPictures("1234567890", 0, 10, (err, pictures) => {
915
+ if (err) {
916
+ console.error("Error:", err);
917
+ return;
918
+ }
1010
919
 
1011
- // Send the message to the best match (best by Facebook's criteria)
1012
- var msg = "Hello!";
1013
- var threadID = data[0].userID;
1014
- api.sendMessage(msg, threadID);
920
+ pictures.forEach(pic => {
921
+ console.log("Image URL:", pic.url);
922
+ console.log("Width:", pic.width);
923
+ console.log("Height:", pic.height);
1015
924
  });
1016
- }
1017
- );
925
+ });
1018
926
  ```
1019
927
 
1020
928
  ---
1021
929
 
1022
- <a name="getUserInfo"></a>
930
+ ### 3.22. getUserID - Get User ID
1023
931
 
1024
- ### api.getUserInfo(ids, callback)
932
+ Get User ID from username or profile URL.
1025
933
 
1026
- Will get some information about the given users.
934
+ #### Syntax:
935
+ ```javascript
936
+ api.getUserID(name, callback);
937
+ ```
1027
938
 
1028
- **Arguments**
939
+ #### Example:
1029
940
 
1030
- - `ids` - Either a string/number for one ID or an array of strings/numbers for a batched query.
1031
- - `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`.
941
+ ```javascript
942
+ // From username
943
+ api.getUserID("john.doe", (err, data) => {
944
+ if (err) {
945
+ console.error("Error:", err);
946
+ return;
947
+ }
948
+ console.log("User ID:", data.userID);
949
+ });
1032
950
 
1033
- **Example**
951
+ // From profile URL
952
+ api.getUserID("https://facebook.com/john.doe", (err, data) => {
953
+ if (err) return console.error(err);
954
+ console.log("User ID:", data.userID);
955
+ });
956
+ ```
1034
957
 
1035
- ```js
1036
- const fs = require("fs");
1037
- const login = require("fca-unofficial");
958
+ ---
1038
959
 
1039
- login(
1040
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1041
- (err, api) => {
1042
- if (err) return console.error(err);
960
+ ### 3.23. getAppState - Get Current AppState
1043
961
 
1044
- api.getUserInfo([1, 2, 3, 4], (err, ret) => {
1045
- if (err) return console.error(err);
962
+ Get current AppState (cookies, session).
1046
963
 
1047
- for (var prop in ret) {
1048
- if (ret.hasOwnProperty(prop) && ret[prop].isBirthday) {
1049
- api.sendMessage("Happy birthday :)", prop);
1050
- }
1051
- }
1052
- });
1053
- }
1054
- );
964
+ #### Syntax:
965
+ ```javascript
966
+ const appState = api.getAppState();
1055
967
  ```
1056
968
 
1057
- ---
969
+ #### Example:
1058
970
 
1059
- <a name="threadColors"></a>
1060
-
1061
- ### api.threadColors
1062
-
1063
- A dictionary mapping names of all currently valid thread themes to their theme ID that are accepted by [`api.changeThreadColor`](#changeThreadColor). These themes, listed below, are the ones present in the palette UI used for selecting thread themes on the Messenger client.
1064
-
1065
- - DefaultBlue: `196241301102133`
1066
- - HotPink: `169463077092846`
1067
- - AquaBlue: `2442142322678320`
1068
- - BrightPurple: `234137870477637`
1069
- - CoralPink: `980963458735625`
1070
- - Orange: `175615189761153`
1071
- - Green: `2136751179887052`
1072
- - LavenderPurple: `2058653964378557`
1073
- - Red: `2129984390566328`
1074
- - Yellow: `174636906462322`
1075
- - TealBlue: `1928399724138152`
1076
- - Aqua: `417639218648241`
1077
- - Mango: `930060997172551`
1078
- - Berry: `164535220883264`
1079
- - Citrus: `370940413392601`
1080
- - Candy: `205488546921017`
1081
- - ~~StarWars: `809305022860427`~~ (Facebook removed it.)
971
+ ```javascript
972
+ const fs = require("fs");
973
+
974
+ // Get and save AppState
975
+ const appState = api.getAppState();
976
+ fs.writeFileSync("appstate.json", JSON.stringify(appState, null, 2));
977
+ console.log("✅ AppState saved!");
978
+
979
+ // Periodically update AppState (every 10 minutes)
980
+ setInterval(() => {
981
+ const updatedAppState = api.getAppState();
982
+ fs.writeFileSync("appstate.json", JSON.stringify(updatedAppState, null, 2));
983
+ console.log("🔄 AppState updated");
984
+ }, 10 * 60 * 1000);
985
+ ```
1082
986
 
1083
987
  ---
1084
988
 
1085
- <a name="handleMessageRequest"></a>
989
+ ### 3.24. deleteMessage - Delete Message (from your side)
1086
990
 
1087
- ### api.handleMessageRequest(threadID, accept[, callback])
991
+ Delete message from your side (not unsend).
1088
992
 
1089
- Accept or ignore message request(s) with thread id `threadID`.
993
+ #### Syntax:
994
+ ```javascript
995
+ api.deleteMessage(messageID, callback);
996
+ ```
1090
997
 
1091
- **Arguments**
998
+ #### Example:
1092
999
 
1093
- - `threadID`: A threadID or array of threadIDs corresponding to the target thread(s). Can be numbers or strings.
1094
- - `accept`: Boolean indicating the new status to assign to the message request(s); true for inbox, false to others.
1095
- - `callback(err)`: A callback called when the query is done (with an error or with null).
1000
+ ```javascript
1001
+ api.deleteMessage("mid.xxx", (err) => {
1002
+ if (err) {
1003
+ console.error("Delete message error:", err);
1004
+ return;
1005
+ }
1006
+ console.log("Message deleted!");
1007
+ });
1008
+ ```
1096
1009
 
1097
1010
  ---
1098
1011
 
1099
- <a name="listen"></a>
1100
-
1101
- ### api.listen([callback])
1102
-
1103
- <a name="listenMqtt"></a>
1104
-
1105
- ### api.listenMqtt([callback])
1106
-
1107
- Will call `callback` when a new message is received on this account.
1108
- 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 an `EventEmitter` that contains function `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.
1109
-
1110
- If `callback` is not defined, or isn't a `Function`, you can listen to messages with event `message` and `error` from `EventEmitter` returned by this function.
1111
-
1112
- **Arguments**
1113
-
1114
- - `callback(error, message)`: A callback called every time the logged-in account receives a new message.
1115
-
1116
- <a name="message"></a>
1117
- **Message**
1118
-
1119
- 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:
1120
-
1121
- <table>
1122
- <tr>
1123
- <th>Event Type</th>
1124
- <th>Field</th>
1125
- <th>Description</th>
1126
- </tr>
1127
- <tr>
1128
- <td rowspan="10">
1129
- <code>"message"</code><br />
1130
- A message was sent to a thread.
1131
- </td>
1132
- <td><code>attachments</code></td>
1133
- <td>An array of attachments to the message. Attachments vary in type, see the attachments table below.</td>
1134
- </tr>
1135
- <tr>
1136
- <td><code>body</code></td>
1137
- <td>The string corresponding to the message that was just received.</td>
1138
- </tr>
1139
- <tr>
1140
- <td><code>isGroup</code></td>
1141
- <td>boolean, true if this thread is a group thread (more than 2 participants).</td>
1142
- </tr>
1143
- <tr>
1144
- <td><code>mentions</code></td>
1145
- <td>An object containing people mentioned/tagged in the message in the format { id: name }</td>
1146
- </tr>
1147
- <tr>
1148
- <td><code>messageID</code></td>
1149
- <td>A string representing the message ID.</td>
1150
- </tr>
1151
- <tr>
1152
- <td><code>senderID</code></td>
1153
- <td>The id of the person who sent the message in the chat with threadID.</td>
1154
- </tr>
1155
- <tr>
1156
- <td><code>threadID</code></td>
1157
- <td>The threadID representing the thread in which the message was sent.</td>
1158
- </tr>
1159
- <tr>
1160
- <td><code>isUnread</code></td>
1161
- <td>Boolean representing whether or not the message was read.</td>
1162
- </tr>
1163
- <tr>
1164
- <td><code>participantIDs</code></td>
1165
- <td>An array containing participant IDs.</td>
1166
- </tr>
1167
- <tr>
1168
- <td><code>type</code></td>
1169
- <td>For this event type, this will always be the string <code>"message"</code>.</td>
1170
- </tr>
1171
- <tr>
1172
- <td rowspan="7">
1173
- <code>"event"</code><br />
1174
- An event occurred within a thread. Note that receiving this event type needs to be enabled with `api.setOptions({ listenEvents: true })`
1175
- </td>
1176
- <td><code>author</code></td>
1177
- <td>The person who performed the event.</td>
1178
- </tr>
1179
- <tr>
1180
- <td><code>logMessageBody</code></td>
1181
- <td>String printed in the chat.</td>
1182
- </tr>
1183
- <tr>
1184
- <td><code>logMessageData</code></td>
1185
- <td>Data relevant to the event.</td>
1186
- </tr>
1187
- <tr>
1188
- <td><code>logMessageType</code></td>
1189
- <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>, <code>log:thread-call</code>, <code>log:thread-admins</code>)</td>
1190
- </tr>
1191
- <tr>
1192
- <td><code>threadID</code></td>
1193
- <td>The threadID representing the thread in which the message was sent.</td>
1194
- </tr>
1195
- <tr>
1196
- <td><code>participantIDs</code></td>
1197
- <td>An array containing participant IDs.</td>
1198
- </tr>
1199
- <tr>
1200
- <td><code>type</code></td>
1201
- <td>For this event type, this will always be the string <code>"event"</code>.</td>
1202
- </tr>
1203
- <tr>
1204
- <td rowspan="5">
1205
- <code>"typ"</code><br />
1206
- A user in a thread is typing. Note that receiving this event type needs to be enabled with `api.setOptions({ listenTyping: true })`
1207
- </td>
1208
- <td><code>from</code></td>
1209
- <td>ID of the user who started/stopped typing.</td>
1210
- </tr>
1211
- <tr>
1212
- <td><code>fromMobile</code></td>
1213
- <td>Boolean representing whether or not the person's using a mobile device to type.</td>
1214
- </tr>
1215
- <tr>
1216
- <td><code>isTyping</code></td>
1217
- <td>Boolean representing whether or not a person started typing.</td>
1218
- </tr>
1219
- <tr>
1220
- <td><code>threadID</code></td>
1221
- <td>The threadID representing the thread in which a user is typing.</td>
1222
- </tr>
1223
- <tr>
1224
- <td><code>type</code></td>
1225
- <td>For this event type, this will always be the string <code>"typ"</code>.</td>
1226
- </tr>
1227
- <tr>
1228
- <td rowspan="3">
1229
- <code>"read"</code><br />
1230
- The current API user has read a message.
1231
- </td>
1232
- <td><code>threadID</code></td>
1233
- <td>The threadID representing the thread in which the message was sent.</td>
1234
- </tr>
1235
- <tr>
1236
- <td><code>time</code></td>
1237
- <td>The time at which the user read the message.</td>
1238
- </tr>
1239
- <tr>
1240
- <td><code>type</code></td>
1241
- <td>For this event type, this will always be the string <code>"read"</code>.</td>
1242
- </tr>
1243
- <tr>
1244
- <td rowspan="4">
1245
- <code>"read_receipt"</code><br />
1246
- A user within a thread has seen a message sent by the API user.
1247
- </td>
1248
- <td><code>reader</code></td>
1249
- <td>ID of the user who just read the message.</td>
1250
- </tr>
1251
- <tr>
1252
- <td><code>threadID</code></td>
1253
- <td>The thread in which the message was read.</td>
1254
- </tr>
1255
- <tr>
1256
- <td><code>time</code></td>
1257
- <td>The time at which the reader read the message.</td>
1258
- </tr>
1259
- <tr>
1260
- <td><code>type</code></td>
1261
- <td>For this event type, this will always be the string <code>"read_receipt"</code>.</td>
1262
- </tr>
1263
- <tr>
1264
- <td rowspan="8">
1265
- <code>"message_reaction"</code><br />
1266
- A user has sent a reaction to a message.
1267
- </td>
1268
- <td><code>messageID</code></td>
1269
- <td>The ID of the message</td>
1270
- </tr>
1271
- <tr>
1272
- <td><code>offlineThreadingID</code></td>
1273
- <td>The offline message ID</td>
1274
- </tr>
1275
- <tr>
1276
- <td><code>reaction</code></td>
1277
- <td>Contains reaction emoji</td>
1278
- </tr>
1279
- <tr>
1280
- <td><code>senderID</code></td>
1281
- <td>ID of the author the message, where has been reaction added</td>
1282
- </tr>
1283
- <tr>
1284
- <td><code>threadID</code></td>
1285
- <td>ID of the thread where the message has been sent</td>
1286
- </tr>
1287
- <tr>
1288
- <td><code>timestamp</code></td>
1289
- <td>Unix Timestamp (in miliseconds) when the reaction was sent</td>
1290
- </tr>
1291
- <tr>
1292
- <td><code>type</code></td>
1293
- <td>For this event type, this will always be the string <code>"message_reaction"</code>.</td>
1294
- </tr>
1295
- <tr>
1296
- <td><code>userID</code></td>
1297
- <td>ID of the reaction sender</td>
1298
- </tr>
1299
- <tr>
1300
- <td rowspan="4"><a name="presence"></a>
1301
- <code>"presence"</code><br />
1302
- 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>
1303
- </td>
1304
- <td><code>statuses</code></td>
1305
- <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>
1306
- </tr>
1307
- <tr>
1308
- <td><code>timestamp</code></td>
1309
- <td>The time when the user was last online.</td>
1310
- </tr>
1311
- <tr>
1312
- <td><code>type</code></td>
1313
- <td>For this event type, this will always be the string <code>"presence"</code>.</td>
1314
- </tr>
1315
- <tr>
1316
- <td><code>userID</code></td>
1317
- <td>The ID of the user whose status this packet is describing.</td>
1318
- </tr>
1319
- <tr>
1320
- <td rowspan="5">
1321
- <code>"message_unsend"</code><br />
1322
- A revoke message request for a message from a thread was received.
1323
- </td>
1324
- <td><code>threadID</code></td>
1325
- <td>The threadID representing the thread in which the revoke message request was received.</td>
1326
- </tr>
1327
- <tr>
1328
- <td><code>senderID</code></td>
1329
- <td>The id of the person who request to revoke message on threadID.</td>
1330
- </tr>
1331
- <tr>
1332
- <td><code>messageID</code></td>
1333
- <td>A string representing the message ID that the person request to revoke message want to.</td>
1334
- </tr>
1335
- <tr>
1336
- <td><code>deletionTimestamp</code></td>
1337
- <td>The time when the request was sent.</td>
1338
- </tr>
1339
- <tr>
1340
- <td><code>type</code></td>
1341
- <td>For this event type, this will always be the string <code>"message_unsend"</code>.</td>
1342
- </tr>
1343
- <tr>
1344
- <td rowspan="11">
1345
- <code>"message_reply"</code><br />
1346
- A reply message was sent to a thread.
1347
- </td>
1348
- <td><code>attachments</code></td>
1349
- <td>An array of attachments to the message. Attachments vary in type, see the attachments table below.</td>
1350
- </tr>
1351
- <tr>
1352
- <td><code>body</code></td>
1353
- <td>The string corresponding to the message that was just received.</td>
1354
- </tr>
1355
- <tr>
1356
- <td><code>isGroup</code></td>
1357
- <td>boolean, true if this thread is a group thread (more than 2 participants).</td>
1358
- </tr>
1359
- <tr>
1360
- <td><code>mentions</code></td>
1361
- <td>An object containing people mentioned/tagged in the message in the format { id: name }</td>
1362
- </tr>
1363
- <tr>
1364
- <td><code>messageID</code></td>
1365
- <td>A string representing the message ID.</td>
1366
- </tr>
1367
- <tr>
1368
- <td><code>senderID</code></td>
1369
- <td>The id of the person who sent the message in the chat with threadID.</td>
1370
- </tr>
1371
- <tr>
1372
- <td><code>threadID</code></td>
1373
- <td>The threadID representing the thread in which the message was sent.</td>
1374
- </tr>
1375
- <tr>
1376
- <td><code>isUnread</code></td>
1377
- <td>Boolean representing whether or not the message was read.</td>
1378
- </tr>
1379
- <tr>
1380
- <td><code>type</code></td>
1381
- <td>For this event type, this will always be the string <code>"message_reply"</code>.</td>
1382
- </tr>
1383
- <tr>
1384
- <td><code>participantIDs</code></td>
1385
- <td>An array containing participant IDs.</td>
1386
- </tr>
1387
- <tr>
1388
- <td><code>messageReply</code></td>
1389
- <td>An object represent a message being replied. Content inside is the same like a normal <code>"message"</code> event.</td>
1390
- </tr>
1391
- </table>
1392
-
1393
- **Attachments**
1394
-
1395
- 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:
1396
-
1397
- | Attachment Type | Fields |
1398
- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
1399
- | `"sticker"` | `ID`, `url`, `packID`, `spriteUrl`, `spriteUrl2x`, `width`, `height`, `caption`, `description`, `frameCount`, `frameRate`, `framesPerRow`, `framesPerCol` |
1400
- | `"file"` | `ID`, `filename`, `url`, `isMalicious`, `contentType` |
1401
- | `"photo"` | `ID`, `filename`, `thumbnailUrl`, `previewUrl`, `previewWidth`, `previewHeight`, `largePreviewUrl`, `largePreviewWidth`, `largePreviewHeight` |
1402
- | `"animated_image"` | `ID`, `filename`, `previewUrl`, `previewWidth`, `previewHeight`, `url`, `width`, `height` |
1403
- | `"video"` | `ID`, `filename`, `previewUrl`, `previewWidth`, `previewHeight`, `url`, `width`, `height`, `duration`, `videoType` |
1404
- | `"audio"` | `ID`, `filename`, `audioType`, `duration`, `url`, `isVoiceMail` |
1405
- | `"location"` | `ID`, `latitude`, `longitude`, `image`, `width`, `height`, `url`, `address` |
1406
- | `"share"` | `ID`, `url`, `title`, `description`, `source`, `image`, `width`, `height`, `playable`, `duration`, `playableUrl`, `subattachments`, `properties` |
1407
-
1408
- **Example**
1409
-
1410
- ```js
1411
- const fs = require("fs");
1412
- const login = require("fca-unofficial");
1012
+ ### 3.25. deleteThread - Delete Thread
1413
1013
 
1414
- // Simple echo bot. He'll repeat anything that you say.
1415
- // Will stop when you say '/stop'
1014
+ Delete conversation from your list.
1416
1015
 
1417
- login(
1418
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1419
- (err, api) => {
1420
- if (err) return console.error(err);
1421
-
1422
- api.setOptions({ listenEvents: true });
1016
+ #### Syntax:
1017
+ ```javascript
1018
+ api.deleteThread(threadID, callback);
1019
+ ```
1423
1020
 
1424
- var listenEmitter = api.listen((err, event) => {
1425
- if (err) return console.error(err);
1021
+ #### Example:
1426
1022
 
1427
- switch (event.type) {
1428
- case "message":
1429
- if (event.body === "/stop") {
1430
- api.sendMessage("Goodbye...", event.threadID);
1431
- return listenEmitter.stopListening();
1432
- }
1433
- api.markAsRead(event.threadID, err => {
1434
- if (err) console.log(err);
1435
- });
1436
- api.sendMessage("TEST BOT: " + event.body, event.threadID);
1437
- break;
1438
- case "event":
1439
- console.log(event);
1440
- break;
1441
- }
1442
- });
1443
- }
1444
- );
1023
+ ```javascript
1024
+ api.deleteThread("1234567890", (err) => {
1025
+ if (err) {
1026
+ console.error("Delete thread error:", err);
1027
+ return;
1028
+ }
1029
+ console.log("Thread deleted!");
1030
+ });
1445
1031
  ```
1446
1032
 
1447
1033
  ---
1448
1034
 
1449
- <a name="logout"></a>
1035
+ ### 3.26. forwardAttachment - Forward Attachment
1450
1036
 
1451
- ### api.logout([callback])
1037
+ Forward attachment from one message to another.
1452
1038
 
1453
- Logs out the current user.
1039
+ #### Syntax:
1040
+ ```javascript
1041
+ api.forwardAttachment(attachmentID, userOrThreadID, callback);
1042
+ ```
1043
+
1044
+ #### Example:
1454
1045
 
1455
- **Arguments**
1046
+ ```javascript
1047
+ api.listenMqtt((err, event) => {
1048
+ if (err) return console.error(err);
1456
1049
 
1457
- - `callback(err)`: A callback called when the query is done (either with an error or with null).
1050
+ if (event.type === "message" && event.attachments.length > 0) {
1051
+ // Forward first attachment
1052
+ const attachmentID = event.attachments[0].ID;
1458
1053
 
1459
- ---
1054
+ api.forwardAttachment(attachmentID, "100012345678901", (err) => {
1055
+ if (err) {
1056
+ console.error("Forward error:", err);
1057
+ return;
1058
+ }
1059
+ console.log("Attachment forwarded!");
1060
+ });
1061
+ }
1062
+ });
1063
+ ```
1460
1064
 
1461
- <a name="markAsDelivered"></a>
1065
+ ---
1462
1066
 
1463
- ### api.markAsDelivered(threadID, messageID[, callback]])
1067
+ ### 3.27. setMessageReaction - React to Message
1464
1068
 
1465
- 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.
1069
+ Add reaction (like, love, haha, wow, sad, angry) to message.
1466
1070
 
1467
- You can also mark new messages as delivered automatically. This is enabled by default. See [api.setOptions](#setOptions).
1071
+ #### Syntax:
1072
+ ```javascript
1073
+ api.setMessageReaction(reaction, messageID, callback);
1074
+ ```
1468
1075
 
1469
- **Arguments**
1076
+ #### Example:
1470
1077
 
1471
- - `threadID` - The id of the thread in which you want to mark the message as delivered.
1472
- - `messageID` - The id of the message want to mark as delivered.
1473
- - `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1078
+ ```javascript
1079
+ // Reaction can be:
1080
+ // "👍" or ":like:" - Like
1081
+ // "❤️" or ":love:" - Love
1082
+ // "😂" or ":haha:" - Haha
1083
+ // "😮" or ":wow:" - Wow
1084
+ // "😢" or ":sad:" - Sad
1085
+ // "😠" or ":angry:" - Angry
1086
+ // "" (empty string) - Remove reaction
1474
1087
 
1475
- **Example**
1088
+ api.listenMqtt((err, event) => {
1089
+ if (err) return console.error(err);
1476
1090
 
1477
- ```js
1478
- const fs = require("fs");
1479
- const login = require("facebook-chat-api");
1091
+ if (event.type === "message" && event.body === "React me") {
1092
+ api.setMessageReaction("❤️", event.messageID, (err) => {
1093
+ if (err) {
1094
+ console.error("React error:", err);
1095
+ return;
1096
+ }
1097
+ console.log("Message reacted!");
1098
+ });
1099
+ }
1100
+ });
1480
1101
 
1481
- login(
1482
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1483
- (err, api) => {
1102
+ // Remove reaction
1103
+ api.setMessageReaction("", "mid.xxx", (err) => {
1484
1104
  if (err) return console.error(err);
1105
+ console.log("Reaction removed!");
1106
+ });
1107
+ ```
1108
+
1109
+ ---
1485
1110
 
1486
- api.listen((err, message) => {
1487
- if (err) return console.error(err);
1111
+ ### 3.28. searchForThread - Search for Thread
1488
1112
 
1489
- // Marks messages as delivered immediately after they're received
1490
- api.markAsDelivered(message.threadID, message.messageID);
1113
+ Search for conversation by name.
1114
+
1115
+ #### Syntax:
1116
+ ```javascript
1117
+ api.searchForThread(name, callback);
1118
+ ```
1119
+
1120
+ #### Example:
1121
+
1122
+ ```javascript
1123
+ api.searchForThread("Study Group", (err, threads) => {
1124
+ if (err) {
1125
+ console.error("Search error:", err);
1126
+ return;
1127
+ }
1128
+
1129
+ threads.forEach(thread => {
1130
+ console.log("Name:", thread.name);
1131
+ console.log("Thread ID:", thread.threadID);
1132
+ console.log("Type:", thread.isGroup ? "Group" : "Personal");
1133
+ console.log("---");
1491
1134
  });
1492
- }
1493
- );
1135
+ });
1494
1136
  ```
1495
1137
 
1496
1138
  ---
1497
1139
 
1498
- <a name="markAsRead"></a>
1140
+ ### 3.29. logout - Logout
1141
+
1142
+ Logout from Facebook account.
1499
1143
 
1500
- ### api.markAsRead(threadID, [read[, callback]])
1144
+ #### Syntax:
1145
+ ```javascript
1146
+ api.logout(callback);
1147
+ ```
1501
1148
 
1502
- Given a threadID will mark all the unread messages in a thread as read. Facebook will take a couple of seconds to show that you've read the messages.
1149
+ #### Example:
1503
1150
 
1504
- You can also mark new messages as read automatically. See [api.setOptions](#setOptions). But be careful, this will make your account getting banned, especially when receiving _HUGE_ amount of messages.
1151
+ ```javascript
1152
+ api.logout((err) => {
1153
+ if (err) {
1154
+ console.error("Logout error:", err);
1155
+ return;
1156
+ }
1157
+ console.log("Logged out successfully!");
1158
+ });
1159
+ ```
1505
1160
 
1506
- **Arguments**
1161
+ ---
1507
1162
 
1508
- - `threadID` - The id of the thread in which you want to mark the messages as read.
1509
- - `read` - An optional boolean where `true` means to mark the message as being "read" and `false` means to mark the message as being "unread".
1510
- - `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1163
+ ### 3.30. getCurrentUserID - Get Current User ID
1511
1164
 
1512
- **Example**
1165
+ Get User ID of currently logged in account.
1513
1166
 
1514
- ```js
1515
- const fs = require("fs");
1516
- const login = require("fca-unofficial");
1167
+ #### Syntax:
1168
+ ```javascript
1169
+ const myUserID = api.getCurrentUserID();
1170
+ ```
1517
1171
 
1518
- login(
1519
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1520
- (err, api) => {
1172
+ #### Example:
1173
+
1174
+ ```javascript
1175
+ const myUserID = api.getCurrentUserID();
1176
+ console.log("Bot's User ID:", myUserID);
1177
+
1178
+ // Use to check if message is from bot
1179
+ api.listenMqtt((err, event) => {
1521
1180
  if (err) return console.error(err);
1522
1181
 
1523
- api.listen((err, message) => {
1524
- if (err) return console.error(err);
1182
+ if (event.type === "message") {
1183
+ if (event.senderID === myUserID) {
1184
+ console.log("This is a message from bot!");
1185
+ } else {
1186
+ console.log("Message from someone else");
1187
+ }
1188
+ }
1189
+ });
1190
+ ```
1525
1191
 
1526
- // Marks messages as read immediately after they're received
1527
- api.markAsRead(message.threadID);
1528
- });
1529
- }
1530
- );
1192
+ ---
1193
+
1194
+ ### 3.31. resolvePhotoUrl - Get High Quality Photo URL
1195
+
1196
+ Get high resolution photo URL from photo ID.
1197
+
1198
+ #### Syntax:
1199
+ ```javascript
1200
+ api.resolvePhotoUrl(photoID, callback);
1201
+ ```
1202
+
1203
+ #### Example:
1204
+
1205
+ ```javascript
1206
+ api.resolvePhotoUrl("1234567890123456", (err, url) => {
1207
+ if (err) {
1208
+ console.error("Error:", err);
1209
+ return;
1210
+ }
1211
+ console.log("High quality image URL:", url);
1212
+ });
1531
1213
  ```
1532
1214
 
1533
1215
  ---
1534
1216
 
1535
- <a name="markAsReadAll"></a>
1217
+ ### 3.32. changeArchivedStatus - Archive/Unarchive Thread
1536
1218
 
1537
- ### api.markAsReadAll([callback])
1219
+ Archive or unarchive conversation.
1220
+
1221
+ #### Syntax:
1222
+ ```javascript
1223
+ api.changeArchivedStatus(threadID, archive, callback);
1224
+ ```
1538
1225
 
1539
- This function will mark all of messages in your inbox readed.
1226
+ #### Example:
1227
+
1228
+ ```javascript
1229
+ // Archive conversation
1230
+ api.changeArchivedStatus("1234567890", true, (err) => {
1231
+ if (err) {
1232
+ console.error("Error:", err);
1233
+ return;
1234
+ }
1235
+ console.log("Thread archived!");
1236
+ });
1237
+
1238
+ // Unarchive
1239
+ api.changeArchivedStatus("1234567890", false, (err) => {
1240
+ if (err) return console.error(err);
1241
+ console.log("Thread unarchived!");
1242
+ });
1243
+ ```
1540
1244
 
1541
1245
  ---
1542
1246
 
1543
- <a name="markAsSeen"></a>
1247
+ ### 3.33. changeBlockedStatus - Block/Unblock User
1248
+
1249
+ Block or unblock user.
1250
+
1251
+ #### Syntax:
1252
+ ```javascript
1253
+ api.changeBlockedStatus(userID, block, callback);
1254
+ ```
1544
1255
 
1545
- ### api.markAsSeen([seenTimestamp][, callback])
1256
+ #### Example:
1546
1257
 
1547
- This function will mark your entire inbox as seen (don't be confused with read!).
1258
+ ```javascript
1259
+ // Block user
1260
+ api.changeBlockedStatus("100012345678901", true, (err) => {
1261
+ if (err) {
1262
+ console.error("Error:", err);
1263
+ return;
1264
+ }
1265
+ console.log("User blocked!");
1266
+ });
1267
+
1268
+ // Unblock
1269
+ api.changeBlockedStatus("100012345678901", false, (err) => {
1270
+ if (err) return console.error(err);
1271
+ console.log("User unblocked!");
1272
+ });
1273
+ ```
1548
1274
 
1549
1275
  ---
1550
1276
 
1551
- <a name="muteThread"></a>
1277
+ ### 3.34. createNewGroup - Create New Group
1278
+
1279
+ Create new group chat with member list.
1280
+
1281
+ #### Syntax:
1282
+ ```javascript
1283
+ api.createNewGroup(participantIDs, groupTitle, callback);
1284
+ ```
1552
1285
 
1553
- ### api.muteThread(threadID, muteSeconds[, callback])
1286
+ #### Example:
1554
1287
 
1555
- Mute a chat for a period of time, or unmute a chat.
1288
+ ```javascript
1289
+ const members = ["100012345678901", "100012345678902", "100012345678903"];
1290
+ const groupName = "New Chat Group";
1556
1291
 
1557
- **Arguments**
1292
+ api.createNewGroup(members, groupName, (err, threadID) => {
1293
+ if (err) {
1294
+ console.error("Create group error:", err);
1295
+ return;
1296
+ }
1297
+ console.log("Group created successfully!");
1298
+ console.log("Thread ID:", threadID);
1558
1299
 
1559
- - `threadID` - The ID of the chat you want to mute.
1560
- - `muteSeconds` - Mute the chat for this amount of seconds. Use `0` to unmute a chat. Use '-1' to mute a chat indefinitely.
1561
- - `callback(err)` - A callback called when the operation is done maybe with an object representing an error.
1300
+ // Send message to new group
1301
+ api.sendMessage("Welcome to the group!", threadID);
1302
+ });
1303
+ ```
1562
1304
 
1563
- **Example**
1305
+ ---
1564
1306
 
1565
- ```js
1307
+ ## 4. COMPLETE BOT EXAMPLES
1308
+
1309
+ ### 4.1. Echo Bot (Message Repeater)
1310
+
1311
+ ```javascript
1566
1312
  const fs = require("fs");
1567
- const login = require("fca-unofficial");
1313
+ const login = require("@dongdev/fca-unofficial");
1568
1314
 
1315
+ // Login
1569
1316
  login(
1570
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1571
- (err, api) => {
1572
- if (err) return console.error(err);
1317
+ { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1318
+ (err, api) => {
1319
+ if (err) {
1320
+ console.error("Login error:", err);
1321
+ return;
1322
+ }
1573
1323
 
1574
- api.listen((err, message) => {
1575
- if (err) return console.error(err);
1324
+ console.log("✅ Bot started!");
1576
1325
 
1577
- // Mute all incoming chats for one minute
1578
- api.muteThread(message.threadID, 60);
1579
- });
1580
- }
1326
+ // Configuration
1327
+ api.setOptions({
1328
+ listenEvents: true,
1329
+ selfListen: false,
1330
+ logLevel: "silent"
1331
+ });
1332
+
1333
+ // Listen for messages
1334
+ api.listenMqtt((err, event) => {
1335
+ if (err) return console.error(err);
1336
+
1337
+ if (event.type === "message") {
1338
+ const { body, threadID, messageID, senderID } = event;
1339
+
1340
+ // Stop bot command
1341
+ if (body === "/stop") {
1342
+ api.sendMessage("Bot stopped!", threadID);
1343
+ process.exit(0);
1344
+ }
1345
+
1346
+ // Echo message
1347
+ api.sendMessage(`📣 Echo: ${body}`, threadID, messageID);
1348
+ }
1349
+ });
1350
+ }
1581
1351
  );
1582
1352
  ```
1583
1353
 
1584
1354
  ---
1585
1355
 
1586
- <a name="removeUserFromGroup"></a>
1356
+ ### 4.2. Group Management Bot
1587
1357
 
1588
- ### api.removeUserFromGroup(userID, threadID[, callback])
1358
+ ```javascript
1359
+ const fs = require("fs");
1360
+ const login = require("@dongdev/fca-unofficial");
1589
1361
 
1590
- Removes a user from a group chat.
1362
+ // Admin list (User IDs)
1363
+ const ADMINS = ["100012345678901", "100012345678902"];
1591
1364
 
1592
- **Arguments**
1365
+ // Check admin permission
1366
+ function isAdmin(userID) {
1367
+ return ADMINS.includes(userID);
1368
+ }
1593
1369
 
1594
- - `userID`: User ID.
1595
- - `threadID`: Group chat ID.
1596
- - `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
1370
+ login(
1371
+ { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1372
+ (err, api) => {
1373
+ if (err) return console.error(err);
1597
1374
 
1598
- ---
1375
+ console.log("✅ Group management bot started!");
1376
+
1377
+ api.setOptions({ listenEvents: true });
1378
+
1379
+ api.listenMqtt((err, event) => {
1380
+ if (err) return console.error(err);
1381
+
1382
+ const { type, threadID, senderID, body, messageID } = event;
1383
+
1384
+ // Handle messages
1385
+ if (type === "message") {
1386
+ // Only admins can use commands
1387
+ if (!isAdmin(senderID)) {
1388
+ if (body.startsWith("/")) {
1389
+ api.sendMessage(
1390
+ "❌ You don't have permission to use this command!",
1391
+ threadID,
1392
+ messageID
1393
+ );
1394
+ }
1395
+ return;
1396
+ }
1397
+
1398
+ // Kick command
1399
+ if (body.startsWith("/kick ")) {
1400
+ const userID = body.split(" ")[1];
1401
+ api.removeUserFromGroup(userID, threadID, (err) => {
1402
+ if (err) {
1403
+ api.sendMessage("❌ Error kicking user!", threadID);
1404
+ } else {
1405
+ api.sendMessage("✅ User kicked!", threadID);
1406
+ }
1407
+ });
1408
+ }
1409
+
1410
+ // Rename command
1411
+ else if (body.startsWith("/rename ")) {
1412
+ const newName = body.substring(8);
1413
+ api.setTitle(newName, threadID, (err) => {
1414
+ if (err) {
1415
+ api.sendMessage("❌ Error renaming group!", threadID);
1416
+ } else {
1417
+ api.sendMessage(`✅ Group renamed to: ${newName}`, threadID);
1418
+ }
1419
+ });
1420
+ }
1421
+
1422
+ // Group info command
1423
+ else if (body === "/info") {
1424
+ api.getThreadInfo(threadID, (err, info) => {
1425
+ if (err) return api.sendMessage("❌ Error getting info!", threadID);
1426
+
1427
+ const message = `
1428
+ 📊 GROUP INFORMATION
1429
+ ━━━━━━━━━━━━━━━
1430
+ 👥 Name: ${info.threadName}
1431
+ 📝 Members: ${info.participantIDs.length}
1432
+ 👑 Admins: ${info.adminIDs.length}
1433
+ 🎨 Color: ${info.color}
1434
+ 😊 Emoji: ${info.emoji || "Default"}
1435
+ `.trim();
1436
+
1437
+ api.sendMessage(message, threadID);
1438
+ });
1439
+ }
1440
+
1441
+ // Help command
1442
+ else if (body === "/help") {
1443
+ const helpMessage = `
1444
+ 🤖 COMMAND LIST
1445
+ ━━━━━━━━━━━━━━━
1446
+ /kick [userID] - Kick member
1447
+ /rename [new name] - Rename group
1448
+ /info - View group info
1449
+ /help - Show help
1450
+ `.trim();
1451
+
1452
+ api.sendMessage(helpMessage, threadID);
1453
+ }
1454
+ }
1599
1455
 
1600
- <a name="resolvePhotoUrl"></a>
1456
+ // Handle events
1457
+ else if (type === "event") {
1458
+ // Welcome new members
1459
+ if (event.logMessageType === "log:subscribe") {
1460
+ const addedUsers = event.logMessageData.addedParticipants;
1461
+ addedUsers.forEach(user => {
1462
+ api.sendMessage(
1463
+ `👋 Welcome ${user.fullName} to the group!`,
1464
+ threadID
1465
+ );
1466
+ });
1467
+ }
1468
+
1469
+ // Notify when someone leaves
1470
+ else if (event.logMessageType === "log:unsubscribe") {
1471
+ api.sendMessage(`👋 Goodbye! A member left the group.`, threadID);
1472
+ }
1473
+ }
1474
+ });
1475
+ }
1476
+ );
1477
+ ```
1601
1478
 
1602
- ### api.resolvePhotoUrl(photoID, callback)
1479
+ ---
1603
1480
 
1604
- Resolves the URL to the full-size photo, given its ID. This function is useful for retrieving the full-size photo URL
1605
- of image attachments in messages, returned by [`api.getThreadHistory`](#getThreadHistory).
1481
+ ### 4.3. AI ChatBot Style (Mock)
1606
1482
 
1607
- **Arguments**
1483
+ ```javascript
1484
+ const fs = require("fs");
1485
+ const login = require("@dongdev/fca-unofficial");
1608
1486
 
1609
- - `photoID`: Photo ID.
1610
- - `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.
1487
+ // Store chat history by threadID
1488
+ const chatHistory = {};
1611
1489
 
1612
- ---
1490
+ // Mock AI response function
1491
+ function getAIResponse(message, threadID) {
1492
+ // Initialize history if not exists
1493
+ if (!chatHistory[threadID]) {
1494
+ chatHistory[threadID] = [];
1495
+ }
1613
1496
 
1614
- <a name="searchForThread"></a>
1497
+ // Add user message to history
1498
+ chatHistory[threadID].push({ role: "user", content: message });
1615
1499
 
1616
- ### api.searchForThread(name, callback)
1500
+ // Limit history to last 10 messages
1501
+ if (chatHistory[threadID].length > 10) {
1502
+ chatHistory[threadID] = chatHistory[threadID].slice(-10);
1503
+ }
1617
1504
 
1618
- > This part is outdated.
1619
- > see #396
1505
+ // Mock response (you can integrate real ChatGPT API here)
1506
+ let response = "";
1507
+
1508
+ if (message.toLowerCase().includes("hello")) {
1509
+ response = "Hello! How can I help you?";
1510
+ } else if (message.toLowerCase().includes("name")) {
1511
+ response = "I'm an AI Assistant Bot!";
1512
+ } else if (message.toLowerCase().includes("weather")) {
1513
+ response = "Sorry, I don't have weather information. Please check weather apps!";
1514
+ } else {
1515
+ response = `I received your message: "${message}". Thank you for chatting with me!`;
1516
+ }
1620
1517
 
1621
- Takes a chat title (thread name) and returns matching results as a formatted threads array (ordered according to Facebook).
1518
+ // Add response to history
1519
+ chatHistory[threadID].push({ role: "assistant", content: response });
1622
1520
 
1623
- **Arguments**
1521
+ return response;
1522
+ }
1624
1523
 
1625
- - `name`: A messageID string or messageID string array
1626
- - `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`
1524
+ login(
1525
+ { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1526
+ (err, api) => {
1527
+ if (err) return console.error(err);
1627
1528
 
1628
- ---
1529
+ console.log("✅ AI Bot started!");
1629
1530
 
1630
- <a name="sendMessage"></a>
1531
+ api.setOptions({
1532
+ listenEvents: true,
1533
+ selfListen: false
1534
+ });
1631
1535
 
1632
- ### api.sendMessage(message, threadID[, callback][, messageid])
1536
+ api.listenMqtt((err, event) => {
1537
+ if (err) return console.error(err);
1633
1538
 
1634
- Sends the given message to the threadID.
1539
+ if (event.type === "message" && event.body) {
1540
+ const { body, threadID, messageID } = event;
1635
1541
 
1636
- **Arguments**
1542
+ // Ignore if doesn't start with "ai" prefix
1543
+ if (!body.toLowerCase().startsWith("ai ")) {
1544
+ return;
1545
+ }
1637
1546
 
1638
- - `message`: A string (for backward compatibility) or a message object as described below.
1639
- - `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.
1640
- - `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.
1641
- - `messageID`: (Optional) A string representing a message you want to reply.
1547
+ // Get message content (remove "ai " prefix)
1548
+ const userMessage = body.substring(3).trim();
1642
1549
 
1643
- **Message Object**:
1550
+ // Show typing indicator
1551
+ api.sendTypingIndicator(threadID);
1644
1552
 
1645
- Various types of message can be sent:
1553
+ // Delay for more natural feel
1554
+ setTimeout(() => {
1555
+ const aiResponse = getAIResponse(userMessage, threadID);
1556
+ api.sendMessage(`🤖 ${aiResponse}`, threadID, messageID);
1557
+ }, 1500);
1558
+ }
1559
+ });
1560
+ }
1561
+ );
1562
+ ```
1646
1563
 
1647
- - _Regular:_ set field `body` to the desired message as a string.
1648
- - _Sticker:_ set a field `sticker` to the desired sticker ID.
1649
- - _File or image:_ Set field `attachment` to a readable stream or an array of readable streams.
1650
- - _URL:_ set a field `url` to the desired URL.
1651
- - _Emoji:_ set field `emoji` to the desired emoji as a string and set field `emojiSize` with size of the emoji (`small`, `medium`, `large`)
1652
- - _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`
1653
- can be passed in to specify the start index to start searching for the `tag` text
1654
- in `body` (default=0). (See below for an example.)
1655
- - _Location:_ set field `location` to an object with `latitude` and `longitude` fields. Optionally set field `current` of the `location` object to true to indicate the location is the user’s current location. Otherwise the location will be sent as a pinned location.
1564
+ ---
1656
1565
 
1657
- 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.
1566
+ ### 4.4. Auto-Reply Bot with Keywords
1658
1567
 
1659
- **Tip**: to find your own ID, you can look inside the cookies. The `userID` is under the name `c_user`.
1568
+ ```javascript
1569
+ const fs = require("fs");
1570
+ const login = require("@dongdev/fca-unofficial");
1571
+
1572
+ // Auto-reply dictionary
1573
+ const autoReplies = {
1574
+ "hello": "Hi there! How can I help you?",
1575
+ "hi": "Hello! What's up?",
1576
+ "bye": "Goodbye! See you later!",
1577
+ "thanks": "You're welcome! 😊",
1578
+ "help": "I'm here to assist! Just ask me anything.",
1579
+ "time": () => `Current time: ${new Date().toLocaleTimeString()}`,
1580
+ "date": () => `Today's date: ${new Date().toLocaleDateString()}`
1581
+ };
1660
1582
 
1661
- **Example (Basic Message)**
1583
+ function getAutoReply(message) {
1584
+ const lowerMessage = message.toLowerCase().trim();
1662
1585
 
1663
- ```js
1664
- const fs = require("fs");
1665
- const login = require("fca-unofficial");
1586
+ // Check for exact matches
1587
+ for (let keyword in autoReplies) {
1588
+ if (lowerMessage.includes(keyword)) {
1589
+ const reply = autoReplies[keyword];
1590
+ // If reply is function, execute it
1591
+ return typeof reply === 'function' ? reply() : reply;
1592
+ }
1593
+ }
1594
+
1595
+ return null; // No match found
1596
+ }
1666
1597
 
1667
1598
  login(
1668
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1669
- (err, api) => {
1670
- if (err) return console.error(err);
1599
+ { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1600
+ (err, api) => {
1601
+ if (err) return console.error(err);
1602
+
1603
+ console.log("✅ Auto-reply bot started!");
1604
+
1605
+ api.setOptions({
1606
+ listenEvents: true,
1607
+ selfListen: false
1608
+ });
1671
1609
 
1672
- var yourID = "000000000000000";
1673
- var msg = { body: "Hey!" };
1674
- api.sendMessage(msg, yourID);
1675
- }
1610
+ api.listenMqtt((err, event) => {
1611
+ if (err) return console.error(err);
1612
+
1613
+ if (event.type === "message" && event.body) {
1614
+ const { body, threadID, messageID } = event;
1615
+
1616
+ const reply = getAutoReply(body);
1617
+
1618
+ if (reply) {
1619
+ api.sendMessage(reply, threadID, messageID);
1620
+ }
1621
+ }
1622
+ });
1623
+ }
1676
1624
  );
1677
1625
  ```
1678
1626
 
1679
- **Example (File upload)**
1627
+ ---
1680
1628
 
1681
- ```js
1629
+ ### 4.5. Command Handler Bot
1630
+
1631
+ ```javascript
1682
1632
  const fs = require("fs");
1683
- const login = require("fca-unofficial");
1633
+ const login = require("@dongdev/fca-unofficial");
1634
+
1635
+ // Command prefix
1636
+ const PREFIX = "/";
1637
+
1638
+ // Commands object
1639
+ const commands = {
1640
+ ping: {
1641
+ description: "Check bot latency",
1642
+ execute: (api, event) => {
1643
+ const start = Date.now();
1644
+ api.sendMessage("Pong! 🏓", event.threadID, (err) => {
1645
+ if (!err) {
1646
+ const latency = Date.now() - start;
1647
+ api.sendMessage(`Latency: ${latency}ms`, event.threadID);
1648
+ }
1649
+ });
1650
+ }
1651
+ },
1652
+
1653
+ userinfo: {
1654
+ description: "Get user information",
1655
+ execute: (api, event) => {
1656
+ api.getUserInfo(event.senderID, (err, userInfo) => {
1657
+ if (err) return api.sendMessage("Error getting user info!", event.threadID);
1658
+
1659
+ const user = userInfo[event.senderID];
1660
+ const info = `
1661
+ 👤 USER INFO
1662
+ ━━━━━━━━━━━━
1663
+ Name: ${user.name}
1664
+ Gender: ${user.gender}
1665
+ Profile: ${user.profileUrl}
1666
+ `.trim();
1667
+
1668
+ api.sendMessage(info, event.threadID);
1669
+ });
1670
+ }
1671
+ },
1672
+
1673
+ time: {
1674
+ description: "Get current time",
1675
+ execute: (api, event) => {
1676
+ const now = new Date();
1677
+ api.sendMessage(`🕐 Current time: ${now.toLocaleString()}`, event.threadID);
1678
+ }
1679
+ },
1680
+
1681
+ help: {
1682
+ description: "Show command list",
1683
+ execute: (api, event) => {
1684
+ let helpText = "📋 AVAILABLE COMMANDS\n━━━━━━━━━━━━━━━\n";
1685
+
1686
+ for (let cmd in commands) {
1687
+ helpText += `${PREFIX}${cmd} - ${commands[cmd].description}\n`;
1688
+ }
1689
+
1690
+ api.sendMessage(helpText, event.threadID);
1691
+ }
1692
+ }
1693
+ };
1684
1694
 
1685
1695
  login(
1686
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1687
- (err, api) => {
1688
- if (err) return console.error(err);
1696
+ { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1697
+ (err, api) => {
1698
+ if (err) return console.error(err);
1699
+
1700
+ console.log("✅ Command handler bot started!");
1689
1701
 
1690
- // This example uploads an image called image.jpg
1691
- var yourID = "000000000000000";
1692
- var msg = {
1693
- body: "Hey!",
1694
- attachment: fs.createReadStream(__dirname + "/image.jpg")
1695
- };
1696
- api.sendMessage(msg, yourID);
1697
- }
1702
+ api.setOptions({ listenEvents: true, selfListen: false });
1703
+
1704
+ api.listenMqtt((err, event) => {
1705
+ if (err) return console.error(err);
1706
+
1707
+ if (event.type === "message" && event.body) {
1708
+ const { body, threadID } = event;
1709
+
1710
+ // Check if message starts with prefix
1711
+ if (!body.startsWith(PREFIX)) return;
1712
+
1713
+ // Parse command
1714
+ const args = body.slice(PREFIX.length).trim().split(/ +/);
1715
+ const commandName = args.shift().toLowerCase();
1716
+
1717
+ // Execute command
1718
+ if (commands[commandName]) {
1719
+ try {
1720
+ commands[commandName].execute(api, event, args);
1721
+ } catch (error) {
1722
+ console.error("Command execution error:", error);
1723
+ api.sendMessage("Error executing command!", threadID);
1724
+ }
1725
+ } else {
1726
+ api.sendMessage(`Unknown command: ${commandName}\nUse ${PREFIX}help for command list`, threadID);
1727
+ }
1728
+ }
1729
+ });
1730
+ }
1698
1731
  );
1699
1732
  ```
1700
1733
 
1701
- **Example (Mention)**
1734
+ ---
1735
+
1736
+ ## 5. ERROR HANDLING & BEST PRACTICES
1702
1737
 
1703
- ```js
1704
- const login = require("fca-unofficial");
1738
+ ### 5.1. Handle Checkpoint/Security Check
1705
1739
 
1706
- login({ email: "EMAIL", password: "PASSWORD" }, (err, api) => {
1707
- if (err) return console.error(err);
1740
+ When Facebook detects unusual activity, account may be checkpointed:
1708
1741
 
1709
- api.listen((err, message) => {
1710
- if (message && message.body) {
1711
- // Getting the actual sender name from ID involves calling
1712
- // `api.getThreadInfo` and `api.getUserInfo`
1713
- api.sendMessage(
1714
- {
1715
- body: "Hello @Sender! @Sender!",
1716
- mentions: [
1717
- {
1718
- tag: "@Sender",
1719
- id: message.senderID,
1720
- fromIndex: 9 // Highlight the second occurrence of @Sender
1721
- }
1722
- ]
1723
- },
1724
- message.threadID
1725
- );
1742
+ ```javascript
1743
+ login(credentials, (err, api) => {
1744
+ if (err) {
1745
+ switch (err.error) {
1746
+ case "login-approval":
1747
+ console.log("❗ 2FA code required");
1748
+ // Handle 2FA
1749
+ break;
1750
+
1751
+ case "checkpoint":
1752
+ console.log("❌ Account checkpointed!");
1753
+ console.log("Please login via browser and verify");
1754
+ break;
1755
+
1756
+ default:
1757
+ console.error("Login error:", err);
1758
+ }
1759
+ return;
1726
1760
  }
1727
- });
1728
1761
  });
1729
1762
  ```
1730
1763
 
1731
- **Example (Location)**
1764
+ ---
1765
+
1766
+ ### 5.2. Auto-save AppState
1767
+
1768
+ ```javascript
1769
+ // Save AppState every 10 minutes
1770
+ setInterval(() => {
1771
+ try {
1772
+ const appState = api.getAppState();
1773
+ fs.writeFileSync("appstate.json", JSON.stringify(appState, null, 2));
1774
+ console.log("🔄 AppState updated");
1775
+ } catch (error) {
1776
+ console.error("Error saving AppState:", error);
1777
+ }
1778
+ }, 10 * 60 * 1000);
1779
+ ```
1780
+
1781
+ ---
1782
+
1783
+ ### 5.3. Connection Error Handling
1732
1784
 
1733
- ```js
1734
- const login = require("fca-unofficial");
1735
- login({ email: "EMAIL", password: "PASSWORD" }, (err, api) => {
1736
- if (err) return console.error(err);
1737
- var yourID = "000000000000000";
1738
- const msg = {
1739
- location: { latitude: 48.858093, longitude: 2.294694, current: true }
1740
- };
1741
- api.sendMessage(msg, yourID);
1785
+ ```javascript
1786
+ api.listenMqtt((err, event) => {
1787
+ if (err) {
1788
+ console.error("Listen error:", err);
1789
+
1790
+ // Retry connection after 5 seconds
1791
+ setTimeout(() => {
1792
+ console.log("🔄 Reconnecting...");
1793
+ api.listenMqtt(arguments.callee);
1794
+ }, 5000);
1795
+ return;
1796
+ }
1797
+
1798
+ // Handle events normally
1742
1799
  });
1743
1800
  ```
1744
1801
 
1745
1802
  ---
1746
1803
 
1747
- <a name="sendTypingIndicator"></a>
1804
+ ### 5.4. Rate Limiting (Avoid Spam)
1805
+
1806
+ ```javascript
1807
+ const MESSAGE_COOLDOWN = {}; // Store last message time
1748
1808
 
1749
- ### api.sendTypingIndicator(threadID[, callback])
1809
+ function canSendMessage(threadID, cooldownTime = 1000) {
1810
+ const now = Date.now();
1811
+ const lastTime = MESSAGE_COOLDOWN[threadID] || 0;
1750
1812
 
1751
- 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`.
1813
+ if (now - lastTime < cooldownTime) {
1814
+ return false;
1815
+ }
1752
1816
 
1753
- **Arguments**
1817
+ MESSAGE_COOLDOWN[threadID] = now;
1818
+ return true;
1819
+ }
1754
1820
 
1755
- - `threadID`: Group chat ID.
1756
- - `callback(err)`: A callback called when the query is done (with an error or with null).
1821
+ api.listenMqtt((err, event) => {
1822
+ if (err) return console.error(err);
1823
+
1824
+ if (event.type === "message") {
1825
+ // Check cooldown
1826
+ if (!canSendMessage(event.threadID, 2000)) {
1827
+ console.log("⏱️ On cooldown...");
1828
+ return;
1829
+ }
1830
+
1831
+ // Handle message
1832
+ api.sendMessage("Response", event.threadID);
1833
+ }
1834
+ });
1835
+ ```
1757
1836
 
1758
1837
  ---
1759
1838
 
1760
- <a name="setMessageReaction"></a>
1839
+ ### 5.5. Logging and Debug
1761
1840
 
1762
- ### api.setMessageReaction(reaction, messageID[, callback[, forceCustomReaction]])
1841
+ ```javascript
1842
+ // Enable detailed logging
1843
+ api.setOptions({
1844
+ logLevel: "verbose" // silent/error/warn/info/verbose
1845
+ });
1763
1846
 
1764
- Sets reaction on message
1847
+ // Custom logger
1848
+ function log(type, message, data = {}) {
1849
+ const timestamp = new Date().toISOString();
1850
+ console.log(`[${timestamp}] [${type.toUpperCase()}] ${message}`, data);
1851
+ }
1765
1852
 
1766
- **Arguments**
1853
+ api.listenMqtt((err, event) => {
1854
+ if (err) {
1855
+ log("error", "Listen error", err);
1856
+ return;
1857
+ }
1767
1858
 
1768
- - `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.
1769
- - `messageID`: A string representing the message ID.
1770
- - `callback(err)`: A callback called when sending the reaction is done.
1771
- - `forceCustomReaction`: Forcing the use of an emoji for setting reaction **(WARNING: NOT TESTED, YOU SHOULD NOT USE THIS AT ALL, UNLESS YOU'RE TESTING A NEW EMOJI)**
1859
+ log("info", "New event", {
1860
+ type: event.type,
1861
+ threadID: event.threadID
1862
+ });
1863
+ });
1864
+ ```
1865
+
1866
+ ---
1772
1867
 
1773
- **Supported Emojis**
1868
+ ### 5.6. Environment Variables for Credentials
1774
1869
 
1775
- | Emoji | Text | Unicode | Shortcuts |
1776
- | ----- | ---- | -------------- | --------------------------- |
1777
- | 😍 | `😍` | `\uD83D\uDE0D` | `:love:`, `:heart_eyes:` |
1778
- | 😆 | `😆` | `\uD83D\uDE06` | `:haha:`, `:laughing:` |
1779
- | 😮 | `😮` | `\uD83D\uDE2E` | `:wow:`, `:open_mouth:` |
1780
- | 😢 | `😢` | `\uD83D\uDE22` | `:sad:`, `:cry:` |
1781
- | 😠 | `😠` | `\uD83D\uDE20` | `:angry:` |
1782
- | 👍 | `👍` | `\uD83D\uDC4D` | `:like:`, `:thumbsup:` |
1783
- | 👎 | `👎` | `\uD83D\uDC4E` | `:dislike:`, `:thumbsdown:` |
1784
- | ❤ | `❤` | `\u2764` | `:heart:` |
1785
- | 💗 | `💗` | `\uD83D\uDC97` | `:glowingheart:` |
1870
+ ```javascript
1871
+ require('dotenv').config();
1872
+
1873
+ const credentials = {
1874
+ email: process.env.FB_EMAIL,
1875
+ password: process.env.FB_PASSWORD
1876
+ };
1877
+
1878
+ // Or use AppState
1879
+ const credentials = {
1880
+ appState: JSON.parse(fs.readFileSync(process.env.APPSTATE_PATH, "utf8"))
1881
+ };
1882
+ ```
1883
+
1884
+ **.env file:**
1885
+ ```
1886
+ FB_EMAIL=your_email@example.com
1887
+ FB_PASSWORD=your_password
1888
+ APPSTATE_PATH=./appstate.json
1889
+ ```
1786
1890
 
1787
1891
  ---
1788
1892
 
1789
- <a name="setOptions"></a>
1893
+ ## 6. IMPORTANT NOTES
1790
1894
 
1791
- ### api.setOptions(options)
1895
+ ### ⚠️ Security Warnings
1792
1896
 
1793
- Sets various configurable options for the api.
1897
+ 1. **Never share AppState**: The `appstate.json` file contains login information, never make it public
1898
+ 2. **Use .gitignore**: Add `appstate.json` to `.gitignore`
1899
+ 3. **Avoid hardcoding passwords**: Use environment variables or config files
1900
+ 4. **Keep dependencies updated**: Regularly update the package
1794
1901
 
1795
- **Arguments**
1902
+ ### 📝 Best Practices
1796
1903
 
1797
- - `options` - An object containing the new values for the options that you want
1798
- to set. If the value for an option is unspecified, it is unchanged. The following options are possible.
1799
- - `pauseLog`: (Default `false`) Set this to `true` if you want to pause the npmlog output.
1800
- - `logLevel`: The desired logging level as determined by npmlog. Choose
1801
- from either `"silly"`, `"verbose"`, `"info"`, `"http"`, `"warn"`, `"error"`, or `"silent"`.
1802
- - `selfListen`: (Default `false`) Set this to `true` if you want your api
1803
- to receive messages from its own account. This is to be used with
1804
- caution, as it can result in loops (a simple echo bot will send messages
1805
- forever).
1806
- - `listenEvents`: (Default `false`) Will make [api.listen](#listen) also handle events (look at api.listen for more details).
1807
- - `pageID`: (Default empty) Makes [api.listen](#listen) only receive messages through the page specified by that ID. Also makes [api.sendMessage](#sendMessage) send from the page.
1808
- - `updatePresence`: (Default `false`) Will make [api.listen](#listen) also return `presence` ([api.listen](#presence) for more details).
1809
- - `forceLogin`: (Default `false`) Will automatically approve of any recent logins and continue with the login process.
1810
- - `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. - `autoMarkDelivery`: (Default `true`) Will automatically mark new messages as delivered. See [api.markAsDelivered](#markAsDelivered). - `autoMarkRead`: (Default `false`) Will automatically mark new messages as read/seen. See [api.markAsRead](#markAsRead). - `proxy`: (Default empty) Set this to proxy server address to use proxy. Note: Only HTTP Proxies which support CONNECT method is supported. - `online`: (Default `true`) Set account's online state.
1904
+ 1. **Use AppState instead of email/password**: Reduces checkpoint risk
1905
+ 2. **Don't spam**: Avoid sending too many messages in short time
1906
+ 3. **Complete error handling**: Always have callbacks to handle errors
1907
+ 4. **Rate limiting**: Limit number of messages/requests
1908
+ 5. **Log activities**: Keep logs for debugging
1811
1909
 
1812
- **Example**
1910
+ ### 🚫 Avoid Getting Banned
1813
1911
 
1814
- ```js
1815
- const fs = require("fs");
1816
- const login = require("fca-unofficial");
1912
+ - Don't login/logout repeatedly
1913
+ - Don't mass message strangers
1914
+ - Don't send spam links
1915
+ - Use real browser User-Agent
1916
+ - Limit requests per minute
1917
+ - Be a responsible Facebook citizen
1918
+
1919
+ ---
1817
1920
 
1818
- // Simple echo bot. This will send messages forever.
1921
+ ## 7. TROUBLESHOOTING
1819
1922
 
1820
- login(
1821
- { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) },
1822
- (err, api) => {
1823
- if (err) return console.error(err);
1923
+ ### Error: "Wrong username/password"
1924
+ - Check email and password
1925
+ - Try logging in manually via browser
1926
+ - Account may be checkpointed
1824
1927
 
1825
- api.setOptions({
1826
- selfListen: true,
1827
- logLevel: "silent"
1828
- });
1928
+ ### Error: "Login approval needed"
1929
+ - Account has 2FA enabled
1930
+ - Provide 2FA verification code
1829
1931
 
1830
- api.listen((err, message) => {
1831
- if (err) return console.error(err);
1932
+ ### Error: "Checkpoint required"
1933
+ - Login to Facebook via browser
1934
+ - Complete verification steps
1935
+ - Get new AppState after verification
1832
1936
 
1833
- // Ignore empty messages (photos etc.)
1834
- if (typeof message.body === "string") {
1835
- api.sendMessage(message.body, message.threadID);
1836
- }
1837
- });
1838
- }
1839
- );
1937
+ ### Bot not receiving messages
1938
+ - Check internet connection
1939
+ - Check logs for detailed errors
1940
+ - Try restarting bot
1941
+ - Update AppState
1942
+
1943
+ ### Messages sending too slowly
1944
+ - Implement message queue
1945
+ - Use rate limiting
1946
+ - Check network latency
1947
+
1948
+ ---
1949
+
1950
+ ## 8. ADVANCED FEATURES
1951
+
1952
+ ### 8.1. Message Queue System
1953
+
1954
+ ```javascript
1955
+ class MessageQueue {
1956
+ constructor(api) {
1957
+ this.api = api;
1958
+ this.queue = [];
1959
+ this.processing = false;
1960
+ this.delay = 1000; // 1 second delay between messages
1961
+ }
1962
+
1963
+ add(message, threadID, messageID) {
1964
+ this.queue.push({ message, threadID, messageID });
1965
+ if (!this.processing) {
1966
+ this.process();
1967
+ }
1968
+ }
1969
+
1970
+ async process() {
1971
+ this.processing = true;
1972
+
1973
+ while (this.queue.length > 0) {
1974
+ const { message, threadID, messageID } = this.queue.shift();
1975
+
1976
+ await new Promise((resolve) => {
1977
+ this.api.sendMessage(message, threadID, messageID, (err) => {
1978
+ if (err) console.error("Send error:", err);
1979
+ setTimeout(resolve, this.delay);
1980
+ });
1981
+ });
1982
+ }
1983
+
1984
+ this.processing = false;
1985
+ }
1986
+ }
1987
+
1988
+ // Usage
1989
+ const messageQueue = new MessageQueue(api);
1990
+
1991
+ api.listenMqtt((err, event) => {
1992
+ if (err) return console.error(err);
1993
+
1994
+ if (event.type === "message") {
1995
+ messageQueue.add("Response", event.threadID, event.messageID);
1996
+ }
1997
+ });
1840
1998
  ```
1841
1999
 
1842
2000
  ---
1843
2001
 
1844
- <a name="setTitle"></a>
2002
+ ### 8.2. Multi-Account Bot Manager
2003
+
2004
+ ```javascript
2005
+ const fs = require("fs");
2006
+ const login = require("@dongdev/fca-unofficial");
2007
+
2008
+ class BotManager {
2009
+ constructor() {
2010
+ this.bots = new Map();
2011
+ }
2012
+
2013
+ async addBot(name, appStatePath) {
2014
+ return new Promise((resolve, reject) => {
2015
+ const credentials = {
2016
+ appState: JSON.parse(fs.readFileSync(appStatePath, "utf8"))
2017
+ };
2018
+
2019
+ login(credentials, (err, api) => {
2020
+ if (err) {
2021
+ reject(err);
2022
+ return;
2023
+ }
2024
+
2025
+ this.bots.set(name, api);
2026
+ console.log(`✅ Bot "${name}" connected`);
2027
+ resolve(api);
2028
+ });
2029
+ });
2030
+ }
2031
+
2032
+ getBot(name) {
2033
+ return this.bots.get(name);
2034
+ }
1845
2035
 
1846
- ### api.setTitle(newTitle, threadID[, callback])
2036
+ getAllBots() {
2037
+ return Array.from(this.bots.values());
2038
+ }
2039
+ }
1847
2040
 
1848
- Sets the title of the group chat with thread id `threadID` to `newTitle`.
2041
+ // Usage
2042
+ const manager = new BotManager();
1849
2043
 
1850
- 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.
2044
+ (async () => {
2045
+ await manager.addBot("bot1", "./appstate1.json");
2046
+ await manager.addBot("bot2", "./appstate2.json");
1851
2047
 
1852
- **Arguments**
2048
+ const bot1 = manager.getBot("bot1");
2049
+ const bot2 = manager.getBot("bot2");
1853
2050
 
1854
- - `newTitle`: A string representing the new title.
1855
- - `threadID`: A string or number representing a thread. It happens to be someone's userID in the case of a one to one conversation.
1856
- - `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.
2051
+ // Use bots independently
2052
+ bot1.sendMessage("Message from Bot 1", threadID);
2053
+ bot2.sendMessage("Message from Bot 2", threadID);
2054
+ })();
2055
+ ```
1857
2056
 
1858
2057
  ---
1859
2058
 
1860
- <a name="unsendMessage"></a>
2059
+ ### 8.3. Database Integration (SQLite Example)
1861
2060
 
1862
- ### api.unsendMessage(messageID[, callback])
2061
+ ```javascript
2062
+ const sqlite3 = require('sqlite3').verbose();
2063
+ const db = new sqlite3.Database('./bot.db');
1863
2064
 
1864
- Revokes a message from anyone that could see that message with `messageID`
2065
+ // Initialize database
2066
+ db.serialize(() => {
2067
+ db.run(`CREATE TABLE IF NOT EXISTS users (
2068
+ user_id TEXT PRIMARY KEY,
2069
+ username TEXT,
2070
+ message_count INTEGER DEFAULT 0,
2071
+ last_interaction TEXT
2072
+ )`);
2073
+ });
1865
2074
 
1866
- Note: This will only work if the message is sent by you and was sent less than 10 minutes ago.
2075
+ // Track user messages
2076
+ function trackUser(userID, username) {
2077
+ db.run(`
2078
+ INSERT INTO users (user_id, username, message_count, last_interaction)
2079
+ VALUES (?, ?, 1, datetime('now'))
2080
+ ON CONFLICT(user_id) DO UPDATE SET
2081
+ message_count = message_count + 1,
2082
+ last_interaction = datetime('now')
2083
+ `, [userID, username]);
2084
+ }
1867
2085
 
1868
- **Arguments**
2086
+ // Get user stats
2087
+ function getUserStats(userID, callback) {
2088
+ db.get('SELECT * FROM users WHERE user_id = ?', [userID], callback);
2089
+ }
1869
2090
 
1870
- - `messageID`: Message ID you want to unsend.
1871
- - `callback(err)`: A callback called when the query is done (with an error or with null).
2091
+ // Usage in bot
2092
+ api.listenMqtt((err, event) => {
2093
+ if (err) return console.error(err);
2094
+
2095
+ if (event.type === "message") {
2096
+ // Track user
2097
+ api.getUserInfo(event.senderID, (err, info) => {
2098
+ if (!err) {
2099
+ const username = info[event.senderID].name;
2100
+ trackUser(event.senderID, username);
2101
+ }
2102
+ });
2103
+
2104
+ // Stats command
2105
+ if (event.body === "/stats") {
2106
+ getUserStats(event.senderID, (err, row) => {
2107
+ if (err || !row) return;
2108
+
2109
+ const stats = `
2110
+ 📊 YOUR STATS
2111
+ ━━━━━━━━━━━━
2112
+ Messages: ${row.message_count}
2113
+ Last seen: ${row.last_interaction}
2114
+ `.trim();
2115
+
2116
+ api.sendMessage(stats, event.threadID);
2117
+ });
2118
+ }
2119
+ }
2120
+ });
2121
+ ```
2122
+
2123
+ ---
2124
+
2125
+ ## 9. RESOURCES
2126
+
2127
+ - **GitHub Repository**: https://github.com/Donix-VN/fca-unofficial
2128
+ - **NPM Package**: @dongdev/fca-unofficial
2129
+ - **AppState Tool**: https://github.com/c3cbot/c3c-fbstate
2130
+ - **Facebook Developer Docs**: https://developers.facebook.com/docs/messenger-platform
1872
2131
 
1873
2132
  ---
2133
+
2134
+ ## Conclusion
2135
+
2136
+ This is a comprehensive documentation for **@dongdev/fca-unofficial** API methods. This library is very powerful but should be used carefully to avoid violating Facebook's policies and getting your account banned.
2137
+
2138
+ **Happy bot coding! 🚀**