scrapebadger 0.3.0 → 0.4.0
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/README.md +31 -5
- package/dist/{index-DQ_jDTcQ.d.cts → index-CIZUd1Zr.d.cts} +341 -1
- package/dist/{index-DQ_jDTcQ.d.ts → index-CIZUd1Zr.d.ts} +341 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +317 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +317 -20
- package/dist/index.mjs.map +1 -1
- package/dist/twitter/index.d.cts +1 -1
- package/dist/twitter/index.d.ts +1 -1
- package/dist/twitter/index.js +255 -10
- package/dist/twitter/index.js.map +1 -1
- package/dist/twitter/index.mjs +255 -11
- package/dist/twitter/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/twitter/index.mjs
CHANGED
|
@@ -10,12 +10,26 @@ function createPaginatedResponse(data, cursor) {
|
|
|
10
10
|
hasMore: !!cursor
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
var RATE_LIMIT_WARN_THRESHOLD = 0.2;
|
|
13
14
|
async function* paginate(fetchPage, options = {}) {
|
|
14
15
|
const { maxItems } = options;
|
|
15
16
|
let cursor;
|
|
16
17
|
let totalYielded = 0;
|
|
17
18
|
do {
|
|
18
|
-
const response = await fetchPage(cursor);
|
|
19
|
+
const { response, rateLimit } = await fetchPage(cursor);
|
|
20
|
+
if (rateLimit) {
|
|
21
|
+
const { limit, remaining, reset } = rateLimit;
|
|
22
|
+
if (limit > 0 && remaining / limit < RATE_LIMIT_WARN_THRESHOLD) {
|
|
23
|
+
const nowSec = Date.now() / 1e3;
|
|
24
|
+
const windowRemainingSec = Math.max(reset - nowSec, 1);
|
|
25
|
+
const delayMs = remaining > 0 ? windowRemainingSec / remaining * 1e3 : windowRemainingSec * 1e3;
|
|
26
|
+
const resetInSec = Math.round(windowRemainingSec);
|
|
27
|
+
console.warn(
|
|
28
|
+
`\x1B[33m\u26A0 ScrapeBadger: Rate limit: ${remaining}/${limit} remaining (resets in ${resetInSec}s), throttling pagination\x1B[0m`
|
|
29
|
+
);
|
|
30
|
+
await sleep(delayMs);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
19
33
|
for (const item of response.data) {
|
|
20
34
|
yield item;
|
|
21
35
|
totalYielded++;
|
|
@@ -26,6 +40,9 @@ async function* paginate(fetchPage, options = {}) {
|
|
|
26
40
|
cursor = response.nextCursor;
|
|
27
41
|
} while (cursor);
|
|
28
42
|
}
|
|
43
|
+
function sleep(ms) {
|
|
44
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
|
+
}
|
|
29
46
|
|
|
30
47
|
// src/twitter/tweets.ts
|
|
31
48
|
var TweetsClient = class {
|
|
@@ -223,7 +240,8 @@ var TweetsClient = class {
|
|
|
223
240
|
*/
|
|
224
241
|
async *getQuotesAll(tweetId, options = {}) {
|
|
225
242
|
const fetchPage = async (cursor) => {
|
|
226
|
-
|
|
243
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/tweets/tweet/${tweetId}/quotes`, { params: { cursor } });
|
|
244
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
227
245
|
};
|
|
228
246
|
yield* paginate(fetchPage, options);
|
|
229
247
|
}
|
|
@@ -290,7 +308,15 @@ var TweetsClient = class {
|
|
|
290
308
|
*/
|
|
291
309
|
async *searchAll(query, options = {}) {
|
|
292
310
|
const fetchPage = async (cursor) => {
|
|
293
|
-
|
|
311
|
+
const { data, rateLimit } = await this.client.requestWithHeaders("/v1/twitter/tweets/advanced_search", {
|
|
312
|
+
params: {
|
|
313
|
+
query,
|
|
314
|
+
query_type: options.queryType ?? "Top",
|
|
315
|
+
count: options.count,
|
|
316
|
+
cursor
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
294
320
|
};
|
|
295
321
|
yield* paginate(fetchPage, options);
|
|
296
322
|
}
|
|
@@ -334,10 +360,64 @@ var TweetsClient = class {
|
|
|
334
360
|
*/
|
|
335
361
|
async *getUserTweetsAll(username, options = {}) {
|
|
336
362
|
const fetchPage = async (cursor) => {
|
|
337
|
-
|
|
363
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/latest_tweets`, { params: { cursor } });
|
|
364
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
338
365
|
};
|
|
339
366
|
yield* paginate(fetchPage, options);
|
|
340
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Get the edit history of a tweet.
|
|
370
|
+
*
|
|
371
|
+
* @param tweetId - The tweet ID to get edit history for.
|
|
372
|
+
* @returns Paginated response containing tweet versions.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```typescript
|
|
376
|
+
* const history = await client.twitter.tweets.getEditHistory("1234567890");
|
|
377
|
+
* console.log(`${history.data.length} version(s) of this tweet`);
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
async getEditHistory(tweetId) {
|
|
381
|
+
const response = await this.client.request(
|
|
382
|
+
`/v1/twitter/tweets/tweet/${tweetId}/edit_history`
|
|
383
|
+
);
|
|
384
|
+
return createPaginatedResponse(response.data ?? [], void 0);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get community notes (Birdwatch) attached to a tweet.
|
|
388
|
+
*
|
|
389
|
+
* @param tweetId - The tweet ID to get community notes for.
|
|
390
|
+
* @returns Paginated response containing community notes.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const notes = await client.twitter.tweets.getCommunityNotes("1234567890");
|
|
395
|
+
* for (const note of notes.data) {
|
|
396
|
+
* console.log(note.text);
|
|
397
|
+
* }
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
async getCommunityNotes(tweetId) {
|
|
401
|
+
const response = await this.client.request(
|
|
402
|
+
`/v1/twitter/tweets/tweet/${tweetId}/community_notes`
|
|
403
|
+
);
|
|
404
|
+
return createPaginatedResponse(response.data ?? [], void 0);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Get a long-form article by its ID.
|
|
408
|
+
*
|
|
409
|
+
* @param articleId - The article ID to fetch.
|
|
410
|
+
* @returns The article data.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```typescript
|
|
414
|
+
* const article = await client.twitter.tweets.getArticle("abc123");
|
|
415
|
+
* console.log(`${article.title}: ${article.text?.slice(0, 100)}...`);
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
async getArticle(articleId) {
|
|
419
|
+
return this.client.request(`/v1/twitter/tweets/article/${articleId}`);
|
|
420
|
+
}
|
|
341
421
|
};
|
|
342
422
|
|
|
343
423
|
// src/twitter/users.ts
|
|
@@ -444,7 +524,8 @@ var UsersClient = class {
|
|
|
444
524
|
*/
|
|
445
525
|
async *getFollowersAll(username, options = {}) {
|
|
446
526
|
const fetchPage = async (cursor) => {
|
|
447
|
-
|
|
527
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/followers`, { params: { cursor } });
|
|
528
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
448
529
|
};
|
|
449
530
|
yield* paginate(fetchPage, options);
|
|
450
531
|
}
|
|
@@ -479,7 +560,8 @@ var UsersClient = class {
|
|
|
479
560
|
*/
|
|
480
561
|
async *getFollowingAll(username, options = {}) {
|
|
481
562
|
const fetchPage = async (cursor) => {
|
|
482
|
-
|
|
563
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/followings`, { params: { cursor } });
|
|
564
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
483
565
|
};
|
|
484
566
|
yield* paginate(fetchPage, options);
|
|
485
567
|
}
|
|
@@ -642,10 +724,97 @@ var UsersClient = class {
|
|
|
642
724
|
*/
|
|
643
725
|
async *searchAll(query, options = {}) {
|
|
644
726
|
const fetchPage = async (cursor) => {
|
|
645
|
-
|
|
727
|
+
const { data, rateLimit } = await this.client.requestWithHeaders("/v1/twitter/users/search_users", { params: { query, cursor } });
|
|
728
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
646
729
|
};
|
|
647
730
|
yield* paginate(fetchPage, options);
|
|
648
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* Get multiple users by their numeric IDs in a single request.
|
|
734
|
+
*
|
|
735
|
+
* @param userIds - List of user IDs to fetch.
|
|
736
|
+
* @returns Paginated response containing the matching users.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const users = await client.twitter.users.getByIds(["44196397", "783214"]);
|
|
741
|
+
* for (const user of users.data) {
|
|
742
|
+
* console.log(`@${user.username}`);
|
|
743
|
+
* }
|
|
744
|
+
* ```
|
|
745
|
+
*/
|
|
746
|
+
async getByIds(userIds) {
|
|
747
|
+
const response = await this.client.request(
|
|
748
|
+
"/v1/twitter/users/batch_by_ids",
|
|
749
|
+
{ params: { user_ids: userIds.join(",") } }
|
|
750
|
+
);
|
|
751
|
+
return createPaginatedResponse(response.data ?? [], void 0);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Get multiple users by their usernames in a single request.
|
|
755
|
+
*
|
|
756
|
+
* @param usernames - List of usernames (without @) to fetch.
|
|
757
|
+
* @returns Paginated response containing the matching users.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```typescript
|
|
761
|
+
* const users = await client.twitter.users.getByUsernames(["elonmusk", "twitter"]);
|
|
762
|
+
* for (const user of users.data) {
|
|
763
|
+
* console.log(`${user.name}: ${user.followers_count?.toLocaleString()} followers`);
|
|
764
|
+
* }
|
|
765
|
+
* ```
|
|
766
|
+
*/
|
|
767
|
+
async getByUsernames(usernames) {
|
|
768
|
+
const response = await this.client.request(
|
|
769
|
+
"/v1/twitter/users/batch_by_usernames",
|
|
770
|
+
{ params: { usernames: usernames.join(",") } }
|
|
771
|
+
);
|
|
772
|
+
return createPaginatedResponse(response.data ?? [], void 0);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Get tweets that mention a user.
|
|
776
|
+
*
|
|
777
|
+
* @param username - The user's username (without @).
|
|
778
|
+
* @param options - Pagination options with optional count.
|
|
779
|
+
* @returns Paginated response containing tweets mentioning the user.
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* const mentions = await client.twitter.users.getMentions("elonmusk");
|
|
784
|
+
* for (const tweet of mentions.data) {
|
|
785
|
+
* console.log(`@${tweet.username}: ${tweet.text.slice(0, 100)}...`);
|
|
786
|
+
* }
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
async getMentions(username, options = {}) {
|
|
790
|
+
const response = await this.client.request(
|
|
791
|
+
`/v1/twitter/users/${username}/mentions`,
|
|
792
|
+
{ params: { count: options.count, cursor: options.cursor } }
|
|
793
|
+
);
|
|
794
|
+
return createPaginatedResponse(response.data ?? [], response.next_cursor);
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get long-form articles authored by a user.
|
|
798
|
+
*
|
|
799
|
+
* @param userId - The user's numeric ID.
|
|
800
|
+
* @param options - Pagination options with optional count.
|
|
801
|
+
* @returns Paginated response containing the user's articles as tweets.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```typescript
|
|
805
|
+
* const articles = await client.twitter.users.getArticles("44196397");
|
|
806
|
+
* for (const article of articles.data) {
|
|
807
|
+
* console.log(article.text?.slice(0, 100));
|
|
808
|
+
* }
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
async getArticles(userId, options = {}) {
|
|
812
|
+
const response = await this.client.request(
|
|
813
|
+
`/v1/twitter/users/${userId}/articles`,
|
|
814
|
+
{ params: { count: options.count, cursor: options.cursor } }
|
|
815
|
+
);
|
|
816
|
+
return createPaginatedResponse(response.data ?? [], response.next_cursor);
|
|
817
|
+
}
|
|
649
818
|
};
|
|
650
819
|
|
|
651
820
|
// src/twitter/lists.ts
|
|
@@ -702,7 +871,8 @@ var ListsClient = class {
|
|
|
702
871
|
*/
|
|
703
872
|
async *getTweetsAll(listId, options = {}) {
|
|
704
873
|
const fetchPage = async (cursor) => {
|
|
705
|
-
|
|
874
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/lists/${listId}/tweets`, { params: { cursor } });
|
|
875
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
706
876
|
};
|
|
707
877
|
yield* paginate(fetchPage, options);
|
|
708
878
|
}
|
|
@@ -737,7 +907,8 @@ var ListsClient = class {
|
|
|
737
907
|
*/
|
|
738
908
|
async *getMembersAll(listId, options = {}) {
|
|
739
909
|
const fetchPage = async (cursor) => {
|
|
740
|
-
|
|
910
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/lists/${listId}/members`, { params: { cursor } });
|
|
911
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
741
912
|
};
|
|
742
913
|
yield* paginate(fetchPage, options);
|
|
743
914
|
}
|
|
@@ -790,6 +961,29 @@ var ListsClient = class {
|
|
|
790
961
|
);
|
|
791
962
|
return createPaginatedResponse(response.data ?? [], response.next_cursor);
|
|
792
963
|
}
|
|
964
|
+
/**
|
|
965
|
+
* Search tweets within a specific list.
|
|
966
|
+
*
|
|
967
|
+
* @param listId - The list ID to search within.
|
|
968
|
+
* @param query - Search query string.
|
|
969
|
+
* @param options - Pagination options with optional count.
|
|
970
|
+
* @returns Paginated response containing matching tweets from the list.
|
|
971
|
+
*
|
|
972
|
+
* @example
|
|
973
|
+
* ```typescript
|
|
974
|
+
* const results = await client.twitter.lists.searchTweets("123456", "python");
|
|
975
|
+
* for (const tweet of results.data) {
|
|
976
|
+
* console.log(`@${tweet.username}: ${tweet.text.slice(0, 100)}...`);
|
|
977
|
+
* }
|
|
978
|
+
* ```
|
|
979
|
+
*/
|
|
980
|
+
async searchTweets(listId, query, options = {}) {
|
|
981
|
+
const response = await this.client.request(
|
|
982
|
+
`/v1/twitter/lists/${listId}/search_tweets`,
|
|
983
|
+
{ params: { query, count: options.count, cursor: options.cursor } }
|
|
984
|
+
);
|
|
985
|
+
return createPaginatedResponse(response.data ?? [], response.next_cursor);
|
|
986
|
+
}
|
|
793
987
|
};
|
|
794
988
|
|
|
795
989
|
// src/twitter/communities.ts
|
|
@@ -885,7 +1079,14 @@ var CommunitiesClient = class {
|
|
|
885
1079
|
*/
|
|
886
1080
|
async *getTweetsAll(communityId, options = {}) {
|
|
887
1081
|
const fetchPage = async (cursor) => {
|
|
888
|
-
|
|
1082
|
+
const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/communities/${communityId}/tweets`, {
|
|
1083
|
+
params: {
|
|
1084
|
+
tweet_type: options.tweetType ?? "Top",
|
|
1085
|
+
count: options.count ?? 40,
|
|
1086
|
+
cursor
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
|
|
889
1090
|
};
|
|
890
1091
|
yield* paginate(fetchPage, options);
|
|
891
1092
|
}
|
|
@@ -1825,6 +2026,46 @@ function verifyWebhookSignature(secret, body, signatureHeader) {
|
|
|
1825
2026
|
}
|
|
1826
2027
|
}
|
|
1827
2028
|
|
|
2029
|
+
// src/twitter/spaces.ts
|
|
2030
|
+
var SpacesClient = class {
|
|
2031
|
+
client;
|
|
2032
|
+
constructor(client) {
|
|
2033
|
+
this.client = client;
|
|
2034
|
+
}
|
|
2035
|
+
/**
|
|
2036
|
+
* Get details for a specific Twitter Space.
|
|
2037
|
+
*
|
|
2038
|
+
* @param spaceId - The Space ID to fetch.
|
|
2039
|
+
* @returns The Space data.
|
|
2040
|
+
* @throws NotFoundError - If the Space doesn't exist.
|
|
2041
|
+
*
|
|
2042
|
+
* @example
|
|
2043
|
+
* ```typescript
|
|
2044
|
+
* const space = await client.twitter.spaces.getDetail("1eaKbrPPbPwKX");
|
|
2045
|
+
* console.log(`${space.title} — ${space.participant_count} participants`);
|
|
2046
|
+
* ```
|
|
2047
|
+
*/
|
|
2048
|
+
async getDetail(spaceId) {
|
|
2049
|
+
return this.client.request(`/v1/twitter/spaces/${spaceId}`);
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Get details for a live video broadcast.
|
|
2053
|
+
*
|
|
2054
|
+
* @param broadcastId - The broadcast ID to fetch.
|
|
2055
|
+
* @returns The broadcast data.
|
|
2056
|
+
* @throws NotFoundError - If the broadcast doesn't exist.
|
|
2057
|
+
*
|
|
2058
|
+
* @example
|
|
2059
|
+
* ```typescript
|
|
2060
|
+
* const broadcast = await client.twitter.spaces.getBroadcast("broadcast123");
|
|
2061
|
+
* console.log(`${broadcast.title}: ${broadcast.total_viewers} viewers`);
|
|
2062
|
+
* ```
|
|
2063
|
+
*/
|
|
2064
|
+
async getBroadcast(broadcastId) {
|
|
2065
|
+
return this.client.request(`/v1/twitter/spaces/broadcast/${broadcastId}`);
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
|
|
1828
2069
|
// src/twitter/client.ts
|
|
1829
2070
|
var TwitterClient = class {
|
|
1830
2071
|
/** Client for tweet operations */
|
|
@@ -1841,6 +2082,8 @@ var TwitterClient = class {
|
|
|
1841
2082
|
geo;
|
|
1842
2083
|
/** Client for real-time stream monitor management and WebSocket streaming */
|
|
1843
2084
|
stream;
|
|
2085
|
+
/** Client for Twitter Spaces and live broadcast operations */
|
|
2086
|
+
spaces;
|
|
1844
2087
|
/**
|
|
1845
2088
|
* Create a new Twitter client.
|
|
1846
2089
|
*
|
|
@@ -1854,9 +2097,10 @@ var TwitterClient = class {
|
|
|
1854
2097
|
this.trends = new TrendsClient(client);
|
|
1855
2098
|
this.geo = new GeoClient(client);
|
|
1856
2099
|
this.stream = new StreamClient(client);
|
|
2100
|
+
this.spaces = new SpacesClient(client);
|
|
1857
2101
|
}
|
|
1858
2102
|
};
|
|
1859
2103
|
|
|
1860
|
-
export { CommunitiesClient, GeoClient, ListsClient, StreamClient, TrendsClient, TweetsClient, TwitterClient, UsersClient, verifyWebhookSignature };
|
|
2104
|
+
export { CommunitiesClient, GeoClient, ListsClient, SpacesClient, StreamClient, TrendsClient, TweetsClient, TwitterClient, UsersClient, verifyWebhookSignature };
|
|
1861
2105
|
//# sourceMappingURL=index.mjs.map
|
|
1862
2106
|
//# sourceMappingURL=index.mjs.map
|