fca-orion-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/config.js +5 -0
  2. package/data/fcaVersion.json +0 -0
  3. package/index.js +417 -0
  4. package/instantUpdate.js +40 -0
  5. package/package.json +30 -0
  6. package/src/addExternalModule.js +19 -0
  7. package/src/addUserToGroup.js +113 -0
  8. package/src/changeAdminStatus.js +79 -0
  9. package/src/changeArchivedStatus.js +55 -0
  10. package/src/changeAvatar.js +126 -0
  11. package/src/changeBio.js +77 -0
  12. package/src/changeBlockedStatus.js +47 -0
  13. package/src/changeGroupImage.js +132 -0
  14. package/src/changeNickname.js +59 -0
  15. package/src/changeThreadColor.js +65 -0
  16. package/src/changeThreadEmoji.js +55 -0
  17. package/src/createNewGroup.js +86 -0
  18. package/src/createPoll.js +71 -0
  19. package/src/deleteMessage.js +56 -0
  20. package/src/deleteThread.js +56 -0
  21. package/src/forwardAttachment.js +60 -0
  22. package/src/getCurrentUserID.js +7 -0
  23. package/src/getEmojiUrl.js +29 -0
  24. package/src/getFriendsList.js +83 -0
  25. package/src/getMessage.js +796 -0
  26. package/src/getThreadHistory.js +666 -0
  27. package/src/getThreadInfo.js +232 -0
  28. package/src/getThreadList.js +241 -0
  29. package/src/getThreadPictures.js +79 -0
  30. package/src/getUserID.js +66 -0
  31. package/src/getUserInfo.js +74 -0
  32. package/src/handleFriendRequest.js +61 -0
  33. package/src/handleMessageRequest.js +65 -0
  34. package/src/httpGet.js +57 -0
  35. package/src/httpPost.js +57 -0
  36. package/src/httpPostFormData.js +63 -0
  37. package/src/listenMqtt.js +853 -0
  38. package/src/logout.js +75 -0
  39. package/src/markAsDelivered.js +58 -0
  40. package/src/markAsRead.js +80 -0
  41. package/src/markAsReadAll.js +50 -0
  42. package/src/markAsSeen.js +59 -0
  43. package/src/muteThread.js +52 -0
  44. package/src/refreshFb_dtsg.js +81 -0
  45. package/src/removeUserFromGroup.js +79 -0
  46. package/src/resolvePhotoUrl.js +45 -0
  47. package/src/searchForThread.js +53 -0
  48. package/src/sendMessage.js +477 -0
  49. package/src/sendTypingIndicator.js +103 -0
  50. package/src/setMessageReaction.js +121 -0
  51. package/src/setPostReaction.js +109 -0
  52. package/src/setTitle.js +86 -0
  53. package/src/threadColors.js +131 -0
  54. package/src/unfriend.js +52 -0
  55. package/src/unsendMessage.js +49 -0
  56. package/src/uploadAttachment.js +95 -0
  57. package/utils.js +1545 -0
