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.
@@ -1,2 +1,2 @@
1
- export { _ as ApiResponse, ae as BillingLog, af as BillingLogList, j as CommunitiesClient, J as Community, E as CommunityBanner, K as CommunityMember, F as CommunityRule, p as CommunityTweetType, ag as ConnectOptions, a6 as ConnectedEvent, a3 as CreateMonitorParams, ac as DeliveryLog, ad as DeliveryLogList, a9 as ErrorEvent, aj as FilterRuleCreate, ao as FilterRuleDeliveryLog, ap as FilterRuleDeliveryLogListResponse, am as FilterRuleListResponse, al as FilterRulePricingTier, aq as FilterRulePricingTiersResponse, ai as FilterRuleResponse, ah as FilterRuleStatus, ak as FilterRuleUpdate, an as FilterRuleValidateResponse, G as GeoClient, l as GeoSearchOptions, H as Hashtag, D as List, $ as ListResponse, L as ListsClient, X as Location, M as Media, a0 as MonitorStatus, a7 as PingEvent, Z as Place, Y as PlaceTrends, r as Poll, q as PollOption, Q as QueryType, m as StreamClient, n as StreamEmitter, aa as StreamEvent, ab as StreamEventType, a1 as StreamMonitor, a2 as StreamMonitorList, a5 as StreamTweet, O as Trend, o as TrendCategory, k as TrendsClient, w as Tweet, a8 as TweetEvent, u as TweetPlace, i as TweetsClient, T as TwitterClient, a4 as UpdateMonitorParams, s as Url, x as User, y as UserAbout, z as UserIds, t as UserMention, U as UsersClient, v as verifyWebhookSignature } from '../index-DQ_jDTcQ.cjs';
1
+ export { a3 as ApiResponse, $ as Article, aj as BillingLog, ak as BillingLogList, a2 as Broadcast, j as CommunitiesClient, K as Community, F as CommunityBanner, O as CommunityMember, a0 as CommunityNote, J as CommunityRule, q as CommunityTweetType, al as ConnectOptions, ab as ConnectedEvent, a8 as CreateMonitorParams, ah as DeliveryLog, ai as DeliveryLogList, ae as ErrorEvent, ao as FilterRuleCreate, at as FilterRuleDeliveryLog, au as FilterRuleDeliveryLogListResponse, ar as FilterRuleListResponse, aq as FilterRulePricingTier, av as FilterRulePricingTiersResponse, an as FilterRuleResponse, am as FilterRuleStatus, ap as FilterRuleUpdate, as as FilterRuleValidateResponse, G as GeoClient, l as GeoSearchOptions, H as Hashtag, E as List, a4 as ListResponse, L as ListsClient, Y as Location, M as Media, a5 as MonitorStatus, ac as PingEvent, _ as Place, Z as PlaceTrends, s as Poll, r as PollOption, Q as QueryType, a1 as Space, o as SpacesClient, m as StreamClient, n as StreamEmitter, af as StreamEvent, ag as StreamEventType, a6 as StreamMonitor, a7 as StreamMonitorList, aa as StreamTweet, X as Trend, p as TrendCategory, k as TrendsClient, x as Tweet, ad as TweetEvent, w as TweetPlace, i as TweetsClient, T as TwitterClient, a9 as UpdateMonitorParams, t as Url, y as User, z as UserAbout, D as UserIds, u as UserMention, U as UsersClient, v as verifyWebhookSignature } from '../index-CIZUd1Zr.cjs';
2
2
  import 'node:events';
