@cexy/hoonfca 1.0.7 → 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 (191) hide show
  1. package/LICENSE +3 -0
  2. package/examples/login-with-cookies.js +102 -0
  3. package/examples/verify.js +70 -0
  4. package/index.js +2 -8
  5. package/package.json +82 -61
  6. package/src/{api/action → apis}/addExternalModule.js +24 -25
  7. package/src/apis/addUserToGroup.js +108 -0
  8. package/src/apis/changeAdminStatus.js +148 -0
  9. package/src/apis/changeArchivedStatus.js +61 -0
  10. package/src/apis/changeAvatar.js +103 -0
  11. package/src/apis/changeBio.js +69 -0
  12. package/src/apis/changeBlockedStatus.js +54 -0
  13. package/src/apis/changeGroupImage.js +136 -0
  14. package/src/apis/changeThreadColor.js +116 -0
  15. package/src/apis/changeThreadEmoji.js +53 -0
  16. package/src/apis/comment.js +207 -0
  17. package/src/apis/createAITheme.js +129 -0
  18. package/src/apis/createNewGroup.js +79 -0
  19. package/src/apis/createPoll.js +73 -0
  20. package/src/apis/deleteMessage.js +44 -0
  21. package/src/apis/deleteThread.js +52 -0
  22. package/src/apis/e2ee.js +17 -0
  23. package/src/apis/editMessage.js +70 -0
  24. package/src/apis/emoji.js +124 -0
  25. package/src/apis/fetchThemeData.js +82 -0
  26. package/src/apis/follow.js +81 -0
  27. package/src/apis/forwardMessage.js +52 -0
  28. package/src/apis/friend.js +243 -0
  29. package/src/apis/gcmember.js +122 -0
  30. package/src/apis/gcname.js +123 -0
  31. package/src/apis/gcrule.js +119 -0
  32. package/src/apis/getAccess.js +111 -0
  33. package/src/apis/getBotInfo.js +88 -0
  34. package/src/apis/getBotInitialData.js +43 -0
  35. package/src/apis/getFriendsList.js +79 -0
  36. package/src/apis/getMessage.js +423 -0
  37. package/src/apis/getTheme.js +95 -0
  38. package/src/apis/getThemeInfo.js +116 -0
  39. package/src/apis/getThreadHistory.js +239 -0
  40. package/src/apis/getThreadInfo.js +267 -0
  41. package/src/apis/getThreadList.js +232 -0
  42. package/src/apis/getThreadPictures.js +58 -0
  43. package/src/apis/getUserID.js +117 -0
  44. package/src/apis/getUserInfo.js +513 -0
  45. package/src/{api/users → apis}/getUserInfoV2.js +146 -134
  46. package/src/apis/handleMessageRequest.js +50 -0
  47. package/src/apis/httpGet.js +63 -0
  48. package/src/apis/httpPost.js +89 -0
  49. package/src/apis/httpPostFormData.js +69 -0
  50. package/src/apis/listenMqtt.js +1236 -0
  51. package/src/apis/listenSpeed.js +179 -0
  52. package/src/apis/logout.js +87 -0
  53. package/src/apis/markAsDelivered.js +47 -0
  54. package/src/{api/messaging → apis}/markAsRead.js +99 -88
  55. package/src/apis/markAsReadAll.js +40 -0
  56. package/src/apis/markAsSeen.js +70 -0
  57. package/src/apis/mqttDeltaValue.js +278 -0
  58. package/src/apis/muteThread.js +45 -0
  59. package/src/apis/nickname.js +132 -0
  60. package/src/apis/notes.js +163 -0
  61. package/src/apis/pinMessage.js +150 -0
  62. package/src/apis/produceMetaTheme.js +180 -0
  63. package/src/apis/realtime.js +182 -0
  64. package/src/apis/removeUserFromGroup.js +117 -0
  65. package/src/apis/resolvePhotoUrl.js +58 -0
  66. package/src/apis/searchForThread.js +154 -0
  67. package/src/apis/sendMessage.js +354 -0
  68. package/src/apis/sendMessageMqtt.js +249 -0
  69. package/src/apis/sendTypingIndicator.js +91 -0
  70. package/src/apis/setMessageReaction.js +27 -0
  71. package/src/apis/setMessageReactionMqtt.js +61 -0
  72. package/src/apis/setThreadTheme.js +260 -0
  73. package/src/apis/setThreadThemeMqtt.js +94 -0
  74. package/src/apis/share.js +107 -0
  75. package/src/apis/shareContact.js +66 -0
  76. package/src/apis/stickers.js +257 -0
  77. package/src/apis/story.js +181 -0
  78. package/src/apis/theme.js +233 -0
  79. package/src/apis/unfriend.js +47 -0
  80. package/src/apis/unsendMessage.js +17 -0
  81. package/src/engine/client.js +92 -0
  82. package/src/engine/models/buildAPI.js +152 -0
  83. package/src/engine/models/loginHelper.js +519 -0
  84. package/src/engine/models/setOptions.js +88 -0
  85. package/src/security/e2ee.js +109 -0
  86. package/src/types/index.d.ts +498 -0
  87. package/src/utils/antiSuspension.js +506 -0
  88. package/src/utils/auth-helpers.js +149 -0
  89. package/src/utils/autoReLogin.js +336 -0
  90. package/src/utils/axios.js +436 -0
  91. package/src/utils/cache.js +54 -0
  92. package/src/utils/clients.js +282 -0
  93. package/src/utils/constants.js +410 -23
  94. package/src/utils/formatters/data/formatAttachment.js +370 -0
  95. package/src/utils/formatters/data/formatDelta.js +109 -0
  96. package/src/utils/formatters/index.js +159 -0
  97. package/src/utils/formatters/value/formatCookie.js +91 -0
  98. package/src/utils/formatters/value/formatDate.js +36 -0
  99. package/src/utils/formatters/value/formatID.js +16 -0
  100. package/src/utils/formatters.js +1373 -0
  101. package/src/utils/headers.js +214 -99
  102. package/src/utils/index.js +153 -0
  103. package/src/utils/monitoring.js +333 -0
  104. package/src/utils/rateLimiter.js +319 -0
  105. package/src/utils/tokenRefresh.js +657 -0
  106. package/src/utils/user-agents.js +238 -0
  107. package/src/utils/validation.js +157 -0
  108. package/CHANGELOG.md +0 -181
  109. package/DOCS.md +0 -2636
  110. package/LICENSE-MIT +0 -21
  111. package/README.md +0 -632
  112. package/func/checkUpdate.js +0 -233
  113. package/func/logger.js +0 -48
  114. package/index.d.ts +0 -746
  115. package/module/config.js +0 -67
  116. package/module/login.js +0 -154
  117. package/module/loginHelper.js +0 -1034
  118. package/module/options.js +0 -54
  119. package/src/api/action/changeAvatar.js +0 -137
  120. package/src/api/action/changeBio.js +0 -75
  121. package/src/api/action/enableAutoSaveAppState.js +0 -73
  122. package/src/api/action/getCurrentUserID.js +0 -7
  123. package/src/api/action/handleFriendRequest.js +0 -57
  124. package/src/api/action/logout.js +0 -76
  125. package/src/api/action/refreshFb_dtsg.js +0 -48
  126. package/src/api/action/setPostReaction.js +0 -106
  127. package/src/api/action/unfriend.js +0 -54
  128. package/src/api/http/httpGet.js +0 -46
  129. package/src/api/http/httpPost.js +0 -52
  130. package/src/api/http/postFormData.js +0 -47
  131. package/src/api/messaging/addUserToGroup.js +0 -68
  132. package/src/api/messaging/changeAdminStatus.js +0 -122
  133. package/src/api/messaging/changeArchivedStatus.js +0 -55
  134. package/src/api/messaging/changeBlockedStatus.js +0 -48
  135. package/src/api/messaging/changeGroupImage.js +0 -90
  136. package/src/api/messaging/changeNickname.js +0 -70
  137. package/src/api/messaging/changeThreadColor.js +0 -79
  138. package/src/api/messaging/changeThreadEmoji.js +0 -106
  139. package/src/api/messaging/createNewGroup.js +0 -88
  140. package/src/api/messaging/createPoll.js +0 -43
  141. package/src/api/messaging/createThemeAI.js +0 -98
  142. package/src/api/messaging/deleteMessage.js +0 -56
  143. package/src/api/messaging/deleteThread.js +0 -56
  144. package/src/api/messaging/editMessage.js +0 -68
  145. package/src/api/messaging/forwardAttachment.js +0 -51
  146. package/src/api/messaging/getEmojiUrl.js +0 -29
  147. package/src/api/messaging/getFriendsList.js +0 -82
  148. package/src/api/messaging/getMessage.js +0 -829
  149. package/src/api/messaging/getThemePictures.js +0 -62
  150. package/src/api/messaging/handleMessageRequest.js +0 -65
  151. package/src/api/messaging/markAsDelivered.js +0 -57
  152. package/src/api/messaging/markAsReadAll.js +0 -49
  153. package/src/api/messaging/markAsSeen.js +0 -61
  154. package/src/api/messaging/muteThread.js +0 -50
  155. package/src/api/messaging/removeUserFromGroup.js +0 -106
  156. package/src/api/messaging/resolvePhotoUrl.js +0 -43
  157. package/src/api/messaging/scheduler.js +0 -264
  158. package/src/api/messaging/searchForThread.js +0 -52
  159. package/src/api/messaging/sendMessage.js +0 -272
  160. package/src/api/messaging/sendTypingIndicator.js +0 -67
  161. package/src/api/messaging/setMessageReaction.js +0 -76
  162. package/src/api/messaging/setTitle.js +0 -119
  163. package/src/api/messaging/shareContact.js +0 -49
  164. package/src/api/messaging/threadColors.js +0 -128
  165. package/src/api/messaging/unsendMessage.js +0 -81
  166. package/src/api/messaging/uploadAttachment.js +0 -94
  167. package/src/api/socket/core/connectMqtt.js +0 -255
  168. package/src/api/socket/core/emitAuth.js +0 -106
  169. package/src/api/socket/core/getSeqID.js +0 -40
  170. package/src/api/socket/core/getTaskResponseData.js +0 -22
  171. package/src/api/socket/core/markDelivery.js +0 -12
  172. package/src/api/socket/core/parseDelta.js +0 -391
  173. package/src/api/socket/detail/buildStream.js +0 -208
  174. package/src/api/socket/detail/constants.js +0 -24
  175. package/src/api/socket/listenMqtt.js +0 -364
  176. package/src/api/socket/middleware/index.js +0 -216
  177. package/src/api/threads/getThreadHistory.js +0 -664
  178. package/src/api/threads/getThreadInfo.js +0 -438
  179. package/src/api/threads/getThreadList.js +0 -293
  180. package/src/api/threads/getThreadPictures.js +0 -78
  181. package/src/api/users/getUserID.js +0 -65
  182. package/src/api/users/getUserInfo.js +0 -327
  183. package/src/core/sendReqMqtt.js +0 -96
  184. package/src/database/models/index.js +0 -87
  185. package/src/database/models/thread.js +0 -45
  186. package/src/database/models/user.js +0 -46
  187. package/src/database/threadData.js +0 -98
  188. package/src/database/userData.js +0 -89
  189. package/src/utils/client.js +0 -320
  190. package/src/utils/format.js +0 -1115
  191. package/src/utils/request.js +0 -305
