bb-fca 2.0.6 → 2.0.8
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/a.html +537 -0
- package/a.json +5915 -0
- package/dist/deltas/apis/posting/group.js +607 -0
- package/dist/deltas/apis/posting/group.js.map +1 -0
- package/dist/deltas/apis/posting/post.js +174 -25
- package/dist/deltas/apis/posting/post.js.map +1 -1
- package/dist/deltas/apis/threads/searchGroup.js +159 -0
- package/dist/deltas/apis/threads/searchGroup.js.map +1 -0
- package/dist/deltas/apis/users/searchGroups.js +221 -0
- package/dist/deltas/apis/users/searchGroups.js.map +1 -0
- package/dist/index.d.ts +79 -3
- package/dist/types/deltas/apis/posting/group.d.ts +90 -0
- package/dist/types/deltas/apis/posting/post.d.ts +1 -1
- package/dist/types/deltas/apis/threads/searchGroup.d.ts +15 -0
- package/dist/types/deltas/apis/users/searchGroups.d.ts +17 -0
- package/package.json +1 -1
- package/src/deltas/apis/posting/group.ts +754 -0
- package/src/deltas/apis/posting/post.ts +190 -32
- package/src/types/index.d.ts +79 -3
- package/task.txt +24 -0
- package/LICENSE-MIT +0 -21
- package/examples/post.example.js +0 -189
|
@@ -289,14 +289,25 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/**
|
|
292
|
-
* Gets comments from a Facebook post.
|
|
293
|
-
* @param {string|object} postID - The post
|
|
294
|
-
* @param {string} postID.
|
|
295
|
-
* @param {string} postID.
|
|
292
|
+
* Gets comments from a Facebook post, with pagination support.
|
|
293
|
+
* @param {string|object} postID - The post URL (string) or options object.
|
|
294
|
+
* @param {string} [postID.url] - Direct Facebook post URL.
|
|
295
|
+
* @param {string} [postID.story_fbid] - The story FBID.
|
|
296
|
+
* @param {string} [postID.id] - The account ID.
|
|
297
|
+
* @param {string} [postID.after] - Cursor string to fetch the next page.
|
|
298
|
+
* @param {string} [postID.feedback_id] - Feedback ID required for pagination (returned in page_info).
|
|
296
299
|
* @param {Function} [callback] - Optional callback function.
|
|
297
|
-
* @returns {Promise<Array>}
|
|
300
|
+
* @returns {Promise<{comments: Array, page_info: object}>} Object with comments array and pagination info.
|
|
298
301
|
*/
|
|
299
|
-
async function getPostComments(postID, callback) {
|
|
302
|
+
async function getPostComments(postID, optionsOrCallback?, callback?) {
|
|
303
|
+
// Support: getComments(url), getComments(url, cb), getComments(url, opts), getComments(url, opts, cb)
|
|
304
|
+
let paginationOpts: any = null;
|
|
305
|
+
if (typeof optionsOrCallback === 'function') {
|
|
306
|
+
callback = optionsOrCallback;
|
|
307
|
+
} else if (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) {
|
|
308
|
+
paginationOpts = optionsOrCallback;
|
|
309
|
+
}
|
|
310
|
+
|
|
300
311
|
let resolveFunc: Function = function() {};
|
|
301
312
|
let rejectFunc: Function = function() {};
|
|
302
313
|
|
|
@@ -313,37 +324,161 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
313
324
|
};
|
|
314
325
|
|
|
315
326
|
try {
|
|
316
|
-
|
|
327
|
+
// ── Pagination mode: fetch next page via GraphQL ──────────────────
|
|
328
|
+
// Merge pagination from either postID (object) or second-arg options
|
|
329
|
+
const afterCursor: string | undefined =
|
|
330
|
+
paginationOpts?.after
|
|
331
|
+
|| (typeof postID === 'object' && postID !== null ? postID.after : undefined);
|
|
332
|
+
const feedbackId: string | undefined =
|
|
333
|
+
paginationOpts?.feedback_id
|
|
334
|
+
|| (typeof postID === 'object' && postID !== null ? postID.feedback_id : undefined);
|
|
335
|
+
|
|
336
|
+
if (afterCursor && feedbackId) {
|
|
337
|
+
// Use GraphQL to fetch next page of comments using the cursor
|
|
338
|
+
// feedbackId is the GraphQL ID (base64 of "feedback:<numeric_id>")
|
|
339
|
+
const variables: Record<string, any> = {
|
|
340
|
+
commentsAfterCount: -1,
|
|
341
|
+
commentsAfterCursor: afterCursor,
|
|
342
|
+
commentsBeforeCount: null,
|
|
343
|
+
commentsBeforeCursor: null,
|
|
344
|
+
commentsIntentToken: null,
|
|
345
|
+
feedLocation: 'POST_PERMALINK_DIALOG',
|
|
346
|
+
focusCommentID: null,
|
|
347
|
+
scale: 1,
|
|
348
|
+
useDefaultActor: false,
|
|
349
|
+
id: feedbackId,
|
|
350
|
+
__relay_internal__pv__CometUFICommentAutoTranslationTyperelayprovider: 'ORIGINAL',
|
|
351
|
+
__relay_internal__pv__CometUFICommentAvatarStickerAnimatedImagerelayprovider: false,
|
|
352
|
+
__relay_internal__pv__CometUFICommentActionLinksRewriteEnabledrelayprovider: false,
|
|
353
|
+
__relay_internal__pv__IsWorkUserrelayprovider: false,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const form = {
|
|
357
|
+
av: ctx.userID,
|
|
358
|
+
fb_api_caller_class: 'RelayModern',
|
|
359
|
+
fb_api_req_friendly_name: 'CommentsListComponentsPaginationQuery',
|
|
360
|
+
variables: JSON.stringify(variables),
|
|
361
|
+
doc_id: '35385759421023325',
|
|
362
|
+
server_timestamps: 'true',
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const gqlRes = await defaultFuncs.post(
|
|
366
|
+
'https://www.facebook.com/api/graphql/',
|
|
367
|
+
ctx.jar,
|
|
368
|
+
form,
|
|
369
|
+
ctx.globalOptions,
|
|
370
|
+
ctx,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
let gqlData: any;
|
|
374
|
+
if (typeof gqlRes.body === 'object' && gqlRes.body !== null && !Buffer.isBuffer(gqlRes.body)) {
|
|
375
|
+
gqlData = gqlRes.body;
|
|
376
|
+
} else {
|
|
377
|
+
const body = gqlRes.body.toString();
|
|
378
|
+
try {
|
|
379
|
+
gqlData = JSON.parse(body);
|
|
380
|
+
} catch (e) {
|
|
381
|
+
// Multi-line JSON stream — take first line
|
|
382
|
+
gqlData = JSON.parse(body.split('\n')[0]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Navigate to comments in GraphQL response
|
|
387
|
+
const commentsConnection =
|
|
388
|
+
gqlData?.data?.node?.comment_rendering_instance_for_feed_location
|
|
389
|
+
?.comments ??
|
|
390
|
+
gqlData?.data?.feedback?.comment_rendering_instance_for_feed_location
|
|
391
|
+
?.comments ??
|
|
392
|
+
null;
|
|
393
|
+
|
|
394
|
+
const result: any[] = [];
|
|
395
|
+
let page_info: any = null;
|
|
396
|
+
|
|
397
|
+
if (commentsConnection) {
|
|
398
|
+
page_info = commentsConnection.page_info || null;
|
|
399
|
+
(commentsConnection.edges || []).forEach((edge: any) => {
|
|
400
|
+
if (!edge?.node) return;
|
|
401
|
+
const c: any = {
|
|
402
|
+
id: edge.node.legacy_fbid,
|
|
403
|
+
graphql_id: edge.node.id,
|
|
404
|
+
text: edge.node.body?.text || edge.node.preferred_body?.text || '',
|
|
405
|
+
created_time: edge.node.created_time,
|
|
406
|
+
author: {
|
|
407
|
+
id: edge.node.author?.id,
|
|
408
|
+
name: edge.node.author?.name,
|
|
409
|
+
avatar: edge.node.author?.profile_picture_depth_0?.uri,
|
|
410
|
+
},
|
|
411
|
+
reply_count: edge.node.feedback?.replies_fields?.count || 0,
|
|
412
|
+
total_reply_count: edge.node.feedback?.replies_fields?.total_count || 0,
|
|
413
|
+
depth: edge.node.depth || 0,
|
|
414
|
+
attachments: edge.node.attachments || [],
|
|
415
|
+
};
|
|
416
|
+
if (edge.node.feedback?.replies_connection?.edges) {
|
|
417
|
+
c.replies = edge.node.feedback.replies_connection.edges
|
|
418
|
+
.map((re: any) => {
|
|
419
|
+
if (!re?.node) return null;
|
|
420
|
+
return {
|
|
421
|
+
id: re.node.legacy_fbid,
|
|
422
|
+
text: re.node.body?.text || re.node.preferred_body?.text || '',
|
|
423
|
+
created_time: re.node.created_time,
|
|
424
|
+
author: {
|
|
425
|
+
id: re.node.author?.id,
|
|
426
|
+
name: re.node.author?.name,
|
|
427
|
+
avatar: re.node.author?.profile_picture_depth_0?.uri,
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
})
|
|
431
|
+
.filter((r: any) => r !== null);
|
|
432
|
+
}
|
|
433
|
+
result.push(c);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
callback(null, { comments: result, page_info });
|
|
438
|
+
return returnPromise;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ── Initial load mode: fetch HTML and parse ───────────────────────
|
|
442
|
+
let url: string;
|
|
317
443
|
|
|
318
|
-
// Handle both string and object input
|
|
319
444
|
if (typeof postID === 'string') {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
445
|
+
if (postID.startsWith('https://') || postID.startsWith('http://')) {
|
|
446
|
+
url = postID;
|
|
447
|
+
} else if (postID.startsWith('/')) {
|
|
448
|
+
url = `https://www.facebook.com${postID}`;
|
|
449
|
+
} else if (/^\d+$/.test(postID)) {
|
|
450
|
+
url = `https://www.facebook.com/permalink.php?story_fbid=${postID}&id=${ctx.userID}`;
|
|
451
|
+
} else {
|
|
452
|
+
url = `https://www.facebook.com/permalink.php?story_fbid=${postID}&id=${ctx.userID}`;
|
|
453
|
+
}
|
|
454
|
+
} else if (typeof postID === 'object' && postID !== null) {
|
|
455
|
+
const story_fbid = postID.story_fbid;
|
|
456
|
+
const accountID = postID.id || ctx.userID;
|
|
457
|
+
const postUrl = postID.url;
|
|
458
|
+
|
|
459
|
+
if (postUrl) {
|
|
460
|
+
url = postUrl.startsWith('http')
|
|
461
|
+
? postUrl
|
|
462
|
+
: `https://www.facebook.com${postUrl}`;
|
|
463
|
+
} else if (story_fbid) {
|
|
464
|
+
url = `https://www.facebook.com/permalink.php?story_fbid=${story_fbid}&id=${accountID}`;
|
|
465
|
+
} else {
|
|
466
|
+
throw new Error('Object input must have story_fbid or url field.');
|
|
467
|
+
}
|
|
326
468
|
} else {
|
|
327
469
|
throw new Error(
|
|
328
|
-
'Invalid input: expected string or object with story_fbid
|
|
470
|
+
'Invalid input: expected string (URL or postID) or object with story_fbid/url.',
|
|
329
471
|
);
|
|
330
472
|
}
|
|
331
473
|
|
|
332
|
-
if (!story_fbid) {
|
|
333
|
-
throw new Error('Post ID or story_fbid is required.');
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Construct the permalink URL
|
|
337
|
-
const url = `https://www.facebook.com/permalink.php?story_fbid=${story_fbid}&id=${accountID}`;
|
|
338
|
-
|
|
339
474
|
// Make GET request to fetch the page HTML with cookies
|
|
340
475
|
const resData = await utils.get(url, ctx.jar, {}, ctx.globalOptions, ctx);
|
|
341
476
|
const html = resData.body.toString();
|
|
342
477
|
|
|
343
|
-
// Extract comments
|
|
344
|
-
const
|
|
478
|
+
// Extract comments and page_info from HTML
|
|
479
|
+
const { comments, page_info } = extractCommentsFromHTML(html);
|
|
345
480
|
|
|
346
|
-
callback(null,
|
|
481
|
+
callback(null, { comments, page_info });
|
|
347
482
|
} catch (err) {
|
|
348
483
|
utils.error('getPostComments', err);
|
|
349
484
|
callback(err);
|
|
@@ -370,9 +505,11 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
370
505
|
/**
|
|
371
506
|
* Extract comments from HTML using new parsing logic
|
|
372
507
|
* @private
|
|
508
|
+
* @returns {{ comments: any[], page_info: any | null }}
|
|
373
509
|
*/
|
|
374
|
-
function extractCommentsFromHTML(htmlContent) {
|
|
375
|
-
const comments = [];
|
|
510
|
+
function extractCommentsFromHTML(htmlContent: string): { comments: any[]; page_info: any | null } {
|
|
511
|
+
const comments: any[] = [];
|
|
512
|
+
let page_info: any = null;
|
|
376
513
|
|
|
377
514
|
try {
|
|
378
515
|
// Find all script tags containing JSON data
|
|
@@ -381,7 +518,7 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
381
518
|
);
|
|
382
519
|
|
|
383
520
|
if (!scriptMatches) {
|
|
384
|
-
return comments;
|
|
521
|
+
return { comments, page_info };
|
|
385
522
|
}
|
|
386
523
|
|
|
387
524
|
// Loop through each script tag
|
|
@@ -442,8 +579,29 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
442
579
|
commentData.edges &&
|
|
443
580
|
Array.isArray(commentData.edges)
|
|
444
581
|
) {
|
|
582
|
+
// Extract page_info (cursor pagination info)
|
|
583
|
+
if (commentData.page_info && !page_info) {
|
|
584
|
+
page_info = {
|
|
585
|
+
end_cursor: commentData.page_info.end_cursor || null,
|
|
586
|
+
has_next_page: commentData.page_info.has_next_page || false,
|
|
587
|
+
has_previous_page: commentData.page_info.has_previous_page || false,
|
|
588
|
+
start_cursor: commentData.page_info.start_cursor || null,
|
|
589
|
+
};
|
|
590
|
+
// Also try to capture feedback_id for GraphQL pagination
|
|
591
|
+
try {
|
|
592
|
+
const feedback =
|
|
593
|
+
result.data.node_v2?.comet_sections?.feedback?.story
|
|
594
|
+
?.story_ufi_container?.story?.feedback_context
|
|
595
|
+
?.feedback_target_with_context?.comment_list_renderer
|
|
596
|
+
?.feedback;
|
|
597
|
+
if (feedback?.id && !page_info.feedback_id) {
|
|
598
|
+
page_info.feedback_id = feedback.id;
|
|
599
|
+
}
|
|
600
|
+
} catch (_) {}
|
|
601
|
+
}
|
|
602
|
+
|
|
445
603
|
// Extract comments
|
|
446
|
-
commentData.edges.forEach((edge) => {
|
|
604
|
+
commentData.edges.forEach((edge: any) => {
|
|
447
605
|
if (!edge.node) return;
|
|
448
606
|
|
|
449
607
|
const comment: any = {
|
|
@@ -472,7 +630,7 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
472
630
|
// Get replies if available
|
|
473
631
|
if (edge.node.feedback?.replies_connection?.edges) {
|
|
474
632
|
comment.replies = edge.node.feedback.replies_connection.edges
|
|
475
|
-
.map((replyEdge) => {
|
|
633
|
+
.map((replyEdge: any) => {
|
|
476
634
|
if (!replyEdge.node) return null;
|
|
477
635
|
return {
|
|
478
636
|
id: replyEdge.node.legacy_fbid,
|
|
@@ -490,7 +648,7 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
490
648
|
},
|
|
491
649
|
};
|
|
492
650
|
})
|
|
493
|
-
.filter((r) => r !== null);
|
|
651
|
+
.filter((r: any) => r !== null);
|
|
494
652
|
}
|
|
495
653
|
|
|
496
654
|
comments.push(comment);
|
|
@@ -510,7 +668,7 @@ export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
|
510
668
|
utils.error('extractCommentsFromHTML', error);
|
|
511
669
|
}
|
|
512
670
|
|
|
513
|
-
return comments;
|
|
671
|
+
return { comments, page_info };
|
|
514
672
|
}
|
|
515
673
|
|
|
516
674
|
/**
|
package/src/types/index.d.ts
CHANGED
|
@@ -88,8 +88,30 @@ export interface PostComment {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export interface GetPostCommentsOptions {
|
|
91
|
-
story_fbid
|
|
91
|
+
/** Numeric story_fbid (used with id to construct permalink.php URL) */
|
|
92
|
+
story_fbid?: string;
|
|
93
|
+
/** Facebook user/page ID (used with story_fbid) */
|
|
92
94
|
id?: string;
|
|
95
|
+
/** Direct Facebook post URL (e.g. https://www.facebook.com/user/posts/pfbid...) */
|
|
96
|
+
url?: string;
|
|
97
|
+
/** Cursor for fetching next page of comments (from page_info.end_cursor) */
|
|
98
|
+
after?: string;
|
|
99
|
+
/** GraphQL feedback ID for pagination (from page_info.feedback_id) */
|
|
100
|
+
feedback_id?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface PageInfo {
|
|
104
|
+
end_cursor: string | null;
|
|
105
|
+
has_next_page: boolean;
|
|
106
|
+
has_previous_page?: boolean;
|
|
107
|
+
start_cursor?: string | null;
|
|
108
|
+
/** GraphQL feedback ID needed for pagination queries */
|
|
109
|
+
feedback_id?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface GetPostCommentsResult {
|
|
113
|
+
comments: PostComment[];
|
|
114
|
+
page_info: PageInfo | null;
|
|
93
115
|
}
|
|
94
116
|
|
|
95
117
|
export interface ShareResult {
|
|
@@ -374,6 +396,56 @@ export interface ThreadInfo {
|
|
|
374
396
|
};
|
|
375
397
|
}
|
|
376
398
|
|
|
399
|
+
export interface SearchGroupOptions {
|
|
400
|
+
limit?: number;
|
|
401
|
+
cursor?: string | null;
|
|
402
|
+
locale?: string;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export interface SearchGroupItem {
|
|
406
|
+
groupID: string | null;
|
|
407
|
+
name: string | null;
|
|
408
|
+
url: string | null;
|
|
409
|
+
profileUrl: string | null;
|
|
410
|
+
imageSrc: string | null;
|
|
411
|
+
summary: string | null;
|
|
412
|
+
privacy: string | null;
|
|
413
|
+
memberText: string | null;
|
|
414
|
+
memberCount: number | null;
|
|
415
|
+
joinState: string | null;
|
|
416
|
+
hasMembershipQuestions: boolean | null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface SearchGroupResult {
|
|
420
|
+
groups: SearchGroupItem[];
|
|
421
|
+
cursor: string | null;
|
|
422
|
+
hasNextPage: boolean;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export interface GroupModule {
|
|
426
|
+
join(groupID: string): Promise<any>;
|
|
427
|
+
leave(groupID: string): Promise<any>;
|
|
428
|
+
getJoinedGroups(
|
|
429
|
+
cursor?: string | null,
|
|
430
|
+
count?: number,
|
|
431
|
+
): Promise<{
|
|
432
|
+
groups: Array<{
|
|
433
|
+
id: string;
|
|
434
|
+
name: string;
|
|
435
|
+
imageUri: string;
|
|
436
|
+
url: string;
|
|
437
|
+
memberCount: string;
|
|
438
|
+
privacy: string;
|
|
439
|
+
}>;
|
|
440
|
+
endCursor: string | null;
|
|
441
|
+
hasNextPage: boolean;
|
|
442
|
+
}>;
|
|
443
|
+
searchGroup(
|
|
444
|
+
keyword: string,
|
|
445
|
+
options?: SearchGroupOptions,
|
|
446
|
+
): Promise<SearchGroupResult>;
|
|
447
|
+
}
|
|
448
|
+
|
|
377
449
|
export interface MessageObject {
|
|
378
450
|
body?: string;
|
|
379
451
|
attachment?: ReadStream[];
|
|
@@ -609,8 +681,9 @@ export interface API {
|
|
|
609
681
|
): Promise<DeletePostResult>;
|
|
610
682
|
getComments(
|
|
611
683
|
postID: string | GetPostCommentsOptions,
|
|
612
|
-
|
|
613
|
-
|
|
684
|
+
optionsOrCallback?: GetPostCommentsOptions | Callback<GetPostCommentsResult>,
|
|
685
|
+
callback?: Callback<GetPostCommentsResult>,
|
|
686
|
+
): Promise<GetPostCommentsResult>;
|
|
614
687
|
uploadPhoto(
|
|
615
688
|
photoPath: string,
|
|
616
689
|
callback?: Callback<UploadPhotoResult>,
|
|
@@ -852,6 +925,9 @@ export interface API {
|
|
|
852
925
|
/** Add an external module to extend the API. */
|
|
853
926
|
addExternalModule(moduleObj: Record<string, Function>): void;
|
|
854
927
|
|
|
928
|
+
/** Group-related API methods. */
|
|
929
|
+
group: GroupModule;
|
|
930
|
+
|
|
855
931
|
/** Allow accessing dynamically loaded methods. */
|
|
856
932
|
[key: string]: any;
|
|
857
933
|
}
|
package/task.txt
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
phân tích cách lấy bình luận sau bằng cách gọi API GET /tuyetcollection/posts/pfbid02jnmdLKUc4Trmxrh86Mzsqj1cnh72vrecnjQHWvUVxLTmiUv6VrFRoTCLEUnHu2rgl HTTP/2
|
|
2
|
+
Host: www.facebook.com
|
|
3
|
+
Cookie: datr=eRioaetI9Tgtc9AgD3mzsOn9; ps_l=1; ps_n=1; sb=yxmoaT1mPQz9xGiZp2QIQntV; ar_debug=1; c_user=61588408996667; wd=958x944; presence=C%7B%22t3%22%3A%5B%5D%2C%22utc3%22%3A1773923504664%2C%22v%22%3A1%7D; fr=14IeYIDrQiJPtK2MI.AWcoZQVO9ti_wNuGf5SwJ2YjisY_-NDBa_RECyRt39--PgA1IH0.Bpu-zR..AAA.0.0.Bpu-zR.AWfs1v74VdJlfYmKCJaGYQlSLv8; xs=40%3AKL_HAZmsZj9pwA%3A2%3A1773922444%3A-1%3A-1%3A%3AAczxyRuNYHr1YUWyu5hzhU_ojR4q0GgEo1ZvwAcW4w
|
|
4
|
+
Dpr: 1
|
|
5
|
+
Viewport-Width: 958
|
|
6
|
+
Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"
|
|
7
|
+
Sec-Ch-Ua-Mobile: ?0
|
|
8
|
+
Sec-Ch-Ua-Platform: "Windows"
|
|
9
|
+
Sec-Ch-Ua-Platform-Version: ""
|
|
10
|
+
Sec-Ch-Ua-Model: ""
|
|
11
|
+
Sec-Ch-Ua-Full-Version-List:
|
|
12
|
+
Sec-Ch-Prefers-Color-Scheme: light
|
|
13
|
+
Accept-Language: en-US,en;q=0.9
|
|
14
|
+
Upgrade-Insecure-Requests: 1
|
|
15
|
+
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36
|
|
16
|
+
Accept: 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
|
|
17
|
+
Sec-Fetch-Site: none
|
|
18
|
+
Sec-Fetch-Mode: navigate
|
|
19
|
+
Sec-Fetch-User: ?1
|
|
20
|
+
Sec-Fetch-Dest: document
|
|
21
|
+
Accept-Encoding: gzip, deflate, br
|
|
22
|
+
Priority: u=0, i
|
|
23
|
+
|
|
24
|
+
sau ghi gọi xong được trả về file a.html. lấy bình từ đoạn này "comments":{"created_comment_insertion_position":"TOP","filtering_footer_string":"\u0110\u00e3 ch\u1ecdn ch\u1ebf \u0111\u1ed9 Ph\u00f9 h\u1ee3p nh\u1ea5t n\u00ean m\u1ed9t s\u1ed1 b\u00ecnh lu\u1eadn c\u00f3 th\u1ec3 b\u1ecb l\u1ecdc ra.","count":838,"page_size":10,"total_count":989,"is_not_behind_the_fold":null,"behind_the_fold_subtext":null,"edges":[{"node":{"id":"Y29tbWVudDoxNTAwODE2NjI0NzQ3MDQ5XzEzMDA2MTQ3NTg1NTM1MDk=","feedback":{
|
package/LICENSE-MIT
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2015 Avery, Benjamin, David, Maude
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|
package/examples/post.example.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example usage of post API functions
|
|
3
|
-
* This demonstrates how to create, delete posts, upload photos, and get comments
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const login = require('../module');
|
|
7
|
-
|
|
8
|
-
// Your Facebook credentials
|
|
9
|
-
const credentials = {
|
|
10
|
-
appState: [] // Add your appState here
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
login(credentials, (err, api) => {
|
|
14
|
-
if (err) {
|
|
15
|
-
console.error('Login failed:', err);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
console.log('Logged in successfully!');
|
|
20
|
-
|
|
21
|
-
// Example 1: Create a post
|
|
22
|
-
api.post.create({
|
|
23
|
-
message: 'Hello from bb-fca!',
|
|
24
|
-
privacy: 'SELF' // Options: 'EVERYONE', 'FRIENDS', 'SELF'
|
|
25
|
-
}, (err, result) => {
|
|
26
|
-
if (err) {
|
|
27
|
-
console.error('Error creating post:', err);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
console.log('Post created successfully!');
|
|
32
|
-
console.log('Post ID:', result.postID);
|
|
33
|
-
console.log('Success:', result.success);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Example 2: Upload a photo
|
|
37
|
-
api.post.uploadPhoto('./path/to/your/photo.png', (err, result) => {
|
|
38
|
-
if (err) {
|
|
39
|
-
console.error('Error uploading photo:', err);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
console.log('Photo uploaded successfully!');
|
|
44
|
-
console.log('Photo ID:', result.photoID);
|
|
45
|
-
console.log('Upload ID:', result.uploadID);
|
|
46
|
-
console.log('Success:', result.success);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Example 3: Upload photo with Promise
|
|
50
|
-
api.post.uploadPhoto('./path/to/your/photo.jpg')
|
|
51
|
-
.then(result => {
|
|
52
|
-
console.log('Photo uploaded:', result.photoID);
|
|
53
|
-
})
|
|
54
|
-
.catch(err => {
|
|
55
|
-
console.error('Upload failed:', err);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Example 3.1: Create a post with photo attachments
|
|
59
|
-
// First upload the photo, then create post with photo ID
|
|
60
|
-
api.post.uploadPhoto('./path/to/your/photo.png')
|
|
61
|
-
.then(uploadResult => {
|
|
62
|
-
return api.post.create({
|
|
63
|
-
message: 'Check out this photo!',
|
|
64
|
-
privacy: 'SELF',
|
|
65
|
-
photos: [uploadResult.photoID] // Attach the uploaded photo
|
|
66
|
-
});
|
|
67
|
-
})
|
|
68
|
-
.then(createResult => {
|
|
69
|
-
console.log('Post with photo created successfully!');
|
|
70
|
-
console.log('Post ID:', createResult.postID);
|
|
71
|
-
})
|
|
72
|
-
.catch(err => {
|
|
73
|
-
console.error('Error:', err);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Example 3.2: Create a post with multiple photos
|
|
77
|
-
Promise.all([
|
|
78
|
-
api.post.uploadPhoto('./photo1.jpg'),
|
|
79
|
-
api.post.uploadPhoto('./photo2.jpg'),
|
|
80
|
-
api.post.uploadPhoto('./photo3.jpg')
|
|
81
|
-
])
|
|
82
|
-
.then(results => {
|
|
83
|
-
const photoIDs = results.map(r => r.photoID);
|
|
84
|
-
return api.post.create({
|
|
85
|
-
message: 'Multiple photos!',
|
|
86
|
-
privacy: 'FRIENDS',
|
|
87
|
-
photos: photoIDs
|
|
88
|
-
});
|
|
89
|
-
})
|
|
90
|
-
.then(result => {
|
|
91
|
-
console.log('Post with multiple photos created!');
|
|
92
|
-
console.log('Post ID:', result.postID);
|
|
93
|
-
})
|
|
94
|
-
.catch(err => {
|
|
95
|
-
console.error('Error:', err);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Example 4: Get comments from a post
|
|
99
|
-
// URL: https://www.facebook.com/permalink.php?story_fbid=pfbid02WHHgPgDR9VuDfXiUR5KCseuh9f2NVmfwddGEgUuYKxrkJpFtqfUKbwcCBV7qAJxel&id=61588408996667
|
|
100
|
-
api.post.getComments({
|
|
101
|
-
story_fbid: 'pfbid02WHHgPgDR9VuDfXiUR5KCseuh9f2NVmfwddGEgUuYKxrkJpFtqfUKbwcCBV7qAJxel',
|
|
102
|
-
id: '61588408996667'
|
|
103
|
-
}, (err, comments) => {
|
|
104
|
-
if (err) {
|
|
105
|
-
console.error('Error getting comments:', err);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(`Found ${comments.length} comments:`);
|
|
110
|
-
comments.forEach((comment, index) => {
|
|
111
|
-
console.log(`\n--- Comment ${index + 1} ---`);
|
|
112
|
-
console.log(`ID: ${comment.id}`);
|
|
113
|
-
console.log(`GraphQL ID: ${comment.graphql_id}`);
|
|
114
|
-
console.log(`Text: ${comment.text}`);
|
|
115
|
-
console.log(`Author: ${comment.author.name} (ID: ${comment.author.id})`);
|
|
116
|
-
console.log(`Created: ${new Date(comment.created_time * 1000).toLocaleString('vi-VN')}`);
|
|
117
|
-
console.log(`Replies: ${comment.reply_count}/${comment.total_reply_count}`);
|
|
118
|
-
console.log(`Depth: ${comment.depth}`);
|
|
119
|
-
|
|
120
|
-
if (comment.replies && comment.replies.length > 0) {
|
|
121
|
-
console.log(` Nested replies:`);
|
|
122
|
-
comment.replies.forEach((reply, i) => {
|
|
123
|
-
console.log(` └─ [${i + 1}] ${reply.author.name}: ${reply.text}`);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Example 5: Get comments using just the story_fbid (will use current user's ID)
|
|
130
|
-
api.post.getComments('pfbid02WHHgPgDR9VuDfXiUR5KCseuh9f2NVmfwddGEgUuYKxrkJpFtqfUKbwcCBV7qAJxel')
|
|
131
|
-
.then(comments => {
|
|
132
|
-
console.log(`\nUsing Promise: Found ${comments.length} comments`);
|
|
133
|
-
})
|
|
134
|
-
.catch(err => {
|
|
135
|
-
console.error('Error:', err);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Example 7: Upload a video
|
|
139
|
-
api.post.uploadVideo('./path/to/your/video.mp4', (err, result) => {
|
|
140
|
-
if (err) {
|
|
141
|
-
console.error('Error uploading video:', err);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
console.log('Video uploaded successfully!');
|
|
146
|
-
console.log('Video ID:', result.videoID);
|
|
147
|
-
console.log('Upload ID:', result.uploadID);
|
|
148
|
-
console.log('Upload Session ID:', result.uploadSessionID);
|
|
149
|
-
console.log('Success:', result.success);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Example 8: Upload video with Promise
|
|
153
|
-
api.post.uploadVideo('./path/to/your/video.mp4')
|
|
154
|
-
.then(result => {
|
|
155
|
-
console.log('Video uploaded:', result.videoID);
|
|
156
|
-
})
|
|
157
|
-
.catch(err => {
|
|
158
|
-
console.error('Video upload failed:', err);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Example 9: Create a post with a video attachment
|
|
162
|
-
api.post.uploadVideo('./path/to/your/video.mp4')
|
|
163
|
-
.then(uploadResult => {
|
|
164
|
-
return api.post.create({
|
|
165
|
-
message: 'Check out this video!',
|
|
166
|
-
privacy: 'SELF',
|
|
167
|
-
videos: [uploadResult.videoID]
|
|
168
|
-
});
|
|
169
|
-
})
|
|
170
|
-
.then(createResult => {
|
|
171
|
-
console.log('Post with video created successfully!');
|
|
172
|
-
console.log('Post ID:', createResult.postID);
|
|
173
|
-
})
|
|
174
|
-
.catch(err => {
|
|
175
|
-
console.error('Error:', err);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Example 10: Delete a post
|
|
179
|
-
api.post.delete('post_id_here', (err, result) => {
|
|
180
|
-
if (err) {
|
|
181
|
-
console.error('Error deleting post:', err);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
console.log('Post deleted successfully!');
|
|
186
|
-
console.log('Deleted Post ID:', result.postID);
|
|
187
|
-
console.log('Success:', result.success);
|
|
188
|
-
});
|
|
189
|
-
});
|