shadowx-fca 2.1.0 โ 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +457 -0
- package/index.js +110 -68
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
Here's a comprehensive README.md for your shadowx-fca package:
|
|
2
|
+
|
|
3
|
+
```markdown
|
|
4
|
+
# shadowx-fca
|
|
5
|
+
|
|
6
|
+
<div align="center">
|
|
7
|
+
<img src="https://img.shields.io/npm/v/shadowx-fca.svg?style=for-the-badge" alt="NPM Version">
|
|
8
|
+
<img src="https://img.shields.io/npm/dt/shadowx-fca.svg?style=for-the-badge" alt="NPM Downloads">
|
|
9
|
+
<img src="https://img.shields.io/github/license/mueidmursalinrifat/shadowx-fca.svg?style=for-the-badge" alt="License">
|
|
10
|
+
<img src="https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg?style=for-the-badge" alt="Node Version">
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>Unofficial Facebook Chat API for Node.js with Auto-Update System</strong><br>
|
|
15
|
+
Modified by Mueid Mursalin Rifat | Original by shadowX
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
## ๐ Table of Contents
|
|
19
|
+
|
|
20
|
+
- [Features](#-features)
|
|
21
|
+
- [Installation](#-installation)
|
|
22
|
+
- [Quick Start](#-quick-start)
|
|
23
|
+
- [Authentication Methods](#-authentication-methods)
|
|
24
|
+
- [API Documentation](#-api-documentation)
|
|
25
|
+
- [Configuration](#-configuration)
|
|
26
|
+
- [Anti-Detection Features](#-anti-detection-features)
|
|
27
|
+
- [Troubleshooting](#-troubleshooting)
|
|
28
|
+
- [Examples](#-examples)
|
|
29
|
+
- [Changelog](#-changelog)
|
|
30
|
+
- [License](#-license)
|
|
31
|
+
|
|
32
|
+
## โจ Features
|
|
33
|
+
|
|
34
|
+
- โ
**Multiple Authentication Methods** - Support for both appState and email/password login
|
|
35
|
+
- โ
**Real-time Messaging** - MQTT/WebSocket based real-time message listening
|
|
36
|
+
- โ
**Auto-Update System** - Automatically checks and updates to latest version
|
|
37
|
+
- โ
**Anti-Detection** - Rotating user agents, rate limiting, and stealth headers
|
|
38
|
+
- โ
**2FA Support** - Two-factor authentication handling
|
|
39
|
+
- โ
**Message Attachments** - Support for images, videos, files, stickers, and more
|
|
40
|
+
- โ
**Typing Indicators** - Send and receive typing status
|
|
41
|
+
- โ
**Read Receipts** - Message read confirmation
|
|
42
|
+
- โ
**Thread Management** - Create, delete, rename threads
|
|
43
|
+
- โ
**Mention Support** - Tag users in messages (both legacy and new formats)
|
|
44
|
+
- โ
**Configurable Options** - Extensive customization options
|
|
45
|
+
|
|
46
|
+
## ๐ฆ Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install shadowx-fca
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Requirements
|
|
53
|
+
|
|
54
|
+
ยท Node.js >= 14.0.0
|
|
55
|
+
ยท npm >= 6.0.0
|
|
56
|
+
|
|
57
|
+
๐ Quick Start
|
|
58
|
+
|
|
59
|
+
Method 1: Using AppState (Recommended)
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const login = require('shadowx-fca');
|
|
63
|
+
const fs = require('fs');
|
|
64
|
+
|
|
65
|
+
// Load saved appState
|
|
66
|
+
const appState = JSON.parse(fs.readFileSync('appstate.json', 'utf8'));
|
|
67
|
+
|
|
68
|
+
login({ appState: appState }, (err, api) => {
|
|
69
|
+
if (err) return console.error('Login failed:', err);
|
|
70
|
+
|
|
71
|
+
console.log('โ
Logged in successfully!');
|
|
72
|
+
|
|
73
|
+
// Listen for incoming messages
|
|
74
|
+
api.listenMqtt((err, message) => {
|
|
75
|
+
if (err) return console.error('Listen error:', err);
|
|
76
|
+
|
|
77
|
+
if (message.type === 'message') {
|
|
78
|
+
console.log(`[${message.senderID}] ${message.body}`);
|
|
79
|
+
|
|
80
|
+
// Reply to message
|
|
81
|
+
api.sendMessage('Hello! I\'m a bot!', message.threadID);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Method 2: Email & Password
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
const login = require('shadowx-fca');
|
|
91
|
+
|
|
92
|
+
login({
|
|
93
|
+
email: 'your_email@example.com',
|
|
94
|
+
password: 'your_password'
|
|
95
|
+
}, (err, api) => {
|
|
96
|
+
if (err) return console.error('Login failed:', err);
|
|
97
|
+
|
|
98
|
+
console.log('โ
Logged in successfully!');
|
|
99
|
+
|
|
100
|
+
// Save appState for future use
|
|
101
|
+
const appState = api.getAppState();
|
|
102
|
+
fs.writeFileSync('appstate.json', JSON.stringify(appState, null, 2));
|
|
103
|
+
|
|
104
|
+
// Your bot logic here
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
๐ Authentication Methods
|
|
109
|
+
|
|
110
|
+
1. AppState (Recommended)
|
|
111
|
+
|
|
112
|
+
Most secure method - uses saved cookies instead of credentials.
|
|
113
|
+
|
|
114
|
+
Get your appState:
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
const login = require('shadowx-fca');
|
|
118
|
+
const fs = require('fs');
|
|
119
|
+
|
|
120
|
+
login({ email: 'your_email', password: 'your_password' }, (err, api) => {
|
|
121
|
+
if (err) return console.error(err);
|
|
122
|
+
|
|
123
|
+
const appState = api.getAppState();
|
|
124
|
+
fs.writeFileSync('appstate.json', JSON.stringify(appState, null, 2));
|
|
125
|
+
console.log('โ
AppState saved! You can now use this file to login.');
|
|
126
|
+
process.exit(0);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
2. Email & Password
|
|
131
|
+
|
|
132
|
+
Traditional method with 2FA support.
|
|
133
|
+
|
|
134
|
+
With 2FA:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
login({ email: 'email@example.com', password: 'password' }, (err, api) => {
|
|
138
|
+
if (err && err.error === 'login-approval') {
|
|
139
|
+
console.log('Enter 2FA code:');
|
|
140
|
+
// Get code from user input
|
|
141
|
+
const twoFACode = '123456';
|
|
142
|
+
err.continue(twoFACode);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
๐ API Documentation
|
|
148
|
+
|
|
149
|
+
Core Methods
|
|
150
|
+
|
|
151
|
+
login(loginData, [options], callback)
|
|
152
|
+
|
|
153
|
+
Main login function.
|
|
154
|
+
|
|
155
|
+
Parameters:
|
|
156
|
+
|
|
157
|
+
ยท loginData - Object containing either appState or email/password
|
|
158
|
+
ยท options - Optional configuration object
|
|
159
|
+
ยท callback - Function called after login
|
|
160
|
+
|
|
161
|
+
api.sendMessage(message, threadID, [callback], [replyToMessage], [isSingleUser])
|
|
162
|
+
|
|
163
|
+
Send a message to a thread.
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// Simple message
|
|
167
|
+
api.sendMessage('Hello world!', 'thread_id_here');
|
|
168
|
+
|
|
169
|
+
// With reply
|
|
170
|
+
api.sendMessage('Replying to you!', 'thread_id_here', null, 'message_id_here');
|
|
171
|
+
|
|
172
|
+
// With mention
|
|
173
|
+
api.sendMessage('Hello @John Doe', 'thread_id_here');
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
api.listenMqtt(callback)
|
|
177
|
+
|
|
178
|
+
Listen for incoming events (messages, typing, presence).
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
api.listenMqtt((err, event) => {
|
|
182
|
+
if (err) return console.error(err);
|
|
183
|
+
|
|
184
|
+
switch(event.type) {
|
|
185
|
+
case 'message':
|
|
186
|
+
console.log('New message:', event.body);
|
|
187
|
+
break;
|
|
188
|
+
case 'event':
|
|
189
|
+
console.log('Thread event:', event.logMessageType);
|
|
190
|
+
break;
|
|
191
|
+
case 'typ':
|
|
192
|
+
console.log(event.isTyping ? 'Typing...' : 'Stopped typing');
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
api.getThreadInfo(threadID, callback)
|
|
199
|
+
|
|
200
|
+
Get information about a thread.
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
api.getThreadInfo('thread_id_here', (err, info) => {
|
|
204
|
+
if (err) return console.error(err);
|
|
205
|
+
console.log('Thread name:', info.name);
|
|
206
|
+
console.log('Participants:', info.participants);
|
|
207
|
+
console.log('Unread count:', info.unreadCount);
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
api.getUserInfo(userID, callback)
|
|
212
|
+
|
|
213
|
+
Get user information.
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
api.getUserInfo('user_id_here', (err, info) => {
|
|
217
|
+
if (err) return console.error(err);
|
|
218
|
+
console.log('Name:', info.name);
|
|
219
|
+
console.log('Gender:', info.gender);
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
api.setOptions(options)
|
|
224
|
+
|
|
225
|
+
Update API options at runtime.
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
api.setOptions({
|
|
229
|
+
listenEvents: true,
|
|
230
|
+
autoMarkRead: false,
|
|
231
|
+
delayBetweenRequests: 2000
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Additional Methods
|
|
236
|
+
|
|
237
|
+
Method Description
|
|
238
|
+
api.getAppState() Get current appState (cookies)
|
|
239
|
+
api.markAsRead(threadID, callback) Mark thread as read
|
|
240
|
+
api.markAsDelivered(threadID, messageID, callback) Mark message as delivered
|
|
241
|
+
api.sendTypingIndicator(threadID, callback) Send typing indicator
|
|
242
|
+
api.changeNickname(nickname, threadID, userID, callback) Change user nickname
|
|
243
|
+
api.addUserToGroup(userID, threadID, callback) Add user to group
|
|
244
|
+
api.removeUserFromGroup(userID, threadID, callback) Remove user from group
|
|
245
|
+
api.createNewGroup(participantIDs, groupName, callback) Create new group
|
|
246
|
+
api.deleteThread(threadID, callback) Delete/leave thread
|
|
247
|
+
api.getThreadList(limit, timestamp, tags, callback) Get thread list
|
|
248
|
+
|
|
249
|
+
โ๏ธ Configuration
|
|
250
|
+
|
|
251
|
+
Global Configuration (config.json)
|
|
252
|
+
|
|
253
|
+
Create config.json in your project root:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"enableTypingIndicator": false,
|
|
258
|
+
"typingDuration": 4000,
|
|
259
|
+
"delayBetweenRequests": 1500,
|
|
260
|
+
"autoMarkDelivery": false,
|
|
261
|
+
"autoMarkRead": false,
|
|
262
|
+
"listenEvents": true,
|
|
263
|
+
"selfListen": false,
|
|
264
|
+
"autoReconnect": true,
|
|
265
|
+
"logLevel": "info",
|
|
266
|
+
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Options Reference
|
|
271
|
+
|
|
272
|
+
Option Type Default Description
|
|
273
|
+
selfListen Boolean false Listen to own messages
|
|
274
|
+
listenEvents Boolean true Listen to thread events
|
|
275
|
+
listenTyping Boolean false Listen to typing indicators
|
|
276
|
+
updatePresence Boolean false Update online status
|
|
277
|
+
forceLogin Boolean false Force login even if suspicious
|
|
278
|
+
autoMarkDelivery Boolean false Auto-mark messages as delivered
|
|
279
|
+
autoMarkRead Boolean false Auto-mark messages as read
|
|
280
|
+
autoReconnect Boolean true Auto-reconnect on disconnect
|
|
281
|
+
delayBetweenRequests Number 1000 Delay between API requests (ms)
|
|
282
|
+
logLevel String "info" Log level (silent/error/warn/info/verbose)
|
|
283
|
+
|
|
284
|
+
๐ก๏ธ Anti-Detection Features
|
|
285
|
+
|
|
286
|
+
shadowx-fca includes several features to avoid Facebook's anti-bot detection:
|
|
287
|
+
|
|
288
|
+
1. Rate Limiting
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
// Set custom delay between requests
|
|
292
|
+
api.setOptions({ delayBetweenRequests: 2000 });
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
2. Rotating User Agents
|
|
296
|
+
|
|
297
|
+
Automatically rotates between different user agents to avoid fingerprinting.
|
|
298
|
+
|
|
299
|
+
3. Stealth Headers
|
|
300
|
+
|
|
301
|
+
Includes realistic browser headers (Sec-Ch-Ua, Accept-Language, etc.)
|
|
302
|
+
|
|
303
|
+
4. Request Queue
|
|
304
|
+
|
|
305
|
+
Automatically queues and spaces out requests to prevent rate limiting.
|
|
306
|
+
|
|
307
|
+
๐ง Troubleshooting
|
|
308
|
+
|
|
309
|
+
Common Issues & Solutions
|
|
310
|
+
|
|
311
|
+
1. "Error! Your cookiestate is not valid!"
|
|
312
|
+
|
|
313
|
+
Solution: Refresh your appState
|
|
314
|
+
|
|
315
|
+
```javascript
|
|
316
|
+
// Get new appState
|
|
317
|
+
login({ email: 'your_email', password: 'your_password' }, (err, api) => {
|
|
318
|
+
const newAppState = api.getAppState();
|
|
319
|
+
fs.writeFileSync('appstate.json', JSON.stringify(newAppState, null, 2));
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
2. Account Suspension
|
|
324
|
+
|
|
325
|
+
Solutions:
|
|
326
|
+
|
|
327
|
+
ยท Increase delay between requests
|
|
328
|
+
ยท Don't spam messages (add 2-3 second delays)
|
|
329
|
+
ยท Use appState instead of email/password
|
|
330
|
+
ยท Avoid running 24/7
|
|
331
|
+
ยท Use a proxy if running multiple bots
|
|
332
|
+
|
|
333
|
+
3. Login Timeout
|
|
334
|
+
|
|
335
|
+
Solution: Increase timeout in options
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
login({ appState: appState }, { timeout: 120000 }, callback);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
4. MQTT Connection Issues
|
|
342
|
+
|
|
343
|
+
Solution: Enable auto-reconnect
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
api.setOptions({ autoReconnect: true });
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
๐ก Examples
|
|
350
|
+
|
|
351
|
+
Simple Echo Bot
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
const login = require('shadowx-fca');
|
|
355
|
+
|
|
356
|
+
login({ appState: require('./appstate.json') }, (err, api) => {
|
|
357
|
+
if (err) return console.error(err);
|
|
358
|
+
|
|
359
|
+
api.listenMqtt((err, message) => {
|
|
360
|
+
if (err) return console.error(err);
|
|
361
|
+
|
|
362
|
+
if (message.type === 'message' && message.body) {
|
|
363
|
+
// Echo the message back
|
|
364
|
+
api.sendMessage(message.body, message.threadID);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Command Handler
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
const commands = {
|
|
374
|
+
'!help': (api, message) => {
|
|
375
|
+
api.sendMessage('Available commands: !help, !time, !ping', message.threadID);
|
|
376
|
+
},
|
|
377
|
+
'!time': (api, message) => {
|
|
378
|
+
api.sendMessage(`Current time: ${new Date().toLocaleString()}`, message.threadID);
|
|
379
|
+
},
|
|
380
|
+
'!ping': (api, message) => {
|
|
381
|
+
api.sendMessage('Pong!', message.threadID);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
login({ appState: require('./appstate.json') }, (err, api) => {
|
|
386
|
+
if (err) return console.error(err);
|
|
387
|
+
|
|
388
|
+
api.listenMqtt((err, message) => {
|
|
389
|
+
if (err) return console.error(err);
|
|
390
|
+
|
|
391
|
+
if (message.type === 'message' && message.body) {
|
|
392
|
+
const cmd = message.body.split(' ')[0];
|
|
393
|
+
if (commands[cmd]) commands[cmd](api, message);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Auto-Reply with Delay
|
|
400
|
+
|
|
401
|
+
```javascript
|
|
402
|
+
login({ appState: require('./appstate.json') }, (err, api) => {
|
|
403
|
+
if (err) return console.error(err);
|
|
404
|
+
|
|
405
|
+
api.listenMqtt((err, message) => {
|
|
406
|
+
if (err) return console.error(err);
|
|
407
|
+
|
|
408
|
+
if (message.type === 'message' && !message.isGroup) {
|
|
409
|
+
// Add delay to avoid rate limiting
|
|
410
|
+
setTimeout(() => {
|
|
411
|
+
api.sendMessage('Thanks for your message! I\'ll reply soon.', message.threadID);
|
|
412
|
+
}, 3000);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
๐ Changelog
|
|
419
|
+
|
|
420
|
+
v2.1.0 (Latest)
|
|
421
|
+
|
|
422
|
+
ยท โจ Added rotating user agents for anti-detection
|
|
423
|
+
ยท ๐ Implemented request queue with rate limiting
|
|
424
|
+
ยท ๐ง Fixed email/password login issues
|
|
425
|
+
ยท ๐ก๏ธ Enhanced security headers
|
|
426
|
+
ยท ๐ Improved documentation
|
|
427
|
+
|
|
428
|
+
v2.0.0
|
|
429
|
+
|
|
430
|
+
ยท Added auto-update system
|
|
431
|
+
ยท Improved MQTT connection stability
|
|
432
|
+
ยท Added support for new mention format
|
|
433
|
+
ยท Fixed 2FA handling
|
|
434
|
+
|
|
435
|
+
๐ License
|
|
436
|
+
|
|
437
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
438
|
+
|
|
439
|
+
โ ๏ธ Disclaimer
|
|
440
|
+
|
|
441
|
+
This project is for educational purposes only. Use at your own risk. The authors are not responsible for any consequences that may arise from using this software, including but not limited to account suspension or legal action from Facebook.
|
|
442
|
+
|
|
443
|
+
๐ค Contributing
|
|
444
|
+
|
|
445
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
446
|
+
|
|
447
|
+
๐ Support
|
|
448
|
+
|
|
449
|
+
ยท Issues: t.me/mueidmursalinrifat
|
|
450
|
+
ยท Author: Mueid Mursalin Rifat
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
<div align="center">
|
|
455
|
+
Made with โค๏ธ by Mueid Mursalin Rifat
|
|
456
|
+
</div>
|
|
457
|
+
```
|
package/index.js
CHANGED
|
@@ -6,24 +6,31 @@ var log = require("npmlog");
|
|
|
6
6
|
var { checkForFCAUpdate } = require("./checkUpdate");
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
|
|
10
|
-
var logger = require("../../func/utils/log.js");
|
|
11
|
-
var { cra, cv, cb, co } = getThemeColors();*/
|
|
9
|
+
|
|
12
10
|
log.maxRecordSize = 100;
|
|
13
11
|
var checkVerified = null;
|
|
12
|
+
|
|
14
13
|
const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
|
|
14
|
+
|
|
15
15
|
global.ditconmemay = false;
|
|
16
16
|
global.stfcaUpdateChecked = false;
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// Rotating user agents to avoid detection
|
|
19
|
+
const USER_AGENTS = [
|
|
20
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
21
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
|
22
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
23
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
24
|
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function getRandomUserAgent() {
|
|
28
|
+
return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Delay function to avoid rate limiting
|
|
32
|
+
function delay(ms) {
|
|
33
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
function setOptions(globalOptions, options) {
|
|
@@ -55,7 +62,7 @@ function setOptions(globalOptions, options) {
|
|
|
55
62
|
break;
|
|
56
63
|
}
|
|
57
64
|
case 'userAgent': {
|
|
58
|
-
globalOptions.userAgent =
|
|
65
|
+
globalOptions.userAgent = options.userAgent || getRandomUserAgent();
|
|
59
66
|
break;
|
|
60
67
|
}
|
|
61
68
|
case 'proxy': {
|
|
@@ -68,6 +75,10 @@ function setOptions(globalOptions, options) {
|
|
|
68
75
|
}
|
|
69
76
|
break;
|
|
70
77
|
}
|
|
78
|
+
case 'delayBetweenRequests': {
|
|
79
|
+
globalOptions.delayBetweenRequests = options.delayBetweenRequests || 1000;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
71
82
|
default: {
|
|
72
83
|
log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
|
|
73
84
|
break;
|
|
@@ -82,6 +93,7 @@ function setOptions(globalOptions, options) {
|
|
|
82
93
|
function buildAPI(globalOptions, html, jar) {
|
|
83
94
|
let fb_dtsg = null;
|
|
84
95
|
let irisSeqID = null;
|
|
96
|
+
|
|
85
97
|
function extractFromHTML() {
|
|
86
98
|
try {
|
|
87
99
|
const $ = cheerio.load(html);
|
|
@@ -129,35 +141,43 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
129
141
|
}
|
|
130
142
|
} catch { }
|
|
131
143
|
if (fb_dtsg) {
|
|
132
|
-
|
|
144
|
+
log.info("Found fb_dtsg token");
|
|
133
145
|
}
|
|
134
146
|
} catch (e) {
|
|
135
|
-
|
|
147
|
+
log.error("Error finding fb_dtsg:", e);
|
|
136
148
|
}
|
|
137
149
|
}
|
|
150
|
+
|
|
138
151
|
extractFromHTML();
|
|
152
|
+
|
|
139
153
|
var userID;
|
|
140
154
|
var cookies = jar.getCookies("https://www.facebook.com");
|
|
141
155
|
var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
|
|
142
156
|
var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
|
|
157
|
+
|
|
143
158
|
if (!userCookie && !tiktikCookie) {
|
|
144
|
-
|
|
159
|
+
log.error("Error! Your cookiestate is not valid!");
|
|
160
|
+
throw new Error("Invalid appState");
|
|
145
161
|
}
|
|
162
|
+
|
|
146
163
|
if (html.includes("/checkpoint/block/?next")) {
|
|
147
|
-
|
|
164
|
+
log.error('error', "Appstate is dead, please refresh it!", 'error');
|
|
165
|
+
throw new Error("AppState expired");
|
|
148
166
|
}
|
|
167
|
+
|
|
149
168
|
userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
|
|
150
|
-
|
|
169
|
+
|
|
151
170
|
try { clearInterval(checkVerified); } catch (_) { }
|
|
171
|
+
|
|
152
172
|
const clientID = (Math.random() * 2147483648 | 0).toString(16);
|
|
153
173
|
let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb&sid=${userID}`;
|
|
154
174
|
let region = "PNB";
|
|
155
175
|
|
|
156
176
|
try {
|
|
157
177
|
const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
|
|
158
|
-
if (endpointMatch.input.includes("601051028565049")) {
|
|
159
|
-
|
|
160
|
-
|
|
178
|
+
if (endpointMatch && endpointMatch.input && endpointMatch.input.includes("601051028565049")) {
|
|
179
|
+
log.error(`Login error detected`);
|
|
180
|
+
global.ditconmemay = true;
|
|
161
181
|
}
|
|
162
182
|
if (endpointMatch) {
|
|
163
183
|
mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
|
|
@@ -165,9 +185,11 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
165
185
|
region = url.searchParams.get('region')?.toUpperCase() || "PNB";
|
|
166
186
|
}
|
|
167
187
|
} catch (e) {
|
|
168
|
-
|
|
188
|
+
log.warn('Using default MQTT endpoint');
|
|
169
189
|
}
|
|
170
|
-
|
|
190
|
+
|
|
191
|
+
log.info('Building API...');
|
|
192
|
+
|
|
171
193
|
var ctx = {
|
|
172
194
|
userID: userID,
|
|
173
195
|
jar: jar,
|
|
@@ -188,11 +210,12 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
188
210
|
wsReqNumber: 0,
|
|
189
211
|
wsTaskNumber: 0,
|
|
190
212
|
reqCallbacks: {},
|
|
191
|
-
threadTypes: {}
|
|
213
|
+
threadTypes: {}
|
|
192
214
|
};
|
|
215
|
+
|
|
193
216
|
let config = { enableTypingIndicator: false, typingDuration: 4000 };
|
|
217
|
+
|
|
194
218
|
try {
|
|
195
|
-
// Prefer global root config (project-level), but fallback to fca/config.json if present.
|
|
196
219
|
const rootConfigPath = path.join(process.cwd(), 'config.json');
|
|
197
220
|
if (fs.existsSync(rootConfigPath)) {
|
|
198
221
|
const rootConfig = JSON.parse(fs.readFileSync(rootConfigPath, 'utf8'));
|
|
@@ -216,15 +239,13 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
216
239
|
if (typeof global.GoatBot.config.typingDuration !== 'undefined') config.typingDuration = global.GoatBot.config.typingDuration;
|
|
217
240
|
}
|
|
218
241
|
} catch (e) {
|
|
219
|
-
|
|
242
|
+
log.warn('Error loading config.json:', e.message);
|
|
220
243
|
}
|
|
221
244
|
|
|
222
245
|
const refreshFcaConfig = () => {
|
|
223
246
|
try {
|
|
224
|
-
// Defaults first
|
|
225
247
|
const updatedConfig = { enableTypingIndicator: false, typingDuration: 4000 };
|
|
226
248
|
|
|
227
|
-
// Layered config sources
|
|
228
249
|
if (fs.existsSync(path.join(process.cwd(), 'config.json'))) {
|
|
229
250
|
const rootConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'config.json'), 'utf8'));
|
|
230
251
|
if (rootConfig && typeof rootConfig === 'object') {
|
|
@@ -248,39 +269,33 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
248
269
|
|
|
249
270
|
ctx.config = updatedConfig;
|
|
250
271
|
config = updatedConfig;
|
|
251
|
-
if (global.GoatBot) global.GoatBot.config = global.GoatBot.config || {};
|
|
252
|
-
if (global.GoatBot && typeof global.GoatBot.config.enableTypingIndicator !== 'undefined') {
|
|
253
|
-
global.GoatBot.config.enableTypingIndicator = updatedConfig.enableTypingIndicator;
|
|
254
|
-
}
|
|
255
|
-
if (global.GoatBot && typeof global.GoatBot.config.typingDuration !== 'undefined') {
|
|
256
|
-
global.GoatBot.config.typingDuration = updatedConfig.typingDuration;
|
|
257
|
-
}
|
|
258
272
|
} catch (e) {
|
|
259
|
-
|
|
273
|
+
log.warn('Failed to refresh fca config:', e.message);
|
|
260
274
|
}
|
|
261
275
|
};
|
|
262
276
|
|
|
263
|
-
// Initial config load
|
|
264
277
|
refreshFcaConfig();
|
|
265
|
-
|
|
266
|
-
// Accessible runtime API for config reload
|
|
267
278
|
ctx.refreshFcaConfig = refreshFcaConfig;
|
|
268
279
|
if (global.GoatBot) {
|
|
269
280
|
global.GoatBot.refreshFcaConfig = refreshFcaConfig;
|
|
270
281
|
}
|
|
271
282
|
|
|
272
283
|
ctx.config = config;
|
|
284
|
+
|
|
273
285
|
var api = {
|
|
274
286
|
setOptions: setOptions.bind(null, globalOptions),
|
|
275
287
|
getAppState: () => utils.getAppState(jar),
|
|
276
288
|
postFormData: (url, body) => utils.makeDefaults(html, userID, ctx).postFormData(url, ctx.jar, body)
|
|
277
289
|
};
|
|
290
|
+
|
|
278
291
|
var defaultFuncs = utils.makeDefaults(html, userID, ctx);
|
|
279
292
|
api.postFormData = function (url, body) {
|
|
280
293
|
return defaultFuncs.postFormData(url, ctx.jar, body);
|
|
281
294
|
};
|
|
295
|
+
|
|
282
296
|
api.getFreshDtsg = async function () {
|
|
283
297
|
try {
|
|
298
|
+
await delay(globalOptions.delayBetweenRequests || 1000);
|
|
284
299
|
const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
|
|
285
300
|
const $ = cheerio.load(res.body);
|
|
286
301
|
let newDtsg;
|
|
@@ -310,12 +325,18 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
310
325
|
|
|
311
326
|
return newDtsg;
|
|
312
327
|
} catch (e) {
|
|
313
|
-
|
|
328
|
+
log.error("Error getting fresh dtsg:", e.message);
|
|
314
329
|
return null;
|
|
315
330
|
}
|
|
316
331
|
};
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
|
|
333
|
+
// Load API modules
|
|
334
|
+
const srcPath = path.join(__dirname, 'src');
|
|
335
|
+
if (fs.existsSync(srcPath)) {
|
|
336
|
+
fs.readdirSync(srcPath).filter(v => v.endsWith('.js')).forEach(v => {
|
|
337
|
+
api[v.replace('.js', '')] = require(`./src/${v}`)(utils.makeDefaults(html, userID, ctx), api, ctx);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
319
340
|
|
|
320
341
|
// Store original sendMessage as the primary method
|
|
321
342
|
const originalSendMessage = api.sendMessage;
|
|
@@ -325,8 +346,7 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
325
346
|
try {
|
|
326
347
|
return await originalSendMessage(msg, threadID, callback, replyToMessage, isSingleUser);
|
|
327
348
|
} catch (error) {
|
|
328
|
-
|
|
329
|
-
console.log('sendMessage failed, using OldMessage fallback:', error.message);
|
|
349
|
+
log.warn('sendMessage failed, using OldMessage fallback:', error.message);
|
|
330
350
|
return api.OldMessage(msg, threadID, callback, replyToMessage, isSingleUser);
|
|
331
351
|
}
|
|
332
352
|
};
|
|
@@ -337,6 +357,7 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
337
357
|
};
|
|
338
358
|
|
|
339
359
|
api.listen = api.listenMqtt;
|
|
360
|
+
|
|
340
361
|
return {
|
|
341
362
|
ctx,
|
|
342
363
|
defaultFuncs,
|
|
@@ -362,23 +383,32 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
|
362
383
|
form.locale = 'en_US';
|
|
363
384
|
form.timezone = '240';
|
|
364
385
|
form.lgnjs = Math.floor(Date.now() / 1000);
|
|
386
|
+
|
|
365
387
|
const willBeCookies = html.split("\"_js_");
|
|
366
388
|
willBeCookies.slice(1).forEach(val => {
|
|
367
389
|
const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
|
|
368
390
|
jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
|
|
369
391
|
});
|
|
370
|
-
|
|
392
|
+
|
|
393
|
+
log.info("Logging in with email/password...");
|
|
394
|
+
await delay(2000); // Delay to avoid rate limiting
|
|
395
|
+
|
|
371
396
|
const loginRes = await utils.post(
|
|
372
397
|
"https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
|
|
373
398
|
jar,
|
|
374
399
|
form,
|
|
375
400
|
loginOptions
|
|
376
401
|
);
|
|
402
|
+
|
|
377
403
|
await utils.saveCookies(jar)(loginRes);
|
|
378
404
|
const headers = loginRes.headers;
|
|
379
|
-
|
|
405
|
+
|
|
406
|
+
if (!headers.location) {
|
|
407
|
+
throw new Error("Wrong username/password or account locked.");
|
|
408
|
+
}
|
|
409
|
+
|
|
380
410
|
if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
|
|
381
|
-
log.info("login", "
|
|
411
|
+
log.info("login", "Login approval required. Checking checkpoint...");
|
|
382
412
|
const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
|
|
383
413
|
await utils.saveCookies(jar)(checkpointRes);
|
|
384
414
|
const checkpointHtml = checkpointRes.body;
|
|
@@ -387,6 +417,7 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
|
387
417
|
$("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
|
|
388
418
|
checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
|
|
389
419
|
const form = utils.arrToForm(checkpointForm);
|
|
420
|
+
|
|
390
421
|
if (checkpointHtml.includes("checkpoint/?next")) {
|
|
391
422
|
return new Promise((resolve, reject) => {
|
|
392
423
|
const submit2FA = async (code) => {
|
|
@@ -422,7 +453,11 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
|
422
453
|
};
|
|
423
454
|
});
|
|
424
455
|
}
|
|
425
|
-
|
|
456
|
+
|
|
457
|
+
if (!loginOptions.forceLogin) {
|
|
458
|
+
throw new Error("Couldn't login. Facebook might have blocked this account or requires verification.");
|
|
459
|
+
}
|
|
460
|
+
|
|
426
461
|
form['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
|
|
427
462
|
await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
|
|
428
463
|
form.name_action_selected = 'save_device';
|
|
@@ -430,39 +465,40 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
|
430
465
|
const appState = utils.getAppState(jar);
|
|
431
466
|
return await loginHelper(appState, email, password, loginOptions, callback);
|
|
432
467
|
}
|
|
468
|
+
|
|
433
469
|
await utils.get('https://www.facebook.com/', jar, null, loginOptions);
|
|
434
470
|
return await utils.saveCookies(jar);
|
|
435
471
|
} catch (error) {
|
|
472
|
+
log.error("Login error:", error.message);
|
|
436
473
|
callback(error);
|
|
437
474
|
}
|
|
438
475
|
};
|
|
439
476
|
}
|
|
440
477
|
|
|
441
|
-
|
|
442
478
|
function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
|
|
443
479
|
let mainPromise = null;
|
|
444
480
|
const jar = utils.getJar();
|
|
481
|
+
|
|
445
482
|
if (appState) {
|
|
446
483
|
try {
|
|
447
|
-
appState
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
appState = appState;
|
|
451
|
-
} catch (e) {
|
|
452
|
-
return callback(new Error("Failed to parse appState"));
|
|
484
|
+
if (typeof appState === 'string') {
|
|
485
|
+
appState = JSON.parse(appState);
|
|
453
486
|
}
|
|
487
|
+
} catch (e) {
|
|
488
|
+
return callback(new Error("Failed to parse appState: " + e.message));
|
|
454
489
|
}
|
|
455
490
|
|
|
456
491
|
try {
|
|
457
492
|
appState.forEach(c => {
|
|
458
|
-
const str = `${c.key}=${c.value};
|
|
493
|
+
const str = `${c.key}=${c.value}; ${c.expires ? 'expires=' + c.expires + ';' : ''} domain=${c.domain}; path=${c.path};`;
|
|
459
494
|
jar.setCookie(str, "http://" + c.domain);
|
|
460
495
|
});
|
|
461
496
|
|
|
462
497
|
mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
|
|
463
498
|
.then(utils.saveCookies(jar));
|
|
464
499
|
} catch (e) {
|
|
465
|
-
|
|
500
|
+
log.error("Error setting cookies:", e.message);
|
|
501
|
+
return callback(new Error("Failed to set appState cookies"));
|
|
466
502
|
}
|
|
467
503
|
} else {
|
|
468
504
|
mainPromise = utils
|
|
@@ -487,7 +523,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
487
523
|
.then(res => {
|
|
488
524
|
const mobileAgentRegex = /MPageLoadClientMetrics/gs;
|
|
489
525
|
if (!mobileAgentRegex.test(res.body)) {
|
|
490
|
-
globalOptions.userAgent =
|
|
526
|
+
globalOptions.userAgent = getRandomUserAgent();
|
|
491
527
|
return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
|
|
492
528
|
}
|
|
493
529
|
return res;
|
|
@@ -517,11 +553,11 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
517
553
|
callback(null, api);
|
|
518
554
|
})
|
|
519
555
|
.catch(e => {
|
|
556
|
+
log.error("Login helper error:", e.message);
|
|
520
557
|
callback(e);
|
|
521
558
|
});
|
|
522
559
|
}
|
|
523
560
|
|
|
524
|
-
|
|
525
561
|
function login(loginData, options, callback) {
|
|
526
562
|
// Check for updates (non-blocking, only once per session)
|
|
527
563
|
if (!global.stfcaUpdateChecked) {
|
|
@@ -548,7 +584,8 @@ function login(loginData, options, callback) {
|
|
|
548
584
|
logRecordSize: 100,
|
|
549
585
|
online: false,
|
|
550
586
|
emitReady: false,
|
|
551
|
-
|
|
587
|
+
delayBetweenRequests: 1500,
|
|
588
|
+
userAgent: getRandomUserAgent()
|
|
552
589
|
};
|
|
553
590
|
|
|
554
591
|
var prCallback = null;
|
|
@@ -566,19 +603,24 @@ function login(loginData, options, callback) {
|
|
|
566
603
|
callback = prCallback;
|
|
567
604
|
}
|
|
568
605
|
|
|
569
|
-
|
|
606
|
+
// Handle different login methods
|
|
607
|
+
if (loginData.appState) {
|
|
608
|
+
setOptions(globalOptions, options);
|
|
609
|
+
loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
|
|
610
|
+
} else if (loginData.email && loginData.password) {
|
|
570
611
|
setOptions(globalOptions, {
|
|
571
|
-
logLevel: "silent",
|
|
572
612
|
forceLogin: true,
|
|
573
|
-
userAgent:
|
|
613
|
+
userAgent: getRandomUserAgent(),
|
|
614
|
+
delayBetweenRequests: 1500
|
|
574
615
|
});
|
|
575
|
-
loginHelper(
|
|
576
|
-
} else
|
|
577
|
-
|
|
578
|
-
|
|
616
|
+
loginHelper(null, loginData.email, loginData.password, globalOptions, callback, prCallback);
|
|
617
|
+
} else {
|
|
618
|
+
const error = new Error("Invalid login data. Provide either appState or email/password");
|
|
619
|
+
if (callback) callback(error);
|
|
620
|
+
else throw error;
|
|
579
621
|
}
|
|
622
|
+
|
|
580
623
|
return returnPromise;
|
|
581
624
|
}
|
|
582
625
|
|
|
583
|
-
|
|
584
626
|
module.exports = login;
|