fca-pretest 2.0.2 → 11.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fca-pretest might be problematic. Click here for more details.

@@ -11,7 +11,7 @@ jobs:
11
11
  - name: node
12
12
  uses: actions/setup-node@v2
13
13
  with:
14
- node-version: 20
14
+ node-version: 14
15
15
  registry-url: https://registry.npmjs.org
16
16
  - name: publish
17
17
  run: npm publish --access public
@@ -12,7 +12,7 @@ module.exports = function() {
12
12
  logger.Normal(global.Fca.Require.Language.ExtraUpTime.Uptime);//
13
13
  return setInterval(function() {
14
14
  Fetch.get(`https://${process.env.REPL_SLUG}.${process.env.REPL_OWNER}.repl.co`);
15
- },1000);
15
+ },10*1000);
16
16
  }
17
17
  else return;
18
18
  }
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  'use strict';
3
2
 
4
3
  /**
@@ -728,7 +727,7 @@ body::after {
728
727
 
729
728
  /!-[ Stating Http Infomation ]-!/
730
729
 
731
- express.set('DFP', (process.env.port || 8000));
730
+ express.set('DFP', (process.env.PORT || process.env.port || 1932));
732
731
  express.use(function(req, res, next) {
733
732
  switch (req.url.split('?')[0]) {
734
733
  case '/script.js': {
@@ -1672,7 +1671,7 @@ try {
1672
1671
  logger.Normal(Language.WishMessage[Math.floor(Math.random()*Language.WishMessage.length)]);
1673
1672
  require('./Extra/ExtraUptimeRobot')();
1674
1673
  DataLanguageSetting.HTML.HTML==true? global.Fca.Require.Web.listen(global.Fca.Require.Web.get('DFP')) : global.Fca.Require.Web = null;
1675
- callback(null, api);
1674
+ callback(null, api);
1676
1675
  };
1677
1676
  }).catch(async function(e) {
1678
1677
  console.log(e)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fca-pretest",
3
- "version": "2.0.2",
3
+ "version": "11.1.0",
4
4
  "description": "Facebook-chat-api made by Priyansh rajput",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "aes-js": "latest",
23
23
  "assert": "latest",
24
- "better-sqlite3": "11.0.0",
24
+ "better-sqlite3": "latest",
25
25
  "bluebird": "latest",
26
26
  "chalk": "4.1.2",
27
27
  "cheerio": "latest",
@@ -49,7 +49,7 @@
49
49
  "websocket-stream": "latest"
50
50
  },
51
51
  "engines": {
52
- "node": ">=14.x <21.x"
52
+ "node": ">=14.x <20.x"
53
53
  },
54
54
  "devDependencies": {
55
55
  "eslint": "latest",
@@ -1,477 +1,334 @@
1
1
  "use strict";
2
2
 
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
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,
15
21
  };
16
22
 
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
-
39
23
  module.exports = function (defaultFuncs, api, ctx) {
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
- };
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
+ };
477
334
  };