fca-pretest 1.0.0 → 2.0.1

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.
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
  };