@@ -1,2 +1,2 @@
1
- export { _ as ApiResponse, ae as BillingLog, af as BillingLogList, j as CommunitiesClient, J as Community, E as CommunityBanner, K as CommunityMember, F as CommunityRule, p as CommunityTweetType, ag as ConnectOptions, a6 as ConnectedEvent, a3 as CreateMonitorParams, ac as DeliveryLog, ad as DeliveryLogList, a9 as ErrorEvent, aj as FilterRuleCreate, ao as FilterRuleDeliveryLog, ap as FilterRuleDeliveryLogListResponse, am as FilterRuleListResponse, al as FilterRulePricingTier, aq as FilterRulePricingTiersResponse, ai as FilterRuleResponse, ah as FilterRuleStatus, ak as FilterRuleUpdate, an as FilterRuleValidateResponse, G as GeoClient, l as GeoSearchOptions, H as Hashtag, D as List, $ as ListResponse, L as ListsClient, X as Location, M as Media, a0 as MonitorStatus, a7 as PingEvent, Z as Place, Y as PlaceTrends, r as Poll, q as PollOption, Q as QueryType, m as StreamClient, n as StreamEmitter, aa as StreamEvent, ab as StreamEventType, a1 as StreamMonitor, a2 as StreamMonitorList, a5 as StreamTweet, O as Trend, o as TrendCategory, k as TrendsClient, w as Tweet, a8 as TweetEvent, u as TweetPlace, i as TweetsClient, T as TwitterClient, a4 as UpdateMonitorParams, s as Url, x as User, y as UserAbout, z as UserIds, t as UserMention, U as UsersClient, v as verifyWebhookSignature } from '../index-DQ_jDTcQ.js';
1
+ export { a3 as ApiResponse, $ as Article, aj as BillingLog, ak as BillingLogList, a2 as Broadcast, j as CommunitiesClient, K as Community, F as CommunityBanner, O as CommunityMember, a0 as CommunityNote, J as CommunityRule, q as CommunityTweetType, al as ConnectOptions, ab as ConnectedEvent, a8 as CreateMonitorParams, ah as DeliveryLog, ai as DeliveryLogList, ae as ErrorEvent, ao as FilterRuleCreate, at as FilterRuleDeliveryLog, au as FilterRuleDeliveryLogListResponse, ar as FilterRuleListResponse, aq as FilterRulePricingTier, av as FilterRulePricingTiersResponse, an as FilterRuleResponse, am as FilterRuleStatus, ap as FilterRuleUpdate, as as FilterRuleValidateResponse, G as GeoClient, l as GeoSearchOptions, H as Hashtag, E as List, a4 as ListResponse, L as ListsClient, Y as Location, M as Media, a5 as MonitorStatus, ac as PingEvent, _ as Place, Z as PlaceTrends, s as Poll, r as PollOption, Q as QueryType, a1 as Space, o as SpacesClient, m as StreamClient, n as StreamEmitter, af as StreamEvent, ag as StreamEventType, a6 as StreamMonitor, a7 as StreamMonitorList, aa as StreamTweet, X as Trend, p as TrendCategory, k as TrendsClient, x as Tweet, ad as TweetEvent, w as TweetPlace, i as TweetsClient, T as TwitterClient, a9 as UpdateMonitorParams, t as Url, y as User, z as UserAbout, D as UserIds, u as UserMention, U as UsersClient, v as verifyWebhookSignature } from '../index-CIZUd1Zr.js';
2
2
  import 'node:events';
@@ -16,12 +16,26 @@ function createPaginatedResponse(data, cursor) {
16
16
  hasMore: !!cursor
17
17
  };
18
18
  }
