fca-pretest 1.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. package/index.js +43 -88
  2. package/package.json +1 -1
  3. package/src/sendMessage.js +471 -328
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  'use strict';
2
3
 
3
4
  /**
@@ -36,7 +37,7 @@ global.Fca = new Object({
36
37
  ObjPriyansh: {
37
38
  "Language": "en",
38
39
  "PreKey": "",
39
- "AutoUpdate": true,
40
+ "AutoUpdate": false,
40
41
  "MainColor": "#9900FF",
41
42
  "MainName": "[ FCA-PRIYANSH ]",
42
43
  "Logo": true,
@@ -300,7 +301,7 @@ function ClassicHTML(UserName,Type,link) {
300
301
  icons.style="opacity: 1"
301
302
  call.style="opacity: 1"
302
303
  },6000)
303
-
304
+
304
305
  </script>
305
306
 
306
307
  <style>/* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300&display=swap'); */
@@ -321,7 +322,7 @@ function ClassicHTML(UserName,Type,link) {
321
322
  height: 100%;
322
323
  width: 100%;
323
324
  }
324
-
325
+
325
326
  }
326
327
  @media only screen and (min-width:46.1875em) and (max-width:63.9375em){
327
328
  video{
@@ -346,7 +347,7 @@ function ClassicHTML(UserName,Type,link) {
346
347
  height: 100%;
347
348
  display: block;
348
349
  }
349
-
350
+
350
351
  }
351
352
  body {
352
353
  height: 100vh;
@@ -387,8 +388,8 @@ body::after {
387
388
  display: none;
388
389
 
389
390
  z-index: 99;
390
-
391
-
391
+
392
+
392
393
  }
393
394
  @keyframes move{
394
395
  0% {
@@ -433,12 +434,12 @@ body::after {
433
434
  background-size: 400%;
434
435
  /* background:linear-gradient(transparent,#45f3ff,#45f3ff,#45f3ff,transparent); */
435
436
  z-index: -1;
436
-
437
+
437
438
  }
438
439
  /* .container::after{
439
440
  content: "";
440
441
  position: absolute;
441
-
442
+
442
443
  inset: 3px;
443
444
  } */
444
445
  @keyframes animate{
@@ -464,8 +465,8 @@ body::after {
464
465
  border-top: 1px solid #FFF2;
465
466
  border-left: 1px solid #FFF2;
466
467
  box-shadow: 4px 4px 12px #0004;
467
-
468
-
468
+
469
+
469
470
  color: #FFF;
470
471
  overflow: hidden;
471
472
  transition: transform 0.7s;
@@ -480,7 +481,7 @@ body::after {
480
481
  border-radius: 50%;
481
482
  overflow: hidden;
482
483
  padding: 0px;
483
-
484
+
484
485
  }
485
486
  .card.move .imgbox{
486
487
  transform: translateY(0);
@@ -533,7 +534,7 @@ body::after {
533
534
  .imgbox img {
534
535
  width: 100%;
535
536
  height: 100%;
536
-
537
+
537
538
  }
538
539
  .name-job {
539
540
  width: 100%;
@@ -567,8 +568,8 @@ body::after {
567
568
  padding: 5px 10px;
568
569
  width: 85px;
569
570
  margin: 5px;
570
-
571
-
571
+
572
+
572
573
  cursor: pointer;
573
574
  }
574
575
  .btn-Follow a{
@@ -582,7 +583,7 @@ body::after {
582
583
  }
583
584
  .btn-Follow:hover{
584
585
  background-color: rgb(2, 250, 2);
585
-
586
+
586
587
  }
587
588
  .btn-Message a{
588
589
  text-decoration: none;
@@ -843,7 +844,7 @@ function buildAPI(globalOptions, html, jar) {
843
844
  }
844
845
  case false: {
845
846
  throw { error: global.Fca.Require.Language.Index.ErrAppState };
846
-
847
+
847
848
  }
848
849
  }
849
850
  }
@@ -868,7 +869,7 @@ function buildAPI(globalOptions, html, jar) {
868
869
  }
869
870
 
870
871
  let Slot = Object.keys(CHECK_MQTT);
871
-
872
+
872
873
  var mqttEndpoint,region,irisSeqID;
873
874
  Object.keys(CHECK_MQTT).map(function(MQTT) {
874
875
  if (CHECK_MQTT[MQTT] && !region) {
@@ -1064,7 +1065,7 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
1064
1065
  $("form input").map((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
1065
1066
  arr = arr.filter(v => { return v.val && v.val.length });
1066
1067
  var from2 = utils.arrToForm(arr);
1067
-
1068
+
1068
1069
  if (html.indexOf("checkpoint/?next") > -1) {
1069
1070
  setTimeout(() => {
1070
1071
  checkVerified = setInterval((_form) => {}, 5000, {
@@ -1693,7 +1694,7 @@ try {
1693
1694
  * It asks the user for their account and password, and then saves it to the database.
1694
1695
  */
1695
1696
 
1696
- /*async function setUserNameAndPassWord() {
1697
+ function setUserNameAndPassWord() {
1697
1698
  let rl = readline.createInterface({
1698
1699
  input: process.stdin,
1699
1700
  output: process.stdout
@@ -1705,79 +1706,33 @@ try {
1705
1706
  console.log(chalk.bold.hex('#9900FF')("[</>]") + chalk.bold.yellow(' => ') + "Machine Version: " + chalk.bold.red(os.version()));
1706
1707
  console.log(chalk.bold.hex('#9900FF')("[</>]") + chalk.bold.yellow(' => ') + "Fca Version: " + chalk.bold.red(localbrand2) + '\n');
1707
1708
  try {
1708
- const configPath = process.argv[2] || "./acc.json";
1709
- const config = require(configPath);
1710
- const Account = config.EMAIL;
1711
- const Password = config.PASSWORD;
1712
-
1713
- try {
1714
- await Database.set("Account", Account);
1715
- await Database.set("Password", Password);
1716
- }
1717
- catch (e) {
1718
- logger.Warning(Language.ErrDataBase);
1719
- logger.Error();
1720
- process.exit(0);
1721
- }
1722
- if (global.Fca.Require.Priyansh.ResetDataLogin) {
1723
- global.Fca.Require.Priyansh.ResetDataLogin = false;
1724
- global.Fca.Require.fs.writeFileSync(process.cwd() + '/PriyanshFca.json', JSON.stringify(global.Fca.Require.Priyansh, null, 4));
1725
- }
1726
- logger.Success(Language.SuccessSetData);
1727
- process.exit(1);
1709
+ rl.question(Language.TypeAccount, (Account) => {
1710
+ if (!Account.includes("@") && global.Fca.Require.utils.getType(parseInt(Account)) != "Number") return logger.Normal(Language.TypeAccountError, function () { process.exit(1) }); //Very Human
1711
+ else rl.question(Language.TypePassword,async function (Password) {
1712
+ rl.close();
1713
+ try {
1714
+ await Database.set("Account", Account);
1715
+ await Database.set("Password", Password);
1716
+ }
1717
+ catch (e) {
1718
+ logger.Warning(Language.ErrDataBase);
1719
+ logger.Error();
1720
+ process.exit(0);
1721
+ }
1722
+ if (global.Fca.Require.Priyansh.ResetDataLogin) {
1723
+ global.Fca.Require.Priyansh.ResetDataLogin = false;
1724
+ global.Fca.Require.fs.writeFileSync(process.cwd() + '/PriyanshFca.json', JSON.stringify(global.Fca.Require.Priyansh, null, 4));
1725
+ }
1726
+ logger.Success(Language.SuccessSetData);
1727
+ process.exit(1);
1728
+ });
1729
+ })
1728
1730
  }
1729
1731
  catch (e) {
1730
1732
  logger.Error(e)
1731
1733
  }
1732
1734
  }
1733
1735
 
1734
- module.exports = setUserNameAndPassWord;
1735
- */
1736
-
1737
- const config = require('./../../config.json');
1738
-
1739
- function setUserNameAndPassWord() {
1740
- let rl = readline.createInterface({
1741
- input: process.stdin,
1742
- output: process.stdout
1743
- });
1744
- let localbrand2 = global.Fca.Version;
1745
- console.clear();
1746
- console.log(figlet.textSync('Horizon', {font: 'ANSI Shadow', horizontalLayout: 'default', verticalLayout: 'default', width: 0, whitespaceBreak: true }));
1747
- console.log(chalk.bold.hex('#9900FF')("[</>]") + chalk.bold.yellow(' => ') + "Operating System: " + chalk.bold.red(os.type()));
1748
- console.log(chalk.bold.hex('#9900FF')("[</>]") + chalk.bold.yellow(' => ') + "Machine Version: " + chalk.bold.red(os.version()));
1749
- console.log(chalk.bold.hex('#9900FF')("[</>]") + chalk.bold.yellow(' => ') + "Fca Version: " + chalk.bold.red(localbrand2) + '\n');
1750
-
1751
- try {
1752
- const Account = config.EMAIL; // Fetch account from config
1753
- const Password = config.PASSWORD; // Fetch password from config
1754
- // Wrap the following code in a Promise to use async/await
1755
- new Promise(async (resolve, reject) => {
1756
- try {
1757
- // Save account and password to database
1758
- await Database.set("Account", Account);
1759
- await Database.set("Password", Password);
1760
- resolve();
1761
- } catch (e) {
1762
- reject(e);
1763
- }
1764
- }).then(() => {
1765
- if (global.Fca.Require.Priyansh.ResetDataLogin) {
1766
- global.Fca.Require.Priyansh.ResetDataLogin = false;
1767
- global.Fca.Require.fs.writeFileSync(process.cwd() + '/PriyanshFca.json', JSON.stringify(global.Fca.Require.Priyansh, null, 4));
1768
- }
1769
- logger.Success(Language.SuccessSetData);
1770
- process.exit(1);
1771
- }).catch((error) => {
1772
- logger.Warning(Language.ErrDataBase);
1773
- logger.Error();
1774
- process.exit(0);
1775
- });
1776
- } catch (e) {
1777
- logger.Error(e);
1778
- }
1779
- }
1780
-
1781
1736
  /**
1782
1737
  * @param {{ email: any; password: any; appState: any; }} loginData
1783
1738
  * @param {{}} options
@@ -1820,7 +1775,7 @@ function login(loginData, options, callback) {
1820
1775
  emitReady: false,
1821
1776
  userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8"
1822
1777
  };
1823
-
1778
+
1824
1779
  if (loginData.email && loginData.password) {
1825
1780
  setOptions(globalOptions, {
1826
1781
  logLevel: "silent",
@@ -1846,7 +1801,7 @@ function login(loginData, options, callback) {
1846
1801
  };
1847
1802
  callback = prCallback;
1848
1803
  }
1849
-
1804
+
1850
1805
  (async function() {
1851
1806
  var Premium = require("./Extra/Src/Premium");
1852
1807
  global.Fca.Data.PremText = await Premium(global.Fca.Require.Security.create().uuid) || "Bạn Đang Sài Phiên Bản: Free !";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fca-pretest",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Facebook-chat-api made by Priyansh rajput",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,334 +1,477 @@
1
1
  "use strict";
2
2
 
3
- /**
4
- * Được Fix Hay Làm Màu Bởi: @HarryWakazaki
5
- * 21/4/2022
6
- */
7
-
8
- var utils = require("../utils");
9
- var log = require("npmlog");
10
- var bluebird = require("bluebird");
11
-
12
- var allowedProperties = {
13
- attachment: true,
14
- url: true,
15
- sticker: true,
16
- emoji: true,
17
- emojiSize: true,
18
- body: true,
19
- mentions: true,
20
- location: true,
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ const allowedProperties = {
7
+ attachment: true,
8
+ url: true,
9
+ sticker: true,
10
+ emoji: true,
11
+ emojiSize: true,
12
+ body: true,
13
+ mentions: true,
14
+ location: true
21
15
  };
22
16
 
17
+ function removeSpecialChar(inputString) { // remove char banned by facebook
18
+ if (typeof inputString !== "string")
19
+ return inputString;
20
+ // Convert string to Buffer
21
+ const buffer = Buffer.from(inputString, 'utf8');
22
+
23
+ // Filter buffer start with ef b8 8f
24
+ let filteredBuffer = Buffer.alloc(0);
25
+ for (let i = 0; i < buffer.length; i++) {
26
+ if (buffer[i] === 0xEF && buffer[i + 1] === 0xB8 && buffer[i + 2] === 0x8F) {
27
+ i += 2; // Skip 3 bytes of buffer starting with ef b8 8f
28
+ } else {
29
+ filteredBuffer = Buffer.concat([filteredBuffer, buffer.slice(i, i + 1)]);
30
+ }
31
+ }
32
+
33
+ // Convert filtered buffer to string
34
+ const convertedString = filteredBuffer.toString('utf8');
35
+
36
+ return convertedString;
37
+ }
38
+
23
39
  module.exports = function (defaultFuncs, api, ctx) {
24
- function uploadAttachment(attachments, callback) {
25
- var uploads = [];
26
-
27
- // create an array of promises
28
- for (var i = 0; i < attachments.length; i++) {
29
- if (!utils.isReadableStream(attachments[i])) throw { error: "Attachment should be a readable stream and not " + utils.getType(attachments[i]) + "." };
30
- var form = {
31
- upload_1024: attachments[i],
32
- voice_clip: "true"
33
- };
34
-
35
- uploads.push(
36
- defaultFuncs
37
- .postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {})
38
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
39
- .then(function (resData) {
40
- if (resData.error) throw resData;
41
- // We have to return the data unformatted unless we want to change it
42
- // back in sendMessage.
43
- return resData.payload.metadata[0];
44
- })
45
- );
46
- }
47
-
48
- // resolve all promises
49
- bluebird
50
- .all(uploads)
51
- .then(resData => callback(null, resData)
52
- )
53
- .catch(function (err) {
54
- log.error("uploadAttachment", err);
55
- return callback(err);
56
- });
57
- }
58
-
59
- function getUrl(url, callback) {
60
- var form = {
61
- image_height: 960,
62
- image_width: 960,
63
- uri: url
64
- };
65
-
66
- defaultFuncs
67
- .post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
68
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
69
- .then(function (resData) {
70
- if (resData.error) return callback(resData);
71
- if (!resData.payload) return callback({ error: "Invalid url" });
72
- callback(null, resData.payload.share_data.share_params);
73
- })
74
- .catch(function (err) {
75
- log.error("getUrl", err);
76
- return callback(err);
77
- });
78
- }
79
-
80
- function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
81
- // There are three cases here:
82
- // 1. threadID is of type array, where we're starting a new group chat with users
83
- // specified in the array.
84
- // 2. User is sending a message to a specific user.
85
- // 3. No additional form params and the message goes to an existing group chat.
86
- if (utils.getType(threadID) === "Array") {
87
- for (var i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
88
- form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
89
- form["client_thread_id"] = "root:" + messageAndOTID;
90
- log.info("sendMessage", "Sending message to multiple users: " + threadID);
91
- }
92
- else {
93
- // This means that threadID is the id of a user, and the chat
94
- // is a single person chat
95
- if (isSingleUser) {
96
- form["specific_to_list[0]"] = "fbid:" + threadID;
97
- form["specific_to_list[1]"] = "fbid:" + ctx.userID;
98
- form["other_user_fbid"] = threadID;
99
- }
100
- else form["thread_fbid"] = threadID;
101
- }
102
-
103
- if (ctx.globalOptions.pageID) {
104
- form["author"] = "fbid:" + ctx.globalOptions.pageID;
105
- form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
106
- form["creator_info[creatorID]"] = ctx.userID;
107
- form["creator_info[creatorType]"] = "direct_admin";
108
- form["creator_info[labelType]"] = "sent_message";
109
- form["creator_info[pageID]"] = ctx.globalOptions.pageID;
110
- form["request_user_id"] = ctx.globalOptions.pageID;
111
- form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
112
- }
113
-
114
- defaultFuncs
115
- .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
116
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
117
- .then(function (resData) {
118
- if (!resData) return callback({ error: "Send message failed." });
119
- if (resData.error) {
120
- if (resData.error === 1545012) log.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
121
- return callback(resData);
122
- }
123
-
124
- var messageInfo = resData.payload.actions.reduce(function (p, v) {
125
- return (
126
- {
127
- threadID: v.thread_fbid,
128
- messageID: v.message_id,
129
- timestamp: v.timestamp
130
- } || p
131
- );
132
- }, null);
133
- return callback(null, messageInfo);
134
- })
135
- .catch(function (err) {
136
- log.error("sendMessage", err);
137
- if (utils.getType(err) == "Object" && err.error === "Not logged in.") ctx.loggedIn = false;
138
- return callback(err,null);
139
- });
140
- }
141
-
142
- function send(form, threadID, messageAndOTID, callback, isGroup) {
143
- //Full Fix sendMessage
144
- if (utils.getType(threadID) === "Array") sendContent(form, threadID, false, messageAndOTID, callback);
145
- else {
146
- var THREADFIX = "ThreadID".replace("ThreadID",threadID); // i cũng đôn nâu
147
- if (THREADFIX.length <= 15 || global.Fca.isUser.includes(threadID)) sendContent(form, threadID, !isGroup, messageAndOTID, callback);
148
- else if (THREADFIX.length >= 15 && THREADFIX.indexOf(1) != 0 || global.Fca.isThread.includes(threadID)) sendContent(form, threadID, threadID.length === 15, messageAndOTID, callback);
149
- else {
150
- if (global.Fca.Data.event.isGroup) {
151
- sendContent(form, threadID, threadID.length === 15, messageAndOTID, callback);
152
- global.Fca.isThread.push(threadID);
153
- }
154
- else {
155
- sendContent(form, threadID, !isGroup, messageAndOTID, callback);
156
- global.Fca.isUser.push(threadID)
157
- }
158
- }
159
- }
160
- }
161
-
162
- function handleUrl(msg, form, callback, cb) {
163
- if (msg.url) {
164
- form["shareable_attachment[share_type]"] = "100";
165
- getUrl(msg.url, function (err, params) {
166
- if (err) return callback(err);
167
- form["shareable_attachment[share_params]"] = params;
168
- cb();
169
- });
170
- }
171
- else cb();
172
- }
173
-
174
- function handleLocation(msg, form, callback, cb) {
175
- if (msg.location) {
176
- if (msg.location.latitude == null || msg.location.longitude == null) return callback({ error: "location property needs both latitude and longitude" });
177
- form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
178
- form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
179
- form["location_attachment[is_current_location]"] = !!msg.location.current;
180
- }
181
- cb();
182
- }
183
-
184
- function handleSticker(msg, form, callback, cb) {
185
- if (msg.sticker) form["sticker_id"] = msg.sticker;
186
- cb();
187
- }
188
-
189
- function handleEmoji(msg, form, callback, cb) {
190
- if (msg.emojiSize != null && msg.emoji == null) return callback({ error: "emoji property is empty" });
191
- if (msg.emoji) {
192
- if (msg.emojiSize == null) msg.emojiSize = "medium";
193
- if (msg.emojiSize != "small" && msg.emojiSize != "medium" && msg.emojiSize != "large") return callback({ error: "emojiSize property is invalid" });
194
- if (form["body"] != null && form["body"] != "") return callback({ error: "body is not empty" });
195
- form["body"] = msg.emoji;
196
- form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
197
- }
198
- cb();
199
- }
200
-
201
- function handleAttachment(msg, form, callback, cb) {
202
- if (msg.attachment) {
203
- form["image_ids"] = [];
204
- form["gif_ids"] = [];
205
- form["file_ids"] = [];
206
- form["video_ids"] = [];
207
- form["audio_ids"] = [];
208
-
209
- if (utils.getType(msg.attachment) !== "Array") msg.attachment = [msg.attachment];
210
-
211
- uploadAttachment(msg.attachment, function (err, files) {
212
- if (err) return callback(err);
213
- files.forEach(function (file) {
214
- var key = Object.keys(file);
215
- var type = key[0]; // image_id, file_id, etc
216
- form["" + type + "s"].push(file[type]); // push the id
217
- });
218
- cb();
219
- });
220
- }
221
- else cb();
222
- }
223
-
224
- function handleMention(msg, form, callback, cb) {
225
- if (msg.mentions) {
226
- for (let i = 0; i < msg.mentions.length; i++) {
227
- const mention = msg.mentions[i];
228
- const tag = mention.tag;
229
- if (typeof tag !== "string") return callback({ error: "Mention tags must be strings." });
230
- const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
231
- if (offset < 0) log.warn("handleMention", 'Mention for "' + tag + '" not found in message string.');
232
- if (mention.id == null) log.warn("handleMention", "Mention id should be non-null.");
233
-
234
- const id = mention.id || 0;
235
- const emptyChar = '\u200E';
236
- form["body"] = emptyChar + msg.body;
237
- form["profile_xmd[" + i + "][offset]"] = offset + 1;
238
- form["profile_xmd[" + i + "][length]"] = tag.length;
239
- form["profile_xmd[" + i + "][id]"] = id;
240
- form["profile_xmd[" + i + "][type]"] = "p";
241
- }
242
- }
243
- cb();
244
- }
245
-
246
- return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
247
- typeof isGroup == "undefined" ? isGroup = null : "";
248
- if (!callback && (utils.getType(threadID) === "Function" || utils.getType(threadID) === "AsyncFunction")) return threadID({ error: "Pass a threadID as a second argument." });
249
- if (!replyToMessage && utils.getType(callback) === "String") {
250
- replyToMessage = callback;
251
- callback = function () { };
252
- }
253
-
254
- var resolveFunc = function () { };
255
- var rejectFunc = function () { };
256
- var returnPromise = new Promise(function (resolve, reject) {
257
- resolveFunc = resolve;
258
- rejectFunc = reject;
259
- });
260
-
261
- if (!callback) {
262
- callback = function (err, data) {
263
- if (err) return rejectFunc(err);
264
- resolveFunc(data);
265
- };
266
- }
267
-
268
- var msgType = utils.getType(msg);
269
- var threadIDType = utils.getType(threadID);
270
- var messageIDType = utils.getType(replyToMessage);
271
-
272
- if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
273
-
274
- // Changing this to accomodate an array of users
275
- if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
276
-
277
- if (replyToMessage && messageIDType !== 'String') return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
278
-
279
- if (msgType === "String") msg = { body: msg };
280
- var disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
281
- if (disallowedProperties.length > 0) return callback({ error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`" });
282
-
283
- var messageAndOTID = utils.generateOfflineThreadingID();
284
-
285
- var form = {
286
- client: "mercury",
287
- action_type: "ma-type:user-generated-message",
288
- author: "fbid:" + ctx.userID,
289
- timestamp: Date.now(),
290
- timestamp_absolute: "Today",
291
- timestamp_relative: utils.generateTimestampRelative(),
292
- timestamp_time_passed: "0",
293
- is_unread: false,
294
- is_cleared: false,
295
- is_forward: false,
296
- is_filtered_content: false,
297
- is_filtered_content_bh: false,
298
- is_filtered_content_account: false,
299
- is_filtered_content_quasar: false,
300
- is_filtered_content_invalid_app: false,
301
- is_spoof_warning: false,
302
- source: "source:chat:web",
303
- "source_tags[0]": "source:chat",
304
- body: msg.body ? msg.body.toString().replace("\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f\ufe0f",' ') : "",
305
- html_body: false,
306
- ui_push_phase: "V3",
307
- status: "0",
308
- offline_threading_id: messageAndOTID,
309
- message_id: messageAndOTID,
310
- threading_id: utils.generateThreadingID(ctx.clientID),
311
- "ephemeral_ttl_mode:": "0",
312
- manual_retry_cnt: "0",
313
- has_attachment: !!(msg.attachment || msg.url || msg.sticker),
314
- signatureID: utils.getSignatureID(),
315
- replied_to_message_id: replyToMessage
316
- };
317
-
318
- handleLocation(msg, form, callback, () =>
319
- handleSticker(msg, form, callback, () =>
320
- handleAttachment(msg, form, callback, () =>
321
- handleUrl(msg, form, callback, () =>
322
- handleEmoji(msg, form, callback, () =>
323
- handleMention(msg, form, callback, () =>
324
- send(form, threadID, messageAndOTID, callback, isGroup)
325
- )
326
- )
327
- )
328
- )
329
- )
330
- );
331
-
332
- return returnPromise;
333
- };
40
+ function uploadAttachment(attachments, callback) {
41
+ const uploads = [];
42
+
43
+ // create an array of promises
44
+ for (let i = 0; i < attachments.length; i++) {
45
+ if (!utils.isReadableStream(attachments[i])) {
46
+ throw {
47
+ error:
48
+ "Attachment should be a readable stream and not " +
49
+ utils.getType(attachments[i]) +
50
+ "."
51
+ };
52
+ }
53
+
54
+ const form = {
55
+ upload_1024: attachments[i],
56
+ voice_clip: "true"
57
+ };
58
+
59
+ uploads.push(
60
+ defaultFuncs
61
+ .postFormData(
62
+ "https://upload.facebook.com/ajax/mercury/upload.php",
63
+ ctx.jar,
64
+ form,
65
+ {}
66
+ )
67
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
68
+ .then(function (resData) {
69
+ if (resData.error) {
70
+ throw resData;
71
+ }
72
+
73
+ // We have to return the data unformatted unless we want to change it
74
+ // back in sendMessage.
75
+ return resData.payload.metadata[0];
76
+ })
77
+ );
78
+ }
79
+
80
+ // resolve all promises
81
+ Promise
82
+ .all(uploads)
83
+ .then(function (resData) {
84
+ callback(null, resData);
85
+ })
86
+ .catch(function (err) {
87
+ log.error("uploadAttachment", err);
88
+ return callback(err);
89
+ });
90
+ }
91
+
92
+ function getUrl(url, callback) {
93
+ const form = {
94
+ image_height: 960,
95
+ image_width: 960,
96
+ uri: url
97
+ };
98
+
99
+ defaultFuncs
100
+ .post(
101
+ "https://www.facebook.com/message_share_attachment/fromURI/",
102
+ ctx.jar,
103
+ form
104
+ )
105
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
106
+ .then(function (resData) {
107
+ if (resData.error) {
108
+ return callback(resData);
109
+ }
110
+
111
+ if (!resData.payload) {
112
+ return callback({ error: "Invalid url" });
113
+ }
114
+
115
+ callback(null, resData.payload.share_data.share_params);
116
+ })
117
+ .catch(function (err) {
118
+ log.error("getUrl", err);
119
+ return callback(err);
120
+ });
121
+ }
122
+
123
+ function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
124
+ // There are three cases here:
125
+ // 1. threadID is of type array, where we're starting a new group chat with users
126
+ // specified in the array.
127
+ // 2. User is sending a message to a specific user.
128
+ // 3. No additional form params and the message goes to an existing group chat.
129
+ if (utils.getType(threadID) === "Array") {
130
+ for (let i = 0; i < threadID.length; i++) {
131
+ form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
132
+ }
133
+ form["specific_to_list[" + threadID.length + "]"] = "fbid:" + (ctx.i_userID || ctx.userID);
134
+ form["client_thread_id"] = "root:" + messageAndOTID;
135
+ log.info("sendMessage", "Sending message to multiple users: " + threadID);
136
+ } else {
137
+ // This means that threadID is the id of a user, and the chat
138
+ // is a single person chat
139
+ if (isSingleUser) {
140
+ form["specific_to_list[0]"] = "fbid:" + threadID;
141
+ form["specific_to_list[1]"] = "fbid:" + (ctx.i_userID || ctx.userID);
142
+ form["other_user_fbid"] = threadID;
143
+ } else {
144
+ form["thread_fbid"] = threadID;
145
+ }
146
+ }
147
+
148
+ if (ctx.globalOptions.pageID) {
149
+ form["author"] = "fbid:" + ctx.globalOptions.pageID;
150
+ form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
151
+ form["creator_info[creatorID]"] = ctx.i_userID || ctx.userID;
152
+ form["creator_info[creatorType]"] = "direct_admin";
153
+ form["creator_info[labelType]"] = "sent_message";
154
+ form["creator_info[pageID]"] = ctx.globalOptions.pageID;
155
+ form["request_user_id"] = ctx.globalOptions.pageID;
156
+ form["creator_info[profileURI]"] =
157
+ "https://www.facebook.com/profile.php?id=" + (ctx.i_userID || ctx.userID);
158
+ }
159
+
160
+ defaultFuncs
161
+ .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
162
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
163
+ .then(function (resData) {
164
+ if (!resData) {
165
+ return callback({ error: "Send message failed." });
166
+ }
167
+
168
+ if (resData.error) {
169
+ if (resData.error === 1545012) {
170
+ log.warn(
171
+ "sendMessage",
172
+ "Got error 1545012. This might mean that you're not part of the conversation " +
173
+ threadID
174
+ );
175
+ }
176
+ else {
177
+ log.error("sendMessage", resData);
178
+ }
179
+ return callback(resData);
180
+ }
181
+
182
+ const messageInfo = resData.payload.actions.reduce(function (p, v) {
183
+ return (
184
+ {
185
+ threadID: v.thread_fbid,
186
+ messageID: v.message_id,
187
+ timestamp: v.timestamp
188
+ } || p
189
+ );
190
+ }, null);
191
+
192
+ return callback(null, messageInfo);
193
+ })
194
+ .catch(function (err) {
195
+ log.error("sendMessage", err);
196
+ if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
197
+ ctx.loggedIn = false;
198
+ }
199
+ return callback(err);
200
+ });
201
+ }
202
+
203
+ function send(form, threadID, messageAndOTID, callback, isGroup) {
204
+ // We're doing a query to this to check if the given id is the id of
205
+ // a user or of a group chat. The form will be different depending
206
+ // on that.
207
+ if (utils.getType(threadID) === "Array") {
208
+ sendContent(form, threadID, false, messageAndOTID, callback);
209
+ } else {
210
+ if (utils.getType(isGroup) != "Boolean") {
211
+ // Removed the use of api.getUserInfo() in the old version to reduce account lockout
212
+ sendContent(form, threadID, threadID.toString().length < 16, messageAndOTID, callback);
213
+ } else {
214
+ sendContent(form, threadID, !isGroup, messageAndOTID, callback);
215
+ }
216
+ }
217
+ }
218
+
219
+ function handleUrl(msg, form, callback, cb) {
220
+ if (msg.url) {
221
+ form["shareable_attachment[share_type]"] = "100";
222
+ getUrl(msg.url, function (err, params) {
223
+ if (err) {
224
+ return callback(err);
225
+ }
226
+
227
+ form["shareable_attachment[share_params]"] = params;
228
+ cb();
229
+ });
230
+ } else {
231
+ cb();
232
+ }
233
+ }
234
+
235
+ function handleLocation(msg, form, callback, cb) {
236
+ if (msg.location) {
237
+ if (msg.location.latitude == null || msg.location.longitude == null) {
238
+ return callback({ error: "location property needs both latitude and longitude" });
239
+ }
240
+
241
+ form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
242
+ form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
243
+ form["location_attachment[is_current_location]"] = !!msg.location.current;
244
+ }
245
+
246
+ cb();
247
+ }
248
+
249
+ function handleSticker(msg, form, callback, cb) {
250
+ if (msg.sticker) {
251
+ form["sticker_id"] = msg.sticker;
252
+ }
253
+ cb();
254
+ }
255
+
256
+ function handleEmoji(msg, form, callback, cb) {
257
+ if (msg.emojiSize != null && msg.emoji == null) {
258
+ return callback({ error: "emoji property is empty" });
259
+ }
260
+ if (msg.emoji) {
261
+ if (msg.emojiSize == null) {
262
+ msg.emojiSize = "medium";
263
+ }
264
+ if (
265
+ msg.emojiSize != "small" &&
266
+ msg.emojiSize != "medium" &&
267
+ msg.emojiSize != "large"
268
+ ) {
269
+ return callback({ error: "emojiSize property is invalid" });
270
+ }
271
+ if (form["body"] != null && form["body"] != "") {
272
+ return callback({ error: "body is not empty" });
273
+ }
274
+ form["body"] = msg.emoji;
275
+ form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
276
+ }
277
+ cb();
278
+ }
279
+
280
+ function handleAttachment(msg, form, callback, cb) {
281
+ if (msg.attachment) {
282
+ form["image_ids"] = [];
283
+ form["gif_ids"] = [];
284
+ form["file_ids"] = [];
285
+ form["video_ids"] = [];
286
+ form["audio_ids"] = [];
287
+
288
+ if (utils.getType(msg.attachment) !== "Array") {
289
+ msg.attachment = [msg.attachment];
290
+ }
291
+
292
+ uploadAttachment(msg.attachment, function (err, files) {
293
+ if (err) {
294
+ return callback(err);
295
+ }
296
+
297
+ files.forEach(function (file) {
298
+ const key = Object.keys(file);
299
+ const type = key[0]; // image_id, file_id, etc
300
+ form["" + type + "s"].push(file[type]); // push the id
301
+ });
302
+ cb();
303
+ });
304
+ } else {
305
+ cb();
306
+ }
307
+ }
308
+
309
+ function handleMention(msg, form, callback, cb) {
310
+ if (msg.mentions) {
311
+ for (let i = 0; i < msg.mentions.length; i++) {
312
+ const mention = msg.mentions[i];
313
+
314
+ const tag = mention.tag;
315
+ if (typeof tag !== "string") {
316
+ return callback({ error: "Mention tags must be strings." });
317
+ }
318
+
319
+ const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
320
+
321
+ if (offset < 0) {
322
+ log.warn(
323
+ "handleMention",
324
+ 'Mention for "' + tag + '" not found in message string.'
325
+ );
326
+ }
327
+
328
+ if (mention.id == null) {
329
+ log.warn("handleMention", "Mention id should be non-null.");
330
+ }
331
+
332
+ const id = mention.id || 0;
333
+ form["profile_xmd[" + i + "][offset]"] = offset;
334
+ form["profile_xmd[" + i + "][length]"] = tag.length;
335
+ form["profile_xmd[" + i + "][id]"] = id;
336
+ form["profile_xmd[" + i + "][type]"] = "p";
337
+ }
338
+ }
339
+ cb();
340
+ }
341
+
342
+ return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
343
+ typeof isGroup == "undefined" ? isGroup = null : "";
344
+ if (
345
+ !callback &&
346
+ (utils.getType(threadID) === "Function" ||
347
+ utils.getType(threadID) === "AsyncFunction")
348
+ ) {
349
+ return threadID({ error: "Pass a threadID as a second argument." });
350
+ }
351
+ if (
352
+ !replyToMessage &&
353
+ utils.getType(callback) === "String"
354
+ ) {
355
+ replyToMessage = callback;
356
+ callback = function () { };
357
+ }
358
+
359
+ let resolveFunc = function () { };
360
+ let rejectFunc = function () { };
361
+ const returnPromise = new Promise(function (resolve, reject) {
362
+ resolveFunc = resolve;
363
+ rejectFunc = reject;
364
+ });
365
+
366
+ if (!callback) {
367
+ callback = function (err, friendList) {
368
+ if (err) {
369
+ return rejectFunc(err);
370
+ }
371
+ resolveFunc(friendList);
372
+ };
373
+ }
374
+
375
+ const msgType = utils.getType(msg);
376
+ const threadIDType = utils.getType(threadID);
377
+ const messageIDType = utils.getType(replyToMessage);
378
+
379
+ if (msgType !== "String" && msgType !== "Object") {
380
+ return callback({
381
+ error:
382
+ "Message should be of type string or object and not " + msgType + "."
383
+ });
384
+ }
385
+
386
+ // Changing this to accomodate an array of users
387
+ if (
388
+ threadIDType !== "Array" &&
389
+ threadIDType !== "Number" &&
390
+ threadIDType !== "String"
391
+ ) {
392
+ return callback({
393
+ error:
394
+ "ThreadID should be of type number, string, or array and not " +
395
+ threadIDType +
396
+ "."
397
+ });
398
+ }
399
+
400
+ if (replyToMessage && messageIDType !== 'String') {
401
+ return callback({
402
+ error:
403
+ "MessageID should be of type string and not " +
404
+ threadIDType +
405
+ "."
406
+ });
407
+ }
408
+
409
+ if (msgType === "String") {
410
+ msg = { body: msg };
411
+ }
412
+
413
+ if (utils.getType(msg.body) === "String") {
414
+ msg.body = removeSpecialChar(msg.body);
415
+ }
416
+
417
+ const disallowedProperties = Object.keys(msg).filter(
418
+ prop => !allowedProperties[prop]
419
+ );
420
+ if (disallowedProperties.length > 0) {
421
+ return callback({
422
+ error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`"
423
+ });
424
+ }
425
+
426
+ const messageAndOTID = utils.generateOfflineThreadingID();
427
+
428
+ const form = {
429
+ client: "mercury",
430
+ action_type: "ma-type:user-generated-message",
431
+ author: "fbid:" + (ctx.i_userID || ctx.userID),
432
+ timestamp: Date.now(),
433
+ timestamp_absolute: "Today",
434
+ timestamp_relative: utils.generateTimestampRelative(),
435
+ timestamp_time_passed: "0",
436
+ is_unread: false,
437
+ is_cleared: false,
438
+ is_forward: false,
439
+ is_filtered_content: false,
440
+ is_filtered_content_bh: false,
441
+ is_filtered_content_account: false,
442
+ is_filtered_content_quasar: false,
443
+ is_filtered_content_invalid_app: false,
444
+ is_spoof_warning: false,
445
+ source: "source:chat:web",
446
+ "source_tags[0]": "source:chat",
447
+ body: msg.body ? msg.body.toString() : "",
448
+ html_body: false,
449
+ ui_push_phase: "V3",
450
+ status: "0",
451
+ offline_threading_id: messageAndOTID,
452
+ message_id: messageAndOTID,
453
+ threading_id: utils.generateThreadingID(ctx.clientID),
454
+ "ephemeral_ttl_mode:": "0",
455
+ manual_retry_cnt: "0",
456
+ has_attachment: !!(msg.attachment || msg.url || msg.sticker),
457
+ signatureID: utils.getSignatureID(),
458
+ replied_to_message_id: replyToMessage
459
+ };
460
+
461
+ handleLocation(msg, form, callback, () =>
462
+ handleSticker(msg, form, callback, () =>
463
+ handleAttachment(msg, form, callback, () =>
464
+ handleUrl(msg, form, callback, () =>
465
+ handleEmoji(msg, form, callback, () =>
466
+ handleMention(msg, form, callback, () =>
467
+ send(form, threadID, messageAndOTID, callback, isGroup)
468
+ )
469
+ )
470
+ )
471
+ )
472
+ )
473
+ );
474
+
475
+ return returnPromise;
476
+ };
334
477
  };