x-twitter-scraper 0.10.5 → 0.10.6

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +49 -17
  4. data/lib/x_twitter_scraper/client.rb +0 -13
  5. data/lib/x_twitter_scraper/file_part.rb +0 -3
  6. data/lib/x_twitter_scraper/internal/transport/base_client.rb +3 -211
  7. data/lib/x_twitter_scraper/internal/transport/pooled_net_requester.rb +2 -56
  8. data/lib/x_twitter_scraper/internal/type/array_of.rb +1 -77
  9. data/lib/x_twitter_scraper/internal/type/base_model.rb +3 -133
  10. data/lib/x_twitter_scraper/internal/type/base_page.rb +0 -13
  11. data/lib/x_twitter_scraper/internal/type/boolean.rb +0 -38
  12. data/lib/x_twitter_scraper/internal/type/converter.rb +0 -136
  13. data/lib/x_twitter_scraper/internal/type/enum.rb +1 -80
  14. data/lib/x_twitter_scraper/internal/type/file_input.rb +0 -39
  15. data/lib/x_twitter_scraper/internal/type/hash_of.rb +1 -77
  16. data/lib/x_twitter_scraper/internal/type/request_parameters.rb +0 -13
  17. data/lib/x_twitter_scraper/internal/type/union.rb +0 -101
  18. data/lib/x_twitter_scraper/internal/type/unknown.rb +0 -40
  19. data/lib/x_twitter_scraper/internal/util.rb +2 -307
  20. data/lib/x_twitter_scraper/models/account_retrieve_response.rb +0 -3
  21. data/lib/x_twitter_scraper/models/compose_create_params.rb +0 -4
  22. data/lib/x_twitter_scraper/models/compose_create_response.rb +0 -4
  23. data/lib/x_twitter_scraper/models/credit_retrieve_balance_response.rb +0 -3
  24. data/lib/x_twitter_scraper/models/credit_topup_balance_response.rb +0 -3
  25. data/lib/x_twitter_scraper/models/draft_list_params.rb +0 -3
  26. data/lib/x_twitter_scraper/models/draw_export_params.rb +0 -3
  27. data/lib/x_twitter_scraper/models/draw_list_params.rb +0 -3
  28. data/lib/x_twitter_scraper/models/draw_run_response.rb +0 -3
  29. data/lib/x_twitter_scraper/models/embedded_tweet.rb +0 -3
  30. data/lib/x_twitter_scraper/models/event.rb +0 -3
  31. data/lib/x_twitter_scraper/models/event_list_params.rb +0 -3
  32. data/lib/x_twitter_scraper/models/extraction_estimate_cost_params.rb +0 -3
  33. data/lib/x_twitter_scraper/models/extraction_list_params.rb +0 -3
  34. data/lib/x_twitter_scraper/models/extraction_run_params.rb +0 -3
  35. data/lib/x_twitter_scraper/models/guest_wallet_create_response.rb +0 -3
  36. data/lib/x_twitter_scraper/models/guest_wallet_retrieve_status_response.rb +0 -3
  37. data/lib/x_twitter_scraper/models/guest_wallet_topup_response.rb +0 -3
  38. data/lib/x_twitter_scraper/models/radar_item.rb +0 -3
  39. data/lib/x_twitter_scraper/models/radar_retrieve_trending_topics_params.rb +0 -3
  40. data/lib/x_twitter_scraper/models/search_tweet.rb +0 -3
  41. data/lib/x_twitter_scraper/models/trend_list_params.rb +0 -3
  42. data/lib/x_twitter_scraper/models/webhook.rb +0 -3
  43. data/lib/x_twitter_scraper/models/x/communities/join_create_response.rb +0 -3
  44. data/lib/x_twitter_scraper/models/x/communities/join_delete_all_response.rb +0 -4
  45. data/lib/x_twitter_scraper/models/x/communities/tweet_list_by_community_params.rb +0 -4
  46. data/lib/x_twitter_scraper/models/x/communities/tweet_list_params.rb +0 -3
  47. data/lib/x_twitter_scraper/models/x/community_create_response.rb +0 -3
  48. data/lib/x_twitter_scraper/models/x/community_delete_response.rb +0 -3
  49. data/lib/x_twitter_scraper/models/x/community_retrieve_members_params.rb +0 -3
  50. data/lib/x_twitter_scraper/models/x/community_retrieve_moderators_params.rb +0 -4
  51. data/lib/x_twitter_scraper/models/x/community_retrieve_search_params.rb +0 -3
  52. data/lib/x_twitter_scraper/models/x/dm_retrieve_history_params.rb +0 -3
  53. data/lib/x_twitter_scraper/models/x/dm_retrieve_history_response.rb +0 -4
  54. data/lib/x_twitter_scraper/models/x/dm_send_response.rb +0 -3
  55. data/lib/x_twitter_scraper/models/x/list_retrieve_followers_params.rb +0 -3
  56. data/lib/x_twitter_scraper/models/x/list_retrieve_members_params.rb +0 -3
  57. data/lib/x_twitter_scraper/models/x/list_retrieve_tweets_params.rb +0 -3
  58. data/lib/x_twitter_scraper/models/x/media_upload_response.rb +0 -3
  59. data/lib/x_twitter_scraper/models/x/profile_update_avatar_response.rb +0 -3
  60. data/lib/x_twitter_scraper/models/x/profile_update_banner_response.rb +0 -3
  61. data/lib/x_twitter_scraper/models/x/profile_update_response.rb +0 -3
  62. data/lib/x_twitter_scraper/models/x/tweet_create_params.rb +0 -3
  63. data/lib/x_twitter_scraper/models/x/tweet_create_response.rb +0 -3
  64. data/lib/x_twitter_scraper/models/x/tweet_delete_response.rb +0 -3
  65. data/lib/x_twitter_scraper/models/x/tweet_detail.rb +0 -3
  66. data/lib/x_twitter_scraper/models/x/tweet_get_favoriters_params.rb +0 -3
  67. data/lib/x_twitter_scraper/models/x/tweet_get_quotes_params.rb +0 -3
  68. data/lib/x_twitter_scraper/models/x/tweet_get_replies_params.rb +0 -3
  69. data/lib/x_twitter_scraper/models/x/tweet_get_retweeters_params.rb +0 -3
  70. data/lib/x_twitter_scraper/models/x/tweet_get_thread_params.rb +0 -3
  71. data/lib/x_twitter_scraper/models/x/tweet_retrieve_response.rb +0 -3
  72. data/lib/x_twitter_scraper/models/x/tweet_search_params.rb +0 -3
  73. data/lib/x_twitter_scraper/models/x/tweets/like_create_response.rb +0 -3
  74. data/lib/x_twitter_scraper/models/x/tweets/like_delete_response.rb +0 -3
  75. data/lib/x_twitter_scraper/models/x/tweets/retweet_create_response.rb +0 -3
  76. data/lib/x_twitter_scraper/models/x/tweets/retweet_delete_response.rb +0 -3
  77. data/lib/x_twitter_scraper/models/x/user_remove_follower_response.rb +0 -3
  78. data/lib/x_twitter_scraper/models/x/user_retrieve_batch_params.rb +0 -3
  79. data/lib/x_twitter_scraper/models/x/user_retrieve_batch_response.rb +0 -3
  80. data/lib/x_twitter_scraper/models/x/user_retrieve_followers_params.rb +0 -3
  81. data/lib/x_twitter_scraper/models/x/user_retrieve_followers_you_know_params.rb +0 -4
  82. data/lib/x_twitter_scraper/models/x/user_retrieve_following_params.rb +0 -3
  83. data/lib/x_twitter_scraper/models/x/user_retrieve_likes_params.rb +0 -3
  84. data/lib/x_twitter_scraper/models/x/user_retrieve_media_params.rb +0 -3
  85. data/lib/x_twitter_scraper/models/x/user_retrieve_mentions_params.rb +0 -3
  86. data/lib/x_twitter_scraper/models/x/user_retrieve_replies_params.rb +0 -3
  87. data/lib/x_twitter_scraper/models/x/user_retrieve_search_params.rb +0 -3
  88. data/lib/x_twitter_scraper/models/x/user_retrieve_tweets_params.rb +0 -3
  89. data/lib/x_twitter_scraper/models/x/user_retrieve_verified_followers_params.rb +0 -4
  90. data/lib/x_twitter_scraper/models/x/users/follow_create_response.rb +0 -3
  91. data/lib/x_twitter_scraper/models/x/users/follow_delete_all_response.rb +0 -3
  92. data/lib/x_twitter_scraper/models/x/write_action_retrieve_response.rb +0 -3
  93. data/lib/x_twitter_scraper/models/x/x_account.rb +0 -3
  94. data/lib/x_twitter_scraper/models/x_get_article_response.rb +0 -4
  95. data/lib/x_twitter_scraper/request_options.rb +0 -2
  96. data/lib/x_twitter_scraper/resources/drafts.rb +0 -3
  97. data/lib/x_twitter_scraper/resources/draws.rb +0 -6
  98. data/lib/x_twitter_scraper/resources/events.rb +0 -3
  99. data/lib/x_twitter_scraper/resources/extractions.rb +0 -9
  100. data/lib/x_twitter_scraper/resources/guest_wallets.rb +0 -6
  101. data/lib/x_twitter_scraper/resources/radar.rb +0 -3
  102. data/lib/x_twitter_scraper/resources/support/tickets.rb +0 -6
  103. data/lib/x_twitter_scraper/resources/trends.rb +0 -3
  104. data/lib/x_twitter_scraper/resources/x/communities/join.rb +0 -6
  105. data/lib/x_twitter_scraper/resources/x/communities/tweets.rb +0 -7
  106. data/lib/x_twitter_scraper/resources/x/communities.rb +0 -16
  107. data/lib/x_twitter_scraper/resources/x/dm.rb +0 -6
  108. data/lib/x_twitter_scraper/resources/x/lists.rb +0 -9
  109. data/lib/x_twitter_scraper/resources/x/media.rb +0 -3
  110. data/lib/x_twitter_scraper/resources/x/profile.rb +0 -9
  111. data/lib/x_twitter_scraper/resources/x/tweets/like.rb +0 -6
  112. data/lib/x_twitter_scraper/resources/x/tweets/retweet.rb +0 -6
  113. data/lib/x_twitter_scraper/resources/x/tweets.rb +0 -24
  114. data/lib/x_twitter_scraper/resources/x/users/follow.rb +0 -6
  115. data/lib/x_twitter_scraper/resources/x/users.rb +0 -38
  116. data/lib/x_twitter_scraper/resources/x.rb +0 -3
  117. data/lib/x_twitter_scraper/version.rb +1 -1
  118. data/lib/x_twitter_scraper.rb +0 -5
  119. data/rbi/x_twitter_scraper/client.rbi +0 -4
  120. data/rbi/x_twitter_scraper/file_part.rbi +0 -1
  121. data/rbi/x_twitter_scraper/internal/transport/base_client.rbi +0 -19
  122. data/rbi/x_twitter_scraper/internal/transport/pooled_net_requester.rbi +0 -9
  123. data/rbi/x_twitter_scraper/internal/type/array_of.rbi +0 -10
  124. data/rbi/x_twitter_scraper/internal/type/base_model.rbi +0 -47
  125. data/rbi/x_twitter_scraper/internal/type/base_page.rbi +0 -4
  126. data/rbi/x_twitter_scraper/internal/type/boolean.rbi +0 -8
  127. data/rbi/x_twitter_scraper/internal/type/converter.rbi +0 -36
  128. data/rbi/x_twitter_scraper/internal/type/enum.rbi +0 -20
  129. data/rbi/x_twitter_scraper/internal/type/file_input.rbi +0 -10
  130. data/rbi/x_twitter_scraper/internal/type/hash_of.rbi +0 -10
  131. data/rbi/x_twitter_scraper/internal/type/request_parameters.rbi +0 -4
  132. data/rbi/x_twitter_scraper/internal/type/union.rbi +0 -18
  133. data/rbi/x_twitter_scraper/internal/type/unknown.rbi +0 -8
  134. data/rbi/x_twitter_scraper/internal/util.rbi +0 -75
  135. data/rbi/x_twitter_scraper/internal.rbi +0 -2
  136. data/rbi/x_twitter_scraper/request_options.rbi +0 -1
  137. data/sig/x_twitter_scraper/internal/transport/base_client.rbs +0 -1
  138. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43da66645ccb25ebf91b2fce7abd6f6e3c9ceaadf07a86086839fd29bb0e2b9f