19
+ var RATE_LIMIT_WARN_THRESHOLD = 0.2;
19
20
  async function* paginate(fetchPage, options = {}) {
20
21
  const { maxItems } = options;
21
22
  let cursor;
22
23
  let totalYielded = 0;
23
24
  do {
24
- const response = await fetchPage(cursor);
25
+ const { response, rateLimit } = await fetchPage(cursor);
26
+ if (rateLimit) {
27
+ const { limit, remaining, reset } = rateLimit;
28
+ if (limit > 0 && remaining / limit < RATE_LIMIT_WARN_THRESHOLD) {
29
+ const nowSec = Date.now() / 1e3;
30
+ const windowRemainingSec = Math.max(reset - nowSec, 1);
31
+ const delayMs = remaining > 0 ? windowRemainingSec / remaining * 1e3 : windowRemainingSec * 1e3;
32
+ const resetInSec = Math.round(windowRemainingSec);
33
+ console.warn(
34
+ `\x1B[33m\u26A0 ScrapeBadger: Rate limit: ${remaining}/${limit} remaining (resets in ${resetInSec}s), throttling pagination\x1B[0m`
35
+ );
36
+ await sleep(delayMs);
37
+ }
38
+ }
25
39
  for (const item of response.data) {
26
40
  yield item;
27
41
  totalYielded++;
@@ -32,6 +46,9 @@ async function* paginate(fetchPage, options = {}) {
32
46
  cursor = response.nextCursor;
33
47
  } while (cursor);
34
48
  }
49
+ function sleep(ms) {
50
+ return new Promise((resolve) => setTimeout(resolve, ms));
51
+ }
35
52
 
36
53
  // src/twitter/tweets.ts
37
54
  var TweetsClient = class {
@@ -229,7 +246,8 @@ var TweetsClient = class {
229
246
  */
230
247
  async *getQuotesAll(tweetId, options = {}) {
231
248
  const fetchPage = async (cursor) => {
232
- return this.getQuotes(tweetId, { ...options, cursor });
249
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/tweets/tweet/${tweetId}/quotes`, { params: { cursor } });
250
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
233
251
  };
234
252
  yield* paginate(fetchPage, options);
235
253
  }
@@ -296,7 +314,15 @@ var TweetsClient = class {
296
314
  */
297
315
  async *searchAll(query, options = {}) {
298
316
  const fetchPage = async (cursor) => {
299
- return this.search(query, { ...options, cursor });
317
+ const { data, rateLimit } = await this.client.requestWithHeaders("/v1/twitter/tweets/advanced_search", {
318
+ params: {
319
+ query,
320
+ query_type: options.queryType ?? "Top",
321
+ count: options.count,
322
+ cursor
323
+ }
324
+ });
325
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
300
326
  };
301
327
  yield* paginate(fetchPage, options);
302
328
  }
@@ -340,10 +366,64 @@ var TweetsClient = class {
340
366
  */
341
367
  async *getUserTweetsAll(username, options = {}) {
342
368
  const fetchPage = async (cursor) => {
343
- return this.getUserTweets(username, { ...options, cursor });
369
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/latest_tweets`, { params: { cursor } });
370
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
344
371
  };
345
372
  yield* paginate(fetchPage, options);
346
373
  }
374
+ /**
375
+ * Get the edit history of a tweet.
376
+ *
377
+ * @param tweetId - The tweet ID to get edit history for.
378
+ * @returns Paginated response containing tweet versions.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const history = await client.twitter.tweets.getEditHistory("1234567890");
383
+ * console.log(`${history.data.length} version(s) of this tweet`);
384
+ * ```
385
+ */
386
+ async getEditHistory(tweetId) {
387
+ const response = await this.client.request(
388
+ `/v1/twitter/tweets/tweet/${tweetId}/edit_history`
389
+ );
390
+ return createPaginatedResponse(response.data ?? [], void 0);
391
+ }
392
+ /**
393
+ * Get community notes (Birdwatch) attached to a tweet.
394
+ *
395
+ * @param tweetId - The tweet ID to get community notes for.
396
+ * @returns Paginated response containing community notes.
397
+ *
398
+ * @example
399
+ * ```typescript
400
+ * const notes = await client.twitter.tweets.getCommunityNotes("1234567890");
401
+ * for (const note of notes.data) {
402
+ * console.log(note.text);
403
+ * }
404
+ * ```
405
+ */
406
+ async getCommunityNotes(tweetId) {
407
+ const response = await this.client.request(
408
+ `/v1/twitter/tweets/tweet/${tweetId}/community_notes`
409
+ );
410
+ return createPaginatedResponse(response.data ?? [], void 0);
411
+ }
412
+ /**
413
+ * Get a long-form article by its ID.
414
+ *
415
+ * @param articleId - The article ID to fetch.
416
+ * @returns The article data.
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const article = await client.twitter.tweets.getArticle("abc123");
421
+ * console.log(`${article.title}: ${article.text?.slice(0, 100)}...`);
422
+ * ```
423
+ */
424
+ async getArticle(articleId) {
425
+ return this.client.request(`/v1/twitter/tweets/article/${articleId}`);
426
+ }
347
427
  };
348
428
 
349
429
  // src/twitter/users.ts
@@ -450,7 +530,8 @@ var UsersClient = class {
450
530
  */
451
531
  async *getFollowersAll(username, options = {}) {
452
532
  const fetchPage = async (cursor) => {
453
- return this.getFollowers(username, { ...options, cursor });
533
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/followers`, { params: { cursor } });
534
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
454
535
  };
455
536
  yield* paginate(fetchPage, options);
456
537
  }
@@ -485,7 +566,8 @@ var UsersClient = class {
485
566
  */
486
567
  async *getFollowingAll(username, options = {}) {
487
568
  const fetchPage = async (cursor) => {
488
- return this.getFollowing(username, { ...options, cursor });
569
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/users/${username}/followings`, { params: { cursor } });
570
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
489
571
  };
490
572
  yield* paginate(fetchPage, options);
491
573
  }
@@ -648,10 +730,97 @@ var UsersClient = class {
648
730
  */
649
731
  async *searchAll(query, options = {}) {
650
732
  const fetchPage = async (cursor) => {
651
- return this.search(query, { ...options, cursor });
733
+ const { data, rateLimit } = await this.client.requestWithHeaders("/v1/twitter/users/search_users", { params: { query, cursor } });
734
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
652
735
  };
653
736
  yield* paginate(fetchPage, options);
654
737
  }
738
+ /**
739
+ * Get multiple users by their numeric IDs in a single request.
740
+ *
741
+ * @param userIds - List of user IDs to fetch.
742
+ * @returns Paginated response containing the matching users.
743
+ *
744
+ * @example
745
+ * ```typescript
746
+ * const users = await client.twitter.users.getByIds(["44196397", "783214"]);
747
+ * for (const user of users.data) {
748
+ * console.log(`@${user.username}`);
749
+ * }
750
+ * ```
751
+ */
752
+ async getByIds(userIds) {
753
+ const response = await this.client.request(
754
+ "/v1/twitter/users/batch_by_ids",
755
+ { params: { user_ids: userIds.join(",") } }
756
+ );
757
+ return createPaginatedResponse(response.data ?? [], void 0);
758
+ }
759
+ /**
760
+ * Get multiple users by their usernames in a single request.
761
+ *
762
+ * @param usernames - List of usernames (without @) to fetch.
763
+ * @returns Paginated response containing the matching users.
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * const users = await client.twitter.users.getByUsernames(["elonmusk", "twitter"]);
768
+ * for (const user of users.data) {
769
+ * console.log(`${user.name}: ${user.followers_count?.toLocaleString()} followers`);
770
+ * }
771
+ * ```
772
+ */
773
+ async getByUsernames(usernames) {
774
+ const response = await this.client.request(
775
+ "/v1/twitter/users/batch_by_usernames",
776
+ { params: { usernames: usernames.join(",") } }
777
+ );
778
+ return createPaginatedResponse(response.data ?? [], void 0);
779
+ }
780
+ /**
781
+ * Get tweets that mention a user.
782
+ *
783
+ * @param username - The user's username (without @).
784
+ * @param options - Pagination options with optional count.
785
+ * @returns Paginated response containing tweets mentioning the user.
786
+ *
787
+ * @example
788
+ * ```typescript
789
+ * const mentions = await client.twitter.users.getMentions("elonmusk");
790
+ * for (const tweet of mentions.data) {
791
+ * console.log(`@${tweet.username}: ${tweet.text.slice(0, 100)}...`);
792
+ * }
793
+ * ```
794
+ */
795
+ async getMentions(username, options = {}) {
796
+ const response = await this.client.request(
797
+ `/v1/twitter/users/${username}/mentions`,
798
+ { params: { count: options.count, cursor: options.cursor } }
799
+ );
800
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
801
+ }
802
+ /**
803
+ * Get long-form articles authored by a user.
804
+ *
805
+ * @param userId - The user's numeric ID.
806
+ * @param options - Pagination options with optional count.
807
+ * @returns Paginated response containing the user's articles as tweets.
808
+ *
809
+ * @example
810
+ * ```typescript
811
+ * const articles = await client.twitter.users.getArticles("44196397");
812
+ * for (const article of articles.data) {
813
+ * console.log(article.text?.slice(0, 100));
814
+ * }
815
+ * ```
816
+ */
817
+ async getArticles(userId, options = {}) {
818
+ const response = await this.client.request(
819
+ `/v1/twitter/users/${userId}/articles`,
820
+ { params: { count: options.count, cursor: options.cursor } }
821
+ );
822
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
823
+ }
655
824
  };
656
825
 
657
826
  // src/twitter/lists.ts
@@ -708,7 +877,8 @@ var ListsClient = class {
708
877
  */
709
878
  async *getTweetsAll(listId, options = {}) {
710
879
  const fetchPage = async (cursor) => {
711
- return this.getTweets(listId, { ...options, cursor });
880
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/lists/${listId}/tweets`, { params: { cursor } });
881
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
712
882
  };
713
883
  yield* paginate(fetchPage, options);
714
884
  }
@@ -743,7 +913,8 @@ var ListsClient = class {
743
913
  */
744
914
  async *getMembersAll(listId, options = {}) {
745
915
  const fetchPage = async (cursor) => {
746
- return this.getMembers(listId, { ...options, cursor });
916
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/lists/${listId}/members`, { params: { cursor } });
917
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
747
918
  };
748
919
  yield* paginate(fetchPage, options);
749
920
  }
@@ -796,6 +967,29 @@ var ListsClient = class {
796
967
  );
797
968
  return createPaginatedResponse(response.data ?? [], response.next_cursor);
798
969
  }
970
+ /**
971
+ * Search tweets within a specific list.
972
+ *
973
+ * @param listId - The list ID to search within.
974
+ * @param query - Search query string.
975
+ * @param options - Pagination options with optional count.
976
+ * @returns Paginated response containing matching tweets from the list.
977
+ *
978
+ * @example
979
+ * ```typescript
980
+ * const results = await client.twitter.lists.searchTweets("123456", "python");
981
+ * for (const tweet of results.data) {
982
+ * console.log(`@${tweet.username}: ${tweet.text.slice(0, 100)}...`);
983
+ * }
984
+ * ```
985
+ */
986
+ async searchTweets(listId, query, options = {}) {
987
+ const response = await this.client.request(
988
+ `/v1/twitter/lists/${listId}/search_tweets`,
989
+ { params: { query, count: options.count, cursor: options.cursor } }
990
+ );
991
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
992
+ }
799
993
  };
