facebook-comments-client 0.1.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.
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/dist/cli.js +1608 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1540 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +293 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.js +1503 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/** Normalized comment author. Every field is null-safe — Facebook data is frequently incomplete. */
|
|
2
|
+
interface FacebookCommentAuthor {
|
|
3
|
+
id: string | null;
|
|
4
|
+
name: string | null;
|
|
5
|
+
profileUrl: string | null;
|
|
6
|
+
/** Author profile-picture URL (validated https Facebook CDN), when rendered. */
|
|
7
|
+
avatarUrl: string | null;
|
|
8
|
+
}
|
|
9
|
+
interface FacebookCommentAttachment {
|
|
10
|
+
type: "image" | "sticker";
|
|
11
|
+
url: string;
|
|
12
|
+
alt: string | null;
|
|
13
|
+
width: number | null;
|
|
14
|
+
height: number | null;
|
|
15
|
+
}
|
|
16
|
+
/** Normalized comment, independent of Facebook's internal response structure. */
|
|
17
|
+
interface FacebookComment {
|
|
18
|
+
id: string | null;
|
|
19
|
+
author: FacebookCommentAuthor;
|
|
20
|
+
message: string;
|
|
21
|
+
/** ISO 8601 string, or null when no parseable timestamp is available. */
|
|
22
|
+
createdAt: string | null;
|
|
23
|
+
reactions: {
|
|
24
|
+
total: number | null;
|
|
25
|
+
};
|
|
26
|
+
/** Parent comment id for replies, null for top-level comments. */
|
|
27
|
+
parentId: string | null;
|
|
28
|
+
attachments?: FacebookCommentAttachment[];
|
|
29
|
+
replies?: FacebookComment[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Shape produced by extractors before normalization.
|
|
33
|
+
* All fields optional — never trust Facebook markup to populate them.
|
|
34
|
+
*/
|
|
35
|
+
interface RawFacebookComment {
|
|
36
|
+
id?: string | null;
|
|
37
|
+
/** Post id embedded in the comment's profile link (comment_id param) — used to
|
|
38
|
+
* drop comments that belong to a different post rendered on the same page. */
|
|
39
|
+
postRefId?: string | null;
|
|
40
|
+
authorId?: string | null;
|
|
41
|
+
authorName?: string | null;
|
|
42
|
+
authorProfileUrl?: string | null;
|
|
43
|
+
authorAvatarUrl?: string | null;
|
|
44
|
+
message?: string | null;
|
|
45
|
+
/** Timestamp exactly as rendered ("2h", "January 5 at 3:04 PM", ISO, ...). */
|
|
46
|
+
createdAtRaw?: string | null;
|
|
47
|
+
reactionsTotal?: number | null;
|
|
48
|
+
parentId?: string | null;
|
|
49
|
+
/** True when the article's aria-label marks it as a reply ("Reply by ...").
|
|
50
|
+
* Facebook renders some replies as flat sibling articles — the flag plus
|
|
51
|
+
* parentAuthorName re-attach them to their parent comment. */
|
|
52
|
+
isReply?: boolean;
|
|
53
|
+
/** Parent comment author name captured from "Reply by X to Y's comment". */
|
|
54
|
+
parentAuthorName?: string | null;
|
|
55
|
+
attachments?: FacebookCommentAttachment[];
|
|
56
|
+
replies?: RawFacebookComment[];
|
|
57
|
+
}
|
|
58
|
+
interface ExtractionOptions {
|
|
59
|
+
maxComments: number;
|
|
60
|
+
includeReplies: boolean;
|
|
61
|
+
maxReplies: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface FacebookPost {
|
|
65
|
+
id: string | null;
|
|
66
|
+
url: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Loose, library-owned session types. Deliberately tolerant (optional fields):
|
|
71
|
+
* sessions exported by older Playwright versions or trimmed by users may omit
|
|
72
|
+
* defaults. ContextFactory normalizes these into Playwright's strict shape.
|
|
73
|
+
*/
|
|
74
|
+
interface Cookie {
|
|
75
|
+
name: string;
|
|
76
|
+
value: string;
|
|
77
|
+
domain: string;
|
|
78
|
+
path?: string;
|
|
79
|
+
/** Unix time in seconds; -1 means session cookie. */
|
|
80
|
+
expires?: number;
|
|
81
|
+
httpOnly?: boolean;
|
|
82
|
+
secure?: boolean;
|
|
83
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
84
|
+
}
|
|
85
|
+
interface StorageStateOrigin {
|
|
86
|
+
origin: string;
|
|
87
|
+
localStorage: Array<{
|
|
88
|
+
name: string;
|
|
89
|
+
value: string;
|
|
90
|
+
}>;
|
|
91
|
+
}
|
|
92
|
+
interface StorageState {
|
|
93
|
+
cookies: Cookie[];
|
|
94
|
+
origins?: StorageStateOrigin[];
|
|
95
|
+
}
|
|
96
|
+
/** Session stored as a storage-state JSON file on disk. */
|
|
97
|
+
interface FileSessionConfig {
|
|
98
|
+
/** "storage" is accepted as an alias of "file". */
|
|
99
|
+
type: "file" | "storage";
|
|
100
|
+
path: string;
|
|
101
|
+
}
|
|
102
|
+
/** Session loaded from a base64-encoded storage state in an environment variable. */
|
|
103
|
+
interface EnvSessionConfig {
|
|
104
|
+
type: "env";
|
|
105
|
+
envKey: string;
|
|
106
|
+
}
|
|
107
|
+
/** Session provided as a plain array of cookies. */
|
|
108
|
+
interface CookiesSessionConfig {
|
|
109
|
+
type: "cookies";
|
|
110
|
+
cookies: Cookie[];
|
|
111
|
+
}
|
|
112
|
+
/** Session provided inline as a storage-state object. */
|
|
113
|
+
interface InlineSessionConfig {
|
|
114
|
+
type: "state";
|
|
115
|
+
state: StorageState;
|
|
116
|
+
}
|
|
117
|
+
type SessionConfig = FileSessionConfig | EnvSessionConfig | CookiesSessionConfig | InlineSessionConfig;
|
|
118
|
+
interface SessionValidationResult {
|
|
119
|
+
/** True when the session data is structurally valid (parses, has expected shape). */
|
|
120
|
+
valid: boolean;
|
|
121
|
+
/** True when the session appears authenticated (auth cookies present and accepted). */
|
|
122
|
+
authenticated: boolean;
|
|
123
|
+
/** Best-effort account name; null when not safely available. */
|
|
124
|
+
user?: {
|
|
125
|
+
name: string | null;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface GetCommentsOptions {
|
|
130
|
+
/** Facebook post URL (group posts, permalinks, photo/video posts, ...). */
|
|
131
|
+
url: string;
|
|
132
|
+
/** Maximum top-level comments to return per call. Default 50. */
|
|
133
|
+
maxComments?: number;
|
|
134
|
+
/** Expand and include reply threads. Default false. */
|
|
135
|
+
includeReplies?: boolean;
|
|
136
|
+
/** Maximum replies per comment when includeReplies is true. Default 20. */
|
|
137
|
+
maxReplies?: number;
|
|
138
|
+
/** Cursor from a previous call's pagination.nextCursor. */
|
|
139
|
+
cursor?: string | null;
|
|
140
|
+
/** Navigation timeout in milliseconds. Default 30000. */
|
|
141
|
+
timeout?: number;
|
|
142
|
+
/** Override the client-level headless setting for this call. */
|
|
143
|
+
headless?: boolean;
|
|
144
|
+
}
|
|
145
|
+
interface PaginationInfo {
|
|
146
|
+
hasMore: boolean;
|
|
147
|
+
nextCursor: string | null;
|
|
148
|
+
}
|
|
149
|
+
interface GetCommentsResult {
|
|
150
|
+
post: FacebookPost;
|
|
151
|
+
comments: FacebookComment[];
|
|
152
|
+
pagination: PaginationInfo;
|
|
153
|
+
}
|
|
154
|
+
interface BrowserOptions {
|
|
155
|
+
/** Default headless mode. Default true. */
|
|
156
|
+
headless?: boolean;
|
|
157
|
+
/** Default Playwright action timeout in milliseconds. Default 30000. */
|
|
158
|
+
timeoutMs?: number;
|
|
159
|
+
/** Optional Chromium binary, useful with serverless Chromium distributions. */
|
|
160
|
+
executablePath?: string;
|
|
161
|
+
/** Extra Chromium launch arguments, useful on serverless platforms. */
|
|
162
|
+
args?: string[];
|
|
163
|
+
}
|
|
164
|
+
type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
165
|
+
interface Logger {
|
|
166
|
+
debug(message: string, ...args: unknown[]): void;
|
|
167
|
+
info(message: string, ...args: unknown[]): void;
|
|
168
|
+
warn(message: string, ...args: unknown[]): void;
|
|
169
|
+
error(message: string, ...args: unknown[]): void;
|
|
170
|
+
}
|
|
171
|
+
interface FacebookCommentsClientOptions {
|
|
172
|
+
session: SessionConfig;
|
|
173
|
+
browser?: BrowserOptions;
|
|
174
|
+
/** Custom logger. A quiet console logger is used by default; sensitive values are always redacted. */
|
|
175
|
+
logger?: Logger;
|
|
176
|
+
}
|
|
177
|
+
interface LoginOptions {
|
|
178
|
+
/** Where to write the exported storage state. */
|
|
179
|
+
outputPath: string;
|
|
180
|
+
/** How long to wait for the user to finish logging in. Default 300000 (5 minutes). */
|
|
181
|
+
timeoutMs?: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
declare class FacebookCommentsClient {
|
|
185
|
+
private readonly options;
|
|
186
|
+
private readonly browserManager;
|
|
187
|
+
private readonly logger;
|
|
188
|
+
constructor(options: FacebookCommentsClientOptions);
|
|
189
|
+
/**
|
|
190
|
+
* Interactive login helper: opens a visible browser, waits for you to log in
|
|
191
|
+
* manually, then exports the storage state. Never enters credentials for you.
|
|
192
|
+
*/
|
|
193
|
+
static login(loginOptions: LoginOptions): Promise<void>;
|
|
194
|
+
/** Fetch and normalize comments for one post URL. */
|
|
195
|
+
getComments(options: GetCommentsOptions): Promise<GetCommentsResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Cheap session check: resolves the session, verifies auth cookies, and
|
|
198
|
+
* confirms Facebook still accepts them. Never throws for session problems —
|
|
199
|
+
* inspect the returned flags instead.
|
|
200
|
+
*/
|
|
201
|
+
validateSession(): Promise<SessionValidationResult>;
|
|
202
|
+
close(): Promise<void>;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type FacebookErrorCode = "FB_CLIENT" | "FB_AUTH" | "FB_SESSION_EXPIRED" | "FB_POST_NOT_FOUND" | "FB_ACCESS_DENIED" | "FB_EXTRACTION";
|
|
206
|
+
interface FacebookErrorOptions {
|
|
207
|
+
retryable?: boolean;
|
|
208
|
+
cause?: unknown;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Base error for all library failures.
|
|
212
|
+
* Messages must never contain session cookies, tokens, or storage-state content.
|
|
213
|
+
*/
|
|
214
|
+
declare class FacebookClientError extends Error {
|
|
215
|
+
readonly code: FacebookErrorCode;
|
|
216
|
+
readonly retryable: boolean;
|
|
217
|
+
constructor(message: string, options?: FacebookErrorOptions & {
|
|
218
|
+
code?: FacebookErrorCode;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Session data is missing, unreadable, malformed, or contains no Facebook auth cookies. */
|
|
223
|
+
declare class FacebookAuthenticationError extends FacebookClientError {
|
|
224
|
+
constructor(message: string, options?: {
|
|
225
|
+
retryable?: boolean;
|
|
226
|
+
cause?: unknown;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Session was structurally valid but Facebook no longer accepts it (login redirect observed). */
|
|
231
|
+
declare class FacebookSessionExpiredError extends FacebookClientError {
|
|
232
|
+
constructor(message: string, options?: {
|
|
233
|
+
retryable?: boolean;
|
|
234
|
+
cause?: unknown;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Post URL does not exist, was deleted, or is not visible ("this content isn't available"). */
|
|
239
|
+
declare class FacebookPostNotFoundError extends FacebookClientError {
|
|
240
|
+
constructor(message: string, options?: {
|
|
241
|
+
retryable?: boolean;
|
|
242
|
+
cause?: unknown;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Post exists but the account lacks access (e.g. private group the account has not joined). */
|
|
247
|
+
declare class FacebookAccessDeniedError extends FacebookClientError {
|
|
248
|
+
constructor(message: string, options?: {
|
|
249
|
+
retryable?: boolean;
|
|
250
|
+
cause?: unknown;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Page loaded but no extractor could produce usable comment data. Usually a markup change. */
|
|
255
|
+
declare class FacebookExtractionError extends FacebookClientError {
|
|
256
|
+
constructor(message: string, options?: {
|
|
257
|
+
retryable?: boolean;
|
|
258
|
+
cause?: unknown;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
interface ParsedPostUrl {
|
|
263
|
+
/** Normalized absolute URL. */
|
|
264
|
+
url: string;
|
|
265
|
+
/** Best-effort post identifier; null when it cannot be determined from the URL. */
|
|
266
|
+
postId: string | null;
|
|
267
|
+
/** Group identifier when the post lives in a group. */
|
|
268
|
+
groupId: string | null;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate and parse a Facebook post URL. Throws FacebookClientError for
|
|
272
|
+
* non-Facebook or non-http(s) URLs. Id extraction is best-effort — a null postId
|
|
273
|
+
* never blocks fetching; extraction falls back to page data.
|
|
274
|
+
*/
|
|
275
|
+
declare function parseFacebookPostUrl(input: string): ParsedPostUrl;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Recursively replace values of sensitive keys so session data never reaches logs
|
|
279
|
+
* or error messages. Known Facebook auth cookie names are always redacted.
|
|
280
|
+
*/
|
|
281
|
+
declare function redact(value: unknown, depth?: number): unknown;
|
|
282
|
+
/** Console logger that redacts every interpolated argument. Quiet (warn+) by default. */
|
|
283
|
+
declare function createConsoleLogger(level?: LogLevel): Logger;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Best-effort timestamp parsing. Understands ISO strings, unix seconds,
|
|
287
|
+
* Facebook relative times ("2h", "5m", "just now", "Yesterday at 3:04 PM")
|
|
288
|
+
* and month-name formats ("January 5 at 3:04 PM", "5 January 2026 at 15:04").
|
|
289
|
+
* Returns null when nothing parses — never throws.
|
|
290
|
+
*/
|
|
291
|
+
declare function parseTimestamp(input: string | null | undefined, now?: Date): string | null;
|
|
292
|
+
|
|
293
|
+
export { type BrowserOptions, type Cookie, type CookiesSessionConfig, type EnvSessionConfig, type ExtractionOptions, FacebookAccessDeniedError, FacebookAuthenticationError, FacebookClientError, type FacebookComment, type FacebookCommentAttachment, type FacebookCommentAuthor, FacebookCommentsClient, type FacebookCommentsClientOptions, type FacebookErrorCode, FacebookExtractionError, type FacebookPost, FacebookPostNotFoundError, FacebookSessionExpiredError, type FileSessionConfig, type GetCommentsOptions, type GetCommentsResult, type InlineSessionConfig, type LogLevel, type Logger, type LoginOptions, type PaginationInfo, type ParsedPostUrl, type RawFacebookComment, type SessionConfig, type SessionValidationResult, type StorageState, createConsoleLogger, parseFacebookPostUrl, parseTimestamp, redact };
|