fca-priyansh 19.0.0 → 20.0.0

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 (135) hide show
  1. package/.gitlab-ci.yml +22 -0
  2. package/CountTime.json +1 -0
  3. package/Extra/Balancer.js +49 -49
  4. package/Extra/BroadcastSystem.js +1 -0
  5. package/Extra/Bypass/956/index.js +233 -233
  6. package/Extra/Bypass/test/aaaa.json +169 -169
  7. package/Extra/Bypass/test/index.js +187 -187
  8. package/Extra/Database/index.js +468 -468
  9. package/Extra/ExtraAddons.js +82 -82
  10. package/Extra/ExtraFindUID.js +61 -61
  11. package/Extra/ExtraGetThread.js +365 -365
  12. package/Extra/ExtraScreenShot.js +430 -430
  13. package/Extra/ExtraUptimeRobot.js +142 -38
  14. package/Extra/Html/Classic/script.js +118 -118
  15. package/Extra/Html/Classic/style.css +7 -7
  16. package/Extra/Security/Base/Step_1.js +5 -5
  17. package/Extra/Security/Base/Step_2.js +22 -22
  18. package/Extra/Security/Base/Step_3.js +22 -22
  19. package/Extra/Security/Base/index.js +190 -190
  20. package/Extra/Security/Index.js +4 -4
  21. package/Extra/Security/Step_1.js +5 -5
  22. package/Extra/Security/Step_2.js +22 -22
  23. package/Extra/Security/Step_3.js +22 -22
  24. package/Extra/Src/Change_Environment.js +24 -24
  25. package/Extra/Src/Check_Update.js +66 -66
  26. package/Extra/Src/History.js +114 -114
  27. package/Extra/Src/Instant_Update.js +64 -64
  28. package/Extra/Src/Last-Run.js +64 -64
  29. package/Extra/Src/Premium.js +81 -81
  30. package/Extra/Src/Websocket.js +212 -212
  31. package/Extra/Src/uuid.js +137 -137
  32. package/Func/AcceptAgreement.js +31 -31
  33. package/Func/ClearCache.js +64 -64
  34. package/Func/ReportV1.js +54 -54
  35. package/LICENSE +21 -21
  36. package/Language/index.json +228 -228
  37. package/Main.js +1 -1290
  38. package/README.md +198 -198
  39. package/broadcast.js +1 -44
  40. package/errorHandler.js +151 -0
  41. package/index.js +1 -448
  42. package/logger.js +69 -66
  43. package/package.json +99 -98
  44. package/src/Dev_Horizon_Data.js +124 -124
  45. package/src/Dev_getThreadInfoOLD.js +421 -421
  46. package/src/Dev_shareTest2.js +68 -68
  47. package/src/Dev_shareTest3.js +71 -71
  48. package/src/Premium.js +24 -24
  49. package/src/Screenshot.js +82 -82
  50. package/src/addExternalModule.js +16 -16
  51. package/src/addUserToGroup.js +79 -79
  52. package/src/changeAdminStatus.js +79 -79
  53. package/src/changeArchivedStatus.js +41 -41
  54. package/src/changeAvt.js +84 -84
  55. package/src/changeBio.js +65 -65
  56. package/src/changeBlockedStatus.js +36 -36
  57. package/src/changeGroupImage.js +106 -106
  58. package/src/changeNickname.js +45 -45
  59. package/src/changeThreadColor.js +62 -62
  60. package/src/changeThreadEmoji.js +42 -42
  61. package/src/changeThreadTheme.js +263 -0
  62. package/src/comment.js +244 -0
  63. package/src/createNewGroup.js +70 -70
  64. package/src/createPoll.js +60 -60
  65. package/src/deleteMessage.js +45 -45
  66. package/src/deleteThread.js +43 -43
  67. package/src/editMessage.js +71 -53
  68. package/src/follow.js +119 -0
  69. package/src/forwardAttachment.js +48 -48
  70. package/src/friend.js +383 -0
  71. package/src/getAccessToken.js +27 -27
  72. package/src/getCurrentUserID.js +7 -7
  73. package/src/getEmojiUrl.js +27 -27
  74. package/src/getFriendsList.js +73 -73
  75. package/src/getMessage.js +102 -102
  76. package/src/getPendingFriendRequests.js +45 -0
  77. package/src/getThreadHistory.js +537 -537
  78. package/src/getThreadInfo.js +424 -423
  79. package/src/getThreadInfoOLD.js +421 -421
  80. package/src/getThreadList.js +213 -213
  81. package/src/getThreadMain.js +219 -219
  82. package/src/getThreadPictures.js +59 -59
  83. package/src/getUID.js +58 -58
  84. package/src/getUserID.js +62 -62
  85. package/src/getUserInfo.js +112 -112
  86. package/src/getUserInfoMain.js +64 -64
  87. package/src/getUserInfoV2.js +31 -31
  88. package/src/getUserInfoV3.js +62 -62
  89. package/src/getUserInfoV4.js +54 -54
  90. package/src/getUserInfoV5.js +60 -60
  91. package/src/handleFriendRequest.js +46 -46
  92. package/src/handleMessageRequest.js +49 -49
  93. package/src/httpGet.js +49 -49
  94. package/src/httpPost.js +48 -48
  95. package/src/httpPostFormData.js +40 -40
  96. package/src/listenMqtt.js +1 -956
  97. package/src/listenMqttV1.js +832 -846
  98. package/src/logout.js +68 -68
  99. package/src/markAsDelivered.js +48 -48
  100. package/src/markAsRead.js +70 -70
  101. package/src/markAsReadAll.js +42 -42
  102. package/src/markAsSeen.js +51 -51
  103. package/src/muteThread.js +47 -47
  104. package/src/notes.js +279 -0
  105. package/src/removeUserFromGroup.js +49 -49
  106. package/src/resolvePhotoUrl.js +37 -37
  107. package/src/searchForThread.js +43 -43
  108. package/src/sendMention.js +325 -0
  109. package/src/sendMessage.js +1 -386
  110. package/src/sendMqttMessage.js +70 -70
  111. package/src/sendTypingIndicator.js +79 -80
  112. package/src/setMessageReaction.js +109 -109
  113. package/src/setPostReaction.js +101 -101
  114. package/src/setTitle.js +74 -74
  115. package/src/share.js +98 -0
  116. package/src/shareContact.js +55 -55
  117. package/src/shareLink.js +58 -58
  118. package/src/stickers.js +525 -0
  119. package/src/story.js +267 -0
  120. package/src/threadColors.js +38 -38
  121. package/src/unfriend.js +43 -43
  122. package/src/unsendMessage.js +47 -47
  123. package/src/unsendMqttMessage.js +65 -65
  124. package/test/data/shareAttach.js +146 -146
  125. package/test/data/test.txt +7 -7
  126. package/test/example-config.json +18 -18
  127. package/test/test-page.js +140 -140
  128. package/test/test.js +385 -385
  129. package/test/testv2.js +2 -2
  130. package/userAgentManager.js +129 -0
  131. package/utils.js +1 -3077
  132. package/.github/workflows/publish.yml +0 -20
  133. package/Extra/Src/Release_Memory.js +0 -160
  134. package/Extra/Src/test.js +0 -28
  135. package/SECURITY.md +0 -17