4
- data.tar.gz: 57f8e8b8924975b71f79ed2f7a25e5d6b9a42024a11b489a14ec1a2a0536a016
3
+ metadata.gz: 46ffcafe78f6098cd45fa1a0f213515a2fb7d789f27ced4809ac735108a33084
4
+ data.tar.gz: a69999072784ab52f468234a7bfe343ad687c34dec12c78d19ecfb90b26ddccb
5
5
  SHA512:
6
- metadata.gz: 32e8fbf3f54f3717f81c99b7551a44ed0d572ce900f9bae41d259ac8f4a30111f89cdd49d4355989d52a9a34ea4e3e05daa98686357b829eda37886217eb2da7
7
- data.tar.gz: 71b43f3cfcc7a21d9c1999210eb0f283d273ff28664aeb2b2eb980c8509f49d618a1f06f91be66fc5d226c585f7ba700375618a98898483f1ca1a7bd7627668f
6
+ metadata.gz: 41655fce506cc950e43ff05a0387836d0b6476ac218988637e99e4bd6069b5f9cd541a9819f6623e4e1b8f71c2ce91bb302aba829703a7a31f847b4a1a300315
7
+ data.tar.gz: 910c622be7636cf6fe992ac2851849a12cd6cdf191a8a57d27eadffe9829c63c80434189a7209c6745e15bf476c6cd6dc58705961c47d0848c3e5b9c314c3d25
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.6 (2026-10-14)
4
+
5
+ [Full changelog from v0.10.5 to v0.10.6](https://github.com/Xquik-dev/x-twitter-scraper-ruby/compare/v0.10.5...v0.10.6)
6
+
7
+ The API and its behavior are the same as in 0.10.5.
8
+
9
+ ### Documentation
10
+
11
+ * The README uses sentence case headings, active voice and whole sentences. Its facts, links and legal lines stay the same.
12
+
13
+ ### Chores
14
+
15
+ * 24 development and test gems move to their latest stable release. The 3 runtime dependencies stay the same.
16
+
3
17
  ## 0.10.5 (2026-10-13)
4
18
 
5
19
  [Full changelog from v0.10.4 to v0.10.5](https://github.com/Xquik-dev/x-twitter-scraper-ruby/compare/v0.10.4...v0.10.5)
data/README.md CHANGED
@@ -14,11 +14,43 @@ Xquik charges only for delivered, unique, filter-matching results and never pads
14
14
  > Xquik is an independent third-party service. Not affiliated with X Corp.
15
15
  > "Twitter" and "X" are trademarks of X Corp.
16
16
 
17
- [Ruby SDK Guide](https://docs.xquik.com/sdks/ruby) | [REST API](https://docs.xquik.com/api-reference/overview) | [RubyDoc](https://gemdocs.org/gems/x-twitter-scraper) | [Webhooks](https://docs.xquik.com/api-reference/webhooks/create)
17
+ [Ruby SDK guide](https://docs.xquik.com/sdks/ruby) | [REST API](https://docs.xquik.com/api-reference/overview) | [RubyDoc](https://gemdocs.org/gems/x-twitter-scraper) | [Webhooks](https://docs.xquik.com/api-reference/webhooks/create)
18
+
19
+ <!-- trust:start -->
20
+
21
+ ## Trust & usage
22
+
23
+ [![Twitter API package downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fdownloads.json)](https://xquik.com/trust#downloads)
24
+ [![Twitter scraper users on Apify](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fapify-users.json)](https://apify.com/xquik)
25
+ [![Twitter scraper runs on Apify](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fapify-runs.json)](https://apify.com/xquik)
26
+ [![Tweet scraper rating on Apify](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fapify-rating.json)](https://apify.com/xquik/x-tweet-scraper/reviews)
27
+ [![Xquik Twitter API accounts](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Faccounts.json)](https://xquik.com/trust#accounts)
28
+ [![Twitter Skill installs](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fskill-installs.json)](https://xquik.com/trust#skill-installs)
29
+ [![Xquik verified revenue on TrustMRR](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Frevenue.json)](https://trustmrr.com/startup/xquik)
30
+ [![Twitter API median response time](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Frest-p50.json)](https://xquik.com/trust#speed)
31
+ [![Xquik GitHub stars](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Fgithub-stars.json)](https://github.com/Xquik-dev)
32
+ [![x-twitter-scraper downloads on RubyGems](https://img.shields.io/endpoint?url=https%3A%2F%2Fxquik.com%2Fbadges%2Frubygems-x-twitter-scraper.json)](https://rubygems.org/gems/x-twitter-scraper)
33
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13739/badge)](https://www.bestpractices.dev/projects/13739)
34
+
35
+ - 51,000+ downloads of Xquik's Twitter API packages on npm, PyPI, RubyGems, NuGet, Packagist & Terraform ([source](https://xquik.com/trust#downloads)) <!-- trust:downloads 51020 -->
36
+ - 7,800+ users of Xquik's Twitter scraper Actors on Apify ([source](https://apify.com/xquik)) <!-- trust:apify-users 7800 -->
37
+ - 5,700,000+ runs of Xquik's Twitter scraper Actors on Apify ([source](https://apify.com/xquik)) <!-- trust:apify-runs 5704701 -->
38
+ - 4.66 of 5 for the X Tweet Scraper from 16 Apify reviews ([source](https://apify.com/xquik/x-tweet-scraper/reviews)) <!-- trust:apify-rating 4.66 16 -->
39
+ - 3,900+ accounts on xquik.com ([source](https://xquik.com/trust#accounts)) <!-- trust:accounts 3924 -->
40
+ - 1,600+ installs of Xquik's Twitter Skills on skills.sh ([source](https://xquik.com/trust#skill-installs)) <!-- trust:skill-installs 1612 -->
41
+ - $38,000+ in revenue verified by TrustMRR ([source](https://trustmrr.com/startup/xquik)) <!-- trust:revenue 38115 -->
42
+ - 501 ms median REST API read time over the last 7 days ([source](https://xquik.com/trust#speed)) <!-- trust:rest-p50 501 -->
43
+ - 370+ GitHub stars across Xquik's repos ([source](https://github.com/Xquik-dev)) <!-- trust:github-stars 374 -->
44
+ - 9,900+ downloads of x-twitter-scraper on RubyGems ([source](https://rubygems.org/gems/x-twitter-scraper)) <!-- trust:rubygems-x-twitter-scraper 9982 -->
45
+ - This repo meets the OpenSSF Best Practices passing criteria ([source](https://www.bestpractices.dev/projects/13739)) <!-- trust:openssf-x-twitter-scraper-ruby 1 -->
46
+
47
+ Checked on 2026-10-15. See each number with its date & source on [xquik.com/trust](https://xquik.com/trust) or as [JSON](https://xquik.com/trust.json).
48
+
49
+ <!-- trust:end -->
18
50
 
19
51
  ## Common Twitter & X tasks
20
52
 
21
- | Task | REST Route | Usage |
53
+ | Task | REST route | Usage |
22
54
  | --- | --- | --- |
23
55
  | Search tweets without the X API | `GET /x/tweets/search` | Use keyword or advanced operator queries. |
24
56
  | Read an X profile timeline | `GET /x/users/{id}/tweets` | Paginate bounded results. |
@@ -32,18 +64,14 @@ Xquik charges only for delivered, unique, filter-matching results and never pads
32
64
 
33
65
  ## Installation
34
66
 
35
- Requires Ruby 3.2.0 or higher.
67
+ The SDK requires Ruby 3.2.0 or later.
36
68
 
37
69
  Add the gem to your `Gemfile`.
38
70
 
39
- <!-- x-release-please-start-version -->
40
-
41
71
  ```ruby
42
- gem "x-twitter-scraper", "~> 0.10.5"
72
+ gem "x-twitter-scraper", "~> 0.10.6"
43
73
  ```
44
74
 
45
- <!-- x-release-please-end -->
46
-
47
75
  ## Usage
48
76
 
49
77
  ```ruby
@@ -51,7 +79,7 @@ require "bundler/setup"
51
79
  require "x_twitter_scraper"
52
80
 
53
81
  x_twitter_scraper = XTwitterScraper::Client.new(
54
- api_key: ENV["X_TWITTER_SCRAPER_API_KEY"] # Optional; the client reads this variable.
82
+ api_key: ENV["X_TWITTER_SCRAPER_API_KEY"] # Optional. The client reads this variable.
55
83
  )
56
84
 
57
85
  response = x_twitter_scraper.x.tweets.search(q: "from:elonmusk", limit: 10)
@@ -77,7 +105,7 @@ rescue XTwitterScraper::Errors::APIStatusError => e
77
105
  end
78
106
  ```
79
107
 
80
- | Cause | Error Type |
108
+ | Cause | Error type |
81
109
  | ---------------- | -------------------------- |
82
110
  | HTTP 400 | `BadRequestError` |
83
111
  | HTTP 401 | `AuthenticationError` |
@@ -151,7 +179,7 @@ account =
151
179
  puts(account[:my_undocumented_property])
152
180
  ```
153
181
 
154
- Use `client.request` for undocumented endpoints while retaining authentication and retries.
182
+ Use `client.request` for undocumented endpoints. It keeps authentication and retries.
155
183
 
156
184
  ```ruby
157
185
  response = client.request(
@@ -167,7 +195,7 @@ response = client.request(
167
195
 
168
196
  Clients are thread-safe. Fork them only when no HTTP requests are in flight.
169
197
  Each client has an HTTP connection pool with a default size of 99.
170
- Reuse one client per application in most cases.
198
+ Reuse 1 client per application in most cases.
171
199
  Requests wait when every connection is busy. This wait counts toward the request timeout.
172
200
  Other SDK classes do not lock their data.
173
201
 
@@ -178,10 +206,8 @@ The gem ships [RBI](https://sorbet.org/docs/rbi) definitions without `sorbet-run
178
206
  Pass request hashes or typed parameter objects.
179
207
 
180
208
  ```ruby
181
- # Hashes work without type safety:
182
209
  x_twitter_scraper.x.tweets.search(q: "from:elonmusk", limit: 10)
183
210
 
184
- # Splat a complete Params object:
185
211
  params = XTwitterScraper::X::TweetSearchParams.new(q: "from:elonmusk", limit: 10)
186
212
  x_twitter_scraper.x.tweets.search(**params)
187
213
  ```
@@ -201,13 +227,11 @@ T.reveal_type(XTwitterScraper::AccountUpdateLocaleParams::Locale::EN)
201
227
  Enum parameters accept constants or literal values.
202
228
 
203
229
  ```ruby
204
- # Enum constants preserve tagged type information:
205
230
  x_twitter_scraper.account.update_locale(
206
231
  locale: XTwitterScraper::AccountUpdateLocaleParams::Locale::EN,
207
232
  # …
208
233
  )
209
234
 
210
- # Literal values also work:
211
235
  x_twitter_scraper.account.update_locale(
212
236
  locale: :en,
213
237
  # …
@@ -218,7 +242,15 @@ x_twitter_scraper.account.update_locale(
218
242
 
219
243
  This package follows [SemVer](https://semver.org/spec/v2.0.0.html). Version `0` releases may change APIs.
220
244
 
221
- Changes to non-runtime `*.rbi` and `*.rbs` definitions remain non-breaking.
245
+ Changes to non-runtime `*.rbi` and `*.rbs` definitions are non-breaking.
246
+
247
+ ## More Xquik tools
248
+
249
+ - [X (Twitter) scraper API & Twitter MCP server](https://github.com/Xquik-dev/x-twitter-scraper) with REST, MCP & Skill setup.
250
+ - Twitter API SDKs for [TypeScript](https://github.com/Xquik-dev/x-twitter-scraper-typescript), [Python](https://github.com/Xquik-dev/x-twitter-scraper-python), [Go](https://github.com/Xquik-dev/x-twitter-scraper-go), [Java](https://github.com/Xquik-dev/x-twitter-scraper-java), [Kotlin](https://github.com/Xquik-dev/x-twitter-scraper-kotlin), [C#](https://github.com/Xquik-dev/x-twitter-scraper-csharp) & [PHP](https://github.com/Xquik-dev/x-twitter-scraper-php).
251
+ - [Twitter CLI & X CLI](https://github.com/Xquik-dev/x-twitter-scraper-cli) for terminal workflows.
252
+ - [Twitter advanced search operators](https://github.com/Xquik-dev/x-twitter-advanced-search) with tested examples.
253
+ - [Xquik X API & Twitter scraper](https://xquik.com) and the [Twitter API documentation](https://docs.xquik.com/api-reference/overview).
222
254
 
223
255
  ## Help & contributing
224
256
 
@@ -88,33 +88,20 @@ module XTwitterScraper
88
88
  # @return [XTwitterScraper::Resources::GuestWallets]
89
89
  attr_reader :guest_wallets
90
90
 
91
- # @api private
92
- #
93
- # @param security [Hash{Symbol=>Boolean}]
94
- #
95
- # @return [Hash{String=>String}]
96
91
  private def auth_headers(security:)
97
92
  {auth_api_key:, oauth_bearer:}.slice(*security.keys).values.reduce({}, :merge)
98
93
  end
99
94
 
100
- # @api private
101
- #
102
- # @return [Hash{String=>String}]
103
95
  private def auth_api_key
104
96
  {"x-api-key" => @api_key}
105
97
  end
106
98
 
107
- # @api private
108
- #
109
- # @return [Hash{String=>String}]
110
99
  private def oauth_bearer
111
100
  return {} if @bearer_token.nil?
112
101
 
113
102
  {"authorization" => "Bearer #{@bearer_token}"}
114
103
  end
115
104
 
116
- # Creates and returns a new client for interacting with the API.
117
- #
118
105
  # @param api_key [String, nil] Defaults to `ENV["X_TWITTER_SCRAPER_API_KEY"]`
119
106
  #
120
107
  # @param bearer_token [String, nil] OAuth 2.1 access token Defaults to `ENV["X_TWITTER_SCRAPER_BEARER_TOKEN"]`
@@ -15,9 +15,6 @@ module XTwitterScraper
15
15
  # @return [String, nil]
16
16
  attr_reader :filename
17
17
 
18
- # @api private
19
- #
20
- # @return [String]
21
18
  private def read
22
19
  case content
23
20
  in Pathname
@@ -7,16 +7,12 @@
7
7
  module XTwitterScraper
8
8
  module Internal
9
9
  module Transport
10
- # @api private
11
- #
12
- # @abstract
13
10
  class BaseClient
14
11
  extend XTwitterScraper::Internal::Util::SorbetRuntimeSupport
15
12
 
16
- # from whatwg fetch spec
17
13
  MAX_REDIRECTS = 20
18
14
 
19
- # rubocop:disable Style/MutableConstant
15
+ # rubocop:disable-next Style/MutableConstant
20
16
  PLATFORM_HEADERS =
21
17
  {
22
18
  "x-stainless-arch" => XTwitterScraper::Internal::Util.arch,
@@ -26,14 +22,8 @@ module XTwitterScraper
26
22
  "x-stainless-runtime" => ::RUBY_ENGINE,
27
23
  "x-stainless-runtime-version" => ::RUBY_ENGINE_VERSION
28
24
  }
29
- # rubocop:enable Style/MutableConstant
30
25
 
31
26
  class << self
32
- # @api private
33
- #
34
- # @param req [Hash{Symbol=>Object}]
35
- #
36
- # @raise [ArgumentError]
37
27
  def validate!(req)
38
28
  keys = [
39
29
  :method,
@@ -60,50 +50,18 @@ module XTwitterScraper
60
50
  end
61
51
  end
62
52
 
63
- # @api private
64
- #
65
- # @param status [Integer]
66
- # @param headers [Hash{String=>String}]
67
- #
68
- # @return [Boolean]
69
53
  def should_retry?(status, headers:)
70
54
  coerced = XTwitterScraper::Internal::Util.coerce_boolean(headers["x-should-retry"])
71
55
  case [coerced, status]
72
56
  in [true | false, _]
73
57
  coerced
74
58
  in [_, 408 | 409 | 429 | (500..)]
75
- # retry on:
76
- # 408: timeouts
77
- # 409: locks
78
- # 429: rate limits
79
- # 500+: unknown errors
80
59
  true
81
60
  else
82
61
  false
83
62
  end
84
63
  end
85
64
 
86
- # @api private
87
- #
88
- # @param request [Hash{Symbol=>Object}] .
89
- #
90
- # @option request [Symbol] :method
91
- #
92
- # @option request [URI::Generic] :url
93
- #
94
- # @option request [Hash{String=>String}] :headers
95
- #
96
- # @option request [Object] :body
97
- #
98
- # @option request [Integer] :max_retries
99
- #
100
- # @option request [Float] :timeout
101
- #
102
- # @param status [Integer]
103
- #
104
- # @param response_headers [Hash{String=>String}]
105
- #
106
- # @return [Hash{Symbol=>Object}]
107
65
  def follow_redirect(request, status:, response_headers:)
108
66
  method, url, headers = request.fetch_values(:method, :url, :headers)
109
67
  location =
@@ -132,7 +90,6 @@ module XTwitterScraper
132
90
  nil
133
91
  end
134
92
 
135
- # from whatwg fetch spec
136
93
  case [status, method]
137
94
  in [301 | 302, :post] | [303, _]
138
95
  drop = %w[content-encoding content-language content-length content-location content-type]
@@ -145,7 +102,6 @@ module XTwitterScraper
145
102
  else
146
103
  end
147
104
 
148
- # from undici
149
105
  if XTwitterScraper::Internal::Util.uri_origin(url) != XTwitterScraper::Internal::Util.uri_origin(location)
150
106
  drop = %w[authorization cookie host proxy-authorization]
151
107
  request = {**request, headers: request.fetch(:headers).except(*drop)}
@@ -154,10 +110,6 @@ module XTwitterScraper
154
110
  request
155
111
  end
156
112
 
157
- # @api private
158
- #
159
- # @param status [Integer, XTwitterScraper::Errors::APIConnectionError]
160
- # @param stream [Enumerable<String>, nil]
161
113
  def reap_connection!(status, stream:)
162
114
  case status
163
115
  in (..199) | (300..499)
@@ -169,40 +121,8 @@ module XTwitterScraper
169
121
  end
170
122
  end
171
123
 
172
- # @return [URI::Generic]
173
- attr_reader :base_url
124
+ attr_reader :base_url, :timeout, :max_retries, :initial_retry_delay, :max_retry_delay, :headers, :idempotency_header, :requester
174
125
 
175
- # @return [Float]
176
- attr_reader :timeout
177
-
178
- # @return [Integer]
179
- attr_reader :max_retries
180
-
181
- # @return [Float]
182
- attr_reader :initial_retry_delay
183
-
184
- # @return [Float]
185
- attr_reader :max_retry_delay
186
-
187
- # @return [Hash{String=>String}]
188
- attr_reader :headers
189
-
190
- # @return [String, nil]
191
- attr_reader :idempotency_header
192
-
193
- # @api private
194
- # @return [XTwitterScraper::Internal::Transport::PooledNetRequester]
195
- attr_reader :requester
196
-
197
- # @api private
198
- #
199
- # @param base_url [String]
200
- # @param timeout [Float]
201
- # @param max_retries [Integer]
202
- # @param initial_retry_delay [Float]
203
- # @param max_retry_delay [Float]
204
- # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}]
205
- # @param idempotency_header [String, nil]
206
126
  def initialize(
207
127
  base_url:,
208
128
  timeout: 0.0,
@@ -231,60 +151,12 @@ module XTwitterScraper
231
151
  @max_retry_delay = max_retry_delay
232
152
  end
233
153
 
234
- # @api private
235
- #
236
- # @return [Hash{String=>String}]
237
154
  private def auth_headers = {}
238
155
 
239
- # @api private
240
- #
241
- # @return [String]
242
156
  private def user_agent = "#{self.class.name}/Ruby #{XTwitterScraper::VERSION}"
243
157
 
244
- # @api private
245
- #
246
- # @return [String]
247
158
  private def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
248
159
 
249
- # @api private
250
- #
251
- # @param req [Hash{Symbol=>Object}] .
252
- #
253
- # @option req [Symbol] :method
254
- #
255
- # @option req [String, Array<String>] :path
256
- #
257
- # @option req [Hash{String=>Array<String>, String, nil}, nil] :query
258
- #
259
- # @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
260
- #
261
- # @option req [Object, nil] :body
262
- #
263
- # @option req [Symbol, Integer, Array<Symbol, Integer>, Proc, nil] :unwrap
264
- #
265
- # @option req [Class<XTwitterScraper::Internal::Type::BasePage>, nil] :page
266
- #
267
- # @option req [Class<XTwitterScraper::Internal::Type::BaseStream>, nil] :stream
268
- #
269
- # @option req [XTwitterScraper::Internal::Type::Converter, Class, nil] :model
270
- #
271
- # @option req [Hash{Symbol=>Boolean}, nil] :security
272
- #
273
- # @param opts [Hash{Symbol=>Object}] .
274
- #
275
- # @option opts [String, nil] :idempotency_key
276
- #
277
- # @option opts [Hash{String=>Array<String>, String, nil}, nil] :extra_query
278
- #
279
- # @option opts [Hash{String=>String, nil}, nil] :extra_headers
280
- #
281
- # @option opts [Object, nil] :extra_body
282
- #
283
- # @option opts [Integer, nil] :max_retries
284
- #
285
- # @option opts [Float, nil] :timeout
286
- #
287
- # @return [Hash{Symbol=>Object}]
288
160
  private def build_request(req, opts)
289
161
  method, uninterpolated_path = req.fetch_values(:method, :path)
290
162
 
@@ -328,9 +200,6 @@ module XTwitterScraper
328
200
  XTwitterScraper::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
329
201
  end
330
202
 
331
- # Generated methods always pass `req[:body]` for operations that define a
332
- # request body, so only elide the content-type header when the operation
333
- # has no body at all, not when an optional body param was omitted.
334
203
  headers.delete("content-type") if body.nil? && !req.key?(:body)
335
204
 
336
205
  url = XTwitterScraper::Internal::Util.join_parsed_uri(
@@ -348,14 +217,7 @@ module XTwitterScraper
348
217
  }
349
218
  end
350
219
 
351
- # @api private
352
- #
353
- # @param headers [Hash{String=>String}]
354
- # @param retry_count [Integer]
355
- #
356
- # @return [Float]
357
220
  private def retry_delay(headers, retry_count:)
358
- # Non-standard extension
359
221
  span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
360
222
  return span if span
361
223
 
@@ -374,30 +236,6 @@ module XTwitterScraper
374
236
  (@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
375
237
  end
376
238
 
377
- # @api private
378
- #
379
- # @param request [Hash{Symbol=>Object}] .
380
- #
381
- # @option request [Symbol] :method
382
- #
383
- # @option request [URI::Generic] :url
384
- #
385
- # @option request [Hash{String=>String}] :headers
386
- #
387
- # @option request [Object] :body
388
- #
389
- # @option request [Integer] :max_retries
390
- #
391
- # @option request [Float] :timeout
392
- #
393
- # @param redirect_count [Integer]
394
- #
395
- # @param retry_count [Integer]
396
- #
397
- # @param send_retry_header [Boolean]
398
- #
399
- # @raise [XTwitterScraper::Errors::APIError]
400
- # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
401
239
  def send_request(request, redirect_count:, retry_count:, send_retry_header:)
402
240
  url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
403
241
  input = {**request.except(:timeout), deadline: XTwitterScraper::Internal::Util.monotonic_secs + timeout}
@@ -467,47 +305,6 @@ module XTwitterScraper
467
305
  end
468
306
  end
469
307
 
470
- # Execute the request specified by `req`. This is the method that all resource
471
- # methods call into.
472
- #
473
- # @overload request(method, path, query: {}, headers: {}, body: nil, unwrap: nil, page: nil, stream: nil, model: XTwitterScraper::Internal::Type::Unknown, security: {auth_api_key: true, oauth_bearer: true}, options: {})
474
- #
475
- # @param method [Symbol]
476
- #
477
- # @param path [String, Array<String>]
478
- #
479
- # @param query [Hash{String=>Array<String>, String, nil}, nil]
480
- #
481
- # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil]
482
- #
483
- # @param body [Object, nil]
484
- #
485
- # @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc, nil]
486
- #
487
- # @param page [Class<XTwitterScraper::Internal::Type::BasePage>, nil]
488
- #
489
- # @param stream [Class<XTwitterScraper::Internal::Type::BaseStream>, nil]
490
- #
491
- # @param model [XTwitterScraper::Internal::Type::Converter, Class, nil]
492
- #
493
- # @param security [Hash{Symbol=>Boolean}, nil]
494
- #
495
- # @param options [XTwitterScraper::RequestOptions, Hash{Symbol=>Object}, nil] .
496
- #
497
- # @option options [String, nil] :idempotency_key
498
- #
499
- # @option options [Hash{String=>Array<String>, String, nil}, nil] :extra_query
500
- #
501
- # @option options [Hash{String=>String, nil}, nil] :extra_headers
502
- #
503
- # @option options [Object, nil] :extra_body
504
- #
505
- # @option options [Integer, nil] :max_retries
506
- #
507
- # @option options [Float, nil] :timeout
508
- #
509
- # @raise [XTwitterScraper::Errors::APIError]
510
- # @return [Object]
511
308
  def request(req)
512
309
  self.class.validate!(req)
513
310
  model = req.fetch(:model) { XTwitterScraper::Internal::Type::Unknown }
@@ -517,7 +314,6 @@ module XTwitterScraper
517
314
  request = build_request(req.except(:options), opts)
518
315
  url = request.fetch(:url)
519
316
 
520
- # Don't send the current retry count in the headers if the caller modified the header defaults.
521
317
  send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0"
522
318
  status, response, stream = send_request(
523
319
  request,
@@ -547,13 +343,9 @@ module XTwitterScraper
547
343
  end
548
344
  end
549
345
 
550
- # @api private
551
- #
552
- # @return [String]
553
346
  def inspect
554
- # rubocop:disable Layout/LineLength
347
+ # rubocop:disable-next Layout/LineLength
555
348
  "#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{@base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"
556
- # rubocop:enable Layout/LineLength
557
349
  end
558
350
 
559
351
  define_sorbet_constant!(:RequestComponents) do
@@ -7,23 +7,14 @@
7
7
  module XTwitterScraper
8
8
  module Internal
9
9
  module Transport
10
- # @api private
11
10
  class PooledNetRequester
12
11
  extend XTwitterScraper::Internal::Util::SorbetRuntimeSupport
13
12
 
14
- # from the golang stdlib
15
- # https://github.com/golang/go/blob/c8eced8580028328fde7c03cbfcb720ce15b2358/src/net/http/transport.go#L49
16
13
  KEEP_ALIVE_TIMEOUT = 30
17
14
 
18
15
  DEFAULT_MAX_CONNECTIONS = [Etc.nprocessors, 99].max
19
16
 
20
17
  class << self
21
- # @api private
22
- #
23
- # @param cert_store [OpenSSL::X509::Store]
24
- # @param url [URI::Generic]
25
- #
26
- # @return [Net::HTTP]
27
18
  def connect(cert_store:, url:)
28
19
  port =
29
20
  case [url.port, url.scheme]
@@ -43,36 +34,18 @@ module XTwitterScraper
43
34
  end
44
35
  end
45
36
 
46
- # @api private
47
- #
48
- # @param conn [Net::HTTP]
49
- # @param deadline [Float]
50
37
  def calibrate_socket_timeout(conn, deadline)
51
38
  timeout = deadline - XTwitterScraper::Internal::Util.monotonic_secs
52
39
  conn.open_timeout = conn.read_timeout = conn.write_timeout = conn.continue_timeout = timeout
53
40
  end
54
41
 
55
- # @api private
56
- #
57
- # @param request [Hash{Symbol=>Object}] .
58
- #
59
- # @option request [Symbol] :method
60
- #
61
- # @option request [URI::Generic] :url
62
- #
63
- # @option request [Hash{String=>String}] :headers
64
- #
65
- # @param blk [Proc]
66
- #
67
- # @yieldparam [String]
68
- # @return [Array(Net::HTTPGenericRequest, Proc)]
69
42
  def build_request(request, &blk)
70
43
  method, url, headers, body = request.fetch_values(:method, :url, :headers, :body)
71
44
  req = Net::HTTPGenericRequest.new(
72
45
  method.to_s.upcase,
73
46
  !body.nil?,
74
47
  method != :head,
75
- URI(url.to_s) # ensure we construct a URI class of the right scheme
48
+ URI(url.to_s)
76
49
  )
77
50
 
78
51
  headers.each { req[_1] = _2 }
@@ -95,14 +68,6 @@ module XTwitterScraper
95
68
  end
96
69
  end
97
70
 
98
- # @api private
99
- #
100
- # @param url [URI::Generic]
101
- # @param deadline [Float]
102
- # @param blk [Proc]
103
- #
104
- # @raise [Timeout::Error]
105
- # @yieldparam [Net::HTTP]
106
71
  private def with_pool(url, deadline:, &blk)
107
72
  origin = XTwitterScraper::Internal::Util.uri_origin(url)
108
73
  timeout = deadline - XTwitterScraper::Internal::Util.monotonic_secs
@@ -116,28 +81,13 @@ module XTwitterScraper
116
81
  pool.with(timeout: timeout, &blk)
117
82
  end
118
83
 
119
- # @api private
120
- #
121
- # @param request [Hash{Symbol=>Object}] .
122
- #
123
- # @option request [Symbol] :method
124
- #
125
- # @option request [URI::Generic] :url
126
- #
127
- # @option request [Hash{String=>String}] :headers
128
- #
129
- # @option request [Object] :body
130
- #
131
- # @option request [Float] :deadline
132
- #
133
- # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
134
84
  def execute(request)
135
85
  url, deadline = request.fetch_values(:url, :deadline)
136
86
 
137
87
  req = nil
138
88
  finished = false
139
89
 
140
- # rubocop:disable Metrics/BlockLength
90
+ # rubocop:disable-next Metrics/BlockLength
141
91
  enum = Enumerator.new do |y|
142
92
  next if finished
143
93
 
@@ -185,7 +135,6 @@ module XTwitterScraper
185
135
  rescue StandardError
186
136
  raise XTwitterScraper::Errors::APIConnectionError.new(url: url, request: req)
187
137
  end
188
- # rubocop:enable Metrics/BlockLength
189
138
 
190
139
  _, response = enum.next
191
140
  body = XTwitterScraper::Internal::Util.fused_enum(enum, external: true) do
@@ -195,9 +144,6 @@ module XTwitterScraper
195
144
  [Integer(response.code), response, body]
196
145
  end
197
146
 
198
- # @api private
199
- #
200
- # @param size [Integer]
201
147
  def initialize(size: self.class::DEFAULT_MAX_CONNECTIONS)
202
148
  @mutex = Mutex.new
203
149
  @size = size