bb-fca 2.0.3 → 2.0.4

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 (33) hide show
  1. package/dist/deltas/apis/users/getUserFriends.js +285 -0
  2. package/dist/deltas/apis/users/getUserFriends.js.map +1 -0
  3. package/dist/deltas/apis/users/searchCity.js +91 -0
  4. package/dist/deltas/apis/users/searchCity.js.map +1 -0
  5. package/dist/deltas/apis/users/searchCompany.js +86 -0
  6. package/dist/deltas/apis/users/searchCompany.js.map +1 -0
  7. package/dist/deltas/apis/users/searchJobTitle.js +85 -0
  8. package/dist/deltas/apis/users/searchJobTitle.js.map +1 -0
  9. package/dist/deltas/apis/users/updateCity.js +98 -0
  10. package/dist/deltas/apis/users/updateCity.js.map +1 -0
  11. package/dist/deltas/apis/users/updateHometown.js +97 -0
  12. package/dist/deltas/apis/users/updateHometown.js.map +1 -0
  13. package/dist/deltas/apis/users/updateWorkExperience.js +131 -0
  14. package/dist/deltas/apis/users/updateWorkExperience.js.map +1 -0
  15. package/dist/index.d.ts +115 -0
  16. package/dist/types/deltas/apis/users/getUserFriends.d.ts +30 -0
  17. package/dist/types/deltas/apis/users/searchCity.d.ts +10 -0
  18. package/dist/types/deltas/apis/users/searchCompany.d.ts +10 -0
  19. package/dist/types/deltas/apis/users/searchJobTitle.d.ts +10 -0
  20. package/dist/types/deltas/apis/users/updateCity.d.ts +10 -0
  21. package/dist/types/deltas/apis/users/updateHometown.d.ts +10 -0
  22. package/dist/types/deltas/apis/users/updateWorkExperience.d.ts +30 -0
  23. package/friend.html +534 -0
  24. package/package.json +1 -1
  25. package/proflie.html +527 -0
  26. package/src/deltas/apis/users/getUserFriends.ts +373 -0
  27. package/src/deltas/apis/users/searchCity.ts +106 -0
  28. package/src/deltas/apis/users/searchCompany.ts +100 -0
  29. package/src/deltas/apis/users/searchJobTitle.ts +99 -0
  30. package/src/deltas/apis/users/updateCity.ts +113 -0
  31. package/src/deltas/apis/users/updateHometown.ts +112 -0
  32. package/src/deltas/apis/users/updateWorkExperience.ts +160 -0
  33. package/src/types/index.d.ts +115 -0
@@ -0,0 +1,113 @@
1
+ import utils = require('../../../utils');
2
+
3
+ /**
4
+ * @ChoruOfficial
5
+ * @description A module for updating the current city on a Facebook profile.
6
+ * Calls ProfileCometCurrentCityProfileFieldSaveMutation via /api/graphql/.
7
+ * @param {Object} defaultFuncs The default functions provided by the API wrapper.
8
+ * @param {Object} api The full API object.
9
+ * @param {Object} ctx The context object containing the user's session state.
10
+ * @returns {Function} An async function that updates the account's current city.
11
+ */
12
+ export default function (defaultFuncs: any, api: any, ctx: any) {
13
+ /**
14
+ * Updates the current city on the logged-in user's profile.
15
+ * Use `api.searchCity()` to get the city `fbid` first.
16
+ * @async
17
+ * @param {string} cityId The Facebook ID of the city (from searchCity's `fbid` field).
18
+ * @param {string} [privacy='EVERYONE'] Privacy setting: 'EVERYONE', 'FRIENDS', or 'SELF'.
19
+ * @returns {Promise<any>} A promise that resolves to the API response data on success.
20
+ * @throws {Error} If the cityId is missing or the API request fails.
21
+ *
22
+ * @example
23
+ * const cities = await api.searchCity('vinh');
24
+ * await api.updateCity(cities[0].fbid);
25
+ * await api.updateCity('112963075384670', 'FRIENDS');
26
+ */
27
+ return async function updateCity(
28
+ cityId: string,
29
+ privacy: string = 'EVERYONE',
30
+ ): Promise<any> {
31
+ if (!cityId) {
32
+ throw new Error('cityId is required. Use searchCity() to find the city fbid.');
33
+ }
34
+
35
+ // sectionToken = base64("app_section:<userID>:2327158227")
36
+ const sectionToken = Buffer.from(
37
+ `app_section:${ctx.userID}:2327158227`,
38
+ ).toString('base64');
39
+
40
+ const variables = {
41
+ collectionToken: 'UNKNOWN',
42
+ input: {
43
+ current_city_id: cityId,
44
+ life_event_publish_type: 'SUPPRESS_ALL',
45
+ logging_data: {
46
+ nav_chain: `ProfileCometAboutTabRoot.react,comet.profile.collection.directory_personal_details,unexpected,${Date.now()},0,,,`,
47
+ },
48
+ privacy: {
49
+ allow: [],
50
+ base_state: privacy,
51
+ deny: [],
52
+ tag_expansion_state: 'UNSPECIFIED',
53
+ },
54
+ actor_id: ctx.userID,
55
+ client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
56
+ },
57
+ scale: 1,
58
+ sectionToken: sectionToken,
59
+ profileID: ctx.userID,
60
+ useDefaultActor: false,
61
+ };
62
+
63
+ const form = {
64
+ av: ctx.userID,
65
+ __user: ctx.userID,
66
+ __a: '1',
67
+ fb_dtsg: ctx.fb_dtsg,
68
+ jazoest: ctx.jazoest,
69
+ lsd: ctx.lsd,
70
+ fb_api_caller_class: 'RelayModern',
71
+ fb_api_req_friendly_name:
72
+ 'ProfileCometCurrentCityProfileFieldSaveMutation',
73
+ variables: JSON.stringify(variables),
74
+ server_timestamps: 'true',
75
+ doc_id: '24052423611121998',
76
+ };
77
+
78
+ const customHeader = {
79
+ 'x-fb-friendly-name':
80
+ 'ProfileCometCurrentCityProfileFieldSaveMutation',
81
+ 'x-fb-lsd': ctx.lsd,
82
+ 'x-asbd-id': '359341',
83
+ origin: 'https://www.facebook.com',
84
+ referer: `https://www.facebook.com/profile.php?id=${ctx.userID}&sk=directory_personal_details`,
85
+ };
86
+
87
+ const resData = await utils.post(
88
+ 'https://www.facebook.com/api/graphql/',
89
+ ctx.jar,
90
+ form,
91
+ ctx.globalOptions,
92
+ ctx,
93
+ customHeader,
94
+ );
95
+
96
+ // Handle both parsed object and string/Buffer response
97
+ let data: any;
98
+ if (typeof resData.body === 'object' && resData.body !== null && !Buffer.isBuffer(resData.body)) {
99
+ data = resData.body;
100
+ } else {
101
+ const body = typeof resData.body === 'string' ? resData.body : resData.body.toString();
102
+ try {
103
+ data = JSON.parse(body);
104
+ } catch {
105
+ const lines = body.split('\n').filter(Boolean);
106
+ data = JSON.parse(lines[0]);
107
+ }
108
+ }
109
+
110
+ if (data.errors) throw new Error(JSON.stringify(data.errors));
111
+ return data.data || data;
112
+ };
113
+ }
@@ -0,0 +1,112 @@
1
+ import utils = require('../../../utils');
2
+
3
+ /**
4
+ * @ChoruOfficial
5
+ * @description A module for updating the hometown on a Facebook profile.
6
+ * Calls ProfileCometHometownProfileFieldSaveMutation via /api/graphql/.
7
+ * @param {Object} defaultFuncs The default functions provided by the API wrapper.
8
+ * @param {Object} api The full API object.
9
+ * @param {Object} ctx The context object containing the user's session state.
10
+ * @returns {Function} An async function that updates the account's hometown.
11
+ */
12
+ export default function (defaultFuncs: any, api: any, ctx: any) {
13
+ /**
14
+ * Updates the hometown on the logged-in user's profile.
15
+ * Use `api.searchCity(query, 'HOMETOWN')` to get the city `fbid` first.
16
+ * @async
17
+ * @param {string} cityId The Facebook ID of the city (from searchCity's `fbid` field).
18
+ * @param {string} [privacy='EVERYONE'] Privacy setting: 'EVERYONE', 'FRIENDS', or 'SELF'.
19
+ * @returns {Promise<any>} A promise that resolves to the API response data on success.
20
+ * @throws {Error} If the cityId is missing or the API request fails.
21
+ *
22
+ * @example
23
+ * const cities = await api.searchCity('ha noi', 'HOMETOWN');
24
+ * await api.updateHometown(cities[0].fbid);
25
+ * await api.updateHometown('106388046062960', 'FRIENDS');
26
+ */
27
+ return async function updateHometown(
28
+ cityId: string,
29
+ privacy: string = 'EVERYONE',
30
+ ): Promise<any> {
31
+ if (!cityId) {
32
+ throw new Error('cityId is required. Use searchCity(query, "HOMETOWN") to find the city fbid.');
33
+ }
34
+
35
+ // sectionToken = base64("app_section:<userID>:2327158227")
36
+ const sectionToken = Buffer.from(
37
+ `app_section:${ctx.userID}:2327158227`,
38
+ ).toString('base64');
39
+
40
+ const variables = {
41
+ collectionToken: 'UNKNOWN',
42
+ input: {
43
+ hometown_city_id: cityId,
44
+ life_event_publish_type: 'SUPPRESS_ALL',
45
+ logging_data: {
46
+ nav_chain: `ProfileCometAboutTabRoot.react,comet.profile.collection.directory_personal_details,via_cold_start,${Date.now()},0,,,`,
47
+ },
48
+ privacy: {
49
+ allow: [],
50
+ base_state: privacy,
51
+ deny: [],
52
+ tag_expansion_state: 'UNSPECIFIED',
53
+ },
54
+ actor_id: ctx.userID,
55
+ client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
56
+ },
57
+ scale: 1,
58
+ sectionToken: sectionToken,
59
+ useDefaultActor: false,
60
+ };
61
+
62
+ const form = {
63
+ av: ctx.userID,
64
+ __user: ctx.userID,
65
+ __a: '1',
66
+ fb_dtsg: ctx.fb_dtsg,
67
+ jazoest: ctx.jazoest,
68
+ lsd: ctx.lsd,
69
+ fb_api_caller_class: 'RelayModern',
70
+ fb_api_req_friendly_name:
71
+ 'ProfileCometHometownProfileFieldSaveMutation',
72
+ variables: JSON.stringify(variables),
73
+ server_timestamps: 'true',
74
+ doc_id: '26631736056430441',
75
+ };
76
+
77
+ const customHeader = {
78
+ 'x-fb-friendly-name':
79
+ 'ProfileCometHometownProfileFieldSaveMutation',
80
+ 'x-fb-lsd': ctx.lsd,
81
+ 'x-asbd-id': '359341',
82
+ origin: 'https://www.facebook.com',
83
+ referer: `https://www.facebook.com/profile.php?id=${ctx.userID}&sk=directory_personal_details`,
84
+ };
85
+
86
+ const resData = await utils.post(
87
+ 'https://www.facebook.com/api/graphql/',
88
+ ctx.jar,
89
+ form,
90
+ ctx.globalOptions,
91
+ ctx,
92
+ customHeader,
93
+ );
94
+
95
+ // Handle both parsed object and string/Buffer response
96
+ let data: any;
97
+ if (typeof resData.body === 'object' && resData.body !== null && !Buffer.isBuffer(resData.body)) {
98
+ data = resData.body;
99
+ } else {
100
+ const body = typeof resData.body === 'string' ? resData.body : resData.body.toString();
101
+ try {
102
+ data = JSON.parse(body);
103
+ } catch {
104
+ const lines = body.split('\n').filter(Boolean);
105
+ data = JSON.parse(lines[0]);
106
+ }
107
+ }
108
+
109
+ if (data.errors) throw new Error(JSON.stringify(data.errors));
110
+ return data.data || data;
111
+ };
112
+ }
@@ -0,0 +1,160 @@
1
+ import utils = require('../../../utils');
2
+
3
+ /**
4
+ * @ChoruOfficial
5
+ * @description A module for adding/updating work experience on a Facebook profile.
6
+ * Calls ProfileCometWorkExperienceSaveMutation via /api/graphql/.
7
+ * @param {Object} defaultFuncs The default functions provided by the API wrapper.
8
+ * @param {Object} api The full API object.
9
+ * @param {Object} ctx The context object containing the user's session state.
10
+ * @returns {Function} An async function that updates work experience.
11
+ */
12
+ export default function updateWorkExperienceModule(defaultFuncs: any, api: any, ctx: any) {
13
+ /**
14
+ * Adds or updates a work experience entry on the profile.
15
+ * Use `api.searchCompany()` and `api.searchJobTitle()` to get IDs first.
16
+ * @async
17
+ * @param {Object} options Work experience details.
18
+ * @param {string} options.companyName The company/employer name.
19
+ * @param {string} [options.companyId] The company fbid from searchCompany (optional, UUID generated if empty).
20
+ * @param {string} [options.positionName] The job title/position name.
21
+ * @param {string} [options.positionId] The job title fbid from searchJobTitle (optional, UUID generated if empty).
22
+ * @param {boolean} [options.isCurrent=true] Whether this is the current job.
23
+ * @param {Object} [options.startDate] Start date: { year, month?, day? }.
24
+ * @param {Object} [options.endDate] End date: { year?, month?, day? } (if not current).
25
+ * @param {string} [options.description] Job description text.
26
+ * @param {string} [options.locationId] City fbid for work location (from searchCity).
27
+ * @param {string} [options.privacy='EVERYONE'] Privacy: 'EVERYONE', 'FRIENDS', or 'SELF'.
28
+ * @param {string} [options.workExperienceID] Existing work experience ID to update (null for new).
29
+ * @returns {Promise<any>} A promise that resolves to the API response data.
30
+ * @throws {Error} If companyName is missing or the API request fails.
31
+ *
32
+ * @example
33
+ * // Add new work experience
34
+ * await api.updateWorkExperience({
35
+ * companyName: 'Google',
36
+ * companyId: '104958162837', // from searchCompany
37
+ * positionName: 'Software Engineer',
38
+ * positionId: '106154556093103', // from searchJobTitle
39
+ * isCurrent: true,
40
+ * startDate: { year: 2024 },
41
+ * });
42
+ */
43
+ return async function updateWorkExperience(
44
+ options: {
45
+ companyName: string;
46
+ companyId?: string;
47
+ positionName?: string;
48
+ positionId?: string;
49
+ isCurrent?: boolean;
50
+ startDate?: { year?: number; month?: number; day?: number };
51
+ endDate?: { year?: number; month?: number; day?: number };
52
+ description?: string;
53
+ locationId?: string;
54
+ privacy?: string;
55
+ workExperienceID?: string | null;
56
+ },
57
+ ): Promise<any> {
58
+ if (!options.companyName) {
59
+ throw new Error('companyName is required.');
60
+ }
61
+
62
+ const privacy = options.privacy || 'EVERYONE';
63
+
64
+ // Generate a UUID-like ID if not provided
65
+ const generateUUID = () =>
66
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
67
+ const r = (Math.random() * 16) | 0;
68
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
69
+ return v.toString(16);
70
+ });
71
+
72
+ // sectionToken = base64("app_section:<userID>:2327158227")
73
+ const sectionToken = Buffer.from(
74
+ `app_section:${ctx.userID}:2327158227`,
75
+ ).toString('base64');
76
+
77
+ const variables: any = {
78
+ collectionToken: 'UNKNOWN',
79
+ input: {
80
+ description: options.description || '',
81
+ employer_id: options.companyId || generateUUID(),
82
+ employer_name: options.companyName,
83
+ end_date: options.endDate || {},
84
+ is_current: options.isCurrent !== undefined ? options.isCurrent : true,
85
+ location_id: options.locationId || '',
86
+ logging_data: {
87
+ nav_chain: `ProfileCometAboutTabRoot.react,comet.profile.collection.directory_work,unexpected,${Date.now()},0,,,`,
88
+ },
89
+ mutation_surface: 'PROFILE',
90
+ position_id: options.positionId || generateUUID(),
91
+ position_name: options.positionName || '',
92
+ privacy: {
93
+ allow: [],
94
+ base_state: privacy,
95
+ deny: [],
96
+ tag_expansion_state: 'UNSPECIFIED',
97
+ },
98
+ start_date: options.startDate || {},
99
+ actor_id: ctx.userID,
100
+ client_mutation_id: Math.floor(Math.random() * 10 + 1).toString(),
101
+ },
102
+ scale: 1,
103
+ sectionToken: sectionToken,
104
+ profileID: ctx.userID,
105
+ workExperienceID: options.workExperienceID || null,
106
+ useDefaultActor: false,
107
+ isProfileDirectory: true,
108
+ shouldFetchPostClick: false,
109
+ };
110
+
111
+ const form = {
112
+ av: ctx.userID,
113
+ __user: ctx.userID,
114
+ __a: '1',
115
+ fb_dtsg: ctx.fb_dtsg,
116
+ jazoest: ctx.jazoest,
117
+ lsd: ctx.lsd,
118
+ fb_api_caller_class: 'RelayModern',
119
+ fb_api_req_friendly_name:
120
+ 'ProfileCometWorkExperienceSaveMutation',
121
+ variables: JSON.stringify(variables),
122
+ server_timestamps: 'true',
123
+ doc_id: '25132001056475784',
124
+ };
125
+
126
+ const customHeader = {
127
+ 'x-fb-friendly-name':
128
+ 'ProfileCometWorkExperienceSaveMutation',
129
+ 'x-fb-lsd': ctx.lsd,
130
+ 'x-asbd-id': '359341',
131
+ origin: 'https://www.facebook.com',
132
+ referer: `https://www.facebook.com/profile.php?id=${ctx.userID}&sk=directory_work`,
133
+ };
134
+
135
+ const resData = await utils.post(
136
+ 'https://www.facebook.com/api/graphql/',
137
+ ctx.jar,
138
+ form,
139
+ ctx.globalOptions,
140
+ ctx,
141
+ customHeader,
142
+ );
143
+
144
+ let data: any;
145
+ if (typeof resData.body === 'object' && resData.body !== null && !Buffer.isBuffer(resData.body)) {
146
+ data = resData.body;
147
+ } else {
148
+ const body = typeof resData.body === 'string' ? resData.body : resData.body.toString();
149
+ try {
150
+ data = JSON.parse(body);
151
+ } catch {
152
+ const lines = body.split('\n').filter(Boolean);
153
+ data = JSON.parse(lines[0]);
154
+ }
155
+ }
156
+
157
+ if (data.errors) throw new Error(JSON.stringify(data.errors));
158
+ return data.data || data;
159
+ };
160
+ }
@@ -699,6 +699,121 @@ export interface API {
699
699
 
700
700
  // ── Session ────────────────────────────────────────────────────
701
701
 
702
+ /**
703
+ * Searches for cities matching the given query string.
704
+ * Uses Facebook's Directory Typeahead data source.
705
+ * @param query The search query (city name or partial name).
706
+ * @param searchCategory The category to search (default: 'CURRENT_CITY').
707
+ */
708
+ searchCity(
709
+ query: string,
710
+ searchCategory?: string,
711
+ ): Promise<Array<{
712
+ fbid: string;
713
+ title: string;
714
+ value: string;
715
+ photoUri: string;
716
+ subtitle: string;
717
+ secondSubtitle: string;
718
+ }>>;
719
+
720
+ /**
721
+ * Searches for companies/workplaces matching the given query string.
722
+ * Uses Facebook's Directory Typeahead data source with WORKPLACE category.
723
+ * @param query The search query (company name or partial name).
724
+ */
725
+ searchCompany(
726
+ query: string,
727
+ ): Promise<Array<{
728
+ fbid: string;
729
+ title: string;
730
+ value: string;
731
+ photoUri: string;
732
+ subtitle: string;
733
+ secondSubtitle: string;
734
+ }>>;
735
+
736
+ /**
737
+ * Searches for job titles/positions matching the given query string.
738
+ * Uses Facebook's Directory Typeahead data source with JOB_TITLE category.
739
+ * @param query The search query (job title or partial name).
740
+ */
741
+ searchJobTitle(
742
+ query: string,
743
+ ): Promise<Array<{
744
+ fbid: string;
745
+ title: string;
746
+ value: string;
747
+ photoUri: string;
748
+ subtitle: string;
749
+ secondSubtitle: string;
750
+ }>>;
751
+
752
+ /**
753
+ * Fetches the friend list of a given user with pagination support.
754
+ * @param userID The Facebook user ID whose friends to fetch.
755
+ * @param count Number of friends per page (default: 8).
756
+ * @param cursor Pagination cursor from previous response's endCursor (default: null).
757
+ */
758
+ getUserFriends(
759
+ userID: string,
760
+ count?: number,
761
+ cursor?: string | null,
762
+ ): Promise<{
763
+ friends: Array<{
764
+ id: string;
765
+ name: string;
766
+ avatarUri: string;
767
+ profileUrl: string;
768
+ mutualFriendsText: string;
769
+ friendshipStatus: string;
770
+ gender: string;
771
+ }>;
772
+ endCursor: string | null;
773
+ hasNextPage: boolean;
774
+ }>;
775
+
776
+ /**
777
+ * Adds or updates a work experience entry on the profile.
778
+ * Use searchCompany() and searchJobTitle() to get IDs first.
779
+ * @param options Work experience details.
780
+ */
781
+ updateWorkExperience(options: {
782
+ companyName: string;
783
+ companyId?: string;
784
+ positionName?: string;
785
+ positionId?: string;
786
+ isCurrent?: boolean;
787
+ startDate?: { year?: number; month?: number; day?: number };
788
+ endDate?: { year?: number; month?: number; day?: number };
789
+ description?: string;
790
+ locationId?: string;
791
+ privacy?: string;
792
+ workExperienceID?: string | null;
793
+ }): Promise<any>;
794
+
795
+ /**
796
+ * Updates the current city on the profile.
797
+ * Use searchCity() to get the city fbid first.
798
+ * @param cityId The Facebook ID of the city (from searchCity's fbid field).
799
+ * @param privacy Privacy setting: 'EVERYONE', 'FRIENDS', or 'SELF' (default: 'EVERYONE').
800
+ */
801
+ updateCity(
802
+ cityId: string,
803
+ privacy?: string,
804
+ ): Promise<any>;
805
+
806
+ /**
807
+ * Updates the hometown on the profile.
808
+ * Use searchCity() to get the city fbid first.
809
+ * @param cityId The Facebook ID of the city (from searchCity's fbid field).
810
+ * @param privacy Privacy setting: 'EVERYONE', 'FRIENDS', or 'SELF' (default: 'EVERYONE').
811
+ */
812
+ updateHometown(
813
+ cityId: string,
814
+ privacy?: string,
815
+ ): Promise<any>;
816
+
702
817
  /**
703
818
  * Changes the Facebook account display name (two-step: preview then confirm).
704
819
  * Calls AccountsCenter GraphQL API.