@@ -1,115 +1,230 @@
1
1
  "use strict";
2
2
 
3
- // Sanitize header value to remove invalid characters
3
+ const { randomUserAgent, generateUserAgentByPersona } = require("./user-agents");
4
+
5
+ /**
6
+ * Strip characters that are illegal in HTTP header values:
7
+ * control chars (except HTAB), DEL, CR, LF, and stringified arrays.
8
+ * This prevents ERR_INVALID_CHAR crashes that expose bot-like behaviour.
9
+ */
4
10
  function sanitizeHeaderValue(value) {
5
- if (value === null || value === undefined) return "";
6
- let str = String(value);
7
-
8
- // Remove array-like strings (e.g., "["performAutoLogin"]")
9
- // This handles cases where arrays were accidentally stringified
10
- if (str.trim().startsWith("[") && str.trim().endsWith("]")) {
11
- // Try to detect if it's a stringified array and remove it
12
- try {
13
- const parsed = JSON.parse(str);
14
- if (Array.isArray(parsed)) {
15
- // If it's an array, return empty string (invalid header value)
16
- return "";
17
- }
18
- } catch {
19
- // Not valid JSON, continue with normal sanitization
11
+ if (value === null || value === undefined) return '';
12
+ let str = String(value);
13
+
14
+ // Detect accidentally stringified JS arrays like '["performAutoLogin"]'
15
+ if (str.trim().startsWith('[') && str.trim().endsWith(']')) {
16
+ try {
17
+ const parsed = JSON.parse(str);
18
+ if (Array.isArray(parsed)) return '';
19
+ } catch (_) {}
20
+ }
21
+
22
+ // Remove control chars (0x00-0x08, 0x0A-0x1F), DEL (0x7F), CR, LF, brackets
23
+ str = str.replace(/[\x00-\x08\x0A-\x1F\x7F\r\n\[\]]/g, '').trim();
24
+ return str;
25
+ }
26
+
27
+ function sanitizeHeaders(headers) {
28
+ const out = {};
29
+ for (const [key, value] of Object.entries(headers)) {
30
+ if (!key || typeof key !== 'string') continue;
31
+ const cleanKey = key.replace(/[^\x21-\x7E]/g, '').trim();
32
+ if (!cleanKey) continue;
33
+ const cleanVal = sanitizeHeaderValue(value);
34
+ if (cleanVal !== '') {
35
+ out[cleanKey] = cleanVal;
36
+ }
20
37
  }
21
- }
38
+ return out;
39
+ }
22
40
 
23
- // Remove invalid characters for HTTP headers:
24
- // - Control characters (0x00-0x1F, except HTAB 0x09)
25
- // - DEL character (0x7F)
26
- // - Newlines and carriage returns
27
- // - Square brackets (often indicate array stringification issues)
28
- str = str.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\r\n\[\]]/g, "").trim();
41
+ const LOCALE_PROFILES = [
42
+ 'en-US,en;q=0.9',
43
+ 'en-GB,en;q=0.9,en-US;q=0.8',
44
+ 'en-US,en;q=0.9,es;q=0.8',
45
+ 'en-US,en;q=0.9,fr;q=0.8',
46
+ 'en-CA,en;q=0.9,fr;q=0.8',
47
+ 'en-AU,en;q=0.9,en-GB;q=0.8'
48
+ ];
29
49
 
30
- return str;
50
+ const TIMEZONE_OFFSETS = [-480, -420, -360, -300, -240, -180, -120, 0, 60, 120, 180, 240, 300, 360, 480, 540, 600];
51
+
52
+ function getRandomLocale() {
53
+ return LOCALE_PROFILES[Math.floor(Math.random() * LOCALE_PROFILES.length)];
31
54
  }
32
55
 
33
- // Sanitize header name to ensure it's valid
34
- function sanitizeHeaderName(name) {
35
- if (!name || typeof name !== "string") return "";
36
- // Remove invalid characters for HTTP header names
37
- return name.replace(/[^\x21-\x7E]/g, "").trim();
56
+ function getRandomTimezone() {
57
+ return TIMEZONE_OFFSETS[Math.floor(Math.random() * TIMEZONE_OFFSETS.length)];
38
58
  }
39
59
 
40
- function getHeaders(url, options, ctx, customHeader) {
41
- const u = new URL(url);
42
- const ua = options?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36";
43
- const referer = options?.referer || "https://www.facebook.com/";
44
- const origin = referer.replace(/\/+$/, "");
45
- const contentType = options?.contentType || "application/x-www-form-urlencoded";
46
- const acceptLang = options?.acceptLanguage || "en-US,en;q=0.9,vi;q=0.8";
47
- const headers = {
48
- Host: sanitizeHeaderValue(u.host),
49
- Origin: sanitizeHeaderValue(origin),
50
- Referer: sanitizeHeaderValue(referer),
51
- "User-Agent": sanitizeHeaderValue(ua),
52
- Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,application/json;q=0.8,*/*;q=0.7",
53
- "Accept-Language": sanitizeHeaderValue(acceptLang),
54
- "Accept-Encoding": "gzip, deflate, br",
55
- "Content-Type": sanitizeHeaderValue(contentType),
56
- Connection: "keep-alive",
57
- DNT: "1",
58
- "Upgrade-Insecure-Requests": "1",
59
- "sec-ch-ua": "\"Chromium\";v=\"139\", \"Not;A=Brand\";v=\"24\", \"Google Chrome\";v=\"139\"",
60
- "sec-ch-ua-mobile": "?0",
61
- "sec-ch-ua-platform": "\"Windows\"",
62
- "sec-ch-ua-arch": "\"x86\"",
63
- "sec-ch-ua-bitness": "\"64\"",
64
- "sec-ch-ua-full-version-list": "\"Chromium\";v=\"139.0.0.0\", \"Not;A=Brand\";v=\"24.0.0.0\", \"Google Chrome\";v=\"139.0.0.0\"",
65
- "sec-ch-ua-platform-version": "\"15.0.0\"",
66
- "Sec-Fetch-Site": "same-origin",
67
- "Sec-Fetch-Mode": "cors",
68
- "Sec-Fetch-Dest": "empty",
69
- "X-Requested-With": "XMLHttpRequest",
70
- Pragma: "no-cache",
71
- "Cache-Control": "no-cache"
72
- };
73
- if (ctx?.region) {
74
- const regionValue = sanitizeHeaderValue(ctx.region);
75
- if (regionValue) headers["X-MSGR-Region"] = regionValue;
76
- }
77
- if (customHeader && typeof customHeader === "object") {
78
- // Filter customHeader to only include valid HTTP header values (strings, numbers, booleans)
79
- // Exclude functions, objects, arrays, and other non-serializable values
80
- for (const [key, value] of Object.entries(customHeader)) {
81
- // Skip null, undefined, functions, objects, and arrays
82
- if (value === null || value === undefined || typeof value === "function") {
83
- continue;
84
- }
85
- if (typeof value === "object") {
86
- // Arrays are objects in JavaScript, so check for arrays explicitly
87
- if (Array.isArray(value)) {
88
- continue;
60
+ /**
61
+ * Generates a comprehensive and realistic set of headers for requests to Facebook.
62
+ * @param {string} url - The target URL.
63
+ * @param {object} options - Global options from context.
64
+ * @param {object} ctx - The application context (containing fb_dtsg, lsd, etc.).
65
+ * @param {object} customHeader - Any extra headers to merge.
66
+ * @param {string} requestType - Type of request: 'xhr' for GraphQL/AJAX or 'navigate' for page navigation
67
+ * @returns {object} A complete headers object.
68
+ */
69
+ function getHeaders(url, options, ctx, customHeader, requestType = 'navigate') {
70
+ const persona = options?.persona || 'desktop';
71
+ const isAndroid = persona === 'android' || persona === 'mobile';
72
+
73
+ let userAgent, secChUa, secChUaFullVersionList, secChUaPlatform, secChUaPlatformVersion;
74
+ let androidData = null;
75
+
76
+ if (isAndroid) {
77
+ if (options && options.cachedAndroidUA) {
78
+ userAgent = options.cachedAndroidUA;
79
+ androidData = {
80
+ resolution: options.cachedAndroidResolution,
81
+ locale: options.cachedAndroidLocale,
82
+ device: options.cachedAndroidDevice
83
+ };
84
+ } else {
85
+ const generated = generateUserAgentByPersona('android', options);
86
+ userAgent = generated.userAgent;
87
+ androidData = {
88
+ resolution: generated.resolution,
89
+ locale: generated.locale,
90
+ device: generated.device
91
+ };
89
92
  }
90
- // Skip plain objects (but allow null which is already handled above)
91
- continue;
92
- }
93
- // Only allow strings, numbers, and booleans - convert to string and sanitize
94
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
95
- const sanitizedKey = sanitizeHeaderName(key);
96
- const sanitizedValue = sanitizeHeaderValue(value);
97
- if (sanitizedKey && sanitizedValue !== "") {
98
- headers[sanitizedKey] = sanitizedValue;
93
+ } else {
94
+ if (options && options.cachedUserAgent) {
95
+ userAgent = options.cachedUserAgent;
96
+ secChUa = options.cachedSecChUa;
97
+ secChUaFullVersionList = options.cachedSecChUaFullVersionList;
98
+ secChUaPlatform = options.cachedSecChUaPlatform;
99
+ secChUaPlatformVersion = options.cachedSecChUaPlatformVersion;
100
+ } else {
101
+ const generated = randomUserAgent();
102
+ userAgent = generated.userAgent;
103
+ secChUa = generated.secChUa;
104
+ secChUaFullVersionList = generated.secChUaFullVersionList;
105
+ secChUaPlatform = generated.secChUaPlatform;
106
+ secChUaPlatformVersion = generated.secChUaPlatformVersion;
99
107
  }
100
- }
101
108
  }
102
- }
103
- // Final pass: sanitize all header values to ensure no invalid characters
104
- const sanitizedHeaders = {};
105
- for (const [key, value] of Object.entries(headers)) {
106
- const sanitizedKey = sanitizeHeaderName(key);
107
- const sanitizedValue = sanitizeHeaderValue(value);
108
- if (sanitizedKey && sanitizedValue !== "") {
109
- sanitizedHeaders[sanitizedKey] = sanitizedValue;
109
+
110
+ const host = new URL(url).hostname;
111
+ const referer = `https://${host}/`;
112
+
113
+ const isXhr = requestType === 'xhr';
114
+
115
+ const locales = options?.cachedLocale || (androidData?.locale ? androidData.locale.replace('_', '-') : getRandomLocale());
116
+ const timezone = options?.cachedTimezone || getRandomTimezone();
117
+
118
+ if (isAndroid) {
119
+ const headers = {
120
+ 'Content-Type': 'application/x-www-form-urlencoded',
121
+ 'Host': host,
122
+ 'Connection': 'keep-alive',
123
+ 'User-Agent': userAgent,
124
+ 'Accept': '*/*',
125
+ 'Accept-Language': locales,
126
+ 'Accept-Encoding': 'gzip, deflate',
127
+ 'X-FB-HTTP-Engine': 'Liger'
128
+ };
129
+
130
+ if (androidData && androidData.resolution) {
131
+ headers['X-FB-Client-Density'] = String(androidData.resolution.density);
132
+ }
133
+
134
+ if (ctx) {
135
+ if (ctx.lsd || ctx.fb_dtsg) {
136
+ headers['X-Fb-Lsd'] = ctx.lsd || ctx.fb_dtsg;
137
+ }
138
+ if (ctx.region) {
139
+ headers['X-MSGR-Region'] = ctx.region;
140
+ }
141
+ // Spin params are spread directly into ctx by buildAPI — ctx.master never exists.
142
+ if (ctx.__spin_r) headers['X-Fb-Spin-R'] = String(ctx.__spin_r);
143
+ if (ctx.__spin_b) headers['X-Fb-Spin-B'] = String(ctx.__spin_b);
144
+ if (ctx.__spin_t) headers['X-Fb-Spin-T'] = String(ctx.__spin_t);
145
+ }
146
+
147
+ if (customHeader) {
148
+ Object.assign(headers, customHeader);
149
+ if (customHeader.noRef) {
150
+ delete headers.Referer;
151
+ }
152
+ }
153
+
154
+ return sanitizeHeaders(headers);
155
+ }
156
+
157
+ const isWindows = secChUaPlatform === '"Windows"';
158
+ const isMac = secChUaPlatform === '"macOS"';
159
+ const isLinux = secChUaPlatform === '"Linux"';
160
+
161
+ const headers = {
162
+ 'Accept': isXhr ? '*/*' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
163
+ 'Accept-Language': locales,
164
+ 'Accept-Encoding': 'gzip, deflate, br',
165
+ 'Cache-Control': 'no-cache',
166
+ 'Connection': 'keep-alive',
167
+ 'DNT': '1',
168
+ 'Dpr': '1',
169
+ 'Host': host,
170
+ 'Pragma': 'no-cache',
171
+ 'Referer': referer,
172
+ 'Sec-Ch-Prefers-Color-Scheme': 'light',
173
+ 'Sec-Ch-Ua': secChUa,
174
+ 'Sec-Ch-Ua-Full-Version-List': secChUaFullVersionList,
175
+ 'Sec-Ch-Ua-Mobile': '?0',
176
+ 'Sec-Ch-Ua-Model': '""',
177
+ 'Sec-Ch-Ua-Platform': secChUaPlatform,
178
+ 'Sec-Ch-Ua-Platform-Version': secChUaPlatformVersion,
179
+ 'Sec-Fetch-Dest': isXhr ? 'empty' : 'document',
180
+ 'Sec-Fetch-Mode': isXhr ? 'cors' : 'navigate',
181
+ 'Sec-Fetch-Site': isXhr ? 'same-origin' : 'none',
182
+ 'User-Agent': userAgent,
183
+ 'Viewport-Width': '1920',
184
+ 'X-FB-Timezone-Offset': String(timezone * 60)
185
+ };
186
+
187
+ if (isWindows || isMac || isLinux) {
188
+ headers['Sec-Ch-Ua-Arch'] = '"x86"';
189
+ headers['Sec-Ch-Ua-Bitness'] = '"64"';
190
+ }
191
+
192
+ if (isXhr) {
193
+ headers['Origin'] = `https://${host}`;
194
+ headers['X-Requested-With'] = 'XMLHttpRequest';
195
+ } else {
196
+ headers['Sec-Fetch-User'] = '?1';
197
+ headers['Upgrade-Insecure-Requests'] = '1';
110
198
  }
111
- }
112
- return sanitizedHeaders;
199
+
200
+ if (ctx) {
201
+ if (ctx.lsd || ctx.fb_dtsg) {
202
+ headers['X-Fb-Lsd'] = ctx.lsd || ctx.fb_dtsg;
203
+ }
204
+ if (ctx.region) {
205
+ headers['X-MSGR-Region'] = ctx.region;
206
+ }
207
+ // Spin params are spread directly into ctx by buildAPI — ctx.master never exists.
208
+ if (ctx.__spin_r) headers['X-Fb-Spin-R'] = String(ctx.__spin_r);
209
+ if (ctx.__spin_b) headers['X-Fb-Spin-B'] = String(ctx.__spin_b);
210
+ if (ctx.__spin_t) headers['X-Fb-Spin-T'] = String(ctx.__spin_t);
211
+ }
212
+
213
+ if (customHeader) {
214
+ Object.assign(headers, customHeader);
215
+ if (customHeader.noRef) {
216
+ delete headers.Referer;
217
+ }
218
+ }
219
+
220
+ return sanitizeHeaders(headers);
113
221
  }
114
222
 
115
- module.exports = { getHeaders };
223
+ const meta = (prop) => new RegExp(`<meta property="${prop}" content="([^"]*)"`);
224
+
225
+ module.exports = {
226
+ getHeaders,
227
+ meta,
228
+ getRandomLocale,
229
+ getRandomTimezone
230
+ };
@@ -0,0 +1,153 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ "use strict";
3
+
4
+ const cheerio = require("cheerio");
5
+ const util = require("util");
6
+
7
+ /**
8
+ * Fetches a URL, scrapes all <script type="application/json"> tags, and returns their parsed content.
9
+ * @param {string} url - The URL to fetch.
10
+ * @param {object} jar - The cookie jar.
11
+ * @param {object} qs - Query string parameters.
12
+ * @param {object} options - Global options.
13
+ * @param {object} ctx - The application context.
14
+ * @param {object} customHeader - Custom headers.
15
+ * @returns {Promise<Array<Object>>} A promise that resolves with an array of parsed JSON objects.
16
+ */
17
+ async function json(url, jar, qs, options, ctx, customHeader) {
18
+ const network = require("./axios");
19
+ const constants = require("./constants");
20
+
21
+ try {
22
+ const res = await network.get(url, jar, qs, options, ctx, customHeader);
23
+ const body = res.body;
24
+ const $ = cheerio.load(body);
25
+ const scripts = $('script[type="application/json"]');
26
+
27
+ if (scripts.length === 0) {
28
+ constants.warn(`No <script type="application/json"> tags found on ${url}`);
29
+ return [];
30
+ }
31
+
32
+ const allJsonData = [];
33
+ scripts.each((index, element) => {
34
+ try {
35
+ const jsonContent = $(element).html();
36
+ if (jsonContent) {
37
+ allJsonData.push(JSON.parse(jsonContent));
38
+ }
39
+ } catch (e) {
40
+ constants.warn(`Could not parse JSON from script #${index + 1} on ${url}`);
41
+ }
42
+ });
43
+
44
+ return allJsonData;
45
+ } catch (error) {
46
+ constants.error(`Error in utils.json fetching from ${url}:`, error);
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Creates an object with pre-filled request defaults based on HTML and user context.
53
+ * @param {string} html - The HTML content.
54
+ * @param {string|number} userID - The user ID.
55
+ * @param {object} ctx - The context containing session data.
56
+ * @returns {object} An object with .get, .post, and .postFormData methods.
57
+ */
58
+ function makeDefaults(html, userID, ctx) {
59
+ const network = require("./axios");
60
+ const constants = require("./constants");
61
+
62
+ let reqCounter = 1;
63
+ const revision = constants.getFrom(html, 'revision":', ",");
64
+
65
+ /**
66
+ * Merges provided data with default request parameters.
67
+ * @param {object} obj - The original query or form object.
68
+ * @returns {object} The merged object.
69
+ */
70
+ function mergeWithDefaults(obj) {
71
+ const newObj = {
72
+ av: userID,
73
+ __user: userID,
74
+ __req: (reqCounter++).toString(36),
75
+ __rev: revision,
76
+ __a: 1,
77
+ ...(ctx && {
78
+ fb_dtsg: ctx.fb_dtsg,
79
+ jazoest: ctx.jazoest,
80
+ }),
81
+ };
82
+
83
+ if (!obj) return newObj;
84
+
85
+ for (const prop in obj) {
86
+ if (obj.hasOwnProperty(prop) && !(prop in newObj)) {
87
+ newObj[prop] = obj[prop];
88
+ }
89
+ }
90
+
91
+ return newObj;
92
+ }
93
+
94
+ return {
95
+ /**
96
+ * Makes a GET request with merged defaults.
97
+ * @param {string} url
98
+ * @param {object} jar
99
+ * @param {object} qs
100
+ * @param {object} ctxx
101
+ * @param {object} [customHeader={}]
102
+ * @returns {Promise<object>}
103
+ */
104
+ get: (url, jar, qs, ctxx, customHeader = {}) =>
105
+ network.get(url, jar, mergeWithDefaults(qs), ctx?.globalOptions, ctxx || ctx, customHeader),
106
+
107
+ /**
108
+ * Makes a POST request with merged defaults.
109
+ * @param {string} url
110
+ * @param {object} jar
111
+ * @param {object} form
112
+ * @param {object} ctxx
113
+ * @param {object} [customHeader={}]
114
+ * @returns {Promise<object>}
115
+ */
116
+ post: (url, jar, form, ctxx, customHeader = {}) =>
117
+ network.post(url, jar, mergeWithDefaults(form), ctx?.globalOptions, ctxx || ctx, customHeader),
118
+
119
+ /**
120
+ * Makes a multipart/form-data POST request with merged defaults.
121
+ * @param {string} url
122
+ * @param {object} jar
123
+ * @param {object} form
124
+ * @param {object} qs
125
+ * @param {object} ctxx
126
+ * @returns {Promise<object>}
127
+ */
128
+ postFormData: (url, jar, form, qs, ctxx) =>
129
+ network.postFormData(
130
+ url,
131
+ jar,
132
+ mergeWithDefaults(form),
133
+ mergeWithDefaults(qs),
134
+ ctx?.globalOptions,
135
+ ctxx || ctx
136
+ ),
137
+ };
138
+ }
139
+
140
+ module.exports = {
141
+ ...require('./axios'),
142
+ ...require('./clients'),
143
+ ...require('./constants'),
144
+ ...require('./formatters'),
145
+ ...require('./headers'),
146
+ ...require('./user-agents'),
147
+ ...require('./antiSuspension'),
148
+ ...require('./rateLimiter'),
149
+ json,
150
+ makeDefaults,
151
+ promisify: (func) => util.promisify(func),
152
+ delay: (ms) => new Promise(r => setTimeout(r, ms))
153
+ };