800
994
 
801
995
  // src/twitter/communities.ts
@@ -891,7 +1085,14 @@ var CommunitiesClient = class {
891
1085
  */
892
1086
  async *getTweetsAll(communityId, options = {}) {
893
1087
  const fetchPage = async (cursor) => {
894
- return this.getTweets(communityId, { ...options, cursor });
1088
+ const { data, rateLimit } = await this.client.requestWithHeaders(`/v1/twitter/communities/${communityId}/tweets`, {
1089
+ params: {
1090
+ tweet_type: options.tweetType ?? "Top",
1091
+ count: options.count ?? 40,
1092
+ cursor
1093
+ }
1094
+ });
1095
+ return { response: createPaginatedResponse(data.data ?? [], data.next_cursor), rateLimit };
895
1096
  };
896
1097
  yield* paginate(fetchPage, options);
897
1098
  }
@@ -1831,6 +2032,46 @@ function verifyWebhookSignature(secret, body, signatureHeader) {
1831
2032
  }
1832
2033
  }
1833
2034
 
2035
+ // src/twitter/spaces.ts
2036
+ var SpacesClient = class {
2037
+ client;
2038
+ constructor(client) {
2039
+ this.client = client;
2040
+ }
2041
+ /**
2042
+ * Get details for a specific Twitter Space.
2043
+ *
2044
+ * @param spaceId - The Space ID to fetch.
2045
+ * @returns The Space data.
2046
+ * @throws NotFoundError - If the Space doesn't exist.
2047
+ *
2048
+ * @example
2049
+ * ```typescript
2050
+ * const space = await client.twitter.spaces.getDetail("1eaKbrPPbPwKX");
2051
+ * console.log(`${space.title} — ${space.participant_count} participants`);
2052
+ * ```
2053
+ */
2054
+ async getDetail(spaceId) {
2055
+ return this.client.request(`/v1/twitter/spaces/${spaceId}`);
2056
+ }
2057
+ /**
2058
+ * Get details for a live video broadcast.
2059
+ *
2060
+ * @param broadcastId - The broadcast ID to fetch.
2061
+ * @returns The broadcast data.
2062
+ * @throws NotFoundError - If the broadcast doesn't exist.
2063
+ *
2064
+ * @example
2065
+ * ```typescript
2066
+ * const broadcast = await client.twitter.spaces.getBroadcast("broadcast123");
2067
+ * console.log(`${broadcast.title}: ${broadcast.total_viewers} viewers`);
2068
+ * ```
2069
+ */
2070
+ async getBroadcast(broadcastId) {
2071
+ return this.client.request(`/v1/twitter/spaces/broadcast/${broadcastId}`);
2072
+ }
2073
+ };
2074
+
1834
2075
  // src/twitter/client.ts
1835
2076
  var TwitterClient = class {
1836
2077
  /** Client for tweet operations */
@@ -1847,6 +2088,8 @@ var TwitterClient = class {
1847
2088
  geo;
1848
2089
  /** Client for real-time stream monitor management and WebSocket streaming */
1849
2090
  stream;
2091
+ /** Client for Twitter Spaces and live broadcast operations */
2092
+ spaces;
1850
2093
  /**
1851
2094
  * Create a new Twitter client.
1852
2095
  *
@@ -1860,12 +2103,14 @@ var TwitterClient = class {
1860
2103
  this.trends = new TrendsClient(client);
1861
2104
  this.geo = new GeoClient(client);
1862
2105
  this.stream = new StreamClient(client);
2106
+ this.spaces = new SpacesClient(client);
1863
2107
  }
1864
2108
  };
1865
2109
 
1866
2110
  exports.CommunitiesClient = CommunitiesClient;
1867
2111
  exports.GeoClient = GeoClient;
1868
2112
  exports.ListsClient = ListsClient;
2113
+ exports.SpacesClient = SpacesClient;
1869
2114
  exports.StreamClient = StreamClient;
1870
2115
  exports.TrendsClient = TrendsClient;
1871
2116
  exports.TweetsClient = TweetsClient;