@@ -1,42 +1,42 @@
1
- "use strict";
2
-
3
- var utils = require("../utils");
4
- var log = require("npmlog");
5
-
6
- module.exports = function (defaultFuncs, api, ctx) {
7
- return function changeThreadEmoji(emoji, threadID, callback) {
8
- var resolveFunc = function () { };
9
- var rejectFunc = function () { };
10
- var returnPromise = new Promise(function (resolve, reject) {
11
- resolveFunc = resolve;
12
- rejectFunc = reject;
13
- });
14
-
15
- if (!callback) {
16
- callback = function (err) {
17
- if (err) return rejectFunc(err);
18
- resolveFunc();
19
- };
20
- }
21
- var form = {
22
- emoji_choice: emoji,
23
- thread_or_other_fbid: threadID
24
- };
25
-
26
- defaultFuncs
27
- .post("https://www.facebook.com/messaging/save_thread_emoji/?source=thread_settings&__pc=EXP1%3Amessengerdotcom_pkg", ctx.jar, form)
28
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
29
- .then(function (resData) {
30
- if (resData.error === 1357031) throw { error: "Trying to change emoji of a chat that doesn't exist. Have at least one message in the thread before trying to change the emoji." };
31
- if (resData.error) throw resData;
32
-
33
- return callback();
34
- })
35
- .catch(function (err) {
36
- log.error("changeThreadEmoji", err);
37
- return callback(err);
38
- });
39
-
40
- return returnPromise;
41
- };
42
- };
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function changeThreadEmoji(emoji, threadID, callback) {
8
+ var resolveFunc = function () { };
9
+ var rejectFunc = function () { };
10
+ var returnPromise = new Promise(function (resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = function (err) {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc();
19
+ };
20
+ }
21
+ var form = {
22
+ emoji_choice: emoji,
23
+ thread_or_other_fbid: threadID
24
+ };
25
+
26
+ defaultFuncs
27
+ .post("https://www.facebook.com/messaging/save_thread_emoji/?source=thread_settings&__pc=EXP1%3Amessengerdotcom_pkg", ctx.jar, form)
28
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
29
+ .then(function (resData) {
30
+ if (resData.error === 1357031) throw { error: "Trying to change emoji of a chat that doesn't exist. Have at least one message in the thread before trying to change the emoji." };
31
+ if (resData.error) throw resData;
32
+
33
+ return callback();
34
+ })
35
+ .catch(function (err) {
36
+ log.error("changeThreadEmoji", err);
37
+ return callback(err);
38
+ });
39
+
40
+ return returnPromise;
41
+ };
42
+ };
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ /**
8
+ * Change thread theme using MQTT (more reliable than HTTP)
9
+ * Made by Choru Official - Ported to fca-updated format
10
+ *
11
+ * Features:
12
+ * - List all available themes: api.changeThreadTheme("list", threadID)
13
+ * - Set theme by name: api.changeThreadTheme("love", threadID)
14
+ * - Set theme by ID: api.changeThreadTheme("168332145275126", threadID)
15
+ * - Partial name match: api.changeThreadTheme("dark", threadID) -> finds "Dark Mode"
16
+ *
17
+ * @param {string} themeName - Theme name, theme ID, or "list" to show all themes
18
+ * @param {string} threadID - Thread ID to change theme
19
+ * @param {function} callback - Optional callback(err, result)
20
+ * @returns {Promise} Promise that resolves with theme data or list
21
+ */
22
+ return function changeThreadTheme(themeName, threadID, callback) {
23
+ var resolveFunc = function () {};
24
+ var rejectFunc = function () {};
25
+ var returnPromise = new Promise(function (resolve, reject) {
26
+ resolveFunc = resolve;
27
+ rejectFunc = reject;
28
+ });
29
+
30
+ if (!callback) {
31
+ callback = function (err, data) {
32
+ if (err) return rejectFunc(err);
33
+ resolveFunc(data);
34
+ };
35
+ }
36
+
37
+ // Validate parameters
38
+ if (!threadID) {
39
+ return callback({ error: "threadID is required to change theme." });
40
+ }
41
+
42
+ if (!themeName) {
43
+ return callback({ error: "themeName (or 'list') is required." });
44
+ }
45
+
46
+ // Check MQTT connection (required for theme change)
47
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
48
+ return callback({
49
+ error: "MQTT not connected. Theme changes require MQTT connection. Make sure bot is fully started with listenMqtt active."
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Fetch all available themes from Facebook GraphQL
55
+ */
56
+ function fetchAllThemes(cb) {
57
+ log.info("changeThreadTheme", "Fetching available themes from Facebook...");
58
+
59
+ var form = {
60
+ fb_api_caller_class: "RelayModern",
61
+ fb_api_req_friendly_name: "MWPThreadThemeQuery_AllThemesQuery",
62
+ variables: JSON.stringify({ version: "default" }),
63
+ server_timestamps: true,
64
+ doc_id: "24474714052117636"
65
+ };
66
+
67
+ defaultFuncs
68
+ .post("https://www.facebook.com/api/graphql/", ctx.jar, form, null, {
69
+ "x-fb-friendly-name": "MWPThreadThemeQuery_AllThemesQuery",
70
+ "x-fb-lsd": ctx.lsd || ctx.fb_dtsg,
71
+ "referer": "https://www.facebook.com/messages/t/" + threadID
72
+ })
73
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
74
+ .then(function (resData) {
75
+ if (resData.errors) {
76
+ return cb({ error: "GraphQL error: " + JSON.stringify(resData.errors) });
77
+ }
78
+
79
+ if (!resData.data || !resData.data.messenger_thread_themes) {
80
+ return cb({ error: "Could not retrieve themes from Facebook." });
81
+ }
82
+
83
+ var themes = resData.data.messenger_thread_themes
84
+ .map(function (themeData) {
85
+ if (!themeData || !themeData.id) return null;
86
+
87
+ return {
88
+ id: themeData.id,
89
+ name: themeData.accessibility_label,
90
+ description: themeData.description,
91
+ appColorMode: themeData.app_color_mode,
92
+ fallbackColor: themeData.fallback_color,
93
+ gradientColors: themeData.gradient_colors,
94
+ backgroundImage: themeData.background_asset?.image?.uri,
95
+ iconAsset: themeData.icon_asset?.image?.uri,
96
+ // Color details
97
+ composerBackgroundColor: themeData.composer_background_color,
98
+ composerTintColor: themeData.composer_tint_color,
99
+ titleBarBackgroundColor: themeData.title_bar_background_color,
100
+ titleBarTextColor: themeData.title_bar_text_color,
101
+ hotLikeColor: themeData.hot_like_color,
102
+ inboundMessageGradientColors: themeData.inbound_message_gradient_colors,
103
+ messageTextColor: themeData.message_text_color
104
+ };
105
+ })
106
+ .filter(Boolean);
107
+
108
+ log.info("changeThreadTheme", "Successfully fetched " + themes.length + " themes");
109
+ cb(null, themes);
110
+ })
111
+ .catch(function (err) {
112
+ log.error("changeThreadTheme", "Failed to fetch themes:", err);
113
+ cb({ error: "Failed to fetch theme list: " + (err.message || err) });
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Set thread theme using MQTT publish
119
+ */
120
+ function setThemeViaMqtt(themeID, actualThemeName, cb) {
121
+ log.info("changeThreadTheme", "Setting theme '" + actualThemeName + "' (ID: " + themeID + ") for thread " + threadID);
122
+
123
+ var currentEpochId = parseInt(utils.generateOfflineThreadingID());
124
+ var publishedCount = 0;
125
+ var errors = [];
126
+
127
+ // Helper to create and publish MQTT message
128
+ function createAndPublish(label, queueName, payload, done) {
129
+ currentEpochId = parseInt(utils.generateOfflineThreadingID());
130
+ ctx.wsReqNumber += 1;
131
+ ctx.wsTaskNumber += 1;
132
+
133
+ var requestId = ctx.wsReqNumber;
134
+
135
+ var queryPayload = {
136
+ thread_key: threadID.toString(),
137
+ theme_fbid: themeID.toString(),
138
+ sync_group: 1
139
+ };
140
+
141
+ // Merge additional payload
142
+ Object.keys(payload).forEach(function(key) {
143
+ queryPayload[key] = payload[key];
144
+ });
145
+
146
+ var query = {
147
+ failure_count: null,
148
+ label: label,
149
+ payload: JSON.stringify(queryPayload),
150
+ queue_name: queueName,
151
+ task_id: ctx.wsTaskNumber
152
+ };
153
+
154
+ var context = {
155
+ app_id: ctx.appID || "2220391788200892",
156
+ payload: {
157
+ epoch_id: currentEpochId,
158
+ tasks: [query],
159
+ version_id: "24631415369801570"
160
+ },
161
+ request_id: requestId,
162
+ type: 3
163
+ };
164
+
165
+ context.payload = JSON.stringify(context.payload);
166
+
167
+ log.info("changeThreadTheme", "Publishing MQTT message: label=" + label + ", queueName=" + queueName);
168
+
169
+ ctx.mqttClient.publish("/ls_req", JSON.stringify(context), { qos: 1, retain: false }, function (err) {
170
+ if (err) {
171
+ log.error("changeThreadTheme", "MQTT publish failed for " + queueName + ":", err);
172
+ errors.push("Failed to publish " + queueName + ": " + err.message);
173
+ } else {
174
+ publishedCount++;
175
+ log.info("changeThreadTheme", "Successfully published " + queueName);
176
+ }
177
+ done();
178
+ });
179
+ }
180
+
181
+ // Publish all required MQTT messages in parallel
182
+ var pending = 4;
183
+ function checkComplete() {
184
+ pending--;
185
+ if (pending === 0) {
186
+ if (errors.length > 0) {
187
+ return cb({ error: "Some MQTT publishes failed: " + errors.join(", ") });
188
+ }
189
+
190
+ var eventData = {
191
+ type: "thread_theme_update",
192
+ threadID: threadID,
193
+ themeID: themeID,
194
+ themeName: actualThemeName,
195
+ senderID: ctx.userID,
196
+ timestamp: Date.now()
197
+ };
198
+
199
+ log.info("changeThreadTheme", "✅ Theme changed successfully!");
200
+ cb(null, eventData);
201
+ }
202
+ }
203
+
204
+ // Publish 4 different MQTT messages (required by Facebook)
205
+ createAndPublish("1013", "ai_generated_theme", {}, checkComplete);
206
+ createAndPublish("1037", "msgr_custom_thread_theme", {}, checkComplete);
207
+ createAndPublish("1028", "thread_theme_writer", {}, checkComplete);
208
+ createAndPublish("43", "thread_theme", { source: null, payload: null }, checkComplete);
209
+ }
210
+
211
+ /**
212
+ * Main logic
213
+ */
214
+ fetchAllThemes(function (err, themes) {
215
+ if (err) {
216
+ return callback(err);
217
+ }
218
+
219
+ // If user wants to list themes
220
+ if (themeName.toLowerCase() === "list") {
221
+ log.info("changeThreadTheme", "Returning list of " + themes.length + " available themes");
222
+ return callback(null, themes);
223
+ }
224
+
225
+ // Find matching theme
226
+ var normalizedThemeName = themeName.toLowerCase();
227
+ var matchedTheme = null;
228
+
229
+ // 1. Try exact ID match
230
+ if (!isNaN(normalizedThemeName)) {
231
+ matchedTheme = themes.find(function (t) {
232
+ return t.id === normalizedThemeName;
233
+ });
234
+ }
235
+
236
+ // 2. Try exact name match
237
+ if (!matchedTheme) {
238
+ matchedTheme = themes.find(function (t) {
239
+ return t.name.toLowerCase() === normalizedThemeName;
240
+ });
241
+ }
242
+
243
+ // 3. Try partial name match
244
+ if (!matchedTheme) {
245
+ matchedTheme = themes.find(function (t) {
246
+ return t.name.toLowerCase().includes(normalizedThemeName);
247
+ });
248
+ }
249
+
250
+ if (!matchedTheme) {
251
+ log.warn("changeThreadTheme", "Theme '" + themeName + "' not found");
252
+ return callback({
253
+ error: "Theme \"" + themeName + "\" not found. Use api.changeThreadTheme('list', threadID) to see available themes."
254
+ });
255
+ }
256
+
257
+ // Set the theme
258
+ setThemeViaMqtt(matchedTheme.id, matchedTheme.name, callback);
259
+ });
260
+
261
+ return returnPromise;
262
+ };
263
+ };
package/src/comment.js ADDED
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Comment API Module
5
+ * Create comments on Facebook posts with support for attachments, mentions, stickers, and URLs
6
+ *
7
+ * @author Priyansh Rajput
8
+ * @github https://github.com/priyanshufsdev
9
+ * @license MIT
10
+ */
11
+
12
+ var utils = require("../utils");
13
+ var log = require("npmlog");
14
+
15
+ /**
16
+ * Handle upload of attachments (images/videos) for comment
17
+ * @param {object} defaultFuncs - Default functions for API requests
18
+ * @param {object} ctx - Context object
19
+ * @param {object} msg - Message object containing attachments
20
+ * @param {object} form - Form object to populate
21
+ * @returns {Promise<void>}
22
+ */
23
+ async function handleUpload(defaultFuncs, ctx, msg, form) {
24
+ if (!msg.attachments || msg.attachments.length === 0) {
25
+ return;
26
+ }
27
+
28
+ var uploads = msg.attachments.map(function(item) {
29
+ if (!utils.isReadableStream(item)) {
30
+ throw new Error('Attachments must be a readable stream.');
31
+ }
32
+
33
+ return defaultFuncs
34
+ .postFormData('https://www.facebook.com/ajax/ufi/upload/', ctx.jar, {
35
+ profile_id: ctx.userID,
36
+ source: 19,
37
+ target_id: ctx.userID,
38
+ file: item
39
+ })
40
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
41
+ .then(function(res) {
42
+ if (res.error || !res.payload || !res.payload.fbid) {
43
+ throw res;
44
+ }
45
+ return { media: { id: res.payload.fbid } };
46
+ });
47
+ });
48
+
49
+ var results = await Promise.all(uploads);
50
+ results.forEach(function(result) {
51
+ form.input.attachments.push(result);
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Handle URL attachment for comment
57
+ * @param {object} msg - Message object
58
+ * @param {object} form - Form object
59
+ */
60
+ function handleURL(msg, form) {
61
+ if (typeof msg.url === 'string') {
62
+ form.input.attachments.push({
63
+ link: {
64
+ external: {
65
+ url: msg.url
66
+ }
67
+ }
68
+ });
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Handle mentions in comment body
74
+ * @param {object} msg - Message object
75
+ * @param {object} form - Form object
76
+ */
77
+ function handleMentions(msg, form) {
78
+ if (!msg.mentions) return;
79
+
80
+ for (var i = 0; i < msg.mentions.length; i++) {
81
+ var item = msg.mentions[i];
82
+ var tag = item.tag;
83
+ var id = item.id;
84
+ var fromIndex = item.fromIndex;
85
+
86
+ if (typeof tag !== 'string' || !id) {
87
+ log.warn('comment', 'Mentions must have a string "tag" and an "id".');
88
+ continue;
89
+ }
90
+
91
+ var offset = msg.body.indexOf(tag, fromIndex || 0);
92
+ if (offset < 0) {
93
+ log.warn('comment', 'Mention for "' + tag + '" not found in message string.');
94
+ continue;
95
+ }
96
+
97
+ form.input.message.ranges.push({
98
+ entity: { id: id },
99
+ length: tag.length,
100
+ offset: offset
101
+ });
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Handle sticker attachment for comment
107
+ * @param {object} msg - Message object
108
+ * @param {object} form - Form object
109
+ */
110
+ function handleSticker(msg, form) {
111
+ if (msg.sticker) {
112
+ form.input.attachments.push({
113
+ media: {
114
+ id: String(msg.sticker)
115
+ }
116
+ });
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Submit final comment form to GraphQL endpoint
122
+ * @param {object} defaultFuncs - Default functions
123
+ * @param {object} ctx - Context object
124
+ * @param {object} form - Fully constructed form object
125
+ * @returns {Promise<object>}
126
+ */
127
+ async function createContent(defaultFuncs, ctx, form) {
128
+ var res = await defaultFuncs
129
+ .post('https://www.facebook.com/api/graphql/', ctx.jar, {
130
+ fb_api_caller_class: 'RelayModern',
131
+ fb_api_req_friendly_name: 'useCometUFICreateCommentMutation',
132
+ variables: JSON.stringify(form),
133
+ server_timestamps: true,
134
+ doc_id: 6993516810709754
135
+ })
136
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
137
+
138
+ if (res.errors) {
139
+ throw res;
140
+ }
141
+
142
+ var commentEdge = res.data.comment_create.feedback_comment_edge;
143
+ return {
144
+ id: commentEdge.node.id,
145
+ url: commentEdge.node.feedback.url,
146
+ count: res.data.comment_create.feedback.total_comment_count
147
+ };
148
+ }
149
+
150
+ module.exports = function(defaultFuncs, api, ctx) {
151
+ /**
152
+ * Create a comment on a Facebook post
153
+ * Can also reply to an existing comment
154
+ *
155
+ * @param {string|object} msg - Message to post (string or object with body, attachments, mentions, etc.)
156
+ * @param {string} postID - ID of the post to comment on
157
+ * @param {string} replyCommentID - Optional: ID of comment to reply to
158
+ * @param {function} callback - Optional callback function
159
+ * @returns {Promise<object>}
160
+ */
161
+ return function createCommentPost(msg, postID, replyCommentID, callback) {
162
+ var resolveFunc = function() {};
163
+ var rejectFunc = function() {};
164
+ var returnPromise = new Promise(function(resolve, reject) {
165
+ resolveFunc = resolve;
166
+ rejectFunc = reject;
167
+ });
168
+
169
+ // Handle optional parameters
170
+ if (typeof replyCommentID === 'function') {
171
+ callback = replyCommentID;
172
+ replyCommentID = null;
173
+ }
174
+
175
+ if (!callback) {
176
+ callback = function(err, data) {
177
+ if (err) return rejectFunc(err);
178
+ resolveFunc(data);
179
+ };
180
+ }
181
+
182
+ // Validation
183
+ if (typeof msg !== 'string' && typeof msg !== 'object') {
184
+ var error = 'Message must be a string or an object.';
185
+ log.error('comment', error);
186
+ return callback({ error: error });
187
+ }
188
+
189
+ if (typeof postID !== 'string') {
190
+ var error2 = 'postID must be a string.';
191
+ log.error('comment', error2);
192
+ return callback({ error: error2 });
193
+ }
194
+
195
+ // Prepare message object
196
+ var messageObject = typeof msg === 'string' ? { body: msg } : msg;
197
+ messageObject.mentions = messageObject.mentions || [];
198
+ messageObject.attachments = messageObject.attachments || [];
199
+
200
+ // Build form
201
+ var form = {
202
+ feedLocation: 'NEWSFEED',
203
+ feedbackSource: 1,
204
+ groupID: null,
205
+ input: {
206
+ client_mutation_id: Math.round(Math.random() * 19).toString(),
207
+ actor_id: ctx.userID,
208
+ attachments: [],
209
+ feedback_id: Buffer.from('feedback:' + postID).toString('base64'),
210
+ message: {
211
+ ranges: [],
212
+ text: messageObject.body || ''
213
+ },
214
+ reply_comment_parent_fbid: replyCommentID || null,
215
+ is_tracking_encrypted: true,
216
+ tracking: [],
217
+ feedback_source: 'NEWS_FEED',
218
+ idempotence_token: 'client:' + utils.getGUID(),
219
+ session_id: utils.getGUID()
220
+ },
221
+ scale: 1,
222
+ useDefaultActor: false
223
+ };
224
+
225
+ // Process all handlers and create comment
226
+ handleUpload(defaultFuncs, ctx, messageObject, form)
227
+ .then(function() {
228
+ handleURL(messageObject, form);
229
+ handleMentions(messageObject, form);
230
+ handleSticker(messageObject, form);
231
+ return createContent(defaultFuncs, ctx, form);
232
+ })
233
+ .then(function(info) {
234
+ log.info('comment', 'Comment created successfully: ' + info.id);
235
+ callback(null, info);
236
+ })
237
+ .catch(function(err) {
238
+ log.error('comment', err);
239
+ callback(err);
240
+ });
241
+
242
+ return returnPromise;
243
+ };
244
+ };