package/config.js ADDED
@@ -0,0 +1,5 @@
1
+
2
+ module.exports = {
3
+ MainName: "[ test ]",
4
+ AutoUpdate: true
5
+ };
File without changes
package/index.js ADDED
@@ -0,0 +1,417 @@
1
+ "use strict";
2
+
3
+ const utils = require("./utils");
4
+ const log = require("npmlog");
5
+ const fs = require('fs');
6
+ const gradient = require("gradient-string");
7
+
8
+ const checkVerified = null;
9
+
10
+ const defaultLogRecordSize = 100;
11
+ log.maxRecordSize = defaultLogRecordSize;
12
+
13
+ const chalk = require('chalk');
14
+
15
+ const error = chalk.bold.red;
16
+ const warning = chalk.hex('#FFA500');
17
+ const success = chalk.greenBright;
18
+
19
+ const configContent = `
20
+ module.exports = {
21
+ MainName: "[ test ]",
22
+ AutoUpdate: true
23
+ };
24
+ `;
25
+
26
+ const configPath = 'config.js';
27
+
28
+ if (!fs.existsSync(configPath)) {
29
+ fs.writeFileSync(configPath, configContent);
30
+ }
31
+
32
+ const { MainName } = configPath;
33
+ const InstantUpdate = require('./instantUpdate.js');
34
+
35
+ function setOptions(globalOptions, options) {
36
+ Object.keys(options).map(function (key) {
37
+ switch (key) {
38
+ case 'online':
39
+ globalOptions.online = Boolean(options.online);
40
+ break;
41
+ case 'logLevel':
42
+ log.level = options.logLevel;
43
+ globalOptions.logLevel = options.logLevel;
44
+ break;
45
+ case 'logRecordSize':
46
+ log.maxRecordSize = options.logRecordSize;
47
+ globalOptions.logRecordSize = options.logRecordSize;
48
+ break;
49
+ case 'selfListen':
50
+ globalOptions.selfListen = Boolean(options.selfListen);
51
+ break;
52
+ case 'selfListenEvent':
53
+ globalOptions.selfListenEvent = options.selfListenEvent;
54
+ break;
55
+ case 'listenEvents':
56
+ globalOptions.listenEvents = Boolean(options.listenEvents);
57
+ break;
58
+ case 'pageID':
59
+ globalOptions.pageID = options.pageID.toString();
60
+ break;
61
+ case 'updatePresence':
62
+ globalOptions.updatePresence = Boolean(options.updatePresence);
63
+ break;
64
+ case 'forceLogin':
65
+ globalOptions.forceLogin = Boolean(options.forceLogin);
66
+ break;
67
+ case 'userAgent':
68
+ globalOptions.userAgent = options.userAgent;
69
+ break;
70
+ case 'autoMarkDelivery':
71
+ globalOptions.autoMarkDelivery = Boolean(options.autoMarkDelivery);
72
+ break;
73
+ case 'autoMarkRead':
74
+ globalOptions.autoMarkRead = Boolean(options.autoMarkRead);
75
+ break;
76
+ case 'listenTyping':
77
+ globalOptions.listenTyping = Boolean(options.listenTyping);
78
+ break;
79
+ case 'proxy':
80
+ if (typeof options.proxy != "string") {
81
+ delete globalOptions.proxy;
82
+ utils.setProxy();
83
+ } else {
84
+ globalOptions.proxy = options.proxy;
85
+ utils.setProxy(globalOptions.proxy);
86
+ }
87
+ break;
88
+ case 'autoReconnect':
89
+ globalOptions.autoReconnect = Boolean(options.autoReconnect);
90
+ break;
91
+ case 'emitReady':
92
+ globalOptions.emitReady = Boolean(options.emitReady);
93
+ break;
94
+ default:
95
+ console.log(gradient('orange', 'yellow')('setOptions') + chalk.warning("Unrecognized option given to setOptions: " + key));
96
+ break;
97
+ }
98
+ });
99
+ }
100
+
101
+ function buildAPI(globalOptions, html, jar) {
102
+ const maybeCookie = jar.getCookies("https://www.facebook.com").filter(function (val) {
103
+ return val.cookieString().split("=")[0] === "c_user";
104
+ });
105
+
106
+ const objCookie = jar.getCookies("https://www.facebook.com").reduce(function (obj, val) {
107
+ obj[val.cookieString().split("=")[0]] = val.cookieString().split("=")[1];
108
+ return obj;
109
+ }, {});
110
+
111
+ if (maybeCookie.length === 0) {
112
+ throw { error: "Error retrieving userID. This can be caused by a lot of things, including getting blocked by Facebook for logging in from an unknown location. Try logging in with a browser to verify." };
113
+ }
114
+
115
+ if (html.indexOf("/checkpoint/block/?next") > -1) {
116
+ console.log(warning("Checkpoint detected. Please log in with a browser to verify."));
117
+ }
118
+
119
+ console.log(gradient('orange', 'yellow').multiline(["▒█▀▄▒█▀▄░▄▀▄░░▒█▒██▀░▄▀▀░▀█▀░░░▄▀▄▒█▀▄░█░▄▀▄░█▄░█", "░█▀▒░█▀▄░▀▄▀░▀▄█░█▄▄░▀▄▄░▒█▒▒░░▀▄▀░█▀▄░█░▀▄▀░█▒▀█",].join('\n')));
120
+ console.log('');
121
+
122
+ const userID = maybeCookie[0].cookieString().split("=")[1].toString();
123
+ const i_userID = objCookie.i_user || null;
124
+ const localVersion = require('../package.json').version;
125
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` Logged in as ${userID}`));
126
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` The version of FCA-ORION you currently have is`) + chalk.greenBright(` ${localVersion}`));
127
+
128
+ try {
129
+ clearInterval(checkVerified);
130
+ } catch (_) { }
131
+
132
+ const clientID = (Math.random() * 2147483648 | 0).toString(16);
133
+
134
+
135
+ const oldFBMQTTMatch = html.match(/irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/);
136
+ let mqttEndpoint = null;
137
+ let region = null;
138
+ let irisSeqID = null;
139
+ let noMqttData = null;
140
+
141
+ if (oldFBMQTTMatch) {
142
+ irisSeqID = oldFBMQTTMatch[1];
143
+ mqttEndpoint = oldFBMQTTMatch[2];
144
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
145
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` Got this account's message region: ${region}`));
146
+ } else {
147
+ const newFBMQTTMatch = html.match(/{"app_id":"219994525426954","endpoint":"(.+?)","iris_seq_id":"(.+?)"}/);
148
+ if (newFBMQTTMatch) {
149
+ irisSeqID = newFBMQTTMatch[2];
150
+ mqttEndpoint = newFBMQTTMatch[1].replace(/\\\//g, "/");
151
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
152
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` Got this account's message region: ${region}`));
153
+ } else {
154
+ const legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/);
155
+ if (legacyFBMQTTMatch) {
156
+ mqttEndpoint = legacyFBMQTTMatch[4];
157
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
158
+ console.log(warning('Cannot get sequence ID with new RegExp. Fallback to old RegExp (without seqID)...'));
159
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` Got this account's message region: ${region}`));
160
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` [Unused] Polling endpoint: ${legacyFBMQTTMatch[6]}`));
161
+ } else {
162
+ console.log(warning('Cannot get MQTT region & sequence ID.'));
163
+ noMqttData = html;
164
+ }
165
+ }
166
+ }
167
+
168
+ // All data available to api functions
169
+ const ctx = {
170
+ userID: userID,
171
+ i_userID: i_userID,
172
+ jar: jar,
173
+ clientID: clientID,
174
+ globalOptions: globalOptions,
175
+ loggedIn: true,
176
+ access_token: 'NONE',
177
+ clientMutationId: 0,
178
+ mqttClient: undefined,
179
+ lastSeqId: irisSeqID,
180
+ syncToken: undefined,
181
+ mqttEndpoint,
182
+ region,
183
+ firstListen: true
184
+ };
185
+
186
+ const api = {
187
+ setOptions: setOptions.bind(null, globalOptions),
188
+ getAppState: function getAppState() {
189
+ const appState = utils.getAppState(jar);
190
+ // filter duplicate
191
+ return appState.filter((item, index, self) => self.findIndex((t) => { return t.key === item.key; }) === index);
192
+ }
193
+ };
194
+
195
+ if (noMqttData) {
196
+ api["htmlData"] = noMqttData;
197
+ }
198
+
199
+ const apiFuncNames = [
200
+ 'addExternalModule',
201
+ 'addUserToGroup',
202
+ 'changeAdminStatus',
203
+ 'changeArchivedStatus',
204
+ 'changeAvatar',
205
+ 'changeBio',
206
+ 'changeBlockedStatus',
207
+ 'changeGroupImage',
208
+ 'changeNickname',
209
+ 'changeThreadColor',
210
+ 'changeThreadEmoji',
211
+ 'createNewGroup',
212
+ 'createPoll',
213
+ 'deleteMessage',
214
+ 'deleteThread',
215
+ 'forwardAttachment',
216
+ 'getCurrentUserID',
217
+ 'getEmojiUrl',
218
+ 'getFriendsList',
219
+ 'getMessage',
220
+ 'getThreadHistory',
221
+ 'getThreadInfo',
222
+ 'getThreadList',
223
+ 'getThreadPictures',
224
+ 'getUserID',
225
+ 'getUserInfo',
226
+ 'handleMessageRequest',
227
+ 'listenMqtt',
228
+ 'logout',
229
+ 'markAsDelivered',
230
+ 'markAsRead',
231
+ 'markAsReadAll',
232
+ 'markAsSeen',
233
+ 'muteThread',
234
+ 'refreshFb_dtsg',
235
+ 'removeUserFromGroup',
236
+ 'resolvePhotoUrl',
237
+ 'searchForThread',
238
+ 'sendMessage',
239
+ 'sendTypingIndicator',
240
+ 'setMessageReaction',
241
+ 'setPostReaction',
242
+ 'setTitle',
243
+ 'threadColors',
244
+ 'unsendMessage',
245
+ 'unfriend',
246
+
247
+ // HTTP
248
+ 'httpGet',
249
+ 'httpPost',
250
+ 'httpPostFormData',
251
+
252
+ 'uploadAttachment'
253
+ ];
254
+
255
+ const defaultFuncs = utils.makeDefaults(html, i_userID || userID, ctx);
256
+
257
+ // Load all api functions in a loop
258
+ apiFuncNames.map(function (v) {
259
+ api[v] = require('./src/' + v)(defaultFuncs, api, ctx);
260
+ });
261
+
262
+ //Removing original `listen` that uses pull.
263
+ //Map it to listenMqtt instead for backward compatibly.
264
+ api.listen = api.listenMqtt;
265
+
266
+ return [ctx, defaultFuncs, api];
267
+ }
268
+
269
+ // Helps the login
270
+ function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
271
+ let mainPromise = null;
272
+ const jar = utils.getJar();
273
+
274
+ // If we're given an appState we loop through it and save each cookie
275
+ // back into the jar.
276
+ if (appState) {
277
+ // check and convert cookie to appState
278
+ if (utils.getType(appState) === 'Array' && appState.some(c => c.name)) {
279
+ appState = appState.map(c => {
280
+ c.key = c.name;
281
+ delete c.name;
282
+ return c;
283
+ });
284
+ }
285
+ else if (utils.getType(appState) === 'String') {
286
+ const arrayAppState = [];
287
+ appState.split(';').forEach(c => {
288
+ const [key, value] = c.split('=');
289
+
290
+ arrayAppState.push({
291
+ key: (key || "").trim(),
292
+ value: (value || "").trim(),
293
+ domain: "facebook.com",
294
+ path: "/",
295
+ expires: new Date().getTime() + 1000 * 60 * 60 * 24 * 365
296
+ });
297
+ });
298
+ appState = arrayAppState;
299
+ }
300
+
301
+ appState.map(function (c) {
302
+ const str = c.key + "=" + c.value + "; expires=" + c.expires + "; domain=" + c.domain + "; path=" + c.path + ";";
303
+ jar.setCookie(str, "http://" + c.domain);
304
+ });
305
+
306
+ // Load the main page.
307
+ mainPromise = utils
308
+ .get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
309
+ .then(utils.saveCookies(jar));
310
+ } else {
311
+ if (email) {
312
+ throw { error: "Currently, the login method by email and password is no longer supported, please use the login method by appState" };
313
+ }
314
+ else {
315
+ throw { error: "No appState given." };
316
+ }
317
+ }
318
+
319
+ let ctx = null;
320
+ let _defaultFuncs = null;
321
+ let api = null;
322
+
323
+ mainPromise = mainPromise
324
+ .then(function (res) {
325
+ // Hacky check for the redirection that happens on some ISPs, which doesn't return statusCode 3xx
326
+ const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
327
+ const redirect = reg.exec(res.body);
328
+ if (redirect && redirect[1]) {
329
+ return utils
330
+ .get(redirect[1], jar, null, globalOptions)
331
+ .then(utils.saveCookies(jar));
332
+ }
333
+ return res;
334
+ })
335
+ .then(function (res) {
336
+ const html = res.body;
337
+ const stuff = buildAPI(globalOptions, html, jar);
338
+ ctx = stuff[0];
339
+ _defaultFuncs = stuff[1];
340
+ api = stuff[2];
341
+ return res;
342
+ });
343
+
344
+ // given a pageID we log in as a page
345
+ if (globalOptions.pageID) {
346
+ mainPromise = mainPromise
347
+ .then(function () {
348
+ return utils
349
+ .get('https://www.facebook.com/' + ctx.globalOptions.pageID + '/messages/?section=messages&subsection=inbox', ctx.jar, null, globalOptions);
350
+ })
351
+ .then(function (resData) {
352
+ let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
353
+ url = url.substring(0, url.length - 1);
354
+
355
+ return utils
356
+ .get('https://www.facebook.com' + url, ctx.jar, null, globalOptions);
357
+ });
358
+ }
359
+
360
+ // At the end we call the callback or catch an exception
361
+ mainPromise
362
+ .then(function () {
363
+ console.log(gradient('orange', 'yellow')(`${MainName || "[ FCA-ORION ]"}`) + chalk.white(` Done logging in.`));
364
+ return callback(null, api);
365
+ })
366
+ .catch(function (e) {
367
+ console.log(error(e.error || e));
368
+ callback(e);
369
+ });
370
+ }
371
+
372
+ function login(loginData, options, callback) {
373
+ if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
374
+ callback = options;
375
+ options = {};
376
+ }
377
+
378
+ const globalOptions = {
379
+ selfListen: false,
380
+ selfListenEvent: false,
381
+ listenEvents: false,
382
+ listenTyping: false,
383
+ updatePresence: false,
384
+ forceLogin: false,
385
+ autoMarkDelivery: true,
386
+ autoMarkRead: false,
387
+ autoReconnect: true,
388
+ logRecordSize: defaultLogRecordSize,
389
+ online: true,
390
+ emitReady: false,
391
+ userAgent: "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"
392
+ };
393
+
394
+ setOptions(globalOptions, options);
395
+
396
+ let prCallback = null;
397
+ if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
398
+ let rejectFunc = null;
399
+ let resolveFunc = null;
400
+ var returnPromise = new Promise(function (resolve, reject) {
401
+ resolveFunc = resolve;
402
+ rejectFunc = reject;
403
+ });
404
+ prCallback = function (error, api) {
405
+ if (error) {
406
+ return rejectFunc(error);
407
+ }
408
+ return resolveFunc(api);
409
+ };
410
+ callback = prCallback;
411
+ }
412
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
413
+ return returnPromise;
414
+ }
415
+
416
+ InstantUpdate();
417
+ module.exports = login;
@@ -0,0 +1,40 @@
1
+ module.exports = async function() {
2
+ const got = require('got');
3
+ const chalk = require('chalk');
4
+ const error = chalk.bold.red;
5
+ const warning = chalk.hex('#FFA500');
6
+ const success = chalk.greenBright;
7
+ const { execSync } = require('child_process');
8
+ const { body } = await got('https://raw.githubusercontent.com/ivancotacte/Global_fca-project-orion/main/InstantAction.json');
9
+ const json = JSON.parse(body);
10
+ const LocalVersion = require('./package.json').version;
11
+ if (Number(LocalVersion.replace(/\./g,"")) < Number(json.Version.replace(/\./g,"")) ) {
12
+ console.log(warning(`[ FCA-UPDATE ] `) + chalk.white("To avoid errors, update FCA-ORIONS: " + LocalVersion + " -> " + json.Version));
13
+ console.log(warning(`[ FCA-UPDATE ] `) + chalk.white("Problem Description: " + json.Problem));
14
+ console.log(warning("[ FCA-UPDATE ] ") + chalk.white("Please contact to owner about update failed and screentshot error log at https://www.facebook.com/icotacteeee"));
15
+ await new Promise(resolve => setTimeout(resolve, 3000));
16
+ try {
17
+ execSync(`npm install fca-project-orion@${json.Version}`, { stdio: 'inherit' });
18
+ console.log(success("[ FCA-UPDATE ] ","Update Complete, Restarting..."));
19
+ await new Promise(resolve => setTimeout(resolve, 3000));
20
+ process.exit(1);
21
+ } catch (err) {
22
+ try {
23
+ console.log(error("[ FCA-UPDATE ] ") + chalk.white("Update Failed, Trying Another Method 1..."));
24
+ await new Promise(resolve => setTimeout(resolve, 3000));
25
+ execSync(`npm install fca-project-orion@${json.Version} --force`, { stdio: 'inherit' });
26
+ console.log(success("[ FCA-UPDATE ] ","Update Complete, Restarting..."));
27
+ await new Promise(resolve => setTimeout(resolve, 3000));
28
+ process.exit(1);
29
+ } catch (err) {
30
+ console.log(e);
31
+ console.log(warning("[ FCA-UPDATE ] ") + chalk.white("Update Failed, Please Update Manually"));
32
+ await new Promise(resolve => setTimeout(resolve, 3000));
33
+ console.log(warning("[ FCA-UPDATE ] ") + chalk.white("Please contact to owner about update failed and screentshot error log at https://www.facebook.com/icotacteeee"));
34
+ await new Promise(resolve => setTimeout(resolve, 3000));
35
+ process.exit(1);
36
+ }
37
+ }
38
+ } else {
39
+ }
40
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "fca-orion-api",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git://github.com/ivancotacte/fca-orion-api.git"
12
+ },
13
+ "keywords": [
14
+ "facebook",
15
+ "chat",
16
+ "api",
17
+ "fca"
18
+ ],
19
+ "bugs": {
20
+ "url": "https://github.com/ivancotacte/fca-orion-api/issues"
21
+ },
22
+ "author": "Avery, David, Maude, Benjamin, UIRI",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "got": "^11.8.6",
26
+ "gradient-string": "^2.0.2",
27
+ "npmlog": "^1.2.0",
28
+ "request": "^2.53.0"
29
+ }
30
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+
5
+ module.exports = function (defaultFuncs, api, ctx) {
6
+ return function addExternalModule(moduleObj) {
7
+ if (utils.getType(moduleObj) == "Object") {
8
+ for (const apiName in moduleObj) {
9
+ if (utils.getType(moduleObj[apiName]) == "Function") {
10
+ api[apiName] = moduleObj[apiName](defaultFuncs, api, ctx);
11
+ } else {
12
+ throw new Error(`Item "${apiName}" in moduleObj must be a function, not ${utils.getType(moduleObj[apiName])}!`);
13
+ }
14
+ }
15
+ } else {
16
+ throw new Error(`moduleObj must be an object, not ${utils.getType(moduleObj)}!`);
17
+ }
18
+ };
19
+ };
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function addUserToGroup(userID, threadID, callback) {
8
+ let resolveFunc = function () { };
9
+ let rejectFunc = function () { };
10
+ const returnPromise = new Promise(function (resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (
16
+ !callback &&
17
+ (utils.getType(threadID) === "Function" ||
18
+ utils.getType(threadID) === "AsyncFunction")
19
+ ) {
20
+ throw new utils.CustomError({ error: "please pass a threadID as a second argument." });
21
+ }
22
+
23
+ if (!callback) {
24
+ callback = function (err) {
25
+ if (err) {
26
+ return rejectFunc(err);
27
+ }
28
+ resolveFunc();
29
+ };
30
+ }
31
+
32
+ if (
33
+ utils.getType(threadID) !== "Number" &&
34
+ utils.getType(threadID) !== "String"
35
+ ) {
36
+ throw new utils.CustomError({
37
+ error:
38
+ "ThreadID should be of type Number or String and not " +
39
+ utils.getType(threadID) +
40
+ "."
41
+ });
42
+ }
43
+
44
+ if (utils.getType(userID) !== "Array") {
45
+ userID = [userID];
46
+ }
47
+
48
+ const messageAndOTID = utils.generateOfflineThreadingID();
49
+ const form = {
50
+ client: "mercury",
51
+ action_type: "ma-type:log-message",
52
+ author: "fbid:" + (ctx.i_userID || ctx.userID),
53
+ thread_id: "",
54
+ timestamp: Date.now(),
55
+ timestamp_absolute: "Today",
56
+ timestamp_relative: utils.generateTimestampRelative(),
57
+ timestamp_time_passed: "0",
58
+ is_unread: false,
59
+ is_cleared: false,
60
+ is_forward: false,
61
+ is_filtered_content: false,
62
+ is_filtered_content_bh: false,
63
+ is_filtered_content_account: false,
64
+ is_spoof_warning: false,
65
+ source: "source:chat:web",
66
+ "source_tags[0]": "source:chat",
67
+ log_message_type: "log:subscribe",
68
+ status: "0",
69
+ offline_threading_id: messageAndOTID,
70
+ message_id: messageAndOTID,
71
+ threading_id: utils.generateThreadingID(ctx.clientID),
72
+ manual_retry_cnt: "0",
73
+ thread_fbid: threadID
74
+ };
75
+
76
+ for (let i = 0; i < userID.length; i++) {
77
+ if (
78
+ utils.getType(userID[i]) !== "Number" &&
79
+ utils.getType(userID[i]) !== "String"
80
+ ) {
81
+ throw new utils.CustomError({
82
+ error:
83
+ "Elements of userID should be of type Number or String and not " +
84
+ utils.getType(userID[i]) +
85
+ "."
86
+ });
87
+ }
88
+
89
+ form["log_message_data[added_participants][" + i + "]"] =
90
+ "fbid:" + userID[i];
91
+ }
92
+
93
+ defaultFuncs
94
+ .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
95
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
96
+ .then(function (resData) {
97
+ if (!resData) {
98
+ throw new utils.CustomError({ error: "Add to group failed." });
99
+ }
100
+ if (resData.error) {
101
+ throw new utils.CustomError(resData);
102
+ }
103
+
104
+ return callback();
105
+ })
106
+ .catch(function (err) {
107
+ log.error("addUserToGroup", err);
108
+ return callback(err);
109
+ });
110
+
111
+ return returnPromise;
112
+ };
113
+ };