unofficial-fb-chat-api
Version:
A Facebook chat API that doesn't rely on XMPP
1,244 lines (980 loc) • 75.1 kB
Markdown
# Documentation
### You can use callback or .then() .catch() or async/await
* [`login(credentials, options, [callback])`](#logincredentials-options-callback) ⇒ <code>Promise</code>
* [`api.addUserToGroup(userID, threadID, [callback])`](#apiaddusertogroupuserid-threadid-callback) ⇒ <code>Promise</code>
* [`api.changeAdminStatus(threadID, adminIDs, adminStatus, [callback])`](#apichangeadminstatusthreadid-adminids-adminstatus-callback) ⇒ <code>Promise</code>
* [`api.changeArchivedStatus(threadOrThreads, archive, [callback])`](#apichangearchivedstatusthreadorthreads-archive-callback) ⇒ <code>Promise</code>
* [`api.changeBlockedStatus(userID, block, [callback])`](#apichangeblockedstatususerid-block-callback) ⇒ <code>Promise</code>
* [`api.changeGroupImage(image, threadID, [callback])`](#apichangegroupimageimage-threadid-callback) ⇒ <code>Promise</code>
* [`api.changeNickname(nickname, threadID, participantID, [callback])`](#apichangenicknamenickname-threadid-participantid-callback) ⇒ <code>Promise</code>
* [`api.changeThreadColor(color, threadID, [callback])`](#apichangethreadcolorcolor-threadid-callback) ⇒ <code>Promise</code>
* [`api.changeThreadEmoji(emoji, threadID, [callback])`](#apichangethreademojiemoji-threadid-callback) ⇒ <code>Promise</code>
* [`api.createNewGroup(participantIDs, groupTitle, [callback])`](#apicreatenewgroupparticipantids-grouptitle-callback) ⇒ <code>Promise</code>
* [`api.createPoll(title, threadID, options, [callback]) (*temporary deprecated because Facebook is updating this feature*)`](#apicreatepolltitle-threadid-options-callback-temporary-deprecated-because-facebook-is-updating-this-feature) ⇒ <code>Promise</code>
* [`api.deleteMessage(messageOrMessages, [callback])`](#apideletemessagemessageormessages-callback) ⇒ <code>Promise</code>
* [`api.deleteThread(threadOrThreads, [callback])`](#apideletethreadthreadorthreads-callback) ⇒ <code>Promise</code>
* [`api.forwardAttachment(attachmentID, userOrUsers, [callback])`](#apiforwardattachmentattachmentid-userorusers-callback) ⇒ <code>Promise</code>
* [`api.getAppState()`](#apigetappstate) ⇒ <code>Array</code>
* [`api.getCurrentUserID()`](#apigetcurrentuserid) ⇒ <code>string</code>
* [`api.getEmojiUrl(c, size, pixelRatio)`](#apigetemojiurlc-size-pixelratio) ⇒ <code>string</code>
* [`api.getFriendsList([callback])`](#apigetfriendslistcallback) ⇒ <code>Promise</code>
* [`api.getMessage(threadID, messageID, [callback])`](#apigetmessagethreadid-messageid-callback) ⇒ <code>Promise</code>
* [`api.getThreadHistory(threadID, amount, timestamp, [callback])`](#apigetthreadhistorythreadid-amount-timestamp-callback) ⇒ <code>Promise</code>
* [`api.getThreadInfo(threadIDs, [callback])`](#apigetthreadinfothreadids-callback) ⇒ <code>Promise</code>
* [`api.getThreadList(limit, timestamp, tags, [callback])`](#apigetthreadlistlimit-timestamp-tags-callback) ⇒ <code>Promise</code>
* [`api.getThreadPictures(threadID, offset, limit, [callback])`](#apigetthreadpicturesthreadid-offset-limit-callback) ⇒ <code>Promise</code>
* [`api.getUserID(name, [callback])`](#apigetuseridname-callback) ⇒ <code>Promise</code>
* [`api.getUserInfo(ids, [callback])`](#apigetuserinfoids-callback) ⇒ <code>Promise</code>
* [`api.handleMessageRequest(threadID, accept, [callback])`](#apihandlemessagerequestthreadid-accept-callback) ⇒ <code>Promise</code>
* [`api.httpGet(url, form, [customHeader], [callback], [notAPI])`](#apihttpgeturl-form-customheader-callback-notapi) ⇒ <code>Promise</code>
* [`api.httpPost(url, form, [customHeader], [callback], [notAPI])`](#apihttpposturl-form-customheader-callback-notapi) ⇒ <code>Promise</code>
* [`api.httpPostFormData(url, form, [customHeader], [callback], [notAPI])`](#apihttppostformdataurl-form-customheader-callback-notapi) ⇒ <code>Promise</code>
* [~~`api.listen([callback])`~~](#apilistencallback) ⇒ <code>Promise</code>
* [`api.listenMqtt([callback])`](#apilistenmqttcallback) ⇒ <code>Promise</code>
* [`api.logout([callback])`](#apilogoutcallback) ⇒ <code>Promise</code>
* [`api.markAsDelivered(threadID, messageID, [callback]`](#apimarkasdeliveredthreadid-messageid-callback) ⇒ <code>Promise</code>
* [`api.markAsRead(threadID, [read, [callback]])`](#apimarkasreadthreadid-read-callback) ⇒ <code>Promise</code>
* [`api.markAsReadAll([callback])`](#apimarkasreadallcallback) ⇒ <code>Promise</code>
* [`api.markAsSeen([seenTimestamp], [callback])`](#apimarkasseenseentimestamp-callback) ⇒ <code>Promise</code>
* [`api.muteThread(threadID, muteSeconds, [callback])`](#apimutethreadthreadid-muteseconds-callback) ⇒ <code>Promise</code>
* [`api.removeUserFromGroup(userID, threadID, [callback])`](#apiremoveuserfromgroupuserid-threadid-callback) ⇒ <code>Promise</code>
* [`api.resolvePhotoUrl(photoID, [callback])`](#apiresolvephotourlphotoid-callback) ⇒ <code>Promise</code>
* [`api.searchForThread(name, [callback])`](#apisearchforthreadname-callback)
* [`api.sendMessage(message, threadID, [callback], messageID)`](#apisendmessagemessage-threadid-callback-messageid) ⇒ <code>Promise</code>
* [`api.sendTypingIndicator(threadID, [callback])`](#apisendtypingindicatorthreadid-callback) ⇒ <code>Promise</code>
* [`api.setMessageReaction(reaction, messageID, [callback], [forceCustomReaction])`](#apisetmessagereactionreaction-messageid-callback-forcecustomreaction) ⇒ <code>Promise</code>
* [`api.setOptions(options)`](#apisetoptionsoptions) ⇒ <code>Promise</code>
* [`api.setPostReaction(postID, type, [callback])`](#apisetpostreactionpostid-type-callback) ⇒ <code>Promise</code>
* [`api.setTitle(newTitle, threadID, [callback])`](#apisettitlenewtitle-threadid-callback) ⇒ <code>Promise</code>
* [`api.threadColors`](#apithreadcolors) ⇒ <code>Object</code>
* [`api.unsendMessage(messageID, [callback])`](#apiunsendmessagemessageid-callback) ⇒ <code>Promise</code>
---------------------------------------
### Password safety
**Read this** before you _copy+paste_ examples from below.
You should not store Facebook password in your scripts.
There are few good reasons:
* People who are standing behind you may look at your "code" and get your password if it is on the screen
* 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_"
* 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.
* 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
Preferred method is to have `login.js` that saves `AppState` to a file and then use that file from all your scripts.
This way you can put password in your code for a minute, login to facebook and then remove it.
If you want to be even more safe: _login.js_ can get password with `require("readline")` or with environment variables like this:
```js
var credentials = {
email: process.env.FB_EMAIL,
password: process.env.FB_PASSWORD
}
```
```bash
FB_EMAIL="john.doe@example.com"
FB_PASSWORD="MySuperHardP@ssw0rd"
nodejs login.js
```
---------------------------------------
<a name="login"></a>
### login(credentials, options, [callback])
This function is returned by `require(...)` and is the main entry point to the API.
It allows the user to log into facebook given the right credentials.
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!)
If `callback` is supplied:
* `callback` will be called with a `null` object (for potential errors) and with an object containing all the available functions if logged in successfully.
* `callback` will be called with an error object if failed to login.
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:
* If `callback` is not supplied (using `Promise`), this function will return a `Promise` that behaves like `Promise` received from `login`.
* If `callback` is supplied, this function will still return a `Promise`, but it will not resolve. Instead, the result is called to `callback`.
__Arguments__
* `credentials`: An object containing the fields `email` and `password` used to login, __*or*__ an object containing the field `appState`.
* `options`: An object representing options to use when logging in (as described in [api.setOptions](#setOptions)).
* `callback(err, api)`: A callback called when login is done (successful or not). `err` is an object containing a field `error`.
__Example (Email & Password)__: (it is no longer usable, please use [this](#loginWithAppstate) alternative method)
```js
const login = require("unofficial-fb-chat-api");
login({email: "FB_EMAIL", password: "FB_PASSWORD"}, (err, api) => {
if(err) return console.error(err);
// Here you can use the api
});
```
__Example (Email & Password then save appState to file)__: (it is no longer usable, please use [this](#loginWithAppstate) alternative method)
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({email: "FB_EMAIL", password: "FB_PASSWORD"}, (err, api) => {
if(err) return console.error(err);
fs.writeFileSync('appstate.json', JSON.stringify(api.getAppState()));
});
```
__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`. (it is no longer usable, please use [this](#loginWithAppstate) alternative method)
__Example__:
```js
const login = require("unofficial-fb-chat-api");
const readline = require("readline");
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const obj = {email: "FB_EMAIL", password: "FB_PASSWORD"};
login(obj, (err, api) => {
if(err) {
switch (err.error) {
case 'login-approval':
console.log('Enter code > ');
rl.on('line', (line) => {
err.continue(line);
rl.close();
});
break;
default:
console.error(err);
}
return;
}
// Logged in!
});
```
__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`.
<a name="loginWithAppstate"></a>
#### __Example (AppState loaded from file)__: You can get fbstate using [this](https://github.com/ntkhang03/c3c-fbstate) extension
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
// Here you can use the api
});
```
---------------------------------------
<a name="addUserToGroup"></a>
### api.addUserToGroup(userID, threadID, [callback])
Adds a user (or array of users) to a group chat.
__Arguments__
* `userID`: User ID or array of user IDs.
* `threadID`: Group chat ID.
* `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
---------------------------------------
<a name="changeAdminStatus"></a>
### api.changeAdminStatus(threadID, adminIDs, adminStatus, [callback])
Given a adminID, or an array of adminIDs, will set the admin status of the user(s) to `adminStatus`.
__Arguments__
* `threadID`: ID of a group chat (can't use in one-to-one conversations)
* `adminIDs`: The id(s) of users you wish to admin/unadmin (string or an array).
* `adminStatus`: Boolean indicating whether the user(s) should be promoted to admin (`true`) or demoted to a regular user (`false`).
* `callback(err)`: A callback called when the query is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if (err) return console.error(err);
let threadID = "0000000000000000";
let newAdmins = ["111111111111111", "222222222222222"];
api.changeAdminStatus(threadID, newAdmins, true, editAdminsCallback);
let adminToRemove = "333333333333333";
api.changeAdminStatus(threadID, adminToRemove, false, editAdminsCallback);
});
function editAdminsCallback(err) {
if (err) return console.error(err);
}
```
---------------------------------------
<a name="changeArchivedStatus"></a>
### api.changeArchivedStatus(threadOrThreads, archive, [callback])
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.
__Arguments__
* `threadOrThreads`: The id(s) of the threads you wish to archive/unarchive.
* `archive`: Boolean indicating the new archive status to assign to the thread(s).
* `callback(err)`: A callback called when the query is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.changeArchivedStatus("000000000000000", true, (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="changeBlockedStatus"></a>
### api.changeBlockedStatus(userID, block, [callback])
Prevents a user from privately contacting you. (Messages in a group chat will still be seen by both parties).
__Arguments__
* `userID`: User ID.
* `block`: Boolean indicating whether to block or unblock the user (true for block).
* `callback(err)`: A callback called when the query is done (either with an error or with no arguments).
---------------------------------------
<a name="changeGroupImage"></a>
### api.changeGroupImage(image, threadID, [callback])
Will change the group chat's image to the given image.
__Arguments__
* `image`: File stream of image.
* `threadID`: String representing the ID of the thread.
* `callback(err)`: A callback called when the change is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.changeGroupImage(fs.createReadStream("./avatar.png"), "000000000000000", (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="changeNickname"></a>
### api.changeNickname(nickname, threadID, participantID, [callback])
Will change the thread user nickname to the one provided.
__Arguments__
* `nickname`: String containing a nickname. Leave empty to reset nickname.
* `threadID`: String representing the ID of the thread.
* `participantID`: String representing the ID of the user.
* `callback(err)`: An optional callback called when the change is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.changeNickname("Example", "000000000000000", "000000000000000", (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="changeThreadColor"></a>
### api.changeThreadColor(color, threadID, [callback])
Will change the thread color to the given hex string color ("#0000ff"). Set it
to empty string if you want the default.
Note: the color needs to start with a "#".
__Arguments__
* `color`: String representing a theme ID (a list of theme ID can be found at `api.threadColors`).
* `threadID`: String representing the ID of the thread.
* `callback(err)`: A callback called when the change is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.changeThreadColor("#0000ff", "000000000000000", (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="changeThreadEmoji"></a>
### api.changeThreadEmoji(emoji, threadID, [callback])
Will change the thread emoji to the one provided.
Note: The UI doesn't play nice with all emoji.
__Arguments__
* `emoji`: String containing a single emoji character.
* `threadID`: String representing the ID of the thread.
* `callback(err)`: A callback called when the change is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.changeThreadEmoji("💯", "000000000000000", (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="createNewGroup"></a>
### api.createNewGroup(participantIDs, groupTitle, [callback])
Create a new group chat.
__Arguments__
* `participantIDs`: An array containing participant IDs. (*Length must be >= 2*)
* `groupTitle`: The title of the new group chat.
* `callback(err, threadID)`: A callback called when created.
---------------------------------------
<a name="createPoll"></a>
### api.createPoll(title, threadID, options, [callback]) (*temporary deprecated because Facebook is updating this feature*)
Creates a poll with the specified title and optional poll options, which can also be initially selected by the logged-in user.
__Arguments__
* `title`: String containing a title for the poll.
* `threadID`: String representing the ID of the thread.
* `options`: An optional `string : bool` dictionary to specify initial poll options and their initial states (selected/not selected), respectively.
* `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.
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.createPoll("Example Poll", "000000000000000", {
"Option 1": false,
"Option 2": true
}, (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="deleteMessage"></a>
### api.deleteMessage(messageOrMessages, [callback])
Takes a messageID or an array of messageIDs and deletes the corresponding message.
__Arguments__
* `messageOrMessages`: A messageID string or messageID string array
* `callback(err)`: A callback called when the query is done (either with an error or null).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.listen((err, message) => {
if(message.body) {
api.sendMessage(message.body, message.threadID, (err, messageInfo) => {
if(err) return console.error(err);
api.deleteMessage(messageInfo.messageID);
});
}
});
});
```
---------------------------------------
<a name="deleteThread"></a>
### api.deleteThread(threadOrThreads, [callback])
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.
__Arguments__
* `threadOrThreads` - The id(s) of the threads you wish to remove from your account.
* `callback(err)` - A callback called when the operation is done, maybe with an object representing an error.
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.deleteThread("000000000000000", (err) => {
if(err) return console.error(err);
});
});
```
---------------------------------------
<a name="forwardAttachment"></a>
### api.forwardAttachment(attachmentID, userOrUsers, [callback])
Forwards corresponding attachment to given userID or to every user from an array of userIDs
__Arguments__
* `attachmentID`: The ID field in the attachment object. Recorded audio cannot be forwarded.
* `userOrUsers`: A userID string or usersID string array
* `callback(err)`: A callback called when the query is done (either with an error or null).
---------------------------------------
<a name="getAppState"></a>
### api.getAppState()
Returns current appState which can be saved to a file or stored in a variable.
---------------------------------------
<a name="getCurrentUserID"></a>
### api.getCurrentUserID()
Returns the currently logged-in user's Facebook user ID.
---------------------------------------
<a name="getEmojiUrl"></a>
### api.getEmojiUrl(c, size, pixelRatio)
Returns the URL to a Facebook Messenger-style emoji image asset.
__note__: This function will return a URL regardless of whether the image at the URL actually exists.
This can happen if, for example, Messenger does not have an image asset for the requested emoji.
__Arguments__
* `c` - The emoji character
* `size` - The width and height of the emoji image; supported sizes are 32, 64, and 128
* `pixelRatio` - The pixel ratio of the emoji image; supported ratios are '1.0' and '1.5' (default is '1.0')
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
// Prints https://static.xx.fbcdn.net/images/emoji.php/v8/z9c/1.0/128/1f40d.png
console.log('Snake emoji, 128px (128x128 with pixel ratio of 1.0');
console.log(api.getEmojiUrl('\ud83d\udc0d', 128));
// Prints https://static.xx.fbcdn.net/images/emoji.php/v8/ze1/1.5/128/1f40d.png
console.log('Snake emoji, 192px (128x128 with pixel ratio of 1.5');
console.log(api.getEmojiUrl('\ud83d\udc0d', 128, '1.5'));
});
```
---------------------------------------
<a name="getFriendsList"></a>
### api.getFriendsList([callback])
Returns an array of objects with some information about your friends.
__Arguments__
* `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`.
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.getFriendsList((err, data) => {
if(err) return console.error(err);
console.log(data.length);
});
});
```
---------------------------------------
<a name="getMessage"></a>
### api.getMessage(threadID, messageID, [callback])
Returns message data from messageID
__Arguments__
* `threadID`: The ID of the thread you want to get the message from.
* `messageID`: The ID of the message you want to get.
* `callback(err, data)` - A callback called when the query is done.
---------------------------------------
<a name="getThreadHistory"></a>
### api.getThreadHistory(threadID, amount, timestamp, [callback])
Takes a threadID, number of messages, a timestamp, and a callback.
__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.
__Arguments__
* `threadID`: A threadID corresponding to the target chat
* `amount`: The amount of messages to *request*
* `timestamp`: Used to described the time of the most recent message to load. If timestamp is `undefined`, facebook will load the most recent messages.
* `callback(error, history)`: If error is null, history will contain an array of message objects.
__Example__
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.
```js
var timestamp = undefined;
function loadNextThreadHistory(api){
api.getThreadHistory(threadID, 50, timestamp, (err, history) => {
if(err) return console.error(err);
/*
Since the timestamp is from a previous loaded message,
that message will be included in this history so we can discard it unless it is the first load.
*/
if(timestamp != undefined) history.pop();
/*
Handle message history
*/
timestamp = history[0].timestamp;
});
}
```
---------------------------------------
<a name="getThreadInfo"></a>
### api.getThreadInfo(threadIDs, [callback])
Takes a threadID and a callback. Works for both single-user and group threads.
__Arguments__
* `threadIDs`: Either a string/number for one ID or an array of strings/numbers for a batched query.
* `callback(err, info)`: If `err` is `null`, `info` will return
<!-- * in nghiêng chữ -->
*if `threadIDs` is an array of IDs, `info` will be an array of objects with the following fields:*
```js
{
"4000000000000000": {
threadID: "4000000000000000",
threadName: "Thread Name",
// ...
// thread info
},
"5000000000000000": {
threadID: "5000000000000000",
threadName: "Thread Name",
// ...
// thread info
},
// ...
}
```
*if `threadIDs` is a single ID, `info` will be an object with the following fields:*
```js
{
threadID: "4000000000000000",
threadName: "Thread Name",
// ...
// thread info
}
```
`info` will contain the following fields:
| Key | Description |
| :---------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| threadID | ID of the thread |
| participantIDs | Array of user IDs in the thread |
| 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. |
| 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. |
| nicknames | Map of nicknames for members of the thread. If there are no nicknames set, this will be `null`. Can be of the form: <ul><li>`{'123456789': "nickname"}`</ul></li> |
| unreadCount | Number of unread messages |
| messageCount | Number of messages |
| imageSrc | URL to the group chat photo. `null` if unset or a 1-1 thread. |
| timestamp | Timestamp of last activity |
| 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. |
| isGroup | boolean, true if this thread is a group thread (more than 2 participants). |
| isSubscribed | |
| folder | The folder that the thread is in. Can be one of: `INBOX`, `ARCHIVED`, `PENDING` or `OTHER` |
| isArchived | True if the thread is archived, false if not |
| cannotReplyReason | If you cannot reply to this thread, this will be a string stating why. Otherwise it will be null. |
| lastReadTimestamp | Timestamp of the last message that is marked as 'read' by the current user. |
| emoji | Object with key 'emoji' whose value is the emoji unicode character. Null if unset. |
| color | String form of the custom color in hexadecimal form. |
| adminIDs | Array of user IDs of the admins of the thread. Empty array if unset. Can be of the form:<ul><li>`[{ id: '123456789' }]`</li></ul> |
| approvalMode | `true` or `false`, used to check if this group requires admin approval to add users |
| 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> |
| inviteLink | Invite link for the thread. <ul><li>`enable`: `true` if the invite link is enabled, `false` if it is disabled</li> <li>`link`: Invite link</li></ul> |
`accountType` is one of the following:
- `"User"`
- `"Page"`
- `"UnavailableMessagingActor"`
- `"ReducedMessagingActor"`
---------------------------------------
<a name="getThreadList"></a>
### api.getThreadList(limit, timestamp, tags, [callback])
Returns information about the user's threads.
__Arguments__
* `limit`: Limit the number of threads to fetch.
* `timestamp`: Request threads *before* this date. `null` means *now*
* `tags`: An array describing which folder to fetch. It should be one of these:
- `["INBOX"]` *(same as `[]`)*
- `["ARCHIVED"]`
- `["PENDING"]`
- `["OTHER"]`
- `["INBOX", "unread"]`
- `["ARCHIVED", "unread"]`
- `["PENDING", "unread"]`
- `["OTHER", "unread"]`
*if you find something new, let us know*
* `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 same structure as [`getThreadInfo`](#getThreadInfo)
`accountType` is one of the following:
- `"User"`
- `"Page"`
- `"UnavailableMessagingActor"`
- `"ReducedMessagingActor"`
(*there might be more*)
<table>
<tr>
<th>Account type</th>
<th>Key</th>
<th>Description</th>
</tr>
<tr>
<td rowspan="12"><code>"User"</code></td>
<td>userID</td>
<td>ID of user</td>
</tr>
<tr>
<td>name</td>
<td>Full name of user</td>
</tr>
<tr>
<td>shortName</td>
<td>Short name of user (most likely first name)</td>
</tr>
<tr>
<td>gender</td>
<td>Either
<code>"MALE"</code>,
<code>"FEMALE"</code>,
<code>"NEUTER"</code> or
<code>"UNKNOWN"</code>
</td>
</tr>
<tr>
<td>url</td>
<td>URL of the user's Facebook profile</td>
</tr>
<tr>
<td>profilePicture</td>
<td>URL of the profile picture</td>
</tr>
<tr>
<td>username</td>
<td>Username of user or
<code>null</code>
</td>
</tr>
<tr>
<td>isViewerFriend</td>
<td>Is the user a friend of you?</td>
</tr>
<tr>
<td>isMessengerUser</td>
<td>Does the user use Messenger?</td>
</tr>
<tr>
<td>isVerified</td>
<td>Is the user verified? (Little blue tick mark)</td>
</tr>
<tr>
<td>isMessageBlockedByViewer</td>
<td>Is the user blocking messages from you?</td>
</tr>
<tr>
<td>isViewerCoworker</td>
<td>Is the user your coworker?
</td>
</tr>
<tr>
<td rowspan="10"><code>"Page"</code></td>
<td>userID</td>
<td>ID of the page</td>
</tr>
<tr>
<td>name</td>
<td>Name of the fanpage</td>
</tr>
<tr>
<td>url</td>
<td>URL of the fanpage</td>
</tr>
<tr>
<td>profilePicture</td>
<td>URL of the profile picture</td>
</tr>
<tr>
<td>username</td>
<td>Username of user or
<code>null</code>
</td>
</tr>
<tr>
<td>acceptsMessengerUserFeedback</td>
<td></td>
</tr>
<tr>
<td>isMessengerUser</td>
<td>Does the fanpage use Messenger?</td>
</tr>
<tr>
<td>isVerified</td>
<td>Is the fanpage verified? (Little blue tick mark)</td>
</tr>
<tr>
<td>isMessengerPlatformBot</td>
<td>Is the fanpage a bot</td>
</tr>
<tr>
<td>isMessageBlockedByViewer</td>
<td>Is the fanpage blocking messages from you?</td>
</tr>
<tr>
<td rowspan="7"><code>"ReducedMessagingActor"</code><br />(account requres verification,<br />messages are hidden)</td>
<td>userID</td>
<td>ID of the user</td>
</tr>
<tr>
<td>name</td>
<td>Name of the user</td>
</tr>
<tr>
<td>url</td>
<td>
<code>null</code>
</td>
</tr>
<tr>
<td>profilePicture</td>
<td>URL of the default Facebook profile picture</td>
</tr>
<tr>
<td>username</td>
<td>Username of user</td>
</td>
</tr>
<tr>
<td>acceptsMessengerUserFeedback</td>
<td></td>
</tr>
<tr>
<td>isMessageBlockedByViewer</td>
<td>Is the user blocking messages from you?</td>
</tr>
<tr>
<td rowspan="7"><code>"UnavailableMessagingActor"</code><br />(account disabled/removed)</td>
<td>userID</td>
<td>ID of the user</td>
</tr>
<tr>
<td>name</td>
<td><em>Facebook User</em> in user's language</td>
</tr>
<tr>
<td>url</td>
<td><code>null</code></td>
</tr>
<tr>
<td>profilePicture</td>
<td>URL of the default **male** Facebook profile picture</td>
</tr>
<tr>
<td>username</td>
<td><code>null</code></td>
</tr>
<tr>
<td>acceptsMessengerUserFeedback</td>
<td></td>
</tr>
<tr>
<td>isMessageBlockedByViewer</td>
<td>Is the user blocking messages from you?</td>
</tr>
</table>
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:
| Key | Description |
| ----------- | --------------------- |
| accountType | type, can be anything |
| userID | ID of the account |
| name | Name of the account |
---------------------------------------
<a name="getThreadPictures"></a>
### api.getThreadPictures(threadID, offset, limit, [callback])
Returns pictures sent in the thread.
__Arguments__
* `threadID`: A threadID corresponding to the target chat
* `offset`: Start index of picture to retrieve, where 0 is the most recent picture
* `limit`: Number of pictures to get, incrementing from the offset index
* `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`.
---------------------------------------
<a name="getUserID"></a>
### api.getUserID(name, [callback])
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).
__Arguments__
* `name` - A string being the name of the item you're looking for.
* `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).
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.getUserID("Marc Zuckerbot", (err, data) => {
if(err) return console.error(err);
// Send the message to the best match (best by Facebook's criteria)
var msg = "Hello!"
var threadID = data[0].userID;
api.sendMessage(msg, threadID);
});
});
```
---------------------------------------
<a name="getUserInfo"></a>
### api.getUserInfo(ids, [callback])
Will get some information about the given users.
__Arguments__
* `ids` - Either a string/number for one ID or an array of strings/numbers for a batched query.
* `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:
| Key | Description |
| ------------- | ------------------------------------------------------------------ |
| name | Name of the user |
| firstName | First name of the user |
| vanity | the username of the user if any |
| thumbSrc | URL of the profile picture |
| profileUrl | URL of the profile |
| gender | the gender of the user, with 1 is Female, 2 is Male, 0 is unknown |
| type | type is generally user, group, page, event or app |
| isFriend | is the user a friend of the current user, either `true` or `false` |
| isBirthday | is the user having a birthday today, either `true` or `false` |
| searchTokens | an array of strings that can be used to search for the user |
| alternateName | |
__Example__
```js
const fs = require("fs-extra");
const login = require("unofficial-fb-chat-api");
login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
if(err) return console.error(err);
api.getUserInfo([1, 2, 3, 4], (err, ret) => {
if(err) return console.error(err);
for(var prop in ret) {
if(ret.hasOwnProperty(prop) && ret[prop].isBirthday) {
api.sendMessage("Happy birthday :)", prop);
}
}
});
});
```
---------------------------------------
<a name="threadColors"></a>
### api.threadColors
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.
<details>
<summary><b>List of colors:</b></summary>
<br>
- MessengerBlue: `196241301102133`
- Viking: `1928399724138152`
- GoldenPoppy: `174636906462322`
- RadicalRed: `2129984390566328`
- Shocking: `2058653964378557`
- FreeSpeechGreen: `2136751179887052`
- Pumpkin: `175615189761153`
- LightCoral: `980963458735625`
- MediumSlateBlue: `234137870477637`
- DeepSkyBlue: `2442142322678320`
- BrilliantRose: `169463077092846`
- DefaultBlue: `196241301102133`
- HotPink: `169463077092846`
- AquaBlue: `2442142322678320`
- BrightPurple: `234137870477637`
- CoralPink: `980963458735625`
- Orange: `175615189761153`
- Green: `2136751179887052`
- LavenderPurple: `2058653964378557`
- Red: `2129984390566328`
- Yellow: `174636906462322`
- TealBlue: `1928399724138152`
- Aqua: `417639218648241`
- Mango: `930060997172551`
- Berry: `164535220883264`
- Citrus: `370940413392601`
- Candy: `205488546921017`
- Earth: `1833559466821043`
- Support: `365557122117011`
- Music: `339021464972092`
- Pride: `1652456634878319`
- DoctorStrange: `538280997628317`
- LoFi: `1060619084701625`
- Sky: `3190514984517598`
- LunarNewYear: `357833546030778`
- Celebration: `627144732056021`
- Chill: `390127158985345`
- StrangerThings: `1059859811490132`
- Dune: `1455149831518874`
- Care: `275041734441112`
- Astrology: `3082966625307060`
- JBalvin: `184305226956268`
- Birthday: `621630955405500`
- Cottagecore: `539927563794799`
- Ocean: `736591620215564`
- Love: `741311439775765`
- TieDye: `230032715012014`
- Monochrome: `788274591712841`
- Default: `3259963564026002`
- Rocket: `582065306070020`
- Berry2: `724096885023603`
- Candy2: `624266884847972`
- Unicorn: `273728810607574`
- Tropical: `262191918210707`
- Maple: `2533652183614000`
- Sushi: `909695489504566`
- Citrus2: `557344741607350`
- Lollipop: `280333826736184`
- Shadow: `271607034185782`
- Rose: `1257453361255152`
- Lavender: `571193503540759`
- Tulip: `2873642949430623`
- Classic: `3273938616164733`
- Peach: `3022526817824329`
- Honey: `672058580051520`
- Kiwi: `3151463484918004`
- Grape: `193497045377796`
- NonBinary: `737761000603635`
- ~~StarWars: `809305022860427`~~ (Facebook removed it.)
</details>
---------------------------------------
<a name="handleMessageRequest"></a>
### api.handleMessageRequest(threadID, accept, [callback])
Accept or ignore message request(s) with thread id `threadID`.
__Arguments__
* `threadID`: A threadID or array of threadIDs corresponding to the target thread(s). Can be numbers or strings.
* `accept`: Boolean indicating the new status to assign to the message request(s); true for inbox, false to others.
* `callback(err)`: A callback called when the query is done (with an error or with null).
---------------------------------------
<a name="httpGet"></a>
### api.httpGet(url, form, [customHeader], [callback], [notAPI])
Get data from a URL with method GET.
__Arguments__
* `url`: A string being the URL to get data from.
* `form`: An object containing the form data to send.
* `customHeader`: An object containing custom headers to send.
* `callback(err, data)`: A callback called when the query is done (either with an error or with the resulting data).
* `notAPI`: A boolean indicating whether the request is made from the API or not (default: false, meaning it's from the API).
---------------------------------------
<a name="httpPost"></a>
### api.httpPost(url, form, [customHeader], [callback], [notAPI])
Get data from a URL with method POST.
__Arguments__
* Similar to [`api.httpGet`](#httpGet).
---------------------------------------
<a name="httpPostFormData"></a>
### api.httpPostFormData(url, form, [customHeader], [callback], [notAPI])
Post form data to a URL.
__Arguments__
* Similar to [`api.httpGet`](#httpGet).
---------------------------------------
<a name="listen"></a>
### ~~api.listen([callback])~~
<a name="listenMqtt"></a>
### api.listenMqtt([callback])
Will call `callback` when a new message is received on this account.
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.
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.
__Arguments__
- `callback(error, message)`: A callback called every time the logged-in account receives a new message.
<a name="message"></a>
__Message__
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:
<table>
<tr>
<th>Event Type</th>
<th>Field</th>
<th>Description</th>
</tr>
<tr>
<td rowspan="9">
<code>"message"</code><br />
A message was sent to a thread.
</td>
<td><code>attachments</code></td>
<td>An array of attachments to the message. Attachments vary in type, see the attachments table below.</td>
</tr>
<tr>
<td><code>body</code></td>
<td>The string corresponding to the message that was just received.</td>
</tr>
<tr>
<td><code>isGroup</code></td>
<td>boolean, true if this thread is a group thread (more than 2 participants).</td>
</tr>
<tr>
<td><code>mentions</code></td>
<td>An object containing people mentioned/tagged in the message in the format { id: name }</td>
</tr>
<tr>
<td><code>messageID</code></td>
<td>A string representing the message ID.</td>
</tr>
<tr>
<td><code>senderID</code></td>
<td>The id of the person who sent the message in the chat with threadID.</td>
</tr>
<tr>
<td><code>threadID</code></td>
<td>The threadID representing the thread in which the message was sent.</td>
</tr>
<tr>
<td><code>isUnread</code></td>
<td>Boolean representing whether or not the message was read.</td>
</tr>
<tr>
<td><code>type</code></td>
<td>For this event type, this will always be the string <code>"message"</code>.</td>
</tr>
<tr>
<td rowspan="6">
<code>"event"</code><br />
An event occurred within a thread. Note that receiving this event type needs to be enabled with `api.setOptions({ listenEvents: true })`
</td>
<td><code>author</code></td>
<td>The person who performed the event.</td>
</tr>
<tr>
<td><code>logMessageBody</code></td>
<td>String printed in the chat.</td>
</tr>
<tr>
<td><code>logMessageData</code></td>
<td>Data relevant to the event.</td>
</tr>
<tr>
<td><code>logMessageType</code></td>
<td>String representing the type of event (<code>log:subscribe</code>, <code>log:unsubscribe</code>, <code>log:thread-name</code>, <code>log:thread-color</code>, <code>log:thread-icon</code>, <code>log:user-nickname</code>)</td>
</tr>
<tr>
<td><code>threadID</code></td>
<td>The threadID representing the thread in which the message was sent.</td>
</tr>
<tr>
<td><code>type</code></td>
<td>For this event type, this will always be the string <code>"event"</code>.</td>
</tr>
<tr>
<td rowspan="5">
<code>"typ"</code><br />
A user in a thread is typing. Note that receiving this event type needs to be enabled with `api.setOptions({ listenTyping