bb-fca 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deltas/apis/posting/friend.js +319 -52
- package/dist/deltas/apis/posting/friend.js.map +1 -1
- package/dist/index.d.ts +41 -2
- package/dist/types/deltas/apis/posting/friend.d.ts +12 -3
- package/package.json +1 -1
- package/src/deltas/apis/posting/friend.ts +518 -158
- package/src/types/index.d.ts +41 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import utils = require(
|
|
1
|
+
import utils = require('../../../utils');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @ChoruOfficial
|
|
@@ -9,7 +9,6 @@ import utils = require("../../../utils");
|
|
|
9
9
|
* @returns {Object} A `friendModule` object with methods for friend interactions.
|
|
10
10
|
*/
|
|
11
11
|
export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* A private helper function to standardize friend data from various GraphQL endpoints.
|
|
15
14
|
* @private
|
|
@@ -21,23 +20,29 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
21
20
|
const viewer = data?.data?.viewer;
|
|
22
21
|
let edges;
|
|
23
22
|
if (type === 'requests' && viewer?.friend_requests?.edges) {
|
|
24
|
-
|
|
23
|
+
edges = viewer.friend_requests.edges;
|
|
25
24
|
} else if (type === 'suggestions' && viewer?.people_you_may_know?.edges) {
|
|
26
|
-
|
|
27
|
-
} else if (
|
|
28
|
-
|
|
25
|
+
edges = viewer.people_you_may_know.edges;
|
|
26
|
+
} else if (
|
|
27
|
+
type === 'list' &&
|
|
28
|
+
data?.data?.node?.all_collections?.nodes[0]?.style_renderer?.collection
|
|
29
|
+
?.pageItems?.edges
|
|
30
|
+
) {
|
|
31
|
+
edges =
|
|
32
|
+
data.data.node.all_collections.nodes[0].style_renderer.collection
|
|
33
|
+
.pageItems.edges;
|
|
29
34
|
} else {
|
|
30
|
-
|
|
35
|
+
return [];
|
|
31
36
|
}
|
|
32
|
-
return edges.map(edge => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
return edges.map((edge) => {
|
|
38
|
+
const node = edge.node;
|
|
39
|
+
return {
|
|
40
|
+
userID: node.id || node.node?.id,
|
|
41
|
+
name: node.name || node.title?.text,
|
|
42
|
+
profilePicture: node.profile_picture?.uri || node.image?.uri,
|
|
43
|
+
socialContext: node.social_context?.text || node.subtitle_text?.text,
|
|
44
|
+
url: node.url,
|
|
45
|
+
};
|
|
41
46
|
});
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -50,79 +55,147 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
50
55
|
*/
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
|
-
* Fetches the list of incoming friend requests.
|
|
58
|
+
* Fetches the list of incoming friend requests by parsing the /friends/requests page.
|
|
54
59
|
* @async
|
|
55
60
|
* @returns {Promise<Array<Object>>} A promise that resolves to an array of friend request objects.
|
|
56
|
-
* @throws {Error} If the API request fails or
|
|
61
|
+
* @throws {Error} If the API request fails or HTML parsing fails.
|
|
57
62
|
*/
|
|
58
63
|
requests: async function() {
|
|
59
64
|
try {
|
|
65
|
+
const url = 'https://www.facebook.com/friends/requests';
|
|
66
|
+
const resData = await utils.get(
|
|
67
|
+
url,
|
|
68
|
+
ctx.jar,
|
|
69
|
+
{},
|
|
70
|
+
ctx.globalOptions,
|
|
71
|
+
ctx,
|
|
72
|
+
);
|
|
73
|
+
const html = resData.body.toString();
|
|
74
|
+
return extractFriendRequestsFromHTML(html);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
utils.error('friend.requests', err);
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Accepts a friend request.
|
|
83
|
+
* @async
|
|
84
|
+
* @param {string} identifier The user ID or name of the person whose friend request is to be accepted. If a name is provided, the function will search through pending requests.
|
|
85
|
+
* @returns {Promise<Object>} A promise that resolves to the API response data upon successful acceptance.
|
|
86
|
+
* @throws {Error} If the identifier is missing, the user is not found in requests, or the API call fails.
|
|
87
|
+
*/
|
|
88
|
+
accept: async function(identifier) {
|
|
89
|
+
try {
|
|
90
|
+
if (!identifier) throw new Error('A name or user ID is required.');
|
|
91
|
+
let targetUserID = identifier;
|
|
92
|
+
if (isNaN(identifier)) {
|
|
93
|
+
const requests = await friendModule.requests();
|
|
94
|
+
const found = requests.find((req) =>
|
|
95
|
+
req.name.toLowerCase().includes(identifier.toLowerCase()),
|
|
96
|
+
);
|
|
97
|
+
if (!found)
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Could not find any friend request matching "${identifier}".`,
|
|
100
|
+
);
|
|
101
|
+
targetUserID = found.userID;
|
|
102
|
+
}
|
|
103
|
+
const variables = {
|
|
104
|
+
input: {
|
|
105
|
+
friend_requester_id: targetUserID,
|
|
106
|
+
friending_channel: 'FRIENDS_HOME_MAIN',
|
|
107
|
+
actor_id: ctx.userID,
|
|
108
|
+
client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
|
|
109
|
+
},
|
|
110
|
+
scale: 3,
|
|
111
|
+
};
|
|
60
112
|
const form = {
|
|
61
113
|
av: ctx.userID,
|
|
62
114
|
__user: ctx.userID,
|
|
63
|
-
__a:
|
|
115
|
+
__a: '1',
|
|
64
116
|
fb_dtsg: ctx.fb_dtsg,
|
|
65
117
|
jazoest: ctx.jazoest,
|
|
66
118
|
lsd: ctx.lsd,
|
|
67
|
-
fb_api_caller_class:
|
|
68
|
-
fb_api_req_friendly_name:
|
|
69
|
-
|
|
70
|
-
|
|
119
|
+
fb_api_caller_class: 'RelayModern',
|
|
120
|
+
fb_api_req_friendly_name:
|
|
121
|
+
'FriendingCometFriendRequestConfirmMutation',
|
|
122
|
+
variables: JSON.stringify(variables),
|
|
123
|
+
doc_id: '24630768433181357',
|
|
71
124
|
};
|
|
72
|
-
const res = await defaultFuncs.post(
|
|
125
|
+
const res = await defaultFuncs.post(
|
|
126
|
+
'https://www.facebook.com/api/graphql/',
|
|
127
|
+
ctx.jar,
|
|
128
|
+
form,
|
|
129
|
+
{},
|
|
130
|
+
);
|
|
73
131
|
if (res.data.errors) throw new Error(JSON.stringify(res.data.errors));
|
|
74
|
-
return
|
|
132
|
+
return res.data.data;
|
|
75
133
|
} catch (err) {
|
|
134
|
+
if (err.message?.includes('1431004')) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
'I cannot accept this friend request right now. There might be a problem with the account or you need to wait.',
|
|
137
|
+
);
|
|
138
|
+
}
|
|
76
139
|
throw err;
|
|
77
140
|
}
|
|
78
141
|
},
|
|
79
142
|
|
|
80
143
|
/**
|
|
81
|
-
*
|
|
144
|
+
* Deletes/rejects a friend request.
|
|
82
145
|
* @async
|
|
83
|
-
* @param {string} identifier The user ID or name of the person whose friend request is to be
|
|
84
|
-
* @returns {Promise<Object>} A promise that resolves to the API response data upon successful
|
|
146
|
+
* @param {string} identifier The user ID or name of the person whose friend request is to be deleted. If a name is provided, the function will search through pending requests.
|
|
147
|
+
* @returns {Promise<Object>} A promise that resolves to the API response data upon successful deletion.
|
|
85
148
|
* @throws {Error} If the identifier is missing, the user is not found in requests, or the API call fails.
|
|
86
149
|
*/
|
|
87
|
-
|
|
150
|
+
delete: async function(identifier) {
|
|
88
151
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
152
|
+
if (!identifier) throw new Error('A name or user ID is required.');
|
|
153
|
+
let targetUserID = identifier;
|
|
154
|
+
if (isNaN(identifier)) {
|
|
155
|
+
const requests = await friendModule.requests();
|
|
156
|
+
const found = requests.find((req) =>
|
|
157
|
+
req.name.toLowerCase().includes(identifier.toLowerCase()),
|
|
158
|
+
);
|
|
159
|
+
if (!found)
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Could not find any friend request matching "${identifier}".`,
|
|
162
|
+
);
|
|
163
|
+
targetUserID = found.userID;
|
|
164
|
+
}
|
|
165
|
+
const variables = {
|
|
166
|
+
input: {
|
|
167
|
+
click_correlation_id: Date.now().toString(),
|
|
168
|
+
click_proof_validation_result: '{"validated":true}',
|
|
169
|
+
friend_requester_id: targetUserID,
|
|
170
|
+
friending_channel: 'FRIENDS_HOME_REQUESTS',
|
|
171
|
+
actor_id: ctx.userID,
|
|
172
|
+
client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
|
|
173
|
+
},
|
|
174
|
+
scale: 1,
|
|
175
|
+
refresh_num: 0,
|
|
176
|
+
};
|
|
177
|
+
const form = {
|
|
178
|
+
av: ctx.userID,
|
|
179
|
+
__user: ctx.userID,
|
|
180
|
+
__a: '1',
|
|
181
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
182
|
+
jazoest: ctx.jazoest,
|
|
183
|
+
lsd: ctx.lsd,
|
|
184
|
+
fb_api_caller_class: 'RelayModern',
|
|
185
|
+
fb_api_req_friendly_name: 'FriendingCometFriendRequestDeleteMutation',
|
|
186
|
+
variables: JSON.stringify(variables),
|
|
187
|
+
doc_id: '25693816160239288',
|
|
188
|
+
};
|
|
189
|
+
const res = await defaultFuncs.post(
|
|
190
|
+
'https://www.facebook.com/api/graphql/',
|
|
191
|
+
ctx.jar,
|
|
192
|
+
form,
|
|
193
|
+
{},
|
|
194
|
+
);
|
|
195
|
+
if (res.data.errors) throw new Error(JSON.stringify(res.data.errors));
|
|
196
|
+
return res.data.data;
|
|
121
197
|
} catch (err) {
|
|
122
|
-
|
|
123
|
-
throw new Error("I cannot accept this friend request right now. There might be a problem with the account or you need to wait.");
|
|
124
|
-
}
|
|
125
|
-
throw err;
|
|
198
|
+
throw err;
|
|
126
199
|
}
|
|
127
200
|
},
|
|
128
201
|
|
|
@@ -134,108 +207,395 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
134
207
|
* @throws {Error} If the API request fails.
|
|
135
208
|
*/
|
|
136
209
|
list: async function(userID = ctx.userID) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
210
|
+
try {
|
|
211
|
+
const sectionToken = Buffer.from(
|
|
212
|
+
`app_section:${userID}:2356318349`,
|
|
213
|
+
).toString('base64');
|
|
214
|
+
const variables = {
|
|
215
|
+
collectionToken: null,
|
|
216
|
+
scale: 2,
|
|
217
|
+
sectionToken: sectionToken,
|
|
218
|
+
useDefaultActor: false,
|
|
219
|
+
userID: userID,
|
|
220
|
+
};
|
|
221
|
+
const form = {
|
|
222
|
+
av: ctx.userID,
|
|
223
|
+
__user: ctx.userID,
|
|
224
|
+
__a: '1',
|
|
225
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
226
|
+
jazoest: ctx.jazoest,
|
|
227
|
+
lsd: ctx.lsd,
|
|
228
|
+
fb_api_caller_class: 'RelayModern',
|
|
229
|
+
fb_api_req_friendly_name: 'ProfileCometTopAppSectionQuery',
|
|
230
|
+
variables: JSON.stringify(variables),
|
|
231
|
+
doc_id: '24492266383698794',
|
|
232
|
+
};
|
|
233
|
+
const res = await defaultFuncs.post(
|
|
234
|
+
'https://www.facebook.com/api/graphql/',
|
|
235
|
+
ctx.jar,
|
|
236
|
+
form,
|
|
237
|
+
{},
|
|
238
|
+
);
|
|
239
|
+
if (res.data.errors) throw new Error(JSON.stringify(res.data.errors));
|
|
240
|
+
return formatFriends(res.data, 'list');
|
|
241
|
+
} catch (err) {
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
164
244
|
},
|
|
165
245
|
|
|
166
246
|
/**
|
|
167
247
|
* @namespace api.friend.suggest
|
|
168
248
|
* @description Functions for managing friend suggestions.
|
|
169
249
|
*/
|
|
250
|
+
getCurrentFriends: getCurrentFriends,
|
|
251
|
+
|
|
170
252
|
suggest: {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Fetches a list of suggested friends (People You May Know).
|
|
255
|
+
* @async
|
|
256
|
+
* @param {number} [limit=30] The maximum number of suggestions to fetch.
|
|
257
|
+
* @returns {Promise<Array<Object>>} A promise that resolves to an array of suggested friend objects.
|
|
258
|
+
* @throws {Error} If the API request fails.
|
|
259
|
+
*/
|
|
260
|
+
list: async function(limit = 30) {
|
|
261
|
+
try {
|
|
262
|
+
const form = {
|
|
263
|
+
av: ctx.userID,
|
|
264
|
+
__user: ctx.userID,
|
|
265
|
+
__a: '1',
|
|
266
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
267
|
+
jazoest: ctx.jazoest,
|
|
268
|
+
lsd: ctx.lsd,
|
|
269
|
+
fb_api_caller_class: 'RelayModern',
|
|
270
|
+
fb_api_req_friendly_name: 'FriendingCometPYMKPanelPaginationQuery',
|
|
271
|
+
variables: JSON.stringify({ count: limit, cursor: null, scale: 3 }),
|
|
272
|
+
doc_id: '9917809191634193',
|
|
273
|
+
};
|
|
274
|
+
const res = await defaultFuncs.post(
|
|
275
|
+
'https://www.facebook.com/api/graphql/',
|
|
276
|
+
ctx.jar,
|
|
277
|
+
form,
|
|
278
|
+
{},
|
|
279
|
+
);
|
|
280
|
+
if (res.data.errors) throw new Error(JSON.stringify(res.data.errors));
|
|
281
|
+
return formatFriends(res.data, 'suggestions');
|
|
282
|
+
} catch (err) {
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
/**
|
|
287
|
+
* Sends a friend request to a user.
|
|
288
|
+
* @async
|
|
289
|
+
* @param {string} userID The ID of the user to send the friend request to.
|
|
290
|
+
* @returns {Promise<Object>} A promise that resolves to the API response data on success.
|
|
291
|
+
* @throws {Error} If the userID is missing or the API request fails.
|
|
292
|
+
*/
|
|
293
|
+
request: async function(userID) {
|
|
294
|
+
try {
|
|
295
|
+
if (!userID) throw new Error('userID is required.');
|
|
296
|
+
const variables = {
|
|
297
|
+
input: {
|
|
298
|
+
friend_requestee_ids: [userID],
|
|
299
|
+
friending_channel: 'FRIENDS_HOME_MAIN',
|
|
300
|
+
actor_id: ctx.userID,
|
|
301
|
+
client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
|
|
302
|
+
},
|
|
303
|
+
scale: 3,
|
|
304
|
+
};
|
|
305
|
+
const form = {
|
|
306
|
+
av: ctx.userID,
|
|
307
|
+
__user: ctx.userID,
|
|
308
|
+
__a: '1',
|
|
309
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
310
|
+
jazoest: ctx.jazoest,
|
|
311
|
+
lsd: ctx.lsd,
|
|
312
|
+
fb_api_caller_class: 'RelayModern',
|
|
313
|
+
fb_api_req_friendly_name: 'FriendingCometFriendRequestSendMutation',
|
|
314
|
+
variables: JSON.stringify(variables),
|
|
315
|
+
doc_id: '23982103144788355',
|
|
316
|
+
};
|
|
317
|
+
const res = await defaultFuncs.post(
|
|
318
|
+
'https://www.facebook.com/api/graphql/',
|
|
319
|
+
ctx.jar,
|
|
320
|
+
form,
|
|
321
|
+
{},
|
|
322
|
+
);
|
|
323
|
+
if (res.data.errors) throw new Error(JSON.stringify(res.data.errors));
|
|
324
|
+
return res.data.data;
|
|
325
|
+
} catch (err) {
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Gets current user's friends list by fetching the friends/list page
|
|
334
|
+
* and extracting all_friends data from the embedded JSON.
|
|
335
|
+
* @async
|
|
336
|
+
* @param {Function} [callback] - Optional callback function.
|
|
337
|
+
* @returns {Promise<Array<Object>>} A promise that resolves to an array of friend objects.
|
|
338
|
+
*/
|
|
339
|
+
async function getCurrentFriends(callback?) {
|
|
340
|
+
let resolveFunc: Function = function() {};
|
|
341
|
+
let rejectFunc: Function = function() {};
|
|
342
|
+
|
|
343
|
+
const returnPromise = new Promise<any>(function(resolve, reject) {
|
|
344
|
+
resolveFunc = resolve;
|
|
345
|
+
rejectFunc = reject;
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
callback =
|
|
349
|
+
callback ||
|
|
350
|
+
function(err, data) {
|
|
351
|
+
if (err) return rejectFunc(err);
|
|
352
|
+
resolveFunc(data);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
const url = 'https://www.facebook.com/friends/list';
|
|
357
|
+
const resData = await utils.get(url, ctx.jar, {}, ctx.globalOptions, ctx);
|
|
358
|
+
const html = resData.body.toString();
|
|
359
|
+
|
|
360
|
+
const friends = extractFriendsFromHTML(html);
|
|
361
|
+
callback(null, friends);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
utils.error('getCurrentFriends', err);
|
|
364
|
+
callback(err);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return returnPromise;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Extracts all_friends data from the friends/list HTML page.
|
|
372
|
+
* Parses embedded <script type="application/json" data-sjs> tags
|
|
373
|
+
* to find the RelayPrefetchedStreamCache containing all_friends.
|
|
374
|
+
* @private
|
|
375
|
+
*/
|
|
376
|
+
function extractFriendsFromHTML(htmlContent) {
|
|
377
|
+
const friends: any[] = [];
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
const scriptMatches = htmlContent.match(
|
|
381
|
+
/<script type="application\/json"\s+data-content-len="\d+"\s+data-sjs>(.*?)<\/script>/gs,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (!scriptMatches) return friends;
|
|
385
|
+
|
|
386
|
+
for (const scriptMatch of scriptMatches) {
|
|
387
|
+
try {
|
|
388
|
+
const jsonMatch = scriptMatch.match(/<script[^>]*>(.*?)<\/script>/s);
|
|
389
|
+
if (!jsonMatch) continue;
|
|
390
|
+
|
|
391
|
+
const jsonData = JSON.parse(jsonMatch[1]);
|
|
392
|
+
|
|
393
|
+
if (!jsonData.require || !Array.isArray(jsonData.require)) continue;
|
|
394
|
+
|
|
395
|
+
for (const req of jsonData.require) {
|
|
396
|
+
if (!Array.isArray(req)) continue;
|
|
397
|
+
|
|
398
|
+
if (req[0] === 'ScheduledServerJS' && req[1] === 'handle') {
|
|
399
|
+
const payload = req[3];
|
|
400
|
+
if (!payload || !Array.isArray(payload)) continue;
|
|
401
|
+
|
|
402
|
+
for (const item of payload) {
|
|
403
|
+
if (!item.__bbox || !item.__bbox.require) continue;
|
|
404
|
+
|
|
405
|
+
for (const bboxReq of item.__bbox.require) {
|
|
406
|
+
if (!Array.isArray(bboxReq)) continue;
|
|
407
|
+
|
|
408
|
+
if (
|
|
409
|
+
bboxReq[0] === 'RelayPrefetchedStreamCache' &&
|
|
410
|
+
bboxReq[1] === 'next'
|
|
411
|
+
) {
|
|
412
|
+
const streamData = bboxReq[3];
|
|
413
|
+
if (
|
|
414
|
+
!streamData ||
|
|
415
|
+
!Array.isArray(streamData) ||
|
|
416
|
+
streamData.length < 2
|
|
417
|
+
)
|
|
418
|
+
continue;
|
|
419
|
+
|
|
420
|
+
const cacheKey = streamData[0];
|
|
421
|
+
if (
|
|
422
|
+
typeof cacheKey !== 'string' ||
|
|
423
|
+
!cacheKey.includes('FriendingCometAllFriendsRootQuery')
|
|
424
|
+
)
|
|
425
|
+
continue;
|
|
426
|
+
|
|
427
|
+
const bboxData = streamData[1];
|
|
428
|
+
if (
|
|
429
|
+
!bboxData ||
|
|
430
|
+
!bboxData.__bbox ||
|
|
431
|
+
!bboxData.__bbox.result
|
|
432
|
+
)
|
|
433
|
+
continue;
|
|
434
|
+
|
|
435
|
+
const viewer = bboxData.__bbox.result?.data?.viewer;
|
|
436
|
+
if (!viewer || !viewer.all_friends) continue;
|
|
437
|
+
|
|
438
|
+
const allFriends = viewer.all_friends;
|
|
439
|
+
const friendCount =
|
|
440
|
+
viewer.all_friends_data?.friend_count ||
|
|
441
|
+
viewer.all_friends_data?.count ||
|
|
442
|
+
0;
|
|
443
|
+
|
|
444
|
+
if (allFriends.edges && Array.isArray(allFriends.edges)) {
|
|
445
|
+
for (const edge of allFriends.edges) {
|
|
446
|
+
if (!edge.node) continue;
|
|
447
|
+
|
|
448
|
+
const node = edge.node;
|
|
449
|
+
friends.push({
|
|
450
|
+
userID: node.id,
|
|
451
|
+
name: node.name,
|
|
452
|
+
shortName: node.short_name,
|
|
453
|
+
gender: node.gender,
|
|
454
|
+
profilePicture: node.profile_picture?.uri,
|
|
455
|
+
profileUrl: node.url,
|
|
456
|
+
friendshipStatus: node.friendship_status,
|
|
457
|
+
subscribeStatus: node.subscribe_status,
|
|
458
|
+
socialContext: node.social_context?.text,
|
|
459
|
+
mutualFriends:
|
|
460
|
+
node.social_context_top_mutual_friends || [],
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Attach pagination and count info
|
|
466
|
+
(friends as any).totalCount = friendCount;
|
|
467
|
+
(friends as any).pageInfo = allFriends.page_info || null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
197
472
|
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
473
|
+
} catch (e) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} catch (error) {
|
|
478
|
+
utils.error('extractFriendsFromHTML', error);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return friends;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Extracts friending_possibilities data from the friends/requests HTML page.
|
|
486
|
+
* Parses embedded <script type="application/json" data-sjs> tags
|
|
487
|
+
* to find the RelayPrefetchedStreamCache containing friend requests.
|
|
488
|
+
* @private
|
|
489
|
+
*/
|
|
490
|
+
function extractFriendRequestsFromHTML(htmlContent) {
|
|
491
|
+
const requests: any[] = [];
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const scriptMatches = htmlContent.match(
|
|
495
|
+
/<script type="application\/json"\s+data-content-len="\d+"\s+data-sjs>(.*?)<\/script>/gs,
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
if (!scriptMatches) return requests;
|
|
499
|
+
|
|
500
|
+
for (const scriptMatch of scriptMatches) {
|
|
501
|
+
try {
|
|
502
|
+
const jsonMatch = scriptMatch.match(/<script[^>]*>(.*?)<\/script>/s);
|
|
503
|
+
if (!jsonMatch) continue;
|
|
504
|
+
|
|
505
|
+
const jsonData = JSON.parse(jsonMatch[1]);
|
|
506
|
+
|
|
507
|
+
if (!jsonData.require || !Array.isArray(jsonData.require)) continue;
|
|
508
|
+
|
|
509
|
+
for (const req of jsonData.require) {
|
|
510
|
+
if (!Array.isArray(req)) continue;
|
|
511
|
+
|
|
512
|
+
if (req[0] === 'ScheduledServerJS' && req[1] === 'handle') {
|
|
513
|
+
const payload = req[3];
|
|
514
|
+
if (!payload || !Array.isArray(payload)) continue;
|
|
515
|
+
|
|
516
|
+
for (const item of payload) {
|
|
517
|
+
if (!item.__bbox || !item.__bbox.require) continue;
|
|
518
|
+
|
|
519
|
+
for (const bboxReq of item.__bbox.require) {
|
|
520
|
+
if (!Array.isArray(bboxReq)) continue;
|
|
521
|
+
|
|
522
|
+
if (
|
|
523
|
+
bboxReq[0] === 'RelayPrefetchedStreamCache' &&
|
|
524
|
+
bboxReq[1] === 'next'
|
|
525
|
+
) {
|
|
526
|
+
const streamData = bboxReq[3];
|
|
527
|
+
if (
|
|
528
|
+
!streamData ||
|
|
529
|
+
!Array.isArray(streamData) ||
|
|
530
|
+
streamData.length < 2
|
|
531
|
+
)
|
|
532
|
+
continue;
|
|
533
|
+
|
|
534
|
+
const cacheKey = streamData[0];
|
|
535
|
+
if (
|
|
536
|
+
typeof cacheKey !== 'string' ||
|
|
537
|
+
!cacheKey.includes(
|
|
538
|
+
'FriendingCometFriendRequestsRootQuery',
|
|
539
|
+
)
|
|
540
|
+
)
|
|
541
|
+
continue;
|
|
542
|
+
|
|
543
|
+
const bboxData = streamData[1];
|
|
544
|
+
if (
|
|
545
|
+
!bboxData ||
|
|
546
|
+
!bboxData.__bbox ||
|
|
547
|
+
!bboxData.__bbox.result
|
|
548
|
+
)
|
|
549
|
+
continue;
|
|
550
|
+
|
|
551
|
+
const viewer = bboxData.__bbox.result?.data?.viewer;
|
|
552
|
+
if (!viewer || !viewer.friending_possibilities) continue;
|
|
553
|
+
|
|
554
|
+
const friendingPossibilities =
|
|
555
|
+
viewer.friending_possibilities;
|
|
556
|
+
const requestCount = friendingPossibilities.count || 0;
|
|
557
|
+
|
|
558
|
+
if (
|
|
559
|
+
friendingPossibilities.edges &&
|
|
560
|
+
Array.isArray(friendingPossibilities.edges)
|
|
561
|
+
) {
|
|
562
|
+
for (const edge of friendingPossibilities.edges) {
|
|
563
|
+
if (!edge.node) continue;
|
|
564
|
+
|
|
565
|
+
const node = edge.node;
|
|
566
|
+
requests.push({
|
|
567
|
+
userID: node.id,
|
|
568
|
+
name: node.name,
|
|
569
|
+
profilePicture: node.profile_picture?.uri,
|
|
570
|
+
profileUrl: node.url,
|
|
571
|
+
friendshipStatus: node.friendship_status,
|
|
572
|
+
socialContext: node.social_context?.text,
|
|
573
|
+
mutualFriends:
|
|
574
|
+
node.social_context_top_mutual_friends || [],
|
|
575
|
+
time: edge.time,
|
|
576
|
+
isSeen: edge.is_seen,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Attach count and pagination info
|
|
582
|
+
(requests as any).totalCount = requestCount;
|
|
583
|
+
(requests as any).pageInfo =
|
|
584
|
+
friendingPossibilities.page_info || null;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
235
588
|
}
|
|
589
|
+
}
|
|
590
|
+
} catch (e) {
|
|
591
|
+
continue;
|
|
236
592
|
}
|
|
593
|
+
}
|
|
594
|
+
} catch (error) {
|
|
595
|
+
utils.error('extractFriendRequestsFromHTML', error);
|
|
237
596
|
}
|
|
238
|
-
|
|
239
|
-
|
|
597
|
+
return requests;
|
|
598
|
+
}
|
|
599
|
+
|
|
240
600
|
return friendModule;
|
|
241
|
-
}
|
|
601
|
+
}
|