zuka-chat-bot 1.5.5 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. package/Main.js +1 -4
  2. package/package.json +1 -1
  3. package/src/sendMessage.js +317 -375
package/Main.js CHANGED
@@ -364,10 +364,7 @@ function buildAPI(globalOptions, html, jar, bypass_region) {
364
364
  };
365
365
  // Load all api functions in a loop
366
366
  apiFuncNames.map(v => api[v] = require('./src/' + v)(defaultFuncs, api, ctx));
367
-
368
- return [ctx, defaultFuncs, api];
369
-
370
- fs.readdirSync(__dirname + "/src").filter((/** @type {string} */File) => File.endsWith(".js") && !File.includes('Dev_')).map((/** @type {string} */File) => {
367
+ fs.readdirSync(__dirname + "/src").filter((/** @type {string} */File) => File.endsWith(".js") && !File.includes('Dev_')).map((/** @type {string} */File) => {
371
368
  if (File == 'getThreadInfo.js' && global.Fca.Require.FastConfig.AntiGetInfo.AntiGetThreadInfo != true || File == 'getUserInfo.js' && global.Fca.Require.FastConfig.AntiGetInfo.AntiGetUserInfo != true) api[File.split('.').slice(0, -1).join('.')] = require('./src/' + (File.includes('getThreadInfo') ? 'getThreadMain.js' : 'getUserInfoMain.js'))(defaultFuncs, api, ctx)
372
369
  else api[File.split('.').slice(0, -1).join('.')] = require('./src/' + File)(defaultFuncs, api, ctx)
373
370
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuka-chat-bot",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "Facebook-chat-api protect and deploy by Kanzu and HZI Team",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,386 +1,328 @@
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
3
  var utils = require("../utils");
9
4
  var log = require("npmlog");
10
5
  var bluebird = require("bluebird");
11
- var fs = require('fs-extra');
12
6
 
13
7
  var allowedProperties = {
14
- attachment: true,
15
- url: true,
16
- sticker: true,
17
- emoji: true,
18
- emojiSize: true,
19
- body: true,
20
- mentions: true,
21
- location: true,
8
+ attachment: true,
9
+ url: true,
10
+ sticker: true,
11
+ emoji: true,
12
+ emojiSize: true,
13
+ body: true,
14
+ mentions: true,
15
+ location: true,
22
16
  };
23
17
 
24
- var AntiText = "Your criminal activity was detected while attempting to send an Appstate file";
25
- var Location_Stack;
26
-
27
18
  module.exports = function (defaultFuncs, api, ctx) {
28
- function uploadAttachment(attachments, callback) {
29
- var uploads = [];
30
-
31
- // create an array of promises
32
- for (var i = 0; i < attachments.length; i++) {
33
- if (!utils.isReadableStream(attachments[i])) throw { error: "Attachment should be a readable stream and not " + utils.getType(attachments[i]) + "." };
34
- var form = {
35
- upload_1024: attachments[i],
36
- voice_clip: "true"
37
- };
38
-
39
- uploads.push(
40
- defaultFuncs
41
- .postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {})
42
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
43
- .then(function (resData) {
44
- if (resData.error) throw resData;
45
- // We have to return the data unformatted unless we want to change it
46
- // back in sendMessage.
47
- return resData.payload.metadata[0];
48
- })
49
- );
50
- }
51
-
52
- // resolve all promises
53
- bluebird
54
- .all(uploads)
55
- .then(resData => callback(null, resData)
56
- )
57
- .catch(function (err) {
58
- log.error("uploadAttachment", err);
59
- return callback(err);
60
- });
61
- }
62
-
63
- function getUrl(url, callback) {
64
- var form = {
65
- image_height: 960,
66
- image_width: 960,
67
- uri: url
68
- };
69
-
70
- defaultFuncs
71
- .post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
72
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
73
- .then(function (resData) {
74
- if (resData.error) return callback(resData);
75
- if (!resData.payload) return callback({ error: "Invalid url" });
76
- callback(null, resData.payload.share_data.share_params);
77
- })
78
- .catch(function (err) {
79
- log.error("getUrl", err);
80
- return callback(err);
81
- });
82
- }
83
-
84
- function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
85
- // There are three cases here:
86
- // 1. threadID is of type array, where we're starting a new group chat with users
87
- // specified in the array.
88
- // 2. User is sending a message to a specific user.
89
- // 3. No additional form params and the message goes to an existing group chat.
90
- if (utils.getType(threadID) === "Array") {
91
- for (var i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
92
- form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
93
- form["client_thread_id"] = "root:" + messageAndOTID;
94
- log.info("sendMessage", "Sending message to multiple users: " + threadID);
95
- }
96
- else {
97
- // This means that threadID is the id of a user, and the chat
98
- // is a single person chat
99
- if (isSingleUser) {
100
- form["specific_to_list[0]"] = "fbid:" + threadID;
101
- form["specific_to_list[1]"] = "fbid:" + ctx.userID;
102
- form["other_user_fbid"] = threadID;
103
- }
104
- else form["thread_fbid"] = threadID;
105
- }
106
-
107
- if (ctx.globalOptions.pageID) {
108
- form["author"] = "fbid:" + ctx.globalOptions.pageID;
109
- form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
110
- form["creator_info[creatorID]"] = ctx.userID;
111
- form["creator_info[creatorType]"] = "direct_admin";
112
- form["creator_info[labelType]"] = "sent_message";
113
- form["creator_info[pageID]"] = ctx.globalOptions.pageID;
114
- form["request_user_id"] = ctx.globalOptions.pageID;
115
- form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
116
- }
117
-
118
- if (global.Fca.Require.FastConfig.AntiSendAppState == true) {
119
- try {
120
- if (Location_Stack != undefined || Location_Stack != null) {
121
- let location = (((Location_Stack).replace("Error",'')).split('\n')[7]).split(' ');
122
- let format = {
123
- Source: (location[6]).split('s:')[0].replace("(",'') + 's',
124
- Line: (location[6]).split('s:')[1].replace(")",'')
125
- };
126
- form.body = AntiText + "\n- Source: " + format.Source + "\n- Line: " + format.Line;
127
- }
128
- }
129
- catch (e) {}
130
- }
131
-
132
- defaultFuncs
133
- .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
134
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
135
- .then(function (resData) {
136
- Location_Stack = undefined;
137
- if (!resData) return callback({ error: "Send message failed." });
138
- if (resData.error) {
139
- if (resData.error === 1545012) log.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
140
- return callback(resData);
141
- }
142
-
143
- var messageInfo = resData.payload.actions.reduce(function (p, v) {
144
- return (
145
- {
146
- threadID: v.thread_fbid,
147
- messageID: v.message_id,
148
- timestamp: v.timestamp
149
- } || p
150
- );
151
- }, null);
152
- return callback(null, messageInfo);
153
- })
154
- .catch(function (err) {
155
- log.error("sendMessage", err);
156
- if (utils.getType(err) == "Object" && err.error === "Not logged in.") ctx.loggedIn = false;
157
- return callback(err,null);
158
- });
159
- }
160
-
161
- function send(form, threadID, messageAndOTID, callback, isGroup) {
162
- //Full Fix sendMessage
163
- if (utils.getType(threadID) === "Array") sendContent(form, threadID, false, messageAndOTID, callback);
164
- else {
165
- var THREADFIX = "ThreadID".replace("ThreadID",threadID); // i cũng đôn nâu
166
- if (THREADFIX.length <= 15 || global.Fca.isUser.includes(threadID)) sendContent(form, threadID, !isGroup, messageAndOTID, callback);
167
- else if (THREADFIX.length >= 15 && THREADFIX.indexOf(1) != 0 || global.Fca.isThread.includes(threadID)) sendContent(form, threadID, threadID.length === 15, messageAndOTID, callback);
168
- else {
169
- if (global.Fca.Data.event.isGroup) {
170
- sendContent(form, threadID, threadID.length === 15, messageAndOTID, callback);
171
- global.Fca.isThread.push(threadID);
172
- }
173
- else {
174
- sendContent(form, threadID, !isGroup, messageAndOTID, callback);
175
- global.Fca.isUser.push(threadID);
176
- }
177
- }
178
- }
179
- }
180
-
181
- function handleUrl(msg, form, callback, cb) {
182
- if (msg.url) {
183
- form["shareable_attachment[share_type]"] = "100";
184
- getUrl(msg.url, function (err, params) {
185
- if (err) return callback(err);
186
- form["shareable_attachment[share_params]"] = params;
187
- cb();
188
- });
189
- }
190
- else cb();
191
- }
192
-
193
- function handleLocation(msg, form, callback, cb) {
194
- if (msg.location) {
195
- if (msg.location.latitude == null || msg.location.longitude == null) return callback({ error: "location property needs both latitude and longitude" });
196
- form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
197
- form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
198
- form["location_attachment[is_current_location]"] = !!msg.location.current;
199
- }
200
- cb();
201
- }
202
-
203
- function handleSticker(msg, form, callback, cb) {
204
- if (msg.sticker) form["sticker_id"] = msg.sticker;
205
- cb();
206
- }
207
-
208
- function handleEmoji(msg, form, callback, cb) {
209
- if (msg.emojiSize != null && msg.emoji == null) return callback({ error: "emoji property is empty" });
210
- if (msg.emoji) {
211
- if (msg.emojiSize == null) msg.emojiSize = "medium";
212
- if (msg.emojiSize != "small" && msg.emojiSize != "medium" && msg.emojiSize != "large") return callback({ error: "emojiSize property is invalid" });
213
- if (form["body"] != null && form["body"] != "") return callback({ error: "body is not empty" });
214
- form["body"] = msg.emoji;
215
- form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
216
- }
217
- cb();
218
- }
219
-
220
- function handleAttachment(msg, form, callback, cb) {
221
- if (msg.attachment) {
222
- form["image_ids"] = [];
223
- form["gif_ids"] = [];
224
- form["file_ids"] = [];
225
- form["video_ids"] = [];
226
- form["audio_ids"] = [];
227
-
228
- if (utils.getType(msg.attachment) !== "Array") msg.attachment = [msg.attachment];
229
-
230
- const isValidAttachment = attachment => /_id$/.test(attachment[0]);
231
-
232
- if (msg.attachment.every(isValidAttachment)) {
233
- msg.attachment.forEach(attachment => form[`${attachment[0]}s`].push(attachment[1]));
234
- return cb();
235
- }
236
-
237
- if (global.Fca.Require.FastConfig.AntiSendAppState) {
238
- try {
239
- const AllowList = [".png", ".mp3", ".mp4", ".wav", ".gif", ".jpg", ".tff"];
240
- const CheckList = [".json", ".js", ".txt", ".docx", '.php'];
241
- var Has;
242
- for (let i = 0; i < (msg.attachment).length; i++) {
243
- if (utils.isReadableStream((msg.attachment)[i])) {
244
- var path = (msg.attachment)[i].path != undefined ? (msg.attachment)[i].path : "nonpath";
245
- if (AllowList.some(i => path.includes(i))) continue;
246
- else if (CheckList.some(i => path.includes(i))) {
247
- let data = fs.readFileSync(path, 'utf-8');
248
- if (data.includes("datr")) {
249
- Has = true;
250
- var err = new Error();
251
- Location_Stack = err.stack;
252
- }
253
- else continue;
254
- }
255
- }
256
- }
257
- if (Has == true) {
258
- msg.attachment = [fs.createReadStream(__dirname + "/../Extra/Src/Image/checkmate.jpg")];
259
- }
260
- }
261
- catch (e) {}
262
- }
263
- uploadAttachment(msg.attachment, function (err, files) {
264
- if (err) return callback(err);
265
- files.forEach(function (file) {
266
- var key = Object.keys(file);
267
- var type = key[0]; // image_id, file_id, etc
268
- form["" + type + "s"].push(file[type]); // push the id
269
- });
270
- cb();
271
- });
272
- }
273
- else cb();
274
- }
275
-
276
- function handleMention(msg, form, callback, cb) {
277
- if (msg.mentions) {
278
- for (let i = 0; i < msg.mentions.length; i++) {
279
- const mention = msg.mentions[i];
280
- const tag = mention.tag;
281
- if (typeof tag !== "string") return callback({ error: "Mention tags must be strings." });
282
- const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
283
- if (offset < 0) log.warn("handleMention", 'Mention for "' + tag + '" not found in message string.');
284
- if (mention.id == null) log.warn("handleMention", "Mention id should be non-null.");
285
-
286
- const id = mention.id || 0;
287
- const emptyChar = '\u200E';
288
- form["body"] = emptyChar + msg.body;
289
- form["profile_xmd[" + i + "][offset]"] = offset + 1;
290
- form["profile_xmd[" + i + "][length]"] = tag.length;
291
- form["profile_xmd[" + i + "][id]"] = id;
292
- form["profile_xmd[" + i + "][type]"] = "p";
293
- }
294
- }
295
- cb();
296
- }
297
-
298
- return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
299
- typeof isGroup == "undefined" ? isGroup = null : "";
300
- if (!callback && (utils.getType(threadID) === "Function" || utils.getType(threadID) === "AsyncFunction")) return threadID({ error: "Pass a threadID as a second argument." });
301
- if (!replyToMessage && utils.getType(callback) === "String") {
302
- replyToMessage = callback;
303
- callback = function () { };
304
- }
305
-
306
- var resolveFunc = function () { };
307
- var rejectFunc = function () { };
308
- var returnPromise = new Promise(function (resolve, reject) {
309
- resolveFunc = resolve;
310
- rejectFunc = reject;
311
- });
312
-
313
- if (!callback) {
314
- callback = function (err, data) {
315
- if (err) return rejectFunc(err);
316
- resolveFunc(data);
317
- };
318
- }
319
-
320
- var msgType = utils.getType(msg);
321
- var threadIDType = utils.getType(threadID);
322
- var messageIDType = utils.getType(replyToMessage);
323
-
324
- if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
325
-
326
- // Changing this to accomodate an array of users
327
- if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
328
-
329
- if (replyToMessage && messageIDType !== 'String') return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
330
-
331
- if (msgType === "String") msg = { body: msg };
332
- var disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
333
- if (disallowedProperties.length > 0) return callback({ error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`" });
334
-
335
- var messageAndOTID = utils.generateOfflineThreadingID();
336
-
337
- var form = {
338
- client: "mercury",
339
- action_type: "ma-type:user-generated-message",
340
- author: "fbid:" + ctx.userID,
341
- timestamp: Date.now(),
342
- timestamp_absolute: "Today",
343
- timestamp_relative: utils.generateTimestampRelative(),
344
- timestamp_time_passed: "0",
345
- is_unread: false,
346
- is_cleared: false,
347
- is_forward: false,
348
- is_filtered_content: false,
349
- is_filtered_content_bh: false,
350
- is_filtered_content_account: false,
351
- is_filtered_content_quasar: false,
352
- is_filtered_content_invalid_app: false,
353
- is_spoof_warning: false,
354
- source: "source:chat:web",
355
- "source_tags[0]": "source:chat",
356
- 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",' ') : "",
357
- html_body: false,
358
- ui_push_phase: "V3",
359
- status: "0",
360
- offline_threading_id: messageAndOTID,
361
- message_id: messageAndOTID,
362
- threading_id: utils.generateThreadingID(ctx.clientID),
363
- "ephemeral_ttl_mode:": "0",
364
- manual_retry_cnt: "0",
365
- has_attachment: !!(msg.attachment || msg.url || msg.sticker),
366
- signatureID: utils.getSignatureID(),
367
- replied_to_message_id: replyToMessage
368
- };
369
-
370
- handleLocation(msg, form, callback, () =>
371
- handleSticker(msg, form, callback, () =>
372
- handleAttachment(msg, form, callback, () =>
373
- handleUrl(msg, form, callback, () =>
374
- handleEmoji(msg, form, callback, () =>
375
- handleMention(msg, form, callback, () =>
376
- send(form, threadID, messageAndOTID, callback, isGroup)
377
- )
378
- )
379
- )
380
- )
381
- )
382
- );
383
-
384
- return returnPromise;
385
- };
19
+ function uploadAttachment(attachments, callback) {
20
+ var uploads = [];
21
+
22
+ // create an array of promises
23
+ for (var i = 0; i < attachments.length; i++) {
24
+ if (!utils.isReadableStream(attachments[i])) throw { error: "Attachment should be a readable stream and not " + utils.getType(attachments[i]) + "." };
25
+ var form = {
26
+ upload_1024: attachments[i],
27
+ voice_clip: "true"
28
+ };
29
+
30
+ uploads.push(
31
+ defaultFuncs
32
+ .postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, {})
33
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
34
+ .then(function (resData) {
35
+ if (resData.error) throw resData;
36
+ // We have to return the data unformatted unless we want to change it
37
+ // back in sendMessage.
38
+ return resData.payload.metadata[0];
39
+ })
40
+ );
41
+ }
42
+
43
+ // resolve all promises
44
+ bluebird
45
+ .all(uploads)
46
+ .then(resData => callback(null, resData))
47
+ .catch(function (err) {
48
+ log.error("uploadAttachment", err);
49
+ return callback(err);
50
+ });
51
+ }
52
+
53
+ function getUrl(url, callback) {
54
+ var form = {
55
+ image_height: 960,
56
+ image_width: 960,
57
+ uri: url
58
+ };
59
+
60
+ defaultFuncs
61
+ .post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
62
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
63
+ .then(function (resData) {
64
+ if (resData.error) return callback(resData);
65
+ if (!resData.payload) return callback({ error: "Invalid url" });
66
+ callback(null, resData.payload.share_data.share_params);
67
+ })
68
+ .catch(function (err) {
69
+ log.error("getUrl", err);
70
+ return callback(err);
71
+ });
72
+ }
73
+
74
+ function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
75
+ // There are three cases here:
76
+ // 1. threadID is of type array, where we're starting a new group chat with users
77
+ // specified in the array.
78
+ // 2. User is sending a message to a specific user.
79
+ // 3. No additional form params and the message goes to an existing group chat.
80
+ if (utils.getType(threadID) === "Array") {
81
+ for (var i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
82
+ form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
83
+ form["client_thread_id"] = "root:" + messageAndOTID;
84
+ log.info("sendMessage", "Sending message to multiple users: " + threadID);
85
+ }
86
+ else {
87
+ // This means that threadID is the id of a user, and the chat
88
+ // is a single person chat
89
+ if (isSingleUser) {
90
+ form["specific_to_list[0]"] = "fbid:" + threadID;
91
+ form["specific_to_list[1]"] = "fbid:" + ctx.userID;
92
+ form["other_user_fbid"] = threadID;
93
+ }
94
+ else form["thread_fbid"] = threadID;
95
+ }
96
+
97
+ if (ctx.globalOptions.pageID) {
98
+ form["author"] = "fbid:" + ctx.globalOptions.pageID;
99
+ form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
100
+ form["creator_info[creatorID]"] = ctx.userID;
101
+ form["creator_info[creatorType]"] = "direct_admin";
102
+ form["creator_info[labelType]"] = "sent_message";
103
+ form["creator_info[pageID]"] = ctx.globalOptions.pageID;
104
+ form["request_user_id"] = ctx.globalOptions.pageID;
105
+ form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
106
+ }
107
+
108
+ defaultFuncs
109
+ .post("https://www.facebook.com/messaging/send/", ctx.jar, form)
110
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
111
+ .then(function (resData) {
112
+ if (!resData) return callback({ error: "Send message failed." });
113
+ if (resData.error) {
114
+ if (resData.error === 1545012) {
115
+ log.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
116
+ }
117
+ else {
118
+ log.error("sendMessage", resData);
119
+ }
120
+ return callback(resData);
121
+ }
122
+
123
+ var messageInfo = resData.payload.actions.reduce(function (p, v) {
124
+ return (
125
+ {
126
+ threadID: v.thread_fbid,
127
+ messageID: v.message_id,
128
+ timestamp: v.timestamp
129
+ } || p
130
+ );
131
+ }, null);
132
+
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);
139
+ });
140
+ }
141
+
142
+ function send(form, threadID, messageAndOTID, callback, isGroup) {
143
+ // We're doing a query to this to check if the given id is the id of
144
+ // a user or of a group chat. The form will be different depending
145
+ // on that.
146
+ if (utils.getType(threadID) === "Array") sendContent(form, threadID, false, messageAndOTID, callback);
147
+ else {
148
+ if (utils.getType(isGroup) != "Boolean") sendContent(form, threadID, threadID.length === 15, messageAndOTID, callback);
149
+ else sendContent(form, threadID, !isGroup, messageAndOTID, callback);
150
+ }
151
+ }
152
+
153
+ function handleUrl(msg, form, callback, cb) {
154
+ if (msg.url) {
155
+ form["shareable_attachment[share_type]"] = "100";
156
+ getUrl(msg.url, function (err, params) {
157
+ if (err) return callback(err);
158
+ form["shareable_attachment[share_params]"] = params;
159
+ cb();
160
+ });
161
+ }
162
+ else cb();
163
+ }
164
+
165
+ function handleLocation(msg, form, callback, cb) {
166
+ if (msg.location) {
167
+ if (msg.location.latitude == null || msg.location.longitude == null) return callback({ error: "location property needs both latitude and longitude" });
168
+ form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
169
+ form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
170
+ form["location_attachment[is_current_location]"] = !!msg.location.current;
171
+ }
172
+ cb();
173
+ }
174
+
175
+ function handleSticker(msg, form, callback, cb) {
176
+ if (msg.sticker) form["sticker_id"] = msg.sticker;
177
+ cb();
178
+ }
179
+
180
+ function handleEmoji(msg, form, callback, cb) {
181
+ if (msg.emojiSize != null && msg.emoji == null) return callback({ error: "emoji property is empty" });
182
+ if (msg.emoji) {
183
+ if (msg.emojiSize == null) msg.emojiSize = "medium";
184
+ if (msg.emojiSize != "small" && msg.emojiSize != "medium" && msg.emojiSize != "large") return callback({ error: "emojiSize property is invalid" });
185
+ if (form["body"] != null && form["body"] != "") return callback({ error: "body is not empty" });
186
+ form["body"] = msg.emoji;
187
+ form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
188
+ }
189
+ cb();
190
+ }
191
+
192
+ function handleAttachment(msg, form, callback, cb) {
193
+ if (msg.attachment) {
194
+ form["image_ids"] = [];
195
+ form["gif_ids"] = [];
196
+ form["file_ids"] = [];
197
+ form["video_ids"] = [];
198
+ form["audio_ids"] = [];
199
+
200
+ if (utils.getType(msg.attachment) !== "Array") msg.attachment = [msg.attachment];
201
+ if (msg.attachment.every(e=>/_id$/.test(e[0]))) {
202
+ //console.log(msg.attachment)
203
+ msg.attachment.map(e=>form[`${e[0]}s`].push(e[1]));
204
+ return cb();
205
+ }
206
+ uploadAttachment(msg.attachment, function (err, files) {
207
+ if (err) return callback(err);
208
+ files.forEach(function (file) {
209
+ var key = Object.keys(file);
210
+ var type = key[0]; // image_id, file_id, etc
211
+ form["" + type + "s"].push(file[type]); // push the id
212
+ });
213
+ cb();
214
+ });
215
+ }
216
+ else cb();
217
+ }
218
+
219
+ function handleMention(msg, form, callback, cb) {
220
+ if (msg.mentions) {
221
+ for (let i = 0; i < msg.mentions.length; i++) {
222
+ const mention = msg.mentions[i];
223
+ const tag = mention.tag;
224
+ if (typeof tag !== "string") return callback({ error: "Mention tags must be strings." });
225
+ const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
226
+ if (offset < 0) log.warn("handleMention", 'Mention for "' + tag + '" not found in message string.');
227
+ if (mention.id == null) log.warn("handleMention", "Mention id should be non-null.");
228
+
229
+ const id = mention.id || 0;
230
+ const emptyChar = '\u200E';
231
+ form["body"] = emptyChar + msg.body;
232
+ form["profile_xmd[" + i + "][offset]"] = offset + 1;
233
+ form["profile_xmd[" + i + "][length]"] = tag.length;
234
+ form["profile_xmd[" + i + "][id]"] = id;
235
+ form["profile_xmd[" + i + "][type]"] = "p";
236
+ }
237
+ }
238
+ cb();
239
+ }
240
+
241
+ return function sendMessage(msg, threadID, callback, replyToMessage, isGroup) {
242
+ typeof isGroup == "undefined" ? isGroup = null : "";
243
+ if (!callback && (utils.getType(threadID) === "Function" || utils.getType(threadID) === "AsyncFunction")) return threadID({ error: "Pass a threadID as a second argument." });
244
+ if (!replyToMessage && utils.getType(callback) === "String") {
245
+ replyToMessage = callback;
246
+ callback = function () { };
247
+ }
248
+
249
+ var resolveFunc = function () { };
250
+ var rejectFunc = function () { };
251
+ var returnPromise = new Promise(function (resolve, reject) {
252
+ resolveFunc = resolve;
253
+ rejectFunc = reject;
254
+ });
255
+
256
+ if (!callback) {
257
+ callback = function (err, data) {
258
+ if (err) return rejectFunc(err);
259
+ resolveFunc(data);
260
+ };
261
+ }
262
+
263
+ var msgType = utils.getType(msg);
264
+ var threadIDType = utils.getType(threadID);
265
+ var messageIDType = utils.getType(replyToMessage);
266
+
267
+ if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
268
+
269
+ // Changing this to accomodate an array of users
270
+ if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
271
+
272
+ if (replyToMessage && messageIDType !== 'String') return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
273
+
274
+ if (msgType === "String") msg = { body: msg };
275
+ var disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
276
+ if (disallowedProperties.length > 0) return callback({ error: "Dissallowed props: `" + disallowedProperties.join(", ") + "`" });
277
+
278
+ var messageAndOTID = utils.generateOfflineThreadingID();
279
+
280
+ var form = {
281
+ client: "mercury",
282
+ action_type: "ma-type:user-generated-message",
283
+ author: "fbid:" + ctx.userID,
284
+ timestamp: Date.now(),
285
+ timestamp_absolute: "Today",
286
+ timestamp_relative: utils.generateTimestampRelative(),
287
+ timestamp_time_passed: "0",
288
+ is_unread: false,
289
+ is_cleared: false,
290
+ is_forward: false,
291
+ is_filtered_content: false,
292
+ is_filtered_content_bh: false,
293
+ is_filtered_content_account: false,
294
+ is_filtered_content_quasar: false,
295
+ is_filtered_content_invalid_app: false,
296
+ is_spoof_warning: false,
297
+ source: "source:chat:web",
298
+ "source_tags[0]": "source:chat",
299
+ body: msg.body ? msg.body.toString() : "",
300
+ html_body: false,
301
+ ui_push_phase: "V3",
302
+ status: "0",
303
+ offline_threading_id: messageAndOTID,
304
+ message_id: messageAndOTID,
305
+ threading_id: utils.generateThreadingID(ctx.clientID),
306
+ "ephemeral_ttl_mode:": "0",
307
+ manual_retry_cnt: "0",
308
+ has_attachment: !!(msg.attachment || msg.url || msg.sticker),
309
+ signatureID: utils.getSignatureID(),
310
+ replied_to_message_id: replyToMessage
311
+ };
312
+
313
+ handleLocation(msg, form, callback, () =>
314
+ handleSticker(msg, form, callback, () =>
315
+ handleAttachment(msg, form, callback, () =>
316
+ handleUrl(msg, form, callback, () =>
317
+ handleEmoji(msg, form, callback, () =>
318
+ handleMention(msg, form, callback, () =>
319
+ send(form, threadID, messageAndOTID, callback, isGroup)
320
+ )
321
+ )
322
+ )
323
+ )
324
+ )
325
+ );
326
+ return returnPromise;
327
+ };
386
328
  };