bb-fca 2.0.10 → 2.0.12
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/messaging/sendPageMessage.js +197 -0
- package/dist/deltas/apis/messaging/sendPageMessage.js.map +1 -0
- package/dist/deltas/apis/posting/group.js +407 -0
- package/dist/deltas/apis/posting/group.js.map +1 -1
- package/dist/deltas/apis/threads/getPageThreadList.js +207 -0
- package/dist/deltas/apis/threads/getPageThreadList.js.map +1 -0
- package/dist/deltas/apis/users/getManagedPages.js +84 -0
- package/dist/deltas/apis/users/getManagedPages.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/types/deltas/apis/messaging/sendPageMessage.d.ts +1 -0
- package/dist/types/deltas/apis/posting/group.d.ts +88 -0
- package/dist/types/deltas/apis/threads/getPageThreadList.d.ts +7 -0
- package/dist/types/deltas/apis/users/getManagedPages.d.ts +1 -0
- package/dist/utils/clients.js +2 -1
- package/dist/utils/clients.js.map +1 -1
- package/dist/utils/index.js +4 -4
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/deltas/apis/messaging/sendPageMessage.ts +246 -0
- package/src/deltas/apis/posting/group.ts +480 -0
- package/src/deltas/apis/threads/getPageThreadList.ts +239 -0
- package/src/deltas/apis/users/getManagedPages.ts +94 -0
- package/src/types/index.d.ts +26 -0
- package/src/utils/clients.ts +2 -1
- package/src/utils/index.ts +4 -4
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// @ChoruOfficial
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
import utils = require('../../../utils');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Formats an event reminder object from a GraphQL response.
|
|
8
|
+
* @param {Object} reminder The raw event reminder object.
|
|
9
|
+
* @returns {Object} A formatted event reminder object.
|
|
10
|
+
*/
|
|
11
|
+
function formatEventReminders(reminder) {
|
|
12
|
+
return {
|
|
13
|
+
reminderID: reminder.id,
|
|
14
|
+
eventCreatorID: reminder.lightweight_event_creator.id,
|
|
15
|
+
time: reminder.time,
|
|
16
|
+
eventType: reminder.lightweight_event_type.toLowerCase(),
|
|
17
|
+
locationName: reminder.location_name,
|
|
18
|
+
locationCoordinates: reminder.location_coordinates,
|
|
19
|
+
locationPage: reminder.location_page,
|
|
20
|
+
eventStatus: reminder.lightweight_event_status.toLowerCase(),
|
|
21
|
+
note: reminder.note,
|
|
22
|
+
repeatMode: reminder.repeat_mode.toLowerCase(),
|
|
23
|
+
eventTitle: reminder.event_title,
|
|
24
|
+
triggerMessage: reminder.trigger_message,
|
|
25
|
+
secondsToNotifyBefore: reminder.seconds_to_notify_before,
|
|
26
|
+
allowsRsvp: reminder.allows_rsvp,
|
|
27
|
+
relatedEvent: reminder.related_event,
|
|
28
|
+
members: reminder.event_reminder_members.edges.map(function(member) {
|
|
29
|
+
return {
|
|
30
|
+
memberID: member.node.id,
|
|
31
|
+
state: member.guest_list_state.toLowerCase(),
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Formats a thread object from a GraphQL response.
|
|
39
|
+
* @param {Object} messageThread The raw message_thread object from GraphQL.
|
|
40
|
+
* @returns {Object | null} A formatted thread object or null if data is invalid.
|
|
41
|
+
*/
|
|
42
|
+
function formatThreadGraphQLResponse(messageThread) {
|
|
43
|
+
if (!messageThread) {
|
|
44
|
+
console.log('[getPageThreadList] messageThread is undefined!');
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!messageThread.thread_key) {
|
|
48
|
+
console.log(
|
|
49
|
+
'[getPageThreadList] thread_key missing for thread:',
|
|
50
|
+
messageThread.id,
|
|
51
|
+
);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const threadID = messageThread.thread_key.thread_fbid
|
|
56
|
+
? messageThread.thread_key.thread_fbid
|
|
57
|
+
: messageThread.thread_key.other_user_id;
|
|
58
|
+
|
|
59
|
+
const lastM = messageThread.last_message;
|
|
60
|
+
const snippetID =
|
|
61
|
+
lastM?.nodes?.[0]?.message_sender?.messaging_actor?.id || null;
|
|
62
|
+
const snippetText = lastM?.nodes?.[0]?.snippet || null;
|
|
63
|
+
const lastR = messageThread.last_read_receipt;
|
|
64
|
+
const lastReadTimestamp = lastR?.nodes?.[0]?.timestamp_precise || null;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
threadID: threadID,
|
|
68
|
+
threadName: messageThread.name,
|
|
69
|
+
participantIDs:
|
|
70
|
+
messageThread.all_participants?.edges?.map(
|
|
71
|
+
(d) => d?.node?.messaging_actor?.id,
|
|
72
|
+
) || [],
|
|
73
|
+
userInfo:
|
|
74
|
+
messageThread.all_participants?.edges?.map((d) => ({
|
|
75
|
+
id: d?.node?.messaging_actor?.id,
|
|
76
|
+
name: d?.node?.messaging_actor?.name,
|
|
77
|
+
firstName: d?.node?.messaging_actor?.short_name,
|
|
78
|
+
vanity: d?.node?.messaging_actor?.username,
|
|
79
|
+
url: d?.node?.messaging_actor?.url,
|
|
80
|
+
thumbSrc: d?.node?.messaging_actor?.big_image_src?.uri,
|
|
81
|
+
profileUrl: d?.node?.messaging_actor?.big_image_src?.uri,
|
|
82
|
+
gender: d?.node?.messaging_actor?.gender,
|
|
83
|
+
type: d?.node?.messaging_actor?.__typename,
|
|
84
|
+
isFriend: d?.node?.messaging_actor?.is_viewer_friend,
|
|
85
|
+
isBirthday: !!d?.node?.messaging_actor?.is_birthday,
|
|
86
|
+
})) || [],
|
|
87
|
+
unreadCount: messageThread.unread_count,
|
|
88
|
+
messageCount: messageThread.messages_count,
|
|
89
|
+
timestamp: messageThread.updated_time_precise,
|
|
90
|
+
muteUntil: messageThread.mute_until,
|
|
91
|
+
isGroup: messageThread.thread_type == 'GROUP',
|
|
92
|
+
isSubscribed: messageThread.is_viewer_subscribed,
|
|
93
|
+
isArchived: messageThread.has_viewer_archived,
|
|
94
|
+
folder: messageThread.folder,
|
|
95
|
+
cannotReplyReason: messageThread.cannot_reply_reason,
|
|
96
|
+
eventReminders: messageThread.event_reminders?.nodes
|
|
97
|
+
? messageThread.event_reminders.nodes.map(formatEventReminders)
|
|
98
|
+
: null,
|
|
99
|
+
emoji: messageThread.customization_info?.emoji || null,
|
|
100
|
+
color: messageThread.customization_info?.outgoing_bubble_color
|
|
101
|
+
? messageThread.customization_info.outgoing_bubble_color.slice(2)
|
|
102
|
+
: null,
|
|
103
|
+
threadTheme: messageThread.thread_theme,
|
|
104
|
+
nicknames: messageThread.customization_info?.participant_customizations
|
|
105
|
+
? messageThread.customization_info.participant_customizations.reduce(
|
|
106
|
+
(res, val) => {
|
|
107
|
+
if (val.nickname) res[val.participant_id] = val.nickname;
|
|
108
|
+
return res;
|
|
109
|
+
},
|
|
110
|
+
{},
|
|
111
|
+
)
|
|
112
|
+
: {},
|
|
113
|
+
adminIDs: messageThread.thread_admins?.map((a) => a.id) || [],
|
|
114
|
+
approvalMode: Boolean(messageThread.approval_mode),
|
|
115
|
+
approvalQueue:
|
|
116
|
+
messageThread.group_approval_queue?.nodes?.map((a) => ({
|
|
117
|
+
inviterID: a.inviter.id,
|
|
118
|
+
requesterID: a.requester.id,
|
|
119
|
+
timestamp: a.request_timestamp,
|
|
120
|
+
request_source: a.request_source,
|
|
121
|
+
})) || [],
|
|
122
|
+
reactionsMuteMode: messageThread.reactions_mute_mode?.toLowerCase() || null,
|
|
123
|
+
mentionsMuteMode: messageThread.mentions_mute_mode?.toLowerCase() || null,
|
|
124
|
+
isPinProtected: messageThread.is_pin_protected,
|
|
125
|
+
relatedPageThread: messageThread.related_page_thread,
|
|
126
|
+
name: messageThread.name,
|
|
127
|
+
snippet: snippetText,
|
|
128
|
+
snippetSender: snippetID,
|
|
129
|
+
snippetAttachments: [],
|
|
130
|
+
serverTimestamp: messageThread.updated_time_precise,
|
|
131
|
+
imageSrc: messageThread.image?.uri || null,
|
|
132
|
+
isCanonicalUser: messageThread.is_canonical_neo_user,
|
|
133
|
+
isCanonical: messageThread.thread_type != 'GROUP',
|
|
134
|
+
recipientsLoadable: true,
|
|
135
|
+
hasEmailParticipant: false,
|
|
136
|
+
readOnly: false,
|
|
137
|
+
canReply: messageThread.cannot_reply_reason == null,
|
|
138
|
+
lastMessageTimestamp: messageThread.last_message?.timestamp_precise || null,
|
|
139
|
+
lastMessageType: 'message',
|
|
140
|
+
lastReadTimestamp: lastReadTimestamp,
|
|
141
|
+
threadType: messageThread.thread_type == 'GROUP' ? 2 : 1,
|
|
142
|
+
inviteLink: {
|
|
143
|
+
enable: messageThread.joinable_mode?.mode == 1,
|
|
144
|
+
link: messageThread.joinable_mode?.link || null,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param {Object} defaultFuncs
|
|
151
|
+
* @param {Object} api
|
|
152
|
+
* @param {Object} ctx
|
|
153
|
+
* @returns {function(pageID: string, limit: number, tags: string[]): Promise<Array<Object>>}
|
|
154
|
+
*/
|
|
155
|
+
export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
156
|
+
/**
|
|
157
|
+
* Retrieves a list of current message threads for a managed Page.
|
|
158
|
+
* @param {string} pageID - The ID of the page.
|
|
159
|
+
* @param {number} limit - The number of threads to retrieve.
|
|
160
|
+
* @param {string[]} tags - An array of tags to filter threads by (e.g., ["INBOX", "ARCHIVED"]).
|
|
161
|
+
* @returns {Promise<Object[]>} A promise that resolves with an array of formatted thread objects.
|
|
162
|
+
*/
|
|
163
|
+
return async function getPageThreadList(
|
|
164
|
+
pageID: string,
|
|
165
|
+
limit: number = 20,
|
|
166
|
+
tags: string[] = ['INBOX'],
|
|
167
|
+
) {
|
|
168
|
+
if (!pageID) {
|
|
169
|
+
throw new Error('getPageThreadList: pageID is required.');
|
|
170
|
+
}
|
|
171
|
+
if (
|
|
172
|
+
utils.getType(limit) !== 'Number' ||
|
|
173
|
+
!Number.isInteger(limit) ||
|
|
174
|
+
limit <= 0
|
|
175
|
+
) {
|
|
176
|
+
throw new Error('getPageThreadList: limit must be a positive integer.');
|
|
177
|
+
}
|
|
178
|
+
if (utils.getType(tags) === 'String') {
|
|
179
|
+
tags = [tags] as any;
|
|
180
|
+
}
|
|
181
|
+
if (utils.getType(tags) !== 'Array') {
|
|
182
|
+
throw new Error('getPageThreadList: tags must be an array.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const variables = {
|
|
186
|
+
limit: limit,
|
|
187
|
+
tags: tags,
|
|
188
|
+
isWorkUser: false,
|
|
189
|
+
includeDeliveryReceipts: true,
|
|
190
|
+
includeSeqID: false,
|
|
191
|
+
is_work_teamwork_not_putting_muted_in_unreads: false,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const form = {
|
|
195
|
+
av: pageID,
|
|
196
|
+
__user: ctx.userID,
|
|
197
|
+
__a: 1,
|
|
198
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
199
|
+
jazoest: ctx.jazoest,
|
|
200
|
+
fb_api_caller_class: 'RelayModern',
|
|
201
|
+
fb_api_req_friendly_name: 'MessengerGraphQLThreadlistFetcher',
|
|
202
|
+
variables: JSON.stringify(variables),
|
|
203
|
+
doc_id: '3566388080113165',
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const resp = await utils
|
|
208
|
+
.post(
|
|
209
|
+
'https://www.facebook.com/api/graphql/',
|
|
210
|
+
ctx.jar,
|
|
211
|
+
form,
|
|
212
|
+
ctx.globalOptions,
|
|
213
|
+
ctx,
|
|
214
|
+
)
|
|
215
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
216
|
+
|
|
217
|
+
if (resp.errors) {
|
|
218
|
+
throw new Error(JSON.stringify(resp.errors));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let nodes = resp?.data?.viewer?.message_threads?.nodes || [];
|
|
222
|
+
|
|
223
|
+
const formatted = nodes
|
|
224
|
+
.map((node, index) => {
|
|
225
|
+
try {
|
|
226
|
+
return formatThreadGraphQLResponse(node);
|
|
227
|
+
} catch (e) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
.filter(Boolean);
|
|
232
|
+
|
|
233
|
+
return formatted;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
utils.error('getPageThreadList', err);
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @ChoruOfficial
|
|
2
|
+
import utils = require('../../../utils');
|
|
3
|
+
import deepdash from 'deepdash';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
deepdash(_);
|
|
6
|
+
|
|
7
|
+
export default function(defaultFuncs: any, api: any, ctx: any) {
|
|
8
|
+
return function getManagedPages(callback?: (err: any, data?: any) => void) {
|
|
9
|
+
let cb: any;
|
|
10
|
+
const returnPromise = new Promise<any>((resolve, reject) => {
|
|
11
|
+
cb = (err: any, resData: any) => {
|
|
12
|
+
if (callback) callback(err, resData);
|
|
13
|
+
if (err) return reject(err);
|
|
14
|
+
resolve(resData);
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const fetchPages = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const url = `https://www.facebook.com/pages/?category=your_pages&ref=bookmarks`;
|
|
21
|
+
const allJsonData = await utils.json(
|
|
22
|
+
url,
|
|
23
|
+
ctx.jar,
|
|
24
|
+
null,
|
|
25
|
+
ctx.globalOptions,
|
|
26
|
+
ctx,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!allJsonData || allJsonData.length === 0) {
|
|
30
|
+
throw new Error(`Could not find JSON data for managed pages.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const results: any[] = [];
|
|
34
|
+
|
|
35
|
+
const deepFind = (obj: any) => {
|
|
36
|
+
if (obj && typeof obj === 'object') {
|
|
37
|
+
if (!Array.isArray(obj)) {
|
|
38
|
+
if (
|
|
39
|
+
obj.profile_switcher_eligible_profiles &&
|
|
40
|
+
Array.isArray(obj.profile_switcher_eligible_profiles.nodes)
|
|
41
|
+
) {
|
|
42
|
+
obj.profile_switcher_eligible_profiles.nodes.forEach(
|
|
43
|
+
(node: any) => {
|
|
44
|
+
const prof = node.profile;
|
|
45
|
+
if (prof && prof.id && prof.name) {
|
|
46
|
+
if (!results.find((r) => r.id === prof.id)) {
|
|
47
|
+
results.push(prof);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (
|
|
54
|
+
obj.first_profiles &&
|
|
55
|
+
Array.isArray(obj.first_profiles.nodes)
|
|
56
|
+
) {
|
|
57
|
+
obj.first_profiles.nodes.forEach((node: any) => {
|
|
58
|
+
const prof = node.profile;
|
|
59
|
+
if (prof && prof.id && prof.name) {
|
|
60
|
+
if (!results.find((r) => r.id === prof.id)) {
|
|
61
|
+
results.push(prof);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
Object.values(obj).forEach(deepFind);
|
|
67
|
+
} else {
|
|
68
|
+
obj.forEach(deepFind);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
deepFind(allJsonData);
|
|
74
|
+
|
|
75
|
+
// Return cleaned up objects
|
|
76
|
+
const cleanResults = results.map((prof) => ({
|
|
77
|
+
id: prof.id,
|
|
78
|
+
name: prof.name,
|
|
79
|
+
profilePicUrl: prof.profile_picture?.uri || null,
|
|
80
|
+
isProfilePlus: !!prof.is_profile_plus,
|
|
81
|
+
delegatePageId: prof.delegate_page_id || null,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
return cb(null, cleanResults);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
utils.error('getManagedPages', err);
|
|
87
|
+
return cb(err);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
fetchPages();
|
|
92
|
+
return returnPromise;
|
|
93
|
+
};
|
|
94
|
+
}
|
package/src/types/index.d.ts
CHANGED
|
@@ -422,6 +422,24 @@ export interface GroupModule {
|
|
|
422
422
|
keyword: string,
|
|
423
423
|
options?: SearchGroupOptions,
|
|
424
424
|
): Promise<SearchGroupResult>;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Resolves a Facebook share URL to the actual group ID.
|
|
428
|
+
*
|
|
429
|
+
* Accepts various formats:
|
|
430
|
+
* - Full URL: `https://www.facebook.com/share/g/14bKqsywAfu/`
|
|
431
|
+
* - Path only: `/share/g/14bKqsywAfu/`
|
|
432
|
+
* - Short key: `14bKqsywAfu`
|
|
433
|
+
*
|
|
434
|
+
* @param shareUrl The share URL, path, or short key to resolve.
|
|
435
|
+
*/
|
|
436
|
+
resolveShareUrl(
|
|
437
|
+
shareUrl: string,
|
|
438
|
+
): Promise<{
|
|
439
|
+
groupID: string;
|
|
440
|
+
name: string | null;
|
|
441
|
+
url: string | null;
|
|
442
|
+
}>;
|
|
425
443
|
}
|
|
426
444
|
|
|
427
445
|
export interface MessageObject {
|
|
@@ -549,6 +567,14 @@ export interface API {
|
|
|
549
567
|
callback?: Callback<ThreadInfo[]>,
|
|
550
568
|
): Promise<ThreadInfo[]>;
|
|
551
569
|
|
|
570
|
+
/** Get a list of threads for a managed Page. */
|
|
571
|
+
getPageThreadList(
|
|
572
|
+
pageID: string,
|
|
573
|
+
limit?: number,
|
|
574
|
+
tags?: string[],
|
|
575
|
+
callback?: Callback<ThreadInfo[]>,
|
|
576
|
+
): Promise<ThreadInfo[]>;
|
|
577
|
+
|
|
552
578
|
/** Get message history for a thread. */
|
|
553
579
|
getThreadHistory(
|
|
554
580
|
threadID: ThreadID,
|
package/src/utils/clients.ts
CHANGED
|
@@ -22,7 +22,8 @@ export function parseAndCheckLogin(ctx: any, http: any, retryCount: number = 0):
|
|
|
22
22
|
|
|
23
23
|
await delay(retryTime);
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const contentType = data.request.headers && data.request.headers["content-type"];
|
|
26
|
+
if (contentType && contentType.split(";")[0] === "multipart/form-data") {
|
|
26
27
|
const newData = await http.postFormData(url, ctx.jar, data.request.formData, data.request.qs, ctx.globalOptions, ctx);
|
|
27
28
|
return await parseAndCheckLogin(ctx, http, retryCount)(newData);
|
|
28
29
|
} else {
|
package/src/utils/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import * as cheerio from "cheerio";
|
|
2
|
+
import * as util from "util";
|
|
1
3
|
import * as network from "./axios";
|
|
2
|
-
import * as headers from "./headers";
|
|
3
4
|
import * as clients from "./clients";
|
|
4
5
|
import * as constants from "./constants";
|
|
5
6
|
import * as formatters from "./formatters";
|
|
7
|
+
import * as headers from "./headers";
|
|
6
8
|
import * as userAgents from "./user-agents";
|
|
7
|
-
import * as cheerio from "cheerio";
|
|
8
|
-
import * as util from "util";
|
|
9
9
|
|
|
10
10
|
async function json(url: string, jar: any, qs?: any, options?: any, ctx?: any, customHeader?: any): Promise<any[]> {
|
|
11
11
|
try {
|
|
@@ -39,7 +39,7 @@ function makeDefaults(html: string, userID: string | number, ctx: any): any {
|
|
|
39
39
|
function mergeWithDefaults(obj?: any): any {
|
|
40
40
|
const newObj: any = { av: userID, __user: userID, __req: (reqCounter++).toString(36), __rev: revision, __a: 1, ...(ctx && { fb_dtsg: ctx.fb_dtsg, jazoest: ctx.jazoest }) };
|
|
41
41
|
if (!obj) return newObj;
|
|
42
|
-
for (const prop in obj) { if (obj.hasOwnProperty(prop)
|
|
42
|
+
for (const prop in obj) { if (obj.hasOwnProperty(prop)) { newObj[prop] = obj[prop]; } }
|
|
43
43
|
return newObj;
|
|
44
44
|
}
|
|
45
45
|
return {
|