From aa06dd748fa884ccfa058fe5ed576d97fae978ef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:01:29 +0000 Subject: [PATCH 1/8] feat(api): api update --- .stats.yml | 8 +- README.md | 6 +- .../com/x_twitter_scraper/api/models/Error.kt | 48 + .../api/models/PaginatedTweets.kt | 7 +- .../api/models/PaginatedUsers.kt | 8 +- .../api/models/draws/DrawExportParams.kt | 15 +- .../api/models/draws/DrawListParams.kt | 4 +- .../api/models/events/EventListParams.kt | 38 +- .../ExtractionEstimateCostParams.kt | 11271 ++++++++++++---- .../ExtractionEstimateCostResponse.kt | 6 + .../ExtractionExportResultsParams.kt | 399 +- .../extractions/ExtractionListParams.kt | 4 +- .../extractions/ExtractionRetrieveParams.kt | 520 +- .../models/extractions/ExtractionRunParams.kt | 11095 +++++++++++---- .../extractions/ExtractionRunResponse.kt | 553 +- .../api/models/monitors/Monitor.kt | 233 +- .../attachments/AttachmentDownloadParams.kt | 214 + .../api/models/x/accounts/XAccount.kt | 21 +- .../CommunityRetrieveMembersParams.kt | 314 +- .../CommunityRetrieveModeratorsParams.kt | 313 +- .../x/lists/ListRetrieveFollowersParams.kt | 328 +- .../x/lists/ListRetrieveMembersParams.kt | 314 +- .../x/tweets/TweetGetFavoritersParams.kt | 328 +- .../models/x/tweets/TweetGetQuotesParams.kt | 384 +- .../models/x/tweets/TweetGetRepliesParams.kt | 871 +- .../x/tweets/TweetGetRepliesResponse.kt | 4 +- .../x/tweets/TweetGetRetweetersParams.kt | 328 +- .../api/models/x/tweets/TweetSearchParams.kt | 582 +- .../models/x/tweets/TweetSearchResponse.kt | 2730 ++++ .../x/users/UserRetrieveFollowersParams.kt | 514 +- .../x/users/UserRetrieveFollowersResponse.kt | 2224 +++ .../UserRetrieveFollowersYouKnowParams.kt | 328 +- .../x/users/UserRetrieveFollowingParams.kt | 514 +- .../x/users/UserRetrieveFollowingResponse.kt | 2224 +++ .../models/x/users/UserRetrieveLikesParams.kt | 384 +- .../models/x/users/UserRetrieveMediaParams.kt | 384 +- .../x/users/UserRetrieveMentionsParams.kt | 384 +- .../x/users/UserRetrieveRepliesParams.kt | 407 +- .../x/users/UserRetrieveSearchParams.kt | 314 +- .../x/users/UserRetrieveTweetsParams.kt | 407 +- .../UserRetrieveVerifiedFollowersParams.kt | 547 +- .../UserRetrieveVerifiedFollowersResponse.kt | 2232 +++ .../api/services/async/SupportServiceAsync.kt | 7 + .../services/async/SupportServiceAsyncImpl.kt | 16 + .../async/support/AttachmentServiceAsync.kt | 111 + .../support/AttachmentServiceAsyncImpl.kt | 74 + .../api/services/async/x/TweetServiceAsync.kt | 20 +- .../services/async/x/TweetServiceAsyncImpl.kt | 9 +- .../api/services/async/x/UserServiceAsync.kt | 98 +- .../services/async/x/UserServiceAsyncImpl.kt | 28 +- .../api/services/blocking/SupportService.kt | 7 + .../services/blocking/SupportServiceImpl.kt | 14 + .../blocking/support/AttachmentService.kt | 117 + .../blocking/support/AttachmentServiceImpl.kt | 72 + .../api/services/blocking/x/TweetService.kt | 19 +- .../services/blocking/x/TweetServiceImpl.kt | 9 +- .../api/services/blocking/x/UserService.kt | 106 +- .../services/blocking/x/UserServiceImpl.kt | 28 +- .../api/models/GeneratedModelContractTest.kt | 35 +- .../api/models/events/EventListParamsTest.kt | 3 + .../ExtractionEstimateCostParamsTest.kt | 207 + .../ExtractionExportResultsParamsTest.kt | 73 + .../ExtractionRetrieveParamsTest.kt | 32 +- .../extractions/ExtractionRunParamsTest.kt | 328 + .../extractions/ExtractionRunResponseTest.kt | 25 +- .../monitors/MonitorListResponseTest.kt | 6 + .../api/models/monitors/MonitorTest.kt | 6 + .../AttachmentDownloadParamsTest.kt | 49 + .../CommunityRetrieveMembersParamsTest.kt | 61 +- .../CommunityRetrieveModeratorsParamsTest.kt | 60 +- .../lists/ListRetrieveFollowersParamsTest.kt | 61 +- .../x/lists/ListRetrieveMembersParamsTest.kt | 61 +- .../x/tweets/TweetGetFavoritersParamsTest.kt | 61 +- .../x/tweets/TweetGetQuotesParamsTest.kt | 57 + .../x/tweets/TweetGetRepliesParamsTest.kt | 81 +- .../x/tweets/TweetGetRetweetersParamsTest.kt | 61 +- .../models/x/tweets/TweetSearchParamsTest.kt | 66 +- .../x/tweets/TweetSearchResponseTest.kt | 3597 +++++ .../users/UserRetrieveFollowersParamsTest.kt | 51 +- .../UserRetrieveFollowersResponseTest.kt | 480 + .../UserRetrieveFollowersYouKnowParamsTest.kt | 55 +- .../users/UserRetrieveFollowingParamsTest.kt | 51 +- .../UserRetrieveFollowingResponseTest.kt | 480 + .../x/users/UserRetrieveLikesParamsTest.kt | 57 + .../x/users/UserRetrieveMediaParamsTest.kt | 57 + .../x/users/UserRetrieveMentionsParamsTest.kt | 57 + .../x/users/UserRetrieveRepliesParamsTest.kt | 57 + .../x/users/UserRetrieveSearchParamsTest.kt | 60 +- .../x/users/UserRetrieveTweetsParamsTest.kt | 57 + ...UserRetrieveVerifiedFollowersParamsTest.kt | 58 +- ...erRetrieveVerifiedFollowersResponseTest.kt | 488 + .../api/services/ServiceParamsTest.kt | 22 +- .../services/async/EventServiceAsyncTest.kt | 1 + .../async/ExtractionServiceAsyncTest.kt | 157 +- .../support/AttachmentServiceAsyncTest.kt | 43 + .../async/x/CommunityServiceAsyncTest.kt | 33 +- .../services/async/x/ListServiceAsyncTest.kt | 34 +- .../services/async/x/TweetServiceAsyncTest.kt | 114 +- .../services/async/x/UserServiceAsyncTest.kt | 197 +- .../api/services/blocking/EventServiceTest.kt | 1 + .../blocking/ExtractionServiceTest.kt | 157 +- .../blocking/support/AttachmentServiceTest.kt | 42 + .../blocking/x/CommunityServiceTest.kt | 33 +- .../services/blocking/x/ListServiceTest.kt | 34 +- .../services/blocking/x/TweetServiceTest.kt | 112 +- .../services/blocking/x/UserServiceTest.kt | 191 +- .../api/proguard/ProGuardCompatibilityTest.kt | 1201 +- 107 files changed, 45958 insertions(+), 5896 deletions(-) create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParams.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponse.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponse.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponse.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponse.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsync.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncImpl.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentService.kt create mode 100644 x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceImpl.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParamsTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponseTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponseTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponseTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponseTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncTest.kt create mode 100644 x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceTest.kt diff --git a/.stats.yml b/.stats.yml index ec9dedf1..f192b6e9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 123 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-5f26f3694399b4ac5a4431c66f7d27c4024f9d0dcd899702de5fde1150e28330.yml -openapi_spec_hash: 362e24e622f9a74ded8b38018e50460c -config_hash: dab26921eb273c16e5570c0485c9ac2c +configured_endpoints: 124 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-7bcac1557175956021a62532c55ce29b8822b5a41d0d59ae67787a6326c0ef8a.yml +openapi_spec_hash: 04e28f19097d9b59119101492100c4ab +config_hash: 217d68ca6999ad03cfc4922e9658297e diff --git a/README.md b/README.md index 2adb17ab..75e4a5ba 100644 --- a/README.md +++ b/README.md @@ -136,15 +136,15 @@ System properties override environment variables. ## Search Posts ```java -import com.x_twitter_scraper.api.models.PaginatedTweets; import com.x_twitter_scraper.api.models.x.tweets.TweetSearchParams; +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse; TweetSearchParams params = TweetSearchParams.builder() .q("from:elonmusk") .limit(10L) .build(); -PaginatedTweets result = client.x().tweets().search(params); +TweetSearchResponse result = client.x().tweets().search(params); ``` Close the client when your application stops: @@ -160,7 +160,7 @@ Call `async()` to receive `CompletableFuture` results: ```java import java.util.concurrent.CompletableFuture; -CompletableFuture result = +CompletableFuture result = client.async().x().tweets().search(params); ``` diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt index 3cfe8c62..208f3c2c 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt @@ -616,6 +616,8 @@ private constructor( @JvmField val MONITOR_ALREADY_EXISTS = of("monitor_already_exists") + @JvmField val MONITOR_PROFILE_UNAVAILABLE = of("monitor_profile_unavailable") + @JvmField val NO_MEDIA = of("no_media") @JvmField val NO_CREDITS = of("no_credits") @@ -650,6 +652,10 @@ private constructor( @JvmField val CONNECTION_CHALLENGE_INACTIVE = of("connection_challenge_inactive") + @JvmField val COVERAGE_CURSOR_GONE = of("coverage_cursor_gone") + + @JvmField val COVERAGE_CURSOR_UNAVAILABLE = of("coverage_cursor_unavailable") + @JvmField val DRAFT_NOT_FOUND = of("draft_not_found") @JvmField val FAVORITERS_UNAVAILABLE = of("favoriters_unavailable") @@ -668,6 +674,8 @@ private constructor( @JvmField val INVALID_COMMUNITY_ID = of("invalid_community_id") + @JvmField val INVALID_COVERAGE_CURSOR = of("invalid_coverage_cursor") + @JvmField val INVALID_IDEMPOTENCY_KEY = of("invalid_idempotency_key") @JvmField val INVALID_LIST_ID = of("invalid_list_id") @@ -792,6 +800,7 @@ private constructor( MISSING_PARAMS, MISSING_QUERY, MONITOR_ALREADY_EXISTS, + MONITOR_PROFILE_UNAVAILABLE, NO_MEDIA, NO_CREDITS, NO_SUBSCRIPTION, @@ -809,6 +818,8 @@ private constructor( CHECKOUT_UNAVAILABLE, CONNECTION_CHALLENGE_EXPIRED, CONNECTION_CHALLENGE_INACTIVE, + COVERAGE_CURSOR_GONE, + COVERAGE_CURSOR_UNAVAILABLE, DRAFT_NOT_FOUND, FAVORITERS_UNAVAILABLE, FORBIDDEN, @@ -818,6 +829,7 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COVERAGE_CURSOR, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, INVALID_PAYMENT_AMOUNT, @@ -903,6 +915,7 @@ private constructor( MISSING_PARAMS, MISSING_QUERY, MONITOR_ALREADY_EXISTS, + MONITOR_PROFILE_UNAVAILABLE, NO_MEDIA, NO_CREDITS, NO_SUBSCRIPTION, @@ -920,6 +933,8 @@ private constructor( CHECKOUT_UNAVAILABLE, CONNECTION_CHALLENGE_EXPIRED, CONNECTION_CHALLENGE_INACTIVE, + COVERAGE_CURSOR_GONE, + COVERAGE_CURSOR_UNAVAILABLE, DRAFT_NOT_FOUND, FAVORITERS_UNAVAILABLE, FORBIDDEN, @@ -929,6 +944,7 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COVERAGE_CURSOR, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, INVALID_PAYMENT_AMOUNT, @@ -1018,6 +1034,7 @@ private constructor( MISSING_PARAMS -> Value.MISSING_PARAMS MISSING_QUERY -> Value.MISSING_QUERY MONITOR_ALREADY_EXISTS -> Value.MONITOR_ALREADY_EXISTS + MONITOR_PROFILE_UNAVAILABLE -> Value.MONITOR_PROFILE_UNAVAILABLE NO_MEDIA -> Value.NO_MEDIA NO_CREDITS -> Value.NO_CREDITS NO_SUBSCRIPTION -> Value.NO_SUBSCRIPTION @@ -1035,6 +1052,8 @@ private constructor( CHECKOUT_UNAVAILABLE -> Value.CHECKOUT_UNAVAILABLE CONNECTION_CHALLENGE_EXPIRED -> Value.CONNECTION_CHALLENGE_EXPIRED CONNECTION_CHALLENGE_INACTIVE -> Value.CONNECTION_CHALLENGE_INACTIVE + COVERAGE_CURSOR_GONE -> Value.COVERAGE_CURSOR_GONE + COVERAGE_CURSOR_UNAVAILABLE -> Value.COVERAGE_CURSOR_UNAVAILABLE DRAFT_NOT_FOUND -> Value.DRAFT_NOT_FOUND FAVORITERS_UNAVAILABLE -> Value.FAVORITERS_UNAVAILABLE FORBIDDEN -> Value.FORBIDDEN @@ -1044,6 +1063,7 @@ private constructor( IDEMPOTENCY_CONFLICT -> Value.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Value.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Value.INVALID_COMMUNITY_ID + INVALID_COVERAGE_CURSOR -> Value.INVALID_COVERAGE_CURSOR INVALID_IDEMPOTENCY_KEY -> Value.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Value.INVALID_LIST_ID INVALID_PAYMENT_AMOUNT -> Value.INVALID_PAYMENT_AMOUNT @@ -1131,6 +1151,7 @@ private constructor( MISSING_PARAMS -> Known.MISSING_PARAMS MISSING_QUERY -> Known.MISSING_QUERY MONITOR_ALREADY_EXISTS -> Known.MONITOR_ALREADY_EXISTS + MONITOR_PROFILE_UNAVAILABLE -> Known.MONITOR_PROFILE_UNAVAILABLE NO_MEDIA -> Known.NO_MEDIA NO_CREDITS -> Known.NO_CREDITS NO_SUBSCRIPTION -> Known.NO_SUBSCRIPTION @@ -1148,6 +1169,8 @@ private constructor( CHECKOUT_UNAVAILABLE -> Known.CHECKOUT_UNAVAILABLE CONNECTION_CHALLENGE_EXPIRED -> Known.CONNECTION_CHALLENGE_EXPIRED CONNECTION_CHALLENGE_INACTIVE -> Known.CONNECTION_CHALLENGE_INACTIVE + COVERAGE_CURSOR_GONE -> Known.COVERAGE_CURSOR_GONE + COVERAGE_CURSOR_UNAVAILABLE -> Known.COVERAGE_CURSOR_UNAVAILABLE DRAFT_NOT_FOUND -> Known.DRAFT_NOT_FOUND FAVORITERS_UNAVAILABLE -> Known.FAVORITERS_UNAVAILABLE FORBIDDEN -> Known.FORBIDDEN @@ -1157,6 +1180,7 @@ private constructor( IDEMPOTENCY_CONFLICT -> Known.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Known.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Known.INVALID_COMMUNITY_ID + INVALID_COVERAGE_CURSOR -> Known.INVALID_COVERAGE_CURSOR INVALID_IDEMPOTENCY_KEY -> Known.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Known.INVALID_LIST_ID INVALID_PAYMENT_AMOUNT -> Known.INVALID_PAYMENT_AMOUNT @@ -1564,6 +1588,8 @@ private constructor( @JvmField val MONITOR_ALREADY_EXISTS = of("monitor_already_exists") + @JvmField val MONITOR_PROFILE_UNAVAILABLE = of("monitor_profile_unavailable") + @JvmField val NO_MEDIA = of("no_media") @JvmField val NO_CREDITS = of("no_credits") @@ -1599,6 +1625,10 @@ private constructor( @JvmField val CONNECTION_CHALLENGE_INACTIVE = of("connection_challenge_inactive") + @JvmField val COVERAGE_CURSOR_GONE = of("coverage_cursor_gone") + + @JvmField val COVERAGE_CURSOR_UNAVAILABLE = of("coverage_cursor_unavailable") + @JvmField val DRAFT_NOT_FOUND = of("draft_not_found") @JvmField val FAVORITERS_UNAVAILABLE = of("favoriters_unavailable") @@ -1617,6 +1647,8 @@ private constructor( @JvmField val INVALID_COMMUNITY_ID = of("invalid_community_id") + @JvmField val INVALID_COVERAGE_CURSOR = of("invalid_coverage_cursor") + @JvmField val INVALID_IDEMPOTENCY_KEY = of("invalid_idempotency_key") @JvmField val INVALID_LIST_ID = of("invalid_list_id") @@ -1741,6 +1773,7 @@ private constructor( MISSING_PARAMS, MISSING_QUERY, MONITOR_ALREADY_EXISTS, + MONITOR_PROFILE_UNAVAILABLE, NO_MEDIA, NO_CREDITS, NO_SUBSCRIPTION, @@ -1758,6 +1791,8 @@ private constructor( CHECKOUT_UNAVAILABLE, CONNECTION_CHALLENGE_EXPIRED, CONNECTION_CHALLENGE_INACTIVE, + COVERAGE_CURSOR_GONE, + COVERAGE_CURSOR_UNAVAILABLE, DRAFT_NOT_FOUND, FAVORITERS_UNAVAILABLE, FORBIDDEN, @@ -1767,6 +1802,7 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COVERAGE_CURSOR, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, INVALID_PAYMENT_AMOUNT, @@ -1852,6 +1888,7 @@ private constructor( MISSING_PARAMS, MISSING_QUERY, MONITOR_ALREADY_EXISTS, + MONITOR_PROFILE_UNAVAILABLE, NO_MEDIA, NO_CREDITS, NO_SUBSCRIPTION, @@ -1869,6 +1906,8 @@ private constructor( CHECKOUT_UNAVAILABLE, CONNECTION_CHALLENGE_EXPIRED, CONNECTION_CHALLENGE_INACTIVE, + COVERAGE_CURSOR_GONE, + COVERAGE_CURSOR_UNAVAILABLE, DRAFT_NOT_FOUND, FAVORITERS_UNAVAILABLE, FORBIDDEN, @@ -1878,6 +1917,7 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COVERAGE_CURSOR, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, INVALID_PAYMENT_AMOUNT, @@ -1966,6 +2006,7 @@ private constructor( MISSING_PARAMS -> Value.MISSING_PARAMS MISSING_QUERY -> Value.MISSING_QUERY MONITOR_ALREADY_EXISTS -> Value.MONITOR_ALREADY_EXISTS + MONITOR_PROFILE_UNAVAILABLE -> Value.MONITOR_PROFILE_UNAVAILABLE NO_MEDIA -> Value.NO_MEDIA NO_CREDITS -> Value.NO_CREDITS NO_SUBSCRIPTION -> Value.NO_SUBSCRIPTION @@ -1983,6 +2024,8 @@ private constructor( CHECKOUT_UNAVAILABLE -> Value.CHECKOUT_UNAVAILABLE CONNECTION_CHALLENGE_EXPIRED -> Value.CONNECTION_CHALLENGE_EXPIRED CONNECTION_CHALLENGE_INACTIVE -> Value.CONNECTION_CHALLENGE_INACTIVE + COVERAGE_CURSOR_GONE -> Value.COVERAGE_CURSOR_GONE + COVERAGE_CURSOR_UNAVAILABLE -> Value.COVERAGE_CURSOR_UNAVAILABLE DRAFT_NOT_FOUND -> Value.DRAFT_NOT_FOUND FAVORITERS_UNAVAILABLE -> Value.FAVORITERS_UNAVAILABLE FORBIDDEN -> Value.FORBIDDEN @@ -1992,6 +2035,7 @@ private constructor( IDEMPOTENCY_CONFLICT -> Value.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Value.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Value.INVALID_COMMUNITY_ID + INVALID_COVERAGE_CURSOR -> Value.INVALID_COVERAGE_CURSOR INVALID_IDEMPOTENCY_KEY -> Value.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Value.INVALID_LIST_ID INVALID_PAYMENT_AMOUNT -> Value.INVALID_PAYMENT_AMOUNT @@ -2079,6 +2123,7 @@ private constructor( MISSING_PARAMS -> Known.MISSING_PARAMS MISSING_QUERY -> Known.MISSING_QUERY MONITOR_ALREADY_EXISTS -> Known.MONITOR_ALREADY_EXISTS + MONITOR_PROFILE_UNAVAILABLE -> Known.MONITOR_PROFILE_UNAVAILABLE NO_MEDIA -> Known.NO_MEDIA NO_CREDITS -> Known.NO_CREDITS NO_SUBSCRIPTION -> Known.NO_SUBSCRIPTION @@ -2096,6 +2141,8 @@ private constructor( CHECKOUT_UNAVAILABLE -> Known.CHECKOUT_UNAVAILABLE CONNECTION_CHALLENGE_EXPIRED -> Known.CONNECTION_CHALLENGE_EXPIRED CONNECTION_CHALLENGE_INACTIVE -> Known.CONNECTION_CHALLENGE_INACTIVE + COVERAGE_CURSOR_GONE -> Known.COVERAGE_CURSOR_GONE + COVERAGE_CURSOR_UNAVAILABLE -> Known.COVERAGE_CURSOR_UNAVAILABLE DRAFT_NOT_FOUND -> Known.DRAFT_NOT_FOUND FAVORITERS_UNAVAILABLE -> Known.FAVORITERS_UNAVAILABLE FORBIDDEN -> Known.FORBIDDEN @@ -2105,6 +2152,7 @@ private constructor( IDEMPOTENCY_CONFLICT -> Known.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Known.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Known.INVALID_COMMUNITY_ID + INVALID_COVERAGE_CURSOR -> Known.INVALID_COVERAGE_CURSOR INVALID_IDEMPOTENCY_KEY -> Known.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Known.INVALID_LIST_ID INVALID_PAYMENT_AMOUNT -> Known.INVALID_PAYMENT_AMOUNT diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedTweets.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedTweets.kt index 5c333815..6eaea7e9 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedTweets.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedTweets.kt @@ -23,9 +23,10 @@ import java.util.Objects import kotlin.jvm.optionals.getOrNull /** - * Paginated tweets. Source visibility, filters, or remaining credits can reduce results. An empty - * filtered page can still have has_next_page true. Follow next_cursor while has_next_page is true. - * Zero affordable results returns 402 insufficient_credits. + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. */ class PaginatedTweets @JsonCreator(mode = JsonCreator.Mode.DISABLED) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedUsers.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedUsers.kt index 85796db6..caf9fd51 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedUsers.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/PaginatedUsers.kt @@ -23,10 +23,10 @@ import java.util.Objects import kotlin.jvm.optionals.getOrNull /** - * Paginated user profiles. The item count can be lower than pageSize when the source returns fewer - * profiles or remaining credits cover fewer results. Follow next_cursor while has_next_page is - * true. A relationship can naturally contain fewer profiles than requested. Zero affordable results - * returns 402 insufficient_credits. + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy behavior. + * Follow next_cursor while has_next_page is true. */ class PaginatedUsers @JsonCreator(mode = JsonCreator.Mode.DISABLED) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawExportParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawExportParams.kt index f5c3010b..16dab493 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawExportParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawExportParams.kt @@ -30,7 +30,10 @@ private constructor( fun id(): Optional = Optional.ofNullable(id) - /** Export output format */ + /** + * Export output format. PDF entry exports include up to 10,000 rows. Other entry formats + * include up to 100,000 rows. + */ fun format(): Format = format /** Export winners or all entries */ @@ -80,7 +83,10 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) - /** Export output format */ + /** + * Export output format. PDF entry exports include up to 10,000 rows. Other entry formats + * include up to 100,000 rows. + */ fun format(format: Format) = apply { this.format = format } /** Export winners or all entries */ @@ -226,7 +232,10 @@ private constructor( } .build() - /** Export output format */ + /** + * Export output format. PDF entry exports include up to 10,000 rows. Other entry formats + * include up to 100,000 rows. + */ class Format @JsonCreator private constructor(private val value: JsonField) : Enum { /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawListParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawListParams.kt index 5a80b967..1148b8e0 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawListParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/draws/DrawListParams.kt @@ -22,7 +22,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(): Optional = Optional.ofNullable(cursor) /** @@ -64,7 +64,7 @@ private constructor( additionalQueryParams = drawListParams.additionalQueryParams.toBuilder() } - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/events/EventListParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/events/EventListParams.kt index 19987546..d6c4ee1a 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/events/EventListParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/events/EventListParams.kt @@ -19,18 +19,22 @@ class EventListParams private constructor( private val cursor: String?, private val eventType: EventType?, + private val keywordMonitorId: String?, private val limit: Long?, private val monitorId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(): Optional = Optional.ofNullable(cursor) /** Filter events by type */ fun eventType(): Optional = Optional.ofNullable(eventType) + /** Keyword monitor ID. */ + fun keywordMonitorId(): Optional = Optional.ofNullable(keywordMonitorId) + /** * Maximum number of items to return (1-100, default 50). For paid per-result endpoints, the * returned count may be lower when remaining credits cannot cover the requested page. If zero @@ -38,7 +42,7 @@ private constructor( */ fun limit(): Optional = Optional.ofNullable(limit) - /** Filter events by monitor ID */ + /** Account monitor ID. */ fun monitorId(): Optional = Optional.ofNullable(monitorId) /** Additional headers to send with the request. */ @@ -62,6 +66,7 @@ private constructor( private var cursor: String? = null private var eventType: EventType? = null + private var keywordMonitorId: String? = null private var limit: Long? = null private var monitorId: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() @@ -71,13 +76,14 @@ private constructor( internal fun from(eventListParams: EventListParams) = apply { cursor = eventListParams.cursor eventType = eventListParams.eventType + keywordMonitorId = eventListParams.keywordMonitorId limit = eventListParams.limit monitorId = eventListParams.monitorId additionalHeaders = eventListParams.additionalHeaders.toBuilder() additionalQueryParams = eventListParams.additionalQueryParams.toBuilder() } - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ @@ -89,6 +95,15 @@ private constructor( /** Alias for calling [Builder.eventType] with `eventType.orElse(null)`. */ fun eventType(eventType: Optional) = eventType(eventType.getOrNull()) + /** Keyword monitor ID. */ + fun keywordMonitorId(keywordMonitorId: String?) = apply { + this.keywordMonitorId = keywordMonitorId + } + + /** Alias for calling [Builder.keywordMonitorId] with `keywordMonitorId.orElse(null)`. */ + fun keywordMonitorId(keywordMonitorId: Optional) = + keywordMonitorId(keywordMonitorId.getOrNull()) + /** * Maximum number of items to return (1-100, default 50). For paid per-result endpoints, the * returned count may be lower when remaining credits cannot cover the requested page. If @@ -106,7 +121,7 @@ private constructor( /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ fun limit(limit: Optional) = limit(limit.getOrNull()) - /** Filter events by monitor ID */ + /** Account monitor ID. */ fun monitorId(monitorId: String?) = apply { this.monitorId = monitorId } /** Alias for calling [Builder.monitorId] with `monitorId.orElse(null)`. */ @@ -219,6 +234,7 @@ private constructor( EventListParams( cursor, eventType, + keywordMonitorId, limit, monitorId, additionalHeaders.build(), @@ -233,6 +249,7 @@ private constructor( .apply { cursor?.let { put("cursor", it) } eventType?.let { put("eventType", it.toString()) } + keywordMonitorId?.let { put("keywordMonitorId", it) } limit?.let { put("limit", it.toString()) } monitorId?.let { put("monitorId", it) } putAll(additionalQueryParams) @@ -247,6 +264,7 @@ private constructor( return other is EventListParams && cursor == other.cursor && eventType == other.eventType && + keywordMonitorId == other.keywordMonitorId && limit == other.limit && monitorId == other.monitorId && additionalHeaders == other.additionalHeaders && @@ -254,8 +272,16 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(cursor, eventType, limit, monitorId, additionalHeaders, additionalQueryParams) + Objects.hash( + cursor, + eventType, + keywordMonitorId, + limit, + monitorId, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "EventListParams{cursor=$cursor, eventType=$eventType, limit=$limit, monitorId=$monitorId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "EventListParams{cursor=$cursor, eventType=$eventType, keywordMonitorId=$keywordMonitorId, limit=$limit, monitorId=$monitorId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParams.kt index b94cdc37..61bd1248 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParams.kt @@ -10,17 +10,31 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer import com.x_twitter_scraper.api.core.Enum import com.x_twitter_scraper.api.core.ExcludeMissing import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.JsonMissing import com.x_twitter_scraper.api.core.JsonValue import com.x_twitter_scraper.api.core.Params +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.core.toImmutable import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.time.LocalDate +import java.time.OffsetDateTime import java.util.Collections import java.util.Objects import java.util.Optional @@ -43,7 +57,7 @@ private constructor( fun toolType(): ToolType = body.toolType() /** - * Raw advanced query string appended to the estimate (tweet_search_extractor) + * Raw advanced search query appended as-is (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -51,8 +65,8 @@ private constructor( fun advancedQuery(): Optional = body.advancedQuery() /** - * Alternative words or quoted phrases for estimated results. Separate with spaces, commas, or - * lines. + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + * (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -60,2816 +74,8165 @@ private constructor( fun anyWords(): Optional = body.anyWords() /** - * Geo bounding box used for estimation, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) + * Bio terms separated by commas or lines. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun boundingBox(): Optional = body.boundingBox() + fun bioContains(): Optional = body.bioContains() /** - * Cashtags applied to the estimate, separated by spaces, commas, or lines. + * Return only Blue-verified Tweet authors. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun cashtags(): Optional = body.cashtags() + fun blueVerifiedOnly(): Optional = body.blueVerifiedOnly() /** - * Conversation ID filter used for estimation (tweet_search_extractor) + * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun conversationId(): Optional = body.conversationId() + fun boundingBox(): Optional = body.boundingBox() /** - * Exact phrase filter for search estimation + * Match the Tweet card name. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun exactPhrase(): Optional = body.exactPhrase() + fun cardName(): Optional = body.cardName() /** - * Words or quoted phrases excluded from estimated results. Separate with spaces, commas, or - * lines. + * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun excludeWords(): Optional = body.excludeWords() + fun cashtags(): Optional = body.cashtags() /** - * Estimate only tweets from this author username (tweet_search_extractor) + * Reply collection strategy. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun fromUser(): Optional = body.fromUser() + fun collectionStrategy(): Optional = body.collectionStrategy() /** - * Hashtags applied to the estimate, separated by spaces, commas, or lines. + * Conversation ID filter (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun hashtags(): Optional = body.hashtags() + fun conversationId(): Optional = body.conversationId() /** - * Estimate only replies to this tweet ID (tweet_search_extractor) + * Merge duplicate results across collection targets. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun inReplyToTweetId(): Optional = body.inReplyToTweetId() + fun dedupeAcrossTargets(): Optional = body.dedupeAcrossTargets() /** - * Language code used for estimate filtering (tweet_search_extractor) + * Keep target duplicates, first rows, or merged overlap. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun language(): Optional = body.language() + fun dedupeMode(): Optional = body.dedupeMode() /** - * Estimate search results within this list ID (tweet_search_extractor) + * Exact phrase to match (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun listId(): Optional = body.listId() + fun exactPhrase(): Optional = body.exactPhrase() /** - * Media type used for estimate filtering (tweet_search_extractor) + * Exclude replies from the source author. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun mediaType(): Optional = body.mediaType() + fun excludeOriginalAuthor(): Optional = body.excludeOriginalAuthor() /** - * Estimate tweets mentioning this username (tweet_search_extractor) + * Exclude a source application. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun mentioning(): Optional = body.mentioning() + fun excludeSource(): Optional = body.excludeSource() /** - * Minimum likes threshold for estimated results (tweet_search_extractor) + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minFaves(): Optional = body.minFaves() + fun excludeWords(): Optional = body.excludeWords() /** - * Minimum quote count threshold for estimated results (tweet_search_extractor) + * Filter by author username (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minQuotes(): Optional = body.minQuotes() + fun fromUser(): Optional = body.fromUser() /** - * Minimum replies threshold for estimated results (tweet_search_extractor) + * Match latitude, longitude, and radius. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minReplies(): Optional = body.minReplies() + fun geocode(): Optional = body.geocode() /** - * Minimum retweets threshold for estimated results (tweet_search_extractor) + * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minRetweets(): Optional = body.minRetweets() + fun hashtags(): Optional = body.hashtags() /** - * Estimate search results within this place ID (tweet_search_extractor) + * Require a profile location. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun place(): Optional = body.place() + fun hasLocation(): Optional = body.hasLocation() /** - * Estimate search results within this country code (tweet_search_extractor) + * Return only replies with media. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun placeCountry(): Optional = body.placeCountry() + fun hasMediaOnly(): Optional = body.hasMediaOnly() /** - * Geo point radius used for estimation, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * Require a profile website. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun pointRadius(): Optional = body.pointRadius() + fun hasWebsite(): Optional = body.hasWebsite() /** - * Quote mode used for estimation (tweet_search_extractor) + * Include the source post in reply results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun quotes(): Optional = body.quotes() + fun includeOriginalPost(): Optional = body.includeOriginalPost() /** - * Estimate only quotes of this tweet ID (tweet_search_extractor) + * Add matching search terms to collection metadata. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun quotesOfTweetId(): Optional = body.quotesOfTweetId() + fun includeSearchTerms(): Optional = body.includeSearchTerms() /** - * Reply mode used for estimation (tweet_search_extractor) + * Add source target metadata to each result. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun replies(): Optional = body.replies() + fun includeTargetMetadata(): Optional = body.includeTargetMetadata() /** - * Maximum number of results to estimate. When set, the estimate caps projected results to this - * value. + * Only replies to this tweet ID (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun resultsLimit(): Optional = body.resultsLimit() + fun inReplyToTweetId(): Optional = body.inReplyToTweetId() /** - * Retweet mode used for estimation (tweet_search_extractor) + * Language code filter (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun retweets(): Optional = body.retweets() + fun language(): Optional = body.language() /** - * Estimate only retweets of this tweet ID (tweet_search_extractor) + * Search within a list ID (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun retweetsOfTweetId(): Optional = body.retweetsOfTweetId() + fun listId(): Optional = body.listId() /** - * Query used to price tweet_search_extractor or community_search. + * Required profile location text. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun searchQuery(): Optional = body.searchQuery() + fun locationContains(): Optional = body.locationContains() /** - * Estimate start date in YYYY-MM-DD format (tweet_search_extractor) + * Maximum nested reply depth. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun sinceDate(): Optional = body.sinceDate() + fun maxDepth(): Optional = body.maxDepth() /** - * Community ID used to price community_post_extractor or community_search. + * Maximum follower count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetCommunityId(): Optional = body.targetCommunityId() + fun maxFollowers(): Optional = body.maxFollowers() /** - * List ID used to price list_follower_explorer, list_member_extractor, or list_post_extractor. + * Maximum following count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetListId(): Optional = body.targetListId() + fun maxFollowing(): Optional = body.maxFollowing() /** - * Space ID used to price space_explorer. + * Return Tweets older than this Tweet ID. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetSpaceId(): Optional = body.targetSpaceId() + fun maxId(): Optional = body.maxId() /** + * Maximum results collected for each target. + * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetTweetId(): Optional = body.targetTweetId() + fun maxItemsPerTarget(): Optional = body.maxItemsPerTarget() /** + * Maximum Tweet like count. + * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetUsername(): Optional = body.targetUsername() + fun maxLikes(): Optional = body.maxLikes() /** - * Estimate replies sent to this username (tweet_search_extractor) + * Reply pages collected for each target. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun toUser(): Optional = body.toUser() + fun maxPagesPerTarget(): Optional = body.maxPagesPerTarget() /** - * Estimate end date in YYYY-MM-DD format (tweet_search_extractor) + * Maximum post count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun untilDate(): Optional = body.untilDate() + fun maxPosts(): Optional = body.maxPosts() /** - * URL substring or domain filter used for estimation (tweet_search_extractor) + * Maximum Tweet quote count. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun url(): Optional = body.url() + fun maxQuotes(): Optional = body.maxQuotes() /** - * Estimate only verified authors (tweet_search_extractor) + * Maximum Tweet reply count. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun verifiedOnly(): Optional = body.verifiedOnly() + fun maxReplies(): Optional = body.maxReplies() /** - * Returns the raw JSON value of [toolType]. + * Maximum Tweet repost count. * - * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _toolType(): JsonField = body._toolType() + fun maxRetweets(): Optional = body.maxRetweets() /** - * Returns the raw JSON value of [advancedQuery]. + * Media type filter (tweet_search_extractor) * - * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _advancedQuery(): JsonField = body._advancedQuery() + fun mediaType(): Optional = body.mediaType() /** - * Returns the raw JSON value of [anyWords]. + * Filter tweets mentioning a username (tweet_search_extractor) * - * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _anyWords(): JsonField = body._anyWords() + fun mentioning(): Optional = body.mentioning() /** - * Returns the raw JSON value of [boundingBox]. + * Minimum profile age in days. * - * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _boundingBox(): JsonField = body._boundingBox() + fun minAccountAgeDays(): Optional = body.minAccountAgeDays() /** - * Returns the raw JSON value of [cashtags]. + * Minimum Tweet bookmark count. * - * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _cashtags(): JsonField = body._cashtags() + fun minBookmarks(): Optional = body.minBookmarks() /** - * Returns the raw JSON value of [conversationId]. + * Minimum likes threshold (tweet_search_extractor) * - * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _conversationId(): JsonField = body._conversationId() + fun minFaves(): Optional = body.minFaves() /** - * Returns the raw JSON value of [exactPhrase]. + * Minimum follower count for profile results. * - * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _exactPhrase(): JsonField = body._exactPhrase() + fun minFollowers(): Optional = body.minFollowers() /** - * Returns the raw JSON value of [excludeWords]. + * Minimum following count for profile results. * - * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _excludeWords(): JsonField = body._excludeWords() + fun minFollowing(): Optional = body.minFollowing() /** - * Returns the raw JSON value of [fromUser]. + * Minimum post count for profile results. * - * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _fromUser(): JsonField = body._fromUser() + fun minPosts(): Optional = body.minPosts() /** - * Returns the raw JSON value of [hashtags]. + * Minimum quote count threshold (tweet_search_extractor) * - * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _hashtags(): JsonField = body._hashtags() + fun minQuotes(): Optional = body.minQuotes() /** - * Returns the raw JSON value of [inReplyToTweetId]. + * Minimum replies threshold (tweet_search_extractor) * - * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _inReplyToTweetId(): JsonField = body._inReplyToTweetId() + fun minReplies(): Optional = body.minReplies() /** - * Returns the raw JSON value of [language]. + * Minimum retweets threshold (tweet_search_extractor) * - * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _language(): JsonField = body._language() + fun minRetweets(): Optional = body.minRetweets() /** - * Returns the raw JSON value of [listId]. + * Minimum Tweet view count. * - * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _listId(): JsonField = body._listId() + fun minViews(): Optional = body.minViews() /** - * Returns the raw JSON value of [mediaType]. + * Only return native reposts. * - * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _mediaType(): JsonField = body._mediaType() + fun nativeRetweets(): Optional = body.nativeRetweets() /** - * Returns the raw JSON value of [mentioning]. + * Match a place name. * - * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _mentioning(): JsonField = body._mentioning() + fun near(): Optional = body.near() /** - * Returns the raw JSON value of [minFaves]. + * Only return news results. * - * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minFaves(): JsonField = body._minFaves() + fun news(): Optional = body.news() /** - * Returns the raw JSON value of [minQuotes]. + * Shortcut for dedupeMode=merge. * - * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minQuotes(): JsonField = body._minQuotes() + fun overlapMode(): Optional = body.overlapMode() /** - * Returns the raw JSON value of [minReplies]. + * Search within a place ID (tweet_search_extractor) * - * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minReplies(): JsonField = body._minReplies() + fun place(): Optional = body.place() /** - * Returns the raw JSON value of [minRetweets]. + * Search within a country code (tweet_search_extractor) * - * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minRetweets(): JsonField = body._minRetweets() + fun placeCountry(): Optional = body.placeCountry() /** - * Returns the raw JSON value of [place]. + * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) * - * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _place(): JsonField = body._place() + fun pointRadius(): Optional = body.pointRadius() /** - * Returns the raw JSON value of [placeCountry]. + * Search ranking applied to every query. * - * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _placeCountry(): JsonField = body._placeCountry() + fun queryType(): Optional = body.queryType() /** - * Returns the raw JSON value of [pointRadius]. + * Quote mode (tweet_search_extractor) * - * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _pointRadius(): JsonField = body._pointRadius() + fun quotes(): Optional = body.quotes() /** - * Returns the raw JSON value of [quotes]. + * Only quotes of this tweet ID (tweet_search_extractor) * - * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _quotes(): JsonField = body._quotes() + fun quotesOfTweetId(): Optional = body.quotesOfTweetId() /** - * Returns the raw JSON value of [quotesOfTweetId]. + * Profile relations processed within one job. * - * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _quotesOfTweetId(): JsonField = body._quotesOfTweetId() + fun relationTargets(): Optional> = body.relationTargets() /** - * Returns the raw JSON value of [replies]. + * Reply mode (tweet_search_extractor) * - * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _replies(): JsonField = body._replies() + fun replies(): Optional = body.replies() /** - * Returns the raw JSON value of [resultsLimit]. + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. * - * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _resultsLimit(): JsonField = body._resultsLimit() + fun resultsLimit(): Optional = body.resultsLimit() /** - * Returns the raw JSON value of [retweets]. + * Retweet mode (tweet_search_extractor) * - * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _retweets(): JsonField = body._retweets() + fun retweets(): Optional = body.retweets() /** - * Returns the raw JSON value of [retweetsOfTweetId]. + * Only retweets of this tweet ID (tweet_search_extractor) * - * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _retweetsOfTweetId(): JsonField = body._retweetsOfTweetId() + fun retweetsOfTweetId(): Optional = body.retweetsOfTweetId() /** - * Returns the raw JSON value of [searchQuery]. + * Enable the safe-search filter. * - * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _searchQuery(): JsonField = body._searchQuery() + fun safe(): Optional = body.safe() /** - * Returns the raw JSON value of [sinceDate]. + * Reply depth scope. * - * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _sinceDate(): JsonField = body._sinceDate() + fun scope(): Optional = body.scope() /** - * Returns the raw JSON value of [targetCommunityId]. + * Search queries processed as one collection job. * - * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetCommunityId(): JsonField = body._targetCommunityId() + fun searchQueries(): Optional> = body.searchQueries() /** - * Returns the raw JSON value of [targetListId]. + * Required for tweet_search_extractor & community_search. * - * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetListId(): JsonField = body._targetListId() + fun searchQuery(): Optional = body.searchQuery() /** - * Returns the raw JSON value of [targetSpaceId]. + * Start date YYYY-MM-DD (tweet_search_extractor) * - * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetSpaceId(): JsonField = body._targetSpaceId() + fun sinceDate(): Optional = body.sinceDate() /** - * Returns the raw JSON value of [targetTweetId]. + * Return Tweets newer than this Tweet ID. * - * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetTweetId(): JsonField = body._targetTweetId() + fun sinceId(): Optional = body.sinceId() /** - * Returns the raw JSON value of [targetUsername]. + * Reply start time as ISO 8601 or Unix seconds. * - * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetUsername(): JsonField = body._targetUsername() + fun sinceTime(): Optional = body.sinceTime() /** - * Returns the raw JSON value of [toUser]. + * Reply result order. * - * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _toUser(): JsonField = body._toUser() + fun sort(): Optional = body.sort() /** - * Returns the raw JSON value of [untilDate]. + * Match the source application. * - * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _untilDate(): JsonField = body._untilDate() + fun source(): Optional = body.source() /** - * Returns the raw JSON value of [url]. + * Resume one reply target from this cursor. * - * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. - */ - fun _url(): JsonField = body._url() + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun startCursor(): Optional = body.startCursor() /** - * Returns the raw JSON value of [verifiedOnly]. + * Required for community_post_extractor & community_search. * - * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _verifiedOnly(): JsonField = body._verifiedOnly() + fun targetCommunityId(): Optional = body.targetCommunityId() - fun _additionalBodyProperties(): Map = body._additionalProperties() + /** + * Community IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetCommunityIds(): Optional> = body.targetCommunityIds() - /** Additional headers to send with the request. */ - fun _additionalHeaders(): Headers = additionalHeaders + /** + * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetListId(): Optional = body.targetListId() - /** Additional query param to send with the request. */ - fun _additionalQueryParams(): QueryParams = additionalQueryParams + /** + * List IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetListIds(): Optional> = body.targetListIds() - fun toBuilder() = Builder().from(this) + /** + * Mixed targets auto-routed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targets(): Optional> = body.targets() - companion object { + /** + * Required for space_explorer. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetSpaceId(): Optional = body.targetSpaceId() - /** - * Returns a mutable builder for constructing an instance of [ExtractionEstimateCostParams]. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetTweetId(): Optional = body.targetTweetId() - /** A builder for [ExtractionEstimateCostParams]. */ - class Builder internal constructor() { + /** + * Tweet IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetTweetIds(): Optional> = body.targetTweetIds() - private var body: Body.Builder = Body.builder() - private var additionalHeaders: Headers.Builder = Headers.builder() - private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetUsername(): Optional = body.targetUsername() - @JvmSynthetic - internal fun from(extractionEstimateCostParams: ExtractionEstimateCostParams) = apply { - body = extractionEstimateCostParams.body.toBuilder() - additionalHeaders = extractionEstimateCostParams.additionalHeaders.toBuilder() - additionalQueryParams = extractionEstimateCostParams.additionalQueryParams.toBuilder() - } + /** + * Usernames processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetUsernames(): Optional> = body.targetUsernames() - /** - * Sets the entire request body. - * - * This is generally only useful if you are already constructing the body separately. - * Otherwise, it's more convenient to use the top-level setters instead: - * - [toolType] - * - [advancedQuery] - * - [anyWords] - * - [boundingBox] - * - [cashtags] - * - etc. - */ - fun body(body: Body) = apply { this.body = body.toBuilder() } + /** + * Filter replies sent to a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun toUser(): Optional = body.toUser() - /** Identifier for the extraction tool used to run a job. */ - fun toolType(toolType: ToolType) = apply { body.toolType(toolType) } + /** + * End date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun untilDate(): Optional = body.untilDate() - /** - * Sets [Builder.toolType] to an arbitrary JSON value. - * - * You should usually call [Builder.toolType] with a well-typed [ToolType] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun toolType(toolType: JsonField) = apply { body.toolType(toolType) } + /** + * Reply end time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun untilTime(): Optional = body.untilTime() - /** Raw advanced query string appended to the estimate (tweet_search_extractor) */ - fun advancedQuery(advancedQuery: String) = apply { body.advancedQuery(advancedQuery) } + /** + * URL substring or domain filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun url(): Optional = body.url() - /** - * Sets [Builder.advancedQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.advancedQuery] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun advancedQuery(advancedQuery: JsonField) = apply { - body.advancedQuery(advancedQuery) - } + /** + * Required username text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun usernameContains(): Optional = body.usernameContains() - /** - * Alternative words or quoted phrases for estimated results. Separate with spaces, commas, - * or lines. - */ - fun anyWords(anyWords: String) = apply { body.anyWords(anyWords) } + /** + * Only verified authors (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun verifiedOnly(): Optional = body.verifiedOnly() - /** - * Sets [Builder.anyWords] to an arbitrary JSON value. - * - * You should usually call [Builder.anyWords] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun anyWords(anyWords: JsonField) = apply { body.anyWords(anyWords) } + /** + * Exact profile verification type. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun verifiedType(): Optional = body.verifiedType() - /** - * Geo bounding box used for estimation, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) - */ - fun boundingBox(boundingBox: String) = apply { body.boundingBox(boundingBox) } + /** + * Set the radius for the near filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun within(): Optional = body.within() - /** - * Sets [Builder.boundingBox] to an arbitrary JSON value. - * - * You should usually call [Builder.boundingBox] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun boundingBox(boundingBox: JsonField) = apply { body.boundingBox(boundingBox) } + /** + * Match Tweets inside a recent time window. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun withinTime(): Optional = body.withinTime() - /** Cashtags applied to the estimate, separated by spaces, commas, or lines. */ - fun cashtags(cashtags: String) = apply { body.cashtags(cashtags) } + /** + * Returns the raw JSON value of [toolType]. + * + * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _toolType(): JsonField = body._toolType() - /** - * Sets [Builder.cashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.cashtags] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun cashtags(cashtags: JsonField) = apply { body.cashtags(cashtags) } + /** + * Returns the raw JSON value of [advancedQuery]. + * + * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _advancedQuery(): JsonField = body._advancedQuery() - /** Conversation ID filter used for estimation (tweet_search_extractor) */ - fun conversationId(conversationId: String) = apply { body.conversationId(conversationId) } + /** + * Returns the raw JSON value of [anyWords]. + * + * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _anyWords(): JsonField = body._anyWords() - /** - * Sets [Builder.conversationId] to an arbitrary JSON value. - * - * You should usually call [Builder.conversationId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun conversationId(conversationId: JsonField) = apply { - body.conversationId(conversationId) - } + /** + * Returns the raw JSON value of [bioContains]. + * + * Unlike [bioContains], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _bioContains(): JsonField = body._bioContains() - /** Exact phrase filter for search estimation */ - fun exactPhrase(exactPhrase: String) = apply { body.exactPhrase(exactPhrase) } + /** + * Returns the raw JSON value of [blueVerifiedOnly]. + * + * Unlike [blueVerifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _blueVerifiedOnly(): JsonField = body._blueVerifiedOnly() - /** - * Sets [Builder.exactPhrase] to an arbitrary JSON value. - * - * You should usually call [Builder.exactPhrase] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun exactPhrase(exactPhrase: JsonField) = apply { body.exactPhrase(exactPhrase) } + /** + * Returns the raw JSON value of [boundingBox]. + * + * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _boundingBox(): JsonField = body._boundingBox() - /** - * Words or quoted phrases excluded from estimated results. Separate with spaces, commas, or - * lines. - */ - fun excludeWords(excludeWords: String) = apply { body.excludeWords(excludeWords) } + /** + * Returns the raw JSON value of [cardName]. + * + * Unlike [cardName], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _cardName(): JsonField = body._cardName() - /** - * Sets [Builder.excludeWords] to an arbitrary JSON value. - * - * You should usually call [Builder.excludeWords] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun excludeWords(excludeWords: JsonField) = apply { - body.excludeWords(excludeWords) - } - - /** Estimate only tweets from this author username (tweet_search_extractor) */ - fun fromUser(fromUser: String) = apply { body.fromUser(fromUser) } + /** + * Returns the raw JSON value of [cashtags]. + * + * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _cashtags(): JsonField = body._cashtags() - /** - * Sets [Builder.fromUser] to an arbitrary JSON value. - * - * You should usually call [Builder.fromUser] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun fromUser(fromUser: JsonField) = apply { body.fromUser(fromUser) } + /** + * Returns the raw JSON value of [collectionStrategy]. + * + * Unlike [collectionStrategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _collectionStrategy(): JsonField = body._collectionStrategy() - /** Hashtags applied to the estimate, separated by spaces, commas, or lines. */ - fun hashtags(hashtags: String) = apply { body.hashtags(hashtags) } + /** + * Returns the raw JSON value of [conversationId]. + * + * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _conversationId(): JsonField = body._conversationId() - /** - * Sets [Builder.hashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.hashtags] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun hashtags(hashtags: JsonField) = apply { body.hashtags(hashtags) } + /** + * Returns the raw JSON value of [dedupeAcrossTargets]. + * + * Unlike [dedupeAcrossTargets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _dedupeAcrossTargets(): JsonField = body._dedupeAcrossTargets() - /** Estimate only replies to this tweet ID (tweet_search_extractor) */ - fun inReplyToTweetId(inReplyToTweetId: String) = apply { - body.inReplyToTweetId(inReplyToTweetId) - } + /** + * Returns the raw JSON value of [dedupeMode]. + * + * Unlike [dedupeMode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _dedupeMode(): JsonField = body._dedupeMode() - /** - * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { - body.inReplyToTweetId(inReplyToTweetId) - } + /** + * Returns the raw JSON value of [exactPhrase]. + * + * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _exactPhrase(): JsonField = body._exactPhrase() - /** Language code used for estimate filtering (tweet_search_extractor) */ - fun language(language: String) = apply { body.language(language) } + /** + * Returns the raw JSON value of [excludeOriginalAuthor]. + * + * Unlike [excludeOriginalAuthor], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _excludeOriginalAuthor(): JsonField = body._excludeOriginalAuthor() - /** - * Sets [Builder.language] to an arbitrary JSON value. - * - * You should usually call [Builder.language] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun language(language: JsonField) = apply { body.language(language) } + /** + * Returns the raw JSON value of [excludeSource]. + * + * Unlike [excludeSource], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _excludeSource(): JsonField = body._excludeSource() - /** Estimate search results within this list ID (tweet_search_extractor) */ - fun listId(listId: String) = apply { body.listId(listId) } + /** + * Returns the raw JSON value of [excludeWords]. + * + * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _excludeWords(): JsonField = body._excludeWords() - /** - * Sets [Builder.listId] to an arbitrary JSON value. - * - * You should usually call [Builder.listId] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun listId(listId: JsonField) = apply { body.listId(listId) } + /** + * Returns the raw JSON value of [fromUser]. + * + * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _fromUser(): JsonField = body._fromUser() - /** Media type used for estimate filtering (tweet_search_extractor) */ - fun mediaType(mediaType: MediaType) = apply { body.mediaType(mediaType) } + /** + * Returns the raw JSON value of [geocode]. + * + * Unlike [geocode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _geocode(): JsonField = body._geocode() - /** - * Sets [Builder.mediaType] to an arbitrary JSON value. - * - * You should usually call [Builder.mediaType] with a well-typed [MediaType] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun mediaType(mediaType: JsonField) = apply { body.mediaType(mediaType) } + /** + * Returns the raw JSON value of [hashtags]. + * + * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hashtags(): JsonField = body._hashtags() - /** Estimate tweets mentioning this username (tweet_search_extractor) */ - fun mentioning(mentioning: String) = apply { body.mentioning(mentioning) } + /** + * Returns the raw JSON value of [hasLocation]. + * + * Unlike [hasLocation], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasLocation(): JsonField = body._hasLocation() - /** - * Sets [Builder.mentioning] to an arbitrary JSON value. - * - * You should usually call [Builder.mentioning] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun mentioning(mentioning: JsonField) = apply { body.mentioning(mentioning) } + /** + * Returns the raw JSON value of [hasMediaOnly]. + * + * Unlike [hasMediaOnly], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasMediaOnly(): JsonField = body._hasMediaOnly() - /** Minimum likes threshold for estimated results (tweet_search_extractor) */ - fun minFaves(minFaves: Long) = apply { body.minFaves(minFaves) } + /** + * Returns the raw JSON value of [hasWebsite]. + * + * Unlike [hasWebsite], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasWebsite(): JsonField = body._hasWebsite() - /** - * Sets [Builder.minFaves] to an arbitrary JSON value. - * - * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minFaves(minFaves: JsonField) = apply { body.minFaves(minFaves) } + /** + * Returns the raw JSON value of [includeOriginalPost]. + * + * Unlike [includeOriginalPost], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeOriginalPost(): JsonField = body._includeOriginalPost() - /** Minimum quote count threshold for estimated results (tweet_search_extractor) */ - fun minQuotes(minQuotes: Long) = apply { body.minQuotes(minQuotes) } + /** + * Returns the raw JSON value of [includeSearchTerms]. + * + * Unlike [includeSearchTerms], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeSearchTerms(): JsonField = body._includeSearchTerms() - /** - * Sets [Builder.minQuotes] to an arbitrary JSON value. - * - * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minQuotes(minQuotes: JsonField) = apply { body.minQuotes(minQuotes) } + /** + * Returns the raw JSON value of [includeTargetMetadata]. + * + * Unlike [includeTargetMetadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeTargetMetadata(): JsonField = body._includeTargetMetadata() - /** Minimum replies threshold for estimated results (tweet_search_extractor) */ - fun minReplies(minReplies: Long) = apply { body.minReplies(minReplies) } + /** + * Returns the raw JSON value of [inReplyToTweetId]. + * + * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _inReplyToTweetId(): JsonField = body._inReplyToTweetId() - /** - * Sets [Builder.minReplies] to an arbitrary JSON value. - * - * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minReplies(minReplies: JsonField) = apply { body.minReplies(minReplies) } + /** + * Returns the raw JSON value of [language]. + * + * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _language(): JsonField = body._language() - /** Minimum retweets threshold for estimated results (tweet_search_extractor) */ - fun minRetweets(minRetweets: Long) = apply { body.minRetweets(minRetweets) } + /** + * Returns the raw JSON value of [listId]. + * + * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _listId(): JsonField = body._listId() - /** - * Sets [Builder.minRetweets] to an arbitrary JSON value. - * - * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun minRetweets(minRetweets: JsonField) = apply { body.minRetweets(minRetweets) } + /** + * Returns the raw JSON value of [locationContains]. + * + * Unlike [locationContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _locationContains(): JsonField = body._locationContains() - /** Estimate search results within this place ID (tweet_search_extractor) */ - fun place(place: String) = apply { body.place(place) } + /** + * Returns the raw JSON value of [maxDepth]. + * + * Unlike [maxDepth], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxDepth(): JsonField = body._maxDepth() - /** - * Sets [Builder.place] to an arbitrary JSON value. - * - * You should usually call [Builder.place] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun place(place: JsonField) = apply { body.place(place) } + /** + * Returns the raw JSON value of [maxFollowers]. + * + * Unlike [maxFollowers], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxFollowers(): JsonField = body._maxFollowers() - /** Estimate search results within this country code (tweet_search_extractor) */ - fun placeCountry(placeCountry: String) = apply { body.placeCountry(placeCountry) } + /** + * Returns the raw JSON value of [maxFollowing]. + * + * Unlike [maxFollowing], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxFollowing(): JsonField = body._maxFollowing() - /** - * Sets [Builder.placeCountry] to an arbitrary JSON value. - * - * You should usually call [Builder.placeCountry] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun placeCountry(placeCountry: JsonField) = apply { - body.placeCountry(placeCountry) - } + /** + * Returns the raw JSON value of [maxId]. + * + * Unlike [maxId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxId(): JsonField = body._maxId() - /** Geo point radius used for estimation, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ - fun pointRadius(pointRadius: String) = apply { body.pointRadius(pointRadius) } + /** + * Returns the raw JSON value of [maxItemsPerTarget]. + * + * Unlike [maxItemsPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _maxItemsPerTarget(): JsonField = body._maxItemsPerTarget() - /** - * Sets [Builder.pointRadius] to an arbitrary JSON value. - * - * You should usually call [Builder.pointRadius] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun pointRadius(pointRadius: JsonField) = apply { body.pointRadius(pointRadius) } + /** + * Returns the raw JSON value of [maxLikes]. + * + * Unlike [maxLikes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxLikes(): JsonField = body._maxLikes() - /** Quote mode used for estimation (tweet_search_extractor) */ - fun quotes(quotes: Quotes) = apply { body.quotes(quotes) } + /** + * Returns the raw JSON value of [maxPagesPerTarget]. + * + * Unlike [maxPagesPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _maxPagesPerTarget(): JsonField = body._maxPagesPerTarget() - /** - * Sets [Builder.quotes] to an arbitrary JSON value. - * - * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun quotes(quotes: JsonField) = apply { body.quotes(quotes) } + /** + * Returns the raw JSON value of [maxPosts]. + * + * Unlike [maxPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxPosts(): JsonField = body._maxPosts() - /** Estimate only quotes of this tweet ID (tweet_search_extractor) */ - fun quotesOfTweetId(quotesOfTweetId: String) = apply { - body.quotesOfTweetId(quotesOfTweetId) - } + /** + * Returns the raw JSON value of [maxQuotes]. + * + * Unlike [maxQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxQuotes(): JsonField = body._maxQuotes() - /** - * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { - body.quotesOfTweetId(quotesOfTweetId) - } + /** + * Returns the raw JSON value of [maxReplies]. + * + * Unlike [maxReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxReplies(): JsonField = body._maxReplies() - /** Reply mode used for estimation (tweet_search_extractor) */ - fun replies(replies: Replies) = apply { body.replies(replies) } + /** + * Returns the raw JSON value of [maxRetweets]. + * + * Unlike [maxRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxRetweets(): JsonField = body._maxRetweets() - /** - * Sets [Builder.replies] to an arbitrary JSON value. - * - * You should usually call [Builder.replies] with a well-typed [Replies] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun replies(replies: JsonField) = apply { body.replies(replies) } + /** + * Returns the raw JSON value of [mediaType]. + * + * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _mediaType(): JsonField = body._mediaType() - /** - * Maximum number of results to estimate. When set, the estimate caps projected results to - * this value. - */ - fun resultsLimit(resultsLimit: Long) = apply { body.resultsLimit(resultsLimit) } + /** + * Returns the raw JSON value of [mentioning]. + * + * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _mentioning(): JsonField = body._mentioning() - /** - * Sets [Builder.resultsLimit] to an arbitrary JSON value. - * - * You should usually call [Builder.resultsLimit] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun resultsLimit(resultsLimit: JsonField) = apply { body.resultsLimit(resultsLimit) } + /** + * Returns the raw JSON value of [minAccountAgeDays]. + * + * Unlike [minAccountAgeDays], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _minAccountAgeDays(): JsonField = body._minAccountAgeDays() - /** Retweet mode used for estimation (tweet_search_extractor) */ - fun retweets(retweets: Retweets) = apply { body.retweets(retweets) } + /** + * Returns the raw JSON value of [minBookmarks]. + * + * Unlike [minBookmarks], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minBookmarks(): JsonField = body._minBookmarks() - /** - * Sets [Builder.retweets] to an arbitrary JSON value. - * - * You should usually call [Builder.retweets] with a well-typed [Retweets] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun retweets(retweets: JsonField) = apply { body.retweets(retweets) } + /** + * Returns the raw JSON value of [minFaves]. + * + * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFaves(): JsonField = body._minFaves() - /** Estimate only retweets of this tweet ID (tweet_search_extractor) */ - fun retweetsOfTweetId(retweetsOfTweetId: String) = apply { - body.retweetsOfTweetId(retweetsOfTweetId) - } + /** + * Returns the raw JSON value of [minFollowers]. + * + * Unlike [minFollowers], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFollowers(): JsonField = body._minFollowers() - /** - * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { - body.retweetsOfTweetId(retweetsOfTweetId) - } + /** + * Returns the raw JSON value of [minFollowing]. + * + * Unlike [minFollowing], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFollowing(): JsonField = body._minFollowing() - /** Query used to price tweet_search_extractor or community_search. */ - fun searchQuery(searchQuery: String) = apply { body.searchQuery(searchQuery) } + /** + * Returns the raw JSON value of [minPosts]. + * + * Unlike [minPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minPosts(): JsonField = body._minPosts() - /** - * Sets [Builder.searchQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.searchQuery] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun searchQuery(searchQuery: JsonField) = apply { body.searchQuery(searchQuery) } + /** + * Returns the raw JSON value of [minQuotes]. + * + * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minQuotes(): JsonField = body._minQuotes() - /** Estimate start date in YYYY-MM-DD format (tweet_search_extractor) */ - fun sinceDate(sinceDate: LocalDate) = apply { body.sinceDate(sinceDate) } + /** + * Returns the raw JSON value of [minReplies]. + * + * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minReplies(): JsonField = body._minReplies() - /** - * Sets [Builder.sinceDate] to an arbitrary JSON value. - * - * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun sinceDate(sinceDate: JsonField) = apply { body.sinceDate(sinceDate) } + /** + * Returns the raw JSON value of [minRetweets]. + * + * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minRetweets(): JsonField = body._minRetweets() - /** Community ID used to price community_post_extractor or community_search. */ - fun targetCommunityId(targetCommunityId: String) = apply { - body.targetCommunityId(targetCommunityId) - } + /** + * Returns the raw JSON value of [minViews]. + * + * Unlike [minViews], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minViews(): JsonField = body._minViews() - /** - * Sets [Builder.targetCommunityId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetCommunityId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetCommunityId(targetCommunityId: JsonField) = apply { - body.targetCommunityId(targetCommunityId) - } + /** + * Returns the raw JSON value of [nativeRetweets]. + * + * Unlike [nativeRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _nativeRetweets(): JsonField = body._nativeRetweets() - /** - * List ID used to price list_follower_explorer, list_member_extractor, or - * list_post_extractor. - */ - fun targetListId(targetListId: String) = apply { body.targetListId(targetListId) } + /** + * Returns the raw JSON value of [near]. + * + * Unlike [near], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _near(): JsonField = body._near() - /** - * Sets [Builder.targetListId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetListId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetListId(targetListId: JsonField) = apply { - body.targetListId(targetListId) - } + /** + * Returns the raw JSON value of [news]. + * + * Unlike [news], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _news(): JsonField = body._news() - /** Space ID used to price space_explorer. */ - fun targetSpaceId(targetSpaceId: String) = apply { body.targetSpaceId(targetSpaceId) } + /** + * Returns the raw JSON value of [overlapMode]. + * + * Unlike [overlapMode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _overlapMode(): JsonField = body._overlapMode() - /** - * Sets [Builder.targetSpaceId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetSpaceId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetSpaceId(targetSpaceId: JsonField) = apply { - body.targetSpaceId(targetSpaceId) - } + /** + * Returns the raw JSON value of [place]. + * + * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _place(): JsonField = body._place() - fun targetTweetId(targetTweetId: String) = apply { body.targetTweetId(targetTweetId) } + /** + * Returns the raw JSON value of [placeCountry]. + * + * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _placeCountry(): JsonField = body._placeCountry() - /** - * Sets [Builder.targetTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetTweetId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetTweetId(targetTweetId: JsonField) = apply { - body.targetTweetId(targetTweetId) - } + /** + * Returns the raw JSON value of [pointRadius]. + * + * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _pointRadius(): JsonField = body._pointRadius() - fun targetUsername(targetUsername: String) = apply { body.targetUsername(targetUsername) } + /** + * Returns the raw JSON value of [queryType]. + * + * Unlike [queryType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _queryType(): JsonField = body._queryType() - /** - * Sets [Builder.targetUsername] to an arbitrary JSON value. - * - * You should usually call [Builder.targetUsername] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetUsername(targetUsername: JsonField) = apply { - body.targetUsername(targetUsername) - } + /** + * Returns the raw JSON value of [quotes]. + * + * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _quotes(): JsonField = body._quotes() - /** Estimate replies sent to this username (tweet_search_extractor) */ - fun toUser(toUser: String) = apply { body.toUser(toUser) } + /** + * Returns the raw JSON value of [quotesOfTweetId]. + * + * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _quotesOfTweetId(): JsonField = body._quotesOfTweetId() - /** - * Sets [Builder.toUser] to an arbitrary JSON value. - * - * You should usually call [Builder.toUser] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun toUser(toUser: JsonField) = apply { body.toUser(toUser) } - - /** Estimate end date in YYYY-MM-DD format (tweet_search_extractor) */ - fun untilDate(untilDate: LocalDate) = apply { body.untilDate(untilDate) } + /** + * Returns the raw JSON value of [relationTargets]. + * + * Unlike [relationTargets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _relationTargets(): JsonField> = body._relationTargets() - /** - * Sets [Builder.untilDate] to an arbitrary JSON value. - * - * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun untilDate(untilDate: JsonField) = apply { body.untilDate(untilDate) } + /** + * Returns the raw JSON value of [replies]. + * + * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _replies(): JsonField = body._replies() - /** URL substring or domain filter used for estimation (tweet_search_extractor) */ - fun url(url: String) = apply { body.url(url) } + /** + * Returns the raw JSON value of [resultsLimit]. + * + * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _resultsLimit(): JsonField = body._resultsLimit() - /** - * Sets [Builder.url] to an arbitrary JSON value. - * - * You should usually call [Builder.url] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun url(url: JsonField) = apply { body.url(url) } + /** + * Returns the raw JSON value of [retweets]. + * + * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _retweets(): JsonField = body._retweets() - /** Estimate only verified authors (tweet_search_extractor) */ - fun verifiedOnly(verifiedOnly: Boolean) = apply { body.verifiedOnly(verifiedOnly) } + /** + * Returns the raw JSON value of [retweetsOfTweetId]. + * + * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _retweetsOfTweetId(): JsonField = body._retweetsOfTweetId() - /** - * Sets [Builder.verifiedOnly] to an arbitrary JSON value. - * - * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun verifiedOnly(verifiedOnly: JsonField) = apply { - body.verifiedOnly(verifiedOnly) - } + /** + * Returns the raw JSON value of [safe]. + * + * Unlike [safe], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _safe(): JsonField = body._safe() - fun additionalBodyProperties(additionalBodyProperties: Map) = apply { - body.additionalProperties(additionalBodyProperties) - } + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _scope(): JsonField = body._scope() - fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { - body.putAdditionalProperty(key, value) - } + /** + * Returns the raw JSON value of [searchQueries]. + * + * Unlike [searchQueries], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _searchQueries(): JsonField> = body._searchQueries() - fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = - apply { - body.putAllAdditionalProperties(additionalBodyProperties) - } + /** + * Returns the raw JSON value of [searchQuery]. + * + * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _searchQuery(): JsonField = body._searchQuery() - fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + /** + * Returns the raw JSON value of [sinceDate]. + * + * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceDate(): JsonField = body._sinceDate() - fun removeAllAdditionalBodyProperties(keys: Set) = apply { - body.removeAllAdditionalProperties(keys) - } + /** + * Returns the raw JSON value of [sinceId]. + * + * Unlike [sinceId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceId(): JsonField = body._sinceId() - fun additionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } + /** + * Returns the raw JSON value of [sinceTime]. + * + * Unlike [sinceTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceTime(): JsonField = body._sinceTime() - fun additionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } + /** + * Returns the raw JSON value of [sort]. + * + * Unlike [sort], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sort(): JsonField = body._sort() - fun putAdditionalHeader(name: String, value: String) = apply { - additionalHeaders.put(name, value) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _source(): JsonField = body._source() - fun putAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.put(name, values) - } + /** + * Returns the raw JSON value of [startCursor]. + * + * Unlike [startCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _startCursor(): JsonField = body._startCursor() - fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetCommunityId]. + * + * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _targetCommunityId(): JsonField = body._targetCommunityId() - fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetCommunityIds]. + * + * Unlike [targetCommunityIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _targetCommunityIds(): JsonField> = body._targetCommunityIds() - fun replaceAdditionalHeaders(name: String, value: String) = apply { - additionalHeaders.replace(name, value) - } + /** + * Returns the raw JSON value of [targetListId]. + * + * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetListId(): JsonField = body._targetListId() - fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.replace(name, values) - } + /** + * Returns the raw JSON value of [targetListIds]. + * + * Unlike [targetListIds], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetListIds(): JsonField> = body._targetListIds() - fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targets]. + * + * Unlike [targets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targets(): JsonField> = body._targets() - fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetSpaceId]. + * + * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetSpaceId(): JsonField = body._targetSpaceId() - fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + /** + * Returns the raw JSON value of [targetTweetId]. + * + * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetTweetId(): JsonField = body._targetTweetId() - fun removeAllAdditionalHeaders(names: Set) = apply { - additionalHeaders.removeAll(names) - } + /** + * Returns the raw JSON value of [targetTweetIds]. + * + * Unlike [targetTweetIds], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetTweetIds(): JsonField> = body._targetTweetIds() - fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } + /** + * Returns the raw JSON value of [targetUsername]. + * + * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetUsername(): JsonField = body._targetUsername() - fun additionalQueryParams(additionalQueryParams: Map>) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } + /** + * Returns the raw JSON value of [targetUsernames]. + * + * Unlike [targetUsernames], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetUsernames(): JsonField> = body._targetUsernames() - fun putAdditionalQueryParam(key: String, value: String) = apply { - additionalQueryParams.put(key, value) - } + /** + * Returns the raw JSON value of [toUser]. + * + * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _toUser(): JsonField = body._toUser() - fun putAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.put(key, values) - } + /** + * Returns the raw JSON value of [untilDate]. + * + * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _untilDate(): JsonField = body._untilDate() - fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [untilTime]. + * + * Unlike [untilTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _untilTime(): JsonField = body._untilTime() - fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _url(): JsonField = body._url() - fun replaceAdditionalQueryParams(key: String, value: String) = apply { - additionalQueryParams.replace(key, value) - } + /** + * Returns the raw JSON value of [usernameContains]. + * + * Unlike [usernameContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _usernameContains(): JsonField = body._usernameContains() - fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.replace(key, values) - } + /** + * Returns the raw JSON value of [verifiedOnly]. + * + * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _verifiedOnly(): JsonField = body._verifiedOnly() - fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [verifiedType]. + * + * Unlike [verifiedType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _verifiedType(): JsonField = body._verifiedType() - fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [within]. + * + * Unlike [within], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _within(): JsonField = body._within() - fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + /** + * Returns the raw JSON value of [withinTime]. + * + * Unlike [withinTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _withinTime(): JsonField = body._withinTime() - fun removeAllAdditionalQueryParams(keys: Set) = apply { - additionalQueryParams.removeAll(keys) - } + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { /** - * Returns an immutable instance of [ExtractionEstimateCostParams]. - * - * Further updates to this [Builder] will not mutate the returned instance. + * Returns a mutable builder for constructing an instance of [ExtractionEstimateCostParams]. * * The following fields are required: * ```java * .toolType() * ``` - * - * @throws IllegalStateException if any required field is unset. */ - fun build(): ExtractionEstimateCostParams = - ExtractionEstimateCostParams( - body.build(), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + @JvmStatic fun builder() = Builder() } - fun _body(): Body = body - - override fun _headers(): Headers = additionalHeaders - - override fun _queryParams(): QueryParams = additionalQueryParams + /** A builder for [ExtractionEstimateCostParams]. */ + class Builder internal constructor() { - class Body - @JsonCreator(mode = JsonCreator.Mode.DISABLED) - private constructor( - private val toolType: JsonField, - private val advancedQuery: JsonField, - private val anyWords: JsonField, - private val boundingBox: JsonField, - private val cashtags: JsonField, - private val conversationId: JsonField, - private val exactPhrase: JsonField, - private val excludeWords: JsonField, - private val fromUser: JsonField, - private val hashtags: JsonField, - private val inReplyToTweetId: JsonField, - private val language: JsonField, - private val listId: JsonField, - private val mediaType: JsonField, - private val mentioning: JsonField, - private val minFaves: JsonField, - private val minQuotes: JsonField, - private val minReplies: JsonField, - private val minRetweets: JsonField, - private val place: JsonField, - private val placeCountry: JsonField, - private val pointRadius: JsonField, - private val quotes: JsonField, - private val quotesOfTweetId: JsonField, - private val replies: JsonField, - private val resultsLimit: JsonField, - private val retweets: JsonField, - private val retweetsOfTweetId: JsonField, - private val searchQuery: JsonField, - private val sinceDate: JsonField, - private val targetCommunityId: JsonField, - private val targetListId: JsonField, - private val targetSpaceId: JsonField, - private val targetTweetId: JsonField, - private val targetUsername: JsonField, - private val toUser: JsonField, - private val untilDate: JsonField, - private val url: JsonField, - private val verifiedOnly: JsonField, - private val additionalProperties: MutableMap, - ) { + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() - @JsonCreator - private constructor( - @JsonProperty("toolType") - @ExcludeMissing - toolType: JsonField = JsonMissing.of(), - @JsonProperty("advancedQuery") - @ExcludeMissing - advancedQuery: JsonField = JsonMissing.of(), - @JsonProperty("anyWords") - @ExcludeMissing - anyWords: JsonField = JsonMissing.of(), - @JsonProperty("boundingBox") - @ExcludeMissing - boundingBox: JsonField = JsonMissing.of(), - @JsonProperty("cashtags") - @ExcludeMissing - cashtags: JsonField = JsonMissing.of(), - @JsonProperty("conversationId") - @ExcludeMissing - conversationId: JsonField = JsonMissing.of(), - @JsonProperty("exactPhrase") - @ExcludeMissing - exactPhrase: JsonField = JsonMissing.of(), - @JsonProperty("excludeWords") - @ExcludeMissing - excludeWords: JsonField = JsonMissing.of(), - @JsonProperty("fromUser") - @ExcludeMissing - fromUser: JsonField = JsonMissing.of(), - @JsonProperty("hashtags") - @ExcludeMissing - hashtags: JsonField = JsonMissing.of(), - @JsonProperty("inReplyToTweetId") - @ExcludeMissing - inReplyToTweetId: JsonField = JsonMissing.of(), - @JsonProperty("language") - @ExcludeMissing - language: JsonField = JsonMissing.of(), - @JsonProperty("listId") @ExcludeMissing listId: JsonField = JsonMissing.of(), - @JsonProperty("mediaType") - @ExcludeMissing - mediaType: JsonField = JsonMissing.of(), - @JsonProperty("mentioning") - @ExcludeMissing - mentioning: JsonField = JsonMissing.of(), - @JsonProperty("minFaves") @ExcludeMissing minFaves: JsonField = JsonMissing.of(), - @JsonProperty("minQuotes") - @ExcludeMissing - minQuotes: JsonField = JsonMissing.of(), - @JsonProperty("minReplies") - @ExcludeMissing - minReplies: JsonField = JsonMissing.of(), - @JsonProperty("minRetweets") - @ExcludeMissing - minRetweets: JsonField = JsonMissing.of(), - @JsonProperty("place") @ExcludeMissing place: JsonField = JsonMissing.of(), - @JsonProperty("placeCountry") - @ExcludeMissing - placeCountry: JsonField = JsonMissing.of(), - @JsonProperty("pointRadius") - @ExcludeMissing - pointRadius: JsonField = JsonMissing.of(), - @JsonProperty("quotes") @ExcludeMissing quotes: JsonField = JsonMissing.of(), - @JsonProperty("quotesOfTweetId") - @ExcludeMissing - quotesOfTweetId: JsonField = JsonMissing.of(), - @JsonProperty("replies") @ExcludeMissing replies: JsonField = JsonMissing.of(), - @JsonProperty("resultsLimit") - @ExcludeMissing - resultsLimit: JsonField = JsonMissing.of(), - @JsonProperty("retweets") - @ExcludeMissing - retweets: JsonField = JsonMissing.of(), - @JsonProperty("retweetsOfTweetId") - @ExcludeMissing - retweetsOfTweetId: JsonField = JsonMissing.of(), - @JsonProperty("searchQuery") - @ExcludeMissing - searchQuery: JsonField = JsonMissing.of(), - @JsonProperty("sinceDate") - @ExcludeMissing - sinceDate: JsonField = JsonMissing.of(), - @JsonProperty("targetCommunityId") - @ExcludeMissing - targetCommunityId: JsonField = JsonMissing.of(), - @JsonProperty("targetListId") - @ExcludeMissing - targetListId: JsonField = JsonMissing.of(), - @JsonProperty("targetSpaceId") - @ExcludeMissing - targetSpaceId: JsonField = JsonMissing.of(), - @JsonProperty("targetTweetId") - @ExcludeMissing - targetTweetId: JsonField = JsonMissing.of(), - @JsonProperty("targetUsername") - @ExcludeMissing - targetUsername: JsonField = JsonMissing.of(), - @JsonProperty("toUser") @ExcludeMissing toUser: JsonField = JsonMissing.of(), - @JsonProperty("untilDate") - @ExcludeMissing - untilDate: JsonField = JsonMissing.of(), - @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), - @JsonProperty("verifiedOnly") - @ExcludeMissing - verifiedOnly: JsonField = JsonMissing.of(), - ) : this( - toolType, - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - mutableMapOf(), - ) + @JvmSynthetic + internal fun from(extractionEstimateCostParams: ExtractionEstimateCostParams) = apply { + body = extractionEstimateCostParams.body.toBuilder() + additionalHeaders = extractionEstimateCostParams.additionalHeaders.toBuilder() + additionalQueryParams = extractionEstimateCostParams.additionalQueryParams.toBuilder() + } /** - * Identifier for the extraction tool used to run a job. + * Sets the entire request body. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or - * is unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [toolType] + * - [advancedQuery] + * - [anyWords] + * - [bioContains] + * - [blueVerifiedOnly] + * - etc. */ - fun toolType(): ToolType = toolType.getRequired("toolType") + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Identifier for the extraction tool used to run a job. */ + fun toolType(toolType: ToolType) = apply { body.toolType(toolType) } /** - * Raw advanced query string appended to the estimate (tweet_search_extractor) + * Sets [Builder.toolType] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.toolType] with a well-typed [ToolType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun advancedQuery(): Optional = advancedQuery.getOptional("advancedQuery") + fun toolType(toolType: JsonField) = apply { body.toolType(toolType) } + + /** Raw advanced search query appended as-is (tweet_search_extractor) */ + fun advancedQuery(advancedQuery: String) = apply { body.advancedQuery(advancedQuery) } /** - * Alternative words or quoted phrases for estimated results. Separate with spaces, commas, - * or lines. + * Sets [Builder.advancedQuery] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.advancedQuery] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun anyWords(): Optional = anyWords.getOptional("anyWords") + fun advancedQuery(advancedQuery: JsonField) = apply { + body.advancedQuery(advancedQuery) + } /** - * Geo bounding box used for estimation, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) - * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + * (tweet_search_extractor) */ - fun boundingBox(): Optional = boundingBox.getOptional("boundingBox") + fun anyWords(anyWords: String) = apply { body.anyWords(anyWords) } /** - * Cashtags applied to the estimate, separated by spaces, commas, or lines. + * Sets [Builder.anyWords] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.anyWords] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun cashtags(): Optional = cashtags.getOptional("cashtags") + fun anyWords(anyWords: JsonField) = apply { body.anyWords(anyWords) } + + /** Bio terms separated by commas or lines. */ + fun bioContains(bioContains: String) = apply { body.bioContains(bioContains) } /** - * Conversation ID filter used for estimation (tweet_search_extractor) + * Sets [Builder.bioContains] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.bioContains] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun conversationId(): Optional = conversationId.getOptional("conversationId") + fun bioContains(bioContains: JsonField) = apply { body.bioContains(bioContains) } + + /** Return only Blue-verified Tweet authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = apply { + body.blueVerifiedOnly(blueVerifiedOnly) + } /** - * Exact phrase filter for search estimation + * Sets [Builder.blueVerifiedOnly] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.blueVerifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun exactPhrase(): Optional = exactPhrase.getOptional("exactPhrase") + fun blueVerifiedOnly(blueVerifiedOnly: JsonField) = apply { + body.blueVerifiedOnly(blueVerifiedOnly) + } + + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ + fun boundingBox(boundingBox: String) = apply { body.boundingBox(boundingBox) } /** - * Words or quoted phrases excluded from estimated results. Separate with spaces, commas, or - * lines. + * Sets [Builder.boundingBox] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.boundingBox] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun excludeWords(): Optional = excludeWords.getOptional("excludeWords") + fun boundingBox(boundingBox: JsonField) = apply { body.boundingBox(boundingBox) } + + /** Match the Tweet card name. */ + fun cardName(cardName: String) = apply { body.cardName(cardName) } /** - * Estimate only tweets from this author username (tweet_search_extractor) + * Sets [Builder.cardName] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.cardName] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun fromUser(): Optional = fromUser.getOptional("fromUser") + fun cardName(cardName: JsonField) = apply { body.cardName(cardName) } + + /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun cashtags(cashtags: String) = apply { body.cashtags(cashtags) } /** - * Hashtags applied to the estimate, separated by spaces, commas, or lines. + * Sets [Builder.cashtags] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.cashtags] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun hashtags(): Optional = hashtags.getOptional("hashtags") + fun cashtags(cashtags: JsonField) = apply { body.cashtags(cashtags) } + + /** Reply collection strategy. */ + fun collectionStrategy(collectionStrategy: CollectionStrategy) = apply { + body.collectionStrategy(collectionStrategy) + } /** - * Estimate only replies to this tweet ID (tweet_search_extractor) + * Sets [Builder.collectionStrategy] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.collectionStrategy] with a well-typed + * [CollectionStrategy] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. */ - fun inReplyToTweetId(): Optional = inReplyToTweetId.getOptional("inReplyToTweetId") + fun collectionStrategy(collectionStrategy: JsonField) = apply { + body.collectionStrategy(collectionStrategy) + } + + /** Conversation ID filter (tweet_search_extractor) */ + fun conversationId(conversationId: String) = apply { body.conversationId(conversationId) } /** - * Language code used for estimate filtering (tweet_search_extractor) + * Sets [Builder.conversationId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.conversationId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun language(): Optional = language.getOptional("language") + fun conversationId(conversationId: JsonField) = apply { + body.conversationId(conversationId) + } + + /** Merge duplicate results across collection targets. */ + fun dedupeAcrossTargets(dedupeAcrossTargets: Boolean) = apply { + body.dedupeAcrossTargets(dedupeAcrossTargets) + } /** - * Estimate search results within this list ID (tweet_search_extractor) + * Sets [Builder.dedupeAcrossTargets] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.dedupeAcrossTargets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun listId(): Optional = listId.getOptional("listId") + fun dedupeAcrossTargets(dedupeAcrossTargets: JsonField) = apply { + body.dedupeAcrossTargets(dedupeAcrossTargets) + } + + /** Keep target duplicates, first rows, or merged overlap. */ + fun dedupeMode(dedupeMode: DedupeMode) = apply { body.dedupeMode(dedupeMode) } /** - * Media type used for estimate filtering (tweet_search_extractor) + * Sets [Builder.dedupeMode] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.dedupeMode] with a well-typed [DedupeMode] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun mediaType(): Optional = mediaType.getOptional("mediaType") + fun dedupeMode(dedupeMode: JsonField) = apply { body.dedupeMode(dedupeMode) } + + /** Exact phrase to match (tweet_search_extractor) */ + fun exactPhrase(exactPhrase: String) = apply { body.exactPhrase(exactPhrase) } /** - * Estimate tweets mentioning this username (tweet_search_extractor) + * Sets [Builder.exactPhrase] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.exactPhrase] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun mentioning(): Optional = mentioning.getOptional("mentioning") + fun exactPhrase(exactPhrase: JsonField) = apply { body.exactPhrase(exactPhrase) } + + /** Exclude replies from the source author. */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean) = apply { + body.excludeOriginalAuthor(excludeOriginalAuthor) + } /** - * Minimum likes threshold for estimated results (tweet_search_extractor) + * Sets [Builder.excludeOriginalAuthor] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeOriginalAuthor] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun minFaves(): Optional = minFaves.getOptional("minFaves") + fun excludeOriginalAuthor(excludeOriginalAuthor: JsonField) = apply { + body.excludeOriginalAuthor(excludeOriginalAuthor) + } + + /** Exclude a source application. */ + fun excludeSource(excludeSource: String) = apply { body.excludeSource(excludeSource) } /** - * Minimum quote count threshold for estimated results (tweet_search_extractor) + * Sets [Builder.excludeSource] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeSource] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun minQuotes(): Optional = minQuotes.getOptional("minQuotes") + fun excludeSource(excludeSource: JsonField) = apply { + body.excludeSource(excludeSource) + } /** - * Minimum replies threshold for estimated results (tweet_search_extractor) - * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) */ - fun minReplies(): Optional = minReplies.getOptional("minReplies") + fun excludeWords(excludeWords: String) = apply { body.excludeWords(excludeWords) } /** - * Minimum retweets threshold for estimated results (tweet_search_extractor) + * Sets [Builder.excludeWords] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeWords] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun minRetweets(): Optional = minRetweets.getOptional("minRetweets") + fun excludeWords(excludeWords: JsonField) = apply { + body.excludeWords(excludeWords) + } + + /** Filter by author username (tweet_search_extractor) */ + fun fromUser(fromUser: String) = apply { body.fromUser(fromUser) } /** - * Estimate search results within this place ID (tweet_search_extractor) + * Sets [Builder.fromUser] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.fromUser] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun place(): Optional = place.getOptional("place") + fun fromUser(fromUser: JsonField) = apply { body.fromUser(fromUser) } + + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String) = apply { body.geocode(geocode) } /** - * Estimate search results within this country code (tweet_search_extractor) + * Sets [Builder.geocode] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.geocode] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun placeCountry(): Optional = placeCountry.getOptional("placeCountry") + fun geocode(geocode: JsonField) = apply { body.geocode(geocode) } + + /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun hashtags(hashtags: String) = apply { body.hashtags(hashtags) } /** - * Geo point radius used for estimation, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * Sets [Builder.hashtags] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hashtags] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun pointRadius(): Optional = pointRadius.getOptional("pointRadius") + fun hashtags(hashtags: JsonField) = apply { body.hashtags(hashtags) } + + /** Require a profile location. */ + fun hasLocation(hasLocation: Boolean) = apply { body.hasLocation(hasLocation) } /** - * Quote mode used for estimation (tweet_search_extractor) + * Sets [Builder.hasLocation] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasLocation] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun quotes(): Optional = quotes.getOptional("quotes") + fun hasLocation(hasLocation: JsonField) = apply { body.hasLocation(hasLocation) } + + /** Return only replies with media. */ + fun hasMediaOnly(hasMediaOnly: Boolean) = apply { body.hasMediaOnly(hasMediaOnly) } /** - * Estimate only quotes of this tweet ID (tweet_search_extractor) + * Sets [Builder.hasMediaOnly] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasMediaOnly] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun quotesOfTweetId(): Optional = quotesOfTweetId.getOptional("quotesOfTweetId") + fun hasMediaOnly(hasMediaOnly: JsonField) = apply { + body.hasMediaOnly(hasMediaOnly) + } + + /** Require a profile website. */ + fun hasWebsite(hasWebsite: Boolean) = apply { body.hasWebsite(hasWebsite) } /** - * Reply mode used for estimation (tweet_search_extractor) + * Sets [Builder.hasWebsite] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasWebsite] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun replies(): Optional = replies.getOptional("replies") + fun hasWebsite(hasWebsite: JsonField) = apply { body.hasWebsite(hasWebsite) } + + /** Include the source post in reply results. */ + fun includeOriginalPost(includeOriginalPost: Boolean) = apply { + body.includeOriginalPost(includeOriginalPost) + } /** - * Maximum number of results to estimate. When set, the estimate caps projected results to - * this value. + * Sets [Builder.includeOriginalPost] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeOriginalPost] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun resultsLimit(): Optional = resultsLimit.getOptional("resultsLimit") + fun includeOriginalPost(includeOriginalPost: JsonField) = apply { + body.includeOriginalPost(includeOriginalPost) + } + + /** Add matching search terms to collection metadata. */ + fun includeSearchTerms(includeSearchTerms: Boolean) = apply { + body.includeSearchTerms(includeSearchTerms) + } /** - * Retweet mode used for estimation (tweet_search_extractor) + * Sets [Builder.includeSearchTerms] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeSearchTerms] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun retweets(): Optional = retweets.getOptional("retweets") + fun includeSearchTerms(includeSearchTerms: JsonField) = apply { + body.includeSearchTerms(includeSearchTerms) + } + + /** Add source target metadata to each result. */ + fun includeTargetMetadata(includeTargetMetadata: Boolean) = apply { + body.includeTargetMetadata(includeTargetMetadata) + } /** - * Estimate only retweets of this tweet ID (tweet_search_extractor) + * Sets [Builder.includeTargetMetadata] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeTargetMetadata] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun retweetsOfTweetId(): Optional = - retweetsOfTweetId.getOptional("retweetsOfTweetId") + fun includeTargetMetadata(includeTargetMetadata: JsonField) = apply { + body.includeTargetMetadata(includeTargetMetadata) + } + + /** Only replies to this tweet ID (tweet_search_extractor) */ + fun inReplyToTweetId(inReplyToTweetId: String) = apply { + body.inReplyToTweetId(inReplyToTweetId) + } /** - * Query used to price tweet_search_extractor or community_search. + * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun searchQuery(): Optional = searchQuery.getOptional("searchQuery") + fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { + body.inReplyToTweetId(inReplyToTweetId) + } + + /** Language code filter (tweet_search_extractor) */ + fun language(language: String) = apply { body.language(language) } /** - * Estimate start date in YYYY-MM-DD format (tweet_search_extractor) + * Sets [Builder.language] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.language] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun sinceDate(): Optional = sinceDate.getOptional("sinceDate") + fun language(language: JsonField) = apply { body.language(language) } + + /** Search within a list ID (tweet_search_extractor) */ + fun listId(listId: String) = apply { body.listId(listId) } /** - * Community ID used to price community_post_extractor or community_search. + * Sets [Builder.listId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.listId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun targetCommunityId(): Optional = - targetCommunityId.getOptional("targetCommunityId") + fun listId(listId: JsonField) = apply { body.listId(listId) } + + /** Required profile location text. */ + fun locationContains(locationContains: String) = apply { + body.locationContains(locationContains) + } /** - * List ID used to price list_follower_explorer, list_member_extractor, or - * list_post_extractor. + * Sets [Builder.locationContains] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.locationContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun targetListId(): Optional = targetListId.getOptional("targetListId") + fun locationContains(locationContains: JsonField) = apply { + body.locationContains(locationContains) + } + + /** Maximum nested reply depth. */ + fun maxDepth(maxDepth: Long) = apply { body.maxDepth(maxDepth) } /** - * Space ID used to price space_explorer. + * Sets [Builder.maxDepth] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxDepth] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun targetSpaceId(): Optional = targetSpaceId.getOptional("targetSpaceId") + fun maxDepth(maxDepth: JsonField) = apply { body.maxDepth(maxDepth) } + + /** Maximum follower count for profile results. */ + fun maxFollowers(maxFollowers: Long) = apply { body.maxFollowers(maxFollowers) } /** - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Sets [Builder.maxFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowers] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun targetTweetId(): Optional = targetTweetId.getOptional("targetTweetId") + fun maxFollowers(maxFollowers: JsonField) = apply { body.maxFollowers(maxFollowers) } + + /** Maximum following count for profile results. */ + fun maxFollowing(maxFollowing: Long) = apply { body.maxFollowing(maxFollowing) } /** - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Sets [Builder.maxFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowing] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun targetUsername(): Optional = targetUsername.getOptional("targetUsername") + fun maxFollowing(maxFollowing: JsonField) = apply { body.maxFollowing(maxFollowing) } + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String) = apply { body.maxId(maxId) } /** - * Estimate replies sent to this username (tweet_search_extractor) + * Sets [Builder.maxId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun toUser(): Optional = toUser.getOptional("toUser") + fun maxId(maxId: JsonField) = apply { body.maxId(maxId) } + + /** Maximum results collected for each target. */ + fun maxItemsPerTarget(maxItemsPerTarget: Long) = apply { + body.maxItemsPerTarget(maxItemsPerTarget) + } /** - * Estimate end date in YYYY-MM-DD format (tweet_search_extractor) + * Sets [Builder.maxItemsPerTarget] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxItemsPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun untilDate(): Optional = untilDate.getOptional("untilDate") + fun maxItemsPerTarget(maxItemsPerTarget: JsonField) = apply { + body.maxItemsPerTarget(maxItemsPerTarget) + } + + /** Maximum Tweet like count. */ + fun maxLikes(maxLikes: Long) = apply { body.maxLikes(maxLikes) } /** - * URL substring or domain filter used for estimation (tweet_search_extractor) + * Sets [Builder.maxLikes] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxLikes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun url(): Optional = url.getOptional("url") + fun maxLikes(maxLikes: JsonField) = apply { body.maxLikes(maxLikes) } + + /** Reply pages collected for each target. */ + fun maxPagesPerTarget(maxPagesPerTarget: Long) = apply { + body.maxPagesPerTarget(maxPagesPerTarget) + } + + /** + * Sets [Builder.maxPagesPerTarget] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPagesPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxPagesPerTarget(maxPagesPerTarget: JsonField) = apply { + body.maxPagesPerTarget(maxPagesPerTarget) + } + + /** Maximum post count for profile results. */ + fun maxPosts(maxPosts: Long) = apply { body.maxPosts(maxPosts) } + + /** + * Sets [Builder.maxPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPosts] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun maxPosts(maxPosts: JsonField) = apply { body.maxPosts(maxPosts) } + + /** Maximum Tweet quote count. */ + fun maxQuotes(maxQuotes: Long) = apply { body.maxQuotes(maxQuotes) } + + /** + * Sets [Builder.maxQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.maxQuotes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun maxQuotes(maxQuotes: JsonField) = apply { body.maxQuotes(maxQuotes) } + + /** Maximum Tweet reply count. */ + fun maxReplies(maxReplies: Long) = apply { body.maxReplies(maxReplies) } + + /** + * Sets [Builder.maxReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.maxReplies] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun maxReplies(maxReplies: JsonField) = apply { body.maxReplies(maxReplies) } + + /** Maximum Tweet repost count. */ + fun maxRetweets(maxRetweets: Long) = apply { body.maxRetweets(maxRetweets) } + + /** + * Sets [Builder.maxRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.maxRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun maxRetweets(maxRetweets: JsonField) = apply { body.maxRetweets(maxRetweets) } + + /** Media type filter (tweet_search_extractor) */ + fun mediaType(mediaType: MediaType) = apply { body.mediaType(mediaType) } + + /** + * Sets [Builder.mediaType] to an arbitrary JSON value. + * + * You should usually call [Builder.mediaType] with a well-typed [MediaType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun mediaType(mediaType: JsonField) = apply { body.mediaType(mediaType) } + + /** Filter tweets mentioning a username (tweet_search_extractor) */ + fun mentioning(mentioning: String) = apply { body.mentioning(mentioning) } + + /** + * Sets [Builder.mentioning] to an arbitrary JSON value. + * + * You should usually call [Builder.mentioning] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun mentioning(mentioning: JsonField) = apply { body.mentioning(mentioning) } + + /** Minimum profile age in days. */ + fun minAccountAgeDays(minAccountAgeDays: Long) = apply { + body.minAccountAgeDays(minAccountAgeDays) + } + + /** + * Sets [Builder.minAccountAgeDays] to an arbitrary JSON value. + * + * You should usually call [Builder.minAccountAgeDays] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minAccountAgeDays(minAccountAgeDays: JsonField) = apply { + body.minAccountAgeDays(minAccountAgeDays) + } + + /** Minimum Tweet bookmark count. */ + fun minBookmarks(minBookmarks: Long) = apply { body.minBookmarks(minBookmarks) } + + /** + * Sets [Builder.minBookmarks] to an arbitrary JSON value. + * + * You should usually call [Builder.minBookmarks] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun minBookmarks(minBookmarks: JsonField) = apply { body.minBookmarks(minBookmarks) } + + /** Minimum likes threshold (tweet_search_extractor) */ + fun minFaves(minFaves: Long) = apply { body.minFaves(minFaves) } + + /** + * Sets [Builder.minFaves] to an arbitrary JSON value. + * + * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minFaves(minFaves: JsonField) = apply { body.minFaves(minFaves) } + + /** Minimum follower count for profile results. */ + fun minFollowers(minFollowers: Long) = apply { body.minFollowers(minFollowers) } + + /** + * Sets [Builder.minFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowers] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun minFollowers(minFollowers: JsonField) = apply { body.minFollowers(minFollowers) } + + /** Minimum following count for profile results. */ + fun minFollowing(minFollowing: Long) = apply { body.minFollowing(minFollowing) } + + /** + * Sets [Builder.minFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowing] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun minFollowing(minFollowing: JsonField) = apply { body.minFollowing(minFollowing) } + + /** Minimum post count for profile results. */ + fun minPosts(minPosts: Long) = apply { body.minPosts(minPosts) } + + /** + * Sets [Builder.minPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.minPosts] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minPosts(minPosts: JsonField) = apply { body.minPosts(minPosts) } + + /** Minimum quote count threshold (tweet_search_extractor) */ + fun minQuotes(minQuotes: Long) = apply { body.minQuotes(minQuotes) } + + /** + * Sets [Builder.minQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minQuotes(minQuotes: JsonField) = apply { body.minQuotes(minQuotes) } + + /** Minimum replies threshold (tweet_search_extractor) */ + fun minReplies(minReplies: Long) = apply { body.minReplies(minReplies) } + + /** + * Sets [Builder.minReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minReplies(minReplies: JsonField) = apply { body.minReplies(minReplies) } + + /** Minimum retweets threshold (tweet_search_extractor) */ + fun minRetweets(minRetweets: Long) = apply { body.minRetweets(minRetweets) } + + /** + * Sets [Builder.minRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun minRetweets(minRetweets: JsonField) = apply { body.minRetweets(minRetweets) } + + /** Minimum Tweet view count. */ + fun minViews(minViews: Long) = apply { body.minViews(minViews) } + + /** + * Sets [Builder.minViews] to an arbitrary JSON value. + * + * You should usually call [Builder.minViews] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minViews(minViews: JsonField) = apply { body.minViews(minViews) } + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean) = apply { body.nativeRetweets(nativeRetweets) } + + /** + * Sets [Builder.nativeRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.nativeRetweets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nativeRetweets(nativeRetweets: JsonField) = apply { + body.nativeRetweets(nativeRetweets) + } + + /** Match a place name. */ + fun near(near: String) = apply { body.near(near) } + + /** + * Sets [Builder.near] to an arbitrary JSON value. + * + * You should usually call [Builder.near] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun near(near: JsonField) = apply { body.near(near) } + + /** Only return news results. */ + fun news(news: Boolean) = apply { body.news(news) } + + /** + * Sets [Builder.news] to an arbitrary JSON value. + * + * You should usually call [Builder.news] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun news(news: JsonField) = apply { body.news(news) } + + /** Shortcut for dedupeMode=merge. */ + fun overlapMode(overlapMode: Boolean) = apply { body.overlapMode(overlapMode) } + + /** + * Sets [Builder.overlapMode] to an arbitrary JSON value. + * + * You should usually call [Builder.overlapMode] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun overlapMode(overlapMode: JsonField) = apply { body.overlapMode(overlapMode) } + + /** Search within a place ID (tweet_search_extractor) */ + fun place(place: String) = apply { body.place(place) } + + /** + * Sets [Builder.place] to an arbitrary JSON value. + * + * You should usually call [Builder.place] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun place(place: JsonField) = apply { body.place(place) } + + /** Search within a country code (tweet_search_extractor) */ + fun placeCountry(placeCountry: String) = apply { body.placeCountry(placeCountry) } + + /** + * Sets [Builder.placeCountry] to an arbitrary JSON value. + * + * You should usually call [Builder.placeCountry] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun placeCountry(placeCountry: JsonField) = apply { + body.placeCountry(placeCountry) + } + + /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ + fun pointRadius(pointRadius: String) = apply { body.pointRadius(pointRadius) } + + /** + * Sets [Builder.pointRadius] to an arbitrary JSON value. + * + * You should usually call [Builder.pointRadius] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun pointRadius(pointRadius: JsonField) = apply { body.pointRadius(pointRadius) } + + /** Search ranking applied to every query. */ + fun queryType(queryType: QueryType) = apply { body.queryType(queryType) } + + /** + * Sets [Builder.queryType] to an arbitrary JSON value. + * + * You should usually call [Builder.queryType] with a well-typed [QueryType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun queryType(queryType: JsonField) = apply { body.queryType(queryType) } + + /** Quote mode (tweet_search_extractor) */ + fun quotes(quotes: Quotes) = apply { body.quotes(quotes) } + + /** + * Sets [Builder.quotes] to an arbitrary JSON value. + * + * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun quotes(quotes: JsonField) = apply { body.quotes(quotes) } + + /** Only quotes of this tweet ID (tweet_search_extractor) */ + fun quotesOfTweetId(quotesOfTweetId: String) = apply { + body.quotesOfTweetId(quotesOfTweetId) + } + + /** + * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { + body.quotesOfTweetId(quotesOfTweetId) + } + + /** Profile relations processed within one job. */ + fun relationTargets(relationTargets: List) = apply { + body.relationTargets(relationTargets) + } + + /** + * Sets [Builder.relationTargets] to an arbitrary JSON value. + * + * You should usually call [Builder.relationTargets] with a well-typed + * `List` value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun relationTargets(relationTargets: JsonField>) = apply { + body.relationTargets(relationTargets) + } + + /** + * Adds a single [RelationTarget] to [relationTargets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRelationTarget(relationTarget: RelationTarget) = apply { + body.addRelationTarget(relationTarget) + } + + /** Reply mode (tweet_search_extractor) */ + fun replies(replies: Replies) = apply { body.replies(replies) } + + /** + * Sets [Builder.replies] to an arbitrary JSON value. + * + * You should usually call [Builder.replies] with a well-typed [Replies] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun replies(replies: JsonField) = apply { body.replies(replies) } + + /** + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. + */ + fun resultsLimit(resultsLimit: Long) = apply { body.resultsLimit(resultsLimit) } + + /** + * Sets [Builder.resultsLimit] to an arbitrary JSON value. + * + * You should usually call [Builder.resultsLimit] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun resultsLimit(resultsLimit: JsonField) = apply { body.resultsLimit(resultsLimit) } + + /** Retweet mode (tweet_search_extractor) */ + fun retweets(retweets: Retweets) = apply { body.retweets(retweets) } + + /** + * Sets [Builder.retweets] to an arbitrary JSON value. + * + * You should usually call [Builder.retweets] with a well-typed [Retweets] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun retweets(retweets: JsonField) = apply { body.retweets(retweets) } + + /** Only retweets of this tweet ID (tweet_search_extractor) */ + fun retweetsOfTweetId(retweetsOfTweetId: String) = apply { + body.retweetsOfTweetId(retweetsOfTweetId) + } + + /** + * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { + body.retweetsOfTweetId(retweetsOfTweetId) + } + + /** Enable the safe-search filter. */ + fun safe(safe: Boolean) = apply { body.safe(safe) } + + /** + * Sets [Builder.safe] to an arbitrary JSON value. + * + * You should usually call [Builder.safe] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun safe(safe: JsonField) = apply { body.safe(safe) } + + /** Reply depth scope. */ + fun scope(scope: Scope) = apply { body.scope(scope) } + + /** + * Sets [Builder.scope] to an arbitrary JSON value. + * + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun scope(scope: JsonField) = apply { body.scope(scope) } + + /** Search queries processed as one collection job. */ + fun searchQueries(searchQueries: List) = apply { body.searchQueries(searchQueries) } + + /** + * Sets [Builder.searchQueries] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQueries] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun searchQueries(searchQueries: JsonField>) = apply { + body.searchQueries(searchQueries) + } + + /** + * Adds a single [String] to [searchQueries]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSearchQuery(searchQuery: String) = apply { body.addSearchQuery(searchQuery) } + + /** Required for tweet_search_extractor & community_search. */ + fun searchQuery(searchQuery: String) = apply { body.searchQuery(searchQuery) } + + /** + * Sets [Builder.searchQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQuery] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun searchQuery(searchQuery: JsonField) = apply { body.searchQuery(searchQuery) } + + /** Start date YYYY-MM-DD (tweet_search_extractor) */ + fun sinceDate(sinceDate: LocalDate) = apply { body.sinceDate(sinceDate) } + + /** + * Sets [Builder.sinceDate] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sinceDate(sinceDate: JsonField) = apply { body.sinceDate(sinceDate) } + + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String) = apply { body.sinceId(sinceId) } + + /** + * Sets [Builder.sinceId] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun sinceId(sinceId: JsonField) = apply { body.sinceId(sinceId) } + + /** Reply start time as ISO 8601 or Unix seconds. */ + fun sinceTime(sinceTime: SinceTime) = apply { body.sinceTime(sinceTime) } + + /** + * Sets [Builder.sinceTime] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceTime] with a well-typed [SinceTime] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sinceTime(sinceTime: JsonField) = apply { body.sinceTime(sinceTime) } + + /** Alias for calling [sinceTime] with `SinceTime.ofOffsetDate(offsetDate)`. */ + fun sinceTime(offsetDate: OffsetDateTime) = apply { body.sinceTime(offsetDate) } + + /** Alias for calling [sinceTime] with `SinceTime.ofInteger(integer)`. */ + fun sinceTime(integer: Long) = apply { body.sinceTime(integer) } + + /** Reply result order. */ + fun sort(sort: Sort) = apply { body.sort(sort) } + + /** + * Sets [Builder.sort] to an arbitrary JSON value. + * + * You should usually call [Builder.sort] with a well-typed [Sort] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun sort(sort: JsonField) = apply { body.sort(sort) } + + /** Match the source application. */ + fun source(source: String) = apply { body.source(source) } + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { body.source(source) } + + /** Resume one reply target from this cursor. */ + fun startCursor(startCursor: String) = apply { body.startCursor(startCursor) } + + /** + * Sets [Builder.startCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.startCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun startCursor(startCursor: JsonField) = apply { body.startCursor(startCursor) } + + /** Required for community_post_extractor & community_search. */ + fun targetCommunityId(targetCommunityId: String) = apply { + body.targetCommunityId(targetCommunityId) + } + + /** + * Sets [Builder.targetCommunityId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetCommunityId(targetCommunityId: JsonField) = apply { + body.targetCommunityId(targetCommunityId) + } + + /** Community IDs processed as one collection job. */ + fun targetCommunityIds(targetCommunityIds: List) = apply { + body.targetCommunityIds(targetCommunityIds) + } + + /** + * Sets [Builder.targetCommunityIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun targetCommunityIds(targetCommunityIds: JsonField>) = apply { + body.targetCommunityIds(targetCommunityIds) + } + + /** + * Adds a single [String] to [targetCommunityIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetCommunityId(targetCommunityId: String) = apply { + body.addTargetCommunityId(targetCommunityId) + } + + /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ + fun targetListId(targetListId: String) = apply { body.targetListId(targetListId) } + + /** + * Sets [Builder.targetListId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetListId(targetListId: JsonField) = apply { + body.targetListId(targetListId) + } + + /** List IDs processed as one collection job. */ + fun targetListIds(targetListIds: List) = apply { body.targetListIds(targetListIds) } + + /** + * Sets [Builder.targetListIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetListIds(targetListIds: JsonField>) = apply { + body.targetListIds(targetListIds) + } + + /** + * Adds a single [String] to [targetListIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetListId(targetListId: String) = apply { body.addTargetListId(targetListId) } + + /** Mixed targets auto-routed within one job. */ + fun targets(targets: List) = apply { body.targets(targets) } + + /** + * Sets [Builder.targets] to an arbitrary JSON value. + * + * You should usually call [Builder.targets] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targets(targets: JsonField>) = apply { body.targets(targets) } + + /** + * Adds a single [Target] to [targets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTarget(target: Target) = apply { body.addTarget(target) } + + /** Alias for calling [addTarget] with `Target.ofString(string)`. */ + fun addTarget(string: String) = apply { body.addTarget(string) } + + /** Alias for calling [addTarget] with `Target.ofUnionMember1(unionMember1)`. */ + fun addTarget(unionMember1: Target.UnionMember1) = apply { body.addTarget(unionMember1) } + + /** Required for space_explorer. */ + fun targetSpaceId(targetSpaceId: String) = apply { body.targetSpaceId(targetSpaceId) } + + /** + * Sets [Builder.targetSpaceId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetSpaceId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetSpaceId(targetSpaceId: JsonField) = apply { + body.targetSpaceId(targetSpaceId) + } + + fun targetTweetId(targetTweetId: String) = apply { body.targetTweetId(targetTweetId) } + + /** + * Sets [Builder.targetTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetTweetId(targetTweetId: JsonField) = apply { + body.targetTweetId(targetTweetId) + } + + /** Tweet IDs processed as one collection job. */ + fun targetTweetIds(targetTweetIds: List) = apply { + body.targetTweetIds(targetTweetIds) + } + + /** + * Sets [Builder.targetTweetIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetTweetIds(targetTweetIds: JsonField>) = apply { + body.targetTweetIds(targetTweetIds) + } + + /** + * Adds a single [String] to [targetTweetIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetTweetId(targetTweetId: String) = apply { body.addTargetTweetId(targetTweetId) } + + fun targetUsername(targetUsername: String) = apply { body.targetUsername(targetUsername) } + + /** + * Sets [Builder.targetUsername] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsername] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsername(targetUsername: JsonField) = apply { + body.targetUsername(targetUsername) + } + + /** Usernames processed as one collection job. */ + fun targetUsernames(targetUsernames: List) = apply { + body.targetUsernames(targetUsernames) + } + + /** + * Sets [Builder.targetUsernames] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsernames] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsernames(targetUsernames: JsonField>) = apply { + body.targetUsernames(targetUsernames) + } + + /** + * Adds a single [String] to [targetUsernames]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetUsername(targetUsername: String) = apply { + body.addTargetUsername(targetUsername) + } + + /** Filter replies sent to a username (tweet_search_extractor) */ + fun toUser(toUser: String) = apply { body.toUser(toUser) } + + /** + * Sets [Builder.toUser] to an arbitrary JSON value. + * + * You should usually call [Builder.toUser] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun toUser(toUser: JsonField) = apply { body.toUser(toUser) } + + /** End date YYYY-MM-DD (tweet_search_extractor) */ + fun untilDate(untilDate: LocalDate) = apply { body.untilDate(untilDate) } + + /** + * Sets [Builder.untilDate] to an arbitrary JSON value. + * + * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun untilDate(untilDate: JsonField) = apply { body.untilDate(untilDate) } + + /** Reply end time as ISO 8601 or Unix seconds. */ + fun untilTime(untilTime: UntilTime) = apply { body.untilTime(untilTime) } + + /** + * Sets [Builder.untilTime] to an arbitrary JSON value. + * + * You should usually call [Builder.untilTime] with a well-typed [UntilTime] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun untilTime(untilTime: JsonField) = apply { body.untilTime(untilTime) } + + /** Alias for calling [untilTime] with `UntilTime.ofOffsetDate(offsetDate)`. */ + fun untilTime(offsetDate: OffsetDateTime) = apply { body.untilTime(offsetDate) } + + /** Alias for calling [untilTime] with `UntilTime.ofInteger(integer)`. */ + fun untilTime(integer: Long) = apply { body.untilTime(integer) } + + /** URL substring or domain filter (tweet_search_extractor) */ + fun url(url: String) = apply { body.url(url) } + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun url(url: JsonField) = apply { body.url(url) } + + /** Required username text. */ + fun usernameContains(usernameContains: String) = apply { + body.usernameContains(usernameContains) + } + + /** + * Sets [Builder.usernameContains] to an arbitrary JSON value. + * + * You should usually call [Builder.usernameContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun usernameContains(usernameContains: JsonField) = apply { + body.usernameContains(usernameContains) + } + + /** Only verified authors (tweet_search_extractor) */ + fun verifiedOnly(verifiedOnly: Boolean) = apply { body.verifiedOnly(verifiedOnly) } + + /** + * Sets [Builder.verifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun verifiedOnly(verifiedOnly: JsonField) = apply { + body.verifiedOnly(verifiedOnly) + } + + /** Exact profile verification type. */ + fun verifiedType(verifiedType: String) = apply { body.verifiedType(verifiedType) } + + /** + * Sets [Builder.verifiedType] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedType] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun verifiedType(verifiedType: JsonField) = apply { + body.verifiedType(verifiedType) + } + + /** Set the radius for the near filter. */ + fun within(within: String) = apply { body.within(within) } + + /** + * Sets [Builder.within] to an arbitrary JSON value. + * + * You should usually call [Builder.within] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun within(within: JsonField) = apply { body.within(within) } + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String) = apply { body.withinTime(withinTime) } + + /** + * Sets [Builder.withinTime] to an arbitrary JSON value. + * + * You should usually call [Builder.withinTime] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun withinTime(withinTime: JsonField) = apply { body.withinTime(withinTime) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExtractionEstimateCostParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExtractionEstimateCostParams = + ExtractionEstimateCostParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** One synchronous estimate or asynchronous extraction request. */ + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val toolType: JsonField, + private val advancedQuery: JsonField, + private val anyWords: JsonField, + private val bioContains: JsonField, + private val blueVerifiedOnly: JsonField, + private val boundingBox: JsonField, + private val cardName: JsonField, + private val cashtags: JsonField, + private val collectionStrategy: JsonField, + private val conversationId: JsonField, + private val dedupeAcrossTargets: JsonField, + private val dedupeMode: JsonField, + private val exactPhrase: JsonField, + private val excludeOriginalAuthor: JsonField, + private val excludeSource: JsonField, + private val excludeWords: JsonField, + private val fromUser: JsonField, + private val geocode: JsonField, + private val hashtags: JsonField, + private val hasLocation: JsonField, + private val hasMediaOnly: JsonField, + private val hasWebsite: JsonField, + private val includeOriginalPost: JsonField, + private val includeSearchTerms: JsonField, + private val includeTargetMetadata: JsonField, + private val inReplyToTweetId: JsonField, + private val language: JsonField, + private val listId: JsonField, + private val locationContains: JsonField, + private val maxDepth: JsonField, + private val maxFollowers: JsonField, + private val maxFollowing: JsonField, + private val maxId: JsonField, + private val maxItemsPerTarget: JsonField, + private val maxLikes: JsonField, + private val maxPagesPerTarget: JsonField, + private val maxPosts: JsonField, + private val maxQuotes: JsonField, + private val maxReplies: JsonField, + private val maxRetweets: JsonField, + private val mediaType: JsonField, + private val mentioning: JsonField, + private val minAccountAgeDays: JsonField, + private val minBookmarks: JsonField, + private val minFaves: JsonField, + private val minFollowers: JsonField, + private val minFollowing: JsonField, + private val minPosts: JsonField, + private val minQuotes: JsonField, + private val minReplies: JsonField, + private val minRetweets: JsonField, + private val minViews: JsonField, + private val nativeRetweets: JsonField, + private val near: JsonField, + private val news: JsonField, + private val overlapMode: JsonField, + private val place: JsonField, + private val placeCountry: JsonField, + private val pointRadius: JsonField, + private val queryType: JsonField, + private val quotes: JsonField, + private val quotesOfTweetId: JsonField, + private val relationTargets: JsonField>, + private val replies: JsonField, + private val resultsLimit: JsonField, + private val retweets: JsonField, + private val retweetsOfTweetId: JsonField, + private val safe: JsonField, + private val scope: JsonField, + private val searchQueries: JsonField>, + private val searchQuery: JsonField, + private val sinceDate: JsonField, + private val sinceId: JsonField, + private val sinceTime: JsonField, + private val sort: JsonField, + private val source: JsonField, + private val startCursor: JsonField, + private val targetCommunityId: JsonField, + private val targetCommunityIds: JsonField>, + private val targetListId: JsonField, + private val targetListIds: JsonField>, + private val targets: JsonField>, + private val targetSpaceId: JsonField, + private val targetTweetId: JsonField, + private val targetTweetIds: JsonField>, + private val targetUsername: JsonField, + private val targetUsernames: JsonField>, + private val toUser: JsonField, + private val untilDate: JsonField, + private val untilTime: JsonField, + private val url: JsonField, + private val usernameContains: JsonField, + private val verifiedOnly: JsonField, + private val verifiedType: JsonField, + private val within: JsonField, + private val withinTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("toolType") + @ExcludeMissing + toolType: JsonField = JsonMissing.of(), + @JsonProperty("advancedQuery") + @ExcludeMissing + advancedQuery: JsonField = JsonMissing.of(), + @JsonProperty("anyWords") + @ExcludeMissing + anyWords: JsonField = JsonMissing.of(), + @JsonProperty("bioContains") + @ExcludeMissing + bioContains: JsonField = JsonMissing.of(), + @JsonProperty("blueVerifiedOnly") + @ExcludeMissing + blueVerifiedOnly: JsonField = JsonMissing.of(), + @JsonProperty("boundingBox") + @ExcludeMissing + boundingBox: JsonField = JsonMissing.of(), + @JsonProperty("cardName") + @ExcludeMissing + cardName: JsonField = JsonMissing.of(), + @JsonProperty("cashtags") + @ExcludeMissing + cashtags: JsonField = JsonMissing.of(), + @JsonProperty("collectionStrategy") + @ExcludeMissing + collectionStrategy: JsonField = JsonMissing.of(), + @JsonProperty("conversationId") + @ExcludeMissing + conversationId: JsonField = JsonMissing.of(), + @JsonProperty("dedupeAcrossTargets") + @ExcludeMissing + dedupeAcrossTargets: JsonField = JsonMissing.of(), + @JsonProperty("dedupeMode") + @ExcludeMissing + dedupeMode: JsonField = JsonMissing.of(), + @JsonProperty("exactPhrase") + @ExcludeMissing + exactPhrase: JsonField = JsonMissing.of(), + @JsonProperty("excludeOriginalAuthor") + @ExcludeMissing + excludeOriginalAuthor: JsonField = JsonMissing.of(), + @JsonProperty("excludeSource") + @ExcludeMissing + excludeSource: JsonField = JsonMissing.of(), + @JsonProperty("excludeWords") + @ExcludeMissing + excludeWords: JsonField = JsonMissing.of(), + @JsonProperty("fromUser") + @ExcludeMissing + fromUser: JsonField = JsonMissing.of(), + @JsonProperty("geocode") @ExcludeMissing geocode: JsonField = JsonMissing.of(), + @JsonProperty("hashtags") + @ExcludeMissing + hashtags: JsonField = JsonMissing.of(), + @JsonProperty("hasLocation") + @ExcludeMissing + hasLocation: JsonField = JsonMissing.of(), + @JsonProperty("hasMediaOnly") + @ExcludeMissing + hasMediaOnly: JsonField = JsonMissing.of(), + @JsonProperty("hasWebsite") + @ExcludeMissing + hasWebsite: JsonField = JsonMissing.of(), + @JsonProperty("includeOriginalPost") + @ExcludeMissing + includeOriginalPost: JsonField = JsonMissing.of(), + @JsonProperty("includeSearchTerms") + @ExcludeMissing + includeSearchTerms: JsonField = JsonMissing.of(), + @JsonProperty("includeTargetMetadata") + @ExcludeMissing + includeTargetMetadata: JsonField = JsonMissing.of(), + @JsonProperty("inReplyToTweetId") + @ExcludeMissing + inReplyToTweetId: JsonField = JsonMissing.of(), + @JsonProperty("language") + @ExcludeMissing + language: JsonField = JsonMissing.of(), + @JsonProperty("listId") @ExcludeMissing listId: JsonField = JsonMissing.of(), + @JsonProperty("locationContains") + @ExcludeMissing + locationContains: JsonField = JsonMissing.of(), + @JsonProperty("maxDepth") @ExcludeMissing maxDepth: JsonField = JsonMissing.of(), + @JsonProperty("maxFollowers") + @ExcludeMissing + maxFollowers: JsonField = JsonMissing.of(), + @JsonProperty("maxFollowing") + @ExcludeMissing + maxFollowing: JsonField = JsonMissing.of(), + @JsonProperty("maxId") @ExcludeMissing maxId: JsonField = JsonMissing.of(), + @JsonProperty("maxItemsPerTarget") + @ExcludeMissing + maxItemsPerTarget: JsonField = JsonMissing.of(), + @JsonProperty("maxLikes") @ExcludeMissing maxLikes: JsonField = JsonMissing.of(), + @JsonProperty("maxPagesPerTarget") + @ExcludeMissing + maxPagesPerTarget: JsonField = JsonMissing.of(), + @JsonProperty("maxPosts") @ExcludeMissing maxPosts: JsonField = JsonMissing.of(), + @JsonProperty("maxQuotes") + @ExcludeMissing + maxQuotes: JsonField = JsonMissing.of(), + @JsonProperty("maxReplies") + @ExcludeMissing + maxReplies: JsonField = JsonMissing.of(), + @JsonProperty("maxRetweets") + @ExcludeMissing + maxRetweets: JsonField = JsonMissing.of(), + @JsonProperty("mediaType") + @ExcludeMissing + mediaType: JsonField = JsonMissing.of(), + @JsonProperty("mentioning") + @ExcludeMissing + mentioning: JsonField = JsonMissing.of(), + @JsonProperty("minAccountAgeDays") + @ExcludeMissing + minAccountAgeDays: JsonField = JsonMissing.of(), + @JsonProperty("minBookmarks") + @ExcludeMissing + minBookmarks: JsonField = JsonMissing.of(), + @JsonProperty("minFaves") @ExcludeMissing minFaves: JsonField = JsonMissing.of(), + @JsonProperty("minFollowers") + @ExcludeMissing + minFollowers: JsonField = JsonMissing.of(), + @JsonProperty("minFollowing") + @ExcludeMissing + minFollowing: JsonField = JsonMissing.of(), + @JsonProperty("minPosts") @ExcludeMissing minPosts: JsonField = JsonMissing.of(), + @JsonProperty("minQuotes") + @ExcludeMissing + minQuotes: JsonField = JsonMissing.of(), + @JsonProperty("minReplies") + @ExcludeMissing + minReplies: JsonField = JsonMissing.of(), + @JsonProperty("minRetweets") + @ExcludeMissing + minRetweets: JsonField = JsonMissing.of(), + @JsonProperty("minViews") @ExcludeMissing minViews: JsonField = JsonMissing.of(), + @JsonProperty("nativeRetweets") + @ExcludeMissing + nativeRetweets: JsonField = JsonMissing.of(), + @JsonProperty("near") @ExcludeMissing near: JsonField = JsonMissing.of(), + @JsonProperty("news") @ExcludeMissing news: JsonField = JsonMissing.of(), + @JsonProperty("overlapMode") + @ExcludeMissing + overlapMode: JsonField = JsonMissing.of(), + @JsonProperty("place") @ExcludeMissing place: JsonField = JsonMissing.of(), + @JsonProperty("placeCountry") + @ExcludeMissing + placeCountry: JsonField = JsonMissing.of(), + @JsonProperty("pointRadius") + @ExcludeMissing + pointRadius: JsonField = JsonMissing.of(), + @JsonProperty("queryType") + @ExcludeMissing + queryType: JsonField = JsonMissing.of(), + @JsonProperty("quotes") @ExcludeMissing quotes: JsonField = JsonMissing.of(), + @JsonProperty("quotesOfTweetId") + @ExcludeMissing + quotesOfTweetId: JsonField = JsonMissing.of(), + @JsonProperty("relationTargets") + @ExcludeMissing + relationTargets: JsonField> = JsonMissing.of(), + @JsonProperty("replies") @ExcludeMissing replies: JsonField = JsonMissing.of(), + @JsonProperty("resultsLimit") + @ExcludeMissing + resultsLimit: JsonField = JsonMissing.of(), + @JsonProperty("retweets") + @ExcludeMissing + retweets: JsonField = JsonMissing.of(), + @JsonProperty("retweetsOfTweetId") + @ExcludeMissing + retweetsOfTweetId: JsonField = JsonMissing.of(), + @JsonProperty("safe") @ExcludeMissing safe: JsonField = JsonMissing.of(), + @JsonProperty("scope") @ExcludeMissing scope: JsonField = JsonMissing.of(), + @JsonProperty("searchQueries") + @ExcludeMissing + searchQueries: JsonField> = JsonMissing.of(), + @JsonProperty("searchQuery") + @ExcludeMissing + searchQuery: JsonField = JsonMissing.of(), + @JsonProperty("sinceDate") + @ExcludeMissing + sinceDate: JsonField = JsonMissing.of(), + @JsonProperty("sinceId") @ExcludeMissing sinceId: JsonField = JsonMissing.of(), + @JsonProperty("sinceTime") + @ExcludeMissing + sinceTime: JsonField = JsonMissing.of(), + @JsonProperty("sort") @ExcludeMissing sort: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("startCursor") + @ExcludeMissing + startCursor: JsonField = JsonMissing.of(), + @JsonProperty("targetCommunityId") + @ExcludeMissing + targetCommunityId: JsonField = JsonMissing.of(), + @JsonProperty("targetCommunityIds") + @ExcludeMissing + targetCommunityIds: JsonField> = JsonMissing.of(), + @JsonProperty("targetListId") + @ExcludeMissing + targetListId: JsonField = JsonMissing.of(), + @JsonProperty("targetListIds") + @ExcludeMissing + targetListIds: JsonField> = JsonMissing.of(), + @JsonProperty("targets") + @ExcludeMissing + targets: JsonField> = JsonMissing.of(), + @JsonProperty("targetSpaceId") + @ExcludeMissing + targetSpaceId: JsonField = JsonMissing.of(), + @JsonProperty("targetTweetId") + @ExcludeMissing + targetTweetId: JsonField = JsonMissing.of(), + @JsonProperty("targetTweetIds") + @ExcludeMissing + targetTweetIds: JsonField> = JsonMissing.of(), + @JsonProperty("targetUsername") + @ExcludeMissing + targetUsername: JsonField = JsonMissing.of(), + @JsonProperty("targetUsernames") + @ExcludeMissing + targetUsernames: JsonField> = JsonMissing.of(), + @JsonProperty("toUser") @ExcludeMissing toUser: JsonField = JsonMissing.of(), + @JsonProperty("untilDate") + @ExcludeMissing + untilDate: JsonField = JsonMissing.of(), + @JsonProperty("untilTime") + @ExcludeMissing + untilTime: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + @JsonProperty("usernameContains") + @ExcludeMissing + usernameContains: JsonField = JsonMissing.of(), + @JsonProperty("verifiedOnly") + @ExcludeMissing + verifiedOnly: JsonField = JsonMissing.of(), + @JsonProperty("verifiedType") + @ExcludeMissing + verifiedType: JsonField = JsonMissing.of(), + @JsonProperty("within") @ExcludeMissing within: JsonField = JsonMissing.of(), + @JsonProperty("withinTime") + @ExcludeMissing + withinTime: JsonField = JsonMissing.of(), + ) : this( + toolType, + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + relationTargets, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + searchQueries, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + targetCommunityIds, + targetListId, + targetListIds, + targets, + targetSpaceId, + targetTweetId, + targetTweetIds, + targetUsername, + targetUsernames, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + mutableMapOf(), + ) + + /** + * Identifier for the extraction tool used to run a job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun toolType(): ToolType = toolType.getRequired("toolType") + + /** + * Raw advanced search query appended as-is (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun advancedQuery(): Optional = advancedQuery.getOptional("advancedQuery") + + /** + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun anyWords(): Optional = anyWords.getOptional("anyWords") + + /** + * Bio terms separated by commas or lines. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun bioContains(): Optional = bioContains.getOptional("bioContains") + + /** + * Return only Blue-verified Tweet authors. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun blueVerifiedOnly(): Optional = blueVerifiedOnly.getOptional("blueVerifiedOnly") + + /** + * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun boundingBox(): Optional = boundingBox.getOptional("boundingBox") + + /** + * Match the Tweet card name. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun cardName(): Optional = cardName.getOptional("cardName") + + /** + * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun cashtags(): Optional = cashtags.getOptional("cashtags") + + /** + * Reply collection strategy. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun collectionStrategy(): Optional = + collectionStrategy.getOptional("collectionStrategy") + + /** + * Conversation ID filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun conversationId(): Optional = conversationId.getOptional("conversationId") + + /** + * Merge duplicate results across collection targets. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun dedupeAcrossTargets(): Optional = + dedupeAcrossTargets.getOptional("dedupeAcrossTargets") + + /** + * Keep target duplicates, first rows, or merged overlap. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun dedupeMode(): Optional = dedupeMode.getOptional("dedupeMode") + + /** + * Exact phrase to match (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun exactPhrase(): Optional = exactPhrase.getOptional("exactPhrase") + + /** + * Exclude replies from the source author. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeOriginalAuthor(): Optional = + excludeOriginalAuthor.getOptional("excludeOriginalAuthor") + + /** + * Exclude a source application. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeSource(): Optional = excludeSource.getOptional("excludeSource") + + /** + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeWords(): Optional = excludeWords.getOptional("excludeWords") + + /** + * Filter by author username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun fromUser(): Optional = fromUser.getOptional("fromUser") + + /** + * Match latitude, longitude, and radius. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun geocode(): Optional = geocode.getOptional("geocode") + + /** + * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hashtags(): Optional = hashtags.getOptional("hashtags") + + /** + * Require a profile location. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasLocation(): Optional = hasLocation.getOptional("hasLocation") + + /** + * Return only replies with media. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasMediaOnly(): Optional = hasMediaOnly.getOptional("hasMediaOnly") + + /** + * Require a profile website. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasWebsite(): Optional = hasWebsite.getOptional("hasWebsite") + + /** + * Include the source post in reply results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeOriginalPost(): Optional = + includeOriginalPost.getOptional("includeOriginalPost") + + /** + * Add matching search terms to collection metadata. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeSearchTerms(): Optional = + includeSearchTerms.getOptional("includeSearchTerms") + + /** + * Add source target metadata to each result. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeTargetMetadata(): Optional = + includeTargetMetadata.getOptional("includeTargetMetadata") + + /** + * Only replies to this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun inReplyToTweetId(): Optional = inReplyToTweetId.getOptional("inReplyToTweetId") + + /** + * Language code filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun language(): Optional = language.getOptional("language") + + /** + * Search within a list ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun listId(): Optional = listId.getOptional("listId") + + /** + * Required profile location text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun locationContains(): Optional = locationContains.getOptional("locationContains") + + /** + * Maximum nested reply depth. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxDepth(): Optional = maxDepth.getOptional("maxDepth") + + /** + * Maximum follower count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxFollowers(): Optional = maxFollowers.getOptional("maxFollowers") + + /** + * Maximum following count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxFollowing(): Optional = maxFollowing.getOptional("maxFollowing") + + /** + * Return Tweets older than this Tweet ID. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxId(): Optional = maxId.getOptional("maxId") + + /** + * Maximum results collected for each target. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxItemsPerTarget(): Optional = maxItemsPerTarget.getOptional("maxItemsPerTarget") + + /** + * Maximum Tweet like count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxLikes(): Optional = maxLikes.getOptional("maxLikes") + + /** + * Reply pages collected for each target. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxPagesPerTarget(): Optional = maxPagesPerTarget.getOptional("maxPagesPerTarget") + + /** + * Maximum post count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxPosts(): Optional = maxPosts.getOptional("maxPosts") + + /** + * Maximum Tweet quote count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxQuotes(): Optional = maxQuotes.getOptional("maxQuotes") + + /** + * Maximum Tweet reply count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxReplies(): Optional = maxReplies.getOptional("maxReplies") + + /** + * Maximum Tweet repost count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxRetweets(): Optional = maxRetweets.getOptional("maxRetweets") + + /** + * Media type filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun mediaType(): Optional = mediaType.getOptional("mediaType") + + /** + * Filter tweets mentioning a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun mentioning(): Optional = mentioning.getOptional("mentioning") + + /** + * Minimum profile age in days. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minAccountAgeDays(): Optional = minAccountAgeDays.getOptional("minAccountAgeDays") + + /** + * Minimum Tweet bookmark count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minBookmarks(): Optional = minBookmarks.getOptional("minBookmarks") + + /** + * Minimum likes threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFaves(): Optional = minFaves.getOptional("minFaves") + + /** + * Minimum follower count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFollowers(): Optional = minFollowers.getOptional("minFollowers") + + /** + * Minimum following count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFollowing(): Optional = minFollowing.getOptional("minFollowing") + + /** + * Minimum post count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minPosts(): Optional = minPosts.getOptional("minPosts") + + /** + * Minimum quote count threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minQuotes(): Optional = minQuotes.getOptional("minQuotes") + + /** + * Minimum replies threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minReplies(): Optional = minReplies.getOptional("minReplies") + + /** + * Minimum retweets threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minRetweets(): Optional = minRetweets.getOptional("minRetweets") + + /** + * Minimum Tweet view count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minViews(): Optional = minViews.getOptional("minViews") + + /** + * Only return native reposts. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun nativeRetweets(): Optional = nativeRetweets.getOptional("nativeRetweets") + + /** + * Match a place name. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun near(): Optional = near.getOptional("near") + + /** + * Only return news results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun news(): Optional = news.getOptional("news") + + /** + * Shortcut for dedupeMode=merge. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun overlapMode(): Optional = overlapMode.getOptional("overlapMode") + + /** + * Search within a place ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun place(): Optional = place.getOptional("place") + + /** + * Search within a country code (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun placeCountry(): Optional = placeCountry.getOptional("placeCountry") + + /** + * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun pointRadius(): Optional = pointRadius.getOptional("pointRadius") + + /** + * Search ranking applied to every query. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun queryType(): Optional = queryType.getOptional("queryType") + + /** + * Quote mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun quotes(): Optional = quotes.getOptional("quotes") + + /** + * Only quotes of this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun quotesOfTweetId(): Optional = quotesOfTweetId.getOptional("quotesOfTweetId") + + /** + * Profile relations processed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun relationTargets(): Optional> = + relationTargets.getOptional("relationTargets") + + /** + * Reply mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun replies(): Optional = replies.getOptional("replies") + + /** + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun resultsLimit(): Optional = resultsLimit.getOptional("resultsLimit") + + /** + * Retweet mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun retweets(): Optional = retweets.getOptional("retweets") + + /** + * Only retweets of this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun retweetsOfTweetId(): Optional = + retweetsOfTweetId.getOptional("retweetsOfTweetId") + + /** + * Enable the safe-search filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun safe(): Optional = safe.getOptional("safe") + + /** + * Reply depth scope. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun scope(): Optional = scope.getOptional("scope") + + /** + * Search queries processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun searchQueries(): Optional> = searchQueries.getOptional("searchQueries") + + /** + * Required for tweet_search_extractor & community_search. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun searchQuery(): Optional = searchQuery.getOptional("searchQuery") + + /** + * Start date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceDate(): Optional = sinceDate.getOptional("sinceDate") + + /** + * Return Tweets newer than this Tweet ID. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceId(): Optional = sinceId.getOptional("sinceId") + + /** + * Reply start time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceTime(): Optional = sinceTime.getOptional("sinceTime") + + /** + * Reply result order. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sort(): Optional = sort.getOptional("sort") + + /** + * Match the source application. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun source(): Optional = source.getOptional("source") + + /** + * Resume one reply target from this cursor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun startCursor(): Optional = startCursor.getOptional("startCursor") + + /** + * Required for community_post_extractor & community_search. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetCommunityId(): Optional = + targetCommunityId.getOptional("targetCommunityId") + + /** + * Community IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetCommunityIds(): Optional> = + targetCommunityIds.getOptional("targetCommunityIds") + + /** + * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetListId(): Optional = targetListId.getOptional("targetListId") + + /** + * List IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetListIds(): Optional> = targetListIds.getOptional("targetListIds") + + /** + * Mixed targets auto-routed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targets(): Optional> = targets.getOptional("targets") + + /** + * Required for space_explorer. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetSpaceId(): Optional = targetSpaceId.getOptional("targetSpaceId") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetTweetId(): Optional = targetTweetId.getOptional("targetTweetId") + + /** + * Tweet IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetTweetIds(): Optional> = targetTweetIds.getOptional("targetTweetIds") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetUsername(): Optional = targetUsername.getOptional("targetUsername") + + /** + * Usernames processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetUsernames(): Optional> = + targetUsernames.getOptional("targetUsernames") + + /** + * Filter replies sent to a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun toUser(): Optional = toUser.getOptional("toUser") + + /** + * End date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun untilDate(): Optional = untilDate.getOptional("untilDate") + + /** + * Reply end time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun untilTime(): Optional = untilTime.getOptional("untilTime") + + /** + * URL substring or domain filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun url(): Optional = url.getOptional("url") + + /** + * Required username text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun usernameContains(): Optional = usernameContains.getOptional("usernameContains") + + /** + * Only verified authors (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun verifiedOnly(): Optional = verifiedOnly.getOptional("verifiedOnly") + + /** + * Exact profile verification type. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun verifiedType(): Optional = verifiedType.getOptional("verifiedType") + + /** + * Set the radius for the near filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun within(): Optional = within.getOptional("within") + + /** + * Match Tweets inside a recent time window. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun withinTime(): Optional = withinTime.getOptional("withinTime") + + /** + * Returns the raw JSON value of [toolType]. + * + * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("toolType") @ExcludeMissing fun _toolType(): JsonField = toolType + + /** + * Returns the raw JSON value of [advancedQuery]. + * + * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("advancedQuery") + @ExcludeMissing + fun _advancedQuery(): JsonField = advancedQuery + + /** + * Returns the raw JSON value of [anyWords]. + * + * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("anyWords") @ExcludeMissing fun _anyWords(): JsonField = anyWords + + /** + * Returns the raw JSON value of [bioContains]. + * + * Unlike [bioContains], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bioContains") + @ExcludeMissing + fun _bioContains(): JsonField = bioContains + + /** + * Returns the raw JSON value of [blueVerifiedOnly]. + * + * Unlike [blueVerifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("blueVerifiedOnly") + @ExcludeMissing + fun _blueVerifiedOnly(): JsonField = blueVerifiedOnly + + /** + * Returns the raw JSON value of [boundingBox]. + * + * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("boundingBox") + @ExcludeMissing + fun _boundingBox(): JsonField = boundingBox + + /** + * Returns the raw JSON value of [cardName]. + * + * Unlike [cardName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cardName") @ExcludeMissing fun _cardName(): JsonField = cardName + + /** + * Returns the raw JSON value of [cashtags]. + * + * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cashtags") @ExcludeMissing fun _cashtags(): JsonField = cashtags + + /** + * Returns the raw JSON value of [collectionStrategy]. + * + * Unlike [collectionStrategy], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("collectionStrategy") + @ExcludeMissing + fun _collectionStrategy(): JsonField = collectionStrategy + + /** + * Returns the raw JSON value of [conversationId]. + * + * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("conversationId") + @ExcludeMissing + fun _conversationId(): JsonField = conversationId + + /** + * Returns the raw JSON value of [dedupeAcrossTargets]. + * + * Unlike [dedupeAcrossTargets], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("dedupeAcrossTargets") + @ExcludeMissing + fun _dedupeAcrossTargets(): JsonField = dedupeAcrossTargets + + /** + * Returns the raw JSON value of [dedupeMode]. + * + * Unlike [dedupeMode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dedupeMode") + @ExcludeMissing + fun _dedupeMode(): JsonField = dedupeMode + + /** + * Returns the raw JSON value of [exactPhrase]. + * + * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("exactPhrase") + @ExcludeMissing + fun _exactPhrase(): JsonField = exactPhrase + + /** + * Returns the raw JSON value of [excludeOriginalAuthor]. + * + * Unlike [excludeOriginalAuthor], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("excludeOriginalAuthor") + @ExcludeMissing + fun _excludeOriginalAuthor(): JsonField = excludeOriginalAuthor + + /** + * Returns the raw JSON value of [excludeSource]. + * + * Unlike [excludeSource], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("excludeSource") + @ExcludeMissing + fun _excludeSource(): JsonField = excludeSource + + /** + * Returns the raw JSON value of [excludeWords]. + * + * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("excludeWords") + @ExcludeMissing + fun _excludeWords(): JsonField = excludeWords + + /** + * Returns the raw JSON value of [fromUser]. + * + * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fromUser") @ExcludeMissing fun _fromUser(): JsonField = fromUser + + /** + * Returns the raw JSON value of [geocode]. + * + * Unlike [geocode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("geocode") @ExcludeMissing fun _geocode(): JsonField = geocode + + /** + * Returns the raw JSON value of [hashtags]. + * + * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hashtags") @ExcludeMissing fun _hashtags(): JsonField = hashtags + + /** + * Returns the raw JSON value of [hasLocation]. + * + * Unlike [hasLocation], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasLocation") + @ExcludeMissing + fun _hasLocation(): JsonField = hasLocation + + /** + * Returns the raw JSON value of [hasMediaOnly]. + * + * Unlike [hasMediaOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("hasMediaOnly") + @ExcludeMissing + fun _hasMediaOnly(): JsonField = hasMediaOnly + + /** + * Returns the raw JSON value of [hasWebsite]. + * + * Unlike [hasWebsite], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasWebsite") + @ExcludeMissing + fun _hasWebsite(): JsonField = hasWebsite + + /** + * Returns the raw JSON value of [includeOriginalPost]. + * + * Unlike [includeOriginalPost], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeOriginalPost") + @ExcludeMissing + fun _includeOriginalPost(): JsonField = includeOriginalPost + + /** + * Returns the raw JSON value of [includeSearchTerms]. + * + * Unlike [includeSearchTerms], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeSearchTerms") + @ExcludeMissing + fun _includeSearchTerms(): JsonField = includeSearchTerms + + /** + * Returns the raw JSON value of [includeTargetMetadata]. + * + * Unlike [includeTargetMetadata], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeTargetMetadata") + @ExcludeMissing + fun _includeTargetMetadata(): JsonField = includeTargetMetadata + + /** + * Returns the raw JSON value of [inReplyToTweetId]. + * + * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("inReplyToTweetId") + @ExcludeMissing + fun _inReplyToTweetId(): JsonField = inReplyToTweetId + + /** + * Returns the raw JSON value of [language]. + * + * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + + /** + * Returns the raw JSON value of [listId]. + * + * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("listId") @ExcludeMissing fun _listId(): JsonField = listId + + /** + * Returns the raw JSON value of [locationContains]. + * + * Unlike [locationContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("locationContains") + @ExcludeMissing + fun _locationContains(): JsonField = locationContains + + /** + * Returns the raw JSON value of [maxDepth]. + * + * Unlike [maxDepth], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxDepth") @ExcludeMissing fun _maxDepth(): JsonField = maxDepth + + /** + * Returns the raw JSON value of [maxFollowers]. + * + * Unlike [maxFollowers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxFollowers") + @ExcludeMissing + fun _maxFollowers(): JsonField = maxFollowers + + /** + * Returns the raw JSON value of [maxFollowing]. + * + * Unlike [maxFollowing], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxFollowing") + @ExcludeMissing + fun _maxFollowing(): JsonField = maxFollowing + + /** + * Returns the raw JSON value of [maxId]. + * + * Unlike [maxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxId") @ExcludeMissing fun _maxId(): JsonField = maxId + + /** + * Returns the raw JSON value of [maxItemsPerTarget]. + * + * Unlike [maxItemsPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxItemsPerTarget") + @ExcludeMissing + fun _maxItemsPerTarget(): JsonField = maxItemsPerTarget + + /** + * Returns the raw JSON value of [maxLikes]. + * + * Unlike [maxLikes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxLikes") @ExcludeMissing fun _maxLikes(): JsonField = maxLikes + + /** + * Returns the raw JSON value of [maxPagesPerTarget]. + * + * Unlike [maxPagesPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxPagesPerTarget") + @ExcludeMissing + fun _maxPagesPerTarget(): JsonField = maxPagesPerTarget + + /** + * Returns the raw JSON value of [maxPosts]. + * + * Unlike [maxPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxPosts") @ExcludeMissing fun _maxPosts(): JsonField = maxPosts + + /** + * Returns the raw JSON value of [maxQuotes]. + * + * Unlike [maxQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxQuotes") @ExcludeMissing fun _maxQuotes(): JsonField = maxQuotes + + /** + * Returns the raw JSON value of [maxReplies]. + * + * Unlike [maxReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxReplies") @ExcludeMissing fun _maxReplies(): JsonField = maxReplies + + /** + * Returns the raw JSON value of [maxRetweets]. + * + * Unlike [maxRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxRetweets") + @ExcludeMissing + fun _maxRetweets(): JsonField = maxRetweets + + /** + * Returns the raw JSON value of [mediaType]. + * + * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mediaType") + @ExcludeMissing + fun _mediaType(): JsonField = mediaType + + /** + * Returns the raw JSON value of [mentioning]. + * + * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mentioning") + @ExcludeMissing + fun _mentioning(): JsonField = mentioning + + /** + * Returns the raw JSON value of [minAccountAgeDays]. + * + * Unlike [minAccountAgeDays], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minAccountAgeDays") + @ExcludeMissing + fun _minAccountAgeDays(): JsonField = minAccountAgeDays + + /** + * Returns the raw JSON value of [minBookmarks]. + * + * Unlike [minBookmarks], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minBookmarks") + @ExcludeMissing + fun _minBookmarks(): JsonField = minBookmarks + + /** + * Returns the raw JSON value of [minFaves]. + * + * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minFaves") @ExcludeMissing fun _minFaves(): JsonField = minFaves + + /** + * Returns the raw JSON value of [minFollowers]. + * + * Unlike [minFollowers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minFollowers") + @ExcludeMissing + fun _minFollowers(): JsonField = minFollowers + + /** + * Returns the raw JSON value of [minFollowing]. + * + * Unlike [minFollowing], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minFollowing") + @ExcludeMissing + fun _minFollowing(): JsonField = minFollowing + + /** + * Returns the raw JSON value of [minPosts]. + * + * Unlike [minPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minPosts") @ExcludeMissing fun _minPosts(): JsonField = minPosts + + /** + * Returns the raw JSON value of [minQuotes]. + * + * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minQuotes") @ExcludeMissing fun _minQuotes(): JsonField = minQuotes + + /** + * Returns the raw JSON value of [minReplies]. + * + * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minReplies") @ExcludeMissing fun _minReplies(): JsonField = minReplies + + /** + * Returns the raw JSON value of [minRetweets]. + * + * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minRetweets") + @ExcludeMissing + fun _minRetweets(): JsonField = minRetweets + + /** + * Returns the raw JSON value of [minViews]. + * + * Unlike [minViews], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minViews") @ExcludeMissing fun _minViews(): JsonField = minViews + + /** + * Returns the raw JSON value of [nativeRetweets]. + * + * Unlike [nativeRetweets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nativeRetweets") + @ExcludeMissing + fun _nativeRetweets(): JsonField = nativeRetweets + + /** + * Returns the raw JSON value of [near]. + * + * Unlike [near], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("near") @ExcludeMissing fun _near(): JsonField = near + + /** + * Returns the raw JSON value of [news]. + * + * Unlike [news], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("news") @ExcludeMissing fun _news(): JsonField = news + + /** + * Returns the raw JSON value of [overlapMode]. + * + * Unlike [overlapMode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("overlapMode") + @ExcludeMissing + fun _overlapMode(): JsonField = overlapMode + + /** + * Returns the raw JSON value of [place]. + * + * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("place") @ExcludeMissing fun _place(): JsonField = place + + /** + * Returns the raw JSON value of [placeCountry]. + * + * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("placeCountry") + @ExcludeMissing + fun _placeCountry(): JsonField = placeCountry + + /** + * Returns the raw JSON value of [pointRadius]. + * + * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pointRadius") + @ExcludeMissing + fun _pointRadius(): JsonField = pointRadius + + /** + * Returns the raw JSON value of [queryType]. + * + * Unlike [queryType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("queryType") + @ExcludeMissing + fun _queryType(): JsonField = queryType + + /** + * Returns the raw JSON value of [quotes]. + * + * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("quotes") @ExcludeMissing fun _quotes(): JsonField = quotes + + /** + * Returns the raw JSON value of [quotesOfTweetId]. + * + * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("quotesOfTweetId") + @ExcludeMissing + fun _quotesOfTweetId(): JsonField = quotesOfTweetId + + /** + * Returns the raw JSON value of [relationTargets]. + * + * Unlike [relationTargets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("relationTargets") + @ExcludeMissing + fun _relationTargets(): JsonField> = relationTargets + + /** + * Returns the raw JSON value of [replies]. + * + * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("replies") @ExcludeMissing fun _replies(): JsonField = replies + + /** + * Returns the raw JSON value of [resultsLimit]. + * + * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("resultsLimit") + @ExcludeMissing + fun _resultsLimit(): JsonField = resultsLimit + + /** + * Returns the raw JSON value of [retweets]. + * + * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("retweets") @ExcludeMissing fun _retweets(): JsonField = retweets + + /** + * Returns the raw JSON value of [retweetsOfTweetId]. + * + * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("retweetsOfTweetId") + @ExcludeMissing + fun _retweetsOfTweetId(): JsonField = retweetsOfTweetId + + /** + * Returns the raw JSON value of [safe]. + * + * Unlike [safe], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("safe") @ExcludeMissing fun _safe(): JsonField = safe + + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scope") @ExcludeMissing fun _scope(): JsonField = scope + + /** + * Returns the raw JSON value of [searchQueries]. + * + * Unlike [searchQueries], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("searchQueries") + @ExcludeMissing + fun _searchQueries(): JsonField> = searchQueries + + /** + * Returns the raw JSON value of [searchQuery]. + * + * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("searchQuery") + @ExcludeMissing + fun _searchQuery(): JsonField = searchQuery + + /** + * Returns the raw JSON value of [sinceDate]. + * + * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceDate") + @ExcludeMissing + fun _sinceDate(): JsonField = sinceDate + + /** + * Returns the raw JSON value of [sinceId]. + * + * Unlike [sinceId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceId") @ExcludeMissing fun _sinceId(): JsonField = sinceId + + /** + * Returns the raw JSON value of [sinceTime]. + * + * Unlike [sinceTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceTime") + @ExcludeMissing + fun _sinceTime(): JsonField = sinceTime + + /** + * Returns the raw JSON value of [sort]. + * + * Unlike [sort], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sort") @ExcludeMissing fun _sort(): JsonField = sort + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [startCursor]. + * + * Unlike [startCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("startCursor") + @ExcludeMissing + fun _startCursor(): JsonField = startCursor + + /** + * Returns the raw JSON value of [targetCommunityId]. + * + * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetCommunityId") + @ExcludeMissing + fun _targetCommunityId(): JsonField = targetCommunityId + + /** + * Returns the raw JSON value of [targetCommunityIds]. + * + * Unlike [targetCommunityIds], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("targetCommunityIds") + @ExcludeMissing + fun _targetCommunityIds(): JsonField> = targetCommunityIds + + /** + * Returns the raw JSON value of [targetListId]. + * + * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetListId") + @ExcludeMissing + fun _targetListId(): JsonField = targetListId + + /** + * Returns the raw JSON value of [targetListIds]. + * + * Unlike [targetListIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetListIds") + @ExcludeMissing + fun _targetListIds(): JsonField> = targetListIds + + /** + * Returns the raw JSON value of [targets]. + * + * Unlike [targets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("targets") @ExcludeMissing fun _targets(): JsonField> = targets + + /** + * Returns the raw JSON value of [targetSpaceId]. + * + * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetSpaceId") + @ExcludeMissing + fun _targetSpaceId(): JsonField = targetSpaceId + + /** + * Returns the raw JSON value of [targetTweetId]. + * + * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetTweetId") + @ExcludeMissing + fun _targetTweetId(): JsonField = targetTweetId + + /** + * Returns the raw JSON value of [targetTweetIds]. + * + * Unlike [targetTweetIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetTweetIds") + @ExcludeMissing + fun _targetTweetIds(): JsonField> = targetTweetIds + + /** + * Returns the raw JSON value of [targetUsername]. + * + * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetUsername") + @ExcludeMissing + fun _targetUsername(): JsonField = targetUsername + + /** + * Returns the raw JSON value of [targetUsernames]. + * + * Unlike [targetUsernames], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetUsernames") + @ExcludeMissing + fun _targetUsernames(): JsonField> = targetUsernames + + /** + * Returns the raw JSON value of [toUser]. + * + * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("toUser") @ExcludeMissing fun _toUser(): JsonField = toUser + + /** + * Returns the raw JSON value of [untilDate]. + * + * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("untilDate") + @ExcludeMissing + fun _untilDate(): JsonField = untilDate + + /** + * Returns the raw JSON value of [untilTime]. + * + * Unlike [untilTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("untilTime") + @ExcludeMissing + fun _untilTime(): JsonField = untilTime + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + /** + * Returns the raw JSON value of [usernameContains]. + * + * Unlike [usernameContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("usernameContains") + @ExcludeMissing + fun _usernameContains(): JsonField = usernameContains + + /** + * Returns the raw JSON value of [verifiedOnly]. + * + * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("verifiedOnly") + @ExcludeMissing + fun _verifiedOnly(): JsonField = verifiedOnly + + /** + * Returns the raw JSON value of [verifiedType]. + * + * Unlike [verifiedType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("verifiedType") + @ExcludeMissing + fun _verifiedType(): JsonField = verifiedType + + /** + * Returns the raw JSON value of [within]. + * + * Unlike [within], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("within") @ExcludeMissing fun _within(): JsonField = within + + /** + * Returns the raw JSON value of [withinTime]. + * + * Unlike [withinTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("withinTime") + @ExcludeMissing + fun _withinTime(): JsonField = withinTime + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var toolType: JsonField? = null + private var advancedQuery: JsonField = JsonMissing.of() + private var anyWords: JsonField = JsonMissing.of() + private var bioContains: JsonField = JsonMissing.of() + private var blueVerifiedOnly: JsonField = JsonMissing.of() + private var boundingBox: JsonField = JsonMissing.of() + private var cardName: JsonField = JsonMissing.of() + private var cashtags: JsonField = JsonMissing.of() + private var collectionStrategy: JsonField = JsonMissing.of() + private var conversationId: JsonField = JsonMissing.of() + private var dedupeAcrossTargets: JsonField = JsonMissing.of() + private var dedupeMode: JsonField = JsonMissing.of() + private var exactPhrase: JsonField = JsonMissing.of() + private var excludeOriginalAuthor: JsonField = JsonMissing.of() + private var excludeSource: JsonField = JsonMissing.of() + private var excludeWords: JsonField = JsonMissing.of() + private var fromUser: JsonField = JsonMissing.of() + private var geocode: JsonField = JsonMissing.of() + private var hashtags: JsonField = JsonMissing.of() + private var hasLocation: JsonField = JsonMissing.of() + private var hasMediaOnly: JsonField = JsonMissing.of() + private var hasWebsite: JsonField = JsonMissing.of() + private var includeOriginalPost: JsonField = JsonMissing.of() + private var includeSearchTerms: JsonField = JsonMissing.of() + private var includeTargetMetadata: JsonField = JsonMissing.of() + private var inReplyToTweetId: JsonField = JsonMissing.of() + private var language: JsonField = JsonMissing.of() + private var listId: JsonField = JsonMissing.of() + private var locationContains: JsonField = JsonMissing.of() + private var maxDepth: JsonField = JsonMissing.of() + private var maxFollowers: JsonField = JsonMissing.of() + private var maxFollowing: JsonField = JsonMissing.of() + private var maxId: JsonField = JsonMissing.of() + private var maxItemsPerTarget: JsonField = JsonMissing.of() + private var maxLikes: JsonField = JsonMissing.of() + private var maxPagesPerTarget: JsonField = JsonMissing.of() + private var maxPosts: JsonField = JsonMissing.of() + private var maxQuotes: JsonField = JsonMissing.of() + private var maxReplies: JsonField = JsonMissing.of() + private var maxRetweets: JsonField = JsonMissing.of() + private var mediaType: JsonField = JsonMissing.of() + private var mentioning: JsonField = JsonMissing.of() + private var minAccountAgeDays: JsonField = JsonMissing.of() + private var minBookmarks: JsonField = JsonMissing.of() + private var minFaves: JsonField = JsonMissing.of() + private var minFollowers: JsonField = JsonMissing.of() + private var minFollowing: JsonField = JsonMissing.of() + private var minPosts: JsonField = JsonMissing.of() + private var minQuotes: JsonField = JsonMissing.of() + private var minReplies: JsonField = JsonMissing.of() + private var minRetweets: JsonField = JsonMissing.of() + private var minViews: JsonField = JsonMissing.of() + private var nativeRetweets: JsonField = JsonMissing.of() + private var near: JsonField = JsonMissing.of() + private var news: JsonField = JsonMissing.of() + private var overlapMode: JsonField = JsonMissing.of() + private var place: JsonField = JsonMissing.of() + private var placeCountry: JsonField = JsonMissing.of() + private var pointRadius: JsonField = JsonMissing.of() + private var queryType: JsonField = JsonMissing.of() + private var quotes: JsonField = JsonMissing.of() + private var quotesOfTweetId: JsonField = JsonMissing.of() + private var relationTargets: JsonField>? = null + private var replies: JsonField = JsonMissing.of() + private var resultsLimit: JsonField = JsonMissing.of() + private var retweets: JsonField = JsonMissing.of() + private var retweetsOfTweetId: JsonField = JsonMissing.of() + private var safe: JsonField = JsonMissing.of() + private var scope: JsonField = JsonMissing.of() + private var searchQueries: JsonField>? = null + private var searchQuery: JsonField = JsonMissing.of() + private var sinceDate: JsonField = JsonMissing.of() + private var sinceId: JsonField = JsonMissing.of() + private var sinceTime: JsonField = JsonMissing.of() + private var sort: JsonField = JsonMissing.of() + private var source: JsonField = JsonMissing.of() + private var startCursor: JsonField = JsonMissing.of() + private var targetCommunityId: JsonField = JsonMissing.of() + private var targetCommunityIds: JsonField>? = null + private var targetListId: JsonField = JsonMissing.of() + private var targetListIds: JsonField>? = null + private var targets: JsonField>? = null + private var targetSpaceId: JsonField = JsonMissing.of() + private var targetTweetId: JsonField = JsonMissing.of() + private var targetTweetIds: JsonField>? = null + private var targetUsername: JsonField = JsonMissing.of() + private var targetUsernames: JsonField>? = null + private var toUser: JsonField = JsonMissing.of() + private var untilDate: JsonField = JsonMissing.of() + private var untilTime: JsonField = JsonMissing.of() + private var url: JsonField = JsonMissing.of() + private var usernameContains: JsonField = JsonMissing.of() + private var verifiedOnly: JsonField = JsonMissing.of() + private var verifiedType: JsonField = JsonMissing.of() + private var within: JsonField = JsonMissing.of() + private var withinTime: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + toolType = body.toolType + advancedQuery = body.advancedQuery + anyWords = body.anyWords + bioContains = body.bioContains + blueVerifiedOnly = body.blueVerifiedOnly + boundingBox = body.boundingBox + cardName = body.cardName + cashtags = body.cashtags + collectionStrategy = body.collectionStrategy + conversationId = body.conversationId + dedupeAcrossTargets = body.dedupeAcrossTargets + dedupeMode = body.dedupeMode + exactPhrase = body.exactPhrase + excludeOriginalAuthor = body.excludeOriginalAuthor + excludeSource = body.excludeSource + excludeWords = body.excludeWords + fromUser = body.fromUser + geocode = body.geocode + hashtags = body.hashtags + hasLocation = body.hasLocation + hasMediaOnly = body.hasMediaOnly + hasWebsite = body.hasWebsite + includeOriginalPost = body.includeOriginalPost + includeSearchTerms = body.includeSearchTerms + includeTargetMetadata = body.includeTargetMetadata + inReplyToTweetId = body.inReplyToTweetId + language = body.language + listId = body.listId + locationContains = body.locationContains + maxDepth = body.maxDepth + maxFollowers = body.maxFollowers + maxFollowing = body.maxFollowing + maxId = body.maxId + maxItemsPerTarget = body.maxItemsPerTarget + maxLikes = body.maxLikes + maxPagesPerTarget = body.maxPagesPerTarget + maxPosts = body.maxPosts + maxQuotes = body.maxQuotes + maxReplies = body.maxReplies + maxRetweets = body.maxRetweets + mediaType = body.mediaType + mentioning = body.mentioning + minAccountAgeDays = body.minAccountAgeDays + minBookmarks = body.minBookmarks + minFaves = body.minFaves + minFollowers = body.minFollowers + minFollowing = body.minFollowing + minPosts = body.minPosts + minQuotes = body.minQuotes + minReplies = body.minReplies + minRetweets = body.minRetweets + minViews = body.minViews + nativeRetweets = body.nativeRetweets + near = body.near + news = body.news + overlapMode = body.overlapMode + place = body.place + placeCountry = body.placeCountry + pointRadius = body.pointRadius + queryType = body.queryType + quotes = body.quotes + quotesOfTweetId = body.quotesOfTweetId + relationTargets = body.relationTargets.map { it.toMutableList() } + replies = body.replies + resultsLimit = body.resultsLimit + retweets = body.retweets + retweetsOfTweetId = body.retweetsOfTweetId + safe = body.safe + scope = body.scope + searchQueries = body.searchQueries.map { it.toMutableList() } + searchQuery = body.searchQuery + sinceDate = body.sinceDate + sinceId = body.sinceId + sinceTime = body.sinceTime + sort = body.sort + source = body.source + startCursor = body.startCursor + targetCommunityId = body.targetCommunityId + targetCommunityIds = body.targetCommunityIds.map { it.toMutableList() } + targetListId = body.targetListId + targetListIds = body.targetListIds.map { it.toMutableList() } + targets = body.targets.map { it.toMutableList() } + targetSpaceId = body.targetSpaceId + targetTweetId = body.targetTweetId + targetTweetIds = body.targetTweetIds.map { it.toMutableList() } + targetUsername = body.targetUsername + targetUsernames = body.targetUsernames.map { it.toMutableList() } + toUser = body.toUser + untilDate = body.untilDate + untilTime = body.untilTime + url = body.url + usernameContains = body.usernameContains + verifiedOnly = body.verifiedOnly + verifiedType = body.verifiedType + within = body.within + withinTime = body.withinTime + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Identifier for the extraction tool used to run a job. */ + fun toolType(toolType: ToolType) = toolType(JsonField.of(toolType)) + + /** + * Sets [Builder.toolType] to an arbitrary JSON value. + * + * You should usually call [Builder.toolType] with a well-typed [ToolType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun toolType(toolType: JsonField) = apply { this.toolType = toolType } + + /** Raw advanced search query appended as-is (tweet_search_extractor) */ + fun advancedQuery(advancedQuery: String) = advancedQuery(JsonField.of(advancedQuery)) + + /** + * Sets [Builder.advancedQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.advancedQuery] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun advancedQuery(advancedQuery: JsonField) = apply { + this.advancedQuery = advancedQuery + } + + /** + * Words or quoted phrases where any one can match. Separate with spaces, commas, or + * lines. (tweet_search_extractor) + */ + fun anyWords(anyWords: String) = anyWords(JsonField.of(anyWords)) + + /** + * Sets [Builder.anyWords] to an arbitrary JSON value. + * + * You should usually call [Builder.anyWords] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun anyWords(anyWords: JsonField) = apply { this.anyWords = anyWords } + + /** Bio terms separated by commas or lines. */ + fun bioContains(bioContains: String) = bioContains(JsonField.of(bioContains)) + + /** + * Sets [Builder.bioContains] to an arbitrary JSON value. + * + * You should usually call [Builder.bioContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bioContains(bioContains: JsonField) = apply { + this.bioContains = bioContains + } + + /** Return only Blue-verified Tweet authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(JsonField.of(blueVerifiedOnly)) + + /** + * Sets [Builder.blueVerifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.blueVerifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun blueVerifiedOnly(blueVerifiedOnly: JsonField) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ + fun boundingBox(boundingBox: String) = boundingBox(JsonField.of(boundingBox)) + + /** + * Sets [Builder.boundingBox] to an arbitrary JSON value. + * + * You should usually call [Builder.boundingBox] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun boundingBox(boundingBox: JsonField) = apply { + this.boundingBox = boundingBox + } + + /** Match the Tweet card name. */ + fun cardName(cardName: String) = cardName(JsonField.of(cardName)) + + /** + * Sets [Builder.cardName] to an arbitrary JSON value. + * + * You should usually call [Builder.cardName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cardName(cardName: JsonField) = apply { this.cardName = cardName } + + /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun cashtags(cashtags: String) = cashtags(JsonField.of(cashtags)) + + /** + * Sets [Builder.cashtags] to an arbitrary JSON value. + * + * You should usually call [Builder.cashtags] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cashtags(cashtags: JsonField) = apply { this.cashtags = cashtags } + + /** Reply collection strategy. */ + fun collectionStrategy(collectionStrategy: CollectionStrategy) = + collectionStrategy(JsonField.of(collectionStrategy)) + + /** + * Sets [Builder.collectionStrategy] to an arbitrary JSON value. + * + * You should usually call [Builder.collectionStrategy] with a well-typed + * [CollectionStrategy] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun collectionStrategy(collectionStrategy: JsonField) = apply { + this.collectionStrategy = collectionStrategy + } + + /** Conversation ID filter (tweet_search_extractor) */ + fun conversationId(conversationId: String) = + conversationId(JsonField.of(conversationId)) + + /** + * Sets [Builder.conversationId] to an arbitrary JSON value. + * + * You should usually call [Builder.conversationId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun conversationId(conversationId: JsonField) = apply { + this.conversationId = conversationId + } + + /** Merge duplicate results across collection targets. */ + fun dedupeAcrossTargets(dedupeAcrossTargets: Boolean) = + dedupeAcrossTargets(JsonField.of(dedupeAcrossTargets)) + + /** + * Sets [Builder.dedupeAcrossTargets] to an arbitrary JSON value. + * + * You should usually call [Builder.dedupeAcrossTargets] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun dedupeAcrossTargets(dedupeAcrossTargets: JsonField) = apply { + this.dedupeAcrossTargets = dedupeAcrossTargets + } + + /** Keep target duplicates, first rows, or merged overlap. */ + fun dedupeMode(dedupeMode: DedupeMode) = dedupeMode(JsonField.of(dedupeMode)) + + /** + * Sets [Builder.dedupeMode] to an arbitrary JSON value. + * + * You should usually call [Builder.dedupeMode] with a well-typed [DedupeMode] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dedupeMode(dedupeMode: JsonField) = apply { + this.dedupeMode = dedupeMode + } + + /** Exact phrase to match (tweet_search_extractor) */ + fun exactPhrase(exactPhrase: String) = exactPhrase(JsonField.of(exactPhrase)) + + /** + * Sets [Builder.exactPhrase] to an arbitrary JSON value. + * + * You should usually call [Builder.exactPhrase] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun exactPhrase(exactPhrase: JsonField) = apply { + this.exactPhrase = exactPhrase + } + + /** Exclude replies from the source author. */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean) = + excludeOriginalAuthor(JsonField.of(excludeOriginalAuthor)) + + /** + * Sets [Builder.excludeOriginalAuthor] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeOriginalAuthor] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun excludeOriginalAuthor(excludeOriginalAuthor: JsonField) = apply { + this.excludeOriginalAuthor = excludeOriginalAuthor + } + + /** Exclude a source application. */ + fun excludeSource(excludeSource: String) = excludeSource(JsonField.of(excludeSource)) + + /** + * Sets [Builder.excludeSource] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeSource] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun excludeSource(excludeSource: JsonField) = apply { + this.excludeSource = excludeSource + } + + /** + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + */ + fun excludeWords(excludeWords: String) = excludeWords(JsonField.of(excludeWords)) + + /** + * Sets [Builder.excludeWords] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeWords] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun excludeWords(excludeWords: JsonField) = apply { + this.excludeWords = excludeWords + } + + /** Filter by author username (tweet_search_extractor) */ + fun fromUser(fromUser: String) = fromUser(JsonField.of(fromUser)) + + /** + * Sets [Builder.fromUser] to an arbitrary JSON value. + * + * You should usually call [Builder.fromUser] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fromUser(fromUser: JsonField) = apply { this.fromUser = fromUser } + + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String) = geocode(JsonField.of(geocode)) + + /** + * Sets [Builder.geocode] to an arbitrary JSON value. + * + * You should usually call [Builder.geocode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun geocode(geocode: JsonField) = apply { this.geocode = geocode } + + /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun hashtags(hashtags: String) = hashtags(JsonField.of(hashtags)) + + /** + * Sets [Builder.hashtags] to an arbitrary JSON value. + * + * You should usually call [Builder.hashtags] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hashtags(hashtags: JsonField) = apply { this.hashtags = hashtags } + + /** Require a profile location. */ + fun hasLocation(hasLocation: Boolean) = hasLocation(JsonField.of(hasLocation)) + + /** + * Sets [Builder.hasLocation] to an arbitrary JSON value. + * + * You should usually call [Builder.hasLocation] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasLocation(hasLocation: JsonField) = apply { + this.hasLocation = hasLocation + } + + /** Return only replies with media. */ + fun hasMediaOnly(hasMediaOnly: Boolean) = hasMediaOnly(JsonField.of(hasMediaOnly)) + + /** + * Sets [Builder.hasMediaOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMediaOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasMediaOnly(hasMediaOnly: JsonField) = apply { + this.hasMediaOnly = hasMediaOnly + } + + /** Require a profile website. */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(JsonField.of(hasWebsite)) + + /** + * Sets [Builder.hasWebsite] to an arbitrary JSON value. + * + * You should usually call [Builder.hasWebsite] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasWebsite(hasWebsite: JsonField) = apply { this.hasWebsite = hasWebsite } + + /** Include the source post in reply results. */ + fun includeOriginalPost(includeOriginalPost: Boolean) = + includeOriginalPost(JsonField.of(includeOriginalPost)) + + /** + * Sets [Builder.includeOriginalPost] to an arbitrary JSON value. + * + * You should usually call [Builder.includeOriginalPost] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeOriginalPost(includeOriginalPost: JsonField) = apply { + this.includeOriginalPost = includeOriginalPost + } + + /** Add matching search terms to collection metadata. */ + fun includeSearchTerms(includeSearchTerms: Boolean) = + includeSearchTerms(JsonField.of(includeSearchTerms)) + + /** + * Sets [Builder.includeSearchTerms] to an arbitrary JSON value. + * + * You should usually call [Builder.includeSearchTerms] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeSearchTerms(includeSearchTerms: JsonField) = apply { + this.includeSearchTerms = includeSearchTerms + } + + /** Add source target metadata to each result. */ + fun includeTargetMetadata(includeTargetMetadata: Boolean) = + includeTargetMetadata(JsonField.of(includeTargetMetadata)) + + /** + * Sets [Builder.includeTargetMetadata] to an arbitrary JSON value. + * + * You should usually call [Builder.includeTargetMetadata] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeTargetMetadata(includeTargetMetadata: JsonField) = apply { + this.includeTargetMetadata = includeTargetMetadata + } + + /** Only replies to this tweet ID (tweet_search_extractor) */ + fun inReplyToTweetId(inReplyToTweetId: String) = + inReplyToTweetId(JsonField.of(inReplyToTweetId)) + + /** + * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { + this.inReplyToTweetId = inReplyToTweetId + } + + /** Language code filter (tweet_search_extractor) */ + fun language(language: String) = language(JsonField.of(language)) + + /** + * Sets [Builder.language] to an arbitrary JSON value. + * + * You should usually call [Builder.language] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun language(language: JsonField) = apply { this.language = language } + + /** Search within a list ID (tweet_search_extractor) */ + fun listId(listId: String) = listId(JsonField.of(listId)) + + /** + * Sets [Builder.listId] to an arbitrary JSON value. + * + * You should usually call [Builder.listId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun listId(listId: JsonField) = apply { this.listId = listId } + + /** Required profile location text. */ + fun locationContains(locationContains: String) = + locationContains(JsonField.of(locationContains)) + + /** + * Sets [Builder.locationContains] to an arbitrary JSON value. + * + * You should usually call [Builder.locationContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locationContains(locationContains: JsonField) = apply { + this.locationContains = locationContains + } + + /** Maximum nested reply depth. */ + fun maxDepth(maxDepth: Long) = maxDepth(JsonField.of(maxDepth)) + + /** + * Sets [Builder.maxDepth] to an arbitrary JSON value. + * + * You should usually call [Builder.maxDepth] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxDepth(maxDepth: JsonField) = apply { this.maxDepth = maxDepth } + + /** Maximum follower count for profile results. */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(JsonField.of(maxFollowers)) + + /** + * Sets [Builder.maxFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxFollowers(maxFollowers: JsonField) = apply { + this.maxFollowers = maxFollowers + } + + /** Maximum following count for profile results. */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(JsonField.of(maxFollowing)) + + /** + * Sets [Builder.maxFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowing] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxFollowing(maxFollowing: JsonField) = apply { + this.maxFollowing = maxFollowing + } + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String) = maxId(JsonField.of(maxId)) + + /** + * Sets [Builder.maxId] to an arbitrary JSON value. + * + * You should usually call [Builder.maxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxId(maxId: JsonField) = apply { this.maxId = maxId } + + /** Maximum results collected for each target. */ + fun maxItemsPerTarget(maxItemsPerTarget: Long) = + maxItemsPerTarget(JsonField.of(maxItemsPerTarget)) + + /** + * Sets [Builder.maxItemsPerTarget] to an arbitrary JSON value. + * + * You should usually call [Builder.maxItemsPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxItemsPerTarget(maxItemsPerTarget: JsonField) = apply { + this.maxItemsPerTarget = maxItemsPerTarget + } + + /** Maximum Tweet like count. */ + fun maxLikes(maxLikes: Long) = maxLikes(JsonField.of(maxLikes)) + + /** + * Sets [Builder.maxLikes] to an arbitrary JSON value. + * + * You should usually call [Builder.maxLikes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxLikes(maxLikes: JsonField) = apply { this.maxLikes = maxLikes } + + /** Reply pages collected for each target. */ + fun maxPagesPerTarget(maxPagesPerTarget: Long) = + maxPagesPerTarget(JsonField.of(maxPagesPerTarget)) + + /** + * Sets [Builder.maxPagesPerTarget] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPagesPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxPagesPerTarget(maxPagesPerTarget: JsonField) = apply { + this.maxPagesPerTarget = maxPagesPerTarget + } + + /** Maximum post count for profile results. */ + fun maxPosts(maxPosts: Long) = maxPosts(JsonField.of(maxPosts)) + + /** + * Sets [Builder.maxPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPosts] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxPosts(maxPosts: JsonField) = apply { this.maxPosts = maxPosts } + + /** Maximum Tweet quote count. */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(JsonField.of(maxQuotes)) + + /** + * Sets [Builder.maxQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.maxQuotes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxQuotes(maxQuotes: JsonField) = apply { this.maxQuotes = maxQuotes } + + /** Maximum Tweet reply count. */ + fun maxReplies(maxReplies: Long) = maxReplies(JsonField.of(maxReplies)) + + /** + * Sets [Builder.maxReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.maxReplies] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxReplies(maxReplies: JsonField) = apply { this.maxReplies = maxReplies } + + /** Maximum Tweet repost count. */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(JsonField.of(maxRetweets)) + + /** + * Sets [Builder.maxRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.maxRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxRetweets(maxRetweets: JsonField) = apply { this.maxRetweets = maxRetweets } + + /** Media type filter (tweet_search_extractor) */ + fun mediaType(mediaType: MediaType) = mediaType(JsonField.of(mediaType)) + + /** + * Sets [Builder.mediaType] to an arbitrary JSON value. + * + * You should usually call [Builder.mediaType] with a well-typed [MediaType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mediaType(mediaType: JsonField) = apply { this.mediaType = mediaType } + + /** Filter tweets mentioning a username (tweet_search_extractor) */ + fun mentioning(mentioning: String) = mentioning(JsonField.of(mentioning)) + + /** + * Sets [Builder.mentioning] to an arbitrary JSON value. + * + * You should usually call [Builder.mentioning] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mentioning(mentioning: JsonField) = apply { this.mentioning = mentioning } + + /** Minimum profile age in days. */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(JsonField.of(minAccountAgeDays)) + + /** + * Sets [Builder.minAccountAgeDays] to an arbitrary JSON value. + * + * You should usually call [Builder.minAccountAgeDays] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minAccountAgeDays(minAccountAgeDays: JsonField) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** Minimum Tweet bookmark count. */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(JsonField.of(minBookmarks)) + + /** + * Sets [Builder.minBookmarks] to an arbitrary JSON value. + * + * You should usually call [Builder.minBookmarks] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minBookmarks(minBookmarks: JsonField) = apply { + this.minBookmarks = minBookmarks + } + + /** Minimum likes threshold (tweet_search_extractor) */ + fun minFaves(minFaves: Long) = minFaves(JsonField.of(minFaves)) + + /** + * Sets [Builder.minFaves] to an arbitrary JSON value. + * + * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFaves(minFaves: JsonField) = apply { this.minFaves = minFaves } + + /** Minimum follower count for profile results. */ + fun minFollowers(minFollowers: Long) = minFollowers(JsonField.of(minFollowers)) + + /** + * Sets [Builder.minFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFollowers(minFollowers: JsonField) = apply { + this.minFollowers = minFollowers + } + + /** Minimum following count for profile results. */ + fun minFollowing(minFollowing: Long) = minFollowing(JsonField.of(minFollowing)) + + /** + * Sets [Builder.minFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowing] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFollowing(minFollowing: JsonField) = apply { + this.minFollowing = minFollowing + } + + /** Minimum post count for profile results. */ + fun minPosts(minPosts: Long) = minPosts(JsonField.of(minPosts)) + + /** + * Sets [Builder.minPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.minPosts] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minPosts(minPosts: JsonField) = apply { this.minPosts = minPosts } + + /** Minimum quote count threshold (tweet_search_extractor) */ + fun minQuotes(minQuotes: Long) = minQuotes(JsonField.of(minQuotes)) + + /** + * Sets [Builder.minQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minQuotes(minQuotes: JsonField) = apply { this.minQuotes = minQuotes } + + /** Minimum replies threshold (tweet_search_extractor) */ + fun minReplies(minReplies: Long) = minReplies(JsonField.of(minReplies)) + + /** + * Sets [Builder.minReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minReplies(minReplies: JsonField) = apply { this.minReplies = minReplies } + + /** Minimum retweets threshold (tweet_search_extractor) */ + fun minRetweets(minRetweets: Long) = minRetweets(JsonField.of(minRetweets)) + + /** + * Sets [Builder.minRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minRetweets(minRetweets: JsonField) = apply { this.minRetweets = minRetweets } + + /** Minimum Tweet view count. */ + fun minViews(minViews: Long) = minViews(JsonField.of(minViews)) + + /** + * Sets [Builder.minViews] to an arbitrary JSON value. + * + * You should usually call [Builder.minViews] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minViews(minViews: JsonField) = apply { this.minViews = minViews } + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean) = + nativeRetweets(JsonField.of(nativeRetweets)) + + /** + * Sets [Builder.nativeRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.nativeRetweets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nativeRetweets(nativeRetweets: JsonField) = apply { + this.nativeRetweets = nativeRetweets + } + + /** Match a place name. */ + fun near(near: String) = near(JsonField.of(near)) + + /** + * Sets [Builder.near] to an arbitrary JSON value. + * + * You should usually call [Builder.near] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun near(near: JsonField) = apply { this.near = near } + + /** Only return news results. */ + fun news(news: Boolean) = news(JsonField.of(news)) + + /** + * Sets [Builder.news] to an arbitrary JSON value. + * + * You should usually call [Builder.news] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun news(news: JsonField) = apply { this.news = news } + + /** Shortcut for dedupeMode=merge. */ + fun overlapMode(overlapMode: Boolean) = overlapMode(JsonField.of(overlapMode)) + + /** + * Sets [Builder.overlapMode] to an arbitrary JSON value. + * + * You should usually call [Builder.overlapMode] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun overlapMode(overlapMode: JsonField) = apply { + this.overlapMode = overlapMode + } + + /** Search within a place ID (tweet_search_extractor) */ + fun place(place: String) = place(JsonField.of(place)) + + /** + * Sets [Builder.place] to an arbitrary JSON value. + * + * You should usually call [Builder.place] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun place(place: JsonField) = apply { this.place = place } + + /** Search within a country code (tweet_search_extractor) */ + fun placeCountry(placeCountry: String) = placeCountry(JsonField.of(placeCountry)) + + /** + * Sets [Builder.placeCountry] to an arbitrary JSON value. + * + * You should usually call [Builder.placeCountry] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun placeCountry(placeCountry: JsonField) = apply { + this.placeCountry = placeCountry + } + + /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ + fun pointRadius(pointRadius: String) = pointRadius(JsonField.of(pointRadius)) + + /** + * Sets [Builder.pointRadius] to an arbitrary JSON value. + * + * You should usually call [Builder.pointRadius] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pointRadius(pointRadius: JsonField) = apply { + this.pointRadius = pointRadius + } + + /** Search ranking applied to every query. */ + fun queryType(queryType: QueryType) = queryType(JsonField.of(queryType)) + + /** + * Sets [Builder.queryType] to an arbitrary JSON value. + * + * You should usually call [Builder.queryType] with a well-typed [QueryType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun queryType(queryType: JsonField) = apply { this.queryType = queryType } + + /** Quote mode (tweet_search_extractor) */ + fun quotes(quotes: Quotes) = quotes(JsonField.of(quotes)) + + /** + * Sets [Builder.quotes] to an arbitrary JSON value. + * + * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quotes(quotes: JsonField) = apply { this.quotes = quotes } + + /** Only quotes of this tweet ID (tweet_search_extractor) */ + fun quotesOfTweetId(quotesOfTweetId: String) = + quotesOfTweetId(JsonField.of(quotesOfTweetId)) + + /** + * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { + this.quotesOfTweetId = quotesOfTweetId + } + + /** Profile relations processed within one job. */ + fun relationTargets(relationTargets: List) = + relationTargets(JsonField.of(relationTargets)) + + /** + * Sets [Builder.relationTargets] to an arbitrary JSON value. + * + * You should usually call [Builder.relationTargets] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun relationTargets(relationTargets: JsonField>) = apply { + this.relationTargets = relationTargets.map { it.toMutableList() } + } + + /** + * Adds a single [RelationTarget] to [relationTargets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRelationTarget(relationTarget: RelationTarget) = apply { + relationTargets = + (relationTargets ?: JsonField.of(mutableListOf())).also { + checkKnown("relationTargets", it).add(relationTarget) + } + } + + /** Reply mode (tweet_search_extractor) */ + fun replies(replies: Replies) = replies(JsonField.of(replies)) + + /** + * Sets [Builder.replies] to an arbitrary JSON value. + * + * You should usually call [Builder.replies] with a well-typed [Replies] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun replies(replies: JsonField) = apply { this.replies = replies } + + /** + * Maximum number of results to extract. When set, the extraction stops after reaching + * this limit. + */ + fun resultsLimit(resultsLimit: Long) = resultsLimit(JsonField.of(resultsLimit)) + + /** + * Sets [Builder.resultsLimit] to an arbitrary JSON value. + * + * You should usually call [Builder.resultsLimit] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun resultsLimit(resultsLimit: JsonField) = apply { + this.resultsLimit = resultsLimit + } + + /** Retweet mode (tweet_search_extractor) */ + fun retweets(retweets: Retweets) = retweets(JsonField.of(retweets)) + + /** + * Sets [Builder.retweets] to an arbitrary JSON value. + * + * You should usually call [Builder.retweets] with a well-typed [Retweets] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun retweets(retweets: JsonField) = apply { this.retweets = retweets } + + /** Only retweets of this tweet ID (tweet_search_extractor) */ + fun retweetsOfTweetId(retweetsOfTweetId: String) = + retweetsOfTweetId(JsonField.of(retweetsOfTweetId)) + + /** + * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { + this.retweetsOfTweetId = retweetsOfTweetId + } + + /** Enable the safe-search filter. */ + fun safe(safe: Boolean) = safe(JsonField.of(safe)) + + /** + * Sets [Builder.safe] to an arbitrary JSON value. + * + * You should usually call [Builder.safe] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun safe(safe: JsonField) = apply { this.safe = safe } + + /** Reply depth scope. */ + fun scope(scope: Scope) = scope(JsonField.of(scope)) + + /** + * Sets [Builder.scope] to an arbitrary JSON value. + * + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun scope(scope: JsonField) = apply { this.scope = scope } + + /** Search queries processed as one collection job. */ + fun searchQueries(searchQueries: List) = + searchQueries(JsonField.of(searchQueries)) + + /** + * Sets [Builder.searchQueries] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQueries] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun searchQueries(searchQueries: JsonField>) = apply { + this.searchQueries = searchQueries.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [searchQueries]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSearchQuery(searchQuery: String) = apply { + searchQueries = + (searchQueries ?: JsonField.of(mutableListOf())).also { + checkKnown("searchQueries", it).add(searchQuery) + } + } + + /** Required for tweet_search_extractor & community_search. */ + fun searchQuery(searchQuery: String) = searchQuery(JsonField.of(searchQuery)) + + /** + * Sets [Builder.searchQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQuery] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun searchQuery(searchQuery: JsonField) = apply { + this.searchQuery = searchQuery + } + + /** Start date YYYY-MM-DD (tweet_search_extractor) */ + fun sinceDate(sinceDate: LocalDate) = sinceDate(JsonField.of(sinceDate)) + + /** + * Sets [Builder.sinceDate] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceDate(sinceDate: JsonField) = apply { this.sinceDate = sinceDate } + + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String) = sinceId(JsonField.of(sinceId)) + + /** + * Sets [Builder.sinceId] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceId(sinceId: JsonField) = apply { this.sinceId = sinceId } + + /** Reply start time as ISO 8601 or Unix seconds. */ + fun sinceTime(sinceTime: SinceTime) = sinceTime(JsonField.of(sinceTime)) + + /** + * Sets [Builder.sinceTime] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceTime] with a well-typed [SinceTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceTime(sinceTime: JsonField) = apply { this.sinceTime = sinceTime } + + /** Alias for calling [sinceTime] with `SinceTime.ofOffsetDate(offsetDate)`. */ + fun sinceTime(offsetDate: OffsetDateTime) = + sinceTime(SinceTime.ofOffsetDate(offsetDate)) + + /** Alias for calling [sinceTime] with `SinceTime.ofInteger(integer)`. */ + fun sinceTime(integer: Long) = sinceTime(SinceTime.ofInteger(integer)) + + /** Reply result order. */ + fun sort(sort: Sort) = sort(JsonField.of(sort)) + + /** + * Sets [Builder.sort] to an arbitrary JSON value. + * + * You should usually call [Builder.sort] with a well-typed [Sort] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sort(sort: JsonField) = apply { this.sort = sort } + + /** Match the source application. */ + fun source(source: String) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Resume one reply target from this cursor. */ + fun startCursor(startCursor: String) = startCursor(JsonField.of(startCursor)) + + /** + * Sets [Builder.startCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.startCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startCursor(startCursor: JsonField) = apply { + this.startCursor = startCursor + } + + /** Required for community_post_extractor & community_search. */ + fun targetCommunityId(targetCommunityId: String) = + targetCommunityId(JsonField.of(targetCommunityId)) + + /** + * Sets [Builder.targetCommunityId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetCommunityId(targetCommunityId: JsonField) = apply { + this.targetCommunityId = targetCommunityId + } + + /** Community IDs processed as one collection job. */ + fun targetCommunityIds(targetCommunityIds: List) = + targetCommunityIds(JsonField.of(targetCommunityIds)) + + /** + * Sets [Builder.targetCommunityIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetCommunityIds(targetCommunityIds: JsonField>) = apply { + this.targetCommunityIds = targetCommunityIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetCommunityIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetCommunityId(targetCommunityId: String) = apply { + targetCommunityIds = + (targetCommunityIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetCommunityIds", it).add(targetCommunityId) + } + } + + /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ + fun targetListId(targetListId: String) = targetListId(JsonField.of(targetListId)) + + /** + * Sets [Builder.targetListId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetListId(targetListId: JsonField) = apply { + this.targetListId = targetListId + } + + /** List IDs processed as one collection job. */ + fun targetListIds(targetListIds: List) = + targetListIds(JsonField.of(targetListIds)) + + /** + * Sets [Builder.targetListIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetListIds(targetListIds: JsonField>) = apply { + this.targetListIds = targetListIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetListIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetListId(targetListId: String) = apply { + targetListIds = + (targetListIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetListIds", it).add(targetListId) + } + } + + /** Mixed targets auto-routed within one job. */ + fun targets(targets: List) = targets(JsonField.of(targets)) + + /** + * Sets [Builder.targets] to an arbitrary JSON value. + * + * You should usually call [Builder.targets] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targets(targets: JsonField>) = apply { + this.targets = targets.map { it.toMutableList() } + } + + /** + * Adds a single [Target] to [targets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTarget(target: Target) = apply { + targets = + (targets ?: JsonField.of(mutableListOf())).also { + checkKnown("targets", it).add(target) + } + } + + /** Alias for calling [addTarget] with `Target.ofString(string)`. */ + fun addTarget(string: String) = addTarget(Target.ofString(string)) + + /** Alias for calling [addTarget] with `Target.ofUnionMember1(unionMember1)`. */ + fun addTarget(unionMember1: Target.UnionMember1) = + addTarget(Target.ofUnionMember1(unionMember1)) + + /** Required for space_explorer. */ + fun targetSpaceId(targetSpaceId: String) = targetSpaceId(JsonField.of(targetSpaceId)) + + /** + * Sets [Builder.targetSpaceId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetSpaceId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetSpaceId(targetSpaceId: JsonField) = apply { + this.targetSpaceId = targetSpaceId + } + + fun targetTweetId(targetTweetId: String) = targetTweetId(JsonField.of(targetTweetId)) + + /** + * Sets [Builder.targetTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetTweetId(targetTweetId: JsonField) = apply { + this.targetTweetId = targetTweetId + } + + /** Tweet IDs processed as one collection job. */ + fun targetTweetIds(targetTweetIds: List) = + targetTweetIds(JsonField.of(targetTweetIds)) + + /** + * Sets [Builder.targetTweetIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetTweetIds(targetTweetIds: JsonField>) = apply { + this.targetTweetIds = targetTweetIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetTweetIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetTweetId(targetTweetId: String) = apply { + targetTweetIds = + (targetTweetIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetTweetIds", it).add(targetTweetId) + } + } + + fun targetUsername(targetUsername: String) = + targetUsername(JsonField.of(targetUsername)) + + /** + * Sets [Builder.targetUsername] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsername] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsername(targetUsername: JsonField) = apply { + this.targetUsername = targetUsername + } + + /** Usernames processed as one collection job. */ + fun targetUsernames(targetUsernames: List) = + targetUsernames(JsonField.of(targetUsernames)) + + /** + * Sets [Builder.targetUsernames] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsernames] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetUsernames(targetUsernames: JsonField>) = apply { + this.targetUsernames = targetUsernames.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetUsernames]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetUsername(targetUsername: String) = apply { + targetUsernames = + (targetUsernames ?: JsonField.of(mutableListOf())).also { + checkKnown("targetUsernames", it).add(targetUsername) + } + } + + /** Filter replies sent to a username (tweet_search_extractor) */ + fun toUser(toUser: String) = toUser(JsonField.of(toUser)) + + /** + * Sets [Builder.toUser] to an arbitrary JSON value. + * + * You should usually call [Builder.toUser] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun toUser(toUser: JsonField) = apply { this.toUser = toUser } + + /** End date YYYY-MM-DD (tweet_search_extractor) */ + fun untilDate(untilDate: LocalDate) = untilDate(JsonField.of(untilDate)) + + /** + * Sets [Builder.untilDate] to an arbitrary JSON value. + * + * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun untilDate(untilDate: JsonField) = apply { this.untilDate = untilDate } + + /** Reply end time as ISO 8601 or Unix seconds. */ + fun untilTime(untilTime: UntilTime) = untilTime(JsonField.of(untilTime)) + + /** + * Sets [Builder.untilTime] to an arbitrary JSON value. + * + * You should usually call [Builder.untilTime] with a well-typed [UntilTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun untilTime(untilTime: JsonField) = apply { this.untilTime = untilTime } + + /** Alias for calling [untilTime] with `UntilTime.ofOffsetDate(offsetDate)`. */ + fun untilTime(offsetDate: OffsetDateTime) = + untilTime(UntilTime.ofOffsetDate(offsetDate)) + + /** Alias for calling [untilTime] with `UntilTime.ofInteger(integer)`. */ + fun untilTime(integer: Long) = untilTime(UntilTime.ofInteger(integer)) + + /** URL substring or domain filter (tweet_search_extractor) */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun url(url: JsonField) = apply { this.url = url } + + /** Required username text. */ + fun usernameContains(usernameContains: String) = + usernameContains(JsonField.of(usernameContains)) + + /** + * Sets [Builder.usernameContains] to an arbitrary JSON value. + * + * You should usually call [Builder.usernameContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun usernameContains(usernameContains: JsonField) = apply { + this.usernameContains = usernameContains + } + + /** Only verified authors (tweet_search_extractor) */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(JsonField.of(verifiedOnly)) + + /** + * Sets [Builder.verifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun verifiedOnly(verifiedOnly: JsonField) = apply { + this.verifiedOnly = verifiedOnly + } + + /** Exact profile verification type. */ + fun verifiedType(verifiedType: String) = verifiedType(JsonField.of(verifiedType)) + + /** + * Sets [Builder.verifiedType] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedType] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun verifiedType(verifiedType: JsonField) = apply { + this.verifiedType = verifiedType + } + + /** Set the radius for the near filter. */ + fun within(within: String) = within(JsonField.of(within)) + + /** + * Sets [Builder.within] to an arbitrary JSON value. + * + * You should usually call [Builder.within] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun within(within: JsonField) = apply { this.within = within } + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String) = withinTime(JsonField.of(withinTime)) + + /** + * Sets [Builder.withinTime] to an arbitrary JSON value. + * + * You should usually call [Builder.withinTime] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun withinTime(withinTime: JsonField) = apply { this.withinTime = withinTime } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("toolType", toolType), + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + (relationTargets ?: JsonMissing.of()).map { it.toImmutable() }, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + (searchQueries ?: JsonMissing.of()).map { it.toImmutable() }, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + (targetCommunityIds ?: JsonMissing.of()).map { it.toImmutable() }, + targetListId, + (targetListIds ?: JsonMissing.of()).map { it.toImmutable() }, + (targets ?: JsonMissing.of()).map { it.toImmutable() }, + targetSpaceId, + targetTweetId, + (targetTweetIds ?: JsonMissing.of()).map { it.toImmutable() }, + targetUsername, + (targetUsernames ?: JsonMissing.of()).map { it.toImmutable() }, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + toolType().validate() + advancedQuery() + anyWords() + bioContains() + blueVerifiedOnly() + boundingBox() + cardName() + cashtags() + collectionStrategy().ifPresent { it.validate() } + conversationId() + dedupeAcrossTargets() + dedupeMode().ifPresent { it.validate() } + exactPhrase() + excludeOriginalAuthor() + excludeSource() + excludeWords() + fromUser() + geocode() + hashtags() + hasLocation() + hasMediaOnly() + hasWebsite() + includeOriginalPost() + includeSearchTerms() + includeTargetMetadata() + inReplyToTweetId() + language() + listId() + locationContains() + maxDepth() + maxFollowers() + maxFollowing() + maxId() + maxItemsPerTarget() + maxLikes() + maxPagesPerTarget() + maxPosts() + maxQuotes() + maxReplies() + maxRetweets() + mediaType().ifPresent { it.validate() } + mentioning() + minAccountAgeDays() + minBookmarks() + minFaves() + minFollowers() + minFollowing() + minPosts() + minQuotes() + minReplies() + minRetweets() + minViews() + nativeRetweets() + near() + news() + overlapMode() + place() + placeCountry() + pointRadius() + queryType().ifPresent { it.validate() } + quotes().ifPresent { it.validate() } + quotesOfTweetId() + relationTargets().ifPresent { it.forEach { it.validate() } } + replies().ifPresent { it.validate() } + resultsLimit() + retweets().ifPresent { it.validate() } + retweetsOfTweetId() + safe() + scope().ifPresent { it.validate() } + searchQueries() + searchQuery() + sinceDate() + sinceId() + sinceTime().ifPresent { it.validate() } + sort().ifPresent { it.validate() } + source() + startCursor() + targetCommunityId() + targetCommunityIds() + targetListId() + targetListIds() + targets().ifPresent { it.forEach { it.validate() } } + targetSpaceId() + targetTweetId() + targetTweetIds() + targetUsername() + targetUsernames() + toUser() + untilDate() + untilTime().ifPresent { it.validate() } + url() + usernameContains() + verifiedOnly() + verifiedType() + within() + withinTime() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (toolType.asKnown().getOrNull()?.validity() ?: 0) + + (if (advancedQuery.asKnown().isPresent) 1 else 0) + + (if (anyWords.asKnown().isPresent) 1 else 0) + + (if (bioContains.asKnown().isPresent) 1 else 0) + + (if (blueVerifiedOnly.asKnown().isPresent) 1 else 0) + + (if (boundingBox.asKnown().isPresent) 1 else 0) + + (if (cardName.asKnown().isPresent) 1 else 0) + + (if (cashtags.asKnown().isPresent) 1 else 0) + + (collectionStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (if (conversationId.asKnown().isPresent) 1 else 0) + + (if (dedupeAcrossTargets.asKnown().isPresent) 1 else 0) + + (dedupeMode.asKnown().getOrNull()?.validity() ?: 0) + + (if (exactPhrase.asKnown().isPresent) 1 else 0) + + (if (excludeOriginalAuthor.asKnown().isPresent) 1 else 0) + + (if (excludeSource.asKnown().isPresent) 1 else 0) + + (if (excludeWords.asKnown().isPresent) 1 else 0) + + (if (fromUser.asKnown().isPresent) 1 else 0) + + (if (geocode.asKnown().isPresent) 1 else 0) + + (if (hashtags.asKnown().isPresent) 1 else 0) + + (if (hasLocation.asKnown().isPresent) 1 else 0) + + (if (hasMediaOnly.asKnown().isPresent) 1 else 0) + + (if (hasWebsite.asKnown().isPresent) 1 else 0) + + (if (includeOriginalPost.asKnown().isPresent) 1 else 0) + + (if (includeSearchTerms.asKnown().isPresent) 1 else 0) + + (if (includeTargetMetadata.asKnown().isPresent) 1 else 0) + + (if (inReplyToTweetId.asKnown().isPresent) 1 else 0) + + (if (language.asKnown().isPresent) 1 else 0) + + (if (listId.asKnown().isPresent) 1 else 0) + + (if (locationContains.asKnown().isPresent) 1 else 0) + + (if (maxDepth.asKnown().isPresent) 1 else 0) + + (if (maxFollowers.asKnown().isPresent) 1 else 0) + + (if (maxFollowing.asKnown().isPresent) 1 else 0) + + (if (maxId.asKnown().isPresent) 1 else 0) + + (if (maxItemsPerTarget.asKnown().isPresent) 1 else 0) + + (if (maxLikes.asKnown().isPresent) 1 else 0) + + (if (maxPagesPerTarget.asKnown().isPresent) 1 else 0) + + (if (maxPosts.asKnown().isPresent) 1 else 0) + + (if (maxQuotes.asKnown().isPresent) 1 else 0) + + (if (maxReplies.asKnown().isPresent) 1 else 0) + + (if (maxRetweets.asKnown().isPresent) 1 else 0) + + (mediaType.asKnown().getOrNull()?.validity() ?: 0) + + (if (mentioning.asKnown().isPresent) 1 else 0) + + (if (minAccountAgeDays.asKnown().isPresent) 1 else 0) + + (if (minBookmarks.asKnown().isPresent) 1 else 0) + + (if (minFaves.asKnown().isPresent) 1 else 0) + + (if (minFollowers.asKnown().isPresent) 1 else 0) + + (if (minFollowing.asKnown().isPresent) 1 else 0) + + (if (minPosts.asKnown().isPresent) 1 else 0) + + (if (minQuotes.asKnown().isPresent) 1 else 0) + + (if (minReplies.asKnown().isPresent) 1 else 0) + + (if (minRetweets.asKnown().isPresent) 1 else 0) + + (if (minViews.asKnown().isPresent) 1 else 0) + + (if (nativeRetweets.asKnown().isPresent) 1 else 0) + + (if (near.asKnown().isPresent) 1 else 0) + + (if (news.asKnown().isPresent) 1 else 0) + + (if (overlapMode.asKnown().isPresent) 1 else 0) + + (if (place.asKnown().isPresent) 1 else 0) + + (if (placeCountry.asKnown().isPresent) 1 else 0) + + (if (pointRadius.asKnown().isPresent) 1 else 0) + + (queryType.asKnown().getOrNull()?.validity() ?: 0) + + (quotes.asKnown().getOrNull()?.validity() ?: 0) + + (if (quotesOfTweetId.asKnown().isPresent) 1 else 0) + + (relationTargets.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (replies.asKnown().getOrNull()?.validity() ?: 0) + + (if (resultsLimit.asKnown().isPresent) 1 else 0) + + (retweets.asKnown().getOrNull()?.validity() ?: 0) + + (if (retweetsOfTweetId.asKnown().isPresent) 1 else 0) + + (if (safe.asKnown().isPresent) 1 else 0) + + (scope.asKnown().getOrNull()?.validity() ?: 0) + + (searchQueries.asKnown().getOrNull()?.size ?: 0) + + (if (searchQuery.asKnown().isPresent) 1 else 0) + + (if (sinceDate.asKnown().isPresent) 1 else 0) + + (if (sinceId.asKnown().isPresent) 1 else 0) + + (sinceTime.asKnown().getOrNull()?.validity() ?: 0) + + (sort.asKnown().getOrNull()?.validity() ?: 0) + + (if (source.asKnown().isPresent) 1 else 0) + + (if (startCursor.asKnown().isPresent) 1 else 0) + + (if (targetCommunityId.asKnown().isPresent) 1 else 0) + + (targetCommunityIds.asKnown().getOrNull()?.size ?: 0) + + (if (targetListId.asKnown().isPresent) 1 else 0) + + (targetListIds.asKnown().getOrNull()?.size ?: 0) + + (targets.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (targetSpaceId.asKnown().isPresent) 1 else 0) + + (if (targetTweetId.asKnown().isPresent) 1 else 0) + + (targetTweetIds.asKnown().getOrNull()?.size ?: 0) + + (if (targetUsername.asKnown().isPresent) 1 else 0) + + (targetUsernames.asKnown().getOrNull()?.size ?: 0) + + (if (toUser.asKnown().isPresent) 1 else 0) + + (if (untilDate.asKnown().isPresent) 1 else 0) + + (untilTime.asKnown().getOrNull()?.validity() ?: 0) + + (if (url.asKnown().isPresent) 1 else 0) + + (if (usernameContains.asKnown().isPresent) 1 else 0) + + (if (verifiedOnly.asKnown().isPresent) 1 else 0) + + (if (verifiedType.asKnown().isPresent) 1 else 0) + + (if (within.asKnown().isPresent) 1 else 0) + + (if (withinTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + toolType == other.toolType && + advancedQuery == other.advancedQuery && + anyWords == other.anyWords && + bioContains == other.bioContains && + blueVerifiedOnly == other.blueVerifiedOnly && + boundingBox == other.boundingBox && + cardName == other.cardName && + cashtags == other.cashtags && + collectionStrategy == other.collectionStrategy && + conversationId == other.conversationId && + dedupeAcrossTargets == other.dedupeAcrossTargets && + dedupeMode == other.dedupeMode && + exactPhrase == other.exactPhrase && + excludeOriginalAuthor == other.excludeOriginalAuthor && + excludeSource == other.excludeSource && + excludeWords == other.excludeWords && + fromUser == other.fromUser && + geocode == other.geocode && + hashtags == other.hashtags && + hasLocation == other.hasLocation && + hasMediaOnly == other.hasMediaOnly && + hasWebsite == other.hasWebsite && + includeOriginalPost == other.includeOriginalPost && + includeSearchTerms == other.includeSearchTerms && + includeTargetMetadata == other.includeTargetMetadata && + inReplyToTweetId == other.inReplyToTweetId && + language == other.language && + listId == other.listId && + locationContains == other.locationContains && + maxDepth == other.maxDepth && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxId == other.maxId && + maxItemsPerTarget == other.maxItemsPerTarget && + maxLikes == other.maxLikes && + maxPagesPerTarget == other.maxPagesPerTarget && + maxPosts == other.maxPosts && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && + mediaType == other.mediaType && + mentioning == other.mentioning && + minAccountAgeDays == other.minAccountAgeDays && + minBookmarks == other.minBookmarks && + minFaves == other.minFaves && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minPosts == other.minPosts && + minQuotes == other.minQuotes && + minReplies == other.minReplies && + minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && + overlapMode == other.overlapMode && + place == other.place && + placeCountry == other.placeCountry && + pointRadius == other.pointRadius && + queryType == other.queryType && + quotes == other.quotes && + quotesOfTweetId == other.quotesOfTweetId && + relationTargets == other.relationTargets && + replies == other.replies && + resultsLimit == other.resultsLimit && + retweets == other.retweets && + retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && + scope == other.scope && + searchQueries == other.searchQueries && + searchQuery == other.searchQuery && + sinceDate == other.sinceDate && + sinceId == other.sinceId && + sinceTime == other.sinceTime && + sort == other.sort && + source == other.source && + startCursor == other.startCursor && + targetCommunityId == other.targetCommunityId && + targetCommunityIds == other.targetCommunityIds && + targetListId == other.targetListId && + targetListIds == other.targetListIds && + targets == other.targets && + targetSpaceId == other.targetSpaceId && + targetTweetId == other.targetTweetId && + targetTweetIds == other.targetTweetIds && + targetUsername == other.targetUsername && + targetUsernames == other.targetUsernames && + toUser == other.toUser && + untilDate == other.untilDate && + untilTime == other.untilTime && + url == other.url && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && + within == other.within && + withinTime == other.withinTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + toolType, + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + relationTargets, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + searchQueries, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + targetCommunityIds, + targetListId, + targetListIds, + targets, + targetSpaceId, + targetTweetId, + targetTweetIds, + targetUsername, + targetUsernames, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + additionalProperties, + ) + } - /** - * Estimate only verified authors (tweet_search_extractor) - * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun verifiedOnly(): Optional = verifiedOnly.getOptional("verifiedOnly") + override fun hashCode(): Int = hashCode - /** - * Returns the raw JSON value of [toolType]. - * - * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("toolType") @ExcludeMissing fun _toolType(): JsonField = toolType + override fun toString() = + "Body{toolType=$toolType, advancedQuery=$advancedQuery, anyWords=$anyWords, bioContains=$bioContains, blueVerifiedOnly=$blueVerifiedOnly, boundingBox=$boundingBox, cardName=$cardName, cashtags=$cashtags, collectionStrategy=$collectionStrategy, conversationId=$conversationId, dedupeAcrossTargets=$dedupeAcrossTargets, dedupeMode=$dedupeMode, exactPhrase=$exactPhrase, excludeOriginalAuthor=$excludeOriginalAuthor, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, hasLocation=$hasLocation, hasMediaOnly=$hasMediaOnly, hasWebsite=$hasWebsite, includeOriginalPost=$includeOriginalPost, includeSearchTerms=$includeSearchTerms, includeTargetMetadata=$includeTargetMetadata, inReplyToTweetId=$inReplyToTweetId, language=$language, listId=$listId, locationContains=$locationContains, maxDepth=$maxDepth, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxId=$maxId, maxItemsPerTarget=$maxItemsPerTarget, maxLikes=$maxLikes, maxPagesPerTarget=$maxPagesPerTarget, maxPosts=$maxPosts, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minAccountAgeDays=$minAccountAgeDays, minBookmarks=$minBookmarks, minFaves=$minFaves, minFollowers=$minFollowers, minFollowing=$minFollowing, minPosts=$minPosts, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, overlapMode=$overlapMode, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, queryType=$queryType, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, relationTargets=$relationTargets, replies=$replies, resultsLimit=$resultsLimit, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, scope=$scope, searchQueries=$searchQueries, searchQuery=$searchQuery, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, sort=$sort, source=$source, startCursor=$startCursor, targetCommunityId=$targetCommunityId, targetCommunityIds=$targetCommunityIds, targetListId=$targetListId, targetListIds=$targetListIds, targets=$targets, targetSpaceId=$targetSpaceId, targetTweetId=$targetTweetId, targetTweetIds=$targetTweetIds, targetUsername=$targetUsername, targetUsernames=$targetUsernames, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, within=$within, withinTime=$withinTime, additionalProperties=$additionalProperties}" + } - /** - * Returns the raw JSON value of [advancedQuery]. - * - * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("advancedQuery") - @ExcludeMissing - fun _advancedQuery(): JsonField = advancedQuery + /** Identifier for the extraction tool used to run a job. */ + class ToolType @JsonCreator private constructor(private val value: JsonField) : Enum { /** - * Returns the raw JSON value of [anyWords]. + * Returns this class instance's raw value. * - * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. */ - @JsonProperty("anyWords") @ExcludeMissing fun _anyWords(): JsonField = anyWords + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Returns the raw JSON value of [boundingBox]. - * - * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("boundingBox") - @ExcludeMissing - fun _boundingBox(): JsonField = boundingBox + companion object { - /** - * Returns the raw JSON value of [cashtags]. - * - * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("cashtags") @ExcludeMissing fun _cashtags(): JsonField = cashtags + @JvmField val ARTICLE_EXTRACTOR = of("article_extractor") - /** - * Returns the raw JSON value of [conversationId]. - * - * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("conversationId") - @ExcludeMissing - fun _conversationId(): JsonField = conversationId + @JvmField val COMMUNITY_EXTRACTOR = of("community_extractor") - /** - * Returns the raw JSON value of [exactPhrase]. - * - * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("exactPhrase") - @ExcludeMissing - fun _exactPhrase(): JsonField = exactPhrase + @JvmField val COMMUNITY_MODERATOR_EXPLORER = of("community_moderator_explorer") - /** - * Returns the raw JSON value of [excludeWords]. - * - * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("excludeWords") - @ExcludeMissing - fun _excludeWords(): JsonField = excludeWords + @JvmField val COMMUNITY_POST_EXTRACTOR = of("community_post_extractor") - /** - * Returns the raw JSON value of [fromUser]. - * - * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("fromUser") @ExcludeMissing fun _fromUser(): JsonField = fromUser + @JvmField val COMMUNITY_SEARCH = of("community_search") - /** - * Returns the raw JSON value of [hashtags]. - * - * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("hashtags") @ExcludeMissing fun _hashtags(): JsonField = hashtags + @JvmField val FAVORITERS = of("favoriters") - /** - * Returns the raw JSON value of [inReplyToTweetId]. - * - * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("inReplyToTweetId") - @ExcludeMissing - fun _inReplyToTweetId(): JsonField = inReplyToTweetId + @JvmField val FOLLOWER_EXPLORER = of("follower_explorer") - /** - * Returns the raw JSON value of [language]. - * - * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + @JvmField val FOLLOWING_EXPLORER = of("following_explorer") - /** - * Returns the raw JSON value of [listId]. - * - * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("listId") @ExcludeMissing fun _listId(): JsonField = listId + @JvmField val LIST_FOLLOWER_EXPLORER = of("list_follower_explorer") - /** - * Returns the raw JSON value of [mediaType]. - * - * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("mediaType") - @ExcludeMissing - fun _mediaType(): JsonField = mediaType + @JvmField val LIST_MEMBER_EXTRACTOR = of("list_member_extractor") - /** - * Returns the raw JSON value of [mentioning]. - * - * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("mentioning") - @ExcludeMissing - fun _mentioning(): JsonField = mentioning + @JvmField val LIST_POST_EXTRACTOR = of("list_post_extractor") - /** - * Returns the raw JSON value of [minFaves]. - * - * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("minFaves") @ExcludeMissing fun _minFaves(): JsonField = minFaves + @JvmField val MENTION_EXTRACTOR = of("mention_extractor") - /** - * Returns the raw JSON value of [minQuotes]. - * - * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("minQuotes") @ExcludeMissing fun _minQuotes(): JsonField = minQuotes + @JvmField val PEOPLE_SEARCH = of("people_search") - /** - * Returns the raw JSON value of [minReplies]. - * - * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("minReplies") @ExcludeMissing fun _minReplies(): JsonField = minReplies + @JvmField val POST_EXTRACTOR = of("post_extractor") - /** - * Returns the raw JSON value of [minRetweets]. - * - * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("minRetweets") - @ExcludeMissing - fun _minRetweets(): JsonField = minRetweets + @JvmField val QUOTE_EXTRACTOR = of("quote_extractor") - /** - * Returns the raw JSON value of [place]. - * - * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("place") @ExcludeMissing fun _place(): JsonField = place + @JvmField val REPLY_EXTRACTOR = of("reply_extractor") - /** - * Returns the raw JSON value of [placeCountry]. - * - * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("placeCountry") - @ExcludeMissing - fun _placeCountry(): JsonField = placeCountry + @JvmField val REPOST_EXTRACTOR = of("repost_extractor") - /** - * Returns the raw JSON value of [pointRadius]. - * - * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("pointRadius") - @ExcludeMissing - fun _pointRadius(): JsonField = pointRadius + @JvmField val SPACE_EXPLORER = of("space_explorer") - /** - * Returns the raw JSON value of [quotes]. - * - * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("quotes") @ExcludeMissing fun _quotes(): JsonField = quotes + @JvmField val THREAD_EXTRACTOR = of("thread_extractor") - /** - * Returns the raw JSON value of [quotesOfTweetId]. - * - * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("quotesOfTweetId") - @ExcludeMissing - fun _quotesOfTweetId(): JsonField = quotesOfTweetId + @JvmField val TWEET_SEARCH_EXTRACTOR = of("tweet_search_extractor") - /** - * Returns the raw JSON value of [replies]. - * - * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("replies") @ExcludeMissing fun _replies(): JsonField = replies + @JvmField val USER_LIKES = of("user_likes") - /** - * Returns the raw JSON value of [resultsLimit]. - * - * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("resultsLimit") - @ExcludeMissing - fun _resultsLimit(): JsonField = resultsLimit + @JvmField val USER_MEDIA = of("user_media") - /** - * Returns the raw JSON value of [retweets]. - * - * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("retweets") @ExcludeMissing fun _retweets(): JsonField = retweets + @JvmField val VERIFIED_FOLLOWER_EXPLORER = of("verified_follower_explorer") - /** - * Returns the raw JSON value of [retweetsOfTweetId]. - * - * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("retweetsOfTweetId") - @ExcludeMissing - fun _retweetsOfTweetId(): JsonField = retweetsOfTweetId + @JvmStatic fun of(value: String) = ToolType(JsonField.of(value)) + } - /** - * Returns the raw JSON value of [searchQuery]. - * - * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("searchQuery") - @ExcludeMissing - fun _searchQuery(): JsonField = searchQuery + /** An enum containing [ToolType]'s known values. */ + enum class Known { + ARTICLE_EXTRACTOR, + COMMUNITY_EXTRACTOR, + COMMUNITY_MODERATOR_EXPLORER, + COMMUNITY_POST_EXTRACTOR, + COMMUNITY_SEARCH, + FAVORITERS, + FOLLOWER_EXPLORER, + FOLLOWING_EXPLORER, + LIST_FOLLOWER_EXPLORER, + LIST_MEMBER_EXTRACTOR, + LIST_POST_EXTRACTOR, + MENTION_EXTRACTOR, + PEOPLE_SEARCH, + POST_EXTRACTOR, + QUOTE_EXTRACTOR, + REPLY_EXTRACTOR, + REPOST_EXTRACTOR, + SPACE_EXPLORER, + THREAD_EXTRACTOR, + TWEET_SEARCH_EXTRACTOR, + USER_LIKES, + USER_MEDIA, + VERIFIED_FOLLOWER_EXPLORER, + } /** - * Returns the raw JSON value of [sinceDate]. + * An enum containing [ToolType]'s known values, as well as an [_UNKNOWN] member. * - * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + * An instance of [ToolType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. */ - @JsonProperty("sinceDate") - @ExcludeMissing - fun _sinceDate(): JsonField = sinceDate + enum class Value { + ARTICLE_EXTRACTOR, + COMMUNITY_EXTRACTOR, + COMMUNITY_MODERATOR_EXPLORER, + COMMUNITY_POST_EXTRACTOR, + COMMUNITY_SEARCH, + FAVORITERS, + FOLLOWER_EXPLORER, + FOLLOWING_EXPLORER, + LIST_FOLLOWER_EXPLORER, + LIST_MEMBER_EXTRACTOR, + LIST_POST_EXTRACTOR, + MENTION_EXTRACTOR, + PEOPLE_SEARCH, + POST_EXTRACTOR, + QUOTE_EXTRACTOR, + REPLY_EXTRACTOR, + REPOST_EXTRACTOR, + SPACE_EXPLORER, + THREAD_EXTRACTOR, + TWEET_SEARCH_EXTRACTOR, + USER_LIKES, + USER_MEDIA, + VERIFIED_FOLLOWER_EXPLORER, + /** An enum member indicating that [ToolType] was instantiated with an unknown value. */ + _UNKNOWN, + } /** - * Returns the raw JSON value of [targetCommunityId]. + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. * - * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected - * type. + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. */ - @JsonProperty("targetCommunityId") - @ExcludeMissing - fun _targetCommunityId(): JsonField = targetCommunityId + fun value(): Value = + when (this) { + ARTICLE_EXTRACTOR -> Value.ARTICLE_EXTRACTOR + COMMUNITY_EXTRACTOR -> Value.COMMUNITY_EXTRACTOR + COMMUNITY_MODERATOR_EXPLORER -> Value.COMMUNITY_MODERATOR_EXPLORER + COMMUNITY_POST_EXTRACTOR -> Value.COMMUNITY_POST_EXTRACTOR + COMMUNITY_SEARCH -> Value.COMMUNITY_SEARCH + FAVORITERS -> Value.FAVORITERS + FOLLOWER_EXPLORER -> Value.FOLLOWER_EXPLORER + FOLLOWING_EXPLORER -> Value.FOLLOWING_EXPLORER + LIST_FOLLOWER_EXPLORER -> Value.LIST_FOLLOWER_EXPLORER + LIST_MEMBER_EXTRACTOR -> Value.LIST_MEMBER_EXTRACTOR + LIST_POST_EXTRACTOR -> Value.LIST_POST_EXTRACTOR + MENTION_EXTRACTOR -> Value.MENTION_EXTRACTOR + PEOPLE_SEARCH -> Value.PEOPLE_SEARCH + POST_EXTRACTOR -> Value.POST_EXTRACTOR + QUOTE_EXTRACTOR -> Value.QUOTE_EXTRACTOR + REPLY_EXTRACTOR -> Value.REPLY_EXTRACTOR + REPOST_EXTRACTOR -> Value.REPOST_EXTRACTOR + SPACE_EXPLORER -> Value.SPACE_EXPLORER + THREAD_EXTRACTOR -> Value.THREAD_EXTRACTOR + TWEET_SEARCH_EXTRACTOR -> Value.TWEET_SEARCH_EXTRACTOR + USER_LIKES -> Value.USER_LIKES + USER_MEDIA -> Value.USER_MEDIA + VERIFIED_FOLLOWER_EXPLORER -> Value.VERIFIED_FOLLOWER_EXPLORER + else -> Value._UNKNOWN + } /** - * Returns the raw JSON value of [targetListId]. + * Returns an enum member corresponding to this class instance's value. * - * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("targetListId") - @ExcludeMissing - fun _targetListId(): JsonField = targetListId - - /** - * Returns the raw JSON value of [targetSpaceId]. + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. * - * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. */ - @JsonProperty("targetSpaceId") - @ExcludeMissing - fun _targetSpaceId(): JsonField = targetSpaceId + fun known(): Known = + when (this) { + ARTICLE_EXTRACTOR -> Known.ARTICLE_EXTRACTOR + COMMUNITY_EXTRACTOR -> Known.COMMUNITY_EXTRACTOR + COMMUNITY_MODERATOR_EXPLORER -> Known.COMMUNITY_MODERATOR_EXPLORER + COMMUNITY_POST_EXTRACTOR -> Known.COMMUNITY_POST_EXTRACTOR + COMMUNITY_SEARCH -> Known.COMMUNITY_SEARCH + FAVORITERS -> Known.FAVORITERS + FOLLOWER_EXPLORER -> Known.FOLLOWER_EXPLORER + FOLLOWING_EXPLORER -> Known.FOLLOWING_EXPLORER + LIST_FOLLOWER_EXPLORER -> Known.LIST_FOLLOWER_EXPLORER + LIST_MEMBER_EXTRACTOR -> Known.LIST_MEMBER_EXTRACTOR + LIST_POST_EXTRACTOR -> Known.LIST_POST_EXTRACTOR + MENTION_EXTRACTOR -> Known.MENTION_EXTRACTOR + PEOPLE_SEARCH -> Known.PEOPLE_SEARCH + POST_EXTRACTOR -> Known.POST_EXTRACTOR + QUOTE_EXTRACTOR -> Known.QUOTE_EXTRACTOR + REPLY_EXTRACTOR -> Known.REPLY_EXTRACTOR + REPOST_EXTRACTOR -> Known.REPOST_EXTRACTOR + SPACE_EXPLORER -> Known.SPACE_EXPLORER + THREAD_EXTRACTOR -> Known.THREAD_EXTRACTOR + TWEET_SEARCH_EXTRACTOR -> Known.TWEET_SEARCH_EXTRACTOR + USER_LIKES -> Known.USER_LIKES + USER_MEDIA -> Known.USER_MEDIA + VERIFIED_FOLLOWER_EXPLORER -> Known.VERIFIED_FOLLOWER_EXPLORER + else -> throw XTwitterScraperInvalidDataException("Unknown ToolType: $value") + } /** - * Returns the raw JSON value of [targetTweetId]. + * Returns this class instance's primitive wire representation. * - * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("targetTweetId") - @ExcludeMissing - fun _targetTweetId(): JsonField = targetTweetId - - /** - * Returns the raw JSON value of [targetUsername]. + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. * - * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. */ - @JsonProperty("targetUsername") - @ExcludeMissing - fun _targetUsername(): JsonField = targetUsername + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - /** - * Returns the raw JSON value of [toUser]. - * - * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("toUser") @ExcludeMissing fun _toUser(): JsonField = toUser + private var validated: Boolean = false /** - * Returns the raw JSON value of [untilDate]. + * Validates that the types of all values in this object match their expected types + * recursively. * - * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("untilDate") - @ExcludeMissing - fun _untilDate(): JsonField = untilDate - - /** - * Returns the raw JSON value of [url]. + * This method is _not_ forwards compatible with new types from the API for existing fields. * - * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. */ - @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + fun validate(): ToolType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } /** - * Returns the raw JSON value of [verifiedOnly]. + * Returns a score indicating how many valid values are contained in this object + * recursively. * - * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected - * type. + * Used for best match union deserialization. */ - @JsonProperty("verifiedOnly") - @ExcludeMissing - fun _verifiedOnly(): JsonField = verifiedOnly + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ToolType && value == other.value } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + override fun hashCode() = value.hashCode() - fun toBuilder() = Builder().from(this) + override fun toString() = value.toString() + } + + /** Reply collection strategy. */ + class CollectionStrategy + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value companion object { - /** - * Returns a mutable builder for constructing an instance of [Body]. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + @JvmField val AUTO = of("auto") - /** A builder for [Body]. */ - class Builder internal constructor() { + @JvmField val COMPLETE = of("complete") - private var toolType: JsonField? = null - private var advancedQuery: JsonField = JsonMissing.of() - private var anyWords: JsonField = JsonMissing.of() - private var boundingBox: JsonField = JsonMissing.of() - private var cashtags: JsonField = JsonMissing.of() - private var conversationId: JsonField = JsonMissing.of() - private var exactPhrase: JsonField = JsonMissing.of() - private var excludeWords: JsonField = JsonMissing.of() - private var fromUser: JsonField = JsonMissing.of() - private var hashtags: JsonField = JsonMissing.of() - private var inReplyToTweetId: JsonField = JsonMissing.of() - private var language: JsonField = JsonMissing.of() - private var listId: JsonField = JsonMissing.of() - private var mediaType: JsonField = JsonMissing.of() - private var mentioning: JsonField = JsonMissing.of() - private var minFaves: JsonField = JsonMissing.of() - private var minQuotes: JsonField = JsonMissing.of() - private var minReplies: JsonField = JsonMissing.of() - private var minRetweets: JsonField = JsonMissing.of() - private var place: JsonField = JsonMissing.of() - private var placeCountry: JsonField = JsonMissing.of() - private var pointRadius: JsonField = JsonMissing.of() - private var quotes: JsonField = JsonMissing.of() - private var quotesOfTweetId: JsonField = JsonMissing.of() - private var replies: JsonField = JsonMissing.of() - private var resultsLimit: JsonField = JsonMissing.of() - private var retweets: JsonField = JsonMissing.of() - private var retweetsOfTweetId: JsonField = JsonMissing.of() - private var searchQuery: JsonField = JsonMissing.of() - private var sinceDate: JsonField = JsonMissing.of() - private var targetCommunityId: JsonField = JsonMissing.of() - private var targetListId: JsonField = JsonMissing.of() - private var targetSpaceId: JsonField = JsonMissing.of() - private var targetTweetId: JsonField = JsonMissing.of() - private var targetUsername: JsonField = JsonMissing.of() - private var toUser: JsonField = JsonMissing.of() - private var untilDate: JsonField = JsonMissing.of() - private var url: JsonField = JsonMissing.of() - private var verifiedOnly: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + @JvmField val DIRECT = of("direct") - @JvmSynthetic - internal fun from(body: Body) = apply { - toolType = body.toolType - advancedQuery = body.advancedQuery - anyWords = body.anyWords - boundingBox = body.boundingBox - cashtags = body.cashtags - conversationId = body.conversationId - exactPhrase = body.exactPhrase - excludeWords = body.excludeWords - fromUser = body.fromUser - hashtags = body.hashtags - inReplyToTweetId = body.inReplyToTweetId - language = body.language - listId = body.listId - mediaType = body.mediaType - mentioning = body.mentioning - minFaves = body.minFaves - minQuotes = body.minQuotes - minReplies = body.minReplies - minRetweets = body.minRetweets - place = body.place - placeCountry = body.placeCountry - pointRadius = body.pointRadius - quotes = body.quotes - quotesOfTweetId = body.quotesOfTweetId - replies = body.replies - resultsLimit = body.resultsLimit - retweets = body.retweets - retweetsOfTweetId = body.retweetsOfTweetId - searchQuery = body.searchQuery - sinceDate = body.sinceDate - targetCommunityId = body.targetCommunityId - targetListId = body.targetListId - targetSpaceId = body.targetSpaceId - targetTweetId = body.targetTweetId - targetUsername = body.targetUsername - toUser = body.toUser - untilDate = body.untilDate - url = body.url - verifiedOnly = body.verifiedOnly - additionalProperties = body.additionalProperties.toMutableMap() - } + @JvmField val SEARCH = of("search") - /** Identifier for the extraction tool used to run a job. */ - fun toolType(toolType: ToolType) = toolType(JsonField.of(toolType)) + @JvmField val THREAD = of("thread") - /** - * Sets [Builder.toolType] to an arbitrary JSON value. - * - * You should usually call [Builder.toolType] with a well-typed [ToolType] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun toolType(toolType: JsonField) = apply { this.toolType = toolType } + @JvmStatic fun of(value: String) = CollectionStrategy(JsonField.of(value)) + } - /** Raw advanced query string appended to the estimate (tweet_search_extractor) */ - fun advancedQuery(advancedQuery: String) = advancedQuery(JsonField.of(advancedQuery)) + /** An enum containing [CollectionStrategy]'s known values. */ + enum class Known { + AUTO, + COMPLETE, + DIRECT, + SEARCH, + THREAD, + } + /** + * An enum containing [CollectionStrategy]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CollectionStrategy] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + AUTO, + COMPLETE, + DIRECT, + SEARCH, + THREAD, /** - * Sets [Builder.advancedQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.advancedQuery] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [CollectionStrategy] was instantiated with an unknown + * value. */ - fun advancedQuery(advancedQuery: JsonField) = apply { - this.advancedQuery = advancedQuery + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + AUTO -> Value.AUTO + COMPLETE -> Value.COMPLETE + DIRECT -> Value.DIRECT + SEARCH -> Value.SEARCH + THREAD -> Value.THREAD + else -> Value._UNKNOWN } - /** - * Alternative words or quoted phrases for estimated results. Separate with spaces, - * commas, or lines. - */ - fun anyWords(anyWords: String) = anyWords(JsonField.of(anyWords)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + AUTO -> Known.AUTO + COMPLETE -> Known.COMPLETE + DIRECT -> Known.DIRECT + SEARCH -> Known.SEARCH + THREAD -> Known.THREAD + else -> + throw XTwitterScraperInvalidDataException("Unknown CollectionStrategy: $value") + } - /** - * Sets [Builder.anyWords] to an arbitrary JSON value. - * - * You should usually call [Builder.anyWords] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun anyWords(anyWords: JsonField) = apply { this.anyWords = anyWords } + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - /** - * Geo bounding box used for estimation, e.g. -74.1 40.6 -73.9 40.8 - * (tweet_search_extractor) - */ - fun boundingBox(boundingBox: String) = boundingBox(JsonField.of(boundingBox)) + private var validated: Boolean = false - /** - * Sets [Builder.boundingBox] to an arbitrary JSON value. - * - * You should usually call [Builder.boundingBox] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun boundingBox(boundingBox: JsonField) = apply { - this.boundingBox = boundingBox + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): CollectionStrategy = apply { + if (validated) { + return@apply } - /** Cashtags applied to the estimate, separated by spaces, commas, or lines. */ - fun cashtags(cashtags: String) = cashtags(JsonField.of(cashtags)) + known() + validated = true + } - /** - * Sets [Builder.cashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.cashtags] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun cashtags(cashtags: JsonField) = apply { this.cashtags = cashtags } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - /** Conversation ID filter used for estimation (tweet_search_extractor) */ - fun conversationId(conversationId: String) = - conversationId(JsonField.of(conversationId)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.conversationId] to an arbitrary JSON value. - * - * You should usually call [Builder.conversationId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun conversationId(conversationId: JsonField) = apply { - this.conversationId = conversationId + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** Exact phrase filter for search estimation */ - fun exactPhrase(exactPhrase: String) = exactPhrase(JsonField.of(exactPhrase)) + return other is CollectionStrategy && value == other.value + } - /** - * Sets [Builder.exactPhrase] to an arbitrary JSON value. - * - * You should usually call [Builder.exactPhrase] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun exactPhrase(exactPhrase: JsonField) = apply { - this.exactPhrase = exactPhrase - } + override fun hashCode() = value.hashCode() - /** - * Words or quoted phrases excluded from estimated results. Separate with spaces, - * commas, or lines. - */ - fun excludeWords(excludeWords: String) = excludeWords(JsonField.of(excludeWords)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.excludeWords] to an arbitrary JSON value. - * - * You should usually call [Builder.excludeWords] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun excludeWords(excludeWords: JsonField) = apply { - this.excludeWords = excludeWords - } + /** Keep target duplicates, first rows, or merged overlap. */ + class DedupeMode @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Estimate only tweets from this author username (tweet_search_extractor) */ - fun fromUser(fromUser: String) = fromUser(JsonField.of(fromUser)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.fromUser] to an arbitrary JSON value. - * - * You should usually call [Builder.fromUser] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun fromUser(fromUser: JsonField) = apply { this.fromUser = fromUser } + companion object { - /** Hashtags applied to the estimate, separated by spaces, commas, or lines. */ - fun hashtags(hashtags: String) = hashtags(JsonField.of(hashtags)) + @JvmField val NONE = of("none") - /** - * Sets [Builder.hashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.hashtags] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun hashtags(hashtags: JsonField) = apply { this.hashtags = hashtags } + @JvmField val FIRST = of("first") - /** Estimate only replies to this tweet ID (tweet_search_extractor) */ - fun inReplyToTweetId(inReplyToTweetId: String) = - inReplyToTweetId(JsonField.of(inReplyToTweetId)) + @JvmField val MERGE = of("merge") + + @JvmStatic fun of(value: String) = DedupeMode(JsonField.of(value)) + } + + /** An enum containing [DedupeMode]'s known values. */ + enum class Known { + NONE, + FIRST, + MERGE, + } + /** + * An enum containing [DedupeMode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DedupeMode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + NONE, + FIRST, + MERGE, /** - * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [DedupeMode] was instantiated with an unknown value. */ - fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { - this.inReplyToTweetId = inReplyToTweetId + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + NONE -> Value.NONE + FIRST -> Value.FIRST + MERGE -> Value.MERGE + else -> Value._UNKNOWN } - /** Language code used for estimate filtering (tweet_search_extractor) */ - fun language(language: String) = language(JsonField.of(language)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + NONE -> Known.NONE + FIRST -> Known.FIRST + MERGE -> Known.MERGE + else -> throw XTwitterScraperInvalidDataException("Unknown DedupeMode: $value") + } - /** - * Sets [Builder.language] to an arbitrary JSON value. - * - * You should usually call [Builder.language] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun language(language: JsonField) = apply { this.language = language } + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - /** Estimate search results within this list ID (tweet_search_extractor) */ - fun listId(listId: String) = listId(JsonField.of(listId)) + private var validated: Boolean = false - /** - * Sets [Builder.listId] to an arbitrary JSON value. - * - * You should usually call [Builder.listId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun listId(listId: JsonField) = apply { this.listId = listId } + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): DedupeMode = apply { + if (validated) { + return@apply + } - /** Media type used for estimate filtering (tweet_search_extractor) */ - fun mediaType(mediaType: MediaType) = mediaType(JsonField.of(mediaType)) + known() + validated = true + } - /** - * Sets [Builder.mediaType] to an arbitrary JSON value. - * - * You should usually call [Builder.mediaType] with a well-typed [MediaType] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun mediaType(mediaType: JsonField) = apply { this.mediaType = mediaType } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - /** Estimate tweets mentioning this username (tweet_search_extractor) */ - fun mentioning(mentioning: String) = mentioning(JsonField.of(mentioning)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.mentioning] to an arbitrary JSON value. - * - * You should usually call [Builder.mentioning] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun mentioning(mentioning: JsonField) = apply { this.mentioning = mentioning } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** Minimum likes threshold for estimated results (tweet_search_extractor) */ - fun minFaves(minFaves: Long) = minFaves(JsonField.of(minFaves)) + return other is DedupeMode && value == other.value + } - /** - * Sets [Builder.minFaves] to an arbitrary JSON value. - * - * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minFaves(minFaves: JsonField) = apply { this.minFaves = minFaves } + override fun hashCode() = value.hashCode() - /** Minimum quote count threshold for estimated results (tweet_search_extractor) */ - fun minQuotes(minQuotes: Long) = minQuotes(JsonField.of(minQuotes)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.minQuotes] to an arbitrary JSON value. - * - * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minQuotes(minQuotes: JsonField) = apply { this.minQuotes = minQuotes } + /** Media type filter (tweet_search_extractor) */ + class MediaType @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Minimum replies threshold for estimated results (tweet_search_extractor) */ - fun minReplies(minReplies: Long) = minReplies(JsonField.of(minReplies)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.minReplies] to an arbitrary JSON value. - * - * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minReplies(minReplies: JsonField) = apply { this.minReplies = minReplies } + companion object { - /** Minimum retweets threshold for estimated results (tweet_search_extractor) */ - fun minRetweets(minRetweets: Long) = minRetweets(JsonField.of(minRetweets)) + @JvmField val IMAGES = of("images") - /** - * Sets [Builder.minRetweets] to an arbitrary JSON value. - * - * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minRetweets(minRetweets: JsonField) = apply { this.minRetweets = minRetweets } + @JvmField val VIDEOS = of("videos") - /** Estimate search results within this place ID (tweet_search_extractor) */ - fun place(place: String) = place(JsonField.of(place)) + @JvmField val GIFS = of("gifs") - /** - * Sets [Builder.place] to an arbitrary JSON value. - * - * You should usually call [Builder.place] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun place(place: JsonField) = apply { this.place = place } + @JvmField val MEDIA = of("media") - /** Estimate search results within this country code (tweet_search_extractor) */ - fun placeCountry(placeCountry: String) = placeCountry(JsonField.of(placeCountry)) + @JvmField val LINKS = of("links") - /** - * Sets [Builder.placeCountry] to an arbitrary JSON value. - * - * You should usually call [Builder.placeCountry] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun placeCountry(placeCountry: JsonField) = apply { - this.placeCountry = placeCountry - } + @JvmField val NONE = of("none") - /** - * Geo point radius used for estimation, e.g. -73.99 40.73 25mi (tweet_search_extractor) - */ - fun pointRadius(pointRadius: String) = pointRadius(JsonField.of(pointRadius)) + @JvmStatic fun of(value: String) = MediaType(JsonField.of(value)) + } + + /** An enum containing [MediaType]'s known values. */ + enum class Known { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, + } + /** + * An enum containing [MediaType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [MediaType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, /** - * Sets [Builder.pointRadius] to an arbitrary JSON value. - * - * You should usually call [Builder.pointRadius] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [MediaType] was instantiated with an unknown value. */ - fun pointRadius(pointRadius: JsonField) = apply { - this.pointRadius = pointRadius + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IMAGES -> Value.IMAGES + VIDEOS -> Value.VIDEOS + GIFS -> Value.GIFS + MEDIA -> Value.MEDIA + LINKS -> Value.LINKS + NONE -> Value.NONE + else -> Value._UNKNOWN } - /** Quote mode used for estimation (tweet_search_extractor) */ - fun quotes(quotes: Quotes) = quotes(JsonField.of(quotes)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + IMAGES -> Known.IMAGES + VIDEOS -> Known.VIDEOS + GIFS -> Known.GIFS + MEDIA -> Known.MEDIA + LINKS -> Known.LINKS + NONE -> Known.NONE + else -> throw XTwitterScraperInvalidDataException("Unknown MediaType: $value") + } - /** - * Sets [Builder.quotes] to an arbitrary JSON value. - * - * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotes(quotes: JsonField) = apply { this.quotes = quotes } + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - /** Estimate only quotes of this tweet ID (tweet_search_extractor) */ - fun quotesOfTweetId(quotesOfTweetId: String) = - quotesOfTweetId(JsonField.of(quotesOfTweetId)) + private var validated: Boolean = false - /** - * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { - this.quotesOfTweetId = quotesOfTweetId + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): MediaType = apply { + if (validated) { + return@apply } - /** Reply mode used for estimation (tweet_search_extractor) */ - fun replies(replies: Replies) = replies(JsonField.of(replies)) + known() + validated = true + } - /** - * Sets [Builder.replies] to an arbitrary JSON value. - * - * You should usually call [Builder.replies] with a well-typed [Replies] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun replies(replies: JsonField) = apply { this.replies = replies } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - /** - * Maximum number of results to estimate. When set, the estimate caps projected results - * to this value. - */ - fun resultsLimit(resultsLimit: Long) = resultsLimit(JsonField.of(resultsLimit)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.resultsLimit] to an arbitrary JSON value. - * - * You should usually call [Builder.resultsLimit] with a well-typed [Long] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun resultsLimit(resultsLimit: JsonField) = apply { - this.resultsLimit = resultsLimit + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** Retweet mode used for estimation (tweet_search_extractor) */ - fun retweets(retweets: Retweets) = retweets(JsonField.of(retweets)) + return other is MediaType && value == other.value + } - /** - * Sets [Builder.retweets] to an arbitrary JSON value. - * - * You should usually call [Builder.retweets] with a well-typed [Retweets] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweets(retweets: JsonField) = apply { this.retweets = retweets } + override fun hashCode() = value.hashCode() - /** Estimate only retweets of this tweet ID (tweet_search_extractor) */ - fun retweetsOfTweetId(retweetsOfTweetId: String) = - retweetsOfTweetId(JsonField.of(retweetsOfTweetId)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { - this.retweetsOfTweetId = retweetsOfTweetId - } + /** Search ranking applied to every query. */ + class QueryType @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Query used to price tweet_search_extractor or community_search. */ - fun searchQuery(searchQuery: String) = searchQuery(JsonField.of(searchQuery)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.searchQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.searchQuery] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun searchQuery(searchQuery: JsonField) = apply { - this.searchQuery = searchQuery - } + companion object { - /** Estimate start date in YYYY-MM-DD format (tweet_search_extractor) */ - fun sinceDate(sinceDate: LocalDate) = sinceDate(JsonField.of(sinceDate)) + @JvmField val LATEST = of("Latest") - /** - * Sets [Builder.sinceDate] to an arbitrary JSON value. - * - * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun sinceDate(sinceDate: JsonField) = apply { this.sinceDate = sinceDate } + @JvmField val TOP = of("Top") - /** Community ID used to price community_post_extractor or community_search. */ - fun targetCommunityId(targetCommunityId: String) = - targetCommunityId(JsonField.of(targetCommunityId)) + @JvmField val BOTH = of("Both") - /** - * Sets [Builder.targetCommunityId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetCommunityId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetCommunityId(targetCommunityId: JsonField) = apply { - this.targetCommunityId = targetCommunityId - } + @JvmStatic fun of(value: String) = QueryType(JsonField.of(value)) + } + + /** An enum containing [QueryType]'s known values. */ + enum class Known { + LATEST, + TOP, + BOTH, + } + /** + * An enum containing [QueryType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [QueryType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LATEST, + TOP, + BOTH, /** - * List ID used to price list_follower_explorer, list_member_extractor, or - * list_post_extractor. + * An enum member indicating that [QueryType] was instantiated with an unknown value. */ - fun targetListId(targetListId: String) = targetListId(JsonField.of(targetListId)) + _UNKNOWN, + } - /** - * Sets [Builder.targetListId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetListId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetListId(targetListId: JsonField) = apply { - this.targetListId = targetListId + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LATEST -> Value.LATEST + TOP -> Value.TOP + BOTH -> Value.BOTH + else -> Value._UNKNOWN } - /** Space ID used to price space_explorer. */ - fun targetSpaceId(targetSpaceId: String) = targetSpaceId(JsonField.of(targetSpaceId)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + LATEST -> Known.LATEST + TOP -> Known.TOP + BOTH -> Known.BOTH + else -> throw XTwitterScraperInvalidDataException("Unknown QueryType: $value") + } - /** - * Sets [Builder.targetSpaceId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetSpaceId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetSpaceId(targetSpaceId: JsonField) = apply { - this.targetSpaceId = targetSpaceId + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") } - fun targetTweetId(targetTweetId: String) = targetTweetId(JsonField.of(targetTweetId)) + private var validated: Boolean = false - /** - * Sets [Builder.targetTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetTweetId(targetTweetId: JsonField) = apply { - this.targetTweetId = targetTweetId + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): QueryType = apply { + if (validated) { + return@apply } - fun targetUsername(targetUsername: String) = - targetUsername(JsonField.of(targetUsername)) + known() + validated = true + } - /** - * Sets [Builder.targetUsername] to an arbitrary JSON value. - * - * You should usually call [Builder.targetUsername] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetUsername(targetUsername: JsonField) = apply { - this.targetUsername = targetUsername + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } - /** Estimate replies sent to this username (tweet_search_extractor) */ - fun toUser(toUser: String) = toUser(JsonField.of(toUser)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.toUser] to an arbitrary JSON value. - * - * You should usually call [Builder.toUser] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun toUser(toUser: JsonField) = apply { this.toUser = toUser } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** Estimate end date in YYYY-MM-DD format (tweet_search_extractor) */ - fun untilDate(untilDate: LocalDate) = untilDate(JsonField.of(untilDate)) + return other is QueryType && value == other.value + } - /** - * Sets [Builder.untilDate] to an arbitrary JSON value. - * - * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun untilDate(untilDate: JsonField) = apply { this.untilDate = untilDate } + override fun hashCode() = value.hashCode() - /** URL substring or domain filter used for estimation (tweet_search_extractor) */ - fun url(url: String) = url(JsonField.of(url)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.url] to an arbitrary JSON value. - * - * You should usually call [Builder.url] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun url(url: JsonField) = apply { this.url = url } + /** Quote mode (tweet_search_extractor) */ + class Quotes @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Estimate only verified authors (tweet_search_extractor) */ - fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(JsonField.of(verifiedOnly)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.verifiedOnly] to an arbitrary JSON value. - * - * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun verifiedOnly(verifiedOnly: JsonField) = apply { - this.verifiedOnly = verifiedOnly - } + companion object { - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + @JvmField val INCLUDE = of("include") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JvmField val EXCLUDE = of("exclude") - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + @JvmField val ONLY = of("only") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + @JvmStatic fun of(value: String) = Quotes(JsonField.of(value)) + } - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** An enum containing [Quotes]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } - /** - * Returns an immutable instance of [Body]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Body = - Body( - checkRequired("toolType", toolType), - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - additionalProperties.toMutableMap(), - ) + /** + * An enum containing [Quotes]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Quotes] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Quotes] was instantiated with an unknown value. */ + _UNKNOWN, } + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Quotes: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + private var validated: Boolean = false /** @@ -2881,50 +8244,12 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Body = apply { + fun validate(): Quotes = apply { if (validated) { return@apply } - toolType().validate() - advancedQuery() - anyWords() - boundingBox() - cashtags() - conversationId() - exactPhrase() - excludeWords() - fromUser() - hashtags() - inReplyToTweetId() - language() - listId() - mediaType().ifPresent { it.validate() } - mentioning() - minFaves() - minQuotes() - minReplies() - minRetweets() - place() - placeCountry() - pointRadius() - quotes().ifPresent { it.validate() } - quotesOfTweetId() - replies().ifPresent { it.validate() } - resultsLimit() - retweets().ifPresent { it.validate() } - retweetsOfTweetId() - searchQuery() - sinceDate() - targetCommunityId() - targetListId() - targetSpaceId() - targetTweetId() - targetUsername() - toUser() - untilDate() - url() - verifiedOnly() + known() validated = true } @@ -2942,272 +8267,436 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic - internal fun validity(): Int = - (toolType.asKnown().getOrNull()?.validity() ?: 0) + - (if (advancedQuery.asKnown().isPresent) 1 else 0) + - (if (anyWords.asKnown().isPresent) 1 else 0) + - (if (boundingBox.asKnown().isPresent) 1 else 0) + - (if (cashtags.asKnown().isPresent) 1 else 0) + - (if (conversationId.asKnown().isPresent) 1 else 0) + - (if (exactPhrase.asKnown().isPresent) 1 else 0) + - (if (excludeWords.asKnown().isPresent) 1 else 0) + - (if (fromUser.asKnown().isPresent) 1 else 0) + - (if (hashtags.asKnown().isPresent) 1 else 0) + - (if (inReplyToTweetId.asKnown().isPresent) 1 else 0) + - (if (language.asKnown().isPresent) 1 else 0) + - (if (listId.asKnown().isPresent) 1 else 0) + - (mediaType.asKnown().getOrNull()?.validity() ?: 0) + - (if (mentioning.asKnown().isPresent) 1 else 0) + - (if (minFaves.asKnown().isPresent) 1 else 0) + - (if (minQuotes.asKnown().isPresent) 1 else 0) + - (if (minReplies.asKnown().isPresent) 1 else 0) + - (if (minRetweets.asKnown().isPresent) 1 else 0) + - (if (place.asKnown().isPresent) 1 else 0) + - (if (placeCountry.asKnown().isPresent) 1 else 0) + - (if (pointRadius.asKnown().isPresent) 1 else 0) + - (quotes.asKnown().getOrNull()?.validity() ?: 0) + - (if (quotesOfTweetId.asKnown().isPresent) 1 else 0) + - (replies.asKnown().getOrNull()?.validity() ?: 0) + - (if (resultsLimit.asKnown().isPresent) 1 else 0) + - (retweets.asKnown().getOrNull()?.validity() ?: 0) + - (if (retweetsOfTweetId.asKnown().isPresent) 1 else 0) + - (if (searchQuery.asKnown().isPresent) 1 else 0) + - (if (sinceDate.asKnown().isPresent) 1 else 0) + - (if (targetCommunityId.asKnown().isPresent) 1 else 0) + - (if (targetListId.asKnown().isPresent) 1 else 0) + - (if (targetSpaceId.asKnown().isPresent) 1 else 0) + - (if (targetTweetId.asKnown().isPresent) 1 else 0) + - (if (targetUsername.asKnown().isPresent) 1 else 0) + - (if (toUser.asKnown().isPresent) 1 else 0) + - (if (untilDate.asKnown().isPresent) 1 else 0) + - (if (url.asKnown().isPresent) 1 else 0) + - (if (verifiedOnly.asKnown().isPresent) 1 else 0) + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Body && - toolType == other.toolType && - advancedQuery == other.advancedQuery && - anyWords == other.anyWords && - boundingBox == other.boundingBox && - cashtags == other.cashtags && - conversationId == other.conversationId && - exactPhrase == other.exactPhrase && - excludeWords == other.excludeWords && - fromUser == other.fromUser && - hashtags == other.hashtags && - inReplyToTweetId == other.inReplyToTweetId && - language == other.language && - listId == other.listId && - mediaType == other.mediaType && - mentioning == other.mentioning && - minFaves == other.minFaves && - minQuotes == other.minQuotes && - minReplies == other.minReplies && - minRetweets == other.minRetweets && - place == other.place && - placeCountry == other.placeCountry && - pointRadius == other.pointRadius && - quotes == other.quotes && - quotesOfTweetId == other.quotesOfTweetId && - replies == other.replies && - resultsLimit == other.resultsLimit && - retweets == other.retweets && - retweetsOfTweetId == other.retweetsOfTweetId && - searchQuery == other.searchQuery && - sinceDate == other.sinceDate && - targetCommunityId == other.targetCommunityId && - targetListId == other.targetListId && - targetSpaceId == other.targetSpaceId && - targetTweetId == other.targetTweetId && - targetUsername == other.targetUsername && - toUser == other.toUser && - untilDate == other.untilDate && - url == other.url && - verifiedOnly == other.verifiedOnly && - additionalProperties == other.additionalProperties + return other is Quotes && value == other.value } - private val hashCode: Int by lazy { - Objects.hash( - toolType, - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - additionalProperties, - ) + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** One target and relation in a mixed profile collection. */ + class RelationTarget + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val relation: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("relation") + @ExcludeMissing + relation: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(relation, value, mutableMapOf()) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun relation(): Relation = relation.getRequired("relation") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun value(): String = value.getRequired("value") + + /** + * Returns the raw JSON value of [relation]. + * + * Unlike [relation], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("relation") @ExcludeMissing fun _relation(): JsonField = relation + + /** + * Returns the raw JSON value of [value]. + * + * Unlike [value], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RelationTarget]. + * + * The following fields are required: + * ```java + * .relation() + * .value() + * ``` + */ + @JvmStatic fun builder() = Builder() } - override fun hashCode(): Int = hashCode + /** A builder for [RelationTarget]. */ + class Builder internal constructor() { - override fun toString() = - "Body{toolType=$toolType, advancedQuery=$advancedQuery, anyWords=$anyWords, boundingBox=$boundingBox, cashtags=$cashtags, conversationId=$conversationId, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, listId=$listId, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, resultsLimit=$resultsLimit, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, searchQuery=$searchQuery, sinceDate=$sinceDate, targetCommunityId=$targetCommunityId, targetListId=$targetListId, targetSpaceId=$targetSpaceId, targetTweetId=$targetTweetId, targetUsername=$targetUsername, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalProperties=$additionalProperties}" - } + private var relation: JsonField? = null + private var value: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() - /** Identifier for the extraction tool used to run a job. */ - class ToolType @JsonCreator private constructor(private val value: JsonField) : Enum { + @JvmSynthetic + internal fun from(relationTarget: RelationTarget) = apply { + relation = relationTarget.relation + value = relationTarget.value + additionalProperties = relationTarget.additionalProperties.toMutableMap() + } + + fun relation(relation: Relation) = relation(JsonField.of(relation)) + + /** + * Sets [Builder.relation] to an arbitrary JSON value. + * + * You should usually call [Builder.relation] with a well-typed [Relation] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun relation(relation: JsonField) = apply { this.relation = relation } + + fun value(value: String) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [RelationTarget]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .relation() + * .value() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RelationTarget = + RelationTarget( + checkRequired("relation", relation), + checkRequired("value", value), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false /** - * Returns this class instance's raw value. + * Validates that the types of all values in this object match their expected types + * recursively. * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + fun validate(): RelationTarget = apply { + if (validated) { + return@apply + } - companion object { + relation().validate() + value() + validated = true + } - @JvmField val ARTICLE_EXTRACTOR = of("article_extractor") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - @JvmField val COMMUNITY_EXTRACTOR = of("community_extractor") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (relation.asKnown().getOrNull()?.validity() ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) - @JvmField val COMMUNITY_MODERATOR_EXPLORER = of("community_moderator_explorer") + class Relation @JsonCreator private constructor(private val value: JsonField) : + Enum { - @JvmField val COMMUNITY_POST_EXTRACTOR = of("community_post_extractor") + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - @JvmField val COMMUNITY_SEARCH = of("community_search") + companion object { - @JvmField val FAVORITERS = of("favoriters") + @JvmField val COMMUNITY_MEMBERS = of("community_members") - @JvmField val FOLLOWER_EXPLORER = of("follower_explorer") + @JvmField val FOLLOWERS = of("followers") - @JvmField val FOLLOWING_EXPLORER = of("following_explorer") + @JvmField val FOLLOWING = of("following") - @JvmField val LIST_FOLLOWER_EXPLORER = of("list_follower_explorer") + @JvmField val LIST_FOLLOWERS = of("list_followers") - @JvmField val LIST_MEMBER_EXTRACTOR = of("list_member_extractor") + @JvmField val LIST_MEMBERS = of("list_members") - @JvmField val LIST_POST_EXTRACTOR = of("list_post_extractor") + @JvmField val VERIFIED_FOLLOWERS = of("verified_followers") - @JvmField val MENTION_EXTRACTOR = of("mention_extractor") + @JvmStatic fun of(value: String) = Relation(JsonField.of(value)) + } - @JvmField val PEOPLE_SEARCH = of("people_search") + /** An enum containing [Relation]'s known values. */ + enum class Known { + COMMUNITY_MEMBERS, + FOLLOWERS, + FOLLOWING, + LIST_FOLLOWERS, + LIST_MEMBERS, + VERIFIED_FOLLOWERS, + } - @JvmField val POST_EXTRACTOR = of("post_extractor") + /** + * An enum containing [Relation]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Relation] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + COMMUNITY_MEMBERS, + FOLLOWERS, + FOLLOWING, + LIST_FOLLOWERS, + LIST_MEMBERS, + VERIFIED_FOLLOWERS, + /** + * An enum member indicating that [Relation] was instantiated with an unknown value. + */ + _UNKNOWN, + } - @JvmField val QUOTE_EXTRACTOR = of("quote_extractor") + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + COMMUNITY_MEMBERS -> Value.COMMUNITY_MEMBERS + FOLLOWERS -> Value.FOLLOWERS + FOLLOWING -> Value.FOLLOWING + LIST_FOLLOWERS -> Value.LIST_FOLLOWERS + LIST_MEMBERS -> Value.LIST_MEMBERS + VERIFIED_FOLLOWERS -> Value.VERIFIED_FOLLOWERS + else -> Value._UNKNOWN + } - @JvmField val REPLY_EXTRACTOR = of("reply_extractor") + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + COMMUNITY_MEMBERS -> Known.COMMUNITY_MEMBERS + FOLLOWERS -> Known.FOLLOWERS + FOLLOWING -> Known.FOLLOWING + LIST_FOLLOWERS -> Known.LIST_FOLLOWERS + LIST_MEMBERS -> Known.LIST_MEMBERS + VERIFIED_FOLLOWERS -> Known.VERIFIED_FOLLOWERS + else -> throw XTwitterScraperInvalidDataException("Unknown Relation: $value") + } - @JvmField val REPOST_EXTRACTOR = of("repost_extractor") + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - @JvmField val SPACE_EXPLORER = of("space_explorer") + private var validated: Boolean = false - @JvmField val THREAD_EXTRACTOR = of("thread_extractor") + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Relation = apply { + if (validated) { + return@apply + } - @JvmField val TWEET_SEARCH_EXTRACTOR = of("tweet_search_extractor") + known() + validated = true + } - @JvmField val USER_LIKES = of("user_likes") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - @JvmField val USER_MEDIA = of("user_media") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - @JvmField val VERIFIED_FOLLOWER_EXPLORER = of("verified_follower_explorer") + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmStatic fun of(value: String) = ToolType(JsonField.of(value)) + return other is Relation && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() } - /** An enum containing [ToolType]'s known values. */ - enum class Known { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RelationTarget && + relation == other.relation && + value == other.value && + additionalProperties == other.additionalProperties } + private val hashCode: Int by lazy { Objects.hash(relation, value, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "RelationTarget{relation=$relation, value=$value, additionalProperties=$additionalProperties}" + } + + /** Reply mode (tweet_search_extractor) */ + class Replies @JsonCreator private constructor(private val value: JsonField) : Enum { + /** - * An enum containing [ToolType]'s known values, as well as an [_UNKNOWN] member. + * Returns this class instance's raw value. * - * An instance of [ToolType] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. */ - enum class Value { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, - /** An enum member indicating that [ToolType] was instantiated with an unknown value. */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val INCLUDE = of("include") + + @JvmField val EXCLUDE = of("exclude") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Replies(JsonField.of(value)) + } + + /** An enum containing [Replies]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } + + /** + * An enum containing [Replies]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Replies] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Replies] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3220,29 +8709,9 @@ private constructor( */ fun value(): Value = when (this) { - ARTICLE_EXTRACTOR -> Value.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Value.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Value.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Value.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Value.COMMUNITY_SEARCH - FAVORITERS -> Value.FAVORITERS - FOLLOWER_EXPLORER -> Value.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Value.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Value.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Value.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Value.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Value.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Value.PEOPLE_SEARCH - POST_EXTRACTOR -> Value.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Value.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Value.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Value.REPOST_EXTRACTOR - SPACE_EXPLORER -> Value.SPACE_EXPLORER - THREAD_EXTRACTOR -> Value.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Value.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Value.USER_LIKES - USER_MEDIA -> Value.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Value.VERIFIED_FOLLOWER_EXPLORER + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY else -> Value._UNKNOWN } @@ -3257,30 +8726,10 @@ private constructor( */ fun known(): Known = when (this) { - ARTICLE_EXTRACTOR -> Known.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Known.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Known.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Known.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Known.COMMUNITY_SEARCH - FAVORITERS -> Known.FAVORITERS - FOLLOWER_EXPLORER -> Known.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Known.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Known.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Known.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Known.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Known.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Known.PEOPLE_SEARCH - POST_EXTRACTOR -> Known.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Known.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Known.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Known.REPOST_EXTRACTOR - SPACE_EXPLORER -> Known.SPACE_EXPLORER - THREAD_EXTRACTOR -> Known.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Known.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Known.USER_LIKES - USER_MEDIA -> Known.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Known.VERIFIED_FOLLOWER_EXPLORER - else -> throw XTwitterScraperInvalidDataException("Unknown ToolType: $value") + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Replies: $value") } /** @@ -3308,7 +8757,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): ToolType = apply { + fun validate(): Replies = apply { if (validated) { return@apply } @@ -3338,7 +8787,7 @@ private constructor( return true } - return other is ToolType && value == other.value + return other is Replies && value == other.value } override fun hashCode() = value.hashCode() @@ -3346,8 +8795,8 @@ private constructor( override fun toString() = value.toString() } - /** Media type used for estimate filtering (tweet_search_extractor) */ - class MediaType @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Retweet mode (tweet_search_extractor) */ + class Retweets @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3361,50 +8810,36 @@ private constructor( companion object { - @JvmField val IMAGES = of("images") - - @JvmField val VIDEOS = of("videos") - - @JvmField val GIFS = of("gifs") - - @JvmField val MEDIA = of("media") + @JvmField val INCLUDE = of("include") - @JvmField val LINKS = of("links") + @JvmField val EXCLUDE = of("exclude") - @JvmField val NONE = of("none") + @JvmField val ONLY = of("only") - @JvmStatic fun of(value: String) = MediaType(JsonField.of(value)) + @JvmStatic fun of(value: String) = Retweets(JsonField.of(value)) } - /** An enum containing [MediaType]'s known values. */ + /** An enum containing [Retweets]'s known values. */ enum class Known { - IMAGES, - VIDEOS, - GIFS, - MEDIA, - LINKS, - NONE, + INCLUDE, + EXCLUDE, + ONLY, } /** - * An enum containing [MediaType]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Retweets]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [MediaType] can contain an unknown value in a couple of cases: + * An instance of [Retweets] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - IMAGES, - VIDEOS, - GIFS, - MEDIA, - LINKS, - NONE, - /** - * An enum member indicating that [MediaType] was instantiated with an unknown value. - */ + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Retweets] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3417,12 +8852,9 @@ private constructor( */ fun value(): Value = when (this) { - IMAGES -> Value.IMAGES - VIDEOS -> Value.VIDEOS - GIFS -> Value.GIFS - MEDIA -> Value.MEDIA - LINKS -> Value.LINKS - NONE -> Value.NONE + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY else -> Value._UNKNOWN } @@ -3437,13 +8869,10 @@ private constructor( */ fun known(): Known = when (this) { - IMAGES -> Known.IMAGES - VIDEOS -> Known.VIDEOS - GIFS -> Known.GIFS - MEDIA -> Known.MEDIA - LINKS -> Known.LINKS - NONE -> Known.NONE - else -> throw XTwitterScraperInvalidDataException("Unknown MediaType: $value") + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Retweets: $value") } /** @@ -3471,7 +8900,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): MediaType = apply { + fun validate(): Retweets = apply { if (validated) { return@apply } @@ -3501,7 +8930,7 @@ private constructor( return true } - return other is MediaType && value == other.value + return other is Retweets && value == other.value } override fun hashCode() = value.hashCode() @@ -3509,8 +8938,8 @@ private constructor( override fun toString() = value.toString() } - /** Quote mode used for estimation (tweet_search_extractor) */ - class Quotes @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Reply depth scope. */ + class Scope @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3524,36 +8953,36 @@ private constructor( companion object { - @JvmField val INCLUDE = of("include") + @JvmField val ALL = of("all") - @JvmField val EXCLUDE = of("exclude") + @JvmField val DIRECT = of("direct") - @JvmField val ONLY = of("only") + @JvmField val NESTED = of("nested") - @JvmStatic fun of(value: String) = Quotes(JsonField.of(value)) + @JvmStatic fun of(value: String) = Scope(JsonField.of(value)) } - /** An enum containing [Quotes]'s known values. */ + /** An enum containing [Scope]'s known values. */ enum class Known { - INCLUDE, - EXCLUDE, - ONLY, + ALL, + DIRECT, + NESTED, } /** - * An enum containing [Quotes]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Scope]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [Quotes] can contain an unknown value in a couple of cases: + * An instance of [Scope] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Quotes] was instantiated with an unknown value. */ + ALL, + DIRECT, + NESTED, + /** An enum member indicating that [Scope] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3566,9 +8995,9 @@ private constructor( */ fun value(): Value = when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY + ALL -> Value.ALL + DIRECT -> Value.DIRECT + NESTED -> Value.NESTED else -> Value._UNKNOWN } @@ -3583,10 +9012,10 @@ private constructor( */ fun known(): Known = when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Quotes: $value") + ALL -> Known.ALL + DIRECT -> Known.DIRECT + NESTED -> Known.NESTED + else -> throw XTwitterScraperInvalidDataException("Unknown Scope: $value") } /** @@ -3614,7 +9043,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Quotes = apply { + fun validate(): Scope = apply { if (validated) { return@apply } @@ -3644,16 +9073,225 @@ private constructor( return true } - return other is Quotes && value == other.value + return other is Scope && value == other.value } override fun hashCode() = value.hashCode() - override fun toString() = value.toString() + override fun toString() = value.toString() + } + + /** Reply start time as ISO 8601 or Unix seconds. */ + @JsonDeserialize(using = SinceTime.Deserializer::class) + @JsonSerialize(using = SinceTime.Serializer::class) + class SinceTime + private constructor( + private val offsetDate: OffsetDateTime? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun offsetDate(): Optional = Optional.ofNullable(offsetDate) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isOffsetDate(): Boolean = offsetDate != null + + fun isInteger(): Boolean = integer != null + + fun asOffsetDate(): OffsetDateTime = offsetDate.getOrThrow("offsetDate") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = sinceTime.accept(new SinceTime.Visitor>() { + * @Override + * public Optional visitOffsetDate(OffsetDateTime offsetDate) { + * return Optional.of(offsetDate.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + offsetDate != null -> visitor.visitOffsetDate(offsetDate) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): SinceTime = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) {} + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) = 1 + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SinceTime && offsetDate == other.offsetDate && integer == other.integer + } + + override fun hashCode(): Int = Objects.hash(offsetDate, integer) + + override fun toString(): String = + when { + offsetDate != null -> "SinceTime{offsetDate=$offsetDate}" + integer != null -> "SinceTime{integer=$integer}" + _json != null -> "SinceTime{_unknown=$_json}" + else -> throw IllegalStateException("Invalid SinceTime") + } + + companion object { + + @JvmStatic + fun ofOffsetDate(offsetDate: OffsetDateTime) = SinceTime(offsetDate = offsetDate) + + @JvmStatic fun ofInteger(integer: Long) = SinceTime(integer = integer) + } + + /** + * An interface that defines how to map each variant of [SinceTime] to a value of type [T]. + */ + interface Visitor { + + fun visitOffsetDate(offsetDate: OffsetDateTime): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [SinceTime] to a value of type [T]. + * + * An instance of [SinceTime] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown SinceTime: $json") + } + } + + internal class Deserializer : BaseDeserializer(SinceTime::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): SinceTime { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + SinceTime(offsetDate = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + SinceTime(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> SinceTime(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(SinceTime::class) { + + override fun serialize( + value: SinceTime, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.offsetDate != null -> generator.writeObject(value.offsetDate) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid SinceTime") + } + } + } } - /** Reply mode used for estimation (tweet_search_extractor) */ - class Replies @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Reply result order. */ + class Sort @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3667,36 +9305,40 @@ private constructor( companion object { - @JvmField val INCLUDE = of("include") + @JvmField val RELEVANCE = of("relevance") - @JvmField val EXCLUDE = of("exclude") + @JvmField val LATEST = of("latest") - @JvmField val ONLY = of("only") + @JvmField val OLDEST = of("oldest") - @JvmStatic fun of(value: String) = Replies(JsonField.of(value)) + @JvmField val LIKES = of("likes") + + @JvmStatic fun of(value: String) = Sort(JsonField.of(value)) } - /** An enum containing [Replies]'s known values. */ + /** An enum containing [Sort]'s known values. */ enum class Known { - INCLUDE, - EXCLUDE, - ONLY, + RELEVANCE, + LATEST, + OLDEST, + LIKES, } /** - * An enum containing [Replies]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Sort]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [Replies] can contain an unknown value in a couple of cases: + * An instance of [Sort] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Replies] was instantiated with an unknown value. */ + RELEVANCE, + LATEST, + OLDEST, + LIKES, + /** An enum member indicating that [Sort] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3709,9 +9351,10 @@ private constructor( */ fun value(): Value = when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY + RELEVANCE -> Value.RELEVANCE + LATEST -> Value.LATEST + OLDEST -> Value.OLDEST + LIKES -> Value.LIKES else -> Value._UNKNOWN } @@ -3726,10 +9369,11 @@ private constructor( */ fun known(): Known = when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Replies: $value") + RELEVANCE -> Known.RELEVANCE + LATEST -> Known.LATEST + OLDEST -> Known.OLDEST + LIKES -> Known.LIKES + else -> throw XTwitterScraperInvalidDataException("Unknown Sort: $value") } /** @@ -3757,7 +9401,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Replies = apply { + fun validate(): Sort = apply { if (validated) { return@apply } @@ -3787,7 +9431,7 @@ private constructor( return true } - return other is Replies && value == other.value + return other is Sort && value == other.value } override fun hashCode() = value.hashCode() @@ -3795,98 +9439,685 @@ private constructor( override fun toString() = value.toString() } - /** Retweet mode used for estimation (tweet_search_extractor) */ - class Retweets @JsonCreator private constructor(private val value: JsonField) : Enum { + /** One auto-routed target in a mixed Tweet collection. */ + @JsonDeserialize(using = Target.Deserializer::class) + @JsonSerialize(using = Target.Serializer::class) + class Target + private constructor( + private val string: String? = null, + private val unionMember1: UnionMember1? = null, + private val _json: JsonValue? = null, + ) { - /** - * Returns this class instance's raw value. - * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. - */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun string(): Optional = Optional.ofNullable(string) - companion object { + fun unionMember1(): Optional = Optional.ofNullable(unionMember1) - @JvmField val INCLUDE = of("include") + fun isString(): Boolean = string != null - @JvmField val EXCLUDE = of("exclude") + fun isUnionMember1(): Boolean = unionMember1 != null - @JvmField val ONLY = of("only") + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun asString(): String = string.getOrThrow("string") - @JvmStatic fun of(value: String) = Retweets(JsonField.of(value)) - } + fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") - /** An enum containing [Retweets]'s known values. */ - enum class Known { - INCLUDE, - EXCLUDE, - ONLY, - } + fun _json(): Optional = Optional.ofNullable(_json) /** - * An enum containing [Retweets]'s known values, as well as an [_UNKNOWN] member. + * Maps this instance's current variant to a value of type [T] using the given [visitor]. * - * An instance of [Retweets] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = target.accept(new Target.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. */ - enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Retweets] was instantiated with an unknown value. */ - _UNKNOWN, - } + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false /** - * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] - * if the class was instantiated with an unknown value. + * Validates that the types of all values in this object match their expected types + * recursively. * - * Use the [known] method instead if you're certain the value is always known or if you want - * to throw for the unknown case. + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. */ - fun value(): Value = - when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY - else -> Value._UNKNOWN + fun validate(): Target = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnionMember1(unionMember1: UnionMember1) { + unionMember1.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } /** - * Returns an enum member corresponding to this class instance's value. - * - * Use the [value] method instead if you're uncertain the value is always known and don't - * want to throw for the unknown case. + * Returns a score indicating how many valid values are contained in this object + * recursively. * - * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a - * known member. + * Used for best match union deserialization. */ - fun known(): Known = - when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Retweets: $value") + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnionMember1(unionMember1: UnionMember1) = + unionMember1.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Target && string == other.string && unionMember1 == other.unionMember1 + } + + override fun hashCode(): Int = Objects.hash(string, unionMember1) + + override fun toString(): String = + when { + string != null -> "Target{string=$string}" + unionMember1 != null -> "Target{unionMember1=$unionMember1}" + _json != null -> "Target{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Target") + } + + companion object { + + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + @JvmStatic fun ofString(string: String) = Target(string = string) + + @JvmStatic + fun ofUnionMember1(unionMember1: UnionMember1) = Target(unionMember1 = unionMember1) + } + + /** An interface that defines how to map each variant of [Target] to a value of type [T]. */ + interface Visitor { + + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun visitString(string: String): T + + fun visitUnionMember1(unionMember1: UnionMember1): T + + /** + * Maps an unknown variant of [Target] to a value of type [T]. + * + * An instance of [Target] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown Target: $json") + } + } + + internal class Deserializer : BaseDeserializer(Target::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Target { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Target(unionMember1 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Target(string = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Target(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Target::class) { + + override fun serialize( + value: Target, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Target") + } + } + } + + class UnionMember1 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val kind: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("kind") @ExcludeMissing kind: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(kind, value, mutableMapOf()) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun kind(): Kind = kind.getRequired("kind") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun value(): String = value.getRequired("value") + + /** + * Returns the raw JSON value of [kind]. + * + * Unlike [kind], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kind") @ExcludeMissing fun _kind(): JsonField = kind + + /** + * Returns the raw JSON value of [value]. + * + * Unlike [value], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnionMember1]. + * + * The following fields are required: + * ```java + * .kind() + * .value() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember1]. */ + class Builder internal constructor() { + + private var kind: JsonField? = null + private var value: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember1: UnionMember1) = apply { + kind = unionMember1.kind + value = unionMember1.value + additionalProperties = unionMember1.additionalProperties.toMutableMap() + } + + fun kind(kind: Kind) = kind(JsonField.of(kind)) + + /** + * Sets [Builder.kind] to an arbitrary JSON value. + * + * You should usually call [Builder.kind] with a well-typed [Kind] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun kind(kind: JsonField) = apply { this.kind = kind } + + fun value(value: String) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UnionMember1]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .kind() + * .value() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember1 = + UnionMember1( + checkRequired("kind", kind), + checkRequired("value", value), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): UnionMember1 = apply { + if (validated) { + return@apply + } + + kind().validate() + value() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (kind.asKnown().getOrNull()?.validity() ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) + + class Kind @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FAVORITERS = of("favoriters") + + @JvmField val LIST = of("list") + + @JvmField val PROFILE = of("profile") + + @JvmField val PROFILE_LIKES = of("profile_likes") + + @JvmField val PROFILE_MEDIA = of("profile_media") + + @JvmField val PROFILE_REPLIES = of("profile_replies") + + @JvmField val QUOTES = of("quotes") + + @JvmField val REPLIES = of("replies") + + @JvmField val RETWEETERS = of("retweeters") + + @JvmField val SEARCH = of("search") + + @JvmField val THREAD = of("thread") + + @JvmField val TWEET = of("tweet") + + @JvmStatic fun of(value: String) = Kind(JsonField.of(value)) + } + + /** An enum containing [Kind]'s known values. */ + enum class Known { + FAVORITERS, + LIST, + PROFILE, + PROFILE_LIKES, + PROFILE_MEDIA, + PROFILE_REPLIES, + QUOTES, + REPLIES, + RETWEETERS, + SEARCH, + THREAD, + TWEET, + } + + /** + * An enum containing [Kind]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Kind] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FAVORITERS, + LIST, + PROFILE, + PROFILE_LIKES, + PROFILE_MEDIA, + PROFILE_REPLIES, + QUOTES, + REPLIES, + RETWEETERS, + SEARCH, + THREAD, + TWEET, + /** + * An enum member indicating that [Kind] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FAVORITERS -> Value.FAVORITERS + LIST -> Value.LIST + PROFILE -> Value.PROFILE + PROFILE_LIKES -> Value.PROFILE_LIKES + PROFILE_MEDIA -> Value.PROFILE_MEDIA + PROFILE_REPLIES -> Value.PROFILE_REPLIES + QUOTES -> Value.QUOTES + REPLIES -> Value.REPLIES + RETWEETERS -> Value.RETWEETERS + SEARCH -> Value.SEARCH + THREAD -> Value.THREAD + TWEET -> Value.TWEET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a + * not a known member. + */ + fun known(): Known = + when (this) { + FAVORITERS -> Known.FAVORITERS + LIST -> Known.LIST + PROFILE -> Known.PROFILE + PROFILE_LIKES -> Known.PROFILE_LIKES + PROFILE_MEDIA -> Known.PROFILE_MEDIA + PROFILE_REPLIES -> Known.PROFILE_REPLIES + QUOTES -> Known.QUOTES + REPLIES -> Known.REPLIES + RETWEETERS -> Known.RETWEETERS + SEARCH -> Known.SEARCH + THREAD -> Known.THREAD + TWEET -> Known.TWEET + else -> throw XTwitterScraperInvalidDataException("Unknown Kind: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does + * not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Kind = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Kind && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnionMember1 && + kind == other.kind && + value == other.value && + additionalProperties == other.additionalProperties } + private val hashCode: Int by lazy { Objects.hash(kind, value, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember1{kind=$kind, value=$value, additionalProperties=$additionalProperties}" + } + } + + /** Reply end time as ISO 8601 or Unix seconds. */ + @JsonDeserialize(using = UntilTime.Deserializer::class) + @JsonSerialize(using = UntilTime.Serializer::class) + class UntilTime + private constructor( + private val offsetDate: OffsetDateTime? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun offsetDate(): Optional = Optional.ofNullable(offsetDate) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isOffsetDate(): Boolean = offsetDate != null + + fun isInteger(): Boolean = integer != null + + fun asOffsetDate(): OffsetDateTime = offsetDate.getOrThrow("offsetDate") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + /** - * Returns this class instance's primitive wire representation. + * Maps this instance's current variant to a value of type [T] using the given [visitor]. * - * This differs from the [toString] method because that method is primarily for debugging - * and generally doesn't throw. + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = untilTime.accept(new UntilTime.Visitor>() { + * @Override + * public Optional visitOffsetDate(OffsetDateTime offsetDate) { + * return Optional.of(offsetDate.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` * - * @throws XTwitterScraperInvalidDataException if this class instance's value does not have - * the expected primitive type. + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. */ - fun asString(): String = - _value().asString().orElseThrow { - XTwitterScraperInvalidDataException("Value is not a String") + fun accept(visitor: Visitor): T = + when { + offsetDate != null -> visitor.visitOffsetDate(offsetDate) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) } private var validated: Boolean = false @@ -3900,12 +10131,18 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Retweets = apply { + fun validate(): UntilTime = apply { if (validated) { return@apply } - known() + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) {} + + override fun visitInteger(integer: Long) {} + } + ) validated = true } @@ -3923,19 +10160,113 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) = 1 + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Retweets && value == other.value + return other is UntilTime && offsetDate == other.offsetDate && integer == other.integer } - override fun hashCode() = value.hashCode() + override fun hashCode(): Int = Objects.hash(offsetDate, integer) - override fun toString() = value.toString() + override fun toString(): String = + when { + offsetDate != null -> "UntilTime{offsetDate=$offsetDate}" + integer != null -> "UntilTime{integer=$integer}" + _json != null -> "UntilTime{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UntilTime") + } + + companion object { + + @JvmStatic + fun ofOffsetDate(offsetDate: OffsetDateTime) = UntilTime(offsetDate = offsetDate) + + @JvmStatic fun ofInteger(integer: Long) = UntilTime(integer = integer) + } + + /** + * An interface that defines how to map each variant of [UntilTime] to a value of type [T]. + */ + interface Visitor { + + fun visitOffsetDate(offsetDate: OffsetDateTime): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [UntilTime] to a value of type [T]. + * + * An instance of [UntilTime] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown UntilTime: $json") + } + } + + internal class Deserializer : BaseDeserializer(UntilTime::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): UntilTime { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UntilTime(offsetDate = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UntilTime(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> UntilTime(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(UntilTime::class) { + + override fun serialize( + value: UntilTime, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.offsetDate != null -> generator.writeObject(value.offsetDate) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UntilTime") + } + } + } } override fun equals(other: Any?): Boolean { diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostResponse.kt index cc909947..40e1b2ab 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostResponse.kt @@ -388,6 +388,8 @@ private constructor( @JvmField val FOLLOWING = of("following") + @JvmField val COLLECTION = of("collection") + @JvmField val PAGINATION_CAP = of("paginationCap") @JvmField val POSTS = of("posts") @@ -409,6 +411,7 @@ private constructor( enum class Known { FOLLOWERS, FOLLOWING, + COLLECTION, PAGINATION_CAP, POSTS, QUOTE_COUNT, @@ -430,6 +433,7 @@ private constructor( enum class Value { FOLLOWERS, FOLLOWING, + COLLECTION, PAGINATION_CAP, POSTS, QUOTE_COUNT, @@ -452,6 +456,7 @@ private constructor( when (this) { FOLLOWERS -> Value.FOLLOWERS FOLLOWING -> Value.FOLLOWING + COLLECTION -> Value.COLLECTION PAGINATION_CAP -> Value.PAGINATION_CAP POSTS -> Value.POSTS QUOTE_COUNT -> Value.QUOTE_COUNT @@ -475,6 +480,7 @@ private constructor( when (this) { FOLLOWERS -> Known.FOLLOWERS FOLLOWING -> Known.FOLLOWING + COLLECTION -> Known.COLLECTION PAGINATION_CAP -> Known.PAGINATION_CAP POSTS -> Known.POSTS QUOTE_COUNT -> Known.QUOTE_COUNT diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParams.kt index 6bd9b460..6a90f54c 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParams.kt @@ -14,6 +14,7 @@ import com.x_twitter_scraper.api.core.checkRequired import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import java.time.LocalDate import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -23,6 +24,24 @@ class ExtractionExportResultsParams private constructor( private val id: String?, private val format: Format, + private val hasDescription: Boolean?, + private val hasLocation: Boolean?, + private val hasMedia: Boolean?, + private val lang: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxPosts: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minLikes: Long?, + private val minPosts: Long?, + private val minReplies: Long?, + private val minRetweets: Long?, + private val minViews: Long?, + private val search: String?, + private val sinceDate: LocalDate?, + private val untilDate: LocalDate?, + private val verified: Boolean?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -32,6 +51,60 @@ private constructor( /** Export file format */ fun format(): Format = format + /** Require a non-empty description. */ + fun hasDescription(): Optional = Optional.ofNullable(hasDescription) + + /** Require a non-empty location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Require media. */ + fun hasMedia(): Optional = Optional.ofNullable(hasMedia) + + /** Filter by language code. */ + fun lang(): Optional = Optional.ofNullable(lang) + + /** Maximum follower count. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. */ + fun maxPosts(): Optional = Optional.ofNullable(maxPosts) + + /** Minimum follower count. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum like count. */ + fun minLikes(): Optional = Optional.ofNullable(minLikes) + + /** Minimum post count. */ + fun minPosts(): Optional = Optional.ofNullable(minPosts) + + /** Minimum reply count. */ + fun minReplies(): Optional = Optional.ofNullable(minReplies) + + /** Minimum repost count. */ + fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + + /** Minimum view count. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Search exported result text. */ + fun search(): Optional = Optional.ofNullable(search) + + /** Include results on or after this date. */ + fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + + /** Include results on or before this date. */ + fun untilDate(): Optional = Optional.ofNullable(untilDate) + + /** Filter by verified status. */ + fun verified(): Optional = Optional.ofNullable(verified) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -59,6 +132,24 @@ private constructor( private var id: String? = null private var format: Format? = null + private var hasDescription: Boolean? = null + private var hasLocation: Boolean? = null + private var hasMedia: Boolean? = null + private var lang: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxPosts: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minLikes: Long? = null + private var minPosts: Long? = null + private var minReplies: Long? = null + private var minRetweets: Long? = null + private var minViews: Long? = null + private var search: String? = null + private var sinceDate: LocalDate? = null + private var untilDate: LocalDate? = null + private var verified: Boolean? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -66,6 +157,24 @@ private constructor( internal fun from(extractionExportResultsParams: ExtractionExportResultsParams) = apply { id = extractionExportResultsParams.id format = extractionExportResultsParams.format + hasDescription = extractionExportResultsParams.hasDescription + hasLocation = extractionExportResultsParams.hasLocation + hasMedia = extractionExportResultsParams.hasMedia + lang = extractionExportResultsParams.lang + maxFollowers = extractionExportResultsParams.maxFollowers + maxFollowing = extractionExportResultsParams.maxFollowing + maxPosts = extractionExportResultsParams.maxPosts + minFollowers = extractionExportResultsParams.minFollowers + minFollowing = extractionExportResultsParams.minFollowing + minLikes = extractionExportResultsParams.minLikes + minPosts = extractionExportResultsParams.minPosts + minReplies = extractionExportResultsParams.minReplies + minRetweets = extractionExportResultsParams.minRetweets + minViews = extractionExportResultsParams.minViews + search = extractionExportResultsParams.search + sinceDate = extractionExportResultsParams.sinceDate + untilDate = extractionExportResultsParams.untilDate + verified = extractionExportResultsParams.verified additionalHeaders = extractionExportResultsParams.additionalHeaders.toBuilder() additionalQueryParams = extractionExportResultsParams.additionalQueryParams.toBuilder() } @@ -78,6 +187,215 @@ private constructor( /** Export file format */ fun format(format: Format) = apply { this.format = format } + /** Require a non-empty description. */ + fun hasDescription(hasDescription: Boolean?) = apply { + this.hasDescription = hasDescription + } + + /** + * Alias for [Builder.hasDescription]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasDescription(hasDescription: Boolean) = hasDescription(hasDescription as Boolean?) + + /** Alias for calling [Builder.hasDescription] with `hasDescription.orElse(null)`. */ + fun hasDescription(hasDescription: Optional) = + hasDescription(hasDescription.getOrNull()) + + /** Require a non-empty location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Require media. */ + fun hasMedia(hasMedia: Boolean?) = apply { this.hasMedia = hasMedia } + + /** + * Alias for [Builder.hasMedia]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasMedia(hasMedia: Boolean) = hasMedia(hasMedia as Boolean?) + + /** Alias for calling [Builder.hasMedia] with `hasMedia.orElse(null)`. */ + fun hasMedia(hasMedia: Optional) = hasMedia(hasMedia.getOrNull()) + + /** Filter by language code. */ + fun lang(lang: String?) = apply { this.lang = lang } + + /** Alias for calling [Builder.lang] with `lang.orElse(null)`. */ + fun lang(lang: Optional) = lang(lang.getOrNull()) + + /** Maximum follower count. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. */ + fun maxPosts(maxPosts: Long?) = apply { this.maxPosts = maxPosts } + + /** + * Alias for [Builder.maxPosts]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxPosts(maxPosts: Long) = maxPosts(maxPosts as Long?) + + /** Alias for calling [Builder.maxPosts] with `maxPosts.orElse(null)`. */ + fun maxPosts(maxPosts: Optional) = maxPosts(maxPosts.getOrNull()) + + /** Minimum follower count. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum like count. */ + fun minLikes(minLikes: Long?) = apply { this.minLikes = minLikes } + + /** + * Alias for [Builder.minLikes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minLikes(minLikes: Long) = minLikes(minLikes as Long?) + + /** Alias for calling [Builder.minLikes] with `minLikes.orElse(null)`. */ + fun minLikes(minLikes: Optional) = minLikes(minLikes.getOrNull()) + + /** Minimum post count. */ + fun minPosts(minPosts: Long?) = apply { this.minPosts = minPosts } + + /** + * Alias for [Builder.minPosts]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minPosts(minPosts: Long) = minPosts(minPosts as Long?) + + /** Alias for calling [Builder.minPosts] with `minPosts.orElse(null)`. */ + fun minPosts(minPosts: Optional) = minPosts(minPosts.getOrNull()) + + /** Minimum reply count. */ + fun minReplies(minReplies: Long?) = apply { this.minReplies = minReplies } + + /** + * Alias for [Builder.minReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minReplies(minReplies: Long) = minReplies(minReplies as Long?) + + /** Alias for calling [Builder.minReplies] with `minReplies.orElse(null)`. */ + fun minReplies(minReplies: Optional) = minReplies(minReplies.getOrNull()) + + /** Minimum repost count. */ + fun minRetweets(minRetweets: Long?) = apply { this.minRetweets = minRetweets } + + /** + * Alias for [Builder.minRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minRetweets(minRetweets: Long) = minRetweets(minRetweets as Long?) + + /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ + fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + + /** Minimum view count. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Search exported result text. */ + fun search(search: String?) = apply { this.search = search } + + /** Alias for calling [Builder.search] with `search.orElse(null)`. */ + fun search(search: Optional) = search(search.getOrNull()) + + /** Include results on or after this date. */ + fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } + + /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ + fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + + /** Include results on or before this date. */ + fun untilDate(untilDate: LocalDate?) = apply { this.untilDate = untilDate } + + /** Alias for calling [Builder.untilDate] with `untilDate.orElse(null)`. */ + fun untilDate(untilDate: Optional) = untilDate(untilDate.getOrNull()) + + /** Filter by verified status. */ + fun verified(verified: Boolean?) = apply { this.verified = verified } + + /** + * Alias for [Builder.verified]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verified(verified: Boolean) = verified(verified as Boolean?) + + /** Alias for calling [Builder.verified] with `verified.orElse(null)`. */ + fun verified(verified: Optional) = verified(verified.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -192,6 +510,24 @@ private constructor( ExtractionExportResultsParams( id, checkRequired("format", format), + hasDescription, + hasLocation, + hasMedia, + lang, + maxFollowers, + maxFollowing, + maxPosts, + minFollowers, + minFollowing, + minLikes, + minPosts, + minReplies, + minRetweets, + minViews, + search, + sinceDate, + untilDate, + verified, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -209,6 +545,24 @@ private constructor( QueryParams.builder() .apply { put("format", format.toString()) + hasDescription?.let { put("hasDescription", it.toString()) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasMedia?.let { put("hasMedia", it.toString()) } + lang?.let { put("lang", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxPosts?.let { put("maxPosts", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minLikes?.let { put("minLikes", it.toString()) } + minPosts?.let { put("minPosts", it.toString()) } + minReplies?.let { put("minReplies", it.toString()) } + minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + search?.let { put("search", it) } + sinceDate?.let { put("sinceDate", it.toString()) } + untilDate?.let { put("untilDate", it.toString()) } + verified?.let { put("verified", it.toString()) } putAll(additionalQueryParams) } .build() @@ -388,13 +742,54 @@ private constructor( return other is ExtractionExportResultsParams && id == other.id && format == other.format && + hasDescription == other.hasDescription && + hasLocation == other.hasLocation && + hasMedia == other.hasMedia && + lang == other.lang && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxPosts == other.maxPosts && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minLikes == other.minLikes && + minPosts == other.minPosts && + minReplies == other.minReplies && + minRetweets == other.minRetweets && + minViews == other.minViews && + search == other.search && + sinceDate == other.sinceDate && + untilDate == other.untilDate && + verified == other.verified && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, format, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + format, + hasDescription, + hasLocation, + hasMedia, + lang, + maxFollowers, + maxFollowing, + maxPosts, + minFollowers, + minFollowing, + minLikes, + minPosts, + minReplies, + minRetweets, + minViews, + search, + sinceDate, + untilDate, + verified, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "ExtractionExportResultsParams{id=$id, format=$format, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "ExtractionExportResultsParams{id=$id, format=$format, hasDescription=$hasDescription, hasLocation=$hasLocation, hasMedia=$hasMedia, lang=$lang, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxPosts=$maxPosts, minFollowers=$minFollowers, minFollowing=$minFollowing, minLikes=$minLikes, minPosts=$minPosts, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, search=$search, sinceDate=$sinceDate, untilDate=$untilDate, verified=$verified, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionListParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionListParams.kt index 04d89ee5..c01c42fa 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionListParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionListParams.kt @@ -28,7 +28,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(): Optional = Optional.ofNullable(cursor) /** @@ -80,7 +80,7 @@ private constructor( additionalQueryParams = extractionListParams.additionalQueryParams.toBuilder() } - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParams.kt index 2e99c681..933a9b21 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParams.kt @@ -6,9 +6,13 @@ package com.x_twitter_scraper.api.models.extractions +import com.fasterxml.jackson.annotation.JsonCreator +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.Params import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -18,19 +22,35 @@ class ExtractionRetrieveParams private constructor( private val id: String?, private val cursor: String?, + private val fieldStyle: FieldStyle?, + private val includeRaw: Boolean?, private val limit: Long?, + private val outputMode: OutputMode?, + private val outputPreset: OutputPreset?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Preserve source keys or convert result field names. */ + fun fieldStyle(): Optional = Optional.ofNullable(fieldStyle) + + /** Use outputMode=raw instead. */ + fun includeRaw(): Optional = Optional.ofNullable(includeRaw) + /** Maximum number of results to return (1-1000, default 100) */ fun limit(): Optional = Optional.ofNullable(limit) + /** Select compact, full, or raw-compatible result fields. */ + fun outputMode(): Optional = Optional.ofNullable(outputMode) + + /** Keep enrichment nested or merge it into each result. */ + fun outputPreset(): Optional = Optional.ofNullable(outputPreset) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -52,7 +72,11 @@ private constructor( private var id: String? = null private var cursor: String? = null + private var fieldStyle: FieldStyle? = null + private var includeRaw: Boolean? = null private var limit: Long? = null + private var outputMode: OutputMode? = null + private var outputPreset: OutputPreset? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -60,7 +84,11 @@ private constructor( internal fun from(extractionRetrieveParams: ExtractionRetrieveParams) = apply { id = extractionRetrieveParams.id cursor = extractionRetrieveParams.cursor + fieldStyle = extractionRetrieveParams.fieldStyle + includeRaw = extractionRetrieveParams.includeRaw limit = extractionRetrieveParams.limit + outputMode = extractionRetrieveParams.outputMode + outputPreset = extractionRetrieveParams.outputPreset additionalHeaders = extractionRetrieveParams.additionalHeaders.toBuilder() additionalQueryParams = extractionRetrieveParams.additionalQueryParams.toBuilder() } @@ -70,12 +98,31 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) - /** Cursor for keyset pagination from prior response next_cursor */ + /** Previous nextCursor. */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Preserve source keys or convert result field names. */ + fun fieldStyle(fieldStyle: FieldStyle?) = apply { this.fieldStyle = fieldStyle } + + /** Alias for calling [Builder.fieldStyle] with `fieldStyle.orElse(null)`. */ + fun fieldStyle(fieldStyle: Optional) = fieldStyle(fieldStyle.getOrNull()) + + /** Use outputMode=raw instead. */ + fun includeRaw(includeRaw: Boolean?) = apply { this.includeRaw = includeRaw } + + /** + * Alias for [Builder.includeRaw]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun includeRaw(includeRaw: Boolean) = includeRaw(includeRaw as Boolean?) + + /** Alias for calling [Builder.includeRaw] with `includeRaw.orElse(null)`. */ + fun includeRaw(includeRaw: Optional) = includeRaw(includeRaw.getOrNull()) + /** Maximum number of results to return (1-1000, default 100) */ fun limit(limit: Long?) = apply { this.limit = limit } @@ -89,6 +136,19 @@ private constructor( /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ fun limit(limit: Optional) = limit(limit.getOrNull()) + /** Select compact, full, or raw-compatible result fields. */ + fun outputMode(outputMode: OutputMode?) = apply { this.outputMode = outputMode } + + /** Alias for calling [Builder.outputMode] with `outputMode.orElse(null)`. */ + fun outputMode(outputMode: Optional) = outputMode(outputMode.getOrNull()) + + /** Keep enrichment nested or merge it into each result. */ + fun outputPreset(outputPreset: OutputPreset?) = apply { this.outputPreset = outputPreset } + + /** Alias for calling [Builder.outputPreset] with `outputPreset.orElse(null)`. */ + fun outputPreset(outputPreset: Optional) = + outputPreset(outputPreset.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -196,7 +256,11 @@ private constructor( ExtractionRetrieveParams( id, cursor, + fieldStyle, + includeRaw, limit, + outputMode, + outputPreset, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -214,11 +278,445 @@ private constructor( QueryParams.builder() .apply { cursor?.let { put("cursor", it) } + fieldStyle?.let { put("fieldStyle", it.toString()) } + includeRaw?.let { put("includeRaw", it.toString()) } limit?.let { put("limit", it.toString()) } + outputMode?.let { put("outputMode", it.toString()) } + outputPreset?.let { put("outputPreset", it.toString()) } putAll(additionalQueryParams) } .build() + /** Preserve source keys or convert result field names. */ + class FieldStyle @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val SOURCE = of("source") + + @JvmField val CAMEL_CASE = of("camelCase") + + @JvmField val SNAKE_CASE = of("snake_case") + + @JvmStatic fun of(value: String) = FieldStyle(JsonField.of(value)) + } + + /** An enum containing [FieldStyle]'s known values. */ + enum class Known { + SOURCE, + CAMEL_CASE, + SNAKE_CASE, + } + + /** + * An enum containing [FieldStyle]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [FieldStyle] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + SOURCE, + CAMEL_CASE, + SNAKE_CASE, + /** + * An enum member indicating that [FieldStyle] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + SOURCE -> Value.SOURCE + CAMEL_CASE -> Value.CAMEL_CASE + SNAKE_CASE -> Value.SNAKE_CASE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + SOURCE -> Known.SOURCE + CAMEL_CASE -> Known.CAMEL_CASE + SNAKE_CASE -> Known.SNAKE_CASE + else -> throw XTwitterScraperInvalidDataException("Unknown FieldStyle: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): FieldStyle = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FieldStyle && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Select compact, full, or raw-compatible result fields. */ + class OutputMode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val COMPACT = of("compact") + + @JvmField val FULL = of("full") + + @JvmField val RAW = of("raw") + + @JvmStatic fun of(value: String) = OutputMode(JsonField.of(value)) + } + + /** An enum containing [OutputMode]'s known values. */ + enum class Known { + COMPACT, + FULL, + RAW, + } + + /** + * An enum containing [OutputMode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [OutputMode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + COMPACT, + FULL, + RAW, + /** + * An enum member indicating that [OutputMode] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + COMPACT -> Value.COMPACT + FULL -> Value.FULL + RAW -> Value.RAW + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + COMPACT -> Known.COMPACT + FULL -> Known.FULL + RAW -> Known.RAW + else -> throw XTwitterScraperInvalidDataException("Unknown OutputMode: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): OutputMode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputMode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Keep enrichment nested or merge it into each result. */ + class OutputPreset @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val NESTED = of("nested") + + @JvmField val FLAT = of("flat") + + @JvmStatic fun of(value: String) = OutputPreset(JsonField.of(value)) + } + + /** An enum containing [OutputPreset]'s known values. */ + enum class Known { + NESTED, + FLAT, + } + + /** + * An enum containing [OutputPreset]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [OutputPreset] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + NESTED, + FLAT, + /** + * An enum member indicating that [OutputPreset] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + NESTED -> Value.NESTED + FLAT -> Value.FLAT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + NESTED -> Known.NESTED + FLAT -> Known.FLAT + else -> throw XTwitterScraperInvalidDataException("Unknown OutputPreset: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): OutputPreset = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputPreset && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -227,14 +725,28 @@ private constructor( return other is ExtractionRetrieveParams && id == other.id && cursor == other.cursor && + fieldStyle == other.fieldStyle && + includeRaw == other.includeRaw && limit == other.limit && + outputMode == other.outputMode && + outputPreset == other.outputPreset && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, limit, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + cursor, + fieldStyle, + includeRaw, + limit, + outputMode, + outputPreset, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "ExtractionRetrieveParams{id=$id, cursor=$cursor, limit=$limit, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "ExtractionRetrieveParams{id=$id, cursor=$cursor, fieldStyle=$fieldStyle, includeRaw=$includeRaw, limit=$limit, outputMode=$outputMode, outputPreset=$outputPreset, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParams.kt index dd656eb7..9b2fb8b0 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParams.kt @@ -10,17 +10,31 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer import com.x_twitter_scraper.api.core.Enum import com.x_twitter_scraper.api.core.ExcludeMissing import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.JsonMissing import com.x_twitter_scraper.api.core.JsonValue import com.x_twitter_scraper.api.core.Params +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.core.toImmutable import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.time.LocalDate +import java.time.OffsetDateTime import java.util.Collections import java.util.Objects import java.util.Optional @@ -29,11 +43,15 @@ import kotlin.jvm.optionals.getOrNull /** Run extraction */ class ExtractionRunParams private constructor( + private val dryRun: Boolean?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { + /** Estimate cost without creating an extraction. */ + fun dryRun(): Optional = Optional.ofNullable(dryRun) + /** * Identifier for the extraction tool used to run a job. * @@ -60,2802 +78,8187 @@ private constructor( fun anyWords(): Optional = body.anyWords() /** - * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) + * Bio terms separated by commas or lines. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun boundingBox(): Optional = body.boundingBox() + fun bioContains(): Optional = body.bioContains() /** - * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * Return only Blue-verified Tweet authors. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun cashtags(): Optional = body.cashtags() + fun blueVerifiedOnly(): Optional = body.blueVerifiedOnly() /** - * Conversation ID filter (tweet_search_extractor) + * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun conversationId(): Optional = body.conversationId() + fun boundingBox(): Optional = body.boundingBox() /** - * Exact phrase to match (tweet_search_extractor) + * Match the Tweet card name. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun exactPhrase(): Optional = body.exactPhrase() + fun cardName(): Optional = body.cardName() /** - * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. - * (tweet_search_extractor) + * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun excludeWords(): Optional = body.excludeWords() + fun cashtags(): Optional = body.cashtags() /** - * Filter by author username (tweet_search_extractor) + * Reply collection strategy. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun fromUser(): Optional = body.fromUser() + fun collectionStrategy(): Optional = body.collectionStrategy() /** - * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * Conversation ID filter (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun hashtags(): Optional = body.hashtags() + fun conversationId(): Optional = body.conversationId() /** - * Only replies to this tweet ID (tweet_search_extractor) + * Merge duplicate results across collection targets. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun inReplyToTweetId(): Optional = body.inReplyToTweetId() + fun dedupeAcrossTargets(): Optional = body.dedupeAcrossTargets() /** - * Language code filter (tweet_search_extractor) + * Keep target duplicates, first rows, or merged overlap. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun language(): Optional = body.language() + fun dedupeMode(): Optional = body.dedupeMode() /** - * Search within a list ID (tweet_search_extractor) + * Exact phrase to match (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun listId(): Optional = body.listId() + fun exactPhrase(): Optional = body.exactPhrase() /** - * Media type filter (tweet_search_extractor) + * Exclude replies from the source author. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun mediaType(): Optional = body.mediaType() + fun excludeOriginalAuthor(): Optional = body.excludeOriginalAuthor() /** - * Filter tweets mentioning a username (tweet_search_extractor) + * Exclude a source application. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun mentioning(): Optional = body.mentioning() + fun excludeSource(): Optional = body.excludeSource() /** - * Minimum likes threshold (tweet_search_extractor) + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minFaves(): Optional = body.minFaves() + fun excludeWords(): Optional = body.excludeWords() /** - * Minimum quote count threshold (tweet_search_extractor) + * Filter by author username (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minQuotes(): Optional = body.minQuotes() + fun fromUser(): Optional = body.fromUser() /** - * Minimum replies threshold (tweet_search_extractor) + * Match latitude, longitude, and radius. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minReplies(): Optional = body.minReplies() + fun geocode(): Optional = body.geocode() /** - * Minimum retweets threshold (tweet_search_extractor) + * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun minRetweets(): Optional = body.minRetweets() + fun hashtags(): Optional = body.hashtags() /** - * Search within a place ID (tweet_search_extractor) + * Require a profile location. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun place(): Optional = body.place() + fun hasLocation(): Optional = body.hasLocation() /** - * Search within a country code (tweet_search_extractor) + * Return only replies with media. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun placeCountry(): Optional = body.placeCountry() + fun hasMediaOnly(): Optional = body.hasMediaOnly() /** - * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * Require a profile website. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun pointRadius(): Optional = body.pointRadius() + fun hasWebsite(): Optional = body.hasWebsite() /** - * Quote mode (tweet_search_extractor) + * Include the source post in reply results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun quotes(): Optional = body.quotes() + fun includeOriginalPost(): Optional = body.includeOriginalPost() /** - * Only quotes of this tweet ID (tweet_search_extractor) + * Add matching search terms to collection metadata. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun quotesOfTweetId(): Optional = body.quotesOfTweetId() + fun includeSearchTerms(): Optional = body.includeSearchTerms() /** - * Reply mode (tweet_search_extractor) + * Add source target metadata to each result. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun replies(): Optional = body.replies() + fun includeTargetMetadata(): Optional = body.includeTargetMetadata() /** - * Maximum number of results to extract. When set, the extraction stops after reaching this - * limit. + * Only replies to this tweet ID (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun resultsLimit(): Optional = body.resultsLimit() + fun inReplyToTweetId(): Optional = body.inReplyToTweetId() /** - * Retweet mode (tweet_search_extractor) + * Language code filter (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun retweets(): Optional = body.retweets() + fun language(): Optional = body.language() /** - * Only retweets of this tweet ID (tweet_search_extractor) + * Search within a list ID (tweet_search_extractor) * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun retweetsOfTweetId(): Optional = body.retweetsOfTweetId() + fun listId(): Optional = body.listId() /** - * Required for tweet_search_extractor & community_search. + * Required profile location text. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun searchQuery(): Optional = body.searchQuery() + fun locationContains(): Optional = body.locationContains() /** - * Start date YYYY-MM-DD (tweet_search_extractor) + * Maximum nested reply depth. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun sinceDate(): Optional = body.sinceDate() + fun maxDepth(): Optional = body.maxDepth() /** - * Required for community_post_extractor & community_search. + * Maximum follower count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetCommunityId(): Optional = body.targetCommunityId() + fun maxFollowers(): Optional = body.maxFollowers() /** - * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * Maximum following count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetListId(): Optional = body.targetListId() + fun maxFollowing(): Optional = body.maxFollowing() /** - * Required for space_explorer. + * Return Tweets older than this Tweet ID. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetSpaceId(): Optional = body.targetSpaceId() + fun maxId(): Optional = body.maxId() /** + * Maximum results collected for each target. + * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetTweetId(): Optional = body.targetTweetId() + fun maxItemsPerTarget(): Optional = body.maxItemsPerTarget() /** + * Maximum Tweet like count. + * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun targetUsername(): Optional = body.targetUsername() + fun maxLikes(): Optional = body.maxLikes() /** - * Filter replies sent to a username (tweet_search_extractor) + * Reply pages collected for each target. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun toUser(): Optional = body.toUser() + fun maxPagesPerTarget(): Optional = body.maxPagesPerTarget() /** - * End date YYYY-MM-DD (tweet_search_extractor) + * Maximum post count for profile results. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun untilDate(): Optional = body.untilDate() + fun maxPosts(): Optional = body.maxPosts() /** - * URL substring or domain filter (tweet_search_extractor) + * Maximum Tweet quote count. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun url(): Optional = body.url() + fun maxQuotes(): Optional = body.maxQuotes() /** - * Only verified authors (tweet_search_extractor) + * Maximum Tweet reply count. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun verifiedOnly(): Optional = body.verifiedOnly() + fun maxReplies(): Optional = body.maxReplies() /** - * Returns the raw JSON value of [toolType]. + * Maximum Tweet repost count. * - * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _toolType(): JsonField = body._toolType() + fun maxRetweets(): Optional = body.maxRetweets() /** - * Returns the raw JSON value of [advancedQuery]. + * Media type filter (tweet_search_extractor) * - * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _advancedQuery(): JsonField = body._advancedQuery() + fun mediaType(): Optional = body.mediaType() /** - * Returns the raw JSON value of [anyWords]. + * Filter tweets mentioning a username (tweet_search_extractor) * - * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _anyWords(): JsonField = body._anyWords() + fun mentioning(): Optional = body.mentioning() /** - * Returns the raw JSON value of [boundingBox]. + * Minimum profile age in days. * - * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _boundingBox(): JsonField = body._boundingBox() + fun minAccountAgeDays(): Optional = body.minAccountAgeDays() /** - * Returns the raw JSON value of [cashtags]. + * Minimum Tweet bookmark count. * - * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _cashtags(): JsonField = body._cashtags() + fun minBookmarks(): Optional = body.minBookmarks() /** - * Returns the raw JSON value of [conversationId]. + * Minimum likes threshold (tweet_search_extractor) * - * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _conversationId(): JsonField = body._conversationId() + fun minFaves(): Optional = body.minFaves() /** - * Returns the raw JSON value of [exactPhrase]. + * Minimum follower count for profile results. * - * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _exactPhrase(): JsonField = body._exactPhrase() + fun minFollowers(): Optional = body.minFollowers() /** - * Returns the raw JSON value of [excludeWords]. + * Minimum following count for profile results. * - * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _excludeWords(): JsonField = body._excludeWords() + fun minFollowing(): Optional = body.minFollowing() /** - * Returns the raw JSON value of [fromUser]. + * Minimum post count for profile results. * - * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _fromUser(): JsonField = body._fromUser() + fun minPosts(): Optional = body.minPosts() /** - * Returns the raw JSON value of [hashtags]. + * Minimum quote count threshold (tweet_search_extractor) * - * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _hashtags(): JsonField = body._hashtags() + fun minQuotes(): Optional = body.minQuotes() /** - * Returns the raw JSON value of [inReplyToTweetId]. + * Minimum replies threshold (tweet_search_extractor) * - * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _inReplyToTweetId(): JsonField = body._inReplyToTweetId() + fun minReplies(): Optional = body.minReplies() /** - * Returns the raw JSON value of [language]. + * Minimum retweets threshold (tweet_search_extractor) * - * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _language(): JsonField = body._language() + fun minRetweets(): Optional = body.minRetweets() /** - * Returns the raw JSON value of [listId]. + * Minimum Tweet view count. * - * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _listId(): JsonField = body._listId() + fun minViews(): Optional = body.minViews() /** - * Returns the raw JSON value of [mediaType]. + * Only return native reposts. * - * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _mediaType(): JsonField = body._mediaType() + fun nativeRetweets(): Optional = body.nativeRetweets() /** - * Returns the raw JSON value of [mentioning]. + * Match a place name. * - * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _mentioning(): JsonField = body._mentioning() + fun near(): Optional = body.near() /** - * Returns the raw JSON value of [minFaves]. + * Only return news results. * - * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minFaves(): JsonField = body._minFaves() + fun news(): Optional = body.news() /** - * Returns the raw JSON value of [minQuotes]. + * Shortcut for dedupeMode=merge. * - * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minQuotes(): JsonField = body._minQuotes() + fun overlapMode(): Optional = body.overlapMode() /** - * Returns the raw JSON value of [minReplies]. + * Search within a place ID (tweet_search_extractor) * - * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minReplies(): JsonField = body._minReplies() + fun place(): Optional = body.place() /** - * Returns the raw JSON value of [minRetweets]. + * Search within a country code (tweet_search_extractor) * - * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _minRetweets(): JsonField = body._minRetweets() + fun placeCountry(): Optional = body.placeCountry() /** - * Returns the raw JSON value of [place]. + * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) * - * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _place(): JsonField = body._place() + fun pointRadius(): Optional = body.pointRadius() /** - * Returns the raw JSON value of [placeCountry]. + * Search ranking applied to every query. * - * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _placeCountry(): JsonField = body._placeCountry() + fun queryType(): Optional = body.queryType() /** - * Returns the raw JSON value of [pointRadius]. + * Quote mode (tweet_search_extractor) * - * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _pointRadius(): JsonField = body._pointRadius() + fun quotes(): Optional = body.quotes() /** - * Returns the raw JSON value of [quotes]. + * Only quotes of this tweet ID (tweet_search_extractor) * - * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _quotes(): JsonField = body._quotes() + fun quotesOfTweetId(): Optional = body.quotesOfTweetId() /** - * Returns the raw JSON value of [quotesOfTweetId]. + * Profile relations processed within one job. * - * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _quotesOfTweetId(): JsonField = body._quotesOfTweetId() + fun relationTargets(): Optional> = body.relationTargets() /** - * Returns the raw JSON value of [replies]. + * Reply mode (tweet_search_extractor) * - * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _replies(): JsonField = body._replies() + fun replies(): Optional = body.replies() /** - * Returns the raw JSON value of [resultsLimit]. + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. * - * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _resultsLimit(): JsonField = body._resultsLimit() + fun resultsLimit(): Optional = body.resultsLimit() /** - * Returns the raw JSON value of [retweets]. + * Retweet mode (tweet_search_extractor) * - * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _retweets(): JsonField = body._retweets() + fun retweets(): Optional = body.retweets() /** - * Returns the raw JSON value of [retweetsOfTweetId]. + * Only retweets of this tweet ID (tweet_search_extractor) * - * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _retweetsOfTweetId(): JsonField = body._retweetsOfTweetId() + fun retweetsOfTweetId(): Optional = body.retweetsOfTweetId() /** - * Returns the raw JSON value of [searchQuery]. + * Enable the safe-search filter. * - * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _searchQuery(): JsonField = body._searchQuery() + fun safe(): Optional = body.safe() /** - * Returns the raw JSON value of [sinceDate]. + * Reply depth scope. * - * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _sinceDate(): JsonField = body._sinceDate() + fun scope(): Optional = body.scope() /** - * Returns the raw JSON value of [targetCommunityId]. + * Search queries processed as one collection job. * - * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetCommunityId(): JsonField = body._targetCommunityId() + fun searchQueries(): Optional> = body.searchQueries() /** - * Returns the raw JSON value of [targetListId]. + * Required for tweet_search_extractor & community_search. * - * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetListId(): JsonField = body._targetListId() + fun searchQuery(): Optional = body.searchQuery() /** - * Returns the raw JSON value of [targetSpaceId]. + * Start date YYYY-MM-DD (tweet_search_extractor) * - * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetSpaceId(): JsonField = body._targetSpaceId() + fun sinceDate(): Optional = body.sinceDate() /** - * Returns the raw JSON value of [targetTweetId]. + * Return Tweets newer than this Tweet ID. * - * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetTweetId(): JsonField = body._targetTweetId() + fun sinceId(): Optional = body.sinceId() /** - * Returns the raw JSON value of [targetUsername]. + * Reply start time as ISO 8601 or Unix seconds. * - * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _targetUsername(): JsonField = body._targetUsername() + fun sinceTime(): Optional = body.sinceTime() /** - * Returns the raw JSON value of [toUser]. + * Reply result order. * - * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _toUser(): JsonField = body._toUser() + fun sort(): Optional = body.sort() /** - * Returns the raw JSON value of [untilDate]. + * Match the source application. * - * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _untilDate(): JsonField = body._untilDate() + fun source(): Optional = body.source() /** - * Returns the raw JSON value of [url]. + * Resume one reply target from this cursor. * - * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _url(): JsonField = body._url() + fun startCursor(): Optional = body.startCursor() /** - * Returns the raw JSON value of [verifiedOnly]. + * Required for community_post_extractor & community_search. * - * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected type. + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun _verifiedOnly(): JsonField = body._verifiedOnly() - - fun _additionalBodyProperties(): Map = body._additionalProperties() + fun targetCommunityId(): Optional = body.targetCommunityId() - /** Additional headers to send with the request. */ - fun _additionalHeaders(): Headers = additionalHeaders + /** + * Community IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetCommunityIds(): Optional> = body.targetCommunityIds() - /** Additional query param to send with the request. */ - fun _additionalQueryParams(): QueryParams = additionalQueryParams + /** + * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetListId(): Optional = body.targetListId() - fun toBuilder() = Builder().from(this) + /** + * List IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetListIds(): Optional> = body.targetListIds() - companion object { + /** + * Mixed targets auto-routed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targets(): Optional> = body.targets() - /** - * Returns a mutable builder for constructing an instance of [ExtractionRunParams]. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + /** + * Required for space_explorer. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetSpaceId(): Optional = body.targetSpaceId() - /** A builder for [ExtractionRunParams]. */ - class Builder internal constructor() { + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetTweetId(): Optional = body.targetTweetId() - private var body: Body.Builder = Body.builder() - private var additionalHeaders: Headers.Builder = Headers.builder() - private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + /** + * Tweet IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetTweetIds(): Optional> = body.targetTweetIds() - @JvmSynthetic - internal fun from(extractionRunParams: ExtractionRunParams) = apply { - body = extractionRunParams.body.toBuilder() - additionalHeaders = extractionRunParams.additionalHeaders.toBuilder() - additionalQueryParams = extractionRunParams.additionalQueryParams.toBuilder() - } + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetUsername(): Optional = body.targetUsername() - /** - * Sets the entire request body. - * - * This is generally only useful if you are already constructing the body separately. - * Otherwise, it's more convenient to use the top-level setters instead: - * - [toolType] - * - [advancedQuery] - * - [anyWords] - * - [boundingBox] - * - [cashtags] - * - etc. - */ - fun body(body: Body) = apply { this.body = body.toBuilder() } + /** + * Usernames processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun targetUsernames(): Optional> = body.targetUsernames() - /** Identifier for the extraction tool used to run a job. */ - fun toolType(toolType: ToolType) = apply { body.toolType(toolType) } + /** + * Filter replies sent to a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun toUser(): Optional = body.toUser() - /** - * Sets [Builder.toolType] to an arbitrary JSON value. - * - * You should usually call [Builder.toolType] with a well-typed [ToolType] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun toolType(toolType: JsonField) = apply { body.toolType(toolType) } + /** + * End date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun untilDate(): Optional = body.untilDate() - /** Raw advanced search query appended as-is (tweet_search_extractor) */ - fun advancedQuery(advancedQuery: String) = apply { body.advancedQuery(advancedQuery) } + /** + * Reply end time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun untilTime(): Optional = body.untilTime() - /** - * Sets [Builder.advancedQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.advancedQuery] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun advancedQuery(advancedQuery: JsonField) = apply { - body.advancedQuery(advancedQuery) - } + /** + * URL substring or domain filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun url(): Optional = body.url() - /** - * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. - * (tweet_search_extractor) - */ - fun anyWords(anyWords: String) = apply { body.anyWords(anyWords) } + /** + * Required username text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun usernameContains(): Optional = body.usernameContains() - /** - * Sets [Builder.anyWords] to an arbitrary JSON value. - * - * You should usually call [Builder.anyWords] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun anyWords(anyWords: JsonField) = apply { body.anyWords(anyWords) } + /** + * Only verified authors (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun verifiedOnly(): Optional = body.verifiedOnly() - /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ - fun boundingBox(boundingBox: String) = apply { body.boundingBox(boundingBox) } + /** + * Exact profile verification type. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun verifiedType(): Optional = body.verifiedType() - /** - * Sets [Builder.boundingBox] to an arbitrary JSON value. - * - * You should usually call [Builder.boundingBox] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun boundingBox(boundingBox: JsonField) = apply { body.boundingBox(boundingBox) } + /** + * Set the radius for the near filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun within(): Optional = body.within() - /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ - fun cashtags(cashtags: String) = apply { body.cashtags(cashtags) } - - /** - * Sets [Builder.cashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.cashtags] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun cashtags(cashtags: JsonField) = apply { body.cashtags(cashtags) } + /** + * Match Tweets inside a recent time window. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun withinTime(): Optional = body.withinTime() - /** Conversation ID filter (tweet_search_extractor) */ - fun conversationId(conversationId: String) = apply { body.conversationId(conversationId) } + /** + * Returns the raw JSON value of [toolType]. + * + * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _toolType(): JsonField = body._toolType() - /** - * Sets [Builder.conversationId] to an arbitrary JSON value. - * - * You should usually call [Builder.conversationId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun conversationId(conversationId: JsonField) = apply { - body.conversationId(conversationId) - } + /** + * Returns the raw JSON value of [advancedQuery]. + * + * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _advancedQuery(): JsonField = body._advancedQuery() - /** Exact phrase to match (tweet_search_extractor) */ - fun exactPhrase(exactPhrase: String) = apply { body.exactPhrase(exactPhrase) } + /** + * Returns the raw JSON value of [anyWords]. + * + * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _anyWords(): JsonField = body._anyWords() - /** - * Sets [Builder.exactPhrase] to an arbitrary JSON value. - * - * You should usually call [Builder.exactPhrase] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun exactPhrase(exactPhrase: JsonField) = apply { body.exactPhrase(exactPhrase) } + /** + * Returns the raw JSON value of [bioContains]. + * + * Unlike [bioContains], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _bioContains(): JsonField = body._bioContains() - /** - * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. - * (tweet_search_extractor) - */ - fun excludeWords(excludeWords: String) = apply { body.excludeWords(excludeWords) } + /** + * Returns the raw JSON value of [blueVerifiedOnly]. + * + * Unlike [blueVerifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _blueVerifiedOnly(): JsonField = body._blueVerifiedOnly() - /** - * Sets [Builder.excludeWords] to an arbitrary JSON value. - * - * You should usually call [Builder.excludeWords] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun excludeWords(excludeWords: JsonField) = apply { - body.excludeWords(excludeWords) - } + /** + * Returns the raw JSON value of [boundingBox]. + * + * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _boundingBox(): JsonField = body._boundingBox() - /** Filter by author username (tweet_search_extractor) */ - fun fromUser(fromUser: String) = apply { body.fromUser(fromUser) } + /** + * Returns the raw JSON value of [cardName]. + * + * Unlike [cardName], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _cardName(): JsonField = body._cardName() - /** - * Sets [Builder.fromUser] to an arbitrary JSON value. - * - * You should usually call [Builder.fromUser] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun fromUser(fromUser: JsonField) = apply { body.fromUser(fromUser) } + /** + * Returns the raw JSON value of [cashtags]. + * + * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _cashtags(): JsonField = body._cashtags() - /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ - fun hashtags(hashtags: String) = apply { body.hashtags(hashtags) } + /** + * Returns the raw JSON value of [collectionStrategy]. + * + * Unlike [collectionStrategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _collectionStrategy(): JsonField = body._collectionStrategy() - /** - * Sets [Builder.hashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.hashtags] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun hashtags(hashtags: JsonField) = apply { body.hashtags(hashtags) } + /** + * Returns the raw JSON value of [conversationId]. + * + * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _conversationId(): JsonField = body._conversationId() - /** Only replies to this tweet ID (tweet_search_extractor) */ - fun inReplyToTweetId(inReplyToTweetId: String) = apply { - body.inReplyToTweetId(inReplyToTweetId) - } + /** + * Returns the raw JSON value of [dedupeAcrossTargets]. + * + * Unlike [dedupeAcrossTargets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _dedupeAcrossTargets(): JsonField = body._dedupeAcrossTargets() - /** - * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { - body.inReplyToTweetId(inReplyToTweetId) - } + /** + * Returns the raw JSON value of [dedupeMode]. + * + * Unlike [dedupeMode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _dedupeMode(): JsonField = body._dedupeMode() - /** Language code filter (tweet_search_extractor) */ - fun language(language: String) = apply { body.language(language) } + /** + * Returns the raw JSON value of [exactPhrase]. + * + * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _exactPhrase(): JsonField = body._exactPhrase() - /** - * Sets [Builder.language] to an arbitrary JSON value. - * - * You should usually call [Builder.language] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun language(language: JsonField) = apply { body.language(language) } + /** + * Returns the raw JSON value of [excludeOriginalAuthor]. + * + * Unlike [excludeOriginalAuthor], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _excludeOriginalAuthor(): JsonField = body._excludeOriginalAuthor() - /** Search within a list ID (tweet_search_extractor) */ - fun listId(listId: String) = apply { body.listId(listId) } + /** + * Returns the raw JSON value of [excludeSource]. + * + * Unlike [excludeSource], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _excludeSource(): JsonField = body._excludeSource() - /** - * Sets [Builder.listId] to an arbitrary JSON value. - * - * You should usually call [Builder.listId] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun listId(listId: JsonField) = apply { body.listId(listId) } + /** + * Returns the raw JSON value of [excludeWords]. + * + * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _excludeWords(): JsonField = body._excludeWords() - /** Media type filter (tweet_search_extractor) */ - fun mediaType(mediaType: MediaType) = apply { body.mediaType(mediaType) } + /** + * Returns the raw JSON value of [fromUser]. + * + * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _fromUser(): JsonField = body._fromUser() - /** - * Sets [Builder.mediaType] to an arbitrary JSON value. - * - * You should usually call [Builder.mediaType] with a well-typed [MediaType] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun mediaType(mediaType: JsonField) = apply { body.mediaType(mediaType) } + /** + * Returns the raw JSON value of [geocode]. + * + * Unlike [geocode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _geocode(): JsonField = body._geocode() - /** Filter tweets mentioning a username (tweet_search_extractor) */ - fun mentioning(mentioning: String) = apply { body.mentioning(mentioning) } + /** + * Returns the raw JSON value of [hashtags]. + * + * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hashtags(): JsonField = body._hashtags() - /** - * Sets [Builder.mentioning] to an arbitrary JSON value. - * - * You should usually call [Builder.mentioning] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun mentioning(mentioning: JsonField) = apply { body.mentioning(mentioning) } + /** + * Returns the raw JSON value of [hasLocation]. + * + * Unlike [hasLocation], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasLocation(): JsonField = body._hasLocation() - /** Minimum likes threshold (tweet_search_extractor) */ - fun minFaves(minFaves: Long) = apply { body.minFaves(minFaves) } + /** + * Returns the raw JSON value of [hasMediaOnly]. + * + * Unlike [hasMediaOnly], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasMediaOnly(): JsonField = body._hasMediaOnly() - /** - * Sets [Builder.minFaves] to an arbitrary JSON value. - * - * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minFaves(minFaves: JsonField) = apply { body.minFaves(minFaves) } + /** + * Returns the raw JSON value of [hasWebsite]. + * + * Unlike [hasWebsite], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasWebsite(): JsonField = body._hasWebsite() - /** Minimum quote count threshold (tweet_search_extractor) */ - fun minQuotes(minQuotes: Long) = apply { body.minQuotes(minQuotes) } + /** + * Returns the raw JSON value of [includeOriginalPost]. + * + * Unlike [includeOriginalPost], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeOriginalPost(): JsonField = body._includeOriginalPost() - /** - * Sets [Builder.minQuotes] to an arbitrary JSON value. - * - * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minQuotes(minQuotes: JsonField) = apply { body.minQuotes(minQuotes) } + /** + * Returns the raw JSON value of [includeSearchTerms]. + * + * Unlike [includeSearchTerms], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeSearchTerms(): JsonField = body._includeSearchTerms() - /** Minimum replies threshold (tweet_search_extractor) */ - fun minReplies(minReplies: Long) = apply { body.minReplies(minReplies) } + /** + * Returns the raw JSON value of [includeTargetMetadata]. + * + * Unlike [includeTargetMetadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _includeTargetMetadata(): JsonField = body._includeTargetMetadata() - /** - * Sets [Builder.minReplies] to an arbitrary JSON value. - * - * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun minReplies(minReplies: JsonField) = apply { body.minReplies(minReplies) } + /** + * Returns the raw JSON value of [inReplyToTweetId]. + * + * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _inReplyToTweetId(): JsonField = body._inReplyToTweetId() - /** Minimum retweets threshold (tweet_search_extractor) */ - fun minRetweets(minRetweets: Long) = apply { body.minRetweets(minRetweets) } + /** + * Returns the raw JSON value of [language]. + * + * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _language(): JsonField = body._language() - /** - * Sets [Builder.minRetweets] to an arbitrary JSON value. - * - * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun minRetweets(minRetweets: JsonField) = apply { body.minRetweets(minRetweets) } + /** + * Returns the raw JSON value of [listId]. + * + * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _listId(): JsonField = body._listId() - /** Search within a place ID (tweet_search_extractor) */ - fun place(place: String) = apply { body.place(place) } + /** + * Returns the raw JSON value of [locationContains]. + * + * Unlike [locationContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _locationContains(): JsonField = body._locationContains() - /** - * Sets [Builder.place] to an arbitrary JSON value. - * - * You should usually call [Builder.place] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun place(place: JsonField) = apply { body.place(place) } + /** + * Returns the raw JSON value of [maxDepth]. + * + * Unlike [maxDepth], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxDepth(): JsonField = body._maxDepth() - /** Search within a country code (tweet_search_extractor) */ - fun placeCountry(placeCountry: String) = apply { body.placeCountry(placeCountry) } + /** + * Returns the raw JSON value of [maxFollowers]. + * + * Unlike [maxFollowers], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxFollowers(): JsonField = body._maxFollowers() - /** - * Sets [Builder.placeCountry] to an arbitrary JSON value. - * - * You should usually call [Builder.placeCountry] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun placeCountry(placeCountry: JsonField) = apply { - body.placeCountry(placeCountry) - } + /** + * Returns the raw JSON value of [maxFollowing]. + * + * Unlike [maxFollowing], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxFollowing(): JsonField = body._maxFollowing() - /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ - fun pointRadius(pointRadius: String) = apply { body.pointRadius(pointRadius) } + /** + * Returns the raw JSON value of [maxId]. + * + * Unlike [maxId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxId(): JsonField = body._maxId() - /** - * Sets [Builder.pointRadius] to an arbitrary JSON value. - * - * You should usually call [Builder.pointRadius] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun pointRadius(pointRadius: JsonField) = apply { body.pointRadius(pointRadius) } + /** + * Returns the raw JSON value of [maxItemsPerTarget]. + * + * Unlike [maxItemsPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _maxItemsPerTarget(): JsonField = body._maxItemsPerTarget() - /** Quote mode (tweet_search_extractor) */ - fun quotes(quotes: Quotes) = apply { body.quotes(quotes) } + /** + * Returns the raw JSON value of [maxLikes]. + * + * Unlike [maxLikes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxLikes(): JsonField = body._maxLikes() - /** - * Sets [Builder.quotes] to an arbitrary JSON value. - * - * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun quotes(quotes: JsonField) = apply { body.quotes(quotes) } + /** + * Returns the raw JSON value of [maxPagesPerTarget]. + * + * Unlike [maxPagesPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _maxPagesPerTarget(): JsonField = body._maxPagesPerTarget() - /** Only quotes of this tweet ID (tweet_search_extractor) */ - fun quotesOfTweetId(quotesOfTweetId: String) = apply { - body.quotesOfTweetId(quotesOfTweetId) - } + /** + * Returns the raw JSON value of [maxPosts]. + * + * Unlike [maxPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxPosts(): JsonField = body._maxPosts() - /** - * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { - body.quotesOfTweetId(quotesOfTweetId) - } + /** + * Returns the raw JSON value of [maxQuotes]. + * + * Unlike [maxQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxQuotes(): JsonField = body._maxQuotes() - /** Reply mode (tweet_search_extractor) */ - fun replies(replies: Replies) = apply { body.replies(replies) } + /** + * Returns the raw JSON value of [maxReplies]. + * + * Unlike [maxReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxReplies(): JsonField = body._maxReplies() - /** - * Sets [Builder.replies] to an arbitrary JSON value. - * - * You should usually call [Builder.replies] with a well-typed [Replies] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun replies(replies: JsonField) = apply { body.replies(replies) } + /** + * Returns the raw JSON value of [maxRetweets]. + * + * Unlike [maxRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _maxRetweets(): JsonField = body._maxRetweets() - /** - * Maximum number of results to extract. When set, the extraction stops after reaching this - * limit. - */ - fun resultsLimit(resultsLimit: Long) = apply { body.resultsLimit(resultsLimit) } + /** + * Returns the raw JSON value of [mediaType]. + * + * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _mediaType(): JsonField = body._mediaType() - /** - * Sets [Builder.resultsLimit] to an arbitrary JSON value. - * - * You should usually call [Builder.resultsLimit] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun resultsLimit(resultsLimit: JsonField) = apply { body.resultsLimit(resultsLimit) } + /** + * Returns the raw JSON value of [mentioning]. + * + * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _mentioning(): JsonField = body._mentioning() - /** Retweet mode (tweet_search_extractor) */ - fun retweets(retweets: Retweets) = apply { body.retweets(retweets) } + /** + * Returns the raw JSON value of [minAccountAgeDays]. + * + * Unlike [minAccountAgeDays], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _minAccountAgeDays(): JsonField = body._minAccountAgeDays() - /** - * Sets [Builder.retweets] to an arbitrary JSON value. - * - * You should usually call [Builder.retweets] with a well-typed [Retweets] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun retweets(retweets: JsonField) = apply { body.retweets(retweets) } + /** + * Returns the raw JSON value of [minBookmarks]. + * + * Unlike [minBookmarks], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minBookmarks(): JsonField = body._minBookmarks() - /** Only retweets of this tweet ID (tweet_search_extractor) */ - fun retweetsOfTweetId(retweetsOfTweetId: String) = apply { - body.retweetsOfTweetId(retweetsOfTweetId) - } + /** + * Returns the raw JSON value of [minFaves]. + * + * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFaves(): JsonField = body._minFaves() - /** - * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { - body.retweetsOfTweetId(retweetsOfTweetId) - } + /** + * Returns the raw JSON value of [minFollowers]. + * + * Unlike [minFollowers], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFollowers(): JsonField = body._minFollowers() - /** Required for tweet_search_extractor & community_search. */ - fun searchQuery(searchQuery: String) = apply { body.searchQuery(searchQuery) } + /** + * Returns the raw JSON value of [minFollowing]. + * + * Unlike [minFollowing], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minFollowing(): JsonField = body._minFollowing() - /** - * Sets [Builder.searchQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.searchQuery] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun searchQuery(searchQuery: JsonField) = apply { body.searchQuery(searchQuery) } + /** + * Returns the raw JSON value of [minPosts]. + * + * Unlike [minPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minPosts(): JsonField = body._minPosts() - /** Start date YYYY-MM-DD (tweet_search_extractor) */ - fun sinceDate(sinceDate: LocalDate) = apply { body.sinceDate(sinceDate) } + /** + * Returns the raw JSON value of [minQuotes]. + * + * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minQuotes(): JsonField = body._minQuotes() - /** - * Sets [Builder.sinceDate] to an arbitrary JSON value. - * - * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun sinceDate(sinceDate: JsonField) = apply { body.sinceDate(sinceDate) } + /** + * Returns the raw JSON value of [minReplies]. + * + * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minReplies(): JsonField = body._minReplies() - /** Required for community_post_extractor & community_search. */ - fun targetCommunityId(targetCommunityId: String) = apply { - body.targetCommunityId(targetCommunityId) - } + /** + * Returns the raw JSON value of [minRetweets]. + * + * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minRetweets(): JsonField = body._minRetweets() - /** - * Sets [Builder.targetCommunityId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetCommunityId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetCommunityId(targetCommunityId: JsonField) = apply { - body.targetCommunityId(targetCommunityId) - } + /** + * Returns the raw JSON value of [minViews]. + * + * Unlike [minViews], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _minViews(): JsonField = body._minViews() - /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ - fun targetListId(targetListId: String) = apply { body.targetListId(targetListId) } + /** + * Returns the raw JSON value of [nativeRetweets]. + * + * Unlike [nativeRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _nativeRetweets(): JsonField = body._nativeRetweets() - /** - * Sets [Builder.targetListId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetListId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetListId(targetListId: JsonField) = apply { - body.targetListId(targetListId) - } + /** + * Returns the raw JSON value of [near]. + * + * Unlike [near], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _near(): JsonField = body._near() - /** Required for space_explorer. */ - fun targetSpaceId(targetSpaceId: String) = apply { body.targetSpaceId(targetSpaceId) } + /** + * Returns the raw JSON value of [news]. + * + * Unlike [news], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _news(): JsonField = body._news() - /** - * Sets [Builder.targetSpaceId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetSpaceId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetSpaceId(targetSpaceId: JsonField) = apply { - body.targetSpaceId(targetSpaceId) - } + /** + * Returns the raw JSON value of [overlapMode]. + * + * Unlike [overlapMode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _overlapMode(): JsonField = body._overlapMode() - fun targetTweetId(targetTweetId: String) = apply { body.targetTweetId(targetTweetId) } + /** + * Returns the raw JSON value of [place]. + * + * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _place(): JsonField = body._place() - /** - * Sets [Builder.targetTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetTweetId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun targetTweetId(targetTweetId: JsonField) = apply { - body.targetTweetId(targetTweetId) - } + /** + * Returns the raw JSON value of [placeCountry]. + * + * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _placeCountry(): JsonField = body._placeCountry() - fun targetUsername(targetUsername: String) = apply { body.targetUsername(targetUsername) } + /** + * Returns the raw JSON value of [pointRadius]. + * + * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _pointRadius(): JsonField = body._pointRadius() - /** - * Sets [Builder.targetUsername] to an arbitrary JSON value. - * - * You should usually call [Builder.targetUsername] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetUsername(targetUsername: JsonField) = apply { - body.targetUsername(targetUsername) - } + /** + * Returns the raw JSON value of [queryType]. + * + * Unlike [queryType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _queryType(): JsonField = body._queryType() - /** Filter replies sent to a username (tweet_search_extractor) */ - fun toUser(toUser: String) = apply { body.toUser(toUser) } + /** + * Returns the raw JSON value of [quotes]. + * + * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _quotes(): JsonField = body._quotes() - /** - * Sets [Builder.toUser] to an arbitrary JSON value. - * - * You should usually call [Builder.toUser] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun toUser(toUser: JsonField) = apply { body.toUser(toUser) } + /** + * Returns the raw JSON value of [quotesOfTweetId]. + * + * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _quotesOfTweetId(): JsonField = body._quotesOfTweetId() - /** End date YYYY-MM-DD (tweet_search_extractor) */ - fun untilDate(untilDate: LocalDate) = apply { body.untilDate(untilDate) } + /** + * Returns the raw JSON value of [relationTargets]. + * + * Unlike [relationTargets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _relationTargets(): JsonField> = body._relationTargets() - /** - * Sets [Builder.untilDate] to an arbitrary JSON value. - * - * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun untilDate(untilDate: JsonField) = apply { body.untilDate(untilDate) } + /** + * Returns the raw JSON value of [replies]. + * + * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _replies(): JsonField = body._replies() - /** URL substring or domain filter (tweet_search_extractor) */ - fun url(url: String) = apply { body.url(url) } + /** + * Returns the raw JSON value of [resultsLimit]. + * + * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _resultsLimit(): JsonField = body._resultsLimit() - /** - * Sets [Builder.url] to an arbitrary JSON value. - * - * You should usually call [Builder.url] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun url(url: JsonField) = apply { body.url(url) } + /** + * Returns the raw JSON value of [retweets]. + * + * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _retweets(): JsonField = body._retweets() - /** Only verified authors (tweet_search_extractor) */ - fun verifiedOnly(verifiedOnly: Boolean) = apply { body.verifiedOnly(verifiedOnly) } + /** + * Returns the raw JSON value of [retweetsOfTweetId]. + * + * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _retweetsOfTweetId(): JsonField = body._retweetsOfTweetId() - /** - * Sets [Builder.verifiedOnly] to an arbitrary JSON value. - * - * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun verifiedOnly(verifiedOnly: JsonField) = apply { - body.verifiedOnly(verifiedOnly) - } + /** + * Returns the raw JSON value of [safe]. + * + * Unlike [safe], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _safe(): JsonField = body._safe() - fun additionalBodyProperties(additionalBodyProperties: Map) = apply { - body.additionalProperties(additionalBodyProperties) - } + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _scope(): JsonField = body._scope() - fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { - body.putAdditionalProperty(key, value) - } + /** + * Returns the raw JSON value of [searchQueries]. + * + * Unlike [searchQueries], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _searchQueries(): JsonField> = body._searchQueries() - fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = - apply { - body.putAllAdditionalProperties(additionalBodyProperties) - } + /** + * Returns the raw JSON value of [searchQuery]. + * + * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _searchQuery(): JsonField = body._searchQuery() - fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + /** + * Returns the raw JSON value of [sinceDate]. + * + * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceDate(): JsonField = body._sinceDate() - fun removeAllAdditionalBodyProperties(keys: Set) = apply { - body.removeAllAdditionalProperties(keys) - } + /** + * Returns the raw JSON value of [sinceId]. + * + * Unlike [sinceId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceId(): JsonField = body._sinceId() - fun additionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } + /** + * Returns the raw JSON value of [sinceTime]. + * + * Unlike [sinceTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sinceTime(): JsonField = body._sinceTime() - fun additionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } + /** + * Returns the raw JSON value of [sort]. + * + * Unlike [sort], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _sort(): JsonField = body._sort() - fun putAdditionalHeader(name: String, value: String) = apply { - additionalHeaders.put(name, value) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _source(): JsonField = body._source() - fun putAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.put(name, values) - } + /** + * Returns the raw JSON value of [startCursor]. + * + * Unlike [startCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _startCursor(): JsonField = body._startCursor() - fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetCommunityId]. + * + * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _targetCommunityId(): JsonField = body._targetCommunityId() - fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetCommunityIds]. + * + * Unlike [targetCommunityIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _targetCommunityIds(): JsonField> = body._targetCommunityIds() - fun replaceAdditionalHeaders(name: String, value: String) = apply { - additionalHeaders.replace(name, value) - } + /** + * Returns the raw JSON value of [targetListId]. + * + * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetListId(): JsonField = body._targetListId() - fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.replace(name, values) - } + /** + * Returns the raw JSON value of [targetListIds]. + * + * Unlike [targetListIds], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetListIds(): JsonField> = body._targetListIds() - fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targets]. + * + * Unlike [targets], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targets(): JsonField> = body._targets() - fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } + /** + * Returns the raw JSON value of [targetSpaceId]. + * + * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetSpaceId(): JsonField = body._targetSpaceId() - fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + /** + * Returns the raw JSON value of [targetTweetId]. + * + * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetTweetId(): JsonField = body._targetTweetId() - fun removeAllAdditionalHeaders(names: Set) = apply { - additionalHeaders.removeAll(names) - } + /** + * Returns the raw JSON value of [targetTweetIds]. + * + * Unlike [targetTweetIds], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetTweetIds(): JsonField> = body._targetTweetIds() - fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } + /** + * Returns the raw JSON value of [targetUsername]. + * + * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetUsername(): JsonField = body._targetUsername() - fun additionalQueryParams(additionalQueryParams: Map>) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } + /** + * Returns the raw JSON value of [targetUsernames]. + * + * Unlike [targetUsernames], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _targetUsernames(): JsonField> = body._targetUsernames() - fun putAdditionalQueryParam(key: String, value: String) = apply { - additionalQueryParams.put(key, value) - } + /** + * Returns the raw JSON value of [toUser]. + * + * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _toUser(): JsonField = body._toUser() - fun putAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.put(key, values) - } + /** + * Returns the raw JSON value of [untilDate]. + * + * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _untilDate(): JsonField = body._untilDate() - fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [untilTime]. + * + * Unlike [untilTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _untilTime(): JsonField = body._untilTime() - fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _url(): JsonField = body._url() - fun replaceAdditionalQueryParams(key: String, value: String) = apply { - additionalQueryParams.replace(key, value) - } + /** + * Returns the raw JSON value of [usernameContains]. + * + * Unlike [usernameContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _usernameContains(): JsonField = body._usernameContains() - fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.replace(key, values) - } + /** + * Returns the raw JSON value of [verifiedOnly]. + * + * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _verifiedOnly(): JsonField = body._verifiedOnly() - fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [verifiedType]. + * + * Unlike [verifiedType], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _verifiedType(): JsonField = body._verifiedType() - fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } + /** + * Returns the raw JSON value of [within]. + * + * Unlike [within], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _within(): JsonField = body._within() - fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + /** + * Returns the raw JSON value of [withinTime]. + * + * Unlike [withinTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _withinTime(): JsonField = body._withinTime() - fun removeAllAdditionalQueryParams(keys: Set) = apply { - additionalQueryParams.removeAll(keys) - } + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { /** - * Returns an immutable instance of [ExtractionRunParams]. - * - * Further updates to this [Builder] will not mutate the returned instance. + * Returns a mutable builder for constructing an instance of [ExtractionRunParams]. * * The following fields are required: * ```java * .toolType() * ``` - * - * @throws IllegalStateException if any required field is unset. */ - fun build(): ExtractionRunParams = - ExtractionRunParams( - body.build(), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + @JvmStatic fun builder() = Builder() } - fun _body(): Body = body + /** A builder for [ExtractionRunParams]. */ + class Builder internal constructor() { - override fun _headers(): Headers = additionalHeaders + private var dryRun: Boolean? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() - override fun _queryParams(): QueryParams = additionalQueryParams + @JvmSynthetic + internal fun from(extractionRunParams: ExtractionRunParams) = apply { + dryRun = extractionRunParams.dryRun + body = extractionRunParams.body.toBuilder() + additionalHeaders = extractionRunParams.additionalHeaders.toBuilder() + additionalQueryParams = extractionRunParams.additionalQueryParams.toBuilder() + } - class Body - @JsonCreator(mode = JsonCreator.Mode.DISABLED) - private constructor( - private val toolType: JsonField, - private val advancedQuery: JsonField, - private val anyWords: JsonField, - private val boundingBox: JsonField, - private val cashtags: JsonField, - private val conversationId: JsonField, - private val exactPhrase: JsonField, - private val excludeWords: JsonField, - private val fromUser: JsonField, - private val hashtags: JsonField, - private val inReplyToTweetId: JsonField, - private val language: JsonField, - private val listId: JsonField, - private val mediaType: JsonField, - private val mentioning: JsonField, - private val minFaves: JsonField, - private val minQuotes: JsonField, - private val minReplies: JsonField, - private val minRetweets: JsonField, - private val place: JsonField, - private val placeCountry: JsonField, - private val pointRadius: JsonField, - private val quotes: JsonField, - private val quotesOfTweetId: JsonField, - private val replies: JsonField, - private val resultsLimit: JsonField, - private val retweets: JsonField, - private val retweetsOfTweetId: JsonField, - private val searchQuery: JsonField, - private val sinceDate: JsonField, - private val targetCommunityId: JsonField, - private val targetListId: JsonField, - private val targetSpaceId: JsonField, - private val targetTweetId: JsonField, - private val targetUsername: JsonField, - private val toUser: JsonField, - private val untilDate: JsonField, - private val url: JsonField, - private val verifiedOnly: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("toolType") - @ExcludeMissing - toolType: JsonField = JsonMissing.of(), - @JsonProperty("advancedQuery") - @ExcludeMissing - advancedQuery: JsonField = JsonMissing.of(), - @JsonProperty("anyWords") - @ExcludeMissing - anyWords: JsonField = JsonMissing.of(), - @JsonProperty("boundingBox") - @ExcludeMissing - boundingBox: JsonField = JsonMissing.of(), - @JsonProperty("cashtags") - @ExcludeMissing - cashtags: JsonField = JsonMissing.of(), - @JsonProperty("conversationId") - @ExcludeMissing - conversationId: JsonField = JsonMissing.of(), - @JsonProperty("exactPhrase") - @ExcludeMissing - exactPhrase: JsonField = JsonMissing.of(), - @JsonProperty("excludeWords") - @ExcludeMissing - excludeWords: JsonField = JsonMissing.of(), - @JsonProperty("fromUser") - @ExcludeMissing - fromUser: JsonField = JsonMissing.of(), - @JsonProperty("hashtags") - @ExcludeMissing - hashtags: JsonField = JsonMissing.of(), - @JsonProperty("inReplyToTweetId") - @ExcludeMissing - inReplyToTweetId: JsonField = JsonMissing.of(), - @JsonProperty("language") - @ExcludeMissing - language: JsonField = JsonMissing.of(), - @JsonProperty("listId") @ExcludeMissing listId: JsonField = JsonMissing.of(), - @JsonProperty("mediaType") - @ExcludeMissing - mediaType: JsonField = JsonMissing.of(), - @JsonProperty("mentioning") - @ExcludeMissing - mentioning: JsonField = JsonMissing.of(), - @JsonProperty("minFaves") @ExcludeMissing minFaves: JsonField = JsonMissing.of(), - @JsonProperty("minQuotes") - @ExcludeMissing - minQuotes: JsonField = JsonMissing.of(), - @JsonProperty("minReplies") - @ExcludeMissing - minReplies: JsonField = JsonMissing.of(), - @JsonProperty("minRetweets") - @ExcludeMissing - minRetweets: JsonField = JsonMissing.of(), - @JsonProperty("place") @ExcludeMissing place: JsonField = JsonMissing.of(), - @JsonProperty("placeCountry") - @ExcludeMissing - placeCountry: JsonField = JsonMissing.of(), - @JsonProperty("pointRadius") - @ExcludeMissing - pointRadius: JsonField = JsonMissing.of(), - @JsonProperty("quotes") @ExcludeMissing quotes: JsonField = JsonMissing.of(), - @JsonProperty("quotesOfTweetId") - @ExcludeMissing - quotesOfTweetId: JsonField = JsonMissing.of(), - @JsonProperty("replies") @ExcludeMissing replies: JsonField = JsonMissing.of(), - @JsonProperty("resultsLimit") - @ExcludeMissing - resultsLimit: JsonField = JsonMissing.of(), - @JsonProperty("retweets") - @ExcludeMissing - retweets: JsonField = JsonMissing.of(), - @JsonProperty("retweetsOfTweetId") - @ExcludeMissing - retweetsOfTweetId: JsonField = JsonMissing.of(), - @JsonProperty("searchQuery") - @ExcludeMissing - searchQuery: JsonField = JsonMissing.of(), - @JsonProperty("sinceDate") - @ExcludeMissing - sinceDate: JsonField = JsonMissing.of(), - @JsonProperty("targetCommunityId") - @ExcludeMissing - targetCommunityId: JsonField = JsonMissing.of(), - @JsonProperty("targetListId") - @ExcludeMissing - targetListId: JsonField = JsonMissing.of(), - @JsonProperty("targetSpaceId") - @ExcludeMissing - targetSpaceId: JsonField = JsonMissing.of(), - @JsonProperty("targetTweetId") - @ExcludeMissing - targetTweetId: JsonField = JsonMissing.of(), - @JsonProperty("targetUsername") - @ExcludeMissing - targetUsername: JsonField = JsonMissing.of(), - @JsonProperty("toUser") @ExcludeMissing toUser: JsonField = JsonMissing.of(), - @JsonProperty("untilDate") - @ExcludeMissing - untilDate: JsonField = JsonMissing.of(), - @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), - @JsonProperty("verifiedOnly") - @ExcludeMissing - verifiedOnly: JsonField = JsonMissing.of(), - ) : this( - toolType, - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - mutableMapOf(), - ) + /** Estimate cost without creating an extraction. */ + fun dryRun(dryRun: Boolean?) = apply { this.dryRun = dryRun } /** - * Identifier for the extraction tool used to run a job. + * Alias for [Builder.dryRun]. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or - * is unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). + * This unboxed primitive overload exists for backwards compatibility. */ - fun toolType(): ToolType = toolType.getRequired("toolType") + fun dryRun(dryRun: Boolean) = dryRun(dryRun as Boolean?) + + /** Alias for calling [Builder.dryRun] with `dryRun.orElse(null)`. */ + fun dryRun(dryRun: Optional) = dryRun(dryRun.getOrNull()) /** - * Raw advanced search query appended as-is (tweet_search_extractor) + * Sets the entire request body. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [toolType] + * - [advancedQuery] + * - [anyWords] + * - [bioContains] + * - [blueVerifiedOnly] + * - etc. */ - fun advancedQuery(): Optional = advancedQuery.getOptional("advancedQuery") + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Identifier for the extraction tool used to run a job. */ + fun toolType(toolType: ToolType) = apply { body.toolType(toolType) } /** - * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. - * (tweet_search_extractor) + * Sets [Builder.toolType] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.toolType] with a well-typed [ToolType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun anyWords(): Optional = anyWords.getOptional("anyWords") + fun toolType(toolType: JsonField) = apply { body.toolType(toolType) } + + /** Raw advanced search query appended as-is (tweet_search_extractor) */ + fun advancedQuery(advancedQuery: String) = apply { body.advancedQuery(advancedQuery) } /** - * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) + * Sets [Builder.advancedQuery] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.advancedQuery] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun boundingBox(): Optional = boundingBox.getOptional("boundingBox") + fun advancedQuery(advancedQuery: JsonField) = apply { + body.advancedQuery(advancedQuery) + } /** - * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) - * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + * (tweet_search_extractor) */ - fun cashtags(): Optional = cashtags.getOptional("cashtags") + fun anyWords(anyWords: String) = apply { body.anyWords(anyWords) } /** - * Conversation ID filter (tweet_search_extractor) + * Sets [Builder.anyWords] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.anyWords] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun conversationId(): Optional = conversationId.getOptional("conversationId") + fun anyWords(anyWords: JsonField) = apply { body.anyWords(anyWords) } + + /** Bio terms separated by commas or lines. */ + fun bioContains(bioContains: String) = apply { body.bioContains(bioContains) } /** - * Exact phrase to match (tweet_search_extractor) + * Sets [Builder.bioContains] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.bioContains] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun exactPhrase(): Optional = exactPhrase.getOptional("exactPhrase") + fun bioContains(bioContains: JsonField) = apply { body.bioContains(bioContains) } + + /** Return only Blue-verified Tweet authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = apply { + body.blueVerifiedOnly(blueVerifiedOnly) + } /** - * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. - * (tweet_search_extractor) + * Sets [Builder.blueVerifiedOnly] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.blueVerifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun excludeWords(): Optional = excludeWords.getOptional("excludeWords") + fun blueVerifiedOnly(blueVerifiedOnly: JsonField) = apply { + body.blueVerifiedOnly(blueVerifiedOnly) + } + + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ + fun boundingBox(boundingBox: String) = apply { body.boundingBox(boundingBox) } /** - * Filter by author username (tweet_search_extractor) + * Sets [Builder.boundingBox] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.boundingBox] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun fromUser(): Optional = fromUser.getOptional("fromUser") + fun boundingBox(boundingBox: JsonField) = apply { body.boundingBox(boundingBox) } + + /** Match the Tweet card name. */ + fun cardName(cardName: String) = apply { body.cardName(cardName) } /** - * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * Sets [Builder.cardName] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.cardName] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun hashtags(): Optional = hashtags.getOptional("hashtags") + fun cardName(cardName: JsonField) = apply { body.cardName(cardName) } + + /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun cashtags(cashtags: String) = apply { body.cashtags(cashtags) } /** - * Only replies to this tweet ID (tweet_search_extractor) + * Sets [Builder.cashtags] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.cashtags] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun inReplyToTweetId(): Optional = inReplyToTweetId.getOptional("inReplyToTweetId") + fun cashtags(cashtags: JsonField) = apply { body.cashtags(cashtags) } + + /** Reply collection strategy. */ + fun collectionStrategy(collectionStrategy: CollectionStrategy) = apply { + body.collectionStrategy(collectionStrategy) + } /** - * Language code filter (tweet_search_extractor) + * Sets [Builder.collectionStrategy] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.collectionStrategy] with a well-typed + * [CollectionStrategy] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. */ - fun language(): Optional = language.getOptional("language") + fun collectionStrategy(collectionStrategy: JsonField) = apply { + body.collectionStrategy(collectionStrategy) + } + + /** Conversation ID filter (tweet_search_extractor) */ + fun conversationId(conversationId: String) = apply { body.conversationId(conversationId) } /** - * Search within a list ID (tweet_search_extractor) + * Sets [Builder.conversationId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.conversationId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun listId(): Optional = listId.getOptional("listId") + fun conversationId(conversationId: JsonField) = apply { + body.conversationId(conversationId) + } + + /** Merge duplicate results across collection targets. */ + fun dedupeAcrossTargets(dedupeAcrossTargets: Boolean) = apply { + body.dedupeAcrossTargets(dedupeAcrossTargets) + } /** - * Media type filter (tweet_search_extractor) + * Sets [Builder.dedupeAcrossTargets] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.dedupeAcrossTargets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun mediaType(): Optional = mediaType.getOptional("mediaType") + fun dedupeAcrossTargets(dedupeAcrossTargets: JsonField) = apply { + body.dedupeAcrossTargets(dedupeAcrossTargets) + } + + /** Keep target duplicates, first rows, or merged overlap. */ + fun dedupeMode(dedupeMode: DedupeMode) = apply { body.dedupeMode(dedupeMode) } /** - * Filter tweets mentioning a username (tweet_search_extractor) + * Sets [Builder.dedupeMode] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.dedupeMode] with a well-typed [DedupeMode] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun mentioning(): Optional = mentioning.getOptional("mentioning") + fun dedupeMode(dedupeMode: JsonField) = apply { body.dedupeMode(dedupeMode) } + + /** Exact phrase to match (tweet_search_extractor) */ + fun exactPhrase(exactPhrase: String) = apply { body.exactPhrase(exactPhrase) } /** - * Minimum likes threshold (tweet_search_extractor) + * Sets [Builder.exactPhrase] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.exactPhrase] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun minFaves(): Optional = minFaves.getOptional("minFaves") + fun exactPhrase(exactPhrase: JsonField) = apply { body.exactPhrase(exactPhrase) } + + /** Exclude replies from the source author. */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean) = apply { + body.excludeOriginalAuthor(excludeOriginalAuthor) + } /** - * Minimum quote count threshold (tweet_search_extractor) + * Sets [Builder.excludeOriginalAuthor] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeOriginalAuthor] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun minQuotes(): Optional = minQuotes.getOptional("minQuotes") + fun excludeOriginalAuthor(excludeOriginalAuthor: JsonField) = apply { + body.excludeOriginalAuthor(excludeOriginalAuthor) + } + + /** Exclude a source application. */ + fun excludeSource(excludeSource: String) = apply { body.excludeSource(excludeSource) } /** - * Minimum replies threshold (tweet_search_extractor) + * Sets [Builder.excludeSource] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeSource] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun minReplies(): Optional = minReplies.getOptional("minReplies") + fun excludeSource(excludeSource: JsonField) = apply { + body.excludeSource(excludeSource) + } /** - * Minimum retweets threshold (tweet_search_extractor) - * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) */ - fun minRetweets(): Optional = minRetweets.getOptional("minRetweets") + fun excludeWords(excludeWords: String) = apply { body.excludeWords(excludeWords) } /** - * Search within a place ID (tweet_search_extractor) + * Sets [Builder.excludeWords] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.excludeWords] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun place(): Optional = place.getOptional("place") + fun excludeWords(excludeWords: JsonField) = apply { + body.excludeWords(excludeWords) + } + + /** Filter by author username (tweet_search_extractor) */ + fun fromUser(fromUser: String) = apply { body.fromUser(fromUser) } /** - * Search within a country code (tweet_search_extractor) + * Sets [Builder.fromUser] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.fromUser] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun placeCountry(): Optional = placeCountry.getOptional("placeCountry") + fun fromUser(fromUser: JsonField) = apply { body.fromUser(fromUser) } + + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String) = apply { body.geocode(geocode) } /** - * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * Sets [Builder.geocode] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.geocode] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun pointRadius(): Optional = pointRadius.getOptional("pointRadius") + fun geocode(geocode: JsonField) = apply { body.geocode(geocode) } + + /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun hashtags(hashtags: String) = apply { body.hashtags(hashtags) } /** - * Quote mode (tweet_search_extractor) + * Sets [Builder.hashtags] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hashtags] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun quotes(): Optional = quotes.getOptional("quotes") + fun hashtags(hashtags: JsonField) = apply { body.hashtags(hashtags) } + + /** Require a profile location. */ + fun hasLocation(hasLocation: Boolean) = apply { body.hasLocation(hasLocation) } /** - * Only quotes of this tweet ID (tweet_search_extractor) + * Sets [Builder.hasLocation] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasLocation] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun quotesOfTweetId(): Optional = quotesOfTweetId.getOptional("quotesOfTweetId") + fun hasLocation(hasLocation: JsonField) = apply { body.hasLocation(hasLocation) } + + /** Return only replies with media. */ + fun hasMediaOnly(hasMediaOnly: Boolean) = apply { body.hasMediaOnly(hasMediaOnly) } /** - * Reply mode (tweet_search_extractor) + * Sets [Builder.hasMediaOnly] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasMediaOnly] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun replies(): Optional = replies.getOptional("replies") + fun hasMediaOnly(hasMediaOnly: JsonField) = apply { + body.hasMediaOnly(hasMediaOnly) + } + + /** Require a profile website. */ + fun hasWebsite(hasWebsite: Boolean) = apply { body.hasWebsite(hasWebsite) } /** - * Maximum number of results to extract. When set, the extraction stops after reaching this - * limit. + * Sets [Builder.hasWebsite] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.hasWebsite] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun resultsLimit(): Optional = resultsLimit.getOptional("resultsLimit") + fun hasWebsite(hasWebsite: JsonField) = apply { body.hasWebsite(hasWebsite) } + + /** Include the source post in reply results. */ + fun includeOriginalPost(includeOriginalPost: Boolean) = apply { + body.includeOriginalPost(includeOriginalPost) + } /** - * Retweet mode (tweet_search_extractor) + * Sets [Builder.includeOriginalPost] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeOriginalPost] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun retweets(): Optional = retweets.getOptional("retweets") + fun includeOriginalPost(includeOriginalPost: JsonField) = apply { + body.includeOriginalPost(includeOriginalPost) + } + + /** Add matching search terms to collection metadata. */ + fun includeSearchTerms(includeSearchTerms: Boolean) = apply { + body.includeSearchTerms(includeSearchTerms) + } /** - * Only retweets of this tweet ID (tweet_search_extractor) + * Sets [Builder.includeSearchTerms] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeSearchTerms] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun retweetsOfTweetId(): Optional = - retweetsOfTweetId.getOptional("retweetsOfTweetId") + fun includeSearchTerms(includeSearchTerms: JsonField) = apply { + body.includeSearchTerms(includeSearchTerms) + } + + /** Add source target metadata to each result. */ + fun includeTargetMetadata(includeTargetMetadata: Boolean) = apply { + body.includeTargetMetadata(includeTargetMetadata) + } /** - * Required for tweet_search_extractor & community_search. + * Sets [Builder.includeTargetMetadata] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.includeTargetMetadata] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun searchQuery(): Optional = searchQuery.getOptional("searchQuery") + fun includeTargetMetadata(includeTargetMetadata: JsonField) = apply { + body.includeTargetMetadata(includeTargetMetadata) + } + + /** Only replies to this tweet ID (tweet_search_extractor) */ + fun inReplyToTweetId(inReplyToTweetId: String) = apply { + body.inReplyToTweetId(inReplyToTweetId) + } /** - * Start date YYYY-MM-DD (tweet_search_extractor) + * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun sinceDate(): Optional = sinceDate.getOptional("sinceDate") + fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { + body.inReplyToTweetId(inReplyToTweetId) + } + + /** Language code filter (tweet_search_extractor) */ + fun language(language: String) = apply { body.language(language) } /** - * Required for community_post_extractor & community_search. + * Sets [Builder.language] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.language] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun targetCommunityId(): Optional = - targetCommunityId.getOptional("targetCommunityId") + fun language(language: JsonField) = apply { body.language(language) } + + /** Search within a list ID (tweet_search_extractor) */ + fun listId(listId: String) = apply { body.listId(listId) } /** - * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * Sets [Builder.listId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.listId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun targetListId(): Optional = targetListId.getOptional("targetListId") + fun listId(listId: JsonField) = apply { body.listId(listId) } + + /** Required profile location text. */ + fun locationContains(locationContains: String) = apply { + body.locationContains(locationContains) + } /** - * Required for space_explorer. + * Sets [Builder.locationContains] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.locationContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun targetSpaceId(): Optional = targetSpaceId.getOptional("targetSpaceId") + fun locationContains(locationContains: JsonField) = apply { + body.locationContains(locationContains) + } - /** - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun targetTweetId(): Optional = targetTweetId.getOptional("targetTweetId") + /** Maximum nested reply depth. */ + fun maxDepth(maxDepth: Long) = apply { body.maxDepth(maxDepth) } /** - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * Sets [Builder.maxDepth] to an arbitrary JSON value. + * + * You should usually call [Builder.maxDepth] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun targetUsername(): Optional = targetUsername.getOptional("targetUsername") + fun maxDepth(maxDepth: JsonField) = apply { body.maxDepth(maxDepth) } + + /** Maximum follower count for profile results. */ + fun maxFollowers(maxFollowers: Long) = apply { body.maxFollowers(maxFollowers) } /** - * Filter replies sent to a username (tweet_search_extractor) + * Sets [Builder.maxFollowers] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxFollowers] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun toUser(): Optional = toUser.getOptional("toUser") + fun maxFollowers(maxFollowers: JsonField) = apply { body.maxFollowers(maxFollowers) } + + /** Maximum following count for profile results. */ + fun maxFollowing(maxFollowing: Long) = apply { body.maxFollowing(maxFollowing) } /** - * End date YYYY-MM-DD (tweet_search_extractor) + * Sets [Builder.maxFollowing] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxFollowing] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun untilDate(): Optional = untilDate.getOptional("untilDate") + fun maxFollowing(maxFollowing: JsonField) = apply { body.maxFollowing(maxFollowing) } + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String) = apply { body.maxId(maxId) } /** - * URL substring or domain filter (tweet_search_extractor) + * Sets [Builder.maxId] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun url(): Optional = url.getOptional("url") + fun maxId(maxId: JsonField) = apply { body.maxId(maxId) } + + /** Maximum results collected for each target. */ + fun maxItemsPerTarget(maxItemsPerTarget: Long) = apply { + body.maxItemsPerTarget(maxItemsPerTarget) + } /** - * Only verified authors (tweet_search_extractor) + * Sets [Builder.maxItemsPerTarget] to an arbitrary JSON value. * - * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). + * You should usually call [Builder.maxItemsPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun verifiedOnly(): Optional = verifiedOnly.getOptional("verifiedOnly") + fun maxItemsPerTarget(maxItemsPerTarget: JsonField) = apply { + body.maxItemsPerTarget(maxItemsPerTarget) + } + + /** Maximum Tweet like count. */ + fun maxLikes(maxLikes: Long) = apply { body.maxLikes(maxLikes) } /** - * Returns the raw JSON value of [toolType]. + * Sets [Builder.maxLikes] to an arbitrary JSON value. * - * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.maxLikes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("toolType") @ExcludeMissing fun _toolType(): JsonField = toolType + fun maxLikes(maxLikes: JsonField) = apply { body.maxLikes(maxLikes) } + + /** Reply pages collected for each target. */ + fun maxPagesPerTarget(maxPagesPerTarget: Long) = apply { + body.maxPagesPerTarget(maxPagesPerTarget) + } /** - * Returns the raw JSON value of [advancedQuery]. + * Sets [Builder.maxPagesPerTarget] to an arbitrary JSON value. * - * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.maxPagesPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("advancedQuery") - @ExcludeMissing - fun _advancedQuery(): JsonField = advancedQuery + fun maxPagesPerTarget(maxPagesPerTarget: JsonField) = apply { + body.maxPagesPerTarget(maxPagesPerTarget) + } + + /** Maximum post count for profile results. */ + fun maxPosts(maxPosts: Long) = apply { body.maxPosts(maxPosts) } /** - * Returns the raw JSON value of [anyWords]. + * Sets [Builder.maxPosts] to an arbitrary JSON value. * - * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.maxPosts] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("anyWords") @ExcludeMissing fun _anyWords(): JsonField = anyWords + fun maxPosts(maxPosts: JsonField) = apply { body.maxPosts(maxPosts) } + + /** Maximum Tweet quote count. */ + fun maxQuotes(maxQuotes: Long) = apply { body.maxQuotes(maxQuotes) } /** - * Returns the raw JSON value of [boundingBox]. + * Sets [Builder.maxQuotes] to an arbitrary JSON value. * - * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.maxQuotes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("boundingBox") - @ExcludeMissing - fun _boundingBox(): JsonField = boundingBox + fun maxQuotes(maxQuotes: JsonField) = apply { body.maxQuotes(maxQuotes) } + + /** Maximum Tweet reply count. */ + fun maxReplies(maxReplies: Long) = apply { body.maxReplies(maxReplies) } /** - * Returns the raw JSON value of [cashtags]. + * Sets [Builder.maxReplies] to an arbitrary JSON value. * - * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.maxReplies] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("cashtags") @ExcludeMissing fun _cashtags(): JsonField = cashtags + fun maxReplies(maxReplies: JsonField) = apply { body.maxReplies(maxReplies) } + + /** Maximum Tweet repost count. */ + fun maxRetweets(maxRetweets: Long) = apply { body.maxRetweets(maxRetweets) } /** - * Returns the raw JSON value of [conversationId]. + * Sets [Builder.maxRetweets] to an arbitrary JSON value. * - * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.maxRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("conversationId") - @ExcludeMissing - fun _conversationId(): JsonField = conversationId + fun maxRetweets(maxRetweets: JsonField) = apply { body.maxRetweets(maxRetweets) } + + /** Media type filter (tweet_search_extractor) */ + fun mediaType(mediaType: MediaType) = apply { body.mediaType(mediaType) } /** - * Returns the raw JSON value of [exactPhrase]. + * Sets [Builder.mediaType] to an arbitrary JSON value. * - * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.mediaType] with a well-typed [MediaType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("exactPhrase") - @ExcludeMissing - fun _exactPhrase(): JsonField = exactPhrase + fun mediaType(mediaType: JsonField) = apply { body.mediaType(mediaType) } + + /** Filter tweets mentioning a username (tweet_search_extractor) */ + fun mentioning(mentioning: String) = apply { body.mentioning(mentioning) } /** - * Returns the raw JSON value of [excludeWords]. + * Sets [Builder.mentioning] to an arbitrary JSON value. * - * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.mentioning] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("excludeWords") - @ExcludeMissing - fun _excludeWords(): JsonField = excludeWords + fun mentioning(mentioning: JsonField) = apply { body.mentioning(mentioning) } + + /** Minimum profile age in days. */ + fun minAccountAgeDays(minAccountAgeDays: Long) = apply { + body.minAccountAgeDays(minAccountAgeDays) + } /** - * Returns the raw JSON value of [fromUser]. + * Sets [Builder.minAccountAgeDays] to an arbitrary JSON value. * - * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minAccountAgeDays] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("fromUser") @ExcludeMissing fun _fromUser(): JsonField = fromUser + fun minAccountAgeDays(minAccountAgeDays: JsonField) = apply { + body.minAccountAgeDays(minAccountAgeDays) + } + + /** Minimum Tweet bookmark count. */ + fun minBookmarks(minBookmarks: Long) = apply { body.minBookmarks(minBookmarks) } /** - * Returns the raw JSON value of [hashtags]. + * Sets [Builder.minBookmarks] to an arbitrary JSON value. * - * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minBookmarks] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("hashtags") @ExcludeMissing fun _hashtags(): JsonField = hashtags + fun minBookmarks(minBookmarks: JsonField) = apply { body.minBookmarks(minBookmarks) } + + /** Minimum likes threshold (tweet_search_extractor) */ + fun minFaves(minFaves: Long) = apply { body.minFaves(minFaves) } /** - * Returns the raw JSON value of [inReplyToTweetId]. + * Sets [Builder.minFaves] to an arbitrary JSON value. * - * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("inReplyToTweetId") - @ExcludeMissing - fun _inReplyToTweetId(): JsonField = inReplyToTweetId + fun minFaves(minFaves: JsonField) = apply { body.minFaves(minFaves) } + + /** Minimum follower count for profile results. */ + fun minFollowers(minFollowers: Long) = apply { body.minFollowers(minFollowers) } /** - * Returns the raw JSON value of [language]. + * Sets [Builder.minFollowers] to an arbitrary JSON value. * - * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minFollowers] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + fun minFollowers(minFollowers: JsonField) = apply { body.minFollowers(minFollowers) } + + /** Minimum following count for profile results. */ + fun minFollowing(minFollowing: Long) = apply { body.minFollowing(minFollowing) } /** - * Returns the raw JSON value of [listId]. + * Sets [Builder.minFollowing] to an arbitrary JSON value. * - * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minFollowing] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("listId") @ExcludeMissing fun _listId(): JsonField = listId + fun minFollowing(minFollowing: JsonField) = apply { body.minFollowing(minFollowing) } + + /** Minimum post count for profile results. */ + fun minPosts(minPosts: Long) = apply { body.minPosts(minPosts) } /** - * Returns the raw JSON value of [mediaType]. + * Sets [Builder.minPosts] to an arbitrary JSON value. * - * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minPosts] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("mediaType") - @ExcludeMissing - fun _mediaType(): JsonField = mediaType + fun minPosts(minPosts: JsonField) = apply { body.minPosts(minPosts) } + + /** Minimum quote count threshold (tweet_search_extractor) */ + fun minQuotes(minQuotes: Long) = apply { body.minQuotes(minQuotes) } /** - * Returns the raw JSON value of [mentioning]. + * Sets [Builder.minQuotes] to an arbitrary JSON value. * - * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("mentioning") - @ExcludeMissing - fun _mentioning(): JsonField = mentioning + fun minQuotes(minQuotes: JsonField) = apply { body.minQuotes(minQuotes) } + + /** Minimum replies threshold (tweet_search_extractor) */ + fun minReplies(minReplies: Long) = apply { body.minReplies(minReplies) } /** - * Returns the raw JSON value of [minFaves]. + * Sets [Builder.minReplies] to an arbitrary JSON value. * - * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("minFaves") @ExcludeMissing fun _minFaves(): JsonField = minFaves + fun minReplies(minReplies: JsonField) = apply { body.minReplies(minReplies) } + + /** Minimum retweets threshold (tweet_search_extractor) */ + fun minRetweets(minRetweets: Long) = apply { body.minRetweets(minRetweets) } /** - * Returns the raw JSON value of [minQuotes]. + * Sets [Builder.minRetweets] to an arbitrary JSON value. * - * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("minQuotes") @ExcludeMissing fun _minQuotes(): JsonField = minQuotes + fun minRetweets(minRetweets: JsonField) = apply { body.minRetweets(minRetweets) } + + /** Minimum Tweet view count. */ + fun minViews(minViews: Long) = apply { body.minViews(minViews) } /** - * Returns the raw JSON value of [minReplies]. + * Sets [Builder.minViews] to an arbitrary JSON value. * - * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.minViews] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("minReplies") @ExcludeMissing fun _minReplies(): JsonField = minReplies + fun minViews(minViews: JsonField) = apply { body.minViews(minViews) } + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean) = apply { body.nativeRetweets(nativeRetweets) } /** - * Returns the raw JSON value of [minRetweets]. + * Sets [Builder.nativeRetweets] to an arbitrary JSON value. * - * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.nativeRetweets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("minRetweets") - @ExcludeMissing - fun _minRetweets(): JsonField = minRetweets + fun nativeRetweets(nativeRetweets: JsonField) = apply { + body.nativeRetweets(nativeRetweets) + } + + /** Match a place name. */ + fun near(near: String) = apply { body.near(near) } /** - * Returns the raw JSON value of [place]. + * Sets [Builder.near] to an arbitrary JSON value. * - * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.near] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("place") @ExcludeMissing fun _place(): JsonField = place + fun near(near: JsonField) = apply { body.near(near) } + + /** Only return news results. */ + fun news(news: Boolean) = apply { body.news(news) } /** - * Returns the raw JSON value of [placeCountry]. + * Sets [Builder.news] to an arbitrary JSON value. * - * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.news] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("placeCountry") - @ExcludeMissing - fun _placeCountry(): JsonField = placeCountry + fun news(news: JsonField) = apply { body.news(news) } + + /** Shortcut for dedupeMode=merge. */ + fun overlapMode(overlapMode: Boolean) = apply { body.overlapMode(overlapMode) } /** - * Returns the raw JSON value of [pointRadius]. + * Sets [Builder.overlapMode] to an arbitrary JSON value. * - * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.overlapMode] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("pointRadius") - @ExcludeMissing - fun _pointRadius(): JsonField = pointRadius + fun overlapMode(overlapMode: JsonField) = apply { body.overlapMode(overlapMode) } + + /** Search within a place ID (tweet_search_extractor) */ + fun place(place: String) = apply { body.place(place) } /** - * Returns the raw JSON value of [quotes]. + * Sets [Builder.place] to an arbitrary JSON value. * - * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.place] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("quotes") @ExcludeMissing fun _quotes(): JsonField = quotes + fun place(place: JsonField) = apply { body.place(place) } + + /** Search within a country code (tweet_search_extractor) */ + fun placeCountry(placeCountry: String) = apply { body.placeCountry(placeCountry) } /** - * Returns the raw JSON value of [quotesOfTweetId]. + * Sets [Builder.placeCountry] to an arbitrary JSON value. * - * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.placeCountry] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("quotesOfTweetId") - @ExcludeMissing - fun _quotesOfTweetId(): JsonField = quotesOfTweetId + fun placeCountry(placeCountry: JsonField) = apply { + body.placeCountry(placeCountry) + } + + /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ + fun pointRadius(pointRadius: String) = apply { body.pointRadius(pointRadius) } /** - * Returns the raw JSON value of [replies]. + * Sets [Builder.pointRadius] to an arbitrary JSON value. * - * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.pointRadius] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("replies") @ExcludeMissing fun _replies(): JsonField = replies + fun pointRadius(pointRadius: JsonField) = apply { body.pointRadius(pointRadius) } + + /** Search ranking applied to every query. */ + fun queryType(queryType: QueryType) = apply { body.queryType(queryType) } /** - * Returns the raw JSON value of [resultsLimit]. + * Sets [Builder.queryType] to an arbitrary JSON value. * - * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.queryType] with a well-typed [QueryType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("resultsLimit") - @ExcludeMissing - fun _resultsLimit(): JsonField = resultsLimit + fun queryType(queryType: JsonField) = apply { body.queryType(queryType) } + + /** Quote mode (tweet_search_extractor) */ + fun quotes(quotes: Quotes) = apply { body.quotes(quotes) } /** - * Returns the raw JSON value of [retweets]. + * Sets [Builder.quotes] to an arbitrary JSON value. * - * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("retweets") @ExcludeMissing fun _retweets(): JsonField = retweets + fun quotes(quotes: JsonField) = apply { body.quotes(quotes) } + + /** Only quotes of this tweet ID (tweet_search_extractor) */ + fun quotesOfTweetId(quotesOfTweetId: String) = apply { + body.quotesOfTweetId(quotesOfTweetId) + } /** - * Returns the raw JSON value of [retweetsOfTweetId]. + * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. * - * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("retweetsOfTweetId") - @ExcludeMissing - fun _retweetsOfTweetId(): JsonField = retweetsOfTweetId + fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { + body.quotesOfTweetId(quotesOfTweetId) + } + + /** Profile relations processed within one job. */ + fun relationTargets(relationTargets: List) = apply { + body.relationTargets(relationTargets) + } /** - * Returns the raw JSON value of [searchQuery]. + * Sets [Builder.relationTargets] to an arbitrary JSON value. * - * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.relationTargets] with a well-typed + * `List` value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. */ - @JsonProperty("searchQuery") - @ExcludeMissing - fun _searchQuery(): JsonField = searchQuery + fun relationTargets(relationTargets: JsonField>) = apply { + body.relationTargets(relationTargets) + } /** - * Returns the raw JSON value of [sinceDate]. + * Adds a single [RelationTarget] to [relationTargets]. * - * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + * @throws IllegalStateException if the field was previously set to a non-list. */ - @JsonProperty("sinceDate") - @ExcludeMissing - fun _sinceDate(): JsonField = sinceDate + fun addRelationTarget(relationTarget: RelationTarget) = apply { + body.addRelationTarget(relationTarget) + } + + /** Reply mode (tweet_search_extractor) */ + fun replies(replies: Replies) = apply { body.replies(replies) } /** - * Returns the raw JSON value of [targetCommunityId]. + * Sets [Builder.replies] to an arbitrary JSON value. * - * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.replies] with a well-typed [Replies] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("targetCommunityId") - @ExcludeMissing - fun _targetCommunityId(): JsonField = targetCommunityId + fun replies(replies: JsonField) = apply { body.replies(replies) } /** - * Returns the raw JSON value of [targetListId]. - * - * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected - * type. + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. */ - @JsonProperty("targetListId") - @ExcludeMissing - fun _targetListId(): JsonField = targetListId + fun resultsLimit(resultsLimit: Long) = apply { body.resultsLimit(resultsLimit) } /** - * Returns the raw JSON value of [targetSpaceId]. + * Sets [Builder.resultsLimit] to an arbitrary JSON value. * - * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.resultsLimit] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("targetSpaceId") - @ExcludeMissing - fun _targetSpaceId(): JsonField = targetSpaceId + fun resultsLimit(resultsLimit: JsonField) = apply { body.resultsLimit(resultsLimit) } + + /** Retweet mode (tweet_search_extractor) */ + fun retweets(retweets: Retweets) = apply { body.retweets(retweets) } /** - * Returns the raw JSON value of [targetTweetId]. + * Sets [Builder.retweets] to an arbitrary JSON value. * - * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.retweets] with a well-typed [Retweets] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - @JsonProperty("targetTweetId") - @ExcludeMissing - fun _targetTweetId(): JsonField = targetTweetId + fun retweets(retweets: JsonField) = apply { body.retweets(retweets) } + + /** Only retweets of this tweet ID (tweet_search_extractor) */ + fun retweetsOfTweetId(retweetsOfTweetId: String) = apply { + body.retweetsOfTweetId(retweetsOfTweetId) + } /** - * Returns the raw JSON value of [targetUsername]. + * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. * - * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected - * type. + * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("targetUsername") - @ExcludeMissing - fun _targetUsername(): JsonField = targetUsername + fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { + body.retweetsOfTweetId(retweetsOfTweetId) + } + + /** Enable the safe-search filter. */ + fun safe(safe: Boolean) = apply { body.safe(safe) } /** - * Returns the raw JSON value of [toUser]. + * Sets [Builder.safe] to an arbitrary JSON value. * - * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.safe] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("toUser") @ExcludeMissing fun _toUser(): JsonField = toUser + fun safe(safe: JsonField) = apply { body.safe(safe) } + + /** Reply depth scope. */ + fun scope(scope: Scope) = apply { body.scope(scope) } /** - * Returns the raw JSON value of [untilDate]. + * Sets [Builder.scope] to an arbitrary JSON value. * - * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - @JsonProperty("untilDate") - @ExcludeMissing - fun _untilDate(): JsonField = untilDate + fun scope(scope: JsonField) = apply { body.scope(scope) } + + /** Search queries processed as one collection job. */ + fun searchQueries(searchQueries: List) = apply { body.searchQueries(searchQueries) } /** - * Returns the raw JSON value of [url]. + * Sets [Builder.searchQueries] to an arbitrary JSON value. * - * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + * You should usually call [Builder.searchQueries] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + fun searchQueries(searchQueries: JsonField>) = apply { + body.searchQueries(searchQueries) + } /** - * Returns the raw JSON value of [verifiedOnly]. + * Adds a single [String] to [searchQueries]. * - * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected - * type. + * @throws IllegalStateException if the field was previously set to a non-list. */ - @JsonProperty("verifiedOnly") - @ExcludeMissing - fun _verifiedOnly(): JsonField = verifiedOnly - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + fun addSearchQuery(searchQuery: String) = apply { body.addSearchQuery(searchQuery) } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + /** Required for tweet_search_extractor & community_search. */ + fun searchQuery(searchQuery: String) = apply { body.searchQuery(searchQuery) } - fun toBuilder() = Builder().from(this) + /** + * Sets [Builder.searchQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQuery] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun searchQuery(searchQuery: JsonField) = apply { body.searchQuery(searchQuery) } - companion object { + /** Start date YYYY-MM-DD (tweet_search_extractor) */ + fun sinceDate(sinceDate: LocalDate) = apply { body.sinceDate(sinceDate) } - /** - * Returns a mutable builder for constructing an instance of [Body]. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + /** + * Sets [Builder.sinceDate] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sinceDate(sinceDate: JsonField) = apply { body.sinceDate(sinceDate) } - /** A builder for [Body]. */ - class Builder internal constructor() { + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String) = apply { body.sinceId(sinceId) } - private var toolType: JsonField? = null - private var advancedQuery: JsonField = JsonMissing.of() - private var anyWords: JsonField = JsonMissing.of() - private var boundingBox: JsonField = JsonMissing.of() - private var cashtags: JsonField = JsonMissing.of() - private var conversationId: JsonField = JsonMissing.of() - private var exactPhrase: JsonField = JsonMissing.of() - private var excludeWords: JsonField = JsonMissing.of() - private var fromUser: JsonField = JsonMissing.of() - private var hashtags: JsonField = JsonMissing.of() - private var inReplyToTweetId: JsonField = JsonMissing.of() - private var language: JsonField = JsonMissing.of() - private var listId: JsonField = JsonMissing.of() - private var mediaType: JsonField = JsonMissing.of() - private var mentioning: JsonField = JsonMissing.of() - private var minFaves: JsonField = JsonMissing.of() - private var minQuotes: JsonField = JsonMissing.of() - private var minReplies: JsonField = JsonMissing.of() - private var minRetweets: JsonField = JsonMissing.of() - private var place: JsonField = JsonMissing.of() - private var placeCountry: JsonField = JsonMissing.of() - private var pointRadius: JsonField = JsonMissing.of() - private var quotes: JsonField = JsonMissing.of() - private var quotesOfTweetId: JsonField = JsonMissing.of() - private var replies: JsonField = JsonMissing.of() - private var resultsLimit: JsonField = JsonMissing.of() - private var retweets: JsonField = JsonMissing.of() - private var retweetsOfTweetId: JsonField = JsonMissing.of() - private var searchQuery: JsonField = JsonMissing.of() - private var sinceDate: JsonField = JsonMissing.of() - private var targetCommunityId: JsonField = JsonMissing.of() - private var targetListId: JsonField = JsonMissing.of() - private var targetSpaceId: JsonField = JsonMissing.of() - private var targetTweetId: JsonField = JsonMissing.of() - private var targetUsername: JsonField = JsonMissing.of() - private var toUser: JsonField = JsonMissing.of() - private var untilDate: JsonField = JsonMissing.of() - private var url: JsonField = JsonMissing.of() - private var verifiedOnly: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /** + * Sets [Builder.sinceId] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun sinceId(sinceId: JsonField) = apply { body.sinceId(sinceId) } - @JvmSynthetic - internal fun from(body: Body) = apply { - toolType = body.toolType - advancedQuery = body.advancedQuery - anyWords = body.anyWords - boundingBox = body.boundingBox - cashtags = body.cashtags - conversationId = body.conversationId - exactPhrase = body.exactPhrase - excludeWords = body.excludeWords - fromUser = body.fromUser - hashtags = body.hashtags - inReplyToTweetId = body.inReplyToTweetId - language = body.language - listId = body.listId - mediaType = body.mediaType - mentioning = body.mentioning - minFaves = body.minFaves - minQuotes = body.minQuotes - minReplies = body.minReplies - minRetweets = body.minRetweets - place = body.place - placeCountry = body.placeCountry - pointRadius = body.pointRadius - quotes = body.quotes - quotesOfTweetId = body.quotesOfTweetId - replies = body.replies - resultsLimit = body.resultsLimit - retweets = body.retweets - retweetsOfTweetId = body.retweetsOfTweetId - searchQuery = body.searchQuery - sinceDate = body.sinceDate - targetCommunityId = body.targetCommunityId - targetListId = body.targetListId - targetSpaceId = body.targetSpaceId - targetTweetId = body.targetTweetId - targetUsername = body.targetUsername - toUser = body.toUser - untilDate = body.untilDate - url = body.url - verifiedOnly = body.verifiedOnly - additionalProperties = body.additionalProperties.toMutableMap() - } + /** Reply start time as ISO 8601 or Unix seconds. */ + fun sinceTime(sinceTime: SinceTime) = apply { body.sinceTime(sinceTime) } - /** Identifier for the extraction tool used to run a job. */ - fun toolType(toolType: ToolType) = toolType(JsonField.of(toolType)) + /** + * Sets [Builder.sinceTime] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceTime] with a well-typed [SinceTime] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sinceTime(sinceTime: JsonField) = apply { body.sinceTime(sinceTime) } - /** - * Sets [Builder.toolType] to an arbitrary JSON value. - * - * You should usually call [Builder.toolType] with a well-typed [ToolType] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun toolType(toolType: JsonField) = apply { this.toolType = toolType } + /** Alias for calling [sinceTime] with `SinceTime.ofOffsetDate(offsetDate)`. */ + fun sinceTime(offsetDate: OffsetDateTime) = apply { body.sinceTime(offsetDate) } - /** Raw advanced search query appended as-is (tweet_search_extractor) */ - fun advancedQuery(advancedQuery: String) = advancedQuery(JsonField.of(advancedQuery)) + /** Alias for calling [sinceTime] with `SinceTime.ofInteger(integer)`. */ + fun sinceTime(integer: Long) = apply { body.sinceTime(integer) } - /** - * Sets [Builder.advancedQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.advancedQuery] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun advancedQuery(advancedQuery: JsonField) = apply { - this.advancedQuery = advancedQuery - } + /** Reply result order. */ + fun sort(sort: Sort) = apply { body.sort(sort) } - /** - * Words or quoted phrases where any one can match. Separate with spaces, commas, or - * lines. (tweet_search_extractor) - */ - fun anyWords(anyWords: String) = anyWords(JsonField.of(anyWords)) + /** + * Sets [Builder.sort] to an arbitrary JSON value. + * + * You should usually call [Builder.sort] with a well-typed [Sort] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun sort(sort: JsonField) = apply { body.sort(sort) } - /** - * Sets [Builder.anyWords] to an arbitrary JSON value. - * - * You should usually call [Builder.anyWords] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun anyWords(anyWords: JsonField) = apply { this.anyWords = anyWords } + /** Match the source application. */ + fun source(source: String) = apply { body.source(source) } - /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ - fun boundingBox(boundingBox: String) = boundingBox(JsonField.of(boundingBox)) + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { body.source(source) } - /** - * Sets [Builder.boundingBox] to an arbitrary JSON value. - * - * You should usually call [Builder.boundingBox] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun boundingBox(boundingBox: JsonField) = apply { - this.boundingBox = boundingBox - } + /** Resume one reply target from this cursor. */ + fun startCursor(startCursor: String) = apply { body.startCursor(startCursor) } - /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ - fun cashtags(cashtags: String) = cashtags(JsonField.of(cashtags)) + /** + * Sets [Builder.startCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.startCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun startCursor(startCursor: JsonField) = apply { body.startCursor(startCursor) } - /** - * Sets [Builder.cashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.cashtags] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun cashtags(cashtags: JsonField) = apply { this.cashtags = cashtags } + /** Required for community_post_extractor & community_search. */ + fun targetCommunityId(targetCommunityId: String) = apply { + body.targetCommunityId(targetCommunityId) + } - /** Conversation ID filter (tweet_search_extractor) */ - fun conversationId(conversationId: String) = + /** + * Sets [Builder.targetCommunityId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetCommunityId(targetCommunityId: JsonField) = apply { + body.targetCommunityId(targetCommunityId) + } + + /** Community IDs processed as one collection job. */ + fun targetCommunityIds(targetCommunityIds: List) = apply { + body.targetCommunityIds(targetCommunityIds) + } + + /** + * Sets [Builder.targetCommunityIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun targetCommunityIds(targetCommunityIds: JsonField>) = apply { + body.targetCommunityIds(targetCommunityIds) + } + + /** + * Adds a single [String] to [targetCommunityIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetCommunityId(targetCommunityId: String) = apply { + body.addTargetCommunityId(targetCommunityId) + } + + /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ + fun targetListId(targetListId: String) = apply { body.targetListId(targetListId) } + + /** + * Sets [Builder.targetListId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetListId(targetListId: JsonField) = apply { + body.targetListId(targetListId) + } + + /** List IDs processed as one collection job. */ + fun targetListIds(targetListIds: List) = apply { body.targetListIds(targetListIds) } + + /** + * Sets [Builder.targetListIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetListIds(targetListIds: JsonField>) = apply { + body.targetListIds(targetListIds) + } + + /** + * Adds a single [String] to [targetListIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetListId(targetListId: String) = apply { body.addTargetListId(targetListId) } + + /** Mixed targets auto-routed within one job. */ + fun targets(targets: List) = apply { body.targets(targets) } + + /** + * Sets [Builder.targets] to an arbitrary JSON value. + * + * You should usually call [Builder.targets] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targets(targets: JsonField>) = apply { body.targets(targets) } + + /** + * Adds a single [Target] to [targets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTarget(target: Target) = apply { body.addTarget(target) } + + /** Alias for calling [addTarget] with `Target.ofString(string)`. */ + fun addTarget(string: String) = apply { body.addTarget(string) } + + /** Alias for calling [addTarget] with `Target.ofUnionMember1(unionMember1)`. */ + fun addTarget(unionMember1: Target.UnionMember1) = apply { body.addTarget(unionMember1) } + + /** Required for space_explorer. */ + fun targetSpaceId(targetSpaceId: String) = apply { body.targetSpaceId(targetSpaceId) } + + /** + * Sets [Builder.targetSpaceId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetSpaceId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetSpaceId(targetSpaceId: JsonField) = apply { + body.targetSpaceId(targetSpaceId) + } + + fun targetTweetId(targetTweetId: String) = apply { body.targetTweetId(targetTweetId) } + + /** + * Sets [Builder.targetTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun targetTweetId(targetTweetId: JsonField) = apply { + body.targetTweetId(targetTweetId) + } + + /** Tweet IDs processed as one collection job. */ + fun targetTweetIds(targetTweetIds: List) = apply { + body.targetTweetIds(targetTweetIds) + } + + /** + * Sets [Builder.targetTweetIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetTweetIds(targetTweetIds: JsonField>) = apply { + body.targetTweetIds(targetTweetIds) + } + + /** + * Adds a single [String] to [targetTweetIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetTweetId(targetTweetId: String) = apply { body.addTargetTweetId(targetTweetId) } + + fun targetUsername(targetUsername: String) = apply { body.targetUsername(targetUsername) } + + /** + * Sets [Builder.targetUsername] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsername] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsername(targetUsername: JsonField) = apply { + body.targetUsername(targetUsername) + } + + /** Usernames processed as one collection job. */ + fun targetUsernames(targetUsernames: List) = apply { + body.targetUsernames(targetUsernames) + } + + /** + * Sets [Builder.targetUsernames] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsernames] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsernames(targetUsernames: JsonField>) = apply { + body.targetUsernames(targetUsernames) + } + + /** + * Adds a single [String] to [targetUsernames]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetUsername(targetUsername: String) = apply { + body.addTargetUsername(targetUsername) + } + + /** Filter replies sent to a username (tweet_search_extractor) */ + fun toUser(toUser: String) = apply { body.toUser(toUser) } + + /** + * Sets [Builder.toUser] to an arbitrary JSON value. + * + * You should usually call [Builder.toUser] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun toUser(toUser: JsonField) = apply { body.toUser(toUser) } + + /** End date YYYY-MM-DD (tweet_search_extractor) */ + fun untilDate(untilDate: LocalDate) = apply { body.untilDate(untilDate) } + + /** + * Sets [Builder.untilDate] to an arbitrary JSON value. + * + * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun untilDate(untilDate: JsonField) = apply { body.untilDate(untilDate) } + + /** Reply end time as ISO 8601 or Unix seconds. */ + fun untilTime(untilTime: UntilTime) = apply { body.untilTime(untilTime) } + + /** + * Sets [Builder.untilTime] to an arbitrary JSON value. + * + * You should usually call [Builder.untilTime] with a well-typed [UntilTime] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun untilTime(untilTime: JsonField) = apply { body.untilTime(untilTime) } + + /** Alias for calling [untilTime] with `UntilTime.ofOffsetDate(offsetDate)`. */ + fun untilTime(offsetDate: OffsetDateTime) = apply { body.untilTime(offsetDate) } + + /** Alias for calling [untilTime] with `UntilTime.ofInteger(integer)`. */ + fun untilTime(integer: Long) = apply { body.untilTime(integer) } + + /** URL substring or domain filter (tweet_search_extractor) */ + fun url(url: String) = apply { body.url(url) } + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun url(url: JsonField) = apply { body.url(url) } + + /** Required username text. */ + fun usernameContains(usernameContains: String) = apply { + body.usernameContains(usernameContains) + } + + /** + * Sets [Builder.usernameContains] to an arbitrary JSON value. + * + * You should usually call [Builder.usernameContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun usernameContains(usernameContains: JsonField) = apply { + body.usernameContains(usernameContains) + } + + /** Only verified authors (tweet_search_extractor) */ + fun verifiedOnly(verifiedOnly: Boolean) = apply { body.verifiedOnly(verifiedOnly) } + + /** + * Sets [Builder.verifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun verifiedOnly(verifiedOnly: JsonField) = apply { + body.verifiedOnly(verifiedOnly) + } + + /** Exact profile verification type. */ + fun verifiedType(verifiedType: String) = apply { body.verifiedType(verifiedType) } + + /** + * Sets [Builder.verifiedType] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedType] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun verifiedType(verifiedType: JsonField) = apply { + body.verifiedType(verifiedType) + } + + /** Set the radius for the near filter. */ + fun within(within: String) = apply { body.within(within) } + + /** + * Sets [Builder.within] to an arbitrary JSON value. + * + * You should usually call [Builder.within] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun within(within: JsonField) = apply { body.within(within) } + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String) = apply { body.withinTime(withinTime) } + + /** + * Sets [Builder.withinTime] to an arbitrary JSON value. + * + * You should usually call [Builder.withinTime] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun withinTime(withinTime: JsonField) = apply { body.withinTime(withinTime) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExtractionRunParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExtractionRunParams = + ExtractionRunParams( + dryRun, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + dryRun?.let { put("dry_run", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + /** One synchronous estimate or asynchronous extraction request. */ + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val toolType: JsonField, + private val advancedQuery: JsonField, + private val anyWords: JsonField, + private val bioContains: JsonField, + private val blueVerifiedOnly: JsonField, + private val boundingBox: JsonField, + private val cardName: JsonField, + private val cashtags: JsonField, + private val collectionStrategy: JsonField, + private val conversationId: JsonField, + private val dedupeAcrossTargets: JsonField, + private val dedupeMode: JsonField, + private val exactPhrase: JsonField, + private val excludeOriginalAuthor: JsonField, + private val excludeSource: JsonField, + private val excludeWords: JsonField, + private val fromUser: JsonField, + private val geocode: JsonField, + private val hashtags: JsonField, + private val hasLocation: JsonField, + private val hasMediaOnly: JsonField, + private val hasWebsite: JsonField, + private val includeOriginalPost: JsonField, + private val includeSearchTerms: JsonField, + private val includeTargetMetadata: JsonField, + private val inReplyToTweetId: JsonField, + private val language: JsonField, + private val listId: JsonField, + private val locationContains: JsonField, + private val maxDepth: JsonField, + private val maxFollowers: JsonField, + private val maxFollowing: JsonField, + private val maxId: JsonField, + private val maxItemsPerTarget: JsonField, + private val maxLikes: JsonField, + private val maxPagesPerTarget: JsonField, + private val maxPosts: JsonField, + private val maxQuotes: JsonField, + private val maxReplies: JsonField, + private val maxRetweets: JsonField, + private val mediaType: JsonField, + private val mentioning: JsonField, + private val minAccountAgeDays: JsonField, + private val minBookmarks: JsonField, + private val minFaves: JsonField, + private val minFollowers: JsonField, + private val minFollowing: JsonField, + private val minPosts: JsonField, + private val minQuotes: JsonField, + private val minReplies: JsonField, + private val minRetweets: JsonField, + private val minViews: JsonField, + private val nativeRetweets: JsonField, + private val near: JsonField, + private val news: JsonField, + private val overlapMode: JsonField, + private val place: JsonField, + private val placeCountry: JsonField, + private val pointRadius: JsonField, + private val queryType: JsonField, + private val quotes: JsonField, + private val quotesOfTweetId: JsonField, + private val relationTargets: JsonField>, + private val replies: JsonField, + private val resultsLimit: JsonField, + private val retweets: JsonField, + private val retweetsOfTweetId: JsonField, + private val safe: JsonField, + private val scope: JsonField, + private val searchQueries: JsonField>, + private val searchQuery: JsonField, + private val sinceDate: JsonField, + private val sinceId: JsonField, + private val sinceTime: JsonField, + private val sort: JsonField, + private val source: JsonField, + private val startCursor: JsonField, + private val targetCommunityId: JsonField, + private val targetCommunityIds: JsonField>, + private val targetListId: JsonField, + private val targetListIds: JsonField>, + private val targets: JsonField>, + private val targetSpaceId: JsonField, + private val targetTweetId: JsonField, + private val targetTweetIds: JsonField>, + private val targetUsername: JsonField, + private val targetUsernames: JsonField>, + private val toUser: JsonField, + private val untilDate: JsonField, + private val untilTime: JsonField, + private val url: JsonField, + private val usernameContains: JsonField, + private val verifiedOnly: JsonField, + private val verifiedType: JsonField, + private val within: JsonField, + private val withinTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("toolType") + @ExcludeMissing + toolType: JsonField = JsonMissing.of(), + @JsonProperty("advancedQuery") + @ExcludeMissing + advancedQuery: JsonField = JsonMissing.of(), + @JsonProperty("anyWords") + @ExcludeMissing + anyWords: JsonField = JsonMissing.of(), + @JsonProperty("bioContains") + @ExcludeMissing + bioContains: JsonField = JsonMissing.of(), + @JsonProperty("blueVerifiedOnly") + @ExcludeMissing + blueVerifiedOnly: JsonField = JsonMissing.of(), + @JsonProperty("boundingBox") + @ExcludeMissing + boundingBox: JsonField = JsonMissing.of(), + @JsonProperty("cardName") + @ExcludeMissing + cardName: JsonField = JsonMissing.of(), + @JsonProperty("cashtags") + @ExcludeMissing + cashtags: JsonField = JsonMissing.of(), + @JsonProperty("collectionStrategy") + @ExcludeMissing + collectionStrategy: JsonField = JsonMissing.of(), + @JsonProperty("conversationId") + @ExcludeMissing + conversationId: JsonField = JsonMissing.of(), + @JsonProperty("dedupeAcrossTargets") + @ExcludeMissing + dedupeAcrossTargets: JsonField = JsonMissing.of(), + @JsonProperty("dedupeMode") + @ExcludeMissing + dedupeMode: JsonField = JsonMissing.of(), + @JsonProperty("exactPhrase") + @ExcludeMissing + exactPhrase: JsonField = JsonMissing.of(), + @JsonProperty("excludeOriginalAuthor") + @ExcludeMissing + excludeOriginalAuthor: JsonField = JsonMissing.of(), + @JsonProperty("excludeSource") + @ExcludeMissing + excludeSource: JsonField = JsonMissing.of(), + @JsonProperty("excludeWords") + @ExcludeMissing + excludeWords: JsonField = JsonMissing.of(), + @JsonProperty("fromUser") + @ExcludeMissing + fromUser: JsonField = JsonMissing.of(), + @JsonProperty("geocode") @ExcludeMissing geocode: JsonField = JsonMissing.of(), + @JsonProperty("hashtags") + @ExcludeMissing + hashtags: JsonField = JsonMissing.of(), + @JsonProperty("hasLocation") + @ExcludeMissing + hasLocation: JsonField = JsonMissing.of(), + @JsonProperty("hasMediaOnly") + @ExcludeMissing + hasMediaOnly: JsonField = JsonMissing.of(), + @JsonProperty("hasWebsite") + @ExcludeMissing + hasWebsite: JsonField = JsonMissing.of(), + @JsonProperty("includeOriginalPost") + @ExcludeMissing + includeOriginalPost: JsonField = JsonMissing.of(), + @JsonProperty("includeSearchTerms") + @ExcludeMissing + includeSearchTerms: JsonField = JsonMissing.of(), + @JsonProperty("includeTargetMetadata") + @ExcludeMissing + includeTargetMetadata: JsonField = JsonMissing.of(), + @JsonProperty("inReplyToTweetId") + @ExcludeMissing + inReplyToTweetId: JsonField = JsonMissing.of(), + @JsonProperty("language") + @ExcludeMissing + language: JsonField = JsonMissing.of(), + @JsonProperty("listId") @ExcludeMissing listId: JsonField = JsonMissing.of(), + @JsonProperty("locationContains") + @ExcludeMissing + locationContains: JsonField = JsonMissing.of(), + @JsonProperty("maxDepth") @ExcludeMissing maxDepth: JsonField = JsonMissing.of(), + @JsonProperty("maxFollowers") + @ExcludeMissing + maxFollowers: JsonField = JsonMissing.of(), + @JsonProperty("maxFollowing") + @ExcludeMissing + maxFollowing: JsonField = JsonMissing.of(), + @JsonProperty("maxId") @ExcludeMissing maxId: JsonField = JsonMissing.of(), + @JsonProperty("maxItemsPerTarget") + @ExcludeMissing + maxItemsPerTarget: JsonField = JsonMissing.of(), + @JsonProperty("maxLikes") @ExcludeMissing maxLikes: JsonField = JsonMissing.of(), + @JsonProperty("maxPagesPerTarget") + @ExcludeMissing + maxPagesPerTarget: JsonField = JsonMissing.of(), + @JsonProperty("maxPosts") @ExcludeMissing maxPosts: JsonField = JsonMissing.of(), + @JsonProperty("maxQuotes") + @ExcludeMissing + maxQuotes: JsonField = JsonMissing.of(), + @JsonProperty("maxReplies") + @ExcludeMissing + maxReplies: JsonField = JsonMissing.of(), + @JsonProperty("maxRetweets") + @ExcludeMissing + maxRetweets: JsonField = JsonMissing.of(), + @JsonProperty("mediaType") + @ExcludeMissing + mediaType: JsonField = JsonMissing.of(), + @JsonProperty("mentioning") + @ExcludeMissing + mentioning: JsonField = JsonMissing.of(), + @JsonProperty("minAccountAgeDays") + @ExcludeMissing + minAccountAgeDays: JsonField = JsonMissing.of(), + @JsonProperty("minBookmarks") + @ExcludeMissing + minBookmarks: JsonField = JsonMissing.of(), + @JsonProperty("minFaves") @ExcludeMissing minFaves: JsonField = JsonMissing.of(), + @JsonProperty("minFollowers") + @ExcludeMissing + minFollowers: JsonField = JsonMissing.of(), + @JsonProperty("minFollowing") + @ExcludeMissing + minFollowing: JsonField = JsonMissing.of(), + @JsonProperty("minPosts") @ExcludeMissing minPosts: JsonField = JsonMissing.of(), + @JsonProperty("minQuotes") + @ExcludeMissing + minQuotes: JsonField = JsonMissing.of(), + @JsonProperty("minReplies") + @ExcludeMissing + minReplies: JsonField = JsonMissing.of(), + @JsonProperty("minRetweets") + @ExcludeMissing + minRetweets: JsonField = JsonMissing.of(), + @JsonProperty("minViews") @ExcludeMissing minViews: JsonField = JsonMissing.of(), + @JsonProperty("nativeRetweets") + @ExcludeMissing + nativeRetweets: JsonField = JsonMissing.of(), + @JsonProperty("near") @ExcludeMissing near: JsonField = JsonMissing.of(), + @JsonProperty("news") @ExcludeMissing news: JsonField = JsonMissing.of(), + @JsonProperty("overlapMode") + @ExcludeMissing + overlapMode: JsonField = JsonMissing.of(), + @JsonProperty("place") @ExcludeMissing place: JsonField = JsonMissing.of(), + @JsonProperty("placeCountry") + @ExcludeMissing + placeCountry: JsonField = JsonMissing.of(), + @JsonProperty("pointRadius") + @ExcludeMissing + pointRadius: JsonField = JsonMissing.of(), + @JsonProperty("queryType") + @ExcludeMissing + queryType: JsonField = JsonMissing.of(), + @JsonProperty("quotes") @ExcludeMissing quotes: JsonField = JsonMissing.of(), + @JsonProperty("quotesOfTweetId") + @ExcludeMissing + quotesOfTweetId: JsonField = JsonMissing.of(), + @JsonProperty("relationTargets") + @ExcludeMissing + relationTargets: JsonField> = JsonMissing.of(), + @JsonProperty("replies") @ExcludeMissing replies: JsonField = JsonMissing.of(), + @JsonProperty("resultsLimit") + @ExcludeMissing + resultsLimit: JsonField = JsonMissing.of(), + @JsonProperty("retweets") + @ExcludeMissing + retweets: JsonField = JsonMissing.of(), + @JsonProperty("retweetsOfTweetId") + @ExcludeMissing + retweetsOfTweetId: JsonField = JsonMissing.of(), + @JsonProperty("safe") @ExcludeMissing safe: JsonField = JsonMissing.of(), + @JsonProperty("scope") @ExcludeMissing scope: JsonField = JsonMissing.of(), + @JsonProperty("searchQueries") + @ExcludeMissing + searchQueries: JsonField> = JsonMissing.of(), + @JsonProperty("searchQuery") + @ExcludeMissing + searchQuery: JsonField = JsonMissing.of(), + @JsonProperty("sinceDate") + @ExcludeMissing + sinceDate: JsonField = JsonMissing.of(), + @JsonProperty("sinceId") @ExcludeMissing sinceId: JsonField = JsonMissing.of(), + @JsonProperty("sinceTime") + @ExcludeMissing + sinceTime: JsonField = JsonMissing.of(), + @JsonProperty("sort") @ExcludeMissing sort: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("startCursor") + @ExcludeMissing + startCursor: JsonField = JsonMissing.of(), + @JsonProperty("targetCommunityId") + @ExcludeMissing + targetCommunityId: JsonField = JsonMissing.of(), + @JsonProperty("targetCommunityIds") + @ExcludeMissing + targetCommunityIds: JsonField> = JsonMissing.of(), + @JsonProperty("targetListId") + @ExcludeMissing + targetListId: JsonField = JsonMissing.of(), + @JsonProperty("targetListIds") + @ExcludeMissing + targetListIds: JsonField> = JsonMissing.of(), + @JsonProperty("targets") + @ExcludeMissing + targets: JsonField> = JsonMissing.of(), + @JsonProperty("targetSpaceId") + @ExcludeMissing + targetSpaceId: JsonField = JsonMissing.of(), + @JsonProperty("targetTweetId") + @ExcludeMissing + targetTweetId: JsonField = JsonMissing.of(), + @JsonProperty("targetTweetIds") + @ExcludeMissing + targetTweetIds: JsonField> = JsonMissing.of(), + @JsonProperty("targetUsername") + @ExcludeMissing + targetUsername: JsonField = JsonMissing.of(), + @JsonProperty("targetUsernames") + @ExcludeMissing + targetUsernames: JsonField> = JsonMissing.of(), + @JsonProperty("toUser") @ExcludeMissing toUser: JsonField = JsonMissing.of(), + @JsonProperty("untilDate") + @ExcludeMissing + untilDate: JsonField = JsonMissing.of(), + @JsonProperty("untilTime") + @ExcludeMissing + untilTime: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + @JsonProperty("usernameContains") + @ExcludeMissing + usernameContains: JsonField = JsonMissing.of(), + @JsonProperty("verifiedOnly") + @ExcludeMissing + verifiedOnly: JsonField = JsonMissing.of(), + @JsonProperty("verifiedType") + @ExcludeMissing + verifiedType: JsonField = JsonMissing.of(), + @JsonProperty("within") @ExcludeMissing within: JsonField = JsonMissing.of(), + @JsonProperty("withinTime") + @ExcludeMissing + withinTime: JsonField = JsonMissing.of(), + ) : this( + toolType, + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + relationTargets, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + searchQueries, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + targetCommunityIds, + targetListId, + targetListIds, + targets, + targetSpaceId, + targetTweetId, + targetTweetIds, + targetUsername, + targetUsernames, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + mutableMapOf(), + ) + + /** + * Identifier for the extraction tool used to run a job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun toolType(): ToolType = toolType.getRequired("toolType") + + /** + * Raw advanced search query appended as-is (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun advancedQuery(): Optional = advancedQuery.getOptional("advancedQuery") + + /** + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun anyWords(): Optional = anyWords.getOptional("anyWords") + + /** + * Bio terms separated by commas or lines. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun bioContains(): Optional = bioContains.getOptional("bioContains") + + /** + * Return only Blue-verified Tweet authors. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun blueVerifiedOnly(): Optional = blueVerifiedOnly.getOptional("blueVerifiedOnly") + + /** + * Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun boundingBox(): Optional = boundingBox.getOptional("boundingBox") + + /** + * Match the Tweet card name. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun cardName(): Optional = cardName.getOptional("cardName") + + /** + * Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun cashtags(): Optional = cashtags.getOptional("cashtags") + + /** + * Reply collection strategy. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun collectionStrategy(): Optional = + collectionStrategy.getOptional("collectionStrategy") + + /** + * Conversation ID filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun conversationId(): Optional = conversationId.getOptional("conversationId") + + /** + * Merge duplicate results across collection targets. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun dedupeAcrossTargets(): Optional = + dedupeAcrossTargets.getOptional("dedupeAcrossTargets") + + /** + * Keep target duplicates, first rows, or merged overlap. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun dedupeMode(): Optional = dedupeMode.getOptional("dedupeMode") + + /** + * Exact phrase to match (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun exactPhrase(): Optional = exactPhrase.getOptional("exactPhrase") + + /** + * Exclude replies from the source author. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeOriginalAuthor(): Optional = + excludeOriginalAuthor.getOptional("excludeOriginalAuthor") + + /** + * Exclude a source application. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeSource(): Optional = excludeSource.getOptional("excludeSource") + + /** + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun excludeWords(): Optional = excludeWords.getOptional("excludeWords") + + /** + * Filter by author username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun fromUser(): Optional = fromUser.getOptional("fromUser") + + /** + * Match latitude, longitude, and radius. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun geocode(): Optional = geocode.getOptional("geocode") + + /** + * Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hashtags(): Optional = hashtags.getOptional("hashtags") + + /** + * Require a profile location. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasLocation(): Optional = hasLocation.getOptional("hasLocation") + + /** + * Return only replies with media. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasMediaOnly(): Optional = hasMediaOnly.getOptional("hasMediaOnly") + + /** + * Require a profile website. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun hasWebsite(): Optional = hasWebsite.getOptional("hasWebsite") + + /** + * Include the source post in reply results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeOriginalPost(): Optional = + includeOriginalPost.getOptional("includeOriginalPost") + + /** + * Add matching search terms to collection metadata. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeSearchTerms(): Optional = + includeSearchTerms.getOptional("includeSearchTerms") + + /** + * Add source target metadata to each result. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun includeTargetMetadata(): Optional = + includeTargetMetadata.getOptional("includeTargetMetadata") + + /** + * Only replies to this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun inReplyToTweetId(): Optional = inReplyToTweetId.getOptional("inReplyToTweetId") + + /** + * Language code filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun language(): Optional = language.getOptional("language") + + /** + * Search within a list ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun listId(): Optional = listId.getOptional("listId") + + /** + * Required profile location text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun locationContains(): Optional = locationContains.getOptional("locationContains") + + /** + * Maximum nested reply depth. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxDepth(): Optional = maxDepth.getOptional("maxDepth") + + /** + * Maximum follower count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxFollowers(): Optional = maxFollowers.getOptional("maxFollowers") + + /** + * Maximum following count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxFollowing(): Optional = maxFollowing.getOptional("maxFollowing") + + /** + * Return Tweets older than this Tweet ID. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxId(): Optional = maxId.getOptional("maxId") + + /** + * Maximum results collected for each target. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxItemsPerTarget(): Optional = maxItemsPerTarget.getOptional("maxItemsPerTarget") + + /** + * Maximum Tweet like count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxLikes(): Optional = maxLikes.getOptional("maxLikes") + + /** + * Reply pages collected for each target. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxPagesPerTarget(): Optional = maxPagesPerTarget.getOptional("maxPagesPerTarget") + + /** + * Maximum post count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxPosts(): Optional = maxPosts.getOptional("maxPosts") + + /** + * Maximum Tweet quote count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxQuotes(): Optional = maxQuotes.getOptional("maxQuotes") + + /** + * Maximum Tweet reply count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxReplies(): Optional = maxReplies.getOptional("maxReplies") + + /** + * Maximum Tweet repost count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun maxRetweets(): Optional = maxRetweets.getOptional("maxRetweets") + + /** + * Media type filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun mediaType(): Optional = mediaType.getOptional("mediaType") + + /** + * Filter tweets mentioning a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun mentioning(): Optional = mentioning.getOptional("mentioning") + + /** + * Minimum profile age in days. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minAccountAgeDays(): Optional = minAccountAgeDays.getOptional("minAccountAgeDays") + + /** + * Minimum Tweet bookmark count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minBookmarks(): Optional = minBookmarks.getOptional("minBookmarks") + + /** + * Minimum likes threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFaves(): Optional = minFaves.getOptional("minFaves") + + /** + * Minimum follower count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFollowers(): Optional = minFollowers.getOptional("minFollowers") + + /** + * Minimum following count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minFollowing(): Optional = minFollowing.getOptional("minFollowing") + + /** + * Minimum post count for profile results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minPosts(): Optional = minPosts.getOptional("minPosts") + + /** + * Minimum quote count threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minQuotes(): Optional = minQuotes.getOptional("minQuotes") + + /** + * Minimum replies threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minReplies(): Optional = minReplies.getOptional("minReplies") + + /** + * Minimum retweets threshold (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minRetweets(): Optional = minRetweets.getOptional("minRetweets") + + /** + * Minimum Tweet view count. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun minViews(): Optional = minViews.getOptional("minViews") + + /** + * Only return native reposts. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun nativeRetweets(): Optional = nativeRetweets.getOptional("nativeRetweets") + + /** + * Match a place name. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun near(): Optional = near.getOptional("near") + + /** + * Only return news results. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun news(): Optional = news.getOptional("news") + + /** + * Shortcut for dedupeMode=merge. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun overlapMode(): Optional = overlapMode.getOptional("overlapMode") + + /** + * Search within a place ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun place(): Optional = place.getOptional("place") + + /** + * Search within a country code (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun placeCountry(): Optional = placeCountry.getOptional("placeCountry") + + /** + * Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun pointRadius(): Optional = pointRadius.getOptional("pointRadius") + + /** + * Search ranking applied to every query. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun queryType(): Optional = queryType.getOptional("queryType") + + /** + * Quote mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun quotes(): Optional = quotes.getOptional("quotes") + + /** + * Only quotes of this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun quotesOfTweetId(): Optional = quotesOfTweetId.getOptional("quotesOfTweetId") + + /** + * Profile relations processed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun relationTargets(): Optional> = + relationTargets.getOptional("relationTargets") + + /** + * Reply mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun replies(): Optional = replies.getOptional("replies") + + /** + * Maximum number of results to extract. When set, the extraction stops after reaching this + * limit. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun resultsLimit(): Optional = resultsLimit.getOptional("resultsLimit") + + /** + * Retweet mode (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun retweets(): Optional = retweets.getOptional("retweets") + + /** + * Only retweets of this tweet ID (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun retweetsOfTweetId(): Optional = + retweetsOfTweetId.getOptional("retweetsOfTweetId") + + /** + * Enable the safe-search filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun safe(): Optional = safe.getOptional("safe") + + /** + * Reply depth scope. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun scope(): Optional = scope.getOptional("scope") + + /** + * Search queries processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun searchQueries(): Optional> = searchQueries.getOptional("searchQueries") + + /** + * Required for tweet_search_extractor & community_search. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun searchQuery(): Optional = searchQuery.getOptional("searchQuery") + + /** + * Start date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceDate(): Optional = sinceDate.getOptional("sinceDate") + + /** + * Return Tweets newer than this Tweet ID. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceId(): Optional = sinceId.getOptional("sinceId") + + /** + * Reply start time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sinceTime(): Optional = sinceTime.getOptional("sinceTime") + + /** + * Reply result order. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun sort(): Optional = sort.getOptional("sort") + + /** + * Match the source application. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun source(): Optional = source.getOptional("source") + + /** + * Resume one reply target from this cursor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun startCursor(): Optional = startCursor.getOptional("startCursor") + + /** + * Required for community_post_extractor & community_search. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetCommunityId(): Optional = + targetCommunityId.getOptional("targetCommunityId") + + /** + * Community IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetCommunityIds(): Optional> = + targetCommunityIds.getOptional("targetCommunityIds") + + /** + * Required for list_follower_explorer, list_member_extractor & list_post_extractor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetListId(): Optional = targetListId.getOptional("targetListId") + + /** + * List IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetListIds(): Optional> = targetListIds.getOptional("targetListIds") + + /** + * Mixed targets auto-routed within one job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targets(): Optional> = targets.getOptional("targets") + + /** + * Required for space_explorer. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetSpaceId(): Optional = targetSpaceId.getOptional("targetSpaceId") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetTweetId(): Optional = targetTweetId.getOptional("targetTweetId") + + /** + * Tweet IDs processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetTweetIds(): Optional> = targetTweetIds.getOptional("targetTweetIds") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetUsername(): Optional = targetUsername.getOptional("targetUsername") + + /** + * Usernames processed as one collection job. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun targetUsernames(): Optional> = + targetUsernames.getOptional("targetUsernames") + + /** + * Filter replies sent to a username (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun toUser(): Optional = toUser.getOptional("toUser") + + /** + * End date YYYY-MM-DD (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun untilDate(): Optional = untilDate.getOptional("untilDate") + + /** + * Reply end time as ISO 8601 or Unix seconds. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun untilTime(): Optional = untilTime.getOptional("untilTime") + + /** + * URL substring or domain filter (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun url(): Optional = url.getOptional("url") + + /** + * Required username text. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun usernameContains(): Optional = usernameContains.getOptional("usernameContains") + + /** + * Only verified authors (tweet_search_extractor) + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun verifiedOnly(): Optional = verifiedOnly.getOptional("verifiedOnly") + + /** + * Exact profile verification type. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun verifiedType(): Optional = verifiedType.getOptional("verifiedType") + + /** + * Set the radius for the near filter. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun within(): Optional = within.getOptional("within") + + /** + * Match Tweets inside a recent time window. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun withinTime(): Optional = withinTime.getOptional("withinTime") + + /** + * Returns the raw JSON value of [toolType]. + * + * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("toolType") @ExcludeMissing fun _toolType(): JsonField = toolType + + /** + * Returns the raw JSON value of [advancedQuery]. + * + * Unlike [advancedQuery], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("advancedQuery") + @ExcludeMissing + fun _advancedQuery(): JsonField = advancedQuery + + /** + * Returns the raw JSON value of [anyWords]. + * + * Unlike [anyWords], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("anyWords") @ExcludeMissing fun _anyWords(): JsonField = anyWords + + /** + * Returns the raw JSON value of [bioContains]. + * + * Unlike [bioContains], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bioContains") + @ExcludeMissing + fun _bioContains(): JsonField = bioContains + + /** + * Returns the raw JSON value of [blueVerifiedOnly]. + * + * Unlike [blueVerifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("blueVerifiedOnly") + @ExcludeMissing + fun _blueVerifiedOnly(): JsonField = blueVerifiedOnly + + /** + * Returns the raw JSON value of [boundingBox]. + * + * Unlike [boundingBox], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("boundingBox") + @ExcludeMissing + fun _boundingBox(): JsonField = boundingBox + + /** + * Returns the raw JSON value of [cardName]. + * + * Unlike [cardName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cardName") @ExcludeMissing fun _cardName(): JsonField = cardName + + /** + * Returns the raw JSON value of [cashtags]. + * + * Unlike [cashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cashtags") @ExcludeMissing fun _cashtags(): JsonField = cashtags + + /** + * Returns the raw JSON value of [collectionStrategy]. + * + * Unlike [collectionStrategy], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("collectionStrategy") + @ExcludeMissing + fun _collectionStrategy(): JsonField = collectionStrategy + + /** + * Returns the raw JSON value of [conversationId]. + * + * Unlike [conversationId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("conversationId") + @ExcludeMissing + fun _conversationId(): JsonField = conversationId + + /** + * Returns the raw JSON value of [dedupeAcrossTargets]. + * + * Unlike [dedupeAcrossTargets], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("dedupeAcrossTargets") + @ExcludeMissing + fun _dedupeAcrossTargets(): JsonField = dedupeAcrossTargets + + /** + * Returns the raw JSON value of [dedupeMode]. + * + * Unlike [dedupeMode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dedupeMode") + @ExcludeMissing + fun _dedupeMode(): JsonField = dedupeMode + + /** + * Returns the raw JSON value of [exactPhrase]. + * + * Unlike [exactPhrase], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("exactPhrase") + @ExcludeMissing + fun _exactPhrase(): JsonField = exactPhrase + + /** + * Returns the raw JSON value of [excludeOriginalAuthor]. + * + * Unlike [excludeOriginalAuthor], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("excludeOriginalAuthor") + @ExcludeMissing + fun _excludeOriginalAuthor(): JsonField = excludeOriginalAuthor + + /** + * Returns the raw JSON value of [excludeSource]. + * + * Unlike [excludeSource], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("excludeSource") + @ExcludeMissing + fun _excludeSource(): JsonField = excludeSource + + /** + * Returns the raw JSON value of [excludeWords]. + * + * Unlike [excludeWords], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("excludeWords") + @ExcludeMissing + fun _excludeWords(): JsonField = excludeWords + + /** + * Returns the raw JSON value of [fromUser]. + * + * Unlike [fromUser], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fromUser") @ExcludeMissing fun _fromUser(): JsonField = fromUser + + /** + * Returns the raw JSON value of [geocode]. + * + * Unlike [geocode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("geocode") @ExcludeMissing fun _geocode(): JsonField = geocode + + /** + * Returns the raw JSON value of [hashtags]. + * + * Unlike [hashtags], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hashtags") @ExcludeMissing fun _hashtags(): JsonField = hashtags + + /** + * Returns the raw JSON value of [hasLocation]. + * + * Unlike [hasLocation], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasLocation") + @ExcludeMissing + fun _hasLocation(): JsonField = hasLocation + + /** + * Returns the raw JSON value of [hasMediaOnly]. + * + * Unlike [hasMediaOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("hasMediaOnly") + @ExcludeMissing + fun _hasMediaOnly(): JsonField = hasMediaOnly + + /** + * Returns the raw JSON value of [hasWebsite]. + * + * Unlike [hasWebsite], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasWebsite") + @ExcludeMissing + fun _hasWebsite(): JsonField = hasWebsite + + /** + * Returns the raw JSON value of [includeOriginalPost]. + * + * Unlike [includeOriginalPost], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeOriginalPost") + @ExcludeMissing + fun _includeOriginalPost(): JsonField = includeOriginalPost + + /** + * Returns the raw JSON value of [includeSearchTerms]. + * + * Unlike [includeSearchTerms], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeSearchTerms") + @ExcludeMissing + fun _includeSearchTerms(): JsonField = includeSearchTerms + + /** + * Returns the raw JSON value of [includeTargetMetadata]. + * + * Unlike [includeTargetMetadata], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("includeTargetMetadata") + @ExcludeMissing + fun _includeTargetMetadata(): JsonField = includeTargetMetadata + + /** + * Returns the raw JSON value of [inReplyToTweetId]. + * + * Unlike [inReplyToTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("inReplyToTweetId") + @ExcludeMissing + fun _inReplyToTweetId(): JsonField = inReplyToTweetId + + /** + * Returns the raw JSON value of [language]. + * + * Unlike [language], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + + /** + * Returns the raw JSON value of [listId]. + * + * Unlike [listId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("listId") @ExcludeMissing fun _listId(): JsonField = listId + + /** + * Returns the raw JSON value of [locationContains]. + * + * Unlike [locationContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("locationContains") + @ExcludeMissing + fun _locationContains(): JsonField = locationContains + + /** + * Returns the raw JSON value of [maxDepth]. + * + * Unlike [maxDepth], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxDepth") @ExcludeMissing fun _maxDepth(): JsonField = maxDepth + + /** + * Returns the raw JSON value of [maxFollowers]. + * + * Unlike [maxFollowers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxFollowers") + @ExcludeMissing + fun _maxFollowers(): JsonField = maxFollowers + + /** + * Returns the raw JSON value of [maxFollowing]. + * + * Unlike [maxFollowing], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxFollowing") + @ExcludeMissing + fun _maxFollowing(): JsonField = maxFollowing + + /** + * Returns the raw JSON value of [maxId]. + * + * Unlike [maxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxId") @ExcludeMissing fun _maxId(): JsonField = maxId + + /** + * Returns the raw JSON value of [maxItemsPerTarget]. + * + * Unlike [maxItemsPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxItemsPerTarget") + @ExcludeMissing + fun _maxItemsPerTarget(): JsonField = maxItemsPerTarget + + /** + * Returns the raw JSON value of [maxLikes]. + * + * Unlike [maxLikes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxLikes") @ExcludeMissing fun _maxLikes(): JsonField = maxLikes + + /** + * Returns the raw JSON value of [maxPagesPerTarget]. + * + * Unlike [maxPagesPerTarget], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("maxPagesPerTarget") + @ExcludeMissing + fun _maxPagesPerTarget(): JsonField = maxPagesPerTarget + + /** + * Returns the raw JSON value of [maxPosts]. + * + * Unlike [maxPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxPosts") @ExcludeMissing fun _maxPosts(): JsonField = maxPosts + + /** + * Returns the raw JSON value of [maxQuotes]. + * + * Unlike [maxQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxQuotes") @ExcludeMissing fun _maxQuotes(): JsonField = maxQuotes + + /** + * Returns the raw JSON value of [maxReplies]. + * + * Unlike [maxReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxReplies") @ExcludeMissing fun _maxReplies(): JsonField = maxReplies + + /** + * Returns the raw JSON value of [maxRetweets]. + * + * Unlike [maxRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxRetweets") + @ExcludeMissing + fun _maxRetweets(): JsonField = maxRetweets + + /** + * Returns the raw JSON value of [mediaType]. + * + * Unlike [mediaType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mediaType") + @ExcludeMissing + fun _mediaType(): JsonField = mediaType + + /** + * Returns the raw JSON value of [mentioning]. + * + * Unlike [mentioning], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mentioning") + @ExcludeMissing + fun _mentioning(): JsonField = mentioning + + /** + * Returns the raw JSON value of [minAccountAgeDays]. + * + * Unlike [minAccountAgeDays], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minAccountAgeDays") + @ExcludeMissing + fun _minAccountAgeDays(): JsonField = minAccountAgeDays + + /** + * Returns the raw JSON value of [minBookmarks]. + * + * Unlike [minBookmarks], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minBookmarks") + @ExcludeMissing + fun _minBookmarks(): JsonField = minBookmarks + + /** + * Returns the raw JSON value of [minFaves]. + * + * Unlike [minFaves], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minFaves") @ExcludeMissing fun _minFaves(): JsonField = minFaves + + /** + * Returns the raw JSON value of [minFollowers]. + * + * Unlike [minFollowers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minFollowers") + @ExcludeMissing + fun _minFollowers(): JsonField = minFollowers + + /** + * Returns the raw JSON value of [minFollowing]. + * + * Unlike [minFollowing], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("minFollowing") + @ExcludeMissing + fun _minFollowing(): JsonField = minFollowing + + /** + * Returns the raw JSON value of [minPosts]. + * + * Unlike [minPosts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minPosts") @ExcludeMissing fun _minPosts(): JsonField = minPosts + + /** + * Returns the raw JSON value of [minQuotes]. + * + * Unlike [minQuotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minQuotes") @ExcludeMissing fun _minQuotes(): JsonField = minQuotes + + /** + * Returns the raw JSON value of [minReplies]. + * + * Unlike [minReplies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minReplies") @ExcludeMissing fun _minReplies(): JsonField = minReplies + + /** + * Returns the raw JSON value of [minRetweets]. + * + * Unlike [minRetweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minRetweets") + @ExcludeMissing + fun _minRetweets(): JsonField = minRetweets + + /** + * Returns the raw JSON value of [minViews]. + * + * Unlike [minViews], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minViews") @ExcludeMissing fun _minViews(): JsonField = minViews + + /** + * Returns the raw JSON value of [nativeRetweets]. + * + * Unlike [nativeRetweets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nativeRetweets") + @ExcludeMissing + fun _nativeRetweets(): JsonField = nativeRetweets + + /** + * Returns the raw JSON value of [near]. + * + * Unlike [near], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("near") @ExcludeMissing fun _near(): JsonField = near + + /** + * Returns the raw JSON value of [news]. + * + * Unlike [news], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("news") @ExcludeMissing fun _news(): JsonField = news + + /** + * Returns the raw JSON value of [overlapMode]. + * + * Unlike [overlapMode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("overlapMode") + @ExcludeMissing + fun _overlapMode(): JsonField = overlapMode + + /** + * Returns the raw JSON value of [place]. + * + * Unlike [place], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("place") @ExcludeMissing fun _place(): JsonField = place + + /** + * Returns the raw JSON value of [placeCountry]. + * + * Unlike [placeCountry], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("placeCountry") + @ExcludeMissing + fun _placeCountry(): JsonField = placeCountry + + /** + * Returns the raw JSON value of [pointRadius]. + * + * Unlike [pointRadius], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pointRadius") + @ExcludeMissing + fun _pointRadius(): JsonField = pointRadius + + /** + * Returns the raw JSON value of [queryType]. + * + * Unlike [queryType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("queryType") + @ExcludeMissing + fun _queryType(): JsonField = queryType + + /** + * Returns the raw JSON value of [quotes]. + * + * Unlike [quotes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("quotes") @ExcludeMissing fun _quotes(): JsonField = quotes + + /** + * Returns the raw JSON value of [quotesOfTweetId]. + * + * Unlike [quotesOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("quotesOfTweetId") + @ExcludeMissing + fun _quotesOfTweetId(): JsonField = quotesOfTweetId + + /** + * Returns the raw JSON value of [relationTargets]. + * + * Unlike [relationTargets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("relationTargets") + @ExcludeMissing + fun _relationTargets(): JsonField> = relationTargets + + /** + * Returns the raw JSON value of [replies]. + * + * Unlike [replies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("replies") @ExcludeMissing fun _replies(): JsonField = replies + + /** + * Returns the raw JSON value of [resultsLimit]. + * + * Unlike [resultsLimit], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("resultsLimit") + @ExcludeMissing + fun _resultsLimit(): JsonField = resultsLimit + + /** + * Returns the raw JSON value of [retweets]. + * + * Unlike [retweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("retweets") @ExcludeMissing fun _retweets(): JsonField = retweets + + /** + * Returns the raw JSON value of [retweetsOfTweetId]. + * + * Unlike [retweetsOfTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("retweetsOfTweetId") + @ExcludeMissing + fun _retweetsOfTweetId(): JsonField = retweetsOfTweetId + + /** + * Returns the raw JSON value of [safe]. + * + * Unlike [safe], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("safe") @ExcludeMissing fun _safe(): JsonField = safe + + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scope") @ExcludeMissing fun _scope(): JsonField = scope + + /** + * Returns the raw JSON value of [searchQueries]. + * + * Unlike [searchQueries], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("searchQueries") + @ExcludeMissing + fun _searchQueries(): JsonField> = searchQueries + + /** + * Returns the raw JSON value of [searchQuery]. + * + * Unlike [searchQuery], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("searchQuery") + @ExcludeMissing + fun _searchQuery(): JsonField = searchQuery + + /** + * Returns the raw JSON value of [sinceDate]. + * + * Unlike [sinceDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceDate") + @ExcludeMissing + fun _sinceDate(): JsonField = sinceDate + + /** + * Returns the raw JSON value of [sinceId]. + * + * Unlike [sinceId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceId") @ExcludeMissing fun _sinceId(): JsonField = sinceId + + /** + * Returns the raw JSON value of [sinceTime]. + * + * Unlike [sinceTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sinceTime") + @ExcludeMissing + fun _sinceTime(): JsonField = sinceTime + + /** + * Returns the raw JSON value of [sort]. + * + * Unlike [sort], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sort") @ExcludeMissing fun _sort(): JsonField = sort + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [startCursor]. + * + * Unlike [startCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("startCursor") + @ExcludeMissing + fun _startCursor(): JsonField = startCursor + + /** + * Returns the raw JSON value of [targetCommunityId]. + * + * Unlike [targetCommunityId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetCommunityId") + @ExcludeMissing + fun _targetCommunityId(): JsonField = targetCommunityId + + /** + * Returns the raw JSON value of [targetCommunityIds]. + * + * Unlike [targetCommunityIds], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("targetCommunityIds") + @ExcludeMissing + fun _targetCommunityIds(): JsonField> = targetCommunityIds + + /** + * Returns the raw JSON value of [targetListId]. + * + * Unlike [targetListId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetListId") + @ExcludeMissing + fun _targetListId(): JsonField = targetListId + + /** + * Returns the raw JSON value of [targetListIds]. + * + * Unlike [targetListIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetListIds") + @ExcludeMissing + fun _targetListIds(): JsonField> = targetListIds + + /** + * Returns the raw JSON value of [targets]. + * + * Unlike [targets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("targets") @ExcludeMissing fun _targets(): JsonField> = targets + + /** + * Returns the raw JSON value of [targetSpaceId]. + * + * Unlike [targetSpaceId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetSpaceId") + @ExcludeMissing + fun _targetSpaceId(): JsonField = targetSpaceId + + /** + * Returns the raw JSON value of [targetTweetId]. + * + * Unlike [targetTweetId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetTweetId") + @ExcludeMissing + fun _targetTweetId(): JsonField = targetTweetId + + /** + * Returns the raw JSON value of [targetTweetIds]. + * + * Unlike [targetTweetIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetTweetIds") + @ExcludeMissing + fun _targetTweetIds(): JsonField> = targetTweetIds + + /** + * Returns the raw JSON value of [targetUsername]. + * + * Unlike [targetUsername], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetUsername") + @ExcludeMissing + fun _targetUsername(): JsonField = targetUsername + + /** + * Returns the raw JSON value of [targetUsernames]. + * + * Unlike [targetUsernames], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("targetUsernames") + @ExcludeMissing + fun _targetUsernames(): JsonField> = targetUsernames + + /** + * Returns the raw JSON value of [toUser]. + * + * Unlike [toUser], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("toUser") @ExcludeMissing fun _toUser(): JsonField = toUser + + /** + * Returns the raw JSON value of [untilDate]. + * + * Unlike [untilDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("untilDate") + @ExcludeMissing + fun _untilDate(): JsonField = untilDate + + /** + * Returns the raw JSON value of [untilTime]. + * + * Unlike [untilTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("untilTime") + @ExcludeMissing + fun _untilTime(): JsonField = untilTime + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + /** + * Returns the raw JSON value of [usernameContains]. + * + * Unlike [usernameContains], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("usernameContains") + @ExcludeMissing + fun _usernameContains(): JsonField = usernameContains + + /** + * Returns the raw JSON value of [verifiedOnly]. + * + * Unlike [verifiedOnly], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("verifiedOnly") + @ExcludeMissing + fun _verifiedOnly(): JsonField = verifiedOnly + + /** + * Returns the raw JSON value of [verifiedType]. + * + * Unlike [verifiedType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("verifiedType") + @ExcludeMissing + fun _verifiedType(): JsonField = verifiedType + + /** + * Returns the raw JSON value of [within]. + * + * Unlike [within], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("within") @ExcludeMissing fun _within(): JsonField = within + + /** + * Returns the raw JSON value of [withinTime]. + * + * Unlike [withinTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("withinTime") + @ExcludeMissing + fun _withinTime(): JsonField = withinTime + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var toolType: JsonField? = null + private var advancedQuery: JsonField = JsonMissing.of() + private var anyWords: JsonField = JsonMissing.of() + private var bioContains: JsonField = JsonMissing.of() + private var blueVerifiedOnly: JsonField = JsonMissing.of() + private var boundingBox: JsonField = JsonMissing.of() + private var cardName: JsonField = JsonMissing.of() + private var cashtags: JsonField = JsonMissing.of() + private var collectionStrategy: JsonField = JsonMissing.of() + private var conversationId: JsonField = JsonMissing.of() + private var dedupeAcrossTargets: JsonField = JsonMissing.of() + private var dedupeMode: JsonField = JsonMissing.of() + private var exactPhrase: JsonField = JsonMissing.of() + private var excludeOriginalAuthor: JsonField = JsonMissing.of() + private var excludeSource: JsonField = JsonMissing.of() + private var excludeWords: JsonField = JsonMissing.of() + private var fromUser: JsonField = JsonMissing.of() + private var geocode: JsonField = JsonMissing.of() + private var hashtags: JsonField = JsonMissing.of() + private var hasLocation: JsonField = JsonMissing.of() + private var hasMediaOnly: JsonField = JsonMissing.of() + private var hasWebsite: JsonField = JsonMissing.of() + private var includeOriginalPost: JsonField = JsonMissing.of() + private var includeSearchTerms: JsonField = JsonMissing.of() + private var includeTargetMetadata: JsonField = JsonMissing.of() + private var inReplyToTweetId: JsonField = JsonMissing.of() + private var language: JsonField = JsonMissing.of() + private var listId: JsonField = JsonMissing.of() + private var locationContains: JsonField = JsonMissing.of() + private var maxDepth: JsonField = JsonMissing.of() + private var maxFollowers: JsonField = JsonMissing.of() + private var maxFollowing: JsonField = JsonMissing.of() + private var maxId: JsonField = JsonMissing.of() + private var maxItemsPerTarget: JsonField = JsonMissing.of() + private var maxLikes: JsonField = JsonMissing.of() + private var maxPagesPerTarget: JsonField = JsonMissing.of() + private var maxPosts: JsonField = JsonMissing.of() + private var maxQuotes: JsonField = JsonMissing.of() + private var maxReplies: JsonField = JsonMissing.of() + private var maxRetweets: JsonField = JsonMissing.of() + private var mediaType: JsonField = JsonMissing.of() + private var mentioning: JsonField = JsonMissing.of() + private var minAccountAgeDays: JsonField = JsonMissing.of() + private var minBookmarks: JsonField = JsonMissing.of() + private var minFaves: JsonField = JsonMissing.of() + private var minFollowers: JsonField = JsonMissing.of() + private var minFollowing: JsonField = JsonMissing.of() + private var minPosts: JsonField = JsonMissing.of() + private var minQuotes: JsonField = JsonMissing.of() + private var minReplies: JsonField = JsonMissing.of() + private var minRetweets: JsonField = JsonMissing.of() + private var minViews: JsonField = JsonMissing.of() + private var nativeRetweets: JsonField = JsonMissing.of() + private var near: JsonField = JsonMissing.of() + private var news: JsonField = JsonMissing.of() + private var overlapMode: JsonField = JsonMissing.of() + private var place: JsonField = JsonMissing.of() + private var placeCountry: JsonField = JsonMissing.of() + private var pointRadius: JsonField = JsonMissing.of() + private var queryType: JsonField = JsonMissing.of() + private var quotes: JsonField = JsonMissing.of() + private var quotesOfTweetId: JsonField = JsonMissing.of() + private var relationTargets: JsonField>? = null + private var replies: JsonField = JsonMissing.of() + private var resultsLimit: JsonField = JsonMissing.of() + private var retweets: JsonField = JsonMissing.of() + private var retweetsOfTweetId: JsonField = JsonMissing.of() + private var safe: JsonField = JsonMissing.of() + private var scope: JsonField = JsonMissing.of() + private var searchQueries: JsonField>? = null + private var searchQuery: JsonField = JsonMissing.of() + private var sinceDate: JsonField = JsonMissing.of() + private var sinceId: JsonField = JsonMissing.of() + private var sinceTime: JsonField = JsonMissing.of() + private var sort: JsonField = JsonMissing.of() + private var source: JsonField = JsonMissing.of() + private var startCursor: JsonField = JsonMissing.of() + private var targetCommunityId: JsonField = JsonMissing.of() + private var targetCommunityIds: JsonField>? = null + private var targetListId: JsonField = JsonMissing.of() + private var targetListIds: JsonField>? = null + private var targets: JsonField>? = null + private var targetSpaceId: JsonField = JsonMissing.of() + private var targetTweetId: JsonField = JsonMissing.of() + private var targetTweetIds: JsonField>? = null + private var targetUsername: JsonField = JsonMissing.of() + private var targetUsernames: JsonField>? = null + private var toUser: JsonField = JsonMissing.of() + private var untilDate: JsonField = JsonMissing.of() + private var untilTime: JsonField = JsonMissing.of() + private var url: JsonField = JsonMissing.of() + private var usernameContains: JsonField = JsonMissing.of() + private var verifiedOnly: JsonField = JsonMissing.of() + private var verifiedType: JsonField = JsonMissing.of() + private var within: JsonField = JsonMissing.of() + private var withinTime: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + toolType = body.toolType + advancedQuery = body.advancedQuery + anyWords = body.anyWords + bioContains = body.bioContains + blueVerifiedOnly = body.blueVerifiedOnly + boundingBox = body.boundingBox + cardName = body.cardName + cashtags = body.cashtags + collectionStrategy = body.collectionStrategy + conversationId = body.conversationId + dedupeAcrossTargets = body.dedupeAcrossTargets + dedupeMode = body.dedupeMode + exactPhrase = body.exactPhrase + excludeOriginalAuthor = body.excludeOriginalAuthor + excludeSource = body.excludeSource + excludeWords = body.excludeWords + fromUser = body.fromUser + geocode = body.geocode + hashtags = body.hashtags + hasLocation = body.hasLocation + hasMediaOnly = body.hasMediaOnly + hasWebsite = body.hasWebsite + includeOriginalPost = body.includeOriginalPost + includeSearchTerms = body.includeSearchTerms + includeTargetMetadata = body.includeTargetMetadata + inReplyToTweetId = body.inReplyToTweetId + language = body.language + listId = body.listId + locationContains = body.locationContains + maxDepth = body.maxDepth + maxFollowers = body.maxFollowers + maxFollowing = body.maxFollowing + maxId = body.maxId + maxItemsPerTarget = body.maxItemsPerTarget + maxLikes = body.maxLikes + maxPagesPerTarget = body.maxPagesPerTarget + maxPosts = body.maxPosts + maxQuotes = body.maxQuotes + maxReplies = body.maxReplies + maxRetweets = body.maxRetweets + mediaType = body.mediaType + mentioning = body.mentioning + minAccountAgeDays = body.minAccountAgeDays + minBookmarks = body.minBookmarks + minFaves = body.minFaves + minFollowers = body.minFollowers + minFollowing = body.minFollowing + minPosts = body.minPosts + minQuotes = body.minQuotes + minReplies = body.minReplies + minRetweets = body.minRetweets + minViews = body.minViews + nativeRetweets = body.nativeRetweets + near = body.near + news = body.news + overlapMode = body.overlapMode + place = body.place + placeCountry = body.placeCountry + pointRadius = body.pointRadius + queryType = body.queryType + quotes = body.quotes + quotesOfTweetId = body.quotesOfTweetId + relationTargets = body.relationTargets.map { it.toMutableList() } + replies = body.replies + resultsLimit = body.resultsLimit + retweets = body.retweets + retweetsOfTweetId = body.retweetsOfTweetId + safe = body.safe + scope = body.scope + searchQueries = body.searchQueries.map { it.toMutableList() } + searchQuery = body.searchQuery + sinceDate = body.sinceDate + sinceId = body.sinceId + sinceTime = body.sinceTime + sort = body.sort + source = body.source + startCursor = body.startCursor + targetCommunityId = body.targetCommunityId + targetCommunityIds = body.targetCommunityIds.map { it.toMutableList() } + targetListId = body.targetListId + targetListIds = body.targetListIds.map { it.toMutableList() } + targets = body.targets.map { it.toMutableList() } + targetSpaceId = body.targetSpaceId + targetTweetId = body.targetTweetId + targetTweetIds = body.targetTweetIds.map { it.toMutableList() } + targetUsername = body.targetUsername + targetUsernames = body.targetUsernames.map { it.toMutableList() } + toUser = body.toUser + untilDate = body.untilDate + untilTime = body.untilTime + url = body.url + usernameContains = body.usernameContains + verifiedOnly = body.verifiedOnly + verifiedType = body.verifiedType + within = body.within + withinTime = body.withinTime + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Identifier for the extraction tool used to run a job. */ + fun toolType(toolType: ToolType) = toolType(JsonField.of(toolType)) + + /** + * Sets [Builder.toolType] to an arbitrary JSON value. + * + * You should usually call [Builder.toolType] with a well-typed [ToolType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun toolType(toolType: JsonField) = apply { this.toolType = toolType } + + /** Raw advanced search query appended as-is (tweet_search_extractor) */ + fun advancedQuery(advancedQuery: String) = advancedQuery(JsonField.of(advancedQuery)) + + /** + * Sets [Builder.advancedQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.advancedQuery] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun advancedQuery(advancedQuery: JsonField) = apply { + this.advancedQuery = advancedQuery + } + + /** + * Words or quoted phrases where any one can match. Separate with spaces, commas, or + * lines. (tweet_search_extractor) + */ + fun anyWords(anyWords: String) = anyWords(JsonField.of(anyWords)) + + /** + * Sets [Builder.anyWords] to an arbitrary JSON value. + * + * You should usually call [Builder.anyWords] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun anyWords(anyWords: JsonField) = apply { this.anyWords = anyWords } + + /** Bio terms separated by commas or lines. */ + fun bioContains(bioContains: String) = bioContains(JsonField.of(bioContains)) + + /** + * Sets [Builder.bioContains] to an arbitrary JSON value. + * + * You should usually call [Builder.bioContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bioContains(bioContains: JsonField) = apply { + this.bioContains = bioContains + } + + /** Return only Blue-verified Tweet authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(JsonField.of(blueVerifiedOnly)) + + /** + * Sets [Builder.blueVerifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.blueVerifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun blueVerifiedOnly(blueVerifiedOnly: JsonField) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8 (tweet_search_extractor) */ + fun boundingBox(boundingBox: String) = boundingBox(JsonField.of(boundingBox)) + + /** + * Sets [Builder.boundingBox] to an arbitrary JSON value. + * + * You should usually call [Builder.boundingBox] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun boundingBox(boundingBox: JsonField) = apply { + this.boundingBox = boundingBox + } + + /** Match the Tweet card name. */ + fun cardName(cardName: String) = cardName(JsonField.of(cardName)) + + /** + * Sets [Builder.cardName] to an arbitrary JSON value. + * + * You should usually call [Builder.cardName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cardName(cardName: JsonField) = apply { this.cardName = cardName } + + /** Cashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun cashtags(cashtags: String) = cashtags(JsonField.of(cashtags)) + + /** + * Sets [Builder.cashtags] to an arbitrary JSON value. + * + * You should usually call [Builder.cashtags] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cashtags(cashtags: JsonField) = apply { this.cashtags = cashtags } + + /** Reply collection strategy. */ + fun collectionStrategy(collectionStrategy: CollectionStrategy) = + collectionStrategy(JsonField.of(collectionStrategy)) + + /** + * Sets [Builder.collectionStrategy] to an arbitrary JSON value. + * + * You should usually call [Builder.collectionStrategy] with a well-typed + * [CollectionStrategy] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun collectionStrategy(collectionStrategy: JsonField) = apply { + this.collectionStrategy = collectionStrategy + } + + /** Conversation ID filter (tweet_search_extractor) */ + fun conversationId(conversationId: String) = conversationId(JsonField.of(conversationId)) - /** - * Sets [Builder.conversationId] to an arbitrary JSON value. - * - * You should usually call [Builder.conversationId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun conversationId(conversationId: JsonField) = apply { - this.conversationId = conversationId + /** + * Sets [Builder.conversationId] to an arbitrary JSON value. + * + * You should usually call [Builder.conversationId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun conversationId(conversationId: JsonField) = apply { + this.conversationId = conversationId + } + + /** Merge duplicate results across collection targets. */ + fun dedupeAcrossTargets(dedupeAcrossTargets: Boolean) = + dedupeAcrossTargets(JsonField.of(dedupeAcrossTargets)) + + /** + * Sets [Builder.dedupeAcrossTargets] to an arbitrary JSON value. + * + * You should usually call [Builder.dedupeAcrossTargets] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun dedupeAcrossTargets(dedupeAcrossTargets: JsonField) = apply { + this.dedupeAcrossTargets = dedupeAcrossTargets + } + + /** Keep target duplicates, first rows, or merged overlap. */ + fun dedupeMode(dedupeMode: DedupeMode) = dedupeMode(JsonField.of(dedupeMode)) + + /** + * Sets [Builder.dedupeMode] to an arbitrary JSON value. + * + * You should usually call [Builder.dedupeMode] with a well-typed [DedupeMode] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dedupeMode(dedupeMode: JsonField) = apply { + this.dedupeMode = dedupeMode + } + + /** Exact phrase to match (tweet_search_extractor) */ + fun exactPhrase(exactPhrase: String) = exactPhrase(JsonField.of(exactPhrase)) + + /** + * Sets [Builder.exactPhrase] to an arbitrary JSON value. + * + * You should usually call [Builder.exactPhrase] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun exactPhrase(exactPhrase: JsonField) = apply { + this.exactPhrase = exactPhrase + } + + /** Exclude replies from the source author. */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean) = + excludeOriginalAuthor(JsonField.of(excludeOriginalAuthor)) + + /** + * Sets [Builder.excludeOriginalAuthor] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeOriginalAuthor] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun excludeOriginalAuthor(excludeOriginalAuthor: JsonField) = apply { + this.excludeOriginalAuthor = excludeOriginalAuthor + } + + /** Exclude a source application. */ + fun excludeSource(excludeSource: String) = excludeSource(JsonField.of(excludeSource)) + + /** + * Sets [Builder.excludeSource] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeSource] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun excludeSource(excludeSource: JsonField) = apply { + this.excludeSource = excludeSource + } + + /** + * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. + * (tweet_search_extractor) + */ + fun excludeWords(excludeWords: String) = excludeWords(JsonField.of(excludeWords)) + + /** + * Sets [Builder.excludeWords] to an arbitrary JSON value. + * + * You should usually call [Builder.excludeWords] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun excludeWords(excludeWords: JsonField) = apply { + this.excludeWords = excludeWords + } + + /** Filter by author username (tweet_search_extractor) */ + fun fromUser(fromUser: String) = fromUser(JsonField.of(fromUser)) + + /** + * Sets [Builder.fromUser] to an arbitrary JSON value. + * + * You should usually call [Builder.fromUser] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fromUser(fromUser: JsonField) = apply { this.fromUser = fromUser } + + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String) = geocode(JsonField.of(geocode)) + + /** + * Sets [Builder.geocode] to an arbitrary JSON value. + * + * You should usually call [Builder.geocode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun geocode(geocode: JsonField) = apply { this.geocode = geocode } + + /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ + fun hashtags(hashtags: String) = hashtags(JsonField.of(hashtags)) + + /** + * Sets [Builder.hashtags] to an arbitrary JSON value. + * + * You should usually call [Builder.hashtags] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hashtags(hashtags: JsonField) = apply { this.hashtags = hashtags } + + /** Require a profile location. */ + fun hasLocation(hasLocation: Boolean) = hasLocation(JsonField.of(hasLocation)) + + /** + * Sets [Builder.hasLocation] to an arbitrary JSON value. + * + * You should usually call [Builder.hasLocation] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasLocation(hasLocation: JsonField) = apply { + this.hasLocation = hasLocation + } + + /** Return only replies with media. */ + fun hasMediaOnly(hasMediaOnly: Boolean) = hasMediaOnly(JsonField.of(hasMediaOnly)) + + /** + * Sets [Builder.hasMediaOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMediaOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasMediaOnly(hasMediaOnly: JsonField) = apply { + this.hasMediaOnly = hasMediaOnly + } + + /** Require a profile website. */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(JsonField.of(hasWebsite)) + + /** + * Sets [Builder.hasWebsite] to an arbitrary JSON value. + * + * You should usually call [Builder.hasWebsite] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasWebsite(hasWebsite: JsonField) = apply { this.hasWebsite = hasWebsite } + + /** Include the source post in reply results. */ + fun includeOriginalPost(includeOriginalPost: Boolean) = + includeOriginalPost(JsonField.of(includeOriginalPost)) + + /** + * Sets [Builder.includeOriginalPost] to an arbitrary JSON value. + * + * You should usually call [Builder.includeOriginalPost] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeOriginalPost(includeOriginalPost: JsonField) = apply { + this.includeOriginalPost = includeOriginalPost + } + + /** Add matching search terms to collection metadata. */ + fun includeSearchTerms(includeSearchTerms: Boolean) = + includeSearchTerms(JsonField.of(includeSearchTerms)) + + /** + * Sets [Builder.includeSearchTerms] to an arbitrary JSON value. + * + * You should usually call [Builder.includeSearchTerms] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeSearchTerms(includeSearchTerms: JsonField) = apply { + this.includeSearchTerms = includeSearchTerms + } + + /** Add source target metadata to each result. */ + fun includeTargetMetadata(includeTargetMetadata: Boolean) = + includeTargetMetadata(JsonField.of(includeTargetMetadata)) + + /** + * Sets [Builder.includeTargetMetadata] to an arbitrary JSON value. + * + * You should usually call [Builder.includeTargetMetadata] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun includeTargetMetadata(includeTargetMetadata: JsonField) = apply { + this.includeTargetMetadata = includeTargetMetadata + } + + /** Only replies to this tweet ID (tweet_search_extractor) */ + fun inReplyToTweetId(inReplyToTweetId: String) = + inReplyToTweetId(JsonField.of(inReplyToTweetId)) + + /** + * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { + this.inReplyToTweetId = inReplyToTweetId + } + + /** Language code filter (tweet_search_extractor) */ + fun language(language: String) = language(JsonField.of(language)) + + /** + * Sets [Builder.language] to an arbitrary JSON value. + * + * You should usually call [Builder.language] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun language(language: JsonField) = apply { this.language = language } + + /** Search within a list ID (tweet_search_extractor) */ + fun listId(listId: String) = listId(JsonField.of(listId)) + + /** + * Sets [Builder.listId] to an arbitrary JSON value. + * + * You should usually call [Builder.listId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun listId(listId: JsonField) = apply { this.listId = listId } + + /** Required profile location text. */ + fun locationContains(locationContains: String) = + locationContains(JsonField.of(locationContains)) + + /** + * Sets [Builder.locationContains] to an arbitrary JSON value. + * + * You should usually call [Builder.locationContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locationContains(locationContains: JsonField) = apply { + this.locationContains = locationContains + } + + /** Maximum nested reply depth. */ + fun maxDepth(maxDepth: Long) = maxDepth(JsonField.of(maxDepth)) + + /** + * Sets [Builder.maxDepth] to an arbitrary JSON value. + * + * You should usually call [Builder.maxDepth] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxDepth(maxDepth: JsonField) = apply { this.maxDepth = maxDepth } + + /** Maximum follower count for profile results. */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(JsonField.of(maxFollowers)) + + /** + * Sets [Builder.maxFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxFollowers(maxFollowers: JsonField) = apply { + this.maxFollowers = maxFollowers + } + + /** Maximum following count for profile results. */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(JsonField.of(maxFollowing)) + + /** + * Sets [Builder.maxFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.maxFollowing] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxFollowing(maxFollowing: JsonField) = apply { + this.maxFollowing = maxFollowing + } + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String) = maxId(JsonField.of(maxId)) + + /** + * Sets [Builder.maxId] to an arbitrary JSON value. + * + * You should usually call [Builder.maxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxId(maxId: JsonField) = apply { this.maxId = maxId } + + /** Maximum results collected for each target. */ + fun maxItemsPerTarget(maxItemsPerTarget: Long) = + maxItemsPerTarget(JsonField.of(maxItemsPerTarget)) + + /** + * Sets [Builder.maxItemsPerTarget] to an arbitrary JSON value. + * + * You should usually call [Builder.maxItemsPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxItemsPerTarget(maxItemsPerTarget: JsonField) = apply { + this.maxItemsPerTarget = maxItemsPerTarget + } + + /** Maximum Tweet like count. */ + fun maxLikes(maxLikes: Long) = maxLikes(JsonField.of(maxLikes)) + + /** + * Sets [Builder.maxLikes] to an arbitrary JSON value. + * + * You should usually call [Builder.maxLikes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxLikes(maxLikes: JsonField) = apply { this.maxLikes = maxLikes } + + /** Reply pages collected for each target. */ + fun maxPagesPerTarget(maxPagesPerTarget: Long) = + maxPagesPerTarget(JsonField.of(maxPagesPerTarget)) + + /** + * Sets [Builder.maxPagesPerTarget] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPagesPerTarget] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxPagesPerTarget(maxPagesPerTarget: JsonField) = apply { + this.maxPagesPerTarget = maxPagesPerTarget + } + + /** Maximum post count for profile results. */ + fun maxPosts(maxPosts: Long) = maxPosts(JsonField.of(maxPosts)) + + /** + * Sets [Builder.maxPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.maxPosts] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxPosts(maxPosts: JsonField) = apply { this.maxPosts = maxPosts } + + /** Maximum Tweet quote count. */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(JsonField.of(maxQuotes)) + + /** + * Sets [Builder.maxQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.maxQuotes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxQuotes(maxQuotes: JsonField) = apply { this.maxQuotes = maxQuotes } + + /** Maximum Tweet reply count. */ + fun maxReplies(maxReplies: Long) = maxReplies(JsonField.of(maxReplies)) + + /** + * Sets [Builder.maxReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.maxReplies] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxReplies(maxReplies: JsonField) = apply { this.maxReplies = maxReplies } + + /** Maximum Tweet repost count. */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(JsonField.of(maxRetweets)) + + /** + * Sets [Builder.maxRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.maxRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun maxRetweets(maxRetweets: JsonField) = apply { this.maxRetweets = maxRetweets } + + /** Media type filter (tweet_search_extractor) */ + fun mediaType(mediaType: MediaType) = mediaType(JsonField.of(mediaType)) + + /** + * Sets [Builder.mediaType] to an arbitrary JSON value. + * + * You should usually call [Builder.mediaType] with a well-typed [MediaType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mediaType(mediaType: JsonField) = apply { this.mediaType = mediaType } + + /** Filter tweets mentioning a username (tweet_search_extractor) */ + fun mentioning(mentioning: String) = mentioning(JsonField.of(mentioning)) + + /** + * Sets [Builder.mentioning] to an arbitrary JSON value. + * + * You should usually call [Builder.mentioning] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mentioning(mentioning: JsonField) = apply { this.mentioning = mentioning } + + /** Minimum profile age in days. */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(JsonField.of(minAccountAgeDays)) + + /** + * Sets [Builder.minAccountAgeDays] to an arbitrary JSON value. + * + * You should usually call [Builder.minAccountAgeDays] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minAccountAgeDays(minAccountAgeDays: JsonField) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** Minimum Tweet bookmark count. */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(JsonField.of(minBookmarks)) + + /** + * Sets [Builder.minBookmarks] to an arbitrary JSON value. + * + * You should usually call [Builder.minBookmarks] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minBookmarks(minBookmarks: JsonField) = apply { + this.minBookmarks = minBookmarks + } + + /** Minimum likes threshold (tweet_search_extractor) */ + fun minFaves(minFaves: Long) = minFaves(JsonField.of(minFaves)) + + /** + * Sets [Builder.minFaves] to an arbitrary JSON value. + * + * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFaves(minFaves: JsonField) = apply { this.minFaves = minFaves } + + /** Minimum follower count for profile results. */ + fun minFollowers(minFollowers: Long) = minFollowers(JsonField.of(minFollowers)) + + /** + * Sets [Builder.minFollowers] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFollowers(minFollowers: JsonField) = apply { + this.minFollowers = minFollowers + } + + /** Minimum following count for profile results. */ + fun minFollowing(minFollowing: Long) = minFollowing(JsonField.of(minFollowing)) + + /** + * Sets [Builder.minFollowing] to an arbitrary JSON value. + * + * You should usually call [Builder.minFollowing] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minFollowing(minFollowing: JsonField) = apply { + this.minFollowing = minFollowing + } + + /** Minimum post count for profile results. */ + fun minPosts(minPosts: Long) = minPosts(JsonField.of(minPosts)) + + /** + * Sets [Builder.minPosts] to an arbitrary JSON value. + * + * You should usually call [Builder.minPosts] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minPosts(minPosts: JsonField) = apply { this.minPosts = minPosts } + + /** Minimum quote count threshold (tweet_search_extractor) */ + fun minQuotes(minQuotes: Long) = minQuotes(JsonField.of(minQuotes)) + + /** + * Sets [Builder.minQuotes] to an arbitrary JSON value. + * + * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minQuotes(minQuotes: JsonField) = apply { this.minQuotes = minQuotes } + + /** Minimum replies threshold (tweet_search_extractor) */ + fun minReplies(minReplies: Long) = minReplies(JsonField.of(minReplies)) + + /** + * Sets [Builder.minReplies] to an arbitrary JSON value. + * + * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minReplies(minReplies: JsonField) = apply { this.minReplies = minReplies } + + /** Minimum retweets threshold (tweet_search_extractor) */ + fun minRetweets(minRetweets: Long) = minRetweets(JsonField.of(minRetweets)) + + /** + * Sets [Builder.minRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minRetweets(minRetweets: JsonField) = apply { this.minRetweets = minRetweets } + + /** Minimum Tweet view count. */ + fun minViews(minViews: Long) = minViews(JsonField.of(minViews)) + + /** + * Sets [Builder.minViews] to an arbitrary JSON value. + * + * You should usually call [Builder.minViews] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun minViews(minViews: JsonField) = apply { this.minViews = minViews } + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean) = + nativeRetweets(JsonField.of(nativeRetweets)) + + /** + * Sets [Builder.nativeRetweets] to an arbitrary JSON value. + * + * You should usually call [Builder.nativeRetweets] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nativeRetweets(nativeRetweets: JsonField) = apply { + this.nativeRetweets = nativeRetweets + } + + /** Match a place name. */ + fun near(near: String) = near(JsonField.of(near)) + + /** + * Sets [Builder.near] to an arbitrary JSON value. + * + * You should usually call [Builder.near] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun near(near: JsonField) = apply { this.near = near } + + /** Only return news results. */ + fun news(news: Boolean) = news(JsonField.of(news)) + + /** + * Sets [Builder.news] to an arbitrary JSON value. + * + * You should usually call [Builder.news] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun news(news: JsonField) = apply { this.news = news } + + /** Shortcut for dedupeMode=merge. */ + fun overlapMode(overlapMode: Boolean) = overlapMode(JsonField.of(overlapMode)) + + /** + * Sets [Builder.overlapMode] to an arbitrary JSON value. + * + * You should usually call [Builder.overlapMode] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun overlapMode(overlapMode: JsonField) = apply { + this.overlapMode = overlapMode + } + + /** Search within a place ID (tweet_search_extractor) */ + fun place(place: String) = place(JsonField.of(place)) + + /** + * Sets [Builder.place] to an arbitrary JSON value. + * + * You should usually call [Builder.place] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun place(place: JsonField) = apply { this.place = place } + + /** Search within a country code (tweet_search_extractor) */ + fun placeCountry(placeCountry: String) = placeCountry(JsonField.of(placeCountry)) + + /** + * Sets [Builder.placeCountry] to an arbitrary JSON value. + * + * You should usually call [Builder.placeCountry] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun placeCountry(placeCountry: JsonField) = apply { + this.placeCountry = placeCountry + } + + /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ + fun pointRadius(pointRadius: String) = pointRadius(JsonField.of(pointRadius)) + + /** + * Sets [Builder.pointRadius] to an arbitrary JSON value. + * + * You should usually call [Builder.pointRadius] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pointRadius(pointRadius: JsonField) = apply { + this.pointRadius = pointRadius + } + + /** Search ranking applied to every query. */ + fun queryType(queryType: QueryType) = queryType(JsonField.of(queryType)) + + /** + * Sets [Builder.queryType] to an arbitrary JSON value. + * + * You should usually call [Builder.queryType] with a well-typed [QueryType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun queryType(queryType: JsonField) = apply { this.queryType = queryType } + + /** Quote mode (tweet_search_extractor) */ + fun quotes(quotes: Quotes) = quotes(JsonField.of(quotes)) + + /** + * Sets [Builder.quotes] to an arbitrary JSON value. + * + * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quotes(quotes: JsonField) = apply { this.quotes = quotes } + + /** Only quotes of this tweet ID (tweet_search_extractor) */ + fun quotesOfTweetId(quotesOfTweetId: String) = + quotesOfTweetId(JsonField.of(quotesOfTweetId)) + + /** + * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { + this.quotesOfTweetId = quotesOfTweetId + } + + /** Profile relations processed within one job. */ + fun relationTargets(relationTargets: List) = + relationTargets(JsonField.of(relationTargets)) + + /** + * Sets [Builder.relationTargets] to an arbitrary JSON value. + * + * You should usually call [Builder.relationTargets] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun relationTargets(relationTargets: JsonField>) = apply { + this.relationTargets = relationTargets.map { it.toMutableList() } + } + + /** + * Adds a single [RelationTarget] to [relationTargets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRelationTarget(relationTarget: RelationTarget) = apply { + relationTargets = + (relationTargets ?: JsonField.of(mutableListOf())).also { + checkKnown("relationTargets", it).add(relationTarget) + } + } + + /** Reply mode (tweet_search_extractor) */ + fun replies(replies: Replies) = replies(JsonField.of(replies)) + + /** + * Sets [Builder.replies] to an arbitrary JSON value. + * + * You should usually call [Builder.replies] with a well-typed [Replies] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun replies(replies: JsonField) = apply { this.replies = replies } + + /** + * Maximum number of results to extract. When set, the extraction stops after reaching + * this limit. + */ + fun resultsLimit(resultsLimit: Long) = resultsLimit(JsonField.of(resultsLimit)) + + /** + * Sets [Builder.resultsLimit] to an arbitrary JSON value. + * + * You should usually call [Builder.resultsLimit] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun resultsLimit(resultsLimit: JsonField) = apply { + this.resultsLimit = resultsLimit + } + + /** Retweet mode (tweet_search_extractor) */ + fun retweets(retweets: Retweets) = retweets(JsonField.of(retweets)) + + /** + * Sets [Builder.retweets] to an arbitrary JSON value. + * + * You should usually call [Builder.retweets] with a well-typed [Retweets] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun retweets(retweets: JsonField) = apply { this.retweets = retweets } + + /** Only retweets of this tweet ID (tweet_search_extractor) */ + fun retweetsOfTweetId(retweetsOfTweetId: String) = + retweetsOfTweetId(JsonField.of(retweetsOfTweetId)) + + /** + * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { + this.retweetsOfTweetId = retweetsOfTweetId + } + + /** Enable the safe-search filter. */ + fun safe(safe: Boolean) = safe(JsonField.of(safe)) + + /** + * Sets [Builder.safe] to an arbitrary JSON value. + * + * You should usually call [Builder.safe] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun safe(safe: JsonField) = apply { this.safe = safe } + + /** Reply depth scope. */ + fun scope(scope: Scope) = scope(JsonField.of(scope)) + + /** + * Sets [Builder.scope] to an arbitrary JSON value. + * + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun scope(scope: JsonField) = apply { this.scope = scope } + + /** Search queries processed as one collection job. */ + fun searchQueries(searchQueries: List) = + searchQueries(JsonField.of(searchQueries)) + + /** + * Sets [Builder.searchQueries] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQueries] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun searchQueries(searchQueries: JsonField>) = apply { + this.searchQueries = searchQueries.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [searchQueries]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSearchQuery(searchQuery: String) = apply { + searchQueries = + (searchQueries ?: JsonField.of(mutableListOf())).also { + checkKnown("searchQueries", it).add(searchQuery) + } + } + + /** Required for tweet_search_extractor & community_search. */ + fun searchQuery(searchQuery: String) = searchQuery(JsonField.of(searchQuery)) + + /** + * Sets [Builder.searchQuery] to an arbitrary JSON value. + * + * You should usually call [Builder.searchQuery] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun searchQuery(searchQuery: JsonField) = apply { + this.searchQuery = searchQuery + } + + /** Start date YYYY-MM-DD (tweet_search_extractor) */ + fun sinceDate(sinceDate: LocalDate) = sinceDate(JsonField.of(sinceDate)) + + /** + * Sets [Builder.sinceDate] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceDate(sinceDate: JsonField) = apply { this.sinceDate = sinceDate } + + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String) = sinceId(JsonField.of(sinceId)) + + /** + * Sets [Builder.sinceId] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceId(sinceId: JsonField) = apply { this.sinceId = sinceId } + + /** Reply start time as ISO 8601 or Unix seconds. */ + fun sinceTime(sinceTime: SinceTime) = sinceTime(JsonField.of(sinceTime)) + + /** + * Sets [Builder.sinceTime] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceTime] with a well-typed [SinceTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sinceTime(sinceTime: JsonField) = apply { this.sinceTime = sinceTime } + + /** Alias for calling [sinceTime] with `SinceTime.ofOffsetDate(offsetDate)`. */ + fun sinceTime(offsetDate: OffsetDateTime) = + sinceTime(SinceTime.ofOffsetDate(offsetDate)) + + /** Alias for calling [sinceTime] with `SinceTime.ofInteger(integer)`. */ + fun sinceTime(integer: Long) = sinceTime(SinceTime.ofInteger(integer)) + + /** Reply result order. */ + fun sort(sort: Sort) = sort(JsonField.of(sort)) + + /** + * Sets [Builder.sort] to an arbitrary JSON value. + * + * You should usually call [Builder.sort] with a well-typed [Sort] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sort(sort: JsonField) = apply { this.sort = sort } + + /** Match the source application. */ + fun source(source: String) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Resume one reply target from this cursor. */ + fun startCursor(startCursor: String) = startCursor(JsonField.of(startCursor)) + + /** + * Sets [Builder.startCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.startCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startCursor(startCursor: JsonField) = apply { + this.startCursor = startCursor + } + + /** Required for community_post_extractor & community_search. */ + fun targetCommunityId(targetCommunityId: String) = + targetCommunityId(JsonField.of(targetCommunityId)) + + /** + * Sets [Builder.targetCommunityId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetCommunityId(targetCommunityId: JsonField) = apply { + this.targetCommunityId = targetCommunityId + } + + /** Community IDs processed as one collection job. */ + fun targetCommunityIds(targetCommunityIds: List) = + targetCommunityIds(JsonField.of(targetCommunityIds)) + + /** + * Sets [Builder.targetCommunityIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetCommunityIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetCommunityIds(targetCommunityIds: JsonField>) = apply { + this.targetCommunityIds = targetCommunityIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetCommunityIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetCommunityId(targetCommunityId: String) = apply { + targetCommunityIds = + (targetCommunityIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetCommunityIds", it).add(targetCommunityId) + } + } + + /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ + fun targetListId(targetListId: String) = targetListId(JsonField.of(targetListId)) + + /** + * Sets [Builder.targetListId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetListId(targetListId: JsonField) = apply { + this.targetListId = targetListId + } + + /** List IDs processed as one collection job. */ + fun targetListIds(targetListIds: List) = + targetListIds(JsonField.of(targetListIds)) + + /** + * Sets [Builder.targetListIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetListIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetListIds(targetListIds: JsonField>) = apply { + this.targetListIds = targetListIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetListIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetListId(targetListId: String) = apply { + targetListIds = + (targetListIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetListIds", it).add(targetListId) + } + } + + /** Mixed targets auto-routed within one job. */ + fun targets(targets: List) = targets(JsonField.of(targets)) + + /** + * Sets [Builder.targets] to an arbitrary JSON value. + * + * You should usually call [Builder.targets] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targets(targets: JsonField>) = apply { + this.targets = targets.map { it.toMutableList() } + } + + /** + * Adds a single [Target] to [targets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTarget(target: Target) = apply { + targets = + (targets ?: JsonField.of(mutableListOf())).also { + checkKnown("targets", it).add(target) + } + } + + /** Alias for calling [addTarget] with `Target.ofString(string)`. */ + fun addTarget(string: String) = addTarget(Target.ofString(string)) + + /** Alias for calling [addTarget] with `Target.ofUnionMember1(unionMember1)`. */ + fun addTarget(unionMember1: Target.UnionMember1) = + addTarget(Target.ofUnionMember1(unionMember1)) + + /** Required for space_explorer. */ + fun targetSpaceId(targetSpaceId: String) = targetSpaceId(JsonField.of(targetSpaceId)) + + /** + * Sets [Builder.targetSpaceId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetSpaceId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetSpaceId(targetSpaceId: JsonField) = apply { + this.targetSpaceId = targetSpaceId + } + + fun targetTweetId(targetTweetId: String) = targetTweetId(JsonField.of(targetTweetId)) + + /** + * Sets [Builder.targetTweetId] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetTweetId(targetTweetId: JsonField) = apply { + this.targetTweetId = targetTweetId + } + + /** Tweet IDs processed as one collection job. */ + fun targetTweetIds(targetTweetIds: List) = + targetTweetIds(JsonField.of(targetTweetIds)) + + /** + * Sets [Builder.targetTweetIds] to an arbitrary JSON value. + * + * You should usually call [Builder.targetTweetIds] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetTweetIds(targetTweetIds: JsonField>) = apply { + this.targetTweetIds = targetTweetIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetTweetIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetTweetId(targetTweetId: String) = apply { + targetTweetIds = + (targetTweetIds ?: JsonField.of(mutableListOf())).also { + checkKnown("targetTweetIds", it).add(targetTweetId) + } + } + + fun targetUsername(targetUsername: String) = + targetUsername(JsonField.of(targetUsername)) + + /** + * Sets [Builder.targetUsername] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsername] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun targetUsername(targetUsername: JsonField) = apply { + this.targetUsername = targetUsername + } + + /** Usernames processed as one collection job. */ + fun targetUsernames(targetUsernames: List) = + targetUsernames(JsonField.of(targetUsernames)) + + /** + * Sets [Builder.targetUsernames] to an arbitrary JSON value. + * + * You should usually call [Builder.targetUsernames] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun targetUsernames(targetUsernames: JsonField>) = apply { + this.targetUsernames = targetUsernames.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [targetUsernames]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTargetUsername(targetUsername: String) = apply { + targetUsernames = + (targetUsernames ?: JsonField.of(mutableListOf())).also { + checkKnown("targetUsernames", it).add(targetUsername) + } + } + + /** Filter replies sent to a username (tweet_search_extractor) */ + fun toUser(toUser: String) = toUser(JsonField.of(toUser)) + + /** + * Sets [Builder.toUser] to an arbitrary JSON value. + * + * You should usually call [Builder.toUser] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun toUser(toUser: JsonField) = apply { this.toUser = toUser } + + /** End date YYYY-MM-DD (tweet_search_extractor) */ + fun untilDate(untilDate: LocalDate) = untilDate(JsonField.of(untilDate)) + + /** + * Sets [Builder.untilDate] to an arbitrary JSON value. + * + * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun untilDate(untilDate: JsonField) = apply { this.untilDate = untilDate } + + /** Reply end time as ISO 8601 or Unix seconds. */ + fun untilTime(untilTime: UntilTime) = untilTime(JsonField.of(untilTime)) + + /** + * Sets [Builder.untilTime] to an arbitrary JSON value. + * + * You should usually call [Builder.untilTime] with a well-typed [UntilTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun untilTime(untilTime: JsonField) = apply { this.untilTime = untilTime } + + /** Alias for calling [untilTime] with `UntilTime.ofOffsetDate(offsetDate)`. */ + fun untilTime(offsetDate: OffsetDateTime) = + untilTime(UntilTime.ofOffsetDate(offsetDate)) + + /** Alias for calling [untilTime] with `UntilTime.ofInteger(integer)`. */ + fun untilTime(integer: Long) = untilTime(UntilTime.ofInteger(integer)) + + /** URL substring or domain filter (tweet_search_extractor) */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun url(url: JsonField) = apply { this.url = url } + + /** Required username text. */ + fun usernameContains(usernameContains: String) = + usernameContains(JsonField.of(usernameContains)) + + /** + * Sets [Builder.usernameContains] to an arbitrary JSON value. + * + * You should usually call [Builder.usernameContains] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun usernameContains(usernameContains: JsonField) = apply { + this.usernameContains = usernameContains + } + + /** Only verified authors (tweet_search_extractor) */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(JsonField.of(verifiedOnly)) + + /** + * Sets [Builder.verifiedOnly] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun verifiedOnly(verifiedOnly: JsonField) = apply { + this.verifiedOnly = verifiedOnly + } + + /** Exact profile verification type. */ + fun verifiedType(verifiedType: String) = verifiedType(JsonField.of(verifiedType)) + + /** + * Sets [Builder.verifiedType] to an arbitrary JSON value. + * + * You should usually call [Builder.verifiedType] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun verifiedType(verifiedType: JsonField) = apply { + this.verifiedType = verifiedType + } + + /** Set the radius for the near filter. */ + fun within(within: String) = within(JsonField.of(within)) + + /** + * Sets [Builder.within] to an arbitrary JSON value. + * + * You should usually call [Builder.within] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun within(within: JsonField) = apply { this.within = within } + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String) = withinTime(JsonField.of(withinTime)) + + /** + * Sets [Builder.withinTime] to an arbitrary JSON value. + * + * You should usually call [Builder.withinTime] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun withinTime(withinTime: JsonField) = apply { this.withinTime = withinTime } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .toolType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("toolType", toolType), + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + (relationTargets ?: JsonMissing.of()).map { it.toImmutable() }, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + (searchQueries ?: JsonMissing.of()).map { it.toImmutable() }, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + (targetCommunityIds ?: JsonMissing.of()).map { it.toImmutable() }, + targetListId, + (targetListIds ?: JsonMissing.of()).map { it.toImmutable() }, + (targets ?: JsonMissing.of()).map { it.toImmutable() }, + targetSpaceId, + targetTweetId, + (targetTweetIds ?: JsonMissing.of()).map { it.toImmutable() }, + targetUsername, + (targetUsernames ?: JsonMissing.of()).map { it.toImmutable() }, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + toolType().validate() + advancedQuery() + anyWords() + bioContains() + blueVerifiedOnly() + boundingBox() + cardName() + cashtags() + collectionStrategy().ifPresent { it.validate() } + conversationId() + dedupeAcrossTargets() + dedupeMode().ifPresent { it.validate() } + exactPhrase() + excludeOriginalAuthor() + excludeSource() + excludeWords() + fromUser() + geocode() + hashtags() + hasLocation() + hasMediaOnly() + hasWebsite() + includeOriginalPost() + includeSearchTerms() + includeTargetMetadata() + inReplyToTweetId() + language() + listId() + locationContains() + maxDepth() + maxFollowers() + maxFollowing() + maxId() + maxItemsPerTarget() + maxLikes() + maxPagesPerTarget() + maxPosts() + maxQuotes() + maxReplies() + maxRetweets() + mediaType().ifPresent { it.validate() } + mentioning() + minAccountAgeDays() + minBookmarks() + minFaves() + minFollowers() + minFollowing() + minPosts() + minQuotes() + minReplies() + minRetweets() + minViews() + nativeRetweets() + near() + news() + overlapMode() + place() + placeCountry() + pointRadius() + queryType().ifPresent { it.validate() } + quotes().ifPresent { it.validate() } + quotesOfTweetId() + relationTargets().ifPresent { it.forEach { it.validate() } } + replies().ifPresent { it.validate() } + resultsLimit() + retweets().ifPresent { it.validate() } + retweetsOfTweetId() + safe() + scope().ifPresent { it.validate() } + searchQueries() + searchQuery() + sinceDate() + sinceId() + sinceTime().ifPresent { it.validate() } + sort().ifPresent { it.validate() } + source() + startCursor() + targetCommunityId() + targetCommunityIds() + targetListId() + targetListIds() + targets().ifPresent { it.forEach { it.validate() } } + targetSpaceId() + targetTweetId() + targetTweetIds() + targetUsername() + targetUsernames() + toUser() + untilDate() + untilTime().ifPresent { it.validate() } + url() + usernameContains() + verifiedOnly() + verifiedType() + within() + withinTime() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (toolType.asKnown().getOrNull()?.validity() ?: 0) + + (if (advancedQuery.asKnown().isPresent) 1 else 0) + + (if (anyWords.asKnown().isPresent) 1 else 0) + + (if (bioContains.asKnown().isPresent) 1 else 0) + + (if (blueVerifiedOnly.asKnown().isPresent) 1 else 0) + + (if (boundingBox.asKnown().isPresent) 1 else 0) + + (if (cardName.asKnown().isPresent) 1 else 0) + + (if (cashtags.asKnown().isPresent) 1 else 0) + + (collectionStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (if (conversationId.asKnown().isPresent) 1 else 0) + + (if (dedupeAcrossTargets.asKnown().isPresent) 1 else 0) + + (dedupeMode.asKnown().getOrNull()?.validity() ?: 0) + + (if (exactPhrase.asKnown().isPresent) 1 else 0) + + (if (excludeOriginalAuthor.asKnown().isPresent) 1 else 0) + + (if (excludeSource.asKnown().isPresent) 1 else 0) + + (if (excludeWords.asKnown().isPresent) 1 else 0) + + (if (fromUser.asKnown().isPresent) 1 else 0) + + (if (geocode.asKnown().isPresent) 1 else 0) + + (if (hashtags.asKnown().isPresent) 1 else 0) + + (if (hasLocation.asKnown().isPresent) 1 else 0) + + (if (hasMediaOnly.asKnown().isPresent) 1 else 0) + + (if (hasWebsite.asKnown().isPresent) 1 else 0) + + (if (includeOriginalPost.asKnown().isPresent) 1 else 0) + + (if (includeSearchTerms.asKnown().isPresent) 1 else 0) + + (if (includeTargetMetadata.asKnown().isPresent) 1 else 0) + + (if (inReplyToTweetId.asKnown().isPresent) 1 else 0) + + (if (language.asKnown().isPresent) 1 else 0) + + (if (listId.asKnown().isPresent) 1 else 0) + + (if (locationContains.asKnown().isPresent) 1 else 0) + + (if (maxDepth.asKnown().isPresent) 1 else 0) + + (if (maxFollowers.asKnown().isPresent) 1 else 0) + + (if (maxFollowing.asKnown().isPresent) 1 else 0) + + (if (maxId.asKnown().isPresent) 1 else 0) + + (if (maxItemsPerTarget.asKnown().isPresent) 1 else 0) + + (if (maxLikes.asKnown().isPresent) 1 else 0) + + (if (maxPagesPerTarget.asKnown().isPresent) 1 else 0) + + (if (maxPosts.asKnown().isPresent) 1 else 0) + + (if (maxQuotes.asKnown().isPresent) 1 else 0) + + (if (maxReplies.asKnown().isPresent) 1 else 0) + + (if (maxRetweets.asKnown().isPresent) 1 else 0) + + (mediaType.asKnown().getOrNull()?.validity() ?: 0) + + (if (mentioning.asKnown().isPresent) 1 else 0) + + (if (minAccountAgeDays.asKnown().isPresent) 1 else 0) + + (if (minBookmarks.asKnown().isPresent) 1 else 0) + + (if (minFaves.asKnown().isPresent) 1 else 0) + + (if (minFollowers.asKnown().isPresent) 1 else 0) + + (if (minFollowing.asKnown().isPresent) 1 else 0) + + (if (minPosts.asKnown().isPresent) 1 else 0) + + (if (minQuotes.asKnown().isPresent) 1 else 0) + + (if (minReplies.asKnown().isPresent) 1 else 0) + + (if (minRetweets.asKnown().isPresent) 1 else 0) + + (if (minViews.asKnown().isPresent) 1 else 0) + + (if (nativeRetweets.asKnown().isPresent) 1 else 0) + + (if (near.asKnown().isPresent) 1 else 0) + + (if (news.asKnown().isPresent) 1 else 0) + + (if (overlapMode.asKnown().isPresent) 1 else 0) + + (if (place.asKnown().isPresent) 1 else 0) + + (if (placeCountry.asKnown().isPresent) 1 else 0) + + (if (pointRadius.asKnown().isPresent) 1 else 0) + + (queryType.asKnown().getOrNull()?.validity() ?: 0) + + (quotes.asKnown().getOrNull()?.validity() ?: 0) + + (if (quotesOfTweetId.asKnown().isPresent) 1 else 0) + + (relationTargets.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (replies.asKnown().getOrNull()?.validity() ?: 0) + + (if (resultsLimit.asKnown().isPresent) 1 else 0) + + (retweets.asKnown().getOrNull()?.validity() ?: 0) + + (if (retweetsOfTweetId.asKnown().isPresent) 1 else 0) + + (if (safe.asKnown().isPresent) 1 else 0) + + (scope.asKnown().getOrNull()?.validity() ?: 0) + + (searchQueries.asKnown().getOrNull()?.size ?: 0) + + (if (searchQuery.asKnown().isPresent) 1 else 0) + + (if (sinceDate.asKnown().isPresent) 1 else 0) + + (if (sinceId.asKnown().isPresent) 1 else 0) + + (sinceTime.asKnown().getOrNull()?.validity() ?: 0) + + (sort.asKnown().getOrNull()?.validity() ?: 0) + + (if (source.asKnown().isPresent) 1 else 0) + + (if (startCursor.asKnown().isPresent) 1 else 0) + + (if (targetCommunityId.asKnown().isPresent) 1 else 0) + + (targetCommunityIds.asKnown().getOrNull()?.size ?: 0) + + (if (targetListId.asKnown().isPresent) 1 else 0) + + (targetListIds.asKnown().getOrNull()?.size ?: 0) + + (targets.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (targetSpaceId.asKnown().isPresent) 1 else 0) + + (if (targetTweetId.asKnown().isPresent) 1 else 0) + + (targetTweetIds.asKnown().getOrNull()?.size ?: 0) + + (if (targetUsername.asKnown().isPresent) 1 else 0) + + (targetUsernames.asKnown().getOrNull()?.size ?: 0) + + (if (toUser.asKnown().isPresent) 1 else 0) + + (if (untilDate.asKnown().isPresent) 1 else 0) + + (untilTime.asKnown().getOrNull()?.validity() ?: 0) + + (if (url.asKnown().isPresent) 1 else 0) + + (if (usernameContains.asKnown().isPresent) 1 else 0) + + (if (verifiedOnly.asKnown().isPresent) 1 else 0) + + (if (verifiedType.asKnown().isPresent) 1 else 0) + + (if (within.asKnown().isPresent) 1 else 0) + + (if (withinTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + toolType == other.toolType && + advancedQuery == other.advancedQuery && + anyWords == other.anyWords && + bioContains == other.bioContains && + blueVerifiedOnly == other.blueVerifiedOnly && + boundingBox == other.boundingBox && + cardName == other.cardName && + cashtags == other.cashtags && + collectionStrategy == other.collectionStrategy && + conversationId == other.conversationId && + dedupeAcrossTargets == other.dedupeAcrossTargets && + dedupeMode == other.dedupeMode && + exactPhrase == other.exactPhrase && + excludeOriginalAuthor == other.excludeOriginalAuthor && + excludeSource == other.excludeSource && + excludeWords == other.excludeWords && + fromUser == other.fromUser && + geocode == other.geocode && + hashtags == other.hashtags && + hasLocation == other.hasLocation && + hasMediaOnly == other.hasMediaOnly && + hasWebsite == other.hasWebsite && + includeOriginalPost == other.includeOriginalPost && + includeSearchTerms == other.includeSearchTerms && + includeTargetMetadata == other.includeTargetMetadata && + inReplyToTweetId == other.inReplyToTweetId && + language == other.language && + listId == other.listId && + locationContains == other.locationContains && + maxDepth == other.maxDepth && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxId == other.maxId && + maxItemsPerTarget == other.maxItemsPerTarget && + maxLikes == other.maxLikes && + maxPagesPerTarget == other.maxPagesPerTarget && + maxPosts == other.maxPosts && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && + mediaType == other.mediaType && + mentioning == other.mentioning && + minAccountAgeDays == other.minAccountAgeDays && + minBookmarks == other.minBookmarks && + minFaves == other.minFaves && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minPosts == other.minPosts && + minQuotes == other.minQuotes && + minReplies == other.minReplies && + minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && + overlapMode == other.overlapMode && + place == other.place && + placeCountry == other.placeCountry && + pointRadius == other.pointRadius && + queryType == other.queryType && + quotes == other.quotes && + quotesOfTweetId == other.quotesOfTweetId && + relationTargets == other.relationTargets && + replies == other.replies && + resultsLimit == other.resultsLimit && + retweets == other.retweets && + retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && + scope == other.scope && + searchQueries == other.searchQueries && + searchQuery == other.searchQuery && + sinceDate == other.sinceDate && + sinceId == other.sinceId && + sinceTime == other.sinceTime && + sort == other.sort && + source == other.source && + startCursor == other.startCursor && + targetCommunityId == other.targetCommunityId && + targetCommunityIds == other.targetCommunityIds && + targetListId == other.targetListId && + targetListIds == other.targetListIds && + targets == other.targets && + targetSpaceId == other.targetSpaceId && + targetTweetId == other.targetTweetId && + targetTweetIds == other.targetTweetIds && + targetUsername == other.targetUsername && + targetUsernames == other.targetUsernames && + toUser == other.toUser && + untilDate == other.untilDate && + untilTime == other.untilTime && + url == other.url && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && + within == other.within && + withinTime == other.withinTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + toolType, + advancedQuery, + anyWords, + bioContains, + blueVerifiedOnly, + boundingBox, + cardName, + cashtags, + collectionStrategy, + conversationId, + dedupeAcrossTargets, + dedupeMode, + exactPhrase, + excludeOriginalAuthor, + excludeSource, + excludeWords, + fromUser, + geocode, + hashtags, + hasLocation, + hasMediaOnly, + hasWebsite, + includeOriginalPost, + includeSearchTerms, + includeTargetMetadata, + inReplyToTweetId, + language, + listId, + locationContains, + maxDepth, + maxFollowers, + maxFollowing, + maxId, + maxItemsPerTarget, + maxLikes, + maxPagesPerTarget, + maxPosts, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minAccountAgeDays, + minBookmarks, + minFaves, + minFollowers, + minFollowing, + minPosts, + minQuotes, + minReplies, + minRetweets, + minViews, + nativeRetweets, + near, + news, + overlapMode, + place, + placeCountry, + pointRadius, + queryType, + quotes, + quotesOfTweetId, + relationTargets, + replies, + resultsLimit, + retweets, + retweetsOfTweetId, + safe, + scope, + searchQueries, + searchQuery, + sinceDate, + sinceId, + sinceTime, + sort, + source, + startCursor, + targetCommunityId, + targetCommunityIds, + targetListId, + targetListIds, + targets, + targetSpaceId, + targetTweetId, + targetTweetIds, + targetUsername, + targetUsernames, + toUser, + untilDate, + untilTime, + url, + usernameContains, + verifiedOnly, + verifiedType, + within, + withinTime, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{toolType=$toolType, advancedQuery=$advancedQuery, anyWords=$anyWords, bioContains=$bioContains, blueVerifiedOnly=$blueVerifiedOnly, boundingBox=$boundingBox, cardName=$cardName, cashtags=$cashtags, collectionStrategy=$collectionStrategy, conversationId=$conversationId, dedupeAcrossTargets=$dedupeAcrossTargets, dedupeMode=$dedupeMode, exactPhrase=$exactPhrase, excludeOriginalAuthor=$excludeOriginalAuthor, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, hasLocation=$hasLocation, hasMediaOnly=$hasMediaOnly, hasWebsite=$hasWebsite, includeOriginalPost=$includeOriginalPost, includeSearchTerms=$includeSearchTerms, includeTargetMetadata=$includeTargetMetadata, inReplyToTweetId=$inReplyToTweetId, language=$language, listId=$listId, locationContains=$locationContains, maxDepth=$maxDepth, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxId=$maxId, maxItemsPerTarget=$maxItemsPerTarget, maxLikes=$maxLikes, maxPagesPerTarget=$maxPagesPerTarget, maxPosts=$maxPosts, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minAccountAgeDays=$minAccountAgeDays, minBookmarks=$minBookmarks, minFaves=$minFaves, minFollowers=$minFollowers, minFollowing=$minFollowing, minPosts=$minPosts, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, overlapMode=$overlapMode, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, queryType=$queryType, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, relationTargets=$relationTargets, replies=$replies, resultsLimit=$resultsLimit, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, scope=$scope, searchQueries=$searchQueries, searchQuery=$searchQuery, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, sort=$sort, source=$source, startCursor=$startCursor, targetCommunityId=$targetCommunityId, targetCommunityIds=$targetCommunityIds, targetListId=$targetListId, targetListIds=$targetListIds, targets=$targets, targetSpaceId=$targetSpaceId, targetTweetId=$targetTweetId, targetTweetIds=$targetTweetIds, targetUsername=$targetUsername, targetUsernames=$targetUsernames, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, within=$within, withinTime=$withinTime, additionalProperties=$additionalProperties}" + } + + /** Identifier for the extraction tool used to run a job. */ + class ToolType @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val ARTICLE_EXTRACTOR = of("article_extractor") + + @JvmField val COMMUNITY_EXTRACTOR = of("community_extractor") + + @JvmField val COMMUNITY_MODERATOR_EXPLORER = of("community_moderator_explorer") + + @JvmField val COMMUNITY_POST_EXTRACTOR = of("community_post_extractor") + + @JvmField val COMMUNITY_SEARCH = of("community_search") + + @JvmField val FAVORITERS = of("favoriters") + + @JvmField val FOLLOWER_EXPLORER = of("follower_explorer") + + @JvmField val FOLLOWING_EXPLORER = of("following_explorer") + + @JvmField val LIST_FOLLOWER_EXPLORER = of("list_follower_explorer") + + @JvmField val LIST_MEMBER_EXTRACTOR = of("list_member_extractor") + + @JvmField val LIST_POST_EXTRACTOR = of("list_post_extractor") + + @JvmField val MENTION_EXTRACTOR = of("mention_extractor") + + @JvmField val PEOPLE_SEARCH = of("people_search") + + @JvmField val POST_EXTRACTOR = of("post_extractor") + + @JvmField val QUOTE_EXTRACTOR = of("quote_extractor") + + @JvmField val REPLY_EXTRACTOR = of("reply_extractor") + + @JvmField val REPOST_EXTRACTOR = of("repost_extractor") + + @JvmField val SPACE_EXPLORER = of("space_explorer") + + @JvmField val THREAD_EXTRACTOR = of("thread_extractor") + + @JvmField val TWEET_SEARCH_EXTRACTOR = of("tweet_search_extractor") + + @JvmField val USER_LIKES = of("user_likes") + + @JvmField val USER_MEDIA = of("user_media") + + @JvmField val VERIFIED_FOLLOWER_EXPLORER = of("verified_follower_explorer") + + @JvmStatic fun of(value: String) = ToolType(JsonField.of(value)) + } + + /** An enum containing [ToolType]'s known values. */ + enum class Known { + ARTICLE_EXTRACTOR, + COMMUNITY_EXTRACTOR, + COMMUNITY_MODERATOR_EXPLORER, + COMMUNITY_POST_EXTRACTOR, + COMMUNITY_SEARCH, + FAVORITERS, + FOLLOWER_EXPLORER, + FOLLOWING_EXPLORER, + LIST_FOLLOWER_EXPLORER, + LIST_MEMBER_EXTRACTOR, + LIST_POST_EXTRACTOR, + MENTION_EXTRACTOR, + PEOPLE_SEARCH, + POST_EXTRACTOR, + QUOTE_EXTRACTOR, + REPLY_EXTRACTOR, + REPOST_EXTRACTOR, + SPACE_EXPLORER, + THREAD_EXTRACTOR, + TWEET_SEARCH_EXTRACTOR, + USER_LIKES, + USER_MEDIA, + VERIFIED_FOLLOWER_EXPLORER, + } + + /** + * An enum containing [ToolType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ToolType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ARTICLE_EXTRACTOR, + COMMUNITY_EXTRACTOR, + COMMUNITY_MODERATOR_EXPLORER, + COMMUNITY_POST_EXTRACTOR, + COMMUNITY_SEARCH, + FAVORITERS, + FOLLOWER_EXPLORER, + FOLLOWING_EXPLORER, + LIST_FOLLOWER_EXPLORER, + LIST_MEMBER_EXTRACTOR, + LIST_POST_EXTRACTOR, + MENTION_EXTRACTOR, + PEOPLE_SEARCH, + POST_EXTRACTOR, + QUOTE_EXTRACTOR, + REPLY_EXTRACTOR, + REPOST_EXTRACTOR, + SPACE_EXPLORER, + THREAD_EXTRACTOR, + TWEET_SEARCH_EXTRACTOR, + USER_LIKES, + USER_MEDIA, + VERIFIED_FOLLOWER_EXPLORER, + /** An enum member indicating that [ToolType] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ARTICLE_EXTRACTOR -> Value.ARTICLE_EXTRACTOR + COMMUNITY_EXTRACTOR -> Value.COMMUNITY_EXTRACTOR + COMMUNITY_MODERATOR_EXPLORER -> Value.COMMUNITY_MODERATOR_EXPLORER + COMMUNITY_POST_EXTRACTOR -> Value.COMMUNITY_POST_EXTRACTOR + COMMUNITY_SEARCH -> Value.COMMUNITY_SEARCH + FAVORITERS -> Value.FAVORITERS + FOLLOWER_EXPLORER -> Value.FOLLOWER_EXPLORER + FOLLOWING_EXPLORER -> Value.FOLLOWING_EXPLORER + LIST_FOLLOWER_EXPLORER -> Value.LIST_FOLLOWER_EXPLORER + LIST_MEMBER_EXTRACTOR -> Value.LIST_MEMBER_EXTRACTOR + LIST_POST_EXTRACTOR -> Value.LIST_POST_EXTRACTOR + MENTION_EXTRACTOR -> Value.MENTION_EXTRACTOR + PEOPLE_SEARCH -> Value.PEOPLE_SEARCH + POST_EXTRACTOR -> Value.POST_EXTRACTOR + QUOTE_EXTRACTOR -> Value.QUOTE_EXTRACTOR + REPLY_EXTRACTOR -> Value.REPLY_EXTRACTOR + REPOST_EXTRACTOR -> Value.REPOST_EXTRACTOR + SPACE_EXPLORER -> Value.SPACE_EXPLORER + THREAD_EXTRACTOR -> Value.THREAD_EXTRACTOR + TWEET_SEARCH_EXTRACTOR -> Value.TWEET_SEARCH_EXTRACTOR + USER_LIKES -> Value.USER_LIKES + USER_MEDIA -> Value.USER_MEDIA + VERIFIED_FOLLOWER_EXPLORER -> Value.VERIFIED_FOLLOWER_EXPLORER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + ARTICLE_EXTRACTOR -> Known.ARTICLE_EXTRACTOR + COMMUNITY_EXTRACTOR -> Known.COMMUNITY_EXTRACTOR + COMMUNITY_MODERATOR_EXPLORER -> Known.COMMUNITY_MODERATOR_EXPLORER + COMMUNITY_POST_EXTRACTOR -> Known.COMMUNITY_POST_EXTRACTOR + COMMUNITY_SEARCH -> Known.COMMUNITY_SEARCH + FAVORITERS -> Known.FAVORITERS + FOLLOWER_EXPLORER -> Known.FOLLOWER_EXPLORER + FOLLOWING_EXPLORER -> Known.FOLLOWING_EXPLORER + LIST_FOLLOWER_EXPLORER -> Known.LIST_FOLLOWER_EXPLORER + LIST_MEMBER_EXTRACTOR -> Known.LIST_MEMBER_EXTRACTOR + LIST_POST_EXTRACTOR -> Known.LIST_POST_EXTRACTOR + MENTION_EXTRACTOR -> Known.MENTION_EXTRACTOR + PEOPLE_SEARCH -> Known.PEOPLE_SEARCH + POST_EXTRACTOR -> Known.POST_EXTRACTOR + QUOTE_EXTRACTOR -> Known.QUOTE_EXTRACTOR + REPLY_EXTRACTOR -> Known.REPLY_EXTRACTOR + REPOST_EXTRACTOR -> Known.REPOST_EXTRACTOR + SPACE_EXPLORER -> Known.SPACE_EXPLORER + THREAD_EXTRACTOR -> Known.THREAD_EXTRACTOR + TWEET_SEARCH_EXTRACTOR -> Known.TWEET_SEARCH_EXTRACTOR + USER_LIKES -> Known.USER_LIKES + USER_MEDIA -> Known.USER_MEDIA + VERIFIED_FOLLOWER_EXPLORER -> Known.VERIFIED_FOLLOWER_EXPLORER + else -> throw XTwitterScraperInvalidDataException("Unknown ToolType: $value") } - /** Exact phrase to match (tweet_search_extractor) */ - fun exactPhrase(exactPhrase: String) = exactPhrase(JsonField.of(exactPhrase)) + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): ToolType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ToolType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Reply collection strategy. */ + class CollectionStrategy + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val AUTO = of("auto") + + @JvmField val COMPLETE = of("complete") + + @JvmField val DIRECT = of("direct") + + @JvmField val SEARCH = of("search") + + @JvmField val THREAD = of("thread") + @JvmStatic fun of(value: String) = CollectionStrategy(JsonField.of(value)) + } + + /** An enum containing [CollectionStrategy]'s known values. */ + enum class Known { + AUTO, + COMPLETE, + DIRECT, + SEARCH, + THREAD, + } + + /** + * An enum containing [CollectionStrategy]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CollectionStrategy] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + AUTO, + COMPLETE, + DIRECT, + SEARCH, + THREAD, /** - * Sets [Builder.exactPhrase] to an arbitrary JSON value. - * - * You should usually call [Builder.exactPhrase] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [CollectionStrategy] was instantiated with an unknown + * value. */ - fun exactPhrase(exactPhrase: JsonField) = apply { - this.exactPhrase = exactPhrase + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + AUTO -> Value.AUTO + COMPLETE -> Value.COMPLETE + DIRECT -> Value.DIRECT + SEARCH -> Value.SEARCH + THREAD -> Value.THREAD + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + AUTO -> Known.AUTO + COMPLETE -> Known.COMPLETE + DIRECT -> Known.DIRECT + SEARCH -> Known.SEARCH + THREAD -> Known.THREAD + else -> + throw XTwitterScraperInvalidDataException("Unknown CollectionStrategy: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): CollectionStrategy = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + return other is CollectionStrategy && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Keep target duplicates, first rows, or merged overlap. */ + class DedupeMode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val NONE = of("none") + + @JvmField val FIRST = of("first") + + @JvmField val MERGE = of("merge") + + @JvmStatic fun of(value: String) = DedupeMode(JsonField.of(value)) + } + + /** An enum containing [DedupeMode]'s known values. */ + enum class Known { + NONE, + FIRST, + MERGE, + } + + /** + * An enum containing [DedupeMode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DedupeMode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + NONE, + FIRST, + MERGE, /** - * Words or quoted phrases to exclude. Separate with spaces, commas, or lines. - * (tweet_search_extractor) + * An enum member indicating that [DedupeMode] was instantiated with an unknown value. */ - fun excludeWords(excludeWords: String) = excludeWords(JsonField.of(excludeWords)) + _UNKNOWN, + } - /** - * Sets [Builder.excludeWords] to an arbitrary JSON value. - * - * You should usually call [Builder.excludeWords] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun excludeWords(excludeWords: JsonField) = apply { - this.excludeWords = excludeWords + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + NONE -> Value.NONE + FIRST -> Value.FIRST + MERGE -> Value.MERGE + else -> Value._UNKNOWN } - /** Filter by author username (tweet_search_extractor) */ - fun fromUser(fromUser: String) = fromUser(JsonField.of(fromUser)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + NONE -> Known.NONE + FIRST -> Known.FIRST + MERGE -> Known.MERGE + else -> throw XTwitterScraperInvalidDataException("Unknown DedupeMode: $value") + } - /** - * Sets [Builder.fromUser] to an arbitrary JSON value. - * - * You should usually call [Builder.fromUser] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun fromUser(fromUser: JsonField) = apply { this.fromUser = fromUser } + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - /** Hashtags separated by spaces, commas, or lines. (tweet_search_extractor) */ - fun hashtags(hashtags: String) = hashtags(JsonField.of(hashtags)) + private var validated: Boolean = false - /** - * Sets [Builder.hashtags] to an arbitrary JSON value. - * - * You should usually call [Builder.hashtags] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun hashtags(hashtags: JsonField) = apply { this.hashtags = hashtags } + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): DedupeMode = apply { + if (validated) { + return@apply + } - /** Only replies to this tweet ID (tweet_search_extractor) */ - fun inReplyToTweetId(inReplyToTweetId: String) = - inReplyToTweetId(JsonField.of(inReplyToTweetId)) + known() + validated = true + } - /** - * Sets [Builder.inReplyToTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.inReplyToTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun inReplyToTweetId(inReplyToTweetId: JsonField) = apply { - this.inReplyToTweetId = inReplyToTweetId + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } - /** Language code filter (tweet_search_extractor) */ - fun language(language: String) = language(JsonField.of(language)) - - /** - * Sets [Builder.language] to an arbitrary JSON value. - * - * You should usually call [Builder.language] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun language(language: JsonField) = apply { this.language = language } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** Search within a list ID (tweet_search_extractor) */ - fun listId(listId: String) = listId(JsonField.of(listId)) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** - * Sets [Builder.listId] to an arbitrary JSON value. - * - * You should usually call [Builder.listId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun listId(listId: JsonField) = apply { this.listId = listId } + return other is DedupeMode && value == other.value + } - /** Media type filter (tweet_search_extractor) */ - fun mediaType(mediaType: MediaType) = mediaType(JsonField.of(mediaType)) + override fun hashCode() = value.hashCode() - /** - * Sets [Builder.mediaType] to an arbitrary JSON value. - * - * You should usually call [Builder.mediaType] with a well-typed [MediaType] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun mediaType(mediaType: JsonField) = apply { this.mediaType = mediaType } + override fun toString() = value.toString() + } - /** Filter tweets mentioning a username (tweet_search_extractor) */ - fun mentioning(mentioning: String) = mentioning(JsonField.of(mentioning)) + /** Media type filter (tweet_search_extractor) */ + class MediaType @JsonCreator private constructor(private val value: JsonField) : Enum { - /** - * Sets [Builder.mentioning] to an arbitrary JSON value. - * - * You should usually call [Builder.mentioning] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun mentioning(mentioning: JsonField) = apply { this.mentioning = mentioning } + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** Minimum likes threshold (tweet_search_extractor) */ - fun minFaves(minFaves: Long) = minFaves(JsonField.of(minFaves)) + companion object { - /** - * Sets [Builder.minFaves] to an arbitrary JSON value. - * - * You should usually call [Builder.minFaves] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minFaves(minFaves: JsonField) = apply { this.minFaves = minFaves } + @JvmField val IMAGES = of("images") - /** Minimum quote count threshold (tweet_search_extractor) */ - fun minQuotes(minQuotes: Long) = minQuotes(JsonField.of(minQuotes)) + @JvmField val VIDEOS = of("videos") - /** - * Sets [Builder.minQuotes] to an arbitrary JSON value. - * - * You should usually call [Builder.minQuotes] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minQuotes(minQuotes: JsonField) = apply { this.minQuotes = minQuotes } + @JvmField val GIFS = of("gifs") - /** Minimum replies threshold (tweet_search_extractor) */ - fun minReplies(minReplies: Long) = minReplies(JsonField.of(minReplies)) + @JvmField val MEDIA = of("media") - /** - * Sets [Builder.minReplies] to an arbitrary JSON value. - * - * You should usually call [Builder.minReplies] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minReplies(minReplies: JsonField) = apply { this.minReplies = minReplies } + @JvmField val LINKS = of("links") - /** Minimum retweets threshold (tweet_search_extractor) */ - fun minRetweets(minRetweets: Long) = minRetweets(JsonField.of(minRetweets)) + @JvmField val NONE = of("none") - /** - * Sets [Builder.minRetweets] to an arbitrary JSON value. - * - * You should usually call [Builder.minRetweets] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun minRetweets(minRetweets: JsonField) = apply { this.minRetweets = minRetweets } + @JvmStatic fun of(value: String) = MediaType(JsonField.of(value)) + } - /** Search within a place ID (tweet_search_extractor) */ - fun place(place: String) = place(JsonField.of(place)) + /** An enum containing [MediaType]'s known values. */ + enum class Known { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, + } + /** + * An enum containing [MediaType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [MediaType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, /** - * Sets [Builder.place] to an arbitrary JSON value. - * - * You should usually call [Builder.place] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [MediaType] was instantiated with an unknown value. */ - fun place(place: JsonField) = apply { this.place = place } - - /** Search within a country code (tweet_search_extractor) */ - fun placeCountry(placeCountry: String) = placeCountry(JsonField.of(placeCountry)) + _UNKNOWN, + } - /** - * Sets [Builder.placeCountry] to an arbitrary JSON value. - * - * You should usually call [Builder.placeCountry] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun placeCountry(placeCountry: JsonField) = apply { - this.placeCountry = placeCountry + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IMAGES -> Value.IMAGES + VIDEOS -> Value.VIDEOS + GIFS -> Value.GIFS + MEDIA -> Value.MEDIA + LINKS -> Value.LINKS + NONE -> Value.NONE + else -> Value._UNKNOWN } - /** Geo point radius, e.g. -73.99 40.73 25mi (tweet_search_extractor) */ - fun pointRadius(pointRadius: String) = pointRadius(JsonField.of(pointRadius)) + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + IMAGES -> Known.IMAGES + VIDEOS -> Known.VIDEOS + GIFS -> Known.GIFS + MEDIA -> Known.MEDIA + LINKS -> Known.LINKS + NONE -> Known.NONE + else -> throw XTwitterScraperInvalidDataException("Unknown MediaType: $value") + } - /** - * Sets [Builder.pointRadius] to an arbitrary JSON value. - * - * You should usually call [Builder.pointRadius] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun pointRadius(pointRadius: JsonField) = apply { - this.pointRadius = pointRadius + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") } - /** Quote mode (tweet_search_extractor) */ - fun quotes(quotes: Quotes) = quotes(JsonField.of(quotes)) + private var validated: Boolean = false - /** - * Sets [Builder.quotes] to an arbitrary JSON value. - * - * You should usually call [Builder.quotes] with a well-typed [Quotes] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotes(quotes: JsonField) = apply { this.quotes = quotes } + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): MediaType = apply { + if (validated) { + return@apply + } - /** Only quotes of this tweet ID (tweet_search_extractor) */ - fun quotesOfTweetId(quotesOfTweetId: String) = - quotesOfTweetId(JsonField.of(quotesOfTweetId)) + known() + validated = true + } - /** - * Sets [Builder.quotesOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.quotesOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun quotesOfTweetId(quotesOfTweetId: JsonField) = apply { - this.quotesOfTweetId = quotesOfTweetId + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } - /** Reply mode (tweet_search_extractor) */ - fun replies(replies: Replies) = replies(JsonField.of(replies)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.replies] to an arbitrary JSON value. - * - * You should usually call [Builder.replies] with a well-typed [Replies] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun replies(replies: JsonField) = apply { this.replies = replies } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** - * Maximum number of results to extract. When set, the extraction stops after reaching - * this limit. - */ - fun resultsLimit(resultsLimit: Long) = resultsLimit(JsonField.of(resultsLimit)) + return other is MediaType && value == other.value + } - /** - * Sets [Builder.resultsLimit] to an arbitrary JSON value. - * - * You should usually call [Builder.resultsLimit] with a well-typed [Long] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun resultsLimit(resultsLimit: JsonField) = apply { - this.resultsLimit = resultsLimit - } + override fun hashCode() = value.hashCode() - /** Retweet mode (tweet_search_extractor) */ - fun retweets(retweets: Retweets) = retweets(JsonField.of(retweets)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.retweets] to an arbitrary JSON value. - * - * You should usually call [Builder.retweets] with a well-typed [Retweets] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweets(retweets: JsonField) = apply { this.retweets = retweets } + /** Search ranking applied to every query. */ + class QueryType @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Only retweets of this tweet ID (tweet_search_extractor) */ - fun retweetsOfTweetId(retweetsOfTweetId: String) = - retweetsOfTweetId(JsonField.of(retweetsOfTweetId)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.retweetsOfTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.retweetsOfTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun retweetsOfTweetId(retweetsOfTweetId: JsonField) = apply { - this.retweetsOfTweetId = retweetsOfTweetId - } + companion object { - /** Required for tweet_search_extractor & community_search. */ - fun searchQuery(searchQuery: String) = searchQuery(JsonField.of(searchQuery)) + @JvmField val LATEST = of("Latest") - /** - * Sets [Builder.searchQuery] to an arbitrary JSON value. - * - * You should usually call [Builder.searchQuery] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun searchQuery(searchQuery: JsonField) = apply { - this.searchQuery = searchQuery - } + @JvmField val TOP = of("Top") - /** Start date YYYY-MM-DD (tweet_search_extractor) */ - fun sinceDate(sinceDate: LocalDate) = sinceDate(JsonField.of(sinceDate)) + @JvmField val BOTH = of("Both") - /** - * Sets [Builder.sinceDate] to an arbitrary JSON value. - * - * You should usually call [Builder.sinceDate] with a well-typed [LocalDate] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun sinceDate(sinceDate: JsonField) = apply { this.sinceDate = sinceDate } + @JvmStatic fun of(value: String) = QueryType(JsonField.of(value)) + } - /** Required for community_post_extractor & community_search. */ - fun targetCommunityId(targetCommunityId: String) = - targetCommunityId(JsonField.of(targetCommunityId)) + /** An enum containing [QueryType]'s known values. */ + enum class Known { + LATEST, + TOP, + BOTH, + } + /** + * An enum containing [QueryType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [QueryType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LATEST, + TOP, + BOTH, /** - * Sets [Builder.targetCommunityId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetCommunityId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. + * An enum member indicating that [QueryType] was instantiated with an unknown value. */ - fun targetCommunityId(targetCommunityId: JsonField) = apply { - this.targetCommunityId = targetCommunityId + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LATEST -> Value.LATEST + TOP -> Value.TOP + BOTH -> Value.BOTH + else -> Value._UNKNOWN } - /** Required for list_follower_explorer, list_member_extractor & list_post_extractor. */ - fun targetListId(targetListId: String) = targetListId(JsonField.of(targetListId)) - - /** - * Sets [Builder.targetListId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetListId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetListId(targetListId: JsonField) = apply { - this.targetListId = targetListId + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + LATEST -> Known.LATEST + TOP -> Known.TOP + BOTH -> Known.BOTH + else -> throw XTwitterScraperInvalidDataException("Unknown QueryType: $value") } - /** Required for space_explorer. */ - fun targetSpaceId(targetSpaceId: String) = targetSpaceId(JsonField.of(targetSpaceId)) - - /** - * Sets [Builder.targetSpaceId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetSpaceId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetSpaceId(targetSpaceId: JsonField) = apply { - this.targetSpaceId = targetSpaceId + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") } - fun targetTweetId(targetTweetId: String) = targetTweetId(JsonField.of(targetTweetId)) + private var validated: Boolean = false - /** - * Sets [Builder.targetTweetId] to an arbitrary JSON value. - * - * You should usually call [Builder.targetTweetId] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetTweetId(targetTweetId: JsonField) = apply { - this.targetTweetId = targetTweetId + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): QueryType = apply { + if (validated) { + return@apply } - fun targetUsername(targetUsername: String) = - targetUsername(JsonField.of(targetUsername)) + known() + validated = true + } - /** - * Sets [Builder.targetUsername] to an arbitrary JSON value. - * - * You should usually call [Builder.targetUsername] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun targetUsername(targetUsername: JsonField) = apply { - this.targetUsername = targetUsername + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } - /** Filter replies sent to a username (tweet_search_extractor) */ - fun toUser(toUser: String) = toUser(JsonField.of(toUser)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * Sets [Builder.toUser] to an arbitrary JSON value. - * - * You should usually call [Builder.toUser] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun toUser(toUser: JsonField) = apply { this.toUser = toUser } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** End date YYYY-MM-DD (tweet_search_extractor) */ - fun untilDate(untilDate: LocalDate) = untilDate(JsonField.of(untilDate)) + return other is QueryType && value == other.value + } - /** - * Sets [Builder.untilDate] to an arbitrary JSON value. - * - * You should usually call [Builder.untilDate] with a well-typed [LocalDate] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun untilDate(untilDate: JsonField) = apply { this.untilDate = untilDate } + override fun hashCode() = value.hashCode() - /** URL substring or domain filter (tweet_search_extractor) */ - fun url(url: String) = url(JsonField.of(url)) + override fun toString() = value.toString() + } - /** - * Sets [Builder.url] to an arbitrary JSON value. - * - * You should usually call [Builder.url] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun url(url: JsonField) = apply { this.url = url } + /** Quote mode (tweet_search_extractor) */ + class Quotes @JsonCreator private constructor(private val value: JsonField) : Enum { - /** Only verified authors (tweet_search_extractor) */ - fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(JsonField.of(verifiedOnly)) + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - /** - * Sets [Builder.verifiedOnly] to an arbitrary JSON value. - * - * You should usually call [Builder.verifiedOnly] with a well-typed [Boolean] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun verifiedOnly(verifiedOnly: JsonField) = apply { - this.verifiedOnly = verifiedOnly - } + companion object { - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + @JvmField val INCLUDE = of("include") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JvmField val EXCLUDE = of("exclude") - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + @JvmField val ONLY = of("only") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + @JvmStatic fun of(value: String) = Quotes(JsonField.of(value)) + } - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** An enum containing [Quotes]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } - /** - * Returns an immutable instance of [Body]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .toolType() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Body = - Body( - checkRequired("toolType", toolType), - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - additionalProperties.toMutableMap(), - ) + /** + * An enum containing [Quotes]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Quotes] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Quotes] was instantiated with an unknown value. */ + _UNKNOWN, } + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Quotes: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + private var validated: Boolean = false /** @@ -2867,50 +8270,12 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Body = apply { + fun validate(): Quotes = apply { if (validated) { return@apply } - toolType().validate() - advancedQuery() - anyWords() - boundingBox() - cashtags() - conversationId() - exactPhrase() - excludeWords() - fromUser() - hashtags() - inReplyToTweetId() - language() - listId() - mediaType().ifPresent { it.validate() } - mentioning() - minFaves() - minQuotes() - minReplies() - minRetweets() - place() - placeCountry() - pointRadius() - quotes().ifPresent { it.validate() } - quotesOfTweetId() - replies().ifPresent { it.validate() } - resultsLimit() - retweets().ifPresent { it.validate() } - retweetsOfTweetId() - searchQuery() - sinceDate() - targetCommunityId() - targetListId() - targetSpaceId() - targetTweetId() - targetUsername() - toUser() - untilDate() - url() - verifiedOnly() + known() validated = true } @@ -2928,272 +8293,436 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic - internal fun validity(): Int = - (toolType.asKnown().getOrNull()?.validity() ?: 0) + - (if (advancedQuery.asKnown().isPresent) 1 else 0) + - (if (anyWords.asKnown().isPresent) 1 else 0) + - (if (boundingBox.asKnown().isPresent) 1 else 0) + - (if (cashtags.asKnown().isPresent) 1 else 0) + - (if (conversationId.asKnown().isPresent) 1 else 0) + - (if (exactPhrase.asKnown().isPresent) 1 else 0) + - (if (excludeWords.asKnown().isPresent) 1 else 0) + - (if (fromUser.asKnown().isPresent) 1 else 0) + - (if (hashtags.asKnown().isPresent) 1 else 0) + - (if (inReplyToTweetId.asKnown().isPresent) 1 else 0) + - (if (language.asKnown().isPresent) 1 else 0) + - (if (listId.asKnown().isPresent) 1 else 0) + - (mediaType.asKnown().getOrNull()?.validity() ?: 0) + - (if (mentioning.asKnown().isPresent) 1 else 0) + - (if (minFaves.asKnown().isPresent) 1 else 0) + - (if (minQuotes.asKnown().isPresent) 1 else 0) + - (if (minReplies.asKnown().isPresent) 1 else 0) + - (if (minRetweets.asKnown().isPresent) 1 else 0) + - (if (place.asKnown().isPresent) 1 else 0) + - (if (placeCountry.asKnown().isPresent) 1 else 0) + - (if (pointRadius.asKnown().isPresent) 1 else 0) + - (quotes.asKnown().getOrNull()?.validity() ?: 0) + - (if (quotesOfTweetId.asKnown().isPresent) 1 else 0) + - (replies.asKnown().getOrNull()?.validity() ?: 0) + - (if (resultsLimit.asKnown().isPresent) 1 else 0) + - (retweets.asKnown().getOrNull()?.validity() ?: 0) + - (if (retweetsOfTweetId.asKnown().isPresent) 1 else 0) + - (if (searchQuery.asKnown().isPresent) 1 else 0) + - (if (sinceDate.asKnown().isPresent) 1 else 0) + - (if (targetCommunityId.asKnown().isPresent) 1 else 0) + - (if (targetListId.asKnown().isPresent) 1 else 0) + - (if (targetSpaceId.asKnown().isPresent) 1 else 0) + - (if (targetTweetId.asKnown().isPresent) 1 else 0) + - (if (targetUsername.asKnown().isPresent) 1 else 0) + - (if (toUser.asKnown().isPresent) 1 else 0) + - (if (untilDate.asKnown().isPresent) 1 else 0) + - (if (url.asKnown().isPresent) 1 else 0) + - (if (verifiedOnly.asKnown().isPresent) 1 else 0) + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Body && - toolType == other.toolType && - advancedQuery == other.advancedQuery && - anyWords == other.anyWords && - boundingBox == other.boundingBox && - cashtags == other.cashtags && - conversationId == other.conversationId && - exactPhrase == other.exactPhrase && - excludeWords == other.excludeWords && - fromUser == other.fromUser && - hashtags == other.hashtags && - inReplyToTweetId == other.inReplyToTweetId && - language == other.language && - listId == other.listId && - mediaType == other.mediaType && - mentioning == other.mentioning && - minFaves == other.minFaves && - minQuotes == other.minQuotes && - minReplies == other.minReplies && - minRetweets == other.minRetweets && - place == other.place && - placeCountry == other.placeCountry && - pointRadius == other.pointRadius && - quotes == other.quotes && - quotesOfTweetId == other.quotesOfTweetId && - replies == other.replies && - resultsLimit == other.resultsLimit && - retweets == other.retweets && - retweetsOfTweetId == other.retweetsOfTweetId && - searchQuery == other.searchQuery && - sinceDate == other.sinceDate && - targetCommunityId == other.targetCommunityId && - targetListId == other.targetListId && - targetSpaceId == other.targetSpaceId && - targetTweetId == other.targetTweetId && - targetUsername == other.targetUsername && - toUser == other.toUser && - untilDate == other.untilDate && - url == other.url && - verifiedOnly == other.verifiedOnly && - additionalProperties == other.additionalProperties + return other is Quotes && value == other.value } - private val hashCode: Int by lazy { - Objects.hash( - toolType, - advancedQuery, - anyWords, - boundingBox, - cashtags, - conversationId, - exactPhrase, - excludeWords, - fromUser, - hashtags, - inReplyToTweetId, - language, - listId, - mediaType, - mentioning, - minFaves, - minQuotes, - minReplies, - minRetweets, - place, - placeCountry, - pointRadius, - quotes, - quotesOfTweetId, - replies, - resultsLimit, - retweets, - retweetsOfTweetId, - searchQuery, - sinceDate, - targetCommunityId, - targetListId, - targetSpaceId, - targetTweetId, - targetUsername, - toUser, - untilDate, - url, - verifiedOnly, - additionalProperties, - ) + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** One target and relation in a mixed profile collection. */ + class RelationTarget + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val relation: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("relation") + @ExcludeMissing + relation: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(relation, value, mutableMapOf()) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun relation(): Relation = relation.getRequired("relation") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun value(): String = value.getRequired("value") + + /** + * Returns the raw JSON value of [relation]. + * + * Unlike [relation], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("relation") @ExcludeMissing fun _relation(): JsonField = relation + + /** + * Returns the raw JSON value of [value]. + * + * Unlike [value], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RelationTarget]. + * + * The following fields are required: + * ```java + * .relation() + * .value() + * ``` + */ + @JvmStatic fun builder() = Builder() } - override fun hashCode(): Int = hashCode + /** A builder for [RelationTarget]. */ + class Builder internal constructor() { - override fun toString() = - "Body{toolType=$toolType, advancedQuery=$advancedQuery, anyWords=$anyWords, boundingBox=$boundingBox, cashtags=$cashtags, conversationId=$conversationId, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, listId=$listId, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, resultsLimit=$resultsLimit, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, searchQuery=$searchQuery, sinceDate=$sinceDate, targetCommunityId=$targetCommunityId, targetListId=$targetListId, targetSpaceId=$targetSpaceId, targetTweetId=$targetTweetId, targetUsername=$targetUsername, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalProperties=$additionalProperties}" - } + private var relation: JsonField? = null + private var value: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() - /** Identifier for the extraction tool used to run a job. */ - class ToolType @JsonCreator private constructor(private val value: JsonField) : Enum { + @JvmSynthetic + internal fun from(relationTarget: RelationTarget) = apply { + relation = relationTarget.relation + value = relationTarget.value + additionalProperties = relationTarget.additionalProperties.toMutableMap() + } + + fun relation(relation: Relation) = relation(JsonField.of(relation)) + + /** + * Sets [Builder.relation] to an arbitrary JSON value. + * + * You should usually call [Builder.relation] with a well-typed [Relation] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun relation(relation: JsonField) = apply { this.relation = relation } + + fun value(value: String) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [RelationTarget]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .relation() + * .value() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RelationTarget = + RelationTarget( + checkRequired("relation", relation), + checkRequired("value", value), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false /** - * Returns this class instance's raw value. + * Validates that the types of all values in this object match their expected types + * recursively. * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + fun validate(): RelationTarget = apply { + if (validated) { + return@apply + } - companion object { + relation().validate() + value() + validated = true + } - @JvmField val ARTICLE_EXTRACTOR = of("article_extractor") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - @JvmField val COMMUNITY_EXTRACTOR = of("community_extractor") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (relation.asKnown().getOrNull()?.validity() ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) - @JvmField val COMMUNITY_MODERATOR_EXPLORER = of("community_moderator_explorer") + class Relation @JsonCreator private constructor(private val value: JsonField) : + Enum { - @JvmField val COMMUNITY_POST_EXTRACTOR = of("community_post_extractor") + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - @JvmField val COMMUNITY_SEARCH = of("community_search") + companion object { - @JvmField val FAVORITERS = of("favoriters") + @JvmField val COMMUNITY_MEMBERS = of("community_members") - @JvmField val FOLLOWER_EXPLORER = of("follower_explorer") + @JvmField val FOLLOWERS = of("followers") - @JvmField val FOLLOWING_EXPLORER = of("following_explorer") + @JvmField val FOLLOWING = of("following") - @JvmField val LIST_FOLLOWER_EXPLORER = of("list_follower_explorer") + @JvmField val LIST_FOLLOWERS = of("list_followers") - @JvmField val LIST_MEMBER_EXTRACTOR = of("list_member_extractor") + @JvmField val LIST_MEMBERS = of("list_members") - @JvmField val LIST_POST_EXTRACTOR = of("list_post_extractor") + @JvmField val VERIFIED_FOLLOWERS = of("verified_followers") - @JvmField val MENTION_EXTRACTOR = of("mention_extractor") + @JvmStatic fun of(value: String) = Relation(JsonField.of(value)) + } - @JvmField val PEOPLE_SEARCH = of("people_search") + /** An enum containing [Relation]'s known values. */ + enum class Known { + COMMUNITY_MEMBERS, + FOLLOWERS, + FOLLOWING, + LIST_FOLLOWERS, + LIST_MEMBERS, + VERIFIED_FOLLOWERS, + } - @JvmField val POST_EXTRACTOR = of("post_extractor") + /** + * An enum containing [Relation]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Relation] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + COMMUNITY_MEMBERS, + FOLLOWERS, + FOLLOWING, + LIST_FOLLOWERS, + LIST_MEMBERS, + VERIFIED_FOLLOWERS, + /** + * An enum member indicating that [Relation] was instantiated with an unknown value. + */ + _UNKNOWN, + } - @JvmField val QUOTE_EXTRACTOR = of("quote_extractor") + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + COMMUNITY_MEMBERS -> Value.COMMUNITY_MEMBERS + FOLLOWERS -> Value.FOLLOWERS + FOLLOWING -> Value.FOLLOWING + LIST_FOLLOWERS -> Value.LIST_FOLLOWERS + LIST_MEMBERS -> Value.LIST_MEMBERS + VERIFIED_FOLLOWERS -> Value.VERIFIED_FOLLOWERS + else -> Value._UNKNOWN + } - @JvmField val REPLY_EXTRACTOR = of("reply_extractor") + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + COMMUNITY_MEMBERS -> Known.COMMUNITY_MEMBERS + FOLLOWERS -> Known.FOLLOWERS + FOLLOWING -> Known.FOLLOWING + LIST_FOLLOWERS -> Known.LIST_FOLLOWERS + LIST_MEMBERS -> Known.LIST_MEMBERS + VERIFIED_FOLLOWERS -> Known.VERIFIED_FOLLOWERS + else -> throw XTwitterScraperInvalidDataException("Unknown Relation: $value") + } - @JvmField val REPOST_EXTRACTOR = of("repost_extractor") + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } - @JvmField val SPACE_EXPLORER = of("space_explorer") + private var validated: Boolean = false - @JvmField val THREAD_EXTRACTOR = of("thread_extractor") + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Relation = apply { + if (validated) { + return@apply + } - @JvmField val TWEET_SEARCH_EXTRACTOR = of("tweet_search_extractor") + known() + validated = true + } - @JvmField val USER_LIKES = of("user_likes") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } - @JvmField val USER_MEDIA = of("user_media") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - @JvmField val VERIFIED_FOLLOWER_EXPLORER = of("verified_follower_explorer") + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmStatic fun of(value: String) = ToolType(JsonField.of(value)) + return other is Relation && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() } - /** An enum containing [ToolType]'s known values. */ - enum class Known { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RelationTarget && + relation == other.relation && + value == other.value && + additionalProperties == other.additionalProperties } + private val hashCode: Int by lazy { Objects.hash(relation, value, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "RelationTarget{relation=$relation, value=$value, additionalProperties=$additionalProperties}" + } + + /** Reply mode (tweet_search_extractor) */ + class Replies @JsonCreator private constructor(private val value: JsonField) : Enum { + /** - * An enum containing [ToolType]'s known values, as well as an [_UNKNOWN] member. + * Returns this class instance's raw value. * - * An instance of [ToolType] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. */ - enum class Value { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, - /** An enum member indicating that [ToolType] was instantiated with an unknown value. */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val INCLUDE = of("include") + + @JvmField val EXCLUDE = of("exclude") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Replies(JsonField.of(value)) + } + + /** An enum containing [Replies]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } + + /** + * An enum containing [Replies]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Replies] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Replies] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3206,29 +8735,9 @@ private constructor( */ fun value(): Value = when (this) { - ARTICLE_EXTRACTOR -> Value.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Value.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Value.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Value.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Value.COMMUNITY_SEARCH - FAVORITERS -> Value.FAVORITERS - FOLLOWER_EXPLORER -> Value.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Value.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Value.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Value.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Value.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Value.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Value.PEOPLE_SEARCH - POST_EXTRACTOR -> Value.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Value.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Value.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Value.REPOST_EXTRACTOR - SPACE_EXPLORER -> Value.SPACE_EXPLORER - THREAD_EXTRACTOR -> Value.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Value.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Value.USER_LIKES - USER_MEDIA -> Value.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Value.VERIFIED_FOLLOWER_EXPLORER + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY else -> Value._UNKNOWN } @@ -3243,30 +8752,10 @@ private constructor( */ fun known(): Known = when (this) { - ARTICLE_EXTRACTOR -> Known.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Known.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Known.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Known.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Known.COMMUNITY_SEARCH - FAVORITERS -> Known.FAVORITERS - FOLLOWER_EXPLORER -> Known.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Known.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Known.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Known.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Known.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Known.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Known.PEOPLE_SEARCH - POST_EXTRACTOR -> Known.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Known.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Known.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Known.REPOST_EXTRACTOR - SPACE_EXPLORER -> Known.SPACE_EXPLORER - THREAD_EXTRACTOR -> Known.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Known.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Known.USER_LIKES - USER_MEDIA -> Known.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Known.VERIFIED_FOLLOWER_EXPLORER - else -> throw XTwitterScraperInvalidDataException("Unknown ToolType: $value") + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Replies: $value") } /** @@ -3294,7 +8783,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): ToolType = apply { + fun validate(): Replies = apply { if (validated) { return@apply } @@ -3324,7 +8813,7 @@ private constructor( return true } - return other is ToolType && value == other.value + return other is Replies && value == other.value } override fun hashCode() = value.hashCode() @@ -3332,8 +8821,8 @@ private constructor( override fun toString() = value.toString() } - /** Media type filter (tweet_search_extractor) */ - class MediaType @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Retweet mode (tweet_search_extractor) */ + class Retweets @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3347,50 +8836,36 @@ private constructor( companion object { - @JvmField val IMAGES = of("images") - - @JvmField val VIDEOS = of("videos") - - @JvmField val GIFS = of("gifs") - - @JvmField val MEDIA = of("media") + @JvmField val INCLUDE = of("include") - @JvmField val LINKS = of("links") + @JvmField val EXCLUDE = of("exclude") - @JvmField val NONE = of("none") + @JvmField val ONLY = of("only") - @JvmStatic fun of(value: String) = MediaType(JsonField.of(value)) + @JvmStatic fun of(value: String) = Retweets(JsonField.of(value)) } - /** An enum containing [MediaType]'s known values. */ + /** An enum containing [Retweets]'s known values. */ enum class Known { - IMAGES, - VIDEOS, - GIFS, - MEDIA, - LINKS, - NONE, + INCLUDE, + EXCLUDE, + ONLY, } /** - * An enum containing [MediaType]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Retweets]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [MediaType] can contain an unknown value in a couple of cases: + * An instance of [Retweets] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - IMAGES, - VIDEOS, - GIFS, - MEDIA, - LINKS, - NONE, - /** - * An enum member indicating that [MediaType] was instantiated with an unknown value. - */ + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Retweets] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3403,12 +8878,9 @@ private constructor( */ fun value(): Value = when (this) { - IMAGES -> Value.IMAGES - VIDEOS -> Value.VIDEOS - GIFS -> Value.GIFS - MEDIA -> Value.MEDIA - LINKS -> Value.LINKS - NONE -> Value.NONE + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY else -> Value._UNKNOWN } @@ -3423,13 +8895,10 @@ private constructor( */ fun known(): Known = when (this) { - IMAGES -> Known.IMAGES - VIDEOS -> Known.VIDEOS - GIFS -> Known.GIFS - MEDIA -> Known.MEDIA - LINKS -> Known.LINKS - NONE -> Known.NONE - else -> throw XTwitterScraperInvalidDataException("Unknown MediaType: $value") + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Retweets: $value") } /** @@ -3457,7 +8926,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): MediaType = apply { + fun validate(): Retweets = apply { if (validated) { return@apply } @@ -3487,7 +8956,7 @@ private constructor( return true } - return other is MediaType && value == other.value + return other is Retweets && value == other.value } override fun hashCode() = value.hashCode() @@ -3495,8 +8964,8 @@ private constructor( override fun toString() = value.toString() } - /** Quote mode (tweet_search_extractor) */ - class Quotes @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Reply depth scope. */ + class Scope @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3510,36 +8979,36 @@ private constructor( companion object { - @JvmField val INCLUDE = of("include") + @JvmField val ALL = of("all") - @JvmField val EXCLUDE = of("exclude") + @JvmField val DIRECT = of("direct") - @JvmField val ONLY = of("only") + @JvmField val NESTED = of("nested") - @JvmStatic fun of(value: String) = Quotes(JsonField.of(value)) + @JvmStatic fun of(value: String) = Scope(JsonField.of(value)) } - /** An enum containing [Quotes]'s known values. */ + /** An enum containing [Scope]'s known values. */ enum class Known { - INCLUDE, - EXCLUDE, - ONLY, + ALL, + DIRECT, + NESTED, } /** - * An enum containing [Quotes]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Scope]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [Quotes] can contain an unknown value in a couple of cases: + * An instance of [Scope] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Quotes] was instantiated with an unknown value. */ + ALL, + DIRECT, + NESTED, + /** An enum member indicating that [Scope] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3552,9 +9021,9 @@ private constructor( */ fun value(): Value = when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY + ALL -> Value.ALL + DIRECT -> Value.DIRECT + NESTED -> Value.NESTED else -> Value._UNKNOWN } @@ -3569,10 +9038,10 @@ private constructor( */ fun known(): Known = when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Quotes: $value") + ALL -> Known.ALL + DIRECT -> Known.DIRECT + NESTED -> Known.NESTED + else -> throw XTwitterScraperInvalidDataException("Unknown Scope: $value") } /** @@ -3600,7 +9069,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Quotes = apply { + fun validate(): Scope = apply { if (validated) { return@apply } @@ -3630,7 +9099,7 @@ private constructor( return true } - return other is Quotes && value == other.value + return other is Scope && value == other.value } override fun hashCode() = value.hashCode() @@ -3638,8 +9107,217 @@ private constructor( override fun toString() = value.toString() } - /** Reply mode (tweet_search_extractor) */ - class Replies @JsonCreator private constructor(private val value: JsonField) : Enum { + /** Reply start time as ISO 8601 or Unix seconds. */ + @JsonDeserialize(using = SinceTime.Deserializer::class) + @JsonSerialize(using = SinceTime.Serializer::class) + class SinceTime + private constructor( + private val offsetDate: OffsetDateTime? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun offsetDate(): Optional = Optional.ofNullable(offsetDate) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isOffsetDate(): Boolean = offsetDate != null + + fun isInteger(): Boolean = integer != null + + fun asOffsetDate(): OffsetDateTime = offsetDate.getOrThrow("offsetDate") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = sinceTime.accept(new SinceTime.Visitor>() { + * @Override + * public Optional visitOffsetDate(OffsetDateTime offsetDate) { + * return Optional.of(offsetDate.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + offsetDate != null -> visitor.visitOffsetDate(offsetDate) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): SinceTime = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) {} + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) = 1 + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SinceTime && offsetDate == other.offsetDate && integer == other.integer + } + + override fun hashCode(): Int = Objects.hash(offsetDate, integer) + + override fun toString(): String = + when { + offsetDate != null -> "SinceTime{offsetDate=$offsetDate}" + integer != null -> "SinceTime{integer=$integer}" + _json != null -> "SinceTime{_unknown=$_json}" + else -> throw IllegalStateException("Invalid SinceTime") + } + + companion object { + + @JvmStatic + fun ofOffsetDate(offsetDate: OffsetDateTime) = SinceTime(offsetDate = offsetDate) + + @JvmStatic fun ofInteger(integer: Long) = SinceTime(integer = integer) + } + + /** + * An interface that defines how to map each variant of [SinceTime] to a value of type [T]. + */ + interface Visitor { + + fun visitOffsetDate(offsetDate: OffsetDateTime): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [SinceTime] to a value of type [T]. + * + * An instance of [SinceTime] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown SinceTime: $json") + } + } + + internal class Deserializer : BaseDeserializer(SinceTime::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): SinceTime { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + SinceTime(offsetDate = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + SinceTime(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> SinceTime(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(SinceTime::class) { + + override fun serialize( + value: SinceTime, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.offsetDate != null -> generator.writeObject(value.offsetDate) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid SinceTime") + } + } + } + } + + /** Reply result order. */ + class Sort @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -3653,36 +9331,40 @@ private constructor( companion object { - @JvmField val INCLUDE = of("include") + @JvmField val RELEVANCE = of("relevance") - @JvmField val EXCLUDE = of("exclude") + @JvmField val LATEST = of("latest") - @JvmField val ONLY = of("only") + @JvmField val OLDEST = of("oldest") - @JvmStatic fun of(value: String) = Replies(JsonField.of(value)) + @JvmField val LIKES = of("likes") + + @JvmStatic fun of(value: String) = Sort(JsonField.of(value)) } - /** An enum containing [Replies]'s known values. */ + /** An enum containing [Sort]'s known values. */ enum class Known { - INCLUDE, - EXCLUDE, - ONLY, + RELEVANCE, + LATEST, + OLDEST, + LIKES, } /** - * An enum containing [Replies]'s known values, as well as an [_UNKNOWN] member. + * An enum containing [Sort]'s known values, as well as an [_UNKNOWN] member. * - * An instance of [Replies] can contain an unknown value in a couple of cases: + * An instance of [Sort] can contain an unknown value in a couple of cases: * - It was deserialized from data that doesn't match any known member. For example, if the * SDK is on an older version than the API, then the API may respond with new members that * the SDK is unaware of. * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Replies] was instantiated with an unknown value. */ + RELEVANCE, + LATEST, + OLDEST, + LIKES, + /** An enum member indicating that [Sort] was instantiated with an unknown value. */ _UNKNOWN, } @@ -3695,9 +9377,10 @@ private constructor( */ fun value(): Value = when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY + RELEVANCE -> Value.RELEVANCE + LATEST -> Value.LATEST + OLDEST -> Value.OLDEST + LIKES -> Value.LIKES else -> Value._UNKNOWN } @@ -3712,10 +9395,11 @@ private constructor( */ fun known(): Known = when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Replies: $value") + RELEVANCE -> Known.RELEVANCE + LATEST -> Known.LATEST + OLDEST -> Known.OLDEST + LIKES -> Known.LIKES + else -> throw XTwitterScraperInvalidDataException("Unknown Sort: $value") } /** @@ -3743,7 +9427,7 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Replies = apply { + fun validate(): Sort = apply { if (validated) { return@apply } @@ -3773,7 +9457,7 @@ private constructor( return true } - return other is Replies && value == other.value + return other is Sort && value == other.value } override fun hashCode() = value.hashCode() @@ -3781,98 +9465,685 @@ private constructor( override fun toString() = value.toString() } - /** Retweet mode (tweet_search_extractor) */ - class Retweets @JsonCreator private constructor(private val value: JsonField) : Enum { + /** One auto-routed target in a mixed Tweet collection. */ + @JsonDeserialize(using = Target.Deserializer::class) + @JsonSerialize(using = Target.Serializer::class) + class Target + private constructor( + private val string: String? = null, + private val unionMember1: UnionMember1? = null, + private val _json: JsonValue? = null, + ) { - /** - * Returns this class instance's raw value. - * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. - */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun string(): Optional = Optional.ofNullable(string) - companion object { + fun unionMember1(): Optional = Optional.ofNullable(unionMember1) - @JvmField val INCLUDE = of("include") + fun isString(): Boolean = string != null - @JvmField val EXCLUDE = of("exclude") + fun isUnionMember1(): Boolean = unionMember1 != null - @JvmField val ONLY = of("only") + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun asString(): String = string.getOrThrow("string") - @JvmStatic fun of(value: String) = Retweets(JsonField.of(value)) - } + fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") - /** An enum containing [Retweets]'s known values. */ - enum class Known { - INCLUDE, - EXCLUDE, - ONLY, - } + fun _json(): Optional = Optional.ofNullable(_json) /** - * An enum containing [Retweets]'s known values, as well as an [_UNKNOWN] member. + * Maps this instance's current variant to a value of type [T] using the given [visitor]. * - * An instance of [Retweets] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = target.accept(new Target.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. */ - enum class Value { - INCLUDE, - EXCLUDE, - ONLY, - /** An enum member indicating that [Retweets] was instantiated with an unknown value. */ - _UNKNOWN, - } + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false /** - * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] - * if the class was instantiated with an unknown value. + * Validates that the types of all values in this object match their expected types + * recursively. * - * Use the [known] method instead if you're certain the value is always known or if you want - * to throw for the unknown case. + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. */ - fun value(): Value = - when (this) { - INCLUDE -> Value.INCLUDE - EXCLUDE -> Value.EXCLUDE - ONLY -> Value.ONLY - else -> Value._UNKNOWN + fun validate(): Target = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnionMember1(unionMember1: UnionMember1) { + unionMember1.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false } /** - * Returns an enum member corresponding to this class instance's value. - * - * Use the [value] method instead if you're uncertain the value is always known and don't - * want to throw for the unknown case. + * Returns a score indicating how many valid values are contained in this object + * recursively. * - * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a - * known member. + * Used for best match union deserialization. */ - fun known(): Known = - when (this) { - INCLUDE -> Known.INCLUDE - EXCLUDE -> Known.EXCLUDE - ONLY -> Known.ONLY - else -> throw XTwitterScraperInvalidDataException("Unknown Retweets: $value") + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnionMember1(unionMember1: UnionMember1) = + unionMember1.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Target && string == other.string && unionMember1 == other.unionMember1 + } + + override fun hashCode(): Int = Objects.hash(string, unionMember1) + + override fun toString(): String = + when { + string != null -> "Target{string=$string}" + unionMember1 != null -> "Target{unionMember1=$unionMember1}" + _json != null -> "Target{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Target") + } + + companion object { + + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + @JvmStatic fun ofString(string: String) = Target(string = string) + + @JvmStatic + fun ofUnionMember1(unionMember1: UnionMember1) = Target(unionMember1 = unionMember1) + } + + /** An interface that defines how to map each variant of [Target] to a value of type [T]. */ + interface Visitor { + + /** X URL, numeric Tweet ID, @username, kind:value target, or search. */ + fun visitString(string: String): T + + fun visitUnionMember1(unionMember1: UnionMember1): T + + /** + * Maps an unknown variant of [Target] to a value of type [T]. + * + * An instance of [Target] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown Target: $json") + } + } + + internal class Deserializer : BaseDeserializer(Target::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Target { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Target(unionMember1 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Target(string = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Target(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Target::class) { + + override fun serialize( + value: Target, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Target") + } + } + } + + class UnionMember1 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val kind: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("kind") @ExcludeMissing kind: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(kind, value, mutableMapOf()) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun kind(): Kind = kind.getRequired("kind") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun value(): String = value.getRequired("value") + + /** + * Returns the raw JSON value of [kind]. + * + * Unlike [kind], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kind") @ExcludeMissing fun _kind(): JsonField = kind + + /** + * Returns the raw JSON value of [value]. + * + * Unlike [value], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnionMember1]. + * + * The following fields are required: + * ```java + * .kind() + * .value() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember1]. */ + class Builder internal constructor() { + + private var kind: JsonField? = null + private var value: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember1: UnionMember1) = apply { + kind = unionMember1.kind + value = unionMember1.value + additionalProperties = unionMember1.additionalProperties.toMutableMap() + } + + fun kind(kind: Kind) = kind(JsonField.of(kind)) + + /** + * Sets [Builder.kind] to an arbitrary JSON value. + * + * You should usually call [Builder.kind] with a well-typed [Kind] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun kind(kind: JsonField) = apply { this.kind = kind } + + fun value(value: String) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UnionMember1]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .kind() + * .value() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember1 = + UnionMember1( + checkRequired("kind", kind), + checkRequired("value", value), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): UnionMember1 = apply { + if (validated) { + return@apply + } + + kind().validate() + value() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (kind.asKnown().getOrNull()?.validity() ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) + + class Kind @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FAVORITERS = of("favoriters") + + @JvmField val LIST = of("list") + + @JvmField val PROFILE = of("profile") + + @JvmField val PROFILE_LIKES = of("profile_likes") + + @JvmField val PROFILE_MEDIA = of("profile_media") + + @JvmField val PROFILE_REPLIES = of("profile_replies") + + @JvmField val QUOTES = of("quotes") + + @JvmField val REPLIES = of("replies") + + @JvmField val RETWEETERS = of("retweeters") + + @JvmField val SEARCH = of("search") + + @JvmField val THREAD = of("thread") + + @JvmField val TWEET = of("tweet") + + @JvmStatic fun of(value: String) = Kind(JsonField.of(value)) + } + + /** An enum containing [Kind]'s known values. */ + enum class Known { + FAVORITERS, + LIST, + PROFILE, + PROFILE_LIKES, + PROFILE_MEDIA, + PROFILE_REPLIES, + QUOTES, + REPLIES, + RETWEETERS, + SEARCH, + THREAD, + TWEET, + } + + /** + * An enum containing [Kind]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Kind] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FAVORITERS, + LIST, + PROFILE, + PROFILE_LIKES, + PROFILE_MEDIA, + PROFILE_REPLIES, + QUOTES, + REPLIES, + RETWEETERS, + SEARCH, + THREAD, + TWEET, + /** + * An enum member indicating that [Kind] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FAVORITERS -> Value.FAVORITERS + LIST -> Value.LIST + PROFILE -> Value.PROFILE + PROFILE_LIKES -> Value.PROFILE_LIKES + PROFILE_MEDIA -> Value.PROFILE_MEDIA + PROFILE_REPLIES -> Value.PROFILE_REPLIES + QUOTES -> Value.QUOTES + REPLIES -> Value.REPLIES + RETWEETERS -> Value.RETWEETERS + SEARCH -> Value.SEARCH + THREAD -> Value.THREAD + TWEET -> Value.TWEET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a + * not a known member. + */ + fun known(): Known = + when (this) { + FAVORITERS -> Known.FAVORITERS + LIST -> Known.LIST + PROFILE -> Known.PROFILE + PROFILE_LIKES -> Known.PROFILE_LIKES + PROFILE_MEDIA -> Known.PROFILE_MEDIA + PROFILE_REPLIES -> Known.PROFILE_REPLIES + QUOTES -> Known.QUOTES + REPLIES -> Known.REPLIES + RETWEETERS -> Known.RETWEETERS + SEARCH -> Known.SEARCH + THREAD -> Known.THREAD + TWEET -> Known.TWEET + else -> throw XTwitterScraperInvalidDataException("Unknown Kind: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does + * not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Kind = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Kind && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnionMember1 && + kind == other.kind && + value == other.value && + additionalProperties == other.additionalProperties } + private val hashCode: Int by lazy { Objects.hash(kind, value, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember1{kind=$kind, value=$value, additionalProperties=$additionalProperties}" + } + } + + /** Reply end time as ISO 8601 or Unix seconds. */ + @JsonDeserialize(using = UntilTime.Deserializer::class) + @JsonSerialize(using = UntilTime.Serializer::class) + class UntilTime + private constructor( + private val offsetDate: OffsetDateTime? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun offsetDate(): Optional = Optional.ofNullable(offsetDate) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isOffsetDate(): Boolean = offsetDate != null + + fun isInteger(): Boolean = integer != null + + fun asOffsetDate(): OffsetDateTime = offsetDate.getOrThrow("offsetDate") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + /** - * Returns this class instance's primitive wire representation. + * Maps this instance's current variant to a value of type [T] using the given [visitor]. * - * This differs from the [toString] method because that method is primarily for debugging - * and generally doesn't throw. + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = untilTime.accept(new UntilTime.Visitor>() { + * @Override + * public Optional visitOffsetDate(OffsetDateTime offsetDate) { + * return Optional.of(offsetDate.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` * - * @throws XTwitterScraperInvalidDataException if this class instance's value does not have - * the expected primitive type. + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. */ - fun asString(): String = - _value().asString().orElseThrow { - XTwitterScraperInvalidDataException("Value is not a String") + fun accept(visitor: Visitor): T = + when { + offsetDate != null -> visitor.visitOffsetDate(offsetDate) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) } private var validated: Boolean = false @@ -3886,12 +10157,18 @@ private constructor( * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't * match its expected type. */ - fun validate(): Retweets = apply { + fun validate(): UntilTime = apply { if (validated) { return@apply } - known() + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) {} + + override fun visitInteger(integer: Long) {} + } + ) validated = true } @@ -3909,19 +10186,113 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitOffsetDate(offsetDate: OffsetDateTime) = 1 + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Retweets && value == other.value + return other is UntilTime && offsetDate == other.offsetDate && integer == other.integer } - override fun hashCode() = value.hashCode() + override fun hashCode(): Int = Objects.hash(offsetDate, integer) - override fun toString() = value.toString() + override fun toString(): String = + when { + offsetDate != null -> "UntilTime{offsetDate=$offsetDate}" + integer != null -> "UntilTime{integer=$integer}" + _json != null -> "UntilTime{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UntilTime") + } + + companion object { + + @JvmStatic + fun ofOffsetDate(offsetDate: OffsetDateTime) = UntilTime(offsetDate = offsetDate) + + @JvmStatic fun ofInteger(integer: Long) = UntilTime(integer = integer) + } + + /** + * An interface that defines how to map each variant of [UntilTime] to a value of type [T]. + */ + interface Visitor { + + fun visitOffsetDate(offsetDate: OffsetDateTime): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [UntilTime] to a value of type [T]. + * + * An instance of [UntilTime] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown UntilTime: $json") + } + } + + internal class Deserializer : BaseDeserializer(UntilTime::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): UntilTime { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UntilTime(offsetDate = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UntilTime(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> UntilTime(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(UntilTime::class) { + + override fun serialize( + value: UntilTime, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.offsetDate != null -> generator.writeObject(value.offsetDate) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UntilTime") + } + } + } } override fun equals(other: Any?): Boolean { @@ -3930,13 +10301,15 @@ private constructor( } return other is ExtractionRunParams && + dryRun == other.dryRun && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } - override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + override fun hashCode(): Int = + Objects.hash(dryRun, body, additionalHeaders, additionalQueryParams) override fun toString() = - "ExtractionRunParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "ExtractionRunParams{dryRun=$dryRun, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponse.kt index 21d673a7..c20c9f8f 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponse.kt @@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -import com.x_twitter_scraper.api.core.Enum import com.x_twitter_scraper.api.core.ExcludeMissing import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.JsonMissing @@ -19,62 +18,133 @@ import com.x_twitter_scraper.api.core.checkRequired import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.util.Collections import java.util.Objects -import kotlin.jvm.optionals.getOrNull +import java.util.Optional class ExtractionRunResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val id: JsonField, - private val status: JsonValue, - private val toolType: JsonField, + private val allowed: JsonField, + private val creditsAvailable: JsonField, + private val creditsRequired: JsonField, + private val estimatedResults: JsonField, + private val source: JsonField, + private val resolvedXUserId: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), - @JsonProperty("status") @ExcludeMissing status: JsonValue = JsonMissing.of(), - @JsonProperty("toolType") @ExcludeMissing toolType: JsonField = JsonMissing.of(), - ) : this(id, status, toolType, mutableMapOf()) + @JsonProperty("allowed") @ExcludeMissing allowed: JsonField = JsonMissing.of(), + @JsonProperty("creditsAvailable") + @ExcludeMissing + creditsAvailable: JsonField = JsonMissing.of(), + @JsonProperty("creditsRequired") + @ExcludeMissing + creditsRequired: JsonField = JsonMissing.of(), + @JsonProperty("estimatedResults") + @ExcludeMissing + estimatedResults: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("resolvedXUserId") + @ExcludeMissing + resolvedXUserId: JsonField = JsonMissing.of(), + ) : this( + allowed, + creditsAvailable, + creditsRequired, + estimatedResults, + source, + resolvedXUserId, + mutableMapOf(), + ) /** * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun id(): String = id.getRequired("id") + fun allowed(): Boolean = allowed.getRequired("allowed") /** - * Expected to always return the following: - * ```java - * JsonValue.from("running") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server responded - * with an unexpected value). + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - @JsonProperty("status") @ExcludeMissing fun _status(): JsonValue = status + fun creditsAvailable(): String = creditsAvailable.getRequired("creditsAvailable") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun creditsRequired(): String = creditsRequired.getRequired("creditsRequired") /** - * Identifier for the extraction tool used to run a job. - * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun toolType(): ToolType = toolType.getRequired("toolType") + fun estimatedResults(): Long = estimatedResults.getRequired("estimatedResults") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): String = source.getRequired("source") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun resolvedXUserId(): Optional = resolvedXUserId.getOptional("resolvedXUserId") + + /** + * Returns the raw JSON value of [allowed]. + * + * Unlike [allowed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("allowed") @ExcludeMissing fun _allowed(): JsonField = allowed + + /** + * Returns the raw JSON value of [creditsAvailable]. + * + * Unlike [creditsAvailable], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("creditsAvailable") + @ExcludeMissing + fun _creditsAvailable(): JsonField = creditsAvailable + + /** + * Returns the raw JSON value of [creditsRequired]. + * + * Unlike [creditsRequired], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("creditsRequired") + @ExcludeMissing + fun _creditsRequired(): JsonField = creditsRequired + + /** + * Returns the raw JSON value of [estimatedResults]. + * + * Unlike [estimatedResults], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("estimatedResults") + @ExcludeMissing + fun _estimatedResults(): JsonField = estimatedResults /** - * Returns the raw JSON value of [id]. + * Returns the raw JSON value of [source]. * - * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [toolType]. + * Returns the raw JSON value of [resolvedXUserId]. * - * Unlike [toolType], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [resolvedXUserId], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("toolType") @ExcludeMissing fun _toolType(): JsonField = toolType + @JsonProperty("resolvedXUserId") + @ExcludeMissing + fun _resolvedXUserId(): JsonField = resolvedXUserId @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -95,8 +165,11 @@ private constructor( * * The following fields are required: * ```java - * .id() - * .toolType() + * .allowed() + * .creditsAvailable() + * .creditsRequired() + * .estimatedResults() + * .source() * ``` */ @JvmStatic fun builder() = Builder() @@ -105,54 +178,100 @@ private constructor( /** A builder for [ExtractionRunResponse]. */ class Builder internal constructor() { - private var id: JsonField? = null - private var status: JsonValue = JsonValue.from("running") - private var toolType: JsonField? = null + private var allowed: JsonField? = null + private var creditsAvailable: JsonField? = null + private var creditsRequired: JsonField? = null + private var estimatedResults: JsonField? = null + private var source: JsonField? = null + private var resolvedXUserId: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(extractionRunResponse: ExtractionRunResponse) = apply { - id = extractionRunResponse.id - status = extractionRunResponse.status - toolType = extractionRunResponse.toolType + allowed = extractionRunResponse.allowed + creditsAvailable = extractionRunResponse.creditsAvailable + creditsRequired = extractionRunResponse.creditsRequired + estimatedResults = extractionRunResponse.estimatedResults + source = extractionRunResponse.source + resolvedXUserId = extractionRunResponse.resolvedXUserId additionalProperties = extractionRunResponse.additionalProperties.toMutableMap() } - fun id(id: String) = id(JsonField.of(id)) + fun allowed(allowed: Boolean) = allowed(JsonField.of(allowed)) /** - * Sets [Builder.id] to an arbitrary JSON value. + * Sets [Builder.allowed] to an arbitrary JSON value. * - * You should usually call [Builder.id] with a well-typed [String] value instead. This + * You should usually call [Builder.allowed] with a well-typed [Boolean] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun id(id: JsonField) = apply { this.id = id } + fun allowed(allowed: JsonField) = apply { this.allowed = allowed } + + fun creditsAvailable(creditsAvailable: String) = + creditsAvailable(JsonField.of(creditsAvailable)) /** - * Sets the field to an arbitrary JSON value. + * Sets [Builder.creditsAvailable] to an arbitrary JSON value. * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("running") - * ``` + * You should usually call [Builder.creditsAvailable] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun creditsAvailable(creditsAvailable: JsonField) = apply { + this.creditsAvailable = creditsAvailable + } + + fun creditsRequired(creditsRequired: String) = + creditsRequired(JsonField.of(creditsRequired)) + + /** + * Sets [Builder.creditsRequired] to an arbitrary JSON value. * - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.creditsRequired] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun status(status: JsonValue) = apply { this.status = status } + fun creditsRequired(creditsRequired: JsonField) = apply { + this.creditsRequired = creditsRequired + } - /** Identifier for the extraction tool used to run a job. */ - fun toolType(toolType: ToolType) = toolType(JsonField.of(toolType)) + fun estimatedResults(estimatedResults: Long) = + estimatedResults(JsonField.of(estimatedResults)) /** - * Sets [Builder.toolType] to an arbitrary JSON value. + * Sets [Builder.estimatedResults] to an arbitrary JSON value. * - * You should usually call [Builder.toolType] with a well-typed [ToolType] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.estimatedResults] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun toolType(toolType: JsonField) = apply { this.toolType = toolType } + fun estimatedResults(estimatedResults: JsonField) = apply { + this.estimatedResults = estimatedResults + } + + fun source(source: String) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + fun resolvedXUserId(resolvedXUserId: String) = + resolvedXUserId(JsonField.of(resolvedXUserId)) + + /** + * Sets [Builder.resolvedXUserId] to an arbitrary JSON value. + * + * You should usually call [Builder.resolvedXUserId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun resolvedXUserId(resolvedXUserId: JsonField) = apply { + this.resolvedXUserId = resolvedXUserId + } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -180,17 +299,23 @@ private constructor( * * The following fields are required: * ```java - * .id() - * .toolType() + * .allowed() + * .creditsAvailable() + * .creditsRequired() + * .estimatedResults() + * .source() * ``` * * @throws IllegalStateException if any required field is unset. */ fun build(): ExtractionRunResponse = ExtractionRunResponse( - checkRequired("id", id), - status, - checkRequired("toolType", toolType), + checkRequired("allowed", allowed), + checkRequired("creditsAvailable", creditsAvailable), + checkRequired("creditsRequired", creditsRequired), + checkRequired("estimatedResults", estimatedResults), + checkRequired("source", source), + resolvedXUserId, additionalProperties.toMutableMap(), ) } @@ -210,13 +335,12 @@ private constructor( return@apply } - id() - _status().let { - if (it != JsonValue.from("running")) { - throw XTwitterScraperInvalidDataException("'status' is invalid, received $it") - } - } - toolType().validate() + allowed() + creditsAvailable() + creditsRequired() + estimatedResults() + source() + resolvedXUserId() validated = true } @@ -235,272 +359,12 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (id.asKnown().isPresent) 1 else 0) + - status.let { if (it == JsonValue.from("running")) 1 else 0 } + - (toolType.asKnown().getOrNull()?.validity() ?: 0) - - /** Identifier for the extraction tool used to run a job. */ - class ToolType @JsonCreator private constructor(private val value: JsonField) : Enum { - - /** - * Returns this class instance's raw value. - * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. - */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value - - companion object { - - @JvmField val ARTICLE_EXTRACTOR = of("article_extractor") - - @JvmField val COMMUNITY_EXTRACTOR = of("community_extractor") - - @JvmField val COMMUNITY_MODERATOR_EXPLORER = of("community_moderator_explorer") - - @JvmField val COMMUNITY_POST_EXTRACTOR = of("community_post_extractor") - - @JvmField val COMMUNITY_SEARCH = of("community_search") - - @JvmField val FAVORITERS = of("favoriters") - - @JvmField val FOLLOWER_EXPLORER = of("follower_explorer") - - @JvmField val FOLLOWING_EXPLORER = of("following_explorer") - - @JvmField val LIST_FOLLOWER_EXPLORER = of("list_follower_explorer") - - @JvmField val LIST_MEMBER_EXTRACTOR = of("list_member_extractor") - - @JvmField val LIST_POST_EXTRACTOR = of("list_post_extractor") - - @JvmField val MENTION_EXTRACTOR = of("mention_extractor") - - @JvmField val PEOPLE_SEARCH = of("people_search") - - @JvmField val POST_EXTRACTOR = of("post_extractor") - - @JvmField val QUOTE_EXTRACTOR = of("quote_extractor") - - @JvmField val REPLY_EXTRACTOR = of("reply_extractor") - - @JvmField val REPOST_EXTRACTOR = of("repost_extractor") - - @JvmField val SPACE_EXPLORER = of("space_explorer") - - @JvmField val THREAD_EXTRACTOR = of("thread_extractor") - - @JvmField val TWEET_SEARCH_EXTRACTOR = of("tweet_search_extractor") - - @JvmField val USER_LIKES = of("user_likes") - - @JvmField val USER_MEDIA = of("user_media") - - @JvmField val VERIFIED_FOLLOWER_EXPLORER = of("verified_follower_explorer") - - @JvmStatic fun of(value: String) = ToolType(JsonField.of(value)) - } - - /** An enum containing [ToolType]'s known values. */ - enum class Known { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, - } - - /** - * An enum containing [ToolType]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [ToolType] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. - */ - enum class Value { - ARTICLE_EXTRACTOR, - COMMUNITY_EXTRACTOR, - COMMUNITY_MODERATOR_EXPLORER, - COMMUNITY_POST_EXTRACTOR, - COMMUNITY_SEARCH, - FAVORITERS, - FOLLOWER_EXPLORER, - FOLLOWING_EXPLORER, - LIST_FOLLOWER_EXPLORER, - LIST_MEMBER_EXTRACTOR, - LIST_POST_EXTRACTOR, - MENTION_EXTRACTOR, - PEOPLE_SEARCH, - POST_EXTRACTOR, - QUOTE_EXTRACTOR, - REPLY_EXTRACTOR, - REPOST_EXTRACTOR, - SPACE_EXPLORER, - THREAD_EXTRACTOR, - TWEET_SEARCH_EXTRACTOR, - USER_LIKES, - USER_MEDIA, - VERIFIED_FOLLOWER_EXPLORER, - /** An enum member indicating that [ToolType] was instantiated with an unknown value. */ - _UNKNOWN, - } - - /** - * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] - * if the class was instantiated with an unknown value. - * - * Use the [known] method instead if you're certain the value is always known or if you want - * to throw for the unknown case. - */ - fun value(): Value = - when (this) { - ARTICLE_EXTRACTOR -> Value.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Value.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Value.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Value.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Value.COMMUNITY_SEARCH - FAVORITERS -> Value.FAVORITERS - FOLLOWER_EXPLORER -> Value.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Value.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Value.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Value.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Value.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Value.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Value.PEOPLE_SEARCH - POST_EXTRACTOR -> Value.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Value.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Value.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Value.REPOST_EXTRACTOR - SPACE_EXPLORER -> Value.SPACE_EXPLORER - THREAD_EXTRACTOR -> Value.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Value.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Value.USER_LIKES - USER_MEDIA -> Value.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Value.VERIFIED_FOLLOWER_EXPLORER - else -> Value._UNKNOWN - } - - /** - * Returns an enum member corresponding to this class instance's value. - * - * Use the [value] method instead if you're uncertain the value is always known and don't - * want to throw for the unknown case. - * - * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a - * known member. - */ - fun known(): Known = - when (this) { - ARTICLE_EXTRACTOR -> Known.ARTICLE_EXTRACTOR - COMMUNITY_EXTRACTOR -> Known.COMMUNITY_EXTRACTOR - COMMUNITY_MODERATOR_EXPLORER -> Known.COMMUNITY_MODERATOR_EXPLORER - COMMUNITY_POST_EXTRACTOR -> Known.COMMUNITY_POST_EXTRACTOR - COMMUNITY_SEARCH -> Known.COMMUNITY_SEARCH - FAVORITERS -> Known.FAVORITERS - FOLLOWER_EXPLORER -> Known.FOLLOWER_EXPLORER - FOLLOWING_EXPLORER -> Known.FOLLOWING_EXPLORER - LIST_FOLLOWER_EXPLORER -> Known.LIST_FOLLOWER_EXPLORER - LIST_MEMBER_EXTRACTOR -> Known.LIST_MEMBER_EXTRACTOR - LIST_POST_EXTRACTOR -> Known.LIST_POST_EXTRACTOR - MENTION_EXTRACTOR -> Known.MENTION_EXTRACTOR - PEOPLE_SEARCH -> Known.PEOPLE_SEARCH - POST_EXTRACTOR -> Known.POST_EXTRACTOR - QUOTE_EXTRACTOR -> Known.QUOTE_EXTRACTOR - REPLY_EXTRACTOR -> Known.REPLY_EXTRACTOR - REPOST_EXTRACTOR -> Known.REPOST_EXTRACTOR - SPACE_EXPLORER -> Known.SPACE_EXPLORER - THREAD_EXTRACTOR -> Known.THREAD_EXTRACTOR - TWEET_SEARCH_EXTRACTOR -> Known.TWEET_SEARCH_EXTRACTOR - USER_LIKES -> Known.USER_LIKES - USER_MEDIA -> Known.USER_MEDIA - VERIFIED_FOLLOWER_EXPLORER -> Known.VERIFIED_FOLLOWER_EXPLORER - else -> throw XTwitterScraperInvalidDataException("Unknown ToolType: $value") - } - - /** - * Returns this class instance's primitive wire representation. - * - * This differs from the [toString] method because that method is primarily for debugging - * and generally doesn't throw. - * - * @throws XTwitterScraperInvalidDataException if this class instance's value does not have - * the expected primitive type. - */ - fun asString(): String = - _value().asString().orElseThrow { - XTwitterScraperInvalidDataException("Value is not a String") - } - - private var validated: Boolean = false - - /** - * Validates that the types of all values in this object match their expected types - * recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing fields. - * - * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't - * match its expected type. - */ - fun validate(): ToolType = apply { - if (validated) { - return@apply - } - - known() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: XTwitterScraperInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return other is ToolType && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } + (if (allowed.asKnown().isPresent) 1 else 0) + + (if (creditsAvailable.asKnown().isPresent) 1 else 0) + + (if (creditsRequired.asKnown().isPresent) 1 else 0) + + (if (estimatedResults.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + (if (resolvedXUserId.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -508,16 +372,29 @@ private constructor( } return other is ExtractionRunResponse && - id == other.id && - status == other.status && - toolType == other.toolType && + allowed == other.allowed && + creditsAvailable == other.creditsAvailable && + creditsRequired == other.creditsRequired && + estimatedResults == other.estimatedResults && + source == other.source && + resolvedXUserId == other.resolvedXUserId && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(id, status, toolType, additionalProperties) } + private val hashCode: Int by lazy { + Objects.hash( + allowed, + creditsAvailable, + creditsRequired, + estimatedResults, + source, + resolvedXUserId, + additionalProperties, + ) + } override fun hashCode(): Int = hashCode override fun toString() = - "ExtractionRunResponse{id=$id, status=$status, toolType=$toolType, additionalProperties=$additionalProperties}" + "ExtractionRunResponse{allowed=$allowed, creditsAvailable=$creditsAvailable, creditsRequired=$creditsRequired, estimatedResults=$estimatedResults, source=$source, resolvedXUserId=$resolvedXUserId, additionalProperties=$additionalProperties}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/monitors/Monitor.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/monitors/Monitor.kt index 35c1fb77..1fda0958 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/monitors/Monitor.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/monitors/Monitor.kt @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty +import com.x_twitter_scraper.api.core.Enum import com.x_twitter_scraper.api.core.ExcludeMissing import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.JsonMissing @@ -22,6 +23,7 @@ import com.x_twitter_scraper.api.models.EventType import java.time.OffsetDateTime import java.util.Collections import java.util.Objects +import java.util.Optional import kotlin.jvm.optionals.getOrNull /** Account monitor that tracks activity for a given X user. */ @@ -35,6 +37,8 @@ private constructor( private val nextBillingAt: JsonField, private val username: JsonField, private val xUserId: JsonField, + private val pausedAt: JsonField, + private val pausedReason: JsonField, private val additionalProperties: MutableMap, ) { @@ -53,7 +57,24 @@ private constructor( nextBillingAt: JsonField = JsonMissing.of(), @JsonProperty("username") @ExcludeMissing username: JsonField = JsonMissing.of(), @JsonProperty("xUserId") @ExcludeMissing xUserId: JsonField = JsonMissing.of(), - ) : this(id, createdAt, eventTypes, isActive, nextBillingAt, username, xUserId, mutableMapOf()) + @JsonProperty("pausedAt") + @ExcludeMissing + pausedAt: JsonField = JsonMissing.of(), + @JsonProperty("pausedReason") + @ExcludeMissing + pausedReason: JsonField = JsonMissing.of(), + ) : this( + id, + createdAt, + eventTypes, + isActive, + nextBillingAt, + username, + xUserId, + pausedAt, + pausedReason, + mutableMapOf(), + ) /** * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is @@ -101,6 +122,22 @@ private constructor( */ fun xUserId(): String = xUserId.getRequired("xUserId") + /** + * When Xquik automatically paused this monitor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pausedAt(): Optional = pausedAt.getOptional("pausedAt") + + /** + * Why Xquik automatically paused this monitor. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pausedReason(): Optional = pausedReason.getOptional("pausedReason") + /** * Returns the raw JSON value of [id]. * @@ -156,6 +193,22 @@ private constructor( */ @JsonProperty("xUserId") @ExcludeMissing fun _xUserId(): JsonField = xUserId + /** + * Returns the raw JSON value of [pausedAt]. + * + * Unlike [pausedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pausedAt") @ExcludeMissing fun _pausedAt(): JsonField = pausedAt + + /** + * Returns the raw JSON value of [pausedReason]. + * + * Unlike [pausedReason], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pausedReason") + @ExcludeMissing + fun _pausedReason(): JsonField = pausedReason + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -197,6 +250,8 @@ private constructor( private var nextBillingAt: JsonField? = null private var username: JsonField? = null private var xUserId: JsonField? = null + private var pausedAt: JsonField = JsonMissing.of() + private var pausedReason: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -208,6 +263,8 @@ private constructor( nextBillingAt = monitor.nextBillingAt username = monitor.username xUserId = monitor.xUserId + pausedAt = monitor.pausedAt + pausedReason = monitor.pausedReason additionalProperties = monitor.additionalProperties.toMutableMap() } @@ -304,6 +361,32 @@ private constructor( */ fun xUserId(xUserId: JsonField) = apply { this.xUserId = xUserId } + /** When Xquik automatically paused this monitor. */ + fun pausedAt(pausedAt: OffsetDateTime) = pausedAt(JsonField.of(pausedAt)) + + /** + * Sets [Builder.pausedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.pausedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pausedAt(pausedAt: JsonField) = apply { this.pausedAt = pausedAt } + + /** Why Xquik automatically paused this monitor. */ + fun pausedReason(pausedReason: PausedReason) = pausedReason(JsonField.of(pausedReason)) + + /** + * Sets [Builder.pausedReason] to an arbitrary JSON value. + * + * You should usually call [Builder.pausedReason] with a well-typed [PausedReason] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pausedReason(pausedReason: JsonField) = apply { + this.pausedReason = pausedReason + } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -350,6 +433,8 @@ private constructor( checkRequired("nextBillingAt", nextBillingAt), checkRequired("username", username), checkRequired("xUserId", xUserId), + pausedAt, + pausedReason, additionalProperties.toMutableMap(), ) } @@ -376,6 +461,8 @@ private constructor( nextBillingAt() username() xUserId() + pausedAt() + pausedReason().ifPresent { it.validate() } validated = true } @@ -400,7 +487,143 @@ private constructor( (if (isActive.asKnown().isPresent) 1 else 0) + (if (nextBillingAt.asKnown().isPresent) 1 else 0) + (if (username.asKnown().isPresent) 1 else 0) + - (if (xUserId.asKnown().isPresent) 1 else 0) + (if (xUserId.asKnown().isPresent) 1 else 0) + + (if (pausedAt.asKnown().isPresent) 1 else 0) + + (pausedReason.asKnown().getOrNull()?.validity() ?: 0) + + /** Why Xquik automatically paused this monitor. */ + class PausedReason @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val X_USER_NOT_FOUND = of("x_user_not_found") + + @JvmStatic fun of(value: String) = PausedReason(JsonField.of(value)) + } + + /** An enum containing [PausedReason]'s known values. */ + enum class Known { + X_USER_NOT_FOUND + } + + /** + * An enum containing [PausedReason]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [PausedReason] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + X_USER_NOT_FOUND, + /** + * An enum member indicating that [PausedReason] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + X_USER_NOT_FOUND -> Value.X_USER_NOT_FOUND + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + X_USER_NOT_FOUND -> Known.X_USER_NOT_FOUND + else -> throw XTwitterScraperInvalidDataException("Unknown PausedReason: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): PausedReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PausedReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -415,6 +638,8 @@ private constructor( nextBillingAt == other.nextBillingAt && username == other.username && xUserId == other.xUserId && + pausedAt == other.pausedAt && + pausedReason == other.pausedReason && additionalProperties == other.additionalProperties } @@ -427,6 +652,8 @@ private constructor( nextBillingAt, username, xUserId, + pausedAt, + pausedReason, additionalProperties, ) } @@ -434,5 +661,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Monitor{id=$id, createdAt=$createdAt, eventTypes=$eventTypes, isActive=$isActive, nextBillingAt=$nextBillingAt, username=$username, xUserId=$xUserId, additionalProperties=$additionalProperties}" + "Monitor{id=$id, createdAt=$createdAt, eventTypes=$eventTypes, isActive=$isActive, nextBillingAt=$nextBillingAt, username=$username, xUserId=$xUserId, pausedAt=$pausedAt, pausedReason=$pausedReason, additionalProperties=$additionalProperties}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParams.kt new file mode 100644 index 00000000..a9b3536c --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParams.kt @@ -0,0 +1,214 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.support.attachments + +import com.x_twitter_scraper.api.core.Params +import com.x_twitter_scraper.api.core.http.Headers +import com.x_twitter_scraper.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Streams an authenticated user's support image or video. Video requests support one standard byte + * range for seeking and resumable playback. + */ +class AttachmentDownloadParams +private constructor( + private val id: String?, + private val range: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun id(): Optional = Optional.ofNullable(id) + + fun range(): Optional = Optional.ofNullable(range) + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): AttachmentDownloadParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [AttachmentDownloadParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AttachmentDownloadParams]. */ + class Builder internal constructor() { + + private var id: String? = null + private var range: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(attachmentDownloadParams: AttachmentDownloadParams) = apply { + id = attachmentDownloadParams.id + range = attachmentDownloadParams.range + additionalHeaders = attachmentDownloadParams.additionalHeaders.toBuilder() + additionalQueryParams = attachmentDownloadParams.additionalQueryParams.toBuilder() + } + + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + fun range(range: String?) = apply { this.range = range } + + /** Alias for calling [Builder.range] with `range.orElse(null)`. */ + fun range(range: Optional) = range(range.getOrNull()) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [AttachmentDownloadParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AttachmentDownloadParams = + AttachmentDownloadParams( + id, + range, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> id ?: "" + else -> "" + } + + override fun _headers(): Headers = + Headers.builder() + .apply { + range?.let { put("Range", it) } + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AttachmentDownloadParams && + id == other.id && + range == other.range && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(id, range, additionalHeaders, additionalQueryParams) + + override fun toString() = + "AttachmentDownloadParams{id=$id, range=$range, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/accounts/XAccount.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/accounts/XAccount.kt index 2dbe4c9b..7ed83856 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/accounts/XAccount.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/accounts/XAccount.kt @@ -79,10 +79,9 @@ private constructor( fun createdAt(): OffsetDateTime = createdAt.getRequired("createdAt") /** - * Derived connection health. `healthy` = ready to use. `needsReauth` = user must submit fresh - * credentials. `locked` = X locked the account; unlock on x.com first. `suspended` = X banned - * the account. `recovering` = cooldown ended; the account can reconnect on its next use. - * `temporaryIssue` = temporary connection problem; wait before the next use. + * Derived health. `healthy` is ready. `needsReauth` needs credentials. `locked` must be + * unlocked on X. `suspended` is banned. `recovering` can reconnect. Wait before using + * `temporaryIssue`. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -262,10 +261,9 @@ private constructor( fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } /** - * Derived connection health. `healthy` = ready to use. `needsReauth` = user must submit - * fresh credentials. `locked` = X locked the account; unlock on x.com first. `suspended` = - * X banned the account. `recovering` = cooldown ended; the account can reconnect on its - * next use. `temporaryIssue` = temporary connection problem; wait before the next use. + * Derived health. `healthy` is ready. `needsReauth` needs credentials. `locked` must be + * unlocked on X. `suspended` is banned. `recovering` can reconnect. Wait before using + * `temporaryIssue`. */ fun health(health: Health) = health(JsonField.of(health)) @@ -435,10 +433,9 @@ private constructor( (if (cookiesObtainedAt.asKnown().isPresent) 1 else 0) /** - * Derived connection health. `healthy` = ready to use. `needsReauth` = user must submit fresh - * credentials. `locked` = X locked the account; unlock on x.com first. `suspended` = X banned - * the account. `recovering` = cooldown ended; the account can reconnect on its next use. - * `temporaryIssue` = temporary connection problem; wait before the next use. + * Derived health. `healthy` is ready. `needsReauth` needs credentials. `locked` must be + * unlocked on X. `suspended` is banned. `recovering` can reconnect. Wait before using + * `temporaryIssue`. */ class Health @JsonCreator private constructor(private val value: JsonField) : Enum { diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParams.kt index ce286858..148617cd 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParams.kt @@ -17,17 +17,64 @@ import kotlin.jvm.optionals.getOrNull class CommunityRetrieveMembersParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** * Items per page (20-200, default 20). This is an upper bound for paid authenticated calls: * remaining credits can reduce the returned page size, and zero affordable results returns 402 @@ -35,6 +82,15 @@ private constructor( */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -58,16 +114,44 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(communityRetrieveMembersParams: CommunityRetrieveMembersParams) = apply { id = communityRetrieveMembersParams.id + bioContains = communityRetrieveMembersParams.bioContains cursor = communityRetrieveMembersParams.cursor + hasLocation = communityRetrieveMembersParams.hasLocation + hasWebsite = communityRetrieveMembersParams.hasWebsite + locationContains = communityRetrieveMembersParams.locationContains + maxFollowers = communityRetrieveMembersParams.maxFollowers + maxFollowing = communityRetrieveMembersParams.maxFollowing + maxStatuses = communityRetrieveMembersParams.maxStatuses + minAccountAgeDays = communityRetrieveMembersParams.minAccountAgeDays + minFollowers = communityRetrieveMembersParams.minFollowers + minFollowing = communityRetrieveMembersParams.minFollowing + minStatuses = communityRetrieveMembersParams.minStatuses pageSize = communityRetrieveMembersParams.pageSize + usernameContains = communityRetrieveMembersParams.usernameContains + verifiedOnly = communityRetrieveMembersParams.verifiedOnly + verifiedType = communityRetrieveMembersParams.verifiedType additionalHeaders = communityRetrieveMembersParams.additionalHeaders.toBuilder() additionalQueryParams = communityRetrieveMembersParams.additionalQueryParams.toBuilder() } @@ -77,12 +161,148 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + /** * Items per page (20-200, default 20). This is an upper bound for paid authenticated calls: * remaining credits can reduce the returned page size, and zero affordable results returns @@ -100,6 +320,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -206,8 +454,22 @@ private constructor( fun build(): CommunityRetrieveMembersParams = CommunityRetrieveMembersParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -224,8 +486,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -237,15 +513,49 @@ private constructor( return other is CommunityRetrieveMembersParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "CommunityRetrieveMembersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "CommunityRetrieveMembersParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParams.kt index 1b28ef00..075715e5 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParams.kt @@ -17,16 +17,72 @@ import kotlin.jvm.optionals.getOrNull class CommunityRetrieveModeratorsParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for community moderators */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -50,7 +106,21 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -58,7 +128,21 @@ private constructor( internal fun from(communityRetrieveModeratorsParams: CommunityRetrieveModeratorsParams) = apply { id = communityRetrieveModeratorsParams.id + bioContains = communityRetrieveModeratorsParams.bioContains cursor = communityRetrieveModeratorsParams.cursor + hasLocation = communityRetrieveModeratorsParams.hasLocation + hasWebsite = communityRetrieveModeratorsParams.hasWebsite + locationContains = communityRetrieveModeratorsParams.locationContains + maxFollowers = communityRetrieveModeratorsParams.maxFollowers + maxFollowing = communityRetrieveModeratorsParams.maxFollowing + maxStatuses = communityRetrieveModeratorsParams.maxStatuses + minAccountAgeDays = communityRetrieveModeratorsParams.minAccountAgeDays + minFollowers = communityRetrieveModeratorsParams.minFollowers + minFollowing = communityRetrieveModeratorsParams.minFollowing + minStatuses = communityRetrieveModeratorsParams.minStatuses + usernameContains = communityRetrieveModeratorsParams.usernameContains + verifiedOnly = communityRetrieveModeratorsParams.verifiedOnly + verifiedType = communityRetrieveModeratorsParams.verifiedType additionalHeaders = communityRetrieveModeratorsParams.additionalHeaders.toBuilder() additionalQueryParams = communityRetrieveModeratorsParams.additionalQueryParams.toBuilder() @@ -69,12 +153,176 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for community moderators */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -181,7 +429,21 @@ private constructor( fun build(): CommunityRetrieveModeratorsParams = CommunityRetrieveModeratorsParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -198,7 +460,21 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -210,14 +486,47 @@ private constructor( return other is CommunityRetrieveModeratorsParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "CommunityRetrieveModeratorsParams{id=$id, cursor=$cursor, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "CommunityRetrieveModeratorsParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParams.kt index 8ccfe892..79aad893 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParams.kt @@ -17,25 +17,80 @@ import kotlin.jvm.optionals.getOrNull class ListRetrieveFollowersParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for list followers */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is true. + * Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -58,16 +113,44 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(listRetrieveFollowersParams: ListRetrieveFollowersParams) = apply { id = listRetrieveFollowersParams.id + bioContains = listRetrieveFollowersParams.bioContains cursor = listRetrieveFollowersParams.cursor + hasLocation = listRetrieveFollowersParams.hasLocation + hasWebsite = listRetrieveFollowersParams.hasWebsite + locationContains = listRetrieveFollowersParams.locationContains + maxFollowers = listRetrieveFollowersParams.maxFollowers + maxFollowing = listRetrieveFollowersParams.maxFollowing + maxStatuses = listRetrieveFollowersParams.maxStatuses + minAccountAgeDays = listRetrieveFollowersParams.minAccountAgeDays + minFollowers = listRetrieveFollowersParams.minFollowers + minFollowing = listRetrieveFollowersParams.minFollowing + minStatuses = listRetrieveFollowersParams.minStatuses pageSize = listRetrieveFollowersParams.pageSize + usernameContains = listRetrieveFollowersParams.usernameContains + verifiedOnly = listRetrieveFollowersParams.verifiedOnly + verifiedType = listRetrieveFollowersParams.verifiedType additionalHeaders = listRetrieveFollowersParams.additionalHeaders.toBuilder() additionalQueryParams = listRetrieveFollowersParams.additionalQueryParams.toBuilder() } @@ -77,17 +160,152 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for list followers */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is + * true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -101,6 +319,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -207,8 +453,22 @@ private constructor( fun build(): ListRetrieveFollowersParams = ListRetrieveFollowersParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -225,8 +485,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -238,15 +512,49 @@ private constructor( return other is ListRetrieveFollowersParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "ListRetrieveFollowersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "ListRetrieveFollowersParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParams.kt index b4863cab..3c4d518d 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParams.kt @@ -17,20 +17,76 @@ import kotlin.jvm.optionals.getOrNull class ListRetrieveMembersParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for list members */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** Members per page (20-200, default 20) */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -53,16 +109,44 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(listRetrieveMembersParams: ListRetrieveMembersParams) = apply { id = listRetrieveMembersParams.id + bioContains = listRetrieveMembersParams.bioContains cursor = listRetrieveMembersParams.cursor + hasLocation = listRetrieveMembersParams.hasLocation + hasWebsite = listRetrieveMembersParams.hasWebsite + locationContains = listRetrieveMembersParams.locationContains + maxFollowers = listRetrieveMembersParams.maxFollowers + maxFollowing = listRetrieveMembersParams.maxFollowing + maxStatuses = listRetrieveMembersParams.maxStatuses + minAccountAgeDays = listRetrieveMembersParams.minAccountAgeDays + minFollowers = listRetrieveMembersParams.minFollowers + minFollowing = listRetrieveMembersParams.minFollowing + minStatuses = listRetrieveMembersParams.minStatuses pageSize = listRetrieveMembersParams.pageSize + usernameContains = listRetrieveMembersParams.usernameContains + verifiedOnly = listRetrieveMembersParams.verifiedOnly + verifiedType = listRetrieveMembersParams.verifiedType additionalHeaders = listRetrieveMembersParams.additionalHeaders.toBuilder() additionalQueryParams = listRetrieveMembersParams.additionalQueryParams.toBuilder() } @@ -72,12 +156,148 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for list members */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + /** Members per page (20-200, default 20) */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -91,6 +311,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -197,8 +445,22 @@ private constructor( fun build(): ListRetrieveMembersParams = ListRetrieveMembersParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -215,8 +477,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -228,15 +504,49 @@ private constructor( return other is ListRetrieveMembersParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "ListRetrieveMembersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "ListRetrieveMembersParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParams.kt index 13c95fe0..4b99b11b 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParams.kt @@ -21,25 +21,80 @@ import kotlin.jvm.optionals.getOrNull class TweetGetFavoritersParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for favoriters */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is true. + * Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -60,16 +115,44 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(tweetGetFavoritersParams: TweetGetFavoritersParams) = apply { id = tweetGetFavoritersParams.id + bioContains = tweetGetFavoritersParams.bioContains cursor = tweetGetFavoritersParams.cursor + hasLocation = tweetGetFavoritersParams.hasLocation + hasWebsite = tweetGetFavoritersParams.hasWebsite + locationContains = tweetGetFavoritersParams.locationContains + maxFollowers = tweetGetFavoritersParams.maxFollowers + maxFollowing = tweetGetFavoritersParams.maxFollowing + maxStatuses = tweetGetFavoritersParams.maxStatuses + minAccountAgeDays = tweetGetFavoritersParams.minAccountAgeDays + minFollowers = tweetGetFavoritersParams.minFollowers + minFollowing = tweetGetFavoritersParams.minFollowing + minStatuses = tweetGetFavoritersParams.minStatuses pageSize = tweetGetFavoritersParams.pageSize + usernameContains = tweetGetFavoritersParams.usernameContains + verifiedOnly = tweetGetFavoritersParams.verifiedOnly + verifiedType = tweetGetFavoritersParams.verifiedType additionalHeaders = tweetGetFavoritersParams.additionalHeaders.toBuilder() additionalQueryParams = tweetGetFavoritersParams.additionalQueryParams.toBuilder() } @@ -79,17 +162,152 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for favoriters */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is + * true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -103,6 +321,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -209,8 +455,22 @@ private constructor( fun build(): TweetGetFavoritersParams = TweetGetFavoritersParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -227,8 +487,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -240,15 +514,49 @@ private constructor( return other is TweetGetFavoritersParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "TweetGetFavoritersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetGetFavoritersParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt index ab20c84d..720b0a10 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt @@ -23,35 +23,54 @@ class TweetGetQuotesParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val includeReplies: Boolean?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, private val sinceTime: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val untilTime: String?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -61,6 +80,12 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) @@ -73,12 +98,18 @@ private constructor( /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -91,12 +122,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -109,6 +158,18 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -130,12 +191,21 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + /** Unix timestamp - return quotes posted after this time */ fun sinceTime(): Optional = Optional.ofNullable(sinceTime) + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -151,6 +221,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -172,35 +248,54 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var includeReplies: Boolean? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null private var sinceTime: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var untilTime: String? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -208,35 +303,54 @@ private constructor( internal fun from(tweetGetQuotesParams: TweetGetQuotesParams) = apply { id = tweetGetQuotesParams.id anyWords = tweetGetQuotesParams.anyWords + blueVerifiedOnly = tweetGetQuotesParams.blueVerifiedOnly + cardName = tweetGetQuotesParams.cardName cashtags = tweetGetQuotesParams.cashtags conversationId = tweetGetQuotesParams.conversationId cursor = tweetGetQuotesParams.cursor exactPhrase = tweetGetQuotesParams.exactPhrase + excludeSource = tweetGetQuotesParams.excludeSource excludeWords = tweetGetQuotesParams.excludeWords fromUser = tweetGetQuotesParams.fromUser + geocode = tweetGetQuotesParams.geocode hashtags = tweetGetQuotesParams.hashtags includeReplies = tweetGetQuotesParams.includeReplies inReplyToTweetId = tweetGetQuotesParams.inReplyToTweetId language = tweetGetQuotesParams.language + maxFaves = tweetGetQuotesParams.maxFaves + maxId = tweetGetQuotesParams.maxId + maxQuotes = tweetGetQuotesParams.maxQuotes + maxReplies = tweetGetQuotesParams.maxReplies + maxRetweets = tweetGetQuotesParams.maxRetweets mediaType = tweetGetQuotesParams.mediaType mentioning = tweetGetQuotesParams.mentioning + minBookmarks = tweetGetQuotesParams.minBookmarks minFaves = tweetGetQuotesParams.minFaves minQuotes = tweetGetQuotesParams.minQuotes minReplies = tweetGetQuotesParams.minReplies minRetweets = tweetGetQuotesParams.minRetweets + minViews = tweetGetQuotesParams.minViews + nativeRetweets = tweetGetQuotesParams.nativeRetweets + near = tweetGetQuotesParams.near + news = tweetGetQuotesParams.news pageSize = tweetGetQuotesParams.pageSize quotes = tweetGetQuotesParams.quotes quotesOfTweetId = tweetGetQuotesParams.quotesOfTweetId replies = tweetGetQuotesParams.replies retweets = tweetGetQuotesParams.retweets retweetsOfTweetId = tweetGetQuotesParams.retweetsOfTweetId + safe = tweetGetQuotesParams.safe sinceDate = tweetGetQuotesParams.sinceDate + sinceId = tweetGetQuotesParams.sinceId sinceTime = tweetGetQuotesParams.sinceTime + source = tweetGetQuotesParams.source toUser = tweetGetQuotesParams.toUser untilDate = tweetGetQuotesParams.untilDate untilTime = tweetGetQuotesParams.untilTime url = tweetGetQuotesParams.url verifiedOnly = tweetGetQuotesParams.verifiedOnly + within = tweetGetQuotesParams.within + withinTime = tweetGetQuotesParams.withinTime additionalHeaders = tweetGetQuotesParams.additionalHeaders.toBuilder() additionalQueryParams = tweetGetQuotesParams.additionalQueryParams.toBuilder() } @@ -254,6 +368,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -279,6 +416,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -291,6 +435,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -328,6 +478,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -340,6 +548,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -392,6 +613,54 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + + /** + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -444,18 +713,43 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + /** Unix timestamp - return quotes posted after this time */ fun sinceTime(sinceTime: String?) = apply { this.sinceTime = sinceTime } /** Alias for calling [Builder.sinceTime] with `sinceTime.orElse(null)`. */ fun sinceTime(sinceTime: Optional) = sinceTime(sinceTime.getOrNull()) + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -493,6 +787,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -600,35 +906,54 @@ private constructor( TweetGetQuotesParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeReplies, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -646,35 +971,54 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } includeReplies?.let { put("includeReplies", it.toString()) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } sinceTime?.let { put("sinceTime", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } untilTime?.let { put("untilTime", it) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1279,35 +1623,54 @@ private constructor( return other is TweetGetQuotesParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && includeReplies == other.includeReplies && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && sinceTime == other.sinceTime && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && untilTime == other.untilTime && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1316,39 +1679,58 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeReplies, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "TweetGetQuotesParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, includeReplies=$includeReplies, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, sinceTime=$sinceTime, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetGetQuotesParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, includeReplies=$includeReplies, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, source=$source, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt index 8ef7a7ee..d5354110 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt @@ -19,45 +19,68 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull /** - * Returns direct replies. Complete mode merges available timeline views, supported rankings, every - * forward cursor module, labeled hidden-content branches, exact-parent time partitions scaled to - * the reported reply count, and search. It separates nested replies and returns 424 below 80% - * coverage. + * Returns direct replies. Omit mode for automatic maximum coverage with resumable pagination. + * Complete mode returns nested replies, diagnostics, and 424 when direct coverage stays below 80%. */ class TweetGetRepliesParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeOriginalAuthor: Boolean?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, + private val hasMediaOnly: Boolean?, + private val includeOriginalPost: Boolean?, private val inReplyToTweetId: String?, private val language: String?, private val limit: Long?, + private val maxDepth: Long?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, private val mode: Mode?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, + private val scope: Scope?, private val sinceDate: LocalDate?, + private val sinceId: String?, private val sinceTime: String?, + private val sort: Sort?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val untilTime: String?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -67,27 +90,51 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) /** Conversation ID filter. */ fun conversationId(): Optional = Optional.ofNullable(conversationId) - /** Pagination cursor for tweet replies */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude replies written by the source-post author. */ + fun excludeOriginalAuthor(): Optional = Optional.ofNullable(excludeOriginalAuthor) + + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) + /** Only return replies containing media. */ + fun hasMediaOnly(): Optional = Optional.ofNullable(hasMediaOnly) + + /** Include the source post and count it toward limit. */ + fun includeOriginalPost(): Optional = Optional.ofNullable(includeOriginalPost) + /** Only replies to this tweet ID. */ fun inReplyToTweetId(): Optional = Optional.ofNullable(inReplyToTweetId) @@ -95,17 +142,39 @@ private constructor( fun language(): Optional = Optional.ofNullable(language) /** - * With mode=complete, maximum combined direct and nested reply rows (1-25000). Without complete - * mode, this is the deprecated pageSize alias and uses the normal 1-100 page range. + * With mode=complete, maximum combined direct and nested reply rows (1-25000, default 25000). + * Automatic pages accept 1-300. Standard pages accept 1-100. Prefer pageSize outside complete + * mode. */ fun limit(): Optional = Optional.ofNullable(limit) + /** Maximum reply depth from the source post. */ + fun maxDepth(): Optional = Optional.ofNullable(maxDepth) + + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -118,15 +187,28 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + /** - * Set complete for maximum-coverage collection. Complete mode accepts only limit. Remove - * cursor, pageSize, count, time ranges, and tweet filters. + * Optional advanced override. Omit mode for automatic maximum direct reply coverage with + * pagination. Standard keeps legacy pagination. Complete returns direct and nested replies with + * diagnostics, scope, depth, sorting, and original-post controls. */ fun mode(): Optional = Optional.ofNullable(mode) + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue while + * has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) @@ -145,12 +227,27 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + + /** Select all replies, direct replies, or nested replies. */ + fun scope(): Optional = Optional.ofNullable(scope) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + /** Unix timestamp - return replies posted after this time */ fun sinceTime(): Optional = Optional.ofNullable(sinceTime) + /** Sort the selected replies before applying limit. */ + fun sort(): Optional = Optional.ofNullable(sort) + + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -166,6 +263,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -187,36 +290,61 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeOriginalAuthor: Boolean? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null + private var hasMediaOnly: Boolean? = null + private var includeOriginalPost: Boolean? = null private var inReplyToTweetId: String? = null private var language: String? = null private var limit: Long? = null + private var maxDepth: Long? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null private var mode: Mode? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null + private var scope: Scope? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null private var sinceTime: String? = null + private var sort: Sort? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var untilTime: String? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -224,36 +352,61 @@ private constructor( internal fun from(tweetGetRepliesParams: TweetGetRepliesParams) = apply { id = tweetGetRepliesParams.id anyWords = tweetGetRepliesParams.anyWords + blueVerifiedOnly = tweetGetRepliesParams.blueVerifiedOnly + cardName = tweetGetRepliesParams.cardName cashtags = tweetGetRepliesParams.cashtags conversationId = tweetGetRepliesParams.conversationId cursor = tweetGetRepliesParams.cursor exactPhrase = tweetGetRepliesParams.exactPhrase + excludeOriginalAuthor = tweetGetRepliesParams.excludeOriginalAuthor + excludeSource = tweetGetRepliesParams.excludeSource excludeWords = tweetGetRepliesParams.excludeWords fromUser = tweetGetRepliesParams.fromUser + geocode = tweetGetRepliesParams.geocode hashtags = tweetGetRepliesParams.hashtags + hasMediaOnly = tweetGetRepliesParams.hasMediaOnly + includeOriginalPost = tweetGetRepliesParams.includeOriginalPost inReplyToTweetId = tweetGetRepliesParams.inReplyToTweetId language = tweetGetRepliesParams.language limit = tweetGetRepliesParams.limit + maxDepth = tweetGetRepliesParams.maxDepth + maxFaves = tweetGetRepliesParams.maxFaves + maxId = tweetGetRepliesParams.maxId + maxQuotes = tweetGetRepliesParams.maxQuotes + maxReplies = tweetGetRepliesParams.maxReplies + maxRetweets = tweetGetRepliesParams.maxRetweets mediaType = tweetGetRepliesParams.mediaType mentioning = tweetGetRepliesParams.mentioning + minBookmarks = tweetGetRepliesParams.minBookmarks minFaves = tweetGetRepliesParams.minFaves minQuotes = tweetGetRepliesParams.minQuotes minReplies = tweetGetRepliesParams.minReplies minRetweets = tweetGetRepliesParams.minRetweets + minViews = tweetGetRepliesParams.minViews mode = tweetGetRepliesParams.mode + nativeRetweets = tweetGetRepliesParams.nativeRetweets + near = tweetGetRepliesParams.near + news = tweetGetRepliesParams.news pageSize = tweetGetRepliesParams.pageSize quotes = tweetGetRepliesParams.quotes quotesOfTweetId = tweetGetRepliesParams.quotesOfTweetId replies = tweetGetRepliesParams.replies retweets = tweetGetRepliesParams.retweets retweetsOfTweetId = tweetGetRepliesParams.retweetsOfTweetId + safe = tweetGetRepliesParams.safe + scope = tweetGetRepliesParams.scope sinceDate = tweetGetRepliesParams.sinceDate + sinceId = tweetGetRepliesParams.sinceId sinceTime = tweetGetRepliesParams.sinceTime + sort = tweetGetRepliesParams.sort + source = tweetGetRepliesParams.source toUser = tweetGetRepliesParams.toUser untilDate = tweetGetRepliesParams.untilDate untilTime = tweetGetRepliesParams.untilTime url = tweetGetRepliesParams.url verifiedOnly = tweetGetRepliesParams.verifiedOnly + within = tweetGetRepliesParams.within + withinTime = tweetGetRepliesParams.withinTime additionalHeaders = tweetGetRepliesParams.additionalHeaders.toBuilder() additionalQueryParams = tweetGetRepliesParams.additionalQueryParams.toBuilder() } @@ -271,6 +424,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -284,7 +460,10 @@ private constructor( fun conversationId(conversationId: Optional) = conversationId(conversationId.getOrNull()) - /** Pagination cursor for tweet replies */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ @@ -296,6 +475,33 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude replies written by the source-post author. */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean?) = apply { + this.excludeOriginalAuthor = excludeOriginalAuthor + } + + /** + * Alias for [Builder.excludeOriginalAuthor]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Boolean) = + excludeOriginalAuthor(excludeOriginalAuthor as Boolean?) + + /** + * Alias for calling [Builder.excludeOriginalAuthor] with + * `excludeOriginalAuthor.orElse(null)`. + */ + fun excludeOriginalAuthor(excludeOriginalAuthor: Optional) = + excludeOriginalAuthor(excludeOriginalAuthor.getOrNull()) + + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -308,12 +514,50 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } /** Alias for calling [Builder.hashtags] with `hashtags.orElse(null)`. */ fun hashtags(hashtags: Optional) = hashtags(hashtags.getOrNull()) + /** Only return replies containing media. */ + fun hasMediaOnly(hasMediaOnly: Boolean?) = apply { this.hasMediaOnly = hasMediaOnly } + + /** + * Alias for [Builder.hasMediaOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasMediaOnly(hasMediaOnly: Boolean) = hasMediaOnly(hasMediaOnly as Boolean?) + + /** Alias for calling [Builder.hasMediaOnly] with `hasMediaOnly.orElse(null)`. */ + fun hasMediaOnly(hasMediaOnly: Optional) = hasMediaOnly(hasMediaOnly.getOrNull()) + + /** Include the source post and count it toward limit. */ + fun includeOriginalPost(includeOriginalPost: Boolean?) = apply { + this.includeOriginalPost = includeOriginalPost + } + + /** + * Alias for [Builder.includeOriginalPost]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun includeOriginalPost(includeOriginalPost: Boolean) = + includeOriginalPost(includeOriginalPost as Boolean?) + + /** + * Alias for calling [Builder.includeOriginalPost] with `includeOriginalPost.orElse(null)`. + */ + fun includeOriginalPost(includeOriginalPost: Optional) = + includeOriginalPost(includeOriginalPost.getOrNull()) + /** Only replies to this tweet ID. */ fun inReplyToTweetId(inReplyToTweetId: String?) = apply { this.inReplyToTweetId = inReplyToTweetId @@ -330,9 +574,9 @@ private constructor( fun language(language: Optional) = language(language.getOrNull()) /** - * With mode=complete, maximum combined direct and nested reply rows (1-25000). Without - * complete mode, this is the deprecated pageSize alias and uses the normal 1-100 page - * range. + * With mode=complete, maximum combined direct and nested reply rows (1-25000, default + * 25000). Automatic pages accept 1-300. Standard pages accept 1-100. Prefer pageSize + * outside complete mode. */ fun limit(limit: Long?) = apply { this.limit = limit } @@ -346,6 +590,77 @@ private constructor( /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ fun limit(limit: Optional) = limit(limit.getOrNull()) + /** Maximum reply depth from the source post. */ + fun maxDepth(maxDepth: Long?) = apply { this.maxDepth = maxDepth } + + /** + * Alias for [Builder.maxDepth]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxDepth(maxDepth: Long) = maxDepth(maxDepth as Long?) + + /** Alias for calling [Builder.maxDepth] with `maxDepth.orElse(null)`. */ + fun maxDepth(maxDepth: Optional) = maxDepth(maxDepth.getOrNull()) + + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -358,6 +673,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -410,18 +738,67 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + /** - * Set complete for maximum-coverage collection. Complete mode accepts only limit. Remove - * cursor, pageSize, count, time ranges, and tweet filters. + * Optional advanced override. Omit mode for automatic maximum direct reply coverage with + * pagination. Standard keeps legacy pagination. Complete returns direct and nested replies + * with diagnostics, scope, depth, sorting, and original-post controls. */ fun mode(mode: Mode?) = apply { this.mode = mode } /** Alias for calling [Builder.mode] with `mode.orElse(null)`. */ fun mode(mode: Optional) = mode(mode.getOrNull()) + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + + /** + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue + * while has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -471,18 +848,55 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + + /** Select all replies, direct replies, or nested replies. */ + fun scope(scope: Scope?) = apply { this.scope = scope } + + /** Alias for calling [Builder.scope] with `scope.orElse(null)`. */ + fun scope(scope: Optional) = scope(scope.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + /** Unix timestamp - return replies posted after this time */ fun sinceTime(sinceTime: String?) = apply { this.sinceTime = sinceTime } /** Alias for calling [Builder.sinceTime] with `sinceTime.orElse(null)`. */ fun sinceTime(sinceTime: Optional) = sinceTime(sinceTime.getOrNull()) + /** Sort the selected replies before applying limit. */ + fun sort(sort: Sort?) = apply { this.sort = sort } + + /** Alias for calling [Builder.sort] with `sort.orElse(null)`. */ + fun sort(sort: Optional) = sort(sort.getOrNull()) + + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -520,6 +934,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -627,36 +1053,61 @@ private constructor( TweetGetRepliesParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeOriginalAuthor, + excludeSource, excludeWords, fromUser, + geocode, hashtags, + hasMediaOnly, + includeOriginalPost, inReplyToTweetId, language, limit, + maxDepth, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, mode, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, + scope, sinceDate, + sinceId, sinceTime, + sort, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -674,36 +1125,61 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeOriginalAuthor?.let { put("excludeOriginalAuthor", it.toString()) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } + hasMediaOnly?.let { put("hasMediaOnly", it.toString()) } + includeOriginalPost?.let { put("includeOriginalPost", it.toString()) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } limit?.let { put("limit", it.toString()) } + maxDepth?.let { put("maxDepth", it.toString()) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } mode?.let { put("mode", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } + scope?.let { put("scope", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } sinceTime?.let { put("sinceTime", it) } + sort?.let { put("sort", it.toString()) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } untilTime?.let { put("untilTime", it) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -872,8 +1348,9 @@ private constructor( } /** - * Set complete for maximum-coverage collection. Complete mode accepts only limit. Remove - * cursor, pageSize, count, time ranges, and tweet filters. + * Optional advanced override. Omit mode for automatic maximum direct reply coverage with + * pagination. Standard keeps legacy pagination. Complete returns direct and nested replies with + * diagnostics, scope, depth, sorting, and original-post controls. */ class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -889,6 +1366,8 @@ private constructor( companion object { + @JvmField val STANDARD = of("standard") + @JvmField val COMPLETE = of("complete") @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) @@ -896,7 +1375,8 @@ private constructor( /** An enum containing [Mode]'s known values. */ enum class Known { - COMPLETE + STANDARD, + COMPLETE, } /** @@ -909,6 +1389,7 @@ private constructor( * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { + STANDARD, COMPLETE, /** An enum member indicating that [Mode] was instantiated with an unknown value. */ _UNKNOWN, @@ -923,6 +1404,7 @@ private constructor( */ fun value(): Value = when (this) { + STANDARD -> Value.STANDARD COMPLETE -> Value.COMPLETE else -> Value._UNKNOWN } @@ -938,6 +1420,7 @@ private constructor( */ fun known(): Known = when (this) { + STANDARD -> Known.STANDARD COMPLETE -> Known.COMPLETE else -> throw XTwitterScraperInvalidDataException("Unknown Mode: $value") } @@ -1434,6 +1917,298 @@ private constructor( override fun toString() = value.toString() } + /** Select all replies, direct replies, or nested replies. */ + class Scope @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val ALL = of("all") + + @JvmField val DIRECT = of("direct") + + @JvmField val NESTED = of("nested") + + @JvmStatic fun of(value: String) = Scope(JsonField.of(value)) + } + + /** An enum containing [Scope]'s known values. */ + enum class Known { + ALL, + DIRECT, + NESTED, + } + + /** + * An enum containing [Scope]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Scope] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ALL, + DIRECT, + NESTED, + /** An enum member indicating that [Scope] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ALL -> Value.ALL + DIRECT -> Value.DIRECT + NESTED -> Value.NESTED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + ALL -> Known.ALL + DIRECT -> Known.DIRECT + NESTED -> Known.NESTED + else -> throw XTwitterScraperInvalidDataException("Unknown Scope: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Scope = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scope && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Sort the selected replies before applying limit. */ + class Sort @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val RELEVANCE = of("relevance") + + @JvmField val LATEST = of("latest") + + @JvmField val OLDEST = of("oldest") + + @JvmField val LIKES = of("likes") + + @JvmStatic fun of(value: String) = Sort(JsonField.of(value)) + } + + /** An enum containing [Sort]'s known values. */ + enum class Known { + RELEVANCE, + LATEST, + OLDEST, + LIKES, + } + + /** + * An enum containing [Sort]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Sort] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + RELEVANCE, + LATEST, + OLDEST, + LIKES, + /** An enum member indicating that [Sort] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + RELEVANCE -> Value.RELEVANCE + LATEST -> Value.LATEST + OLDEST -> Value.OLDEST + LIKES -> Value.LIKES + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + RELEVANCE -> Known.RELEVANCE + LATEST -> Known.LATEST + OLDEST -> Known.OLDEST + LIKES -> Known.LIKES + else -> throw XTwitterScraperInvalidDataException("Unknown Sort: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Sort = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Sort && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1442,36 +2217,61 @@ private constructor( return other is TweetGetRepliesParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeOriginalAuthor == other.excludeOriginalAuthor && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && + hasMediaOnly == other.hasMediaOnly && + includeOriginalPost == other.includeOriginalPost && inReplyToTweetId == other.inReplyToTweetId && language == other.language && limit == other.limit && + maxDepth == other.maxDepth && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && mode == other.mode && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && + scope == other.scope && sinceDate == other.sinceDate && + sinceId == other.sinceId && sinceTime == other.sinceTime && + sort == other.sort && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && untilTime == other.untilTime && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1480,40 +2280,65 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeOriginalAuthor, + excludeSource, excludeWords, fromUser, + geocode, hashtags, + hasMediaOnly, + includeOriginalPost, inReplyToTweetId, language, limit, + maxDepth, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, mode, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, + scope, sinceDate, + sinceId, sinceTime, + sort, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "TweetGetRepliesParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, limit=$limit, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, mode=$mode, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, sinceTime=$sinceTime, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetGetRepliesParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeOriginalAuthor=$excludeOriginalAuthor, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, hasMediaOnly=$hasMediaOnly, includeOriginalPost=$includeOriginalPost, inReplyToTweetId=$inReplyToTweetId, language=$language, limit=$limit, maxDepth=$maxDepth, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, mode=$mode, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, scope=$scope, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, sort=$sort, source=$source, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesResponse.kt index b1d9b6d8..7330100e 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesResponse.kt @@ -23,8 +23,8 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull /** - * Reply rows. Complete mode also returns nested replies and coverage diagnostics. Keep nested - * replies separate from direct coverage. + * Direct reply rows. No-mode requests use resumable automatic coverage. Complete mode also returns + * nested replies and coverage diagnostics. Keep nested replies separate from direct coverage. */ class TweetGetRepliesResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParams.kt index 210a50c0..61a1cc83 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParams.kt @@ -17,25 +17,80 @@ import kotlin.jvm.optionals.getOrNull class TweetGetRetweetersParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for retweeters */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is true. + * Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -56,16 +111,44 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(tweetGetRetweetersParams: TweetGetRetweetersParams) = apply { id = tweetGetRetweetersParams.id + bioContains = tweetGetRetweetersParams.bioContains cursor = tweetGetRetweetersParams.cursor + hasLocation = tweetGetRetweetersParams.hasLocation + hasWebsite = tweetGetRetweetersParams.hasWebsite + locationContains = tweetGetRetweetersParams.locationContains + maxFollowers = tweetGetRetweetersParams.maxFollowers + maxFollowing = tweetGetRetweetersParams.maxFollowing + maxStatuses = tweetGetRetweetersParams.maxStatuses + minAccountAgeDays = tweetGetRetweetersParams.minAccountAgeDays + minFollowers = tweetGetRetweetersParams.minFollowers + minFollowing = tweetGetRetweetersParams.minFollowing + minStatuses = tweetGetRetweetersParams.minStatuses pageSize = tweetGetRetweetersParams.pageSize + usernameContains = tweetGetRetweetersParams.usernameContains + verifiedOnly = tweetGetRetweetersParams.verifiedOnly + verifiedType = tweetGetRetweetersParams.verifiedType additionalHeaders = tweetGetRetweetersParams.additionalHeaders.toBuilder() additionalQueryParams = tweetGetRetweetersParams.additionalQueryParams.toBuilder() } @@ -75,17 +158,152 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for retweeters */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is + * true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -99,6 +317,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -205,8 +451,22 @@ private constructor( fun build(): TweetGetRetweetersParams = TweetGetRetweetersParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -223,8 +483,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -236,15 +510,49 @@ private constructor( return other is TweetGetRetweetersParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "TweetGetRetweetersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetGetRetweetersParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt index b609f3a4..3c120d7f 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt @@ -19,30 +19,45 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Search tweets by query, Tweet ID, X status URL, or account date window */ +/** No-mode search maximizes coverage. */ class TweetSearchParams private constructor( private val q: String, private val advancedQuery: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, private val boundingBox: String?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val inReplyToTweetId: String?, private val language: String?, private val limit: Long?, private val listId: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val mode: Mode?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val place: String?, private val placeCountry: String?, private val pointRadius: String?, @@ -52,18 +67,23 @@ private constructor( private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, private val sinceTime: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val untilTime: String?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - /** Search query (keywords, */ + /** Query, Tweet ID, or status URL. Valid inline bounds apply per page. */ fun q(): String = q /** Raw advanced search query appended as-is. */ @@ -72,27 +92,42 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8. */ fun boundingBox(): Optional = Optional.ofNullable(boundingBox) + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) /** Conversation ID filter. */ fun conversationId(): Optional = Optional.ofNullable(conversationId) - /** Pagination cursor from previous response */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -103,21 +138,39 @@ private constructor( fun language(): Optional = Optional.ofNullable(language) /** - * Max tweets to return (server paginates internally). Omit for single page (~20). This is an - * upper bound for paid authenticated calls: remaining credits can reduce the returned page - * size, and zero affordable results returns 402 insufficient_credits. + * Result upper bound. Omit it for the existing 20-row page size. Explicit coverage defaults to + * 2000 and allows 10000. For paid requests, remaining credits can reduce results. Zero + * affordable results returns 402. */ fun limit(): Optional = Optional.ofNullable(limit) /** Search within a list ID. */ fun listId(): Optional = Optional.ofNullable(listId) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -130,6 +183,24 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + fun mode(): Optional = Optional.ofNullable(mode) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** Search within a place ID. */ fun place(): Optional = Optional.ofNullable(place) @@ -157,19 +228,28 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) - /** ISO 8601 timestamp - only return tweets after this time */ + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + + /** Inclusive ISO bound. */ fun sinceTime(): Optional = Optional.ofNullable(sinceTime) + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) /** End date in YYYY-MM-DD format. */ fun untilDate(): Optional = Optional.ofNullable(untilDate) - /** ISO 8601 timestamp - only return tweets before this time */ + /** Exclusive ISO bound. */ fun untilTime(): Optional = Optional.ofNullable(untilTime) /** URL substring or domain filter. */ @@ -178,6 +258,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -205,24 +291,39 @@ private constructor( private var q: String? = null private var advancedQuery: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null private var boundingBox: String? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var inReplyToTweetId: String? = null private var language: String? = null private var limit: Long? = null private var listId: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var mode: Mode? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var place: String? = null private var placeCountry: String? = null private var pointRadius: String? = null @@ -232,13 +333,18 @@ private constructor( private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null private var sinceTime: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var untilTime: String? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -247,24 +353,39 @@ private constructor( q = tweetSearchParams.q advancedQuery = tweetSearchParams.advancedQuery anyWords = tweetSearchParams.anyWords + blueVerifiedOnly = tweetSearchParams.blueVerifiedOnly boundingBox = tweetSearchParams.boundingBox + cardName = tweetSearchParams.cardName cashtags = tweetSearchParams.cashtags conversationId = tweetSearchParams.conversationId cursor = tweetSearchParams.cursor exactPhrase = tweetSearchParams.exactPhrase + excludeSource = tweetSearchParams.excludeSource excludeWords = tweetSearchParams.excludeWords fromUser = tweetSearchParams.fromUser + geocode = tweetSearchParams.geocode hashtags = tweetSearchParams.hashtags inReplyToTweetId = tweetSearchParams.inReplyToTweetId language = tweetSearchParams.language limit = tweetSearchParams.limit listId = tweetSearchParams.listId + maxFaves = tweetSearchParams.maxFaves + maxId = tweetSearchParams.maxId + maxQuotes = tweetSearchParams.maxQuotes + maxReplies = tweetSearchParams.maxReplies + maxRetweets = tweetSearchParams.maxRetweets mediaType = tweetSearchParams.mediaType mentioning = tweetSearchParams.mentioning + minBookmarks = tweetSearchParams.minBookmarks minFaves = tweetSearchParams.minFaves minQuotes = tweetSearchParams.minQuotes minReplies = tweetSearchParams.minReplies minRetweets = tweetSearchParams.minRetweets + minViews = tweetSearchParams.minViews + mode = tweetSearchParams.mode + nativeRetweets = tweetSearchParams.nativeRetweets + near = tweetSearchParams.near + news = tweetSearchParams.news place = tweetSearchParams.place placeCountry = tweetSearchParams.placeCountry pointRadius = tweetSearchParams.pointRadius @@ -274,18 +395,23 @@ private constructor( replies = tweetSearchParams.replies retweets = tweetSearchParams.retweets retweetsOfTweetId = tweetSearchParams.retweetsOfTweetId + safe = tweetSearchParams.safe sinceDate = tweetSearchParams.sinceDate + sinceId = tweetSearchParams.sinceId sinceTime = tweetSearchParams.sinceTime + source = tweetSearchParams.source toUser = tweetSearchParams.toUser untilDate = tweetSearchParams.untilDate untilTime = tweetSearchParams.untilTime url = tweetSearchParams.url verifiedOnly = tweetSearchParams.verifiedOnly + within = tweetSearchParams.within + withinTime = tweetSearchParams.withinTime additionalHeaders = tweetSearchParams.additionalHeaders.toBuilder() additionalQueryParams = tweetSearchParams.additionalQueryParams.toBuilder() } - /** Search query (keywords, */ + /** Query, Tweet ID, or status URL. Valid inline bounds apply per page. */ fun q(q: String) = apply { this.q = q } /** Raw advanced search query appended as-is. */ @@ -303,12 +429,35 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + /** Geo bounding box, e.g. -74.1 40.6 -73.9 40.8. */ fun boundingBox(boundingBox: String?) = apply { this.boundingBox = boundingBox } /** Alias for calling [Builder.boundingBox] with `boundingBox.orElse(null)`. */ fun boundingBox(boundingBox: Optional) = boundingBox(boundingBox.getOrNull()) + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -322,7 +471,10 @@ private constructor( fun conversationId(conversationId: Optional) = conversationId(conversationId.getOrNull()) - /** Pagination cursor from previous response */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ @@ -334,6 +486,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -346,6 +505,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -368,9 +533,9 @@ private constructor( fun language(language: Optional) = language(language.getOrNull()) /** - * Max tweets to return (server paginates internally). Omit for single page (~20). This is - * an upper bound for paid authenticated calls: remaining credits can reduce the returned - * page size, and zero affordable results returns 402 insufficient_credits. + * Result upper bound. Omit it for the existing 20-row page size. Explicit coverage defaults + * to 2000 and allows 10000. For paid requests, remaining credits can reduce results. Zero + * affordable results returns 402. */ fun limit(limit: Long?) = apply { this.limit = limit } @@ -390,6 +555,64 @@ private constructor( /** Alias for calling [Builder.listId] with `listId.orElse(null)`. */ fun listId(listId: Optional) = listId(listId.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -402,6 +625,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -454,6 +690,63 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage + * returns diagnostics once and rejects cursors. + */ + fun mode(mode: Mode?) = apply { this.mode = mode } + + /** Alias for calling [Builder.mode] with `mode.orElse(null)`. */ + fun mode(mode: Optional) = mode(mode.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + + /** + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + /** Search within a place ID. */ fun place(place: String?) = apply { this.place = place } @@ -514,18 +807,43 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) - /** ISO 8601 timestamp - only return tweets after this time */ + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + + /** Inclusive ISO bound. */ fun sinceTime(sinceTime: String?) = apply { this.sinceTime = sinceTime } /** Alias for calling [Builder.sinceTime] with `sinceTime.orElse(null)`. */ fun sinceTime(sinceTime: Optional) = sinceTime(sinceTime.getOrNull()) + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -538,7 +856,7 @@ private constructor( /** Alias for calling [Builder.untilDate] with `untilDate.orElse(null)`. */ fun untilDate(untilDate: Optional) = untilDate(untilDate.getOrNull()) - /** ISO 8601 timestamp - only return tweets before this time */ + /** Exclusive ISO bound. */ fun untilTime(untilTime: String?) = apply { this.untilTime = untilTime } /** Alias for calling [Builder.untilTime] with `untilTime.orElse(null)`. */ @@ -563,6 +881,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -678,24 +1008,39 @@ private constructor( checkRequired("q", q), advancedQuery, anyWords, + blueVerifiedOnly, boundingBox, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, limit, listId, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + mode, + nativeRetweets, + near, + news, place, placeCountry, pointRadius, @@ -705,13 +1050,18 @@ private constructor( replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -725,24 +1075,39 @@ private constructor( put("q", q) advancedQuery?.let { put("advancedQuery", it) } anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } boundingBox?.let { put("boundingBox", it) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } limit?.let { put("limit", it.toString()) } listId?.let { put("listId", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + mode?.let { put("mode", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } place?.let { put("place", it) } placeCountry?.let { put("placeCountry", it) } pointRadius?.let { put("pointRadius", it) } @@ -752,13 +1117,18 @@ private constructor( replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } sinceTime?.let { put("sinceTime", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } untilTime?.let { put("untilTime", it) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -926,6 +1296,146 @@ private constructor( override fun toString() = value.toString() } + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val STANDARD = of("standard") + + @JvmField val COVERAGE = of("coverage") + + @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) + } + + /** An enum containing [Mode]'s known values. */ + enum class Known { + STANDARD, + COVERAGE, + } + + /** + * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Mode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + STANDARD, + COVERAGE, + /** An enum member indicating that [Mode] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + STANDARD -> Value.STANDARD + COVERAGE -> Value.COVERAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + STANDARD -> Known.STANDARD + COVERAGE -> Known.COVERAGE + else -> throw XTwitterScraperInvalidDataException("Unknown Mode: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Mode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Mode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + /** Sort order - Latest (chronological) or Top (engagement-ranked) */ class QueryType @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1503,24 +2013,39 @@ private constructor( q == other.q && advancedQuery == other.advancedQuery && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && boundingBox == other.boundingBox && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && inReplyToTweetId == other.inReplyToTweetId && language == other.language && limit == other.limit && listId == other.listId && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + mode == other.mode && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && place == other.place && placeCountry == other.placeCountry && pointRadius == other.pointRadius && @@ -1530,13 +2055,18 @@ private constructor( replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && sinceTime == other.sinceTime && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && untilTime == other.untilTime && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1546,24 +2076,39 @@ private constructor( q, advancedQuery, anyWords, + blueVerifiedOnly, boundingBox, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, limit, listId, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + mode, + nativeRetweets, + near, + news, place, placeCountry, pointRadius, @@ -1573,17 +2118,22 @@ private constructor( replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "TweetSearchParams{q=$q, advancedQuery=$advancedQuery, anyWords=$anyWords, boundingBox=$boundingBox, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, limit=$limit, listId=$listId, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, queryType=$queryType, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, sinceTime=$sinceTime, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetSearchParams{q=$q, advancedQuery=$advancedQuery, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, boundingBox=$boundingBox, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, limit=$limit, listId=$listId, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, mode=$mode, nativeRetweets=$nativeRetweets, near=$near, news=$news, place=$place, placeCountry=$placeCountry, pointRadius=$pointRadius, queryType=$queryType, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, source=$source, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponse.kt new file mode 100644 index 00000000..7f7bca43 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponse.kt @@ -0,0 +1,2730 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.tweets + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.ExcludeMissing +import com.x_twitter_scraper.api.core.JsonField +import com.x_twitter_scraper.api.core.JsonMissing +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow +import com.x_twitter_scraper.api.core.toImmutable +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedTweets +import com.x_twitter_scraper.api.models.SearchTweet +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ +@JsonDeserialize(using = TweetSearchResponse.Deserializer::class) +@JsonSerialize(using = TweetSearchResponse.Serializer::class) +class TweetSearchResponse +private constructor( + private val paginatedTweets: PaginatedTweets? = null, + private val coverage: TweetSearchCoverageResponse? = null, + private val _json: JsonValue? = null, +) { + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ + fun paginatedTweets(): Optional = Optional.ofNullable(paginatedTweets) + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ + fun coverage(): Optional = Optional.ofNullable(coverage) + + fun isPaginatedTweets(): Boolean = paginatedTweets != null + + fun isCoverage(): Boolean = coverage != null + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ + fun asPaginatedTweets(): PaginatedTweets = paginatedTweets.getOrThrow("paginatedTweets") + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ + fun asCoverage(): TweetSearchCoverageResponse = coverage.getOrThrow("coverage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = tweetSearchResponse.accept(new TweetSearchResponse.Visitor>() { + * @Override + * public Optional visitPaginatedTweets(PaginatedTweets paginatedTweets) { + * return Optional.of(paginatedTweets.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + paginatedTweets != null -> visitor.visitPaginatedTweets(paginatedTweets) + coverage != null -> visitor.visitCoverage(coverage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): TweetSearchResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitPaginatedTweets(paginatedTweets: PaginatedTweets) { + paginatedTweets.validate() + } + + override fun visitCoverage(coverage: TweetSearchCoverageResponse) { + coverage.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitPaginatedTweets(paginatedTweets: PaginatedTweets) = + paginatedTweets.validity() + + override fun visitCoverage(coverage: TweetSearchCoverageResponse) = + coverage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TweetSearchResponse && + paginatedTweets == other.paginatedTweets && + coverage == other.coverage + } + + override fun hashCode(): Int = Objects.hash(paginatedTweets, coverage) + + override fun toString(): String = + when { + paginatedTweets != null -> "TweetSearchResponse{paginatedTweets=$paginatedTweets}" + coverage != null -> "TweetSearchResponse{coverage=$coverage}" + _json != null -> "TweetSearchResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TweetSearchResponse") + } + + companion object { + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. + * Shape, filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. + * Follow next_cursor while has_next_page is true. An empty filtered page can still have + * has_next_page true. + */ + @JvmStatic + fun ofPaginatedTweets(paginatedTweets: PaginatedTweets) = + TweetSearchResponse(paginatedTweets = paginatedTweets) + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. + * Shape, filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. + * Follow next_cursor while has_next_page is true. An empty filtered page can still have + * has_next_page true. + */ + @JvmStatic + fun ofCoverage(coverage: TweetSearchCoverageResponse) = + TweetSearchResponse(coverage = coverage) + } + + /** + * An interface that defines how to map each variant of [TweetSearchResponse] to a value of type + * [T]. + */ + interface Visitor { + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. + * Shape, filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. + * Follow next_cursor while has_next_page is true. An empty filtered page can still have + * has_next_page true. + */ + fun visitPaginatedTweets(paginatedTweets: PaginatedTweets): T + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. + * Shape, filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. + * Follow next_cursor while has_next_page is true. An empty filtered page can still have + * has_next_page true. + */ + fun visitCoverage(coverage: TweetSearchCoverageResponse): T + + /** + * Maps an unknown variant of [TweetSearchResponse] to a value of type [T]. + * + * An instance of [TweetSearchResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException("Unknown TweetSearchResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer(TweetSearchResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): TweetSearchResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TweetSearchResponse(paginatedTweets = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TweetSearchResponse(coverage = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> TweetSearchResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(TweetSearchResponse::class) { + + override fun serialize( + value: TweetSearchResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.paginatedTweets != null -> generator.writeObject(value.paginatedTweets) + value.coverage != null -> generator.writeObject(value.coverage) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TweetSearchResponse") + } + } + } + + /** + * No-mode search, user Tweet, user reply, and direct reply reads use automatic coverage. Shape, + * filters, aliases, and billing stay compatible. Unprefixed cursors remain legacy. Follow + * next_cursor while has_next_page is true. An empty filtered page can still have has_next_page + * true. + */ + class TweetSearchCoverageResponse + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val hasNextPage: JsonField, + private val nextCursor: JsonField, + private val tweets: JsonField>, + private val diagnostic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("has_next_page") + @ExcludeMissing + hasNextPage: JsonField = JsonMissing.of(), + @JsonProperty("next_cursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("tweets") + @ExcludeMissing + tweets: JsonField> = JsonMissing.of(), + @JsonProperty("diagnostic") + @ExcludeMissing + diagnostic: JsonField = JsonMissing.of(), + ) : this(hasNextPage, nextCursor, tweets, diagnostic, mutableMapOf()) + + fun toPaginatedTweets(): PaginatedTweets = + PaginatedTweets.builder() + .hasNextPage(hasNextPage) + .nextCursor(nextCursor) + .tweets(tweets) + .build() + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun hasNextPage(): Boolean = hasNextPage.getRequired("has_next_page") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun nextCursor(): String = nextCursor.getRequired("next_cursor") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun tweets(): List = tweets.getRequired("tweets") + + /** + * Coverage evidence across parallel search strategies. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun diagnostic(): Diagnostic = diagnostic.getRequired("diagnostic") + + /** + * Returns the raw JSON value of [hasNextPage]. + * + * Unlike [hasNextPage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("has_next_page") + @ExcludeMissing + fun _hasNextPage(): JsonField = hasNextPage + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("next_cursor") + @ExcludeMissing + fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [tweets]. + * + * Unlike [tweets], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tweets") @ExcludeMissing fun _tweets(): JsonField> = tweets + + /** + * Returns the raw JSON value of [diagnostic]. + * + * Unlike [diagnostic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("diagnostic") + @ExcludeMissing + fun _diagnostic(): JsonField = diagnostic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [TweetSearchCoverageResponse]. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .tweets() + * .diagnostic() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TweetSearchCoverageResponse]. */ + class Builder internal constructor() { + + private var hasNextPage: JsonField? = null + private var nextCursor: JsonField? = null + private var tweets: JsonField>? = null + private var diagnostic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(tweetSearchCoverageResponse: TweetSearchCoverageResponse) = apply { + hasNextPage = tweetSearchCoverageResponse.hasNextPage + nextCursor = tweetSearchCoverageResponse.nextCursor + tweets = tweetSearchCoverageResponse.tweets.map { it.toMutableList() } + diagnostic = tweetSearchCoverageResponse.diagnostic + additionalProperties = + tweetSearchCoverageResponse.additionalProperties.toMutableMap() + } + + fun hasNextPage(hasNextPage: Boolean) = hasNextPage(JsonField.of(hasNextPage)) + + /** + * Sets [Builder.hasNextPage] to an arbitrary JSON value. + * + * You should usually call [Builder.hasNextPage] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasNextPage(hasNextPage: JsonField) = apply { + this.hasNextPage = hasNextPage + } + + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + fun tweets(tweets: List) = tweets(JsonField.of(tweets)) + + /** + * Sets [Builder.tweets] to an arbitrary JSON value. + * + * You should usually call [Builder.tweets] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun tweets(tweets: JsonField>) = apply { + this.tweets = tweets.map { it.toMutableList() } + } + + /** + * Adds a single [SearchTweet] to [tweets]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTweet(tweet: SearchTweet) = apply { + tweets = + (tweets ?: JsonField.of(mutableListOf())).also { + checkKnown("tweets", it).add(tweet) + } + } + + /** Coverage evidence across parallel search strategies. */ + fun diagnostic(diagnostic: Diagnostic) = diagnostic(JsonField.of(diagnostic)) + + /** + * Sets [Builder.diagnostic] to an arbitrary JSON value. + * + * You should usually call [Builder.diagnostic] with a well-typed [Diagnostic] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun diagnostic(diagnostic: JsonField) = apply { + this.diagnostic = diagnostic + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TweetSearchCoverageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .tweets() + * .diagnostic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TweetSearchCoverageResponse = + TweetSearchCoverageResponse( + checkRequired("hasNextPage", hasNextPage), + checkRequired("nextCursor", nextCursor), + checkRequired("tweets", tweets).map { it.toImmutable() }, + checkRequired("diagnostic", diagnostic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): TweetSearchCoverageResponse = apply { + if (validated) { + return@apply + } + + hasNextPage() + nextCursor() + tweets().forEach { it.validate() } + diagnostic().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (hasNextPage.asKnown().isPresent) 1 else 0) + + (if (nextCursor.asKnown().isPresent) 1 else 0) + + (tweets.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (diagnostic.asKnown().getOrNull()?.validity() ?: 0) + + /** Coverage evidence across parallel search strategies. */ + class Diagnostic + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val complete: JsonField, + private val cursorFailureCount: JsonField, + private val deadlineReached: JsonField, + private val duplicateCount: JsonField, + private val failedStrategyCount: JsonField, + private val malformedCount: JsonField, + private val pagesFetched: JsonField, + private val partitioned: JsonField, + private val responseTruncated: JsonField, + private val resultLimitReached: JsonField, + private val returnedTweets: JsonField, + private val stalledStrategyCount: JsonField, + private val strategies: JsonField>, + private val strategyCount: JsonField, + private val uniqueTweets: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("complete") + @ExcludeMissing + complete: JsonField = JsonMissing.of(), + @JsonProperty("cursorFailureCount") + @ExcludeMissing + cursorFailureCount: JsonField = JsonMissing.of(), + @JsonProperty("deadlineReached") + @ExcludeMissing + deadlineReached: JsonField = JsonMissing.of(), + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("failedStrategyCount") + @ExcludeMissing + failedStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("malformedCount") + @ExcludeMissing + malformedCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("partitioned") + @ExcludeMissing + partitioned: JsonField = JsonMissing.of(), + @JsonProperty("responseTruncated") + @ExcludeMissing + responseTruncated: JsonField = JsonMissing.of(), + @JsonProperty("resultLimitReached") + @ExcludeMissing + resultLimitReached: JsonField = JsonMissing.of(), + @JsonProperty("returnedTweets") + @ExcludeMissing + returnedTweets: JsonField = JsonMissing.of(), + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + stalledStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("strategies") + @ExcludeMissing + strategies: JsonField> = JsonMissing.of(), + @JsonProperty("strategyCount") + @ExcludeMissing + strategyCount: JsonField = JsonMissing.of(), + @JsonProperty("uniqueTweets") + @ExcludeMissing + uniqueTweets: JsonField = JsonMissing.of(), + ) : this( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + partitioned, + responseTruncated, + resultLimitReached, + returnedTweets, + stalledStrategyCount, + strategies, + strategyCount, + uniqueTweets, + mutableMapOf(), + ) + + /** + * True when every strategy exhausted its source. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun complete(): Boolean = complete.getRequired("complete") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun cursorFailureCount(): Long = cursorFailureCount.getRequired("cursorFailureCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun deadlineReached(): Boolean = deadlineReached.getRequired("deadlineReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun failedStrategyCount(): Long = failedStrategyCount.getRequired("failedStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun malformedCount(): Long = malformedCount.getRequired("malformedCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * Whether bounded time windows ran in parallel. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun partitioned(): Boolean = partitioned.getRequired("partitioned") + + /** + * Whether credits or the requested limit reduced output. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun responseTruncated(): Boolean = responseTruncated.getRequired("responseTruncated") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun resultLimitReached(): Boolean = resultLimitReached.getRequired("resultLimitReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun returnedTweets(): Long = returnedTweets.getRequired("returnedTweets") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun stalledStrategyCount(): Long = + stalledStrategyCount.getRequired("stalledStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategies(): List = strategies.getRequired("strategies") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategyCount(): Long = strategyCount.getRequired("strategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun uniqueTweets(): Long = uniqueTweets.getRequired("uniqueTweets") + + /** + * Returns the raw JSON value of [complete]. + * + * Unlike [complete], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("complete") @ExcludeMissing fun _complete(): JsonField = complete + + /** + * Returns the raw JSON value of [cursorFailureCount]. + * + * Unlike [cursorFailureCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("cursorFailureCount") + @ExcludeMissing + fun _cursorFailureCount(): JsonField = cursorFailureCount + + /** + * Returns the raw JSON value of [deadlineReached]. + * + * Unlike [deadlineReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("deadlineReached") + @ExcludeMissing + fun _deadlineReached(): JsonField = deadlineReached + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [failedStrategyCount]. + * + * Unlike [failedStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("failedStrategyCount") + @ExcludeMissing + fun _failedStrategyCount(): JsonField = failedStrategyCount + + /** + * Returns the raw JSON value of [malformedCount]. + * + * Unlike [malformedCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("malformedCount") + @ExcludeMissing + fun _malformedCount(): JsonField = malformedCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [partitioned]. + * + * Unlike [partitioned], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("partitioned") + @ExcludeMissing + fun _partitioned(): JsonField = partitioned + + /** + * Returns the raw JSON value of [responseTruncated]. + * + * Unlike [responseTruncated], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("responseTruncated") + @ExcludeMissing + fun _responseTruncated(): JsonField = responseTruncated + + /** + * Returns the raw JSON value of [resultLimitReached]. + * + * Unlike [resultLimitReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("resultLimitReached") + @ExcludeMissing + fun _resultLimitReached(): JsonField = resultLimitReached + + /** + * Returns the raw JSON value of [returnedTweets]. + * + * Unlike [returnedTweets], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("returnedTweets") + @ExcludeMissing + fun _returnedTweets(): JsonField = returnedTweets + + /** + * Returns the raw JSON value of [stalledStrategyCount]. + * + * Unlike [stalledStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + fun _stalledStrategyCount(): JsonField = stalledStrategyCount + + /** + * Returns the raw JSON value of [strategies]. + * + * Unlike [strategies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategies") + @ExcludeMissing + fun _strategies(): JsonField> = strategies + + /** + * Returns the raw JSON value of [strategyCount]. + * + * Unlike [strategyCount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategyCount") + @ExcludeMissing + fun _strategyCount(): JsonField = strategyCount + + /** + * Returns the raw JSON value of [uniqueTweets]. + * + * Unlike [uniqueTweets], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("uniqueTweets") + @ExcludeMissing + fun _uniqueTweets(): JsonField = uniqueTweets + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Diagnostic]. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .partitioned() + * .responseTruncated() + * .resultLimitReached() + * .returnedTweets() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueTweets() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Diagnostic]. */ + class Builder internal constructor() { + + private var complete: JsonField? = null + private var cursorFailureCount: JsonField? = null + private var deadlineReached: JsonField? = null + private var duplicateCount: JsonField? = null + private var failedStrategyCount: JsonField? = null + private var malformedCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var partitioned: JsonField? = null + private var responseTruncated: JsonField? = null + private var resultLimitReached: JsonField? = null + private var returnedTweets: JsonField? = null + private var stalledStrategyCount: JsonField? = null + private var strategies: JsonField>? = null + private var strategyCount: JsonField? = null + private var uniqueTweets: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(diagnostic: Diagnostic) = apply { + complete = diagnostic.complete + cursorFailureCount = diagnostic.cursorFailureCount + deadlineReached = diagnostic.deadlineReached + duplicateCount = diagnostic.duplicateCount + failedStrategyCount = diagnostic.failedStrategyCount + malformedCount = diagnostic.malformedCount + pagesFetched = diagnostic.pagesFetched + partitioned = diagnostic.partitioned + responseTruncated = diagnostic.responseTruncated + resultLimitReached = diagnostic.resultLimitReached + returnedTweets = diagnostic.returnedTweets + stalledStrategyCount = diagnostic.stalledStrategyCount + strategies = diagnostic.strategies.map { it.toMutableList() } + strategyCount = diagnostic.strategyCount + uniqueTweets = diagnostic.uniqueTweets + additionalProperties = diagnostic.additionalProperties.toMutableMap() + } + + /** True when every strategy exhausted its source. */ + fun complete(complete: Boolean) = complete(JsonField.of(complete)) + + /** + * Sets [Builder.complete] to an arbitrary JSON value. + * + * You should usually call [Builder.complete] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun complete(complete: JsonField) = apply { this.complete = complete } + + fun cursorFailureCount(cursorFailureCount: Long) = + cursorFailureCount(JsonField.of(cursorFailureCount)) + + /** + * Sets [Builder.cursorFailureCount] to an arbitrary JSON value. + * + * You should usually call [Builder.cursorFailureCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun cursorFailureCount(cursorFailureCount: JsonField) = apply { + this.cursorFailureCount = cursorFailureCount + } + + fun deadlineReached(deadlineReached: Boolean) = + deadlineReached(JsonField.of(deadlineReached)) + + /** + * Sets [Builder.deadlineReached] to an arbitrary JSON value. + * + * You should usually call [Builder.deadlineReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun deadlineReached(deadlineReached: JsonField) = apply { + this.deadlineReached = deadlineReached + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun failedStrategyCount(failedStrategyCount: Long) = + failedStrategyCount(JsonField.of(failedStrategyCount)) + + /** + * Sets [Builder.failedStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.failedStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun failedStrategyCount(failedStrategyCount: JsonField) = apply { + this.failedStrategyCount = failedStrategyCount + } + + fun malformedCount(malformedCount: Long) = + malformedCount(JsonField.of(malformedCount)) + + /** + * Sets [Builder.malformedCount] to an arbitrary JSON value. + * + * You should usually call [Builder.malformedCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun malformedCount(malformedCount: JsonField) = apply { + this.malformedCount = malformedCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + /** Whether bounded time windows ran in parallel. */ + fun partitioned(partitioned: Boolean) = partitioned(JsonField.of(partitioned)) + + /** + * Sets [Builder.partitioned] to an arbitrary JSON value. + * + * You should usually call [Builder.partitioned] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun partitioned(partitioned: JsonField) = apply { + this.partitioned = partitioned + } + + /** Whether credits or the requested limit reduced output. */ + fun responseTruncated(responseTruncated: Boolean) = + responseTruncated(JsonField.of(responseTruncated)) + + /** + * Sets [Builder.responseTruncated] to an arbitrary JSON value. + * + * You should usually call [Builder.responseTruncated] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun responseTruncated(responseTruncated: JsonField) = apply { + this.responseTruncated = responseTruncated + } + + fun resultLimitReached(resultLimitReached: Boolean) = + resultLimitReached(JsonField.of(resultLimitReached)) + + /** + * Sets [Builder.resultLimitReached] to an arbitrary JSON value. + * + * You should usually call [Builder.resultLimitReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun resultLimitReached(resultLimitReached: JsonField) = apply { + this.resultLimitReached = resultLimitReached + } + + fun returnedTweets(returnedTweets: Long) = + returnedTweets(JsonField.of(returnedTweets)) + + /** + * Sets [Builder.returnedTweets] to an arbitrary JSON value. + * + * You should usually call [Builder.returnedTweets] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun returnedTweets(returnedTweets: JsonField) = apply { + this.returnedTweets = returnedTweets + } + + fun stalledStrategyCount(stalledStrategyCount: Long) = + stalledStrategyCount(JsonField.of(stalledStrategyCount)) + + /** + * Sets [Builder.stalledStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.stalledStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun stalledStrategyCount(stalledStrategyCount: JsonField) = apply { + this.stalledStrategyCount = stalledStrategyCount + } + + fun strategies(strategies: List) = strategies(JsonField.of(strategies)) + + /** + * Sets [Builder.strategies] to an arbitrary JSON value. + * + * You should usually call [Builder.strategies] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun strategies(strategies: JsonField>) = apply { + this.strategies = strategies.map { it.toMutableList() } + } + + /** + * Adds a single [Strategy] to [strategies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addStrategy(strategy: Strategy) = apply { + strategies = + (strategies ?: JsonField.of(mutableListOf())).also { + checkKnown("strategies", it).add(strategy) + } + } + + fun strategyCount(strategyCount: Long) = strategyCount(JsonField.of(strategyCount)) + + /** + * Sets [Builder.strategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.strategyCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun strategyCount(strategyCount: JsonField) = apply { + this.strategyCount = strategyCount + } + + fun uniqueTweets(uniqueTweets: Long) = uniqueTweets(JsonField.of(uniqueTweets)) + + /** + * Sets [Builder.uniqueTweets] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueTweets] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun uniqueTweets(uniqueTweets: JsonField) = apply { + this.uniqueTweets = uniqueTweets + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Diagnostic]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .partitioned() + * .responseTruncated() + * .resultLimitReached() + * .returnedTweets() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueTweets() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Diagnostic = + Diagnostic( + checkRequired("complete", complete), + checkRequired("cursorFailureCount", cursorFailureCount), + checkRequired("deadlineReached", deadlineReached), + checkRequired("duplicateCount", duplicateCount), + checkRequired("failedStrategyCount", failedStrategyCount), + checkRequired("malformedCount", malformedCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("partitioned", partitioned), + checkRequired("responseTruncated", responseTruncated), + checkRequired("resultLimitReached", resultLimitReached), + checkRequired("returnedTweets", returnedTweets), + checkRequired("stalledStrategyCount", stalledStrategyCount), + checkRequired("strategies", strategies).map { it.toImmutable() }, + checkRequired("strategyCount", strategyCount), + checkRequired("uniqueTweets", uniqueTweets), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Diagnostic = apply { + if (validated) { + return@apply + } + + complete() + cursorFailureCount() + deadlineReached() + duplicateCount() + failedStrategyCount() + malformedCount() + pagesFetched() + partitioned() + responseTruncated() + resultLimitReached() + returnedTweets() + stalledStrategyCount() + strategies().forEach { it.validate() } + strategyCount() + uniqueTweets() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (complete.asKnown().isPresent) 1 else 0) + + (if (cursorFailureCount.asKnown().isPresent) 1 else 0) + + (if (deadlineReached.asKnown().isPresent) 1 else 0) + + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (failedStrategyCount.asKnown().isPresent) 1 else 0) + + (if (malformedCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (if (partitioned.asKnown().isPresent) 1 else 0) + + (if (responseTruncated.asKnown().isPresent) 1 else 0) + + (if (resultLimitReached.asKnown().isPresent) 1 else 0) + + (if (returnedTweets.asKnown().isPresent) 1 else 0) + + (if (stalledStrategyCount.asKnown().isPresent) 1 else 0) + + (strategies.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (strategyCount.asKnown().isPresent) 1 else 0) + + (if (uniqueTweets.asKnown().isPresent) 1 else 0) + + class Strategy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val duplicateCount: JsonField, + private val pagesFetched: JsonField, + private val queryType: JsonField, + private val stopReason: JsonField, + private val strategy: JsonField, + private val uniqueAdded: JsonField, + private val window: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("queryType") + @ExcludeMissing + queryType: JsonField = JsonMissing.of(), + @JsonProperty("stopReason") + @ExcludeMissing + stopReason: JsonField = JsonMissing.of(), + @JsonProperty("strategy") + @ExcludeMissing + strategy: JsonField = JsonMissing.of(), + @JsonProperty("uniqueAdded") + @ExcludeMissing + uniqueAdded: JsonField = JsonMissing.of(), + @JsonProperty("window") + @ExcludeMissing + window: JsonField = JsonMissing.of(), + ) : this( + duplicateCount, + pagesFetched, + queryType, + stopReason, + strategy, + uniqueAdded, + window, + mutableMapOf(), + ) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun queryType(): QueryType = queryType.getRequired("queryType") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun stopReason(): StopReason = stopReason.getRequired("stopReason") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun strategy(): Long = strategy.getRequired("strategy") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun uniqueAdded(): Long = uniqueAdded.getRequired("uniqueAdded") + + /** + * Non-overlapping time partition used by one strategy. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun window(): Optional = window.getOptional("window") + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [queryType]. + * + * Unlike [queryType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("queryType") + @ExcludeMissing + fun _queryType(): JsonField = queryType + + /** + * Returns the raw JSON value of [stopReason]. + * + * Unlike [stopReason], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stopReason") + @ExcludeMissing + fun _stopReason(): JsonField = stopReason + + /** + * Returns the raw JSON value of [strategy]. + * + * Unlike [strategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategy") + @ExcludeMissing + fun _strategy(): JsonField = strategy + + /** + * Returns the raw JSON value of [uniqueAdded]. + * + * Unlike [uniqueAdded], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("uniqueAdded") + @ExcludeMissing + fun _uniqueAdded(): JsonField = uniqueAdded + + /** + * Returns the raw JSON value of [window]. + * + * Unlike [window], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("window") @ExcludeMissing fun _window(): JsonField = window + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Strategy]. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .queryType() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Strategy]. */ + class Builder internal constructor() { + + private var duplicateCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var queryType: JsonField? = null + private var stopReason: JsonField? = null + private var strategy: JsonField? = null + private var uniqueAdded: JsonField? = null + private var window: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(strategy: Strategy) = apply { + duplicateCount = strategy.duplicateCount + pagesFetched = strategy.pagesFetched + queryType = strategy.queryType + stopReason = strategy.stopReason + this.strategy = strategy.strategy + uniqueAdded = strategy.uniqueAdded + window = strategy.window + additionalProperties = strategy.additionalProperties.toMutableMap() + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + fun queryType(queryType: QueryType) = queryType(JsonField.of(queryType)) + + /** + * Sets [Builder.queryType] to an arbitrary JSON value. + * + * You should usually call [Builder.queryType] with a well-typed [QueryType] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun queryType(queryType: JsonField) = apply { + this.queryType = queryType + } + + fun stopReason(stopReason: StopReason) = stopReason(JsonField.of(stopReason)) + + /** + * Sets [Builder.stopReason] to an arbitrary JSON value. + * + * You should usually call [Builder.stopReason] with a well-typed [StopReason] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun stopReason(stopReason: JsonField) = apply { + this.stopReason = stopReason + } + + fun strategy(strategy: Long) = strategy(JsonField.of(strategy)) + + /** + * Sets [Builder.strategy] to an arbitrary JSON value. + * + * You should usually call [Builder.strategy] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun strategy(strategy: JsonField) = apply { this.strategy = strategy } + + fun uniqueAdded(uniqueAdded: Long) = uniqueAdded(JsonField.of(uniqueAdded)) + + /** + * Sets [Builder.uniqueAdded] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueAdded] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun uniqueAdded(uniqueAdded: JsonField) = apply { + this.uniqueAdded = uniqueAdded + } + + /** Non-overlapping time partition used by one strategy. */ + fun window(window: Window) = window(JsonField.of(window)) + + /** + * Sets [Builder.window] to an arbitrary JSON value. + * + * You should usually call [Builder.window] with a well-typed [Window] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun window(window: JsonField) = apply { this.window = window } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Strategy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .queryType() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Strategy = + Strategy( + checkRequired("duplicateCount", duplicateCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("queryType", queryType), + checkRequired("stopReason", stopReason), + checkRequired("strategy", strategy), + checkRequired("uniqueAdded", uniqueAdded), + window, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Strategy = apply { + if (validated) { + return@apply + } + + duplicateCount() + pagesFetched() + queryType().validate() + stopReason().validate() + strategy() + uniqueAdded() + window().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (queryType.asKnown().getOrNull()?.validity() ?: 0) + + (stopReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (strategy.asKnown().isPresent) 1 else 0) + + (if (uniqueAdded.asKnown().isPresent) 1 else 0) + + (window.asKnown().getOrNull()?.validity() ?: 0) + + class QueryType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + @JvmField val LATEST = of("Latest") + + @JvmField val TOP = of("Top") + + @JvmStatic fun of(value: String) = QueryType(JsonField.of(value)) + } + + /** An enum containing [QueryType]'s known values. */ + enum class Known { + LATEST, + TOP, + } + + /** + * An enum containing [QueryType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [QueryType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LATEST, + TOP, + /** + * An enum member indicating that [QueryType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LATEST -> Value.LATEST + TOP -> Value.TOP + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is + * a not a known member. + */ + fun known(): Known = + when (this) { + LATEST -> Known.LATEST + TOP -> Known.TOP + else -> + throw XTwitterScraperInvalidDataException( + "Unknown QueryType: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): QueryType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QueryType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class StopReason + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + @JvmField val CURSOR_FAILURE = of("cursor_failure") + + @JvmField val DEADLINE = of("deadline") + + @JvmField val EXHAUSTED = of("exhausted") + + @JvmField val FAILED = of("failed") + + @JvmField val PAGE_LIMIT = of("page_limit") + + @JvmField val RESULT_LIMIT = of("result_limit") + + @JvmField val STALLED = of("stalled") + + @JvmStatic fun of(value: String) = StopReason(JsonField.of(value)) + } + + /** An enum containing [StopReason]'s known values. */ + enum class Known { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + } + + /** + * An enum containing [StopReason]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [StopReason] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + /** + * An enum member indicating that [StopReason] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CURSOR_FAILURE -> Value.CURSOR_FAILURE + DEADLINE -> Value.DEADLINE + EXHAUSTED -> Value.EXHAUSTED + FAILED -> Value.FAILED + PAGE_LIMIT -> Value.PAGE_LIMIT + RESULT_LIMIT -> Value.RESULT_LIMIT + STALLED -> Value.STALLED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is + * a not a known member. + */ + fun known(): Known = + when (this) { + CURSOR_FAILURE -> Known.CURSOR_FAILURE + DEADLINE -> Known.DEADLINE + EXHAUSTED -> Known.EXHAUSTED + FAILED -> Known.FAILED + PAGE_LIMIT -> Known.PAGE_LIMIT + RESULT_LIMIT -> Known.RESULT_LIMIT + STALLED -> Known.STALLED + else -> + throw XTwitterScraperInvalidDataException( + "Unknown StopReason: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): StopReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StopReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Non-overlapping time partition used by one strategy. */ + class Window + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val sinceTime: JsonField, + private val untilTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("sinceTime") + @ExcludeMissing + sinceTime: JsonField = JsonMissing.of(), + @JsonProperty("untilTime") + @ExcludeMissing + untilTime: JsonField = JsonMissing.of(), + ) : this(sinceTime, untilTime, mutableMapOf()) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an + * unexpected type or is unexpectedly missing or null (e.g. if the server + * responded with an unexpected value). + */ + fun sinceTime(): OffsetDateTime = sinceTime.getRequired("sinceTime") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an + * unexpected type or is unexpectedly missing or null (e.g. if the server + * responded with an unexpected value). + */ + fun untilTime(): OffsetDateTime = untilTime.getRequired("untilTime") + + /** + * Returns the raw JSON value of [sinceTime]. + * + * Unlike [sinceTime], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("sinceTime") + @ExcludeMissing + fun _sinceTime(): JsonField = sinceTime + + /** + * Returns the raw JSON value of [untilTime]. + * + * Unlike [untilTime], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("untilTime") + @ExcludeMissing + fun _untilTime(): JsonField = untilTime + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Window]. + * + * The following fields are required: + * ```java + * .sinceTime() + * .untilTime() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Window]. */ + class Builder internal constructor() { + + private var sinceTime: JsonField? = null + private var untilTime: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(window: Window) = apply { + sinceTime = window.sinceTime + untilTime = window.untilTime + additionalProperties = window.additionalProperties.toMutableMap() + } + + fun sinceTime(sinceTime: OffsetDateTime) = + sinceTime(JsonField.of(sinceTime)) + + /** + * Sets [Builder.sinceTime] to an arbitrary JSON value. + * + * You should usually call [Builder.sinceTime] with a well-typed + * [OffsetDateTime] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun sinceTime(sinceTime: JsonField) = apply { + this.sinceTime = sinceTime + } + + fun untilTime(untilTime: OffsetDateTime) = + untilTime(JsonField.of(untilTime)) + + /** + * Sets [Builder.untilTime] to an arbitrary JSON value. + * + * You should usually call [Builder.untilTime] with a well-typed + * [OffsetDateTime] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun untilTime(untilTime: JsonField) = apply { + this.untilTime = untilTime + } + + fun additionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties( + additionalProperties: Map + ) = apply { this.additionalProperties.putAll(additionalProperties) } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Window]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .sinceTime() + * .untilTime() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Window = + Window( + checkRequired("sinceTime", sinceTime), + checkRequired("untilTime", untilTime), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Window = apply { + if (validated) { + return@apply + } + + sinceTime() + untilTime() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (sinceTime.asKnown().isPresent) 1 else 0) + + (if (untilTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Window && + sinceTime == other.sinceTime && + untilTime == other.untilTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(sinceTime, untilTime, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Window{sinceTime=$sinceTime, untilTime=$untilTime, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Strategy && + duplicateCount == other.duplicateCount && + pagesFetched == other.pagesFetched && + queryType == other.queryType && + stopReason == other.stopReason && + strategy == other.strategy && + uniqueAdded == other.uniqueAdded && + window == other.window && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + duplicateCount, + pagesFetched, + queryType, + stopReason, + strategy, + uniqueAdded, + window, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Strategy{duplicateCount=$duplicateCount, pagesFetched=$pagesFetched, queryType=$queryType, stopReason=$stopReason, strategy=$strategy, uniqueAdded=$uniqueAdded, window=$window, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Diagnostic && + complete == other.complete && + cursorFailureCount == other.cursorFailureCount && + deadlineReached == other.deadlineReached && + duplicateCount == other.duplicateCount && + failedStrategyCount == other.failedStrategyCount && + malformedCount == other.malformedCount && + pagesFetched == other.pagesFetched && + partitioned == other.partitioned && + responseTruncated == other.responseTruncated && + resultLimitReached == other.resultLimitReached && + returnedTweets == other.returnedTweets && + stalledStrategyCount == other.stalledStrategyCount && + strategies == other.strategies && + strategyCount == other.strategyCount && + uniqueTweets == other.uniqueTweets && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + partitioned, + responseTruncated, + resultLimitReached, + returnedTweets, + stalledStrategyCount, + strategies, + strategyCount, + uniqueTweets, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Diagnostic{complete=$complete, cursorFailureCount=$cursorFailureCount, deadlineReached=$deadlineReached, duplicateCount=$duplicateCount, failedStrategyCount=$failedStrategyCount, malformedCount=$malformedCount, pagesFetched=$pagesFetched, partitioned=$partitioned, responseTruncated=$responseTruncated, resultLimitReached=$resultLimitReached, returnedTweets=$returnedTweets, stalledStrategyCount=$stalledStrategyCount, strategies=$strategies, strategyCount=$strategyCount, uniqueTweets=$uniqueTweets, additionalProperties=$additionalProperties}" + } + + class HasNextPage @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FALSE = of(false) + + @JvmStatic fun of(value: Boolean) = HasNextPage(JsonField.of(value)) + } + + /** An enum containing [HasNextPage]'s known values. */ + enum class Known { + FALSE + } + + /** + * An enum containing [HasNextPage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [HasNextPage] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FALSE, + /** + * An enum member indicating that [HasNextPage] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FALSE -> Value.FALSE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + FALSE -> Known.FALSE + else -> throw XTwitterScraperInvalidDataException("Unknown HasNextPage: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asBoolean(): Boolean = + _value().asBoolean().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a Boolean") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): HasNextPage = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is HasNextPage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class NextCursor @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val EMPTY = of("") + + @JvmStatic fun of(value: String) = NextCursor(JsonField.of(value)) + } + + /** An enum containing [NextCursor]'s known values. */ + enum class Known { + EMPTY + } + + /** + * An enum containing [NextCursor]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [NextCursor] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + EMPTY, + /** + * An enum member indicating that [NextCursor] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + EMPTY -> Value.EMPTY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + EMPTY -> Known.EMPTY + else -> throw XTwitterScraperInvalidDataException("Unknown NextCursor: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): NextCursor = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NextCursor && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TweetSearchCoverageResponse && + hasNextPage == other.hasNextPage && + nextCursor == other.nextCursor && + tweets == other.tweets && + diagnostic == other.diagnostic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(hasNextPage, nextCursor, tweets, diagnostic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TweetSearchCoverageResponse{hasNextPage=$hasNextPage, nextCursor=$nextCursor, tweets=$tweets, diagnostic=$diagnostic, additionalProperties=$additionalProperties}" + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParams.kt index 302e8e51..48765887 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParams.kt @@ -6,9 +6,13 @@ package com.x_twitter_scraper.api.models.x.users +import com.fasterxml.jackson.annotation.JsonCreator +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.Params import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -18,9 +22,24 @@ class UserRetrieveFollowersParams private constructor( private val id: String?, private val after: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, private val limit: Long?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, + private val mode: Mode?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -30,20 +49,72 @@ private constructor( /** Legacy cursor alias. Prefer cursor. */ fun after(): Optional = Optional.ofNullable(after) - /** Pagination cursor for followers list */ + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) - /** Legacy integer page size alias for following lists. Prefer pageSize. */ + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ fun limit(): Optional = Optional.ofNullable(limit) + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + fun mode(): Optional = Optional.ofNullable(mode) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. Continue + * with has_next_page. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -67,9 +138,24 @@ private constructor( private var id: String? = null private var after: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null private var limit: Long? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null + private var mode: Mode? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -77,9 +163,24 @@ private constructor( internal fun from(userRetrieveFollowersParams: UserRetrieveFollowersParams) = apply { id = userRetrieveFollowersParams.id after = userRetrieveFollowersParams.after + bioContains = userRetrieveFollowersParams.bioContains cursor = userRetrieveFollowersParams.cursor + hasLocation = userRetrieveFollowersParams.hasLocation + hasWebsite = userRetrieveFollowersParams.hasWebsite limit = userRetrieveFollowersParams.limit + locationContains = userRetrieveFollowersParams.locationContains + maxFollowers = userRetrieveFollowersParams.maxFollowers + maxFollowing = userRetrieveFollowersParams.maxFollowing + maxStatuses = userRetrieveFollowersParams.maxStatuses + minAccountAgeDays = userRetrieveFollowersParams.minAccountAgeDays + minFollowers = userRetrieveFollowersParams.minFollowers + minFollowing = userRetrieveFollowersParams.minFollowing + minStatuses = userRetrieveFollowersParams.minStatuses + mode = userRetrieveFollowersParams.mode pageSize = userRetrieveFollowersParams.pageSize + usernameContains = userRetrieveFollowersParams.usernameContains + verifiedOnly = userRetrieveFollowersParams.verifiedOnly + verifiedType = userRetrieveFollowersParams.verifiedType additionalHeaders = userRetrieveFollowersParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveFollowersParams.additionalQueryParams.toBuilder() } @@ -95,13 +196,51 @@ private constructor( /** Alias for calling [Builder.after] with `after.orElse(null)`. */ fun after(after: Optional) = after(after.getOrNull()) - /** Pagination cursor for followers list */ + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) - /** Legacy integer page size alias for following lists. Prefer pageSize. */ + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ fun limit(limit: Long?) = apply { this.limit = limit } /** @@ -114,11 +253,122 @@ private constructor( /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ fun limit(limit: Optional) = limit(limit.getOrNull()) + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage + * returns diagnostics once and rejects cursors. + */ + fun mode(mode: Mode?) = apply { this.mode = mode } + + /** Alias for calling [Builder.mode] with `mode.orElse(null)`. */ + fun mode(mode: Optional) = mode(mode.getOrNull()) + + /** + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. + * Continue with has_next_page. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -132,6 +382,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -239,9 +517,24 @@ private constructor( UserRetrieveFollowersParams( id, after, + bioContains, cursor, + hasLocation, + hasWebsite, limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -259,13 +552,168 @@ private constructor( QueryParams.builder() .apply { after?.let { put("after", it) } + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } limit?.let { put("limit", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } + mode?.let { put("mode", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val STANDARD = of("standard") + + @JvmField val COVERAGE = of("coverage") + + @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) + } + + /** An enum containing [Mode]'s known values. */ + enum class Known { + STANDARD, + COVERAGE, + } + + /** + * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Mode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + STANDARD, + COVERAGE, + /** An enum member indicating that [Mode] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + STANDARD -> Value.STANDARD + COVERAGE -> Value.COVERAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + STANDARD -> Known.STANDARD + COVERAGE -> Known.COVERAGE + else -> throw XTwitterScraperInvalidDataException("Unknown Mode: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Mode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Mode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -274,16 +722,54 @@ private constructor( return other is UserRetrieveFollowersParams && id == other.id && after == other.after && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && limit == other.limit && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && + mode == other.mode && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, after, cursor, limit, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + after, + bioContains, + cursor, + hasLocation, + hasWebsite, + limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "UserRetrieveFollowersParams{id=$id, after=$after, cursor=$cursor, limit=$limit, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveFollowersParams{id=$id, after=$after, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, limit=$limit, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, mode=$mode, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponse.kt new file mode 100644 index 00000000..6a025ae9 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponse.kt @@ -0,0 +1,2224 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.ExcludeMissing +import com.x_twitter_scraper.api.core.JsonField +import com.x_twitter_scraper.api.core.JsonMissing +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow +import com.x_twitter_scraper.api.core.toImmutable +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy behavior. + * Follow next_cursor while has_next_page is true. + */ +@JsonDeserialize(using = UserRetrieveFollowersResponse.Deserializer::class) +@JsonSerialize(using = UserRetrieveFollowersResponse.Serializer::class) +class UserRetrieveFollowersResponse +private constructor( + private val paginatedUsers: PaginatedUsers? = null, + private val listCoverage: UserListCoverageResponse? = null, + private val _json: JsonValue? = null, +) { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun paginatedUsers(): Optional = Optional.ofNullable(paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun listCoverage(): Optional = Optional.ofNullable(listCoverage) + + fun isPaginatedUsers(): Boolean = paginatedUsers != null + + fun isListCoverage(): Boolean = listCoverage != null + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asPaginatedUsers(): PaginatedUsers = paginatedUsers.getOrThrow("paginatedUsers") + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asListCoverage(): UserListCoverageResponse = listCoverage.getOrThrow("listCoverage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = userRetrieveFollowersResponse.accept(new UserRetrieveFollowersResponse.Visitor>() { + * @Override + * public Optional visitPaginatedUsers(PaginatedUsers paginatedUsers) { + * return Optional.of(paginatedUsers.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + paginatedUsers != null -> visitor.visitPaginatedUsers(paginatedUsers) + listCoverage != null -> visitor.visitListCoverage(listCoverage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UserRetrieveFollowersResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) { + paginatedUsers.validate() + } + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) { + listCoverage.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) = + paginatedUsers.validity() + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) = + listCoverage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserRetrieveFollowersResponse && + paginatedUsers == other.paginatedUsers && + listCoverage == other.listCoverage + } + + override fun hashCode(): Int = Objects.hash(paginatedUsers, listCoverage) + + override fun toString(): String = + when { + paginatedUsers != null -> + "UserRetrieveFollowersResponse{paginatedUsers=$paginatedUsers}" + listCoverage != null -> "UserRetrieveFollowersResponse{listCoverage=$listCoverage}" + _json != null -> "UserRetrieveFollowersResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UserRetrieveFollowersResponse") + } + + companion object { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofPaginatedUsers(paginatedUsers: PaginatedUsers) = + UserRetrieveFollowersResponse(paginatedUsers = paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofListCoverage(listCoverage: UserListCoverageResponse) = + UserRetrieveFollowersResponse(listCoverage = listCoverage) + } + + /** + * An interface that defines how to map each variant of [UserRetrieveFollowersResponse] to a + * value of type [T]. + */ + interface Visitor { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitPaginatedUsers(paginatedUsers: PaginatedUsers): T + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitListCoverage(listCoverage: UserListCoverageResponse): T + + /** + * Maps an unknown variant of [UserRetrieveFollowersResponse] to a value of type [T]. + * + * An instance of [UserRetrieveFollowersResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException( + "Unknown UserRetrieveFollowersResponse: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(UserRetrieveFollowersResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): UserRetrieveFollowersResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveFollowersResponse(paginatedUsers = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveFollowersResponse(listCoverage = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> UserRetrieveFollowersResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(UserRetrieveFollowersResponse::class) { + + override fun serialize( + value: UserRetrieveFollowersResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.paginatedUsers != null -> generator.writeObject(value.paginatedUsers) + value.listCoverage != null -> generator.writeObject(value.listCoverage) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UserRetrieveFollowersResponse") + } + } + } + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + class UserListCoverageResponse + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val hasNextPage: JsonField, + private val nextCursor: JsonField, + private val users: JsonField>, + private val diagnostic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("has_next_page") + @ExcludeMissing + hasNextPage: JsonField = JsonMissing.of(), + @JsonProperty("next_cursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("users") + @ExcludeMissing + users: JsonField> = JsonMissing.of(), + @JsonProperty("diagnostic") + @ExcludeMissing + diagnostic: JsonField = JsonMissing.of(), + ) : this(hasNextPage, nextCursor, users, diagnostic, mutableMapOf()) + + fun toPaginatedUsers(): PaginatedUsers = + PaginatedUsers.builder() + .hasNextPage(hasNextPage) + .nextCursor(nextCursor) + .users(users) + .build() + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun hasNextPage(): Boolean = hasNextPage.getRequired("has_next_page") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun nextCursor(): String = nextCursor.getRequired("next_cursor") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun users(): List = users.getRequired("users") + + /** + * Coverage evidence across parallel relationship strategies. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun diagnostic(): Diagnostic = diagnostic.getRequired("diagnostic") + + /** + * Returns the raw JSON value of [hasNextPage]. + * + * Unlike [hasNextPage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("has_next_page") + @ExcludeMissing + fun _hasNextPage(): JsonField = hasNextPage + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("next_cursor") + @ExcludeMissing + fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [users]. + * + * Unlike [users], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("users") @ExcludeMissing fun _users(): JsonField> = users + + /** + * Returns the raw JSON value of [diagnostic]. + * + * Unlike [diagnostic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("diagnostic") + @ExcludeMissing + fun _diagnostic(): JsonField = diagnostic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UserListCoverageResponse]. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UserListCoverageResponse]. */ + class Builder internal constructor() { + + private var hasNextPage: JsonField? = null + private var nextCursor: JsonField? = null + private var users: JsonField>? = null + private var diagnostic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(userListCoverageResponse: UserListCoverageResponse) = apply { + hasNextPage = userListCoverageResponse.hasNextPage + nextCursor = userListCoverageResponse.nextCursor + users = userListCoverageResponse.users.map { it.toMutableList() } + diagnostic = userListCoverageResponse.diagnostic + additionalProperties = userListCoverageResponse.additionalProperties.toMutableMap() + } + + fun hasNextPage(hasNextPage: Boolean) = hasNextPage(JsonField.of(hasNextPage)) + + /** + * Sets [Builder.hasNextPage] to an arbitrary JSON value. + * + * You should usually call [Builder.hasNextPage] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasNextPage(hasNextPage: JsonField) = apply { + this.hasNextPage = hasNextPage + } + + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + fun users(users: List) = users(JsonField.of(users)) + + /** + * Sets [Builder.users] to an arbitrary JSON value. + * + * You should usually call [Builder.users] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun users(users: JsonField>) = apply { + this.users = users.map { it.toMutableList() } + } + + /** + * Adds a single [UserProfile] to [users]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addUser(user: UserProfile) = apply { + users = + (users ?: JsonField.of(mutableListOf())).also { + checkKnown("users", it).add(user) + } + } + + /** Coverage evidence across parallel relationship strategies. */ + fun diagnostic(diagnostic: Diagnostic) = diagnostic(JsonField.of(diagnostic)) + + /** + * Sets [Builder.diagnostic] to an arbitrary JSON value. + * + * You should usually call [Builder.diagnostic] with a well-typed [Diagnostic] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun diagnostic(diagnostic: JsonField) = apply { + this.diagnostic = diagnostic + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UserListCoverageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UserListCoverageResponse = + UserListCoverageResponse( + checkRequired("hasNextPage", hasNextPage), + checkRequired("nextCursor", nextCursor), + checkRequired("users", users).map { it.toImmutable() }, + checkRequired("diagnostic", diagnostic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): UserListCoverageResponse = apply { + if (validated) { + return@apply + } + + hasNextPage() + nextCursor() + users().forEach { it.validate() } + diagnostic().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (hasNextPage.asKnown().isPresent) 1 else 0) + + (if (nextCursor.asKnown().isPresent) 1 else 0) + + (users.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (diagnostic.asKnown().getOrNull()?.validity() ?: 0) + + /** Coverage evidence across parallel relationship strategies. */ + class Diagnostic + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val complete: JsonField, + private val cursorFailureCount: JsonField, + private val deadlineReached: JsonField, + private val duplicateCount: JsonField, + private val failedStrategyCount: JsonField, + private val malformedCount: JsonField, + private val pagesFetched: JsonField, + private val responseTruncated: JsonField, + private val resultLimitReached: JsonField, + private val returnedUsers: JsonField, + private val stalledStrategyCount: JsonField, + private val strategies: JsonField>, + private val strategyCount: JsonField, + private val uniqueUsers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("complete") + @ExcludeMissing + complete: JsonField = JsonMissing.of(), + @JsonProperty("cursorFailureCount") + @ExcludeMissing + cursorFailureCount: JsonField = JsonMissing.of(), + @JsonProperty("deadlineReached") + @ExcludeMissing + deadlineReached: JsonField = JsonMissing.of(), + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("failedStrategyCount") + @ExcludeMissing + failedStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("malformedCount") + @ExcludeMissing + malformedCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("responseTruncated") + @ExcludeMissing + responseTruncated: JsonField = JsonMissing.of(), + @JsonProperty("resultLimitReached") + @ExcludeMissing + resultLimitReached: JsonField = JsonMissing.of(), + @JsonProperty("returnedUsers") + @ExcludeMissing + returnedUsers: JsonField = JsonMissing.of(), + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + stalledStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("strategies") + @ExcludeMissing + strategies: JsonField> = JsonMissing.of(), + @JsonProperty("strategyCount") + @ExcludeMissing + strategyCount: JsonField = JsonMissing.of(), + @JsonProperty("uniqueUsers") + @ExcludeMissing + uniqueUsers: JsonField = JsonMissing.of(), + ) : this( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + mutableMapOf(), + ) + + /** + * True when every strategy exhausted its source. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun complete(): Boolean = complete.getRequired("complete") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun cursorFailureCount(): Long = cursorFailureCount.getRequired("cursorFailureCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun deadlineReached(): Boolean = deadlineReached.getRequired("deadlineReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun failedStrategyCount(): Long = failedStrategyCount.getRequired("failedStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun malformedCount(): Long = malformedCount.getRequired("malformedCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * Whether credits or the requested limit reduced output. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun responseTruncated(): Boolean = responseTruncated.getRequired("responseTruncated") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun resultLimitReached(): Boolean = resultLimitReached.getRequired("resultLimitReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun returnedUsers(): Long = returnedUsers.getRequired("returnedUsers") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun stalledStrategyCount(): Long = + stalledStrategyCount.getRequired("stalledStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategies(): List = strategies.getRequired("strategies") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategyCount(): Long = strategyCount.getRequired("strategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun uniqueUsers(): Long = uniqueUsers.getRequired("uniqueUsers") + + /** + * Returns the raw JSON value of [complete]. + * + * Unlike [complete], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("complete") @ExcludeMissing fun _complete(): JsonField = complete + + /** + * Returns the raw JSON value of [cursorFailureCount]. + * + * Unlike [cursorFailureCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("cursorFailureCount") + @ExcludeMissing + fun _cursorFailureCount(): JsonField = cursorFailureCount + + /** + * Returns the raw JSON value of [deadlineReached]. + * + * Unlike [deadlineReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("deadlineReached") + @ExcludeMissing + fun _deadlineReached(): JsonField = deadlineReached + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [failedStrategyCount]. + * + * Unlike [failedStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("failedStrategyCount") + @ExcludeMissing + fun _failedStrategyCount(): JsonField = failedStrategyCount + + /** + * Returns the raw JSON value of [malformedCount]. + * + * Unlike [malformedCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("malformedCount") + @ExcludeMissing + fun _malformedCount(): JsonField = malformedCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [responseTruncated]. + * + * Unlike [responseTruncated], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("responseTruncated") + @ExcludeMissing + fun _responseTruncated(): JsonField = responseTruncated + + /** + * Returns the raw JSON value of [resultLimitReached]. + * + * Unlike [resultLimitReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("resultLimitReached") + @ExcludeMissing + fun _resultLimitReached(): JsonField = resultLimitReached + + /** + * Returns the raw JSON value of [returnedUsers]. + * + * Unlike [returnedUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("returnedUsers") + @ExcludeMissing + fun _returnedUsers(): JsonField = returnedUsers + + /** + * Returns the raw JSON value of [stalledStrategyCount]. + * + * Unlike [stalledStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + fun _stalledStrategyCount(): JsonField = stalledStrategyCount + + /** + * Returns the raw JSON value of [strategies]. + * + * Unlike [strategies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategies") + @ExcludeMissing + fun _strategies(): JsonField> = strategies + + /** + * Returns the raw JSON value of [strategyCount]. + * + * Unlike [strategyCount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategyCount") + @ExcludeMissing + fun _strategyCount(): JsonField = strategyCount + + /** + * Returns the raw JSON value of [uniqueUsers]. + * + * Unlike [uniqueUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("uniqueUsers") + @ExcludeMissing + fun _uniqueUsers(): JsonField = uniqueUsers + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Diagnostic]. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Diagnostic]. */ + class Builder internal constructor() { + + private var complete: JsonField? = null + private var cursorFailureCount: JsonField? = null + private var deadlineReached: JsonField? = null + private var duplicateCount: JsonField? = null + private var failedStrategyCount: JsonField? = null + private var malformedCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var responseTruncated: JsonField? = null + private var resultLimitReached: JsonField? = null + private var returnedUsers: JsonField? = null + private var stalledStrategyCount: JsonField? = null + private var strategies: JsonField>? = null + private var strategyCount: JsonField? = null + private var uniqueUsers: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(diagnostic: Diagnostic) = apply { + complete = diagnostic.complete + cursorFailureCount = diagnostic.cursorFailureCount + deadlineReached = diagnostic.deadlineReached + duplicateCount = diagnostic.duplicateCount + failedStrategyCount = diagnostic.failedStrategyCount + malformedCount = diagnostic.malformedCount + pagesFetched = diagnostic.pagesFetched + responseTruncated = diagnostic.responseTruncated + resultLimitReached = diagnostic.resultLimitReached + returnedUsers = diagnostic.returnedUsers + stalledStrategyCount = diagnostic.stalledStrategyCount + strategies = diagnostic.strategies.map { it.toMutableList() } + strategyCount = diagnostic.strategyCount + uniqueUsers = diagnostic.uniqueUsers + additionalProperties = diagnostic.additionalProperties.toMutableMap() + } + + /** True when every strategy exhausted its source. */ + fun complete(complete: Boolean) = complete(JsonField.of(complete)) + + /** + * Sets [Builder.complete] to an arbitrary JSON value. + * + * You should usually call [Builder.complete] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun complete(complete: JsonField) = apply { this.complete = complete } + + fun cursorFailureCount(cursorFailureCount: Long) = + cursorFailureCount(JsonField.of(cursorFailureCount)) + + /** + * Sets [Builder.cursorFailureCount] to an arbitrary JSON value. + * + * You should usually call [Builder.cursorFailureCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun cursorFailureCount(cursorFailureCount: JsonField) = apply { + this.cursorFailureCount = cursorFailureCount + } + + fun deadlineReached(deadlineReached: Boolean) = + deadlineReached(JsonField.of(deadlineReached)) + + /** + * Sets [Builder.deadlineReached] to an arbitrary JSON value. + * + * You should usually call [Builder.deadlineReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun deadlineReached(deadlineReached: JsonField) = apply { + this.deadlineReached = deadlineReached + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun failedStrategyCount(failedStrategyCount: Long) = + failedStrategyCount(JsonField.of(failedStrategyCount)) + + /** + * Sets [Builder.failedStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.failedStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun failedStrategyCount(failedStrategyCount: JsonField) = apply { + this.failedStrategyCount = failedStrategyCount + } + + fun malformedCount(malformedCount: Long) = + malformedCount(JsonField.of(malformedCount)) + + /** + * Sets [Builder.malformedCount] to an arbitrary JSON value. + * + * You should usually call [Builder.malformedCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun malformedCount(malformedCount: JsonField) = apply { + this.malformedCount = malformedCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + /** Whether credits or the requested limit reduced output. */ + fun responseTruncated(responseTruncated: Boolean) = + responseTruncated(JsonField.of(responseTruncated)) + + /** + * Sets [Builder.responseTruncated] to an arbitrary JSON value. + * + * You should usually call [Builder.responseTruncated] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun responseTruncated(responseTruncated: JsonField) = apply { + this.responseTruncated = responseTruncated + } + + fun resultLimitReached(resultLimitReached: Boolean) = + resultLimitReached(JsonField.of(resultLimitReached)) + + /** + * Sets [Builder.resultLimitReached] to an arbitrary JSON value. + * + * You should usually call [Builder.resultLimitReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun resultLimitReached(resultLimitReached: JsonField) = apply { + this.resultLimitReached = resultLimitReached + } + + fun returnedUsers(returnedUsers: Long) = returnedUsers(JsonField.of(returnedUsers)) + + /** + * Sets [Builder.returnedUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.returnedUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun returnedUsers(returnedUsers: JsonField) = apply { + this.returnedUsers = returnedUsers + } + + fun stalledStrategyCount(stalledStrategyCount: Long) = + stalledStrategyCount(JsonField.of(stalledStrategyCount)) + + /** + * Sets [Builder.stalledStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.stalledStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun stalledStrategyCount(stalledStrategyCount: JsonField) = apply { + this.stalledStrategyCount = stalledStrategyCount + } + + fun strategies(strategies: List) = strategies(JsonField.of(strategies)) + + /** + * Sets [Builder.strategies] to an arbitrary JSON value. + * + * You should usually call [Builder.strategies] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun strategies(strategies: JsonField>) = apply { + this.strategies = strategies.map { it.toMutableList() } + } + + /** + * Adds a single [Strategy] to [strategies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addStrategy(strategy: Strategy) = apply { + strategies = + (strategies ?: JsonField.of(mutableListOf())).also { + checkKnown("strategies", it).add(strategy) + } + } + + fun strategyCount(strategyCount: Long) = strategyCount(JsonField.of(strategyCount)) + + /** + * Sets [Builder.strategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.strategyCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun strategyCount(strategyCount: JsonField) = apply { + this.strategyCount = strategyCount + } + + fun uniqueUsers(uniqueUsers: Long) = uniqueUsers(JsonField.of(uniqueUsers)) + + /** + * Sets [Builder.uniqueUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun uniqueUsers(uniqueUsers: JsonField) = apply { + this.uniqueUsers = uniqueUsers + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Diagnostic]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Diagnostic = + Diagnostic( + checkRequired("complete", complete), + checkRequired("cursorFailureCount", cursorFailureCount), + checkRequired("deadlineReached", deadlineReached), + checkRequired("duplicateCount", duplicateCount), + checkRequired("failedStrategyCount", failedStrategyCount), + checkRequired("malformedCount", malformedCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("responseTruncated", responseTruncated), + checkRequired("resultLimitReached", resultLimitReached), + checkRequired("returnedUsers", returnedUsers), + checkRequired("stalledStrategyCount", stalledStrategyCount), + checkRequired("strategies", strategies).map { it.toImmutable() }, + checkRequired("strategyCount", strategyCount), + checkRequired("uniqueUsers", uniqueUsers), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Diagnostic = apply { + if (validated) { + return@apply + } + + complete() + cursorFailureCount() + deadlineReached() + duplicateCount() + failedStrategyCount() + malformedCount() + pagesFetched() + responseTruncated() + resultLimitReached() + returnedUsers() + stalledStrategyCount() + strategies().forEach { it.validate() } + strategyCount() + uniqueUsers() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (complete.asKnown().isPresent) 1 else 0) + + (if (cursorFailureCount.asKnown().isPresent) 1 else 0) + + (if (deadlineReached.asKnown().isPresent) 1 else 0) + + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (failedStrategyCount.asKnown().isPresent) 1 else 0) + + (if (malformedCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (if (responseTruncated.asKnown().isPresent) 1 else 0) + + (if (resultLimitReached.asKnown().isPresent) 1 else 0) + + (if (returnedUsers.asKnown().isPresent) 1 else 0) + + (if (stalledStrategyCount.asKnown().isPresent) 1 else 0) + + (strategies.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (strategyCount.asKnown().isPresent) 1 else 0) + + (if (uniqueUsers.asKnown().isPresent) 1 else 0) + + class Strategy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val duplicateCount: JsonField, + private val pagesFetched: JsonField, + private val stopReason: JsonField, + private val strategy: JsonField, + private val uniqueAdded: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("stopReason") + @ExcludeMissing + stopReason: JsonField = JsonMissing.of(), + @JsonProperty("strategy") + @ExcludeMissing + strategy: JsonField = JsonMissing.of(), + @JsonProperty("uniqueAdded") + @ExcludeMissing + uniqueAdded: JsonField = JsonMissing.of(), + ) : this( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + mutableMapOf(), + ) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun stopReason(): StopReason = stopReason.getRequired("stopReason") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun strategy(): Long = strategy.getRequired("strategy") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun uniqueAdded(): Long = uniqueAdded.getRequired("uniqueAdded") + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [stopReason]. + * + * Unlike [stopReason], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stopReason") + @ExcludeMissing + fun _stopReason(): JsonField = stopReason + + /** + * Returns the raw JSON value of [strategy]. + * + * Unlike [strategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategy") + @ExcludeMissing + fun _strategy(): JsonField = strategy + + /** + * Returns the raw JSON value of [uniqueAdded]. + * + * Unlike [uniqueAdded], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("uniqueAdded") + @ExcludeMissing + fun _uniqueAdded(): JsonField = uniqueAdded + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Strategy]. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Strategy]. */ + class Builder internal constructor() { + + private var duplicateCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var stopReason: JsonField? = null + private var strategy: JsonField? = null + private var uniqueAdded: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(strategy: Strategy) = apply { + duplicateCount = strategy.duplicateCount + pagesFetched = strategy.pagesFetched + stopReason = strategy.stopReason + this.strategy = strategy.strategy + uniqueAdded = strategy.uniqueAdded + additionalProperties = strategy.additionalProperties.toMutableMap() + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + fun stopReason(stopReason: StopReason) = stopReason(JsonField.of(stopReason)) + + /** + * Sets [Builder.stopReason] to an arbitrary JSON value. + * + * You should usually call [Builder.stopReason] with a well-typed [StopReason] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun stopReason(stopReason: JsonField) = apply { + this.stopReason = stopReason + } + + fun strategy(strategy: Long) = strategy(JsonField.of(strategy)) + + /** + * Sets [Builder.strategy] to an arbitrary JSON value. + * + * You should usually call [Builder.strategy] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun strategy(strategy: JsonField) = apply { this.strategy = strategy } + + fun uniqueAdded(uniqueAdded: Long) = uniqueAdded(JsonField.of(uniqueAdded)) + + /** + * Sets [Builder.uniqueAdded] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueAdded] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun uniqueAdded(uniqueAdded: JsonField) = apply { + this.uniqueAdded = uniqueAdded + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Strategy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Strategy = + Strategy( + checkRequired("duplicateCount", duplicateCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("stopReason", stopReason), + checkRequired("strategy", strategy), + checkRequired("uniqueAdded", uniqueAdded), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Strategy = apply { + if (validated) { + return@apply + } + + duplicateCount() + pagesFetched() + stopReason().validate() + strategy() + uniqueAdded() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (stopReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (strategy.asKnown().isPresent) 1 else 0) + + (if (uniqueAdded.asKnown().isPresent) 1 else 0) + + class StopReason + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + @JvmField val CURSOR_FAILURE = of("cursor_failure") + + @JvmField val DEADLINE = of("deadline") + + @JvmField val EXHAUSTED = of("exhausted") + + @JvmField val FAILED = of("failed") + + @JvmField val PAGE_LIMIT = of("page_limit") + + @JvmField val RESULT_LIMIT = of("result_limit") + + @JvmField val STALLED = of("stalled") + + @JvmStatic fun of(value: String) = StopReason(JsonField.of(value)) + } + + /** An enum containing [StopReason]'s known values. */ + enum class Known { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + } + + /** + * An enum containing [StopReason]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [StopReason] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + /** + * An enum member indicating that [StopReason] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CURSOR_FAILURE -> Value.CURSOR_FAILURE + DEADLINE -> Value.DEADLINE + EXHAUSTED -> Value.EXHAUSTED + FAILED -> Value.FAILED + PAGE_LIMIT -> Value.PAGE_LIMIT + RESULT_LIMIT -> Value.RESULT_LIMIT + STALLED -> Value.STALLED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is + * a not a known member. + */ + fun known(): Known = + when (this) { + CURSOR_FAILURE -> Known.CURSOR_FAILURE + DEADLINE -> Known.DEADLINE + EXHAUSTED -> Known.EXHAUSTED + FAILED -> Known.FAILED + PAGE_LIMIT -> Known.PAGE_LIMIT + RESULT_LIMIT -> Known.RESULT_LIMIT + STALLED -> Known.STALLED + else -> + throw XTwitterScraperInvalidDataException( + "Unknown StopReason: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): StopReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StopReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Strategy && + duplicateCount == other.duplicateCount && + pagesFetched == other.pagesFetched && + stopReason == other.stopReason && + strategy == other.strategy && + uniqueAdded == other.uniqueAdded && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Strategy{duplicateCount=$duplicateCount, pagesFetched=$pagesFetched, stopReason=$stopReason, strategy=$strategy, uniqueAdded=$uniqueAdded, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Diagnostic && + complete == other.complete && + cursorFailureCount == other.cursorFailureCount && + deadlineReached == other.deadlineReached && + duplicateCount == other.duplicateCount && + failedStrategyCount == other.failedStrategyCount && + malformedCount == other.malformedCount && + pagesFetched == other.pagesFetched && + responseTruncated == other.responseTruncated && + resultLimitReached == other.resultLimitReached && + returnedUsers == other.returnedUsers && + stalledStrategyCount == other.stalledStrategyCount && + strategies == other.strategies && + strategyCount == other.strategyCount && + uniqueUsers == other.uniqueUsers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Diagnostic{complete=$complete, cursorFailureCount=$cursorFailureCount, deadlineReached=$deadlineReached, duplicateCount=$duplicateCount, failedStrategyCount=$failedStrategyCount, malformedCount=$malformedCount, pagesFetched=$pagesFetched, responseTruncated=$responseTruncated, resultLimitReached=$resultLimitReached, returnedUsers=$returnedUsers, stalledStrategyCount=$stalledStrategyCount, strategies=$strategies, strategyCount=$strategyCount, uniqueUsers=$uniqueUsers, additionalProperties=$additionalProperties}" + } + + class HasNextPage @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FALSE = of(false) + + @JvmStatic fun of(value: Boolean) = HasNextPage(JsonField.of(value)) + } + + /** An enum containing [HasNextPage]'s known values. */ + enum class Known { + FALSE + } + + /** + * An enum containing [HasNextPage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [HasNextPage] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FALSE, + /** + * An enum member indicating that [HasNextPage] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FALSE -> Value.FALSE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + FALSE -> Known.FALSE + else -> throw XTwitterScraperInvalidDataException("Unknown HasNextPage: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asBoolean(): Boolean = + _value().asBoolean().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a Boolean") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): HasNextPage = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is HasNextPage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class NextCursor @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val EMPTY = of("") + + @JvmStatic fun of(value: String) = NextCursor(JsonField.of(value)) + } + + /** An enum containing [NextCursor]'s known values. */ + enum class Known { + EMPTY + } + + /** + * An enum containing [NextCursor]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [NextCursor] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + EMPTY, + /** + * An enum member indicating that [NextCursor] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + EMPTY -> Value.EMPTY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + EMPTY -> Known.EMPTY + else -> throw XTwitterScraperInvalidDataException("Unknown NextCursor: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): NextCursor = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NextCursor && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserListCoverageResponse && + hasNextPage == other.hasNextPage && + nextCursor == other.nextCursor && + users == other.users && + diagnostic == other.diagnostic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(hasNextPage, nextCursor, users, diagnostic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UserListCoverageResponse{hasNextPage=$hasNextPage, nextCursor=$nextCursor, users=$users, diagnostic=$diagnostic, additionalProperties=$additionalProperties}" + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParams.kt index 75977009..fe69ed02 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParams.kt @@ -17,25 +17,80 @@ import kotlin.jvm.optionals.getOrNull class UserRetrieveFollowersYouKnowParams private constructor( private val id: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for followers-you-know */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is true. + * Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -59,8 +114,22 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -68,8 +137,22 @@ private constructor( internal fun from(userRetrieveFollowersYouKnowParams: UserRetrieveFollowersYouKnowParams) = apply { id = userRetrieveFollowersYouKnowParams.id + bioContains = userRetrieveFollowersYouKnowParams.bioContains cursor = userRetrieveFollowersYouKnowParams.cursor + hasLocation = userRetrieveFollowersYouKnowParams.hasLocation + hasWebsite = userRetrieveFollowersYouKnowParams.hasWebsite + locationContains = userRetrieveFollowersYouKnowParams.locationContains + maxFollowers = userRetrieveFollowersYouKnowParams.maxFollowers + maxFollowing = userRetrieveFollowersYouKnowParams.maxFollowing + maxStatuses = userRetrieveFollowersYouKnowParams.maxStatuses + minAccountAgeDays = userRetrieveFollowersYouKnowParams.minAccountAgeDays + minFollowers = userRetrieveFollowersYouKnowParams.minFollowers + minFollowing = userRetrieveFollowersYouKnowParams.minFollowing + minStatuses = userRetrieveFollowersYouKnowParams.minStatuses pageSize = userRetrieveFollowersYouKnowParams.pageSize + usernameContains = userRetrieveFollowersYouKnowParams.usernameContains + verifiedOnly = userRetrieveFollowersYouKnowParams.verifiedOnly + verifiedType = userRetrieveFollowersYouKnowParams.verifiedType additionalHeaders = userRetrieveFollowersYouKnowParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveFollowersYouKnowParams.additionalQueryParams.toBuilder() @@ -80,17 +163,152 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for followers-you-know */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Maximum user profiles requested from this page (20-200, default 200). Source, filters, or + * credits can return fewer profiles. Keep requesting next_cursor while has_next_page is + * true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -104,6 +322,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -210,8 +456,22 @@ private constructor( fun build(): UserRetrieveFollowersYouKnowParams = UserRetrieveFollowersYouKnowParams( id, + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -228,8 +488,22 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -241,15 +515,49 @@ private constructor( return other is UserRetrieveFollowersYouKnowParams && id == other.id && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "UserRetrieveFollowersYouKnowParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveFollowersYouKnowParams{id=$id, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParams.kt index 2fee61b2..732afd87 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParams.kt @@ -6,9 +6,13 @@ package com.x_twitter_scraper.api.models.x.users +import com.fasterxml.jackson.annotation.JsonCreator +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.Params import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -18,9 +22,24 @@ class UserRetrieveFollowingParams private constructor( private val id: String?, private val after: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, private val limit: Long?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, + private val mode: Mode?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -30,20 +49,72 @@ private constructor( /** Deprecated following cursor alias. Prefer cursor. */ fun after(): Optional = Optional.ofNullable(after) - /** Pagination cursor for following list */ + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) - /** Legacy page size alias. Prefer pageSize. */ + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ fun limit(): Optional = Optional.ofNullable(limit) + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + fun mode(): Optional = Optional.ofNullable(mode) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. Continue + * with has_next_page. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -67,9 +138,24 @@ private constructor( private var id: String? = null private var after: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null private var limit: Long? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null + private var mode: Mode? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -77,9 +163,24 @@ private constructor( internal fun from(userRetrieveFollowingParams: UserRetrieveFollowingParams) = apply { id = userRetrieveFollowingParams.id after = userRetrieveFollowingParams.after + bioContains = userRetrieveFollowingParams.bioContains cursor = userRetrieveFollowingParams.cursor + hasLocation = userRetrieveFollowingParams.hasLocation + hasWebsite = userRetrieveFollowingParams.hasWebsite limit = userRetrieveFollowingParams.limit + locationContains = userRetrieveFollowingParams.locationContains + maxFollowers = userRetrieveFollowingParams.maxFollowers + maxFollowing = userRetrieveFollowingParams.maxFollowing + maxStatuses = userRetrieveFollowingParams.maxStatuses + minAccountAgeDays = userRetrieveFollowingParams.minAccountAgeDays + minFollowers = userRetrieveFollowingParams.minFollowers + minFollowing = userRetrieveFollowingParams.minFollowing + minStatuses = userRetrieveFollowingParams.minStatuses + mode = userRetrieveFollowingParams.mode pageSize = userRetrieveFollowingParams.pageSize + usernameContains = userRetrieveFollowingParams.usernameContains + verifiedOnly = userRetrieveFollowingParams.verifiedOnly + verifiedType = userRetrieveFollowingParams.verifiedType additionalHeaders = userRetrieveFollowingParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveFollowingParams.additionalQueryParams.toBuilder() } @@ -95,13 +196,51 @@ private constructor( /** Alias for calling [Builder.after] with `after.orElse(null)`. */ fun after(after: Optional) = after(after.getOrNull()) - /** Pagination cursor for following list */ + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) - /** Legacy page size alias. Prefer pageSize. */ + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ fun limit(limit: Long?) = apply { this.limit = limit } /** @@ -114,11 +253,122 @@ private constructor( /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ fun limit(limit: Optional) = limit(limit.getOrNull()) + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage + * returns diagnostics once and rejects cursors. + */ + fun mode(mode: Mode?) = apply { this.mode = mode } + + /** Alias for calling [Builder.mode] with `mode.orElse(null)`. */ + fun mode(mode: Optional) = mode(mode.getOrNull()) + + /** + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. + * Continue with has_next_page. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -132,6 +382,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -239,9 +517,24 @@ private constructor( UserRetrieveFollowingParams( id, after, + bioContains, cursor, + hasLocation, + hasWebsite, limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -259,13 +552,168 @@ private constructor( QueryParams.builder() .apply { after?.let { put("after", it) } + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } limit?.let { put("limit", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } + mode?.let { put("mode", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val STANDARD = of("standard") + + @JvmField val COVERAGE = of("coverage") + + @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) + } + + /** An enum containing [Mode]'s known values. */ + enum class Known { + STANDARD, + COVERAGE, + } + + /** + * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Mode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + STANDARD, + COVERAGE, + /** An enum member indicating that [Mode] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + STANDARD -> Value.STANDARD + COVERAGE -> Value.COVERAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + STANDARD -> Known.STANDARD + COVERAGE -> Known.COVERAGE + else -> throw XTwitterScraperInvalidDataException("Unknown Mode: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Mode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Mode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -274,16 +722,54 @@ private constructor( return other is UserRetrieveFollowingParams && id == other.id && after == other.after && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && limit == other.limit && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && + mode == other.mode && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, after, cursor, limit, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + after, + bioContains, + cursor, + hasLocation, + hasWebsite, + limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "UserRetrieveFollowingParams{id=$id, after=$after, cursor=$cursor, limit=$limit, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveFollowingParams{id=$id, after=$after, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, limit=$limit, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, mode=$mode, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponse.kt new file mode 100644 index 00000000..3be5e03b --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponse.kt @@ -0,0 +1,2224 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.ExcludeMissing +import com.x_twitter_scraper.api.core.JsonField +import com.x_twitter_scraper.api.core.JsonMissing +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow +import com.x_twitter_scraper.api.core.toImmutable +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy behavior. + * Follow next_cursor while has_next_page is true. + */ +@JsonDeserialize(using = UserRetrieveFollowingResponse.Deserializer::class) +@JsonSerialize(using = UserRetrieveFollowingResponse.Serializer::class) +class UserRetrieveFollowingResponse +private constructor( + private val paginatedUsers: PaginatedUsers? = null, + private val listCoverage: UserListCoverageResponse? = null, + private val _json: JsonValue? = null, +) { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun paginatedUsers(): Optional = Optional.ofNullable(paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun listCoverage(): Optional = Optional.ofNullable(listCoverage) + + fun isPaginatedUsers(): Boolean = paginatedUsers != null + + fun isListCoverage(): Boolean = listCoverage != null + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asPaginatedUsers(): PaginatedUsers = paginatedUsers.getOrThrow("paginatedUsers") + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asListCoverage(): UserListCoverageResponse = listCoverage.getOrThrow("listCoverage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = userRetrieveFollowingResponse.accept(new UserRetrieveFollowingResponse.Visitor>() { + * @Override + * public Optional visitPaginatedUsers(PaginatedUsers paginatedUsers) { + * return Optional.of(paginatedUsers.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + paginatedUsers != null -> visitor.visitPaginatedUsers(paginatedUsers) + listCoverage != null -> visitor.visitListCoverage(listCoverage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UserRetrieveFollowingResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) { + paginatedUsers.validate() + } + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) { + listCoverage.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) = + paginatedUsers.validity() + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) = + listCoverage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserRetrieveFollowingResponse && + paginatedUsers == other.paginatedUsers && + listCoverage == other.listCoverage + } + + override fun hashCode(): Int = Objects.hash(paginatedUsers, listCoverage) + + override fun toString(): String = + when { + paginatedUsers != null -> + "UserRetrieveFollowingResponse{paginatedUsers=$paginatedUsers}" + listCoverage != null -> "UserRetrieveFollowingResponse{listCoverage=$listCoverage}" + _json != null -> "UserRetrieveFollowingResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UserRetrieveFollowingResponse") + } + + companion object { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofPaginatedUsers(paginatedUsers: PaginatedUsers) = + UserRetrieveFollowingResponse(paginatedUsers = paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofListCoverage(listCoverage: UserListCoverageResponse) = + UserRetrieveFollowingResponse(listCoverage = listCoverage) + } + + /** + * An interface that defines how to map each variant of [UserRetrieveFollowingResponse] to a + * value of type [T]. + */ + interface Visitor { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitPaginatedUsers(paginatedUsers: PaginatedUsers): T + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitListCoverage(listCoverage: UserListCoverageResponse): T + + /** + * Maps an unknown variant of [UserRetrieveFollowingResponse] to a value of type [T]. + * + * An instance of [UserRetrieveFollowingResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException( + "Unknown UserRetrieveFollowingResponse: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(UserRetrieveFollowingResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): UserRetrieveFollowingResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveFollowingResponse(paginatedUsers = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveFollowingResponse(listCoverage = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> UserRetrieveFollowingResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(UserRetrieveFollowingResponse::class) { + + override fun serialize( + value: UserRetrieveFollowingResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.paginatedUsers != null -> generator.writeObject(value.paginatedUsers) + value.listCoverage != null -> generator.writeObject(value.listCoverage) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UserRetrieveFollowingResponse") + } + } + } + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + class UserListCoverageResponse + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val hasNextPage: JsonField, + private val nextCursor: JsonField, + private val users: JsonField>, + private val diagnostic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("has_next_page") + @ExcludeMissing + hasNextPage: JsonField = JsonMissing.of(), + @JsonProperty("next_cursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("users") + @ExcludeMissing + users: JsonField> = JsonMissing.of(), + @JsonProperty("diagnostic") + @ExcludeMissing + diagnostic: JsonField = JsonMissing.of(), + ) : this(hasNextPage, nextCursor, users, diagnostic, mutableMapOf()) + + fun toPaginatedUsers(): PaginatedUsers = + PaginatedUsers.builder() + .hasNextPage(hasNextPage) + .nextCursor(nextCursor) + .users(users) + .build() + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun hasNextPage(): Boolean = hasNextPage.getRequired("has_next_page") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun nextCursor(): String = nextCursor.getRequired("next_cursor") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun users(): List = users.getRequired("users") + + /** + * Coverage evidence across parallel relationship strategies. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun diagnostic(): Diagnostic = diagnostic.getRequired("diagnostic") + + /** + * Returns the raw JSON value of [hasNextPage]. + * + * Unlike [hasNextPage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("has_next_page") + @ExcludeMissing + fun _hasNextPage(): JsonField = hasNextPage + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("next_cursor") + @ExcludeMissing + fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [users]. + * + * Unlike [users], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("users") @ExcludeMissing fun _users(): JsonField> = users + + /** + * Returns the raw JSON value of [diagnostic]. + * + * Unlike [diagnostic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("diagnostic") + @ExcludeMissing + fun _diagnostic(): JsonField = diagnostic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UserListCoverageResponse]. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UserListCoverageResponse]. */ + class Builder internal constructor() { + + private var hasNextPage: JsonField? = null + private var nextCursor: JsonField? = null + private var users: JsonField>? = null + private var diagnostic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(userListCoverageResponse: UserListCoverageResponse) = apply { + hasNextPage = userListCoverageResponse.hasNextPage + nextCursor = userListCoverageResponse.nextCursor + users = userListCoverageResponse.users.map { it.toMutableList() } + diagnostic = userListCoverageResponse.diagnostic + additionalProperties = userListCoverageResponse.additionalProperties.toMutableMap() + } + + fun hasNextPage(hasNextPage: Boolean) = hasNextPage(JsonField.of(hasNextPage)) + + /** + * Sets [Builder.hasNextPage] to an arbitrary JSON value. + * + * You should usually call [Builder.hasNextPage] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasNextPage(hasNextPage: JsonField) = apply { + this.hasNextPage = hasNextPage + } + + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + fun users(users: List) = users(JsonField.of(users)) + + /** + * Sets [Builder.users] to an arbitrary JSON value. + * + * You should usually call [Builder.users] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun users(users: JsonField>) = apply { + this.users = users.map { it.toMutableList() } + } + + /** + * Adds a single [UserProfile] to [users]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addUser(user: UserProfile) = apply { + users = + (users ?: JsonField.of(mutableListOf())).also { + checkKnown("users", it).add(user) + } + } + + /** Coverage evidence across parallel relationship strategies. */ + fun diagnostic(diagnostic: Diagnostic) = diagnostic(JsonField.of(diagnostic)) + + /** + * Sets [Builder.diagnostic] to an arbitrary JSON value. + * + * You should usually call [Builder.diagnostic] with a well-typed [Diagnostic] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun diagnostic(diagnostic: JsonField) = apply { + this.diagnostic = diagnostic + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UserListCoverageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UserListCoverageResponse = + UserListCoverageResponse( + checkRequired("hasNextPage", hasNextPage), + checkRequired("nextCursor", nextCursor), + checkRequired("users", users).map { it.toImmutable() }, + checkRequired("diagnostic", diagnostic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): UserListCoverageResponse = apply { + if (validated) { + return@apply + } + + hasNextPage() + nextCursor() + users().forEach { it.validate() } + diagnostic().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (hasNextPage.asKnown().isPresent) 1 else 0) + + (if (nextCursor.asKnown().isPresent) 1 else 0) + + (users.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (diagnostic.asKnown().getOrNull()?.validity() ?: 0) + + /** Coverage evidence across parallel relationship strategies. */ + class Diagnostic + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val complete: JsonField, + private val cursorFailureCount: JsonField, + private val deadlineReached: JsonField, + private val duplicateCount: JsonField, + private val failedStrategyCount: JsonField, + private val malformedCount: JsonField, + private val pagesFetched: JsonField, + private val responseTruncated: JsonField, + private val resultLimitReached: JsonField, + private val returnedUsers: JsonField, + private val stalledStrategyCount: JsonField, + private val strategies: JsonField>, + private val strategyCount: JsonField, + private val uniqueUsers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("complete") + @ExcludeMissing + complete: JsonField = JsonMissing.of(), + @JsonProperty("cursorFailureCount") + @ExcludeMissing + cursorFailureCount: JsonField = JsonMissing.of(), + @JsonProperty("deadlineReached") + @ExcludeMissing + deadlineReached: JsonField = JsonMissing.of(), + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("failedStrategyCount") + @ExcludeMissing + failedStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("malformedCount") + @ExcludeMissing + malformedCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("responseTruncated") + @ExcludeMissing + responseTruncated: JsonField = JsonMissing.of(), + @JsonProperty("resultLimitReached") + @ExcludeMissing + resultLimitReached: JsonField = JsonMissing.of(), + @JsonProperty("returnedUsers") + @ExcludeMissing + returnedUsers: JsonField = JsonMissing.of(), + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + stalledStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("strategies") + @ExcludeMissing + strategies: JsonField> = JsonMissing.of(), + @JsonProperty("strategyCount") + @ExcludeMissing + strategyCount: JsonField = JsonMissing.of(), + @JsonProperty("uniqueUsers") + @ExcludeMissing + uniqueUsers: JsonField = JsonMissing.of(), + ) : this( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + mutableMapOf(), + ) + + /** + * True when every strategy exhausted its source. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun complete(): Boolean = complete.getRequired("complete") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun cursorFailureCount(): Long = cursorFailureCount.getRequired("cursorFailureCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun deadlineReached(): Boolean = deadlineReached.getRequired("deadlineReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun failedStrategyCount(): Long = failedStrategyCount.getRequired("failedStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun malformedCount(): Long = malformedCount.getRequired("malformedCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * Whether credits or the requested limit reduced output. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun responseTruncated(): Boolean = responseTruncated.getRequired("responseTruncated") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun resultLimitReached(): Boolean = resultLimitReached.getRequired("resultLimitReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun returnedUsers(): Long = returnedUsers.getRequired("returnedUsers") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun stalledStrategyCount(): Long = + stalledStrategyCount.getRequired("stalledStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategies(): List = strategies.getRequired("strategies") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategyCount(): Long = strategyCount.getRequired("strategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun uniqueUsers(): Long = uniqueUsers.getRequired("uniqueUsers") + + /** + * Returns the raw JSON value of [complete]. + * + * Unlike [complete], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("complete") @ExcludeMissing fun _complete(): JsonField = complete + + /** + * Returns the raw JSON value of [cursorFailureCount]. + * + * Unlike [cursorFailureCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("cursorFailureCount") + @ExcludeMissing + fun _cursorFailureCount(): JsonField = cursorFailureCount + + /** + * Returns the raw JSON value of [deadlineReached]. + * + * Unlike [deadlineReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("deadlineReached") + @ExcludeMissing + fun _deadlineReached(): JsonField = deadlineReached + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [failedStrategyCount]. + * + * Unlike [failedStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("failedStrategyCount") + @ExcludeMissing + fun _failedStrategyCount(): JsonField = failedStrategyCount + + /** + * Returns the raw JSON value of [malformedCount]. + * + * Unlike [malformedCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("malformedCount") + @ExcludeMissing + fun _malformedCount(): JsonField = malformedCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [responseTruncated]. + * + * Unlike [responseTruncated], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("responseTruncated") + @ExcludeMissing + fun _responseTruncated(): JsonField = responseTruncated + + /** + * Returns the raw JSON value of [resultLimitReached]. + * + * Unlike [resultLimitReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("resultLimitReached") + @ExcludeMissing + fun _resultLimitReached(): JsonField = resultLimitReached + + /** + * Returns the raw JSON value of [returnedUsers]. + * + * Unlike [returnedUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("returnedUsers") + @ExcludeMissing + fun _returnedUsers(): JsonField = returnedUsers + + /** + * Returns the raw JSON value of [stalledStrategyCount]. + * + * Unlike [stalledStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + fun _stalledStrategyCount(): JsonField = stalledStrategyCount + + /** + * Returns the raw JSON value of [strategies]. + * + * Unlike [strategies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategies") + @ExcludeMissing + fun _strategies(): JsonField> = strategies + + /** + * Returns the raw JSON value of [strategyCount]. + * + * Unlike [strategyCount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategyCount") + @ExcludeMissing + fun _strategyCount(): JsonField = strategyCount + + /** + * Returns the raw JSON value of [uniqueUsers]. + * + * Unlike [uniqueUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("uniqueUsers") + @ExcludeMissing + fun _uniqueUsers(): JsonField = uniqueUsers + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Diagnostic]. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Diagnostic]. */ + class Builder internal constructor() { + + private var complete: JsonField? = null + private var cursorFailureCount: JsonField? = null + private var deadlineReached: JsonField? = null + private var duplicateCount: JsonField? = null + private var failedStrategyCount: JsonField? = null + private var malformedCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var responseTruncated: JsonField? = null + private var resultLimitReached: JsonField? = null + private var returnedUsers: JsonField? = null + private var stalledStrategyCount: JsonField? = null + private var strategies: JsonField>? = null + private var strategyCount: JsonField? = null + private var uniqueUsers: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(diagnostic: Diagnostic) = apply { + complete = diagnostic.complete + cursorFailureCount = diagnostic.cursorFailureCount + deadlineReached = diagnostic.deadlineReached + duplicateCount = diagnostic.duplicateCount + failedStrategyCount = diagnostic.failedStrategyCount + malformedCount = diagnostic.malformedCount + pagesFetched = diagnostic.pagesFetched + responseTruncated = diagnostic.responseTruncated + resultLimitReached = diagnostic.resultLimitReached + returnedUsers = diagnostic.returnedUsers + stalledStrategyCount = diagnostic.stalledStrategyCount + strategies = diagnostic.strategies.map { it.toMutableList() } + strategyCount = diagnostic.strategyCount + uniqueUsers = diagnostic.uniqueUsers + additionalProperties = diagnostic.additionalProperties.toMutableMap() + } + + /** True when every strategy exhausted its source. */ + fun complete(complete: Boolean) = complete(JsonField.of(complete)) + + /** + * Sets [Builder.complete] to an arbitrary JSON value. + * + * You should usually call [Builder.complete] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun complete(complete: JsonField) = apply { this.complete = complete } + + fun cursorFailureCount(cursorFailureCount: Long) = + cursorFailureCount(JsonField.of(cursorFailureCount)) + + /** + * Sets [Builder.cursorFailureCount] to an arbitrary JSON value. + * + * You should usually call [Builder.cursorFailureCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun cursorFailureCount(cursorFailureCount: JsonField) = apply { + this.cursorFailureCount = cursorFailureCount + } + + fun deadlineReached(deadlineReached: Boolean) = + deadlineReached(JsonField.of(deadlineReached)) + + /** + * Sets [Builder.deadlineReached] to an arbitrary JSON value. + * + * You should usually call [Builder.deadlineReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun deadlineReached(deadlineReached: JsonField) = apply { + this.deadlineReached = deadlineReached + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun failedStrategyCount(failedStrategyCount: Long) = + failedStrategyCount(JsonField.of(failedStrategyCount)) + + /** + * Sets [Builder.failedStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.failedStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun failedStrategyCount(failedStrategyCount: JsonField) = apply { + this.failedStrategyCount = failedStrategyCount + } + + fun malformedCount(malformedCount: Long) = + malformedCount(JsonField.of(malformedCount)) + + /** + * Sets [Builder.malformedCount] to an arbitrary JSON value. + * + * You should usually call [Builder.malformedCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun malformedCount(malformedCount: JsonField) = apply { + this.malformedCount = malformedCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + /** Whether credits or the requested limit reduced output. */ + fun responseTruncated(responseTruncated: Boolean) = + responseTruncated(JsonField.of(responseTruncated)) + + /** + * Sets [Builder.responseTruncated] to an arbitrary JSON value. + * + * You should usually call [Builder.responseTruncated] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun responseTruncated(responseTruncated: JsonField) = apply { + this.responseTruncated = responseTruncated + } + + fun resultLimitReached(resultLimitReached: Boolean) = + resultLimitReached(JsonField.of(resultLimitReached)) + + /** + * Sets [Builder.resultLimitReached] to an arbitrary JSON value. + * + * You should usually call [Builder.resultLimitReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun resultLimitReached(resultLimitReached: JsonField) = apply { + this.resultLimitReached = resultLimitReached + } + + fun returnedUsers(returnedUsers: Long) = returnedUsers(JsonField.of(returnedUsers)) + + /** + * Sets [Builder.returnedUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.returnedUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun returnedUsers(returnedUsers: JsonField) = apply { + this.returnedUsers = returnedUsers + } + + fun stalledStrategyCount(stalledStrategyCount: Long) = + stalledStrategyCount(JsonField.of(stalledStrategyCount)) + + /** + * Sets [Builder.stalledStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.stalledStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun stalledStrategyCount(stalledStrategyCount: JsonField) = apply { + this.stalledStrategyCount = stalledStrategyCount + } + + fun strategies(strategies: List) = strategies(JsonField.of(strategies)) + + /** + * Sets [Builder.strategies] to an arbitrary JSON value. + * + * You should usually call [Builder.strategies] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun strategies(strategies: JsonField>) = apply { + this.strategies = strategies.map { it.toMutableList() } + } + + /** + * Adds a single [Strategy] to [strategies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addStrategy(strategy: Strategy) = apply { + strategies = + (strategies ?: JsonField.of(mutableListOf())).also { + checkKnown("strategies", it).add(strategy) + } + } + + fun strategyCount(strategyCount: Long) = strategyCount(JsonField.of(strategyCount)) + + /** + * Sets [Builder.strategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.strategyCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun strategyCount(strategyCount: JsonField) = apply { + this.strategyCount = strategyCount + } + + fun uniqueUsers(uniqueUsers: Long) = uniqueUsers(JsonField.of(uniqueUsers)) + + /** + * Sets [Builder.uniqueUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun uniqueUsers(uniqueUsers: JsonField) = apply { + this.uniqueUsers = uniqueUsers + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Diagnostic]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Diagnostic = + Diagnostic( + checkRequired("complete", complete), + checkRequired("cursorFailureCount", cursorFailureCount), + checkRequired("deadlineReached", deadlineReached), + checkRequired("duplicateCount", duplicateCount), + checkRequired("failedStrategyCount", failedStrategyCount), + checkRequired("malformedCount", malformedCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("responseTruncated", responseTruncated), + checkRequired("resultLimitReached", resultLimitReached), + checkRequired("returnedUsers", returnedUsers), + checkRequired("stalledStrategyCount", stalledStrategyCount), + checkRequired("strategies", strategies).map { it.toImmutable() }, + checkRequired("strategyCount", strategyCount), + checkRequired("uniqueUsers", uniqueUsers), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Diagnostic = apply { + if (validated) { + return@apply + } + + complete() + cursorFailureCount() + deadlineReached() + duplicateCount() + failedStrategyCount() + malformedCount() + pagesFetched() + responseTruncated() + resultLimitReached() + returnedUsers() + stalledStrategyCount() + strategies().forEach { it.validate() } + strategyCount() + uniqueUsers() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (complete.asKnown().isPresent) 1 else 0) + + (if (cursorFailureCount.asKnown().isPresent) 1 else 0) + + (if (deadlineReached.asKnown().isPresent) 1 else 0) + + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (failedStrategyCount.asKnown().isPresent) 1 else 0) + + (if (malformedCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (if (responseTruncated.asKnown().isPresent) 1 else 0) + + (if (resultLimitReached.asKnown().isPresent) 1 else 0) + + (if (returnedUsers.asKnown().isPresent) 1 else 0) + + (if (stalledStrategyCount.asKnown().isPresent) 1 else 0) + + (strategies.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (strategyCount.asKnown().isPresent) 1 else 0) + + (if (uniqueUsers.asKnown().isPresent) 1 else 0) + + class Strategy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val duplicateCount: JsonField, + private val pagesFetched: JsonField, + private val stopReason: JsonField, + private val strategy: JsonField, + private val uniqueAdded: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("stopReason") + @ExcludeMissing + stopReason: JsonField = JsonMissing.of(), + @JsonProperty("strategy") + @ExcludeMissing + strategy: JsonField = JsonMissing.of(), + @JsonProperty("uniqueAdded") + @ExcludeMissing + uniqueAdded: JsonField = JsonMissing.of(), + ) : this( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + mutableMapOf(), + ) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun stopReason(): StopReason = stopReason.getRequired("stopReason") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun strategy(): Long = strategy.getRequired("strategy") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun uniqueAdded(): Long = uniqueAdded.getRequired("uniqueAdded") + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [stopReason]. + * + * Unlike [stopReason], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stopReason") + @ExcludeMissing + fun _stopReason(): JsonField = stopReason + + /** + * Returns the raw JSON value of [strategy]. + * + * Unlike [strategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategy") + @ExcludeMissing + fun _strategy(): JsonField = strategy + + /** + * Returns the raw JSON value of [uniqueAdded]. + * + * Unlike [uniqueAdded], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("uniqueAdded") + @ExcludeMissing + fun _uniqueAdded(): JsonField = uniqueAdded + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Strategy]. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Strategy]. */ + class Builder internal constructor() { + + private var duplicateCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var stopReason: JsonField? = null + private var strategy: JsonField? = null + private var uniqueAdded: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(strategy: Strategy) = apply { + duplicateCount = strategy.duplicateCount + pagesFetched = strategy.pagesFetched + stopReason = strategy.stopReason + this.strategy = strategy.strategy + uniqueAdded = strategy.uniqueAdded + additionalProperties = strategy.additionalProperties.toMutableMap() + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + fun stopReason(stopReason: StopReason) = stopReason(JsonField.of(stopReason)) + + /** + * Sets [Builder.stopReason] to an arbitrary JSON value. + * + * You should usually call [Builder.stopReason] with a well-typed [StopReason] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun stopReason(stopReason: JsonField) = apply { + this.stopReason = stopReason + } + + fun strategy(strategy: Long) = strategy(JsonField.of(strategy)) + + /** + * Sets [Builder.strategy] to an arbitrary JSON value. + * + * You should usually call [Builder.strategy] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun strategy(strategy: JsonField) = apply { this.strategy = strategy } + + fun uniqueAdded(uniqueAdded: Long) = uniqueAdded(JsonField.of(uniqueAdded)) + + /** + * Sets [Builder.uniqueAdded] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueAdded] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun uniqueAdded(uniqueAdded: JsonField) = apply { + this.uniqueAdded = uniqueAdded + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Strategy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Strategy = + Strategy( + checkRequired("duplicateCount", duplicateCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("stopReason", stopReason), + checkRequired("strategy", strategy), + checkRequired("uniqueAdded", uniqueAdded), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Strategy = apply { + if (validated) { + return@apply + } + + duplicateCount() + pagesFetched() + stopReason().validate() + strategy() + uniqueAdded() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (stopReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (strategy.asKnown().isPresent) 1 else 0) + + (if (uniqueAdded.asKnown().isPresent) 1 else 0) + + class StopReason + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + @JvmField val CURSOR_FAILURE = of("cursor_failure") + + @JvmField val DEADLINE = of("deadline") + + @JvmField val EXHAUSTED = of("exhausted") + + @JvmField val FAILED = of("failed") + + @JvmField val PAGE_LIMIT = of("page_limit") + + @JvmField val RESULT_LIMIT = of("result_limit") + + @JvmField val STALLED = of("stalled") + + @JvmStatic fun of(value: String) = StopReason(JsonField.of(value)) + } + + /** An enum containing [StopReason]'s known values. */ + enum class Known { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + } + + /** + * An enum containing [StopReason]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [StopReason] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + /** + * An enum member indicating that [StopReason] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CURSOR_FAILURE -> Value.CURSOR_FAILURE + DEADLINE -> Value.DEADLINE + EXHAUSTED -> Value.EXHAUSTED + FAILED -> Value.FAILED + PAGE_LIMIT -> Value.PAGE_LIMIT + RESULT_LIMIT -> Value.RESULT_LIMIT + STALLED -> Value.STALLED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is + * a not a known member. + */ + fun known(): Known = + when (this) { + CURSOR_FAILURE -> Known.CURSOR_FAILURE + DEADLINE -> Known.DEADLINE + EXHAUSTED -> Known.EXHAUSTED + FAILED -> Known.FAILED + PAGE_LIMIT -> Known.PAGE_LIMIT + RESULT_LIMIT -> Known.RESULT_LIMIT + STALLED -> Known.STALLED + else -> + throw XTwitterScraperInvalidDataException( + "Unknown StopReason: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): StopReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StopReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Strategy && + duplicateCount == other.duplicateCount && + pagesFetched == other.pagesFetched && + stopReason == other.stopReason && + strategy == other.strategy && + uniqueAdded == other.uniqueAdded && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Strategy{duplicateCount=$duplicateCount, pagesFetched=$pagesFetched, stopReason=$stopReason, strategy=$strategy, uniqueAdded=$uniqueAdded, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Diagnostic && + complete == other.complete && + cursorFailureCount == other.cursorFailureCount && + deadlineReached == other.deadlineReached && + duplicateCount == other.duplicateCount && + failedStrategyCount == other.failedStrategyCount && + malformedCount == other.malformedCount && + pagesFetched == other.pagesFetched && + responseTruncated == other.responseTruncated && + resultLimitReached == other.resultLimitReached && + returnedUsers == other.returnedUsers && + stalledStrategyCount == other.stalledStrategyCount && + strategies == other.strategies && + strategyCount == other.strategyCount && + uniqueUsers == other.uniqueUsers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Diagnostic{complete=$complete, cursorFailureCount=$cursorFailureCount, deadlineReached=$deadlineReached, duplicateCount=$duplicateCount, failedStrategyCount=$failedStrategyCount, malformedCount=$malformedCount, pagesFetched=$pagesFetched, responseTruncated=$responseTruncated, resultLimitReached=$resultLimitReached, returnedUsers=$returnedUsers, stalledStrategyCount=$stalledStrategyCount, strategies=$strategies, strategyCount=$strategyCount, uniqueUsers=$uniqueUsers, additionalProperties=$additionalProperties}" + } + + class HasNextPage @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FALSE = of(false) + + @JvmStatic fun of(value: Boolean) = HasNextPage(JsonField.of(value)) + } + + /** An enum containing [HasNextPage]'s known values. */ + enum class Known { + FALSE + } + + /** + * An enum containing [HasNextPage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [HasNextPage] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FALSE, + /** + * An enum member indicating that [HasNextPage] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FALSE -> Value.FALSE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + FALSE -> Known.FALSE + else -> throw XTwitterScraperInvalidDataException("Unknown HasNextPage: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asBoolean(): Boolean = + _value().asBoolean().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a Boolean") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): HasNextPage = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is HasNextPage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class NextCursor @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val EMPTY = of("") + + @JvmStatic fun of(value: String) = NextCursor(JsonField.of(value)) + } + + /** An enum containing [NextCursor]'s known values. */ + enum class Known { + EMPTY + } + + /** + * An enum containing [NextCursor]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [NextCursor] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + EMPTY, + /** + * An enum member indicating that [NextCursor] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + EMPTY -> Value.EMPTY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + EMPTY -> Known.EMPTY + else -> throw XTwitterScraperInvalidDataException("Unknown NextCursor: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): NextCursor = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NextCursor && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserListCoverageResponse && + hasNextPage == other.hasNextPage && + nextCursor == other.nextCursor && + users == other.users && + diagnostic == other.diagnostic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(hasNextPage, nextCursor, users, diagnostic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UserListCoverageResponse{hasNextPage=$hasNextPage, nextCursor=$nextCursor, users=$users, diagnostic=$diagnostic, additionalProperties=$additionalProperties}" + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt index 17a6de1d..eb2aad91 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt @@ -23,32 +23,51 @@ class UserRetrieveLikesParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -58,6 +77,12 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) @@ -70,12 +95,18 @@ private constructor( /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -85,12 +116,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -103,6 +152,18 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -124,9 +185,18 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -139,6 +209,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -160,32 +236,51 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -193,32 +288,51 @@ private constructor( internal fun from(userRetrieveLikesParams: UserRetrieveLikesParams) = apply { id = userRetrieveLikesParams.id anyWords = userRetrieveLikesParams.anyWords + blueVerifiedOnly = userRetrieveLikesParams.blueVerifiedOnly + cardName = userRetrieveLikesParams.cardName cashtags = userRetrieveLikesParams.cashtags conversationId = userRetrieveLikesParams.conversationId cursor = userRetrieveLikesParams.cursor exactPhrase = userRetrieveLikesParams.exactPhrase + excludeSource = userRetrieveLikesParams.excludeSource excludeWords = userRetrieveLikesParams.excludeWords fromUser = userRetrieveLikesParams.fromUser + geocode = userRetrieveLikesParams.geocode hashtags = userRetrieveLikesParams.hashtags inReplyToTweetId = userRetrieveLikesParams.inReplyToTweetId language = userRetrieveLikesParams.language + maxFaves = userRetrieveLikesParams.maxFaves + maxId = userRetrieveLikesParams.maxId + maxQuotes = userRetrieveLikesParams.maxQuotes + maxReplies = userRetrieveLikesParams.maxReplies + maxRetweets = userRetrieveLikesParams.maxRetweets mediaType = userRetrieveLikesParams.mediaType mentioning = userRetrieveLikesParams.mentioning + minBookmarks = userRetrieveLikesParams.minBookmarks minFaves = userRetrieveLikesParams.minFaves minQuotes = userRetrieveLikesParams.minQuotes minReplies = userRetrieveLikesParams.minReplies minRetweets = userRetrieveLikesParams.minRetweets + minViews = userRetrieveLikesParams.minViews + nativeRetweets = userRetrieveLikesParams.nativeRetweets + near = userRetrieveLikesParams.near + news = userRetrieveLikesParams.news pageSize = userRetrieveLikesParams.pageSize quotes = userRetrieveLikesParams.quotes quotesOfTweetId = userRetrieveLikesParams.quotesOfTweetId replies = userRetrieveLikesParams.replies retweets = userRetrieveLikesParams.retweets retweetsOfTweetId = userRetrieveLikesParams.retweetsOfTweetId + safe = userRetrieveLikesParams.safe sinceDate = userRetrieveLikesParams.sinceDate + sinceId = userRetrieveLikesParams.sinceId + source = userRetrieveLikesParams.source toUser = userRetrieveLikesParams.toUser untilDate = userRetrieveLikesParams.untilDate url = userRetrieveLikesParams.url verifiedOnly = userRetrieveLikesParams.verifiedOnly + within = userRetrieveLikesParams.within + withinTime = userRetrieveLikesParams.withinTime additionalHeaders = userRetrieveLikesParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveLikesParams.additionalQueryParams.toBuilder() } @@ -236,6 +350,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -261,6 +398,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -273,6 +417,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -294,6 +444,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -306,6 +514,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -358,6 +579,54 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + + /** + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -410,12 +679,37 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -447,6 +741,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -554,32 +860,51 @@ private constructor( UserRetrieveLikesParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -597,32 +922,51 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1227,32 +1571,51 @@ private constructor( return other is UserRetrieveLikesParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1261,36 +1624,55 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "UserRetrieveLikesParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveLikesParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, source=$source, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt index 09da7c6d..a8666866 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt @@ -23,32 +23,51 @@ class UserRetrieveMediaParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -58,6 +77,12 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) @@ -70,12 +95,18 @@ private constructor( /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -85,12 +116,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -103,6 +152,18 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -124,9 +185,18 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -139,6 +209,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -160,32 +236,51 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -193,32 +288,51 @@ private constructor( internal fun from(userRetrieveMediaParams: UserRetrieveMediaParams) = apply { id = userRetrieveMediaParams.id anyWords = userRetrieveMediaParams.anyWords + blueVerifiedOnly = userRetrieveMediaParams.blueVerifiedOnly + cardName = userRetrieveMediaParams.cardName cashtags = userRetrieveMediaParams.cashtags conversationId = userRetrieveMediaParams.conversationId cursor = userRetrieveMediaParams.cursor exactPhrase = userRetrieveMediaParams.exactPhrase + excludeSource = userRetrieveMediaParams.excludeSource excludeWords = userRetrieveMediaParams.excludeWords fromUser = userRetrieveMediaParams.fromUser + geocode = userRetrieveMediaParams.geocode hashtags = userRetrieveMediaParams.hashtags inReplyToTweetId = userRetrieveMediaParams.inReplyToTweetId language = userRetrieveMediaParams.language + maxFaves = userRetrieveMediaParams.maxFaves + maxId = userRetrieveMediaParams.maxId + maxQuotes = userRetrieveMediaParams.maxQuotes + maxReplies = userRetrieveMediaParams.maxReplies + maxRetweets = userRetrieveMediaParams.maxRetweets mediaType = userRetrieveMediaParams.mediaType mentioning = userRetrieveMediaParams.mentioning + minBookmarks = userRetrieveMediaParams.minBookmarks minFaves = userRetrieveMediaParams.minFaves minQuotes = userRetrieveMediaParams.minQuotes minReplies = userRetrieveMediaParams.minReplies minRetweets = userRetrieveMediaParams.minRetweets + minViews = userRetrieveMediaParams.minViews + nativeRetweets = userRetrieveMediaParams.nativeRetweets + near = userRetrieveMediaParams.near + news = userRetrieveMediaParams.news pageSize = userRetrieveMediaParams.pageSize quotes = userRetrieveMediaParams.quotes quotesOfTweetId = userRetrieveMediaParams.quotesOfTweetId replies = userRetrieveMediaParams.replies retweets = userRetrieveMediaParams.retweets retweetsOfTweetId = userRetrieveMediaParams.retweetsOfTweetId + safe = userRetrieveMediaParams.safe sinceDate = userRetrieveMediaParams.sinceDate + sinceId = userRetrieveMediaParams.sinceId + source = userRetrieveMediaParams.source toUser = userRetrieveMediaParams.toUser untilDate = userRetrieveMediaParams.untilDate url = userRetrieveMediaParams.url verifiedOnly = userRetrieveMediaParams.verifiedOnly + within = userRetrieveMediaParams.within + withinTime = userRetrieveMediaParams.withinTime additionalHeaders = userRetrieveMediaParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveMediaParams.additionalQueryParams.toBuilder() } @@ -236,6 +350,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -261,6 +398,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -273,6 +417,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -294,6 +444,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -306,6 +514,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -358,6 +579,54 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + + /** + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -410,12 +679,37 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -447,6 +741,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -554,32 +860,51 @@ private constructor( UserRetrieveMediaParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -597,32 +922,51 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1227,32 +1571,51 @@ private constructor( return other is UserRetrieveMediaParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1261,36 +1624,55 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "UserRetrieveMediaParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveMediaParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, source=$source, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt index 8b86d07c..e273d259 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt @@ -23,34 +23,53 @@ class UserRetrieveMentionsParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, private val sinceTime: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val untilTime: String?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -60,6 +79,12 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) @@ -72,12 +97,18 @@ private constructor( /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -87,12 +118,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -105,6 +154,18 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -126,12 +187,21 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + /** Unix timestamp - return mentions after this time */ fun sinceTime(): Optional = Optional.ofNullable(sinceTime) + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -147,6 +217,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -170,34 +246,53 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null private var sinceTime: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var untilTime: String? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -205,34 +300,53 @@ private constructor( internal fun from(userRetrieveMentionsParams: UserRetrieveMentionsParams) = apply { id = userRetrieveMentionsParams.id anyWords = userRetrieveMentionsParams.anyWords + blueVerifiedOnly = userRetrieveMentionsParams.blueVerifiedOnly + cardName = userRetrieveMentionsParams.cardName cashtags = userRetrieveMentionsParams.cashtags conversationId = userRetrieveMentionsParams.conversationId cursor = userRetrieveMentionsParams.cursor exactPhrase = userRetrieveMentionsParams.exactPhrase + excludeSource = userRetrieveMentionsParams.excludeSource excludeWords = userRetrieveMentionsParams.excludeWords fromUser = userRetrieveMentionsParams.fromUser + geocode = userRetrieveMentionsParams.geocode hashtags = userRetrieveMentionsParams.hashtags inReplyToTweetId = userRetrieveMentionsParams.inReplyToTweetId language = userRetrieveMentionsParams.language + maxFaves = userRetrieveMentionsParams.maxFaves + maxId = userRetrieveMentionsParams.maxId + maxQuotes = userRetrieveMentionsParams.maxQuotes + maxReplies = userRetrieveMentionsParams.maxReplies + maxRetweets = userRetrieveMentionsParams.maxRetweets mediaType = userRetrieveMentionsParams.mediaType mentioning = userRetrieveMentionsParams.mentioning + minBookmarks = userRetrieveMentionsParams.minBookmarks minFaves = userRetrieveMentionsParams.minFaves minQuotes = userRetrieveMentionsParams.minQuotes minReplies = userRetrieveMentionsParams.minReplies minRetweets = userRetrieveMentionsParams.minRetweets + minViews = userRetrieveMentionsParams.minViews + nativeRetweets = userRetrieveMentionsParams.nativeRetweets + near = userRetrieveMentionsParams.near + news = userRetrieveMentionsParams.news pageSize = userRetrieveMentionsParams.pageSize quotes = userRetrieveMentionsParams.quotes quotesOfTweetId = userRetrieveMentionsParams.quotesOfTweetId replies = userRetrieveMentionsParams.replies retweets = userRetrieveMentionsParams.retweets retweetsOfTweetId = userRetrieveMentionsParams.retweetsOfTweetId + safe = userRetrieveMentionsParams.safe sinceDate = userRetrieveMentionsParams.sinceDate + sinceId = userRetrieveMentionsParams.sinceId sinceTime = userRetrieveMentionsParams.sinceTime + source = userRetrieveMentionsParams.source toUser = userRetrieveMentionsParams.toUser untilDate = userRetrieveMentionsParams.untilDate untilTime = userRetrieveMentionsParams.untilTime url = userRetrieveMentionsParams.url verifiedOnly = userRetrieveMentionsParams.verifiedOnly + within = userRetrieveMentionsParams.within + withinTime = userRetrieveMentionsParams.withinTime additionalHeaders = userRetrieveMentionsParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveMentionsParams.additionalQueryParams.toBuilder() } @@ -250,6 +364,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -275,6 +412,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -287,6 +431,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -308,6 +458,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -320,6 +528,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -372,6 +593,54 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + + /** + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -424,18 +693,43 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + /** Unix timestamp - return mentions after this time */ fun sinceTime(sinceTime: String?) = apply { this.sinceTime = sinceTime } /** Alias for calling [Builder.sinceTime] with `sinceTime.orElse(null)`. */ fun sinceTime(sinceTime: Optional) = sinceTime(sinceTime.getOrNull()) + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -473,6 +767,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -580,34 +886,53 @@ private constructor( UserRetrieveMentionsParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -625,34 +950,53 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } sinceTime?.let { put("sinceTime", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } untilTime?.let { put("untilTime", it) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1257,34 +1601,53 @@ private constructor( return other is UserRetrieveMentionsParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && sinceTime == other.sinceTime && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && untilTime == other.untilTime && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1293,38 +1656,57 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, sinceTime, + source, toUser, untilDate, untilTime, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "UserRetrieveMentionsParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, sinceTime=$sinceTime, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveMentionsParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, sinceTime=$sinceTime, source=$source, toUser=$toUser, untilDate=$untilDate, untilTime=$untilTime, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt index 36be5199..06cb8e45 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt @@ -18,38 +18,60 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Returns the user's timeline with replies included by default. */ +/** + * Returns target-authored posts and replies. Omit mode for automatic maximum coverage. Pass + * next_cursor unchanged. Unprefixed cursors stay legacy. Excludes other-author context. + */ class UserRetrieveRepliesParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val includeParentTweet: Boolean?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -59,24 +81,39 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) /** Conversation ID filter. */ fun conversationId(): Optional = Optional.ofNullable(conversationId) - /** Pagination cursor for user replies */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -89,12 +126,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -107,9 +162,21 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue while + * has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) @@ -128,9 +195,18 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -143,6 +219,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -166,33 +248,52 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var includeParentTweet: Boolean? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -200,33 +301,52 @@ private constructor( internal fun from(userRetrieveRepliesParams: UserRetrieveRepliesParams) = apply { id = userRetrieveRepliesParams.id anyWords = userRetrieveRepliesParams.anyWords + blueVerifiedOnly = userRetrieveRepliesParams.blueVerifiedOnly + cardName = userRetrieveRepliesParams.cardName cashtags = userRetrieveRepliesParams.cashtags conversationId = userRetrieveRepliesParams.conversationId cursor = userRetrieveRepliesParams.cursor exactPhrase = userRetrieveRepliesParams.exactPhrase + excludeSource = userRetrieveRepliesParams.excludeSource excludeWords = userRetrieveRepliesParams.excludeWords fromUser = userRetrieveRepliesParams.fromUser + geocode = userRetrieveRepliesParams.geocode hashtags = userRetrieveRepliesParams.hashtags includeParentTweet = userRetrieveRepliesParams.includeParentTweet inReplyToTweetId = userRetrieveRepliesParams.inReplyToTweetId language = userRetrieveRepliesParams.language + maxFaves = userRetrieveRepliesParams.maxFaves + maxId = userRetrieveRepliesParams.maxId + maxQuotes = userRetrieveRepliesParams.maxQuotes + maxReplies = userRetrieveRepliesParams.maxReplies + maxRetweets = userRetrieveRepliesParams.maxRetweets mediaType = userRetrieveRepliesParams.mediaType mentioning = userRetrieveRepliesParams.mentioning + minBookmarks = userRetrieveRepliesParams.minBookmarks minFaves = userRetrieveRepliesParams.minFaves minQuotes = userRetrieveRepliesParams.minQuotes minReplies = userRetrieveRepliesParams.minReplies minRetweets = userRetrieveRepliesParams.minRetweets + minViews = userRetrieveRepliesParams.minViews + nativeRetweets = userRetrieveRepliesParams.nativeRetweets + near = userRetrieveRepliesParams.near + news = userRetrieveRepliesParams.news pageSize = userRetrieveRepliesParams.pageSize quotes = userRetrieveRepliesParams.quotes quotesOfTweetId = userRetrieveRepliesParams.quotesOfTweetId replies = userRetrieveRepliesParams.replies retweets = userRetrieveRepliesParams.retweets retweetsOfTweetId = userRetrieveRepliesParams.retweetsOfTweetId + safe = userRetrieveRepliesParams.safe sinceDate = userRetrieveRepliesParams.sinceDate + sinceId = userRetrieveRepliesParams.sinceId + source = userRetrieveRepliesParams.source toUser = userRetrieveRepliesParams.toUser untilDate = userRetrieveRepliesParams.untilDate url = userRetrieveRepliesParams.url verifiedOnly = userRetrieveRepliesParams.verifiedOnly + within = userRetrieveRepliesParams.within + withinTime = userRetrieveRepliesParams.withinTime additionalHeaders = userRetrieveRepliesParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveRepliesParams.additionalQueryParams.toBuilder() } @@ -244,6 +364,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -257,7 +400,10 @@ private constructor( fun conversationId(conversationId: Optional) = conversationId(conversationId.getOrNull()) - /** Pagination cursor for user replies */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ @@ -269,6 +415,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -281,6 +434,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -321,6 +480,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -333,6 +550,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -385,9 +615,57 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + + /** + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue + * while has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -437,12 +715,37 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -474,6 +777,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -581,33 +896,52 @@ private constructor( UserRetrieveRepliesParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeParentTweet, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -625,33 +959,52 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } includeParentTweet?.let { put("includeParentTweet", it.toString()) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1256,33 +1609,52 @@ private constructor( return other is UserRetrieveRepliesParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && includeParentTweet == other.includeParentTweet && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1291,37 +1663,56 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeParentTweet, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "UserRetrieveRepliesParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, includeParentTweet=$includeParentTweet, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveRepliesParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, includeParentTweet=$includeParentTweet, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, source=$source, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParams.kt index 81d71dbd..cb93ebef 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParams.kt @@ -18,7 +18,21 @@ import kotlin.jvm.optionals.getOrNull class UserRetrieveSearchParams private constructor( private val q: String, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -26,9 +40,51 @@ private constructor( /** User search query */ fun q(): String = q + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + /** Pagination cursor for user search */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -54,14 +110,42 @@ private constructor( class Builder internal constructor() { private var q: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(userRetrieveSearchParams: UserRetrieveSearchParams) = apply { q = userRetrieveSearchParams.q + bioContains = userRetrieveSearchParams.bioContains cursor = userRetrieveSearchParams.cursor + hasLocation = userRetrieveSearchParams.hasLocation + hasWebsite = userRetrieveSearchParams.hasWebsite + locationContains = userRetrieveSearchParams.locationContains + maxFollowers = userRetrieveSearchParams.maxFollowers + maxFollowing = userRetrieveSearchParams.maxFollowing + maxStatuses = userRetrieveSearchParams.maxStatuses + minAccountAgeDays = userRetrieveSearchParams.minAccountAgeDays + minFollowers = userRetrieveSearchParams.minFollowers + minFollowing = userRetrieveSearchParams.minFollowing + minStatuses = userRetrieveSearchParams.minStatuses + usernameContains = userRetrieveSearchParams.usernameContains + verifiedOnly = userRetrieveSearchParams.verifiedOnly + verifiedType = userRetrieveSearchParams.verifiedType additionalHeaders = userRetrieveSearchParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveSearchParams.additionalQueryParams.toBuilder() } @@ -69,12 +153,176 @@ private constructor( /** User search query */ fun q(q: String) = apply { this.q = q } + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + /** Pagination cursor for user search */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -188,7 +436,21 @@ private constructor( fun build(): UserRetrieveSearchParams = UserRetrieveSearchParams( checkRequired("q", q), + bioContains, cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -200,7 +462,21 @@ private constructor( QueryParams.builder() .apply { put("q", q) + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() @@ -212,13 +488,47 @@ private constructor( return other is UserRetrieveSearchParams && q == other.q && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } - override fun hashCode(): Int = Objects.hash(q, cursor, additionalHeaders, additionalQueryParams) + override fun hashCode(): Int = + Objects.hash( + q, + bioContains, + cursor, + hasLocation, + hasWebsite, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "UserRetrieveSearchParams{q=$q, cursor=$cursor, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveSearchParams{q=$q, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt index 5b2578a5..757c0f46 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt @@ -18,39 +18,61 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** List recent tweets posted by a user */ +/** + * Omit mode for automatic maximum coverage. Pass next_cursor unchanged. Unprefixed cursors use + * legacy pagination. Shape and billing stay the same. + */ class UserRetrieveTweetsParams private constructor( private val id: String?, private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cardName: String?, private val cashtags: String?, private val conversationId: String?, private val cursor: String?, private val exactPhrase: String?, + private val excludeSource: String?, private val excludeWords: String?, private val fromUser: String?, + private val geocode: String?, private val hashtags: String?, private val includeParentTweet: Boolean?, private val includeReplies: Boolean?, private val inReplyToTweetId: String?, private val language: String?, + private val maxFaves: Long?, + private val maxId: String?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, private val mediaType: MediaType?, private val mentioning: String?, + private val minBookmarks: Long?, private val minFaves: Long?, private val minQuotes: Long?, private val minReplies: Long?, private val minRetweets: Long?, + private val minViews: Long?, + private val nativeRetweets: Boolean?, + private val near: String?, + private val news: Boolean?, private val pageSize: Long?, private val quotes: Quotes?, private val quotesOfTweetId: String?, private val replies: Replies?, private val retweets: Retweets?, private val retweetsOfTweetId: String?, + private val safe: Boolean?, private val sinceDate: LocalDate?, + private val sinceId: String?, + private val source: String?, private val toUser: String?, private val untilDate: LocalDate?, private val url: String?, private val verifiedOnly: Boolean?, + private val within: String?, + private val withinTime: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -60,24 +82,39 @@ private constructor( /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ fun anyWords(): Optional = Optional.ofNullable(anyWords) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Match the Tweet card name. */ + fun cardName(): Optional = Optional.ofNullable(cardName) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(): Optional = Optional.ofNullable(cashtags) /** Conversation ID filter. */ fun conversationId(): Optional = Optional.ofNullable(conversationId) - /** Pagination cursor for user tweets */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) /** Exact phrase to match. */ fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + /** Exclude a source application. */ + fun excludeSource(): Optional = Optional.ofNullable(excludeSource) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(): Optional = Optional.ofNullable(excludeWords) /** Filter by author username. */ fun fromUser(): Optional = Optional.ofNullable(fromUser) + /** Match latitude, longitude, and radius. */ + fun geocode(): Optional = Optional.ofNullable(geocode) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(): Optional = Optional.ofNullable(hashtags) @@ -93,12 +130,30 @@ private constructor( /** Language code filter, e.g. en or tr. */ fun language(): Optional = Optional.ofNullable(language) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(): Optional = Optional.ofNullable(maxId) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + /** Filter by media type. */ fun mediaType(): Optional = Optional.ofNullable(mediaType) /** Filter tweets mentioning a username. */ fun mentioning(): Optional = Optional.ofNullable(mentioning) + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + /** Minimum likes threshold. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) @@ -111,9 +166,21 @@ private constructor( /** Minimum retweets threshold. */ fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + + /** Only return native reposts. */ + fun nativeRetweets(): Optional = Optional.ofNullable(nativeRetweets) + + /** Match a place name. */ + fun near(): Optional = Optional.ofNullable(near) + + /** Only return news results. */ + fun news(): Optional = Optional.ofNullable(news) + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue while + * has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) @@ -132,9 +199,18 @@ private constructor( /** Only retweets of this tweet ID. */ fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + /** Enable the safe-search filter. */ + fun safe(): Optional = Optional.ofNullable(safe) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(): Optional = Optional.ofNullable(sinceId) + + /** Match the source application. */ + fun source(): Optional = Optional.ofNullable(source) + /** Filter replies sent to a username. */ fun toUser(): Optional = Optional.ofNullable(toUser) @@ -147,6 +223,12 @@ private constructor( /** Only return tweets from verified authors. */ fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Set the radius for the near filter. */ + fun within(): Optional = Optional.ofNullable(within) + + /** Match Tweets inside a recent time window. */ + fun withinTime(): Optional = Optional.ofNullable(withinTime) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -168,34 +250,53 @@ private constructor( private var id: String? = null private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cardName: String? = null private var cashtags: String? = null private var conversationId: String? = null private var cursor: String? = null private var exactPhrase: String? = null + private var excludeSource: String? = null private var excludeWords: String? = null private var fromUser: String? = null + private var geocode: String? = null private var hashtags: String? = null private var includeParentTweet: Boolean? = null private var includeReplies: Boolean? = null private var inReplyToTweetId: String? = null private var language: String? = null + private var maxFaves: Long? = null + private var maxId: String? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null private var mediaType: MediaType? = null private var mentioning: String? = null + private var minBookmarks: Long? = null private var minFaves: Long? = null private var minQuotes: Long? = null private var minReplies: Long? = null private var minRetweets: Long? = null + private var minViews: Long? = null + private var nativeRetweets: Boolean? = null + private var near: String? = null + private var news: Boolean? = null private var pageSize: Long? = null private var quotes: Quotes? = null private var quotesOfTweetId: String? = null private var replies: Replies? = null private var retweets: Retweets? = null private var retweetsOfTweetId: String? = null + private var safe: Boolean? = null private var sinceDate: LocalDate? = null + private var sinceId: String? = null + private var source: String? = null private var toUser: String? = null private var untilDate: LocalDate? = null private var url: String? = null private var verifiedOnly: Boolean? = null + private var within: String? = null + private var withinTime: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -203,34 +304,53 @@ private constructor( internal fun from(userRetrieveTweetsParams: UserRetrieveTweetsParams) = apply { id = userRetrieveTweetsParams.id anyWords = userRetrieveTweetsParams.anyWords + blueVerifiedOnly = userRetrieveTweetsParams.blueVerifiedOnly + cardName = userRetrieveTweetsParams.cardName cashtags = userRetrieveTweetsParams.cashtags conversationId = userRetrieveTweetsParams.conversationId cursor = userRetrieveTweetsParams.cursor exactPhrase = userRetrieveTweetsParams.exactPhrase + excludeSource = userRetrieveTweetsParams.excludeSource excludeWords = userRetrieveTweetsParams.excludeWords fromUser = userRetrieveTweetsParams.fromUser + geocode = userRetrieveTweetsParams.geocode hashtags = userRetrieveTweetsParams.hashtags includeParentTweet = userRetrieveTweetsParams.includeParentTweet includeReplies = userRetrieveTweetsParams.includeReplies inReplyToTweetId = userRetrieveTweetsParams.inReplyToTweetId language = userRetrieveTweetsParams.language + maxFaves = userRetrieveTweetsParams.maxFaves + maxId = userRetrieveTweetsParams.maxId + maxQuotes = userRetrieveTweetsParams.maxQuotes + maxReplies = userRetrieveTweetsParams.maxReplies + maxRetweets = userRetrieveTweetsParams.maxRetweets mediaType = userRetrieveTweetsParams.mediaType mentioning = userRetrieveTweetsParams.mentioning + minBookmarks = userRetrieveTweetsParams.minBookmarks minFaves = userRetrieveTweetsParams.minFaves minQuotes = userRetrieveTweetsParams.minQuotes minReplies = userRetrieveTweetsParams.minReplies minRetweets = userRetrieveTweetsParams.minRetweets + minViews = userRetrieveTweetsParams.minViews + nativeRetweets = userRetrieveTweetsParams.nativeRetweets + near = userRetrieveTweetsParams.near + news = userRetrieveTweetsParams.news pageSize = userRetrieveTweetsParams.pageSize quotes = userRetrieveTweetsParams.quotes quotesOfTweetId = userRetrieveTweetsParams.quotesOfTweetId replies = userRetrieveTweetsParams.replies retweets = userRetrieveTweetsParams.retweets retweetsOfTweetId = userRetrieveTweetsParams.retweetsOfTweetId + safe = userRetrieveTweetsParams.safe sinceDate = userRetrieveTweetsParams.sinceDate + sinceId = userRetrieveTweetsParams.sinceId + source = userRetrieveTweetsParams.source toUser = userRetrieveTweetsParams.toUser untilDate = userRetrieveTweetsParams.untilDate url = userRetrieveTweetsParams.url verifiedOnly = userRetrieveTweetsParams.verifiedOnly + within = userRetrieveTweetsParams.within + withinTime = userRetrieveTweetsParams.withinTime additionalHeaders = userRetrieveTweetsParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveTweetsParams.additionalQueryParams.toBuilder() } @@ -248,6 +368,29 @@ private constructor( /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Match the Tweet card name. */ + fun cardName(cardName: String?) = apply { this.cardName = cardName } + + /** Alias for calling [Builder.cardName] with `cardName.orElse(null)`. */ + fun cardName(cardName: Optional) = cardName(cardName.getOrNull()) + /** Cashtags separated by spaces, commas, or lines. */ fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } @@ -261,7 +404,10 @@ private constructor( fun conversationId(conversationId: Optional) = conversationId(conversationId.getOrNull()) - /** Pagination cursor for user tweets */ + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ @@ -273,6 +419,13 @@ private constructor( /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + /** Exclude a source application. */ + fun excludeSource(excludeSource: String?) = apply { this.excludeSource = excludeSource } + + /** Alias for calling [Builder.excludeSource] with `excludeSource.orElse(null)`. */ + fun excludeSource(excludeSource: Optional) = + excludeSource(excludeSource.getOrNull()) + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } @@ -285,6 +438,12 @@ private constructor( /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + /** Match latitude, longitude, and radius. */ + fun geocode(geocode: String?) = apply { this.geocode = geocode } + + /** Alias for calling [Builder.geocode] with `geocode.orElse(null)`. */ + fun geocode(geocode: Optional) = geocode(geocode.getOrNull()) + /** Hashtags separated by spaces, commas, or lines. */ fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } @@ -341,6 +500,64 @@ private constructor( /** Alias for calling [Builder.language] with `language.orElse(null)`. */ fun language(language: Optional) = language(language.getOrNull()) + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Return Tweets older than this Tweet ID. */ + fun maxId(maxId: String?) = apply { this.maxId = maxId } + + /** Alias for calling [Builder.maxId] with `maxId.orElse(null)`. */ + fun maxId(maxId: Optional) = maxId(maxId.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + /** Filter by media type. */ fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } @@ -353,6 +570,19 @@ private constructor( /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + /** Minimum likes threshold. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } @@ -405,9 +635,57 @@ private constructor( /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + + /** Only return native reposts. */ + fun nativeRetweets(nativeRetweets: Boolean?) = apply { + this.nativeRetweets = nativeRetweets + } + + /** + * Alias for [Builder.nativeRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nativeRetweets(nativeRetweets: Boolean) = nativeRetweets(nativeRetweets as Boolean?) + + /** Alias for calling [Builder.nativeRetweets] with `nativeRetweets.orElse(null)`. */ + fun nativeRetweets(nativeRetweets: Optional) = + nativeRetweets(nativeRetweets.getOrNull()) + + /** Match a place name. */ + fun near(near: String?) = apply { this.near = near } + + /** Alias for calling [Builder.near] with `near.orElse(null)`. */ + fun near(near: Optional) = near(near.getOrNull()) + + /** Only return news results. */ + fun news(news: Boolean?) = apply { this.news = news } + /** - * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. - * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. + * Alias for [Builder.news]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun news(news: Boolean) = news(news as Boolean?) + + /** Alias for calling [Builder.news] with `news.orElse(null)`. */ + fun news(news: Optional) = news(news.getOrNull()) + + /** + * Automatic pages accept 1-300 Tweets. Standard pages keep 1-100. Default 20. Continue + * while has_next_page is true. Deprecated aliases remain accepted. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -457,12 +735,37 @@ private constructor( fun retweetsOfTweetId(retweetsOfTweetId: Optional) = retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + /** Enable the safe-search filter. */ + fun safe(safe: Boolean?) = apply { this.safe = safe } + + /** + * Alias for [Builder.safe]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun safe(safe: Boolean) = safe(safe as Boolean?) + + /** Alias for calling [Builder.safe] with `safe.orElse(null)`. */ + fun safe(safe: Optional) = safe(safe.getOrNull()) + /** Start date in YYYY-MM-DD format. */ fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + /** Return Tweets newer than this Tweet ID. */ + fun sinceId(sinceId: String?) = apply { this.sinceId = sinceId } + + /** Alias for calling [Builder.sinceId] with `sinceId.orElse(null)`. */ + fun sinceId(sinceId: Optional) = sinceId(sinceId.getOrNull()) + + /** Match the source application. */ + fun source(source: String?) = apply { this.source = source } + + /** Alias for calling [Builder.source] with `source.orElse(null)`. */ + fun source(source: Optional) = source(source.getOrNull()) + /** Filter replies sent to a username. */ fun toUser(toUser: String?) = apply { this.toUser = toUser } @@ -494,6 +797,18 @@ private constructor( /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + /** Set the radius for the near filter. */ + fun within(within: String?) = apply { this.within = within } + + /** Alias for calling [Builder.within] with `within.orElse(null)`. */ + fun within(within: Optional) = within(within.getOrNull()) + + /** Match Tweets inside a recent time window. */ + fun withinTime(withinTime: String?) = apply { this.withinTime = withinTime } + + /** Alias for calling [Builder.withinTime] with `withinTime.orElse(null)`. */ + fun withinTime(withinTime: Optional) = withinTime(withinTime.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -601,34 +916,53 @@ private constructor( UserRetrieveTweetsParams( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeParentTweet, includeReplies, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -646,34 +980,53 @@ private constructor( QueryParams.builder() .apply { anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cardName?.let { put("cardName", it) } cashtags?.let { put("cashtags", it) } conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } exactPhrase?.let { put("exactPhrase", it) } + excludeSource?.let { put("excludeSource", it) } excludeWords?.let { put("excludeWords", it) } fromUser?.let { put("fromUser", it) } + geocode?.let { put("geocode", it) } hashtags?.let { put("hashtags", it) } includeParentTweet?.let { put("includeParentTweet", it.toString()) } includeReplies?.let { put("includeReplies", it.toString()) } inReplyToTweetId?.let { put("inReplyToTweetId", it) } language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxId?.let { put("maxId", it) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } mediaType?.let { put("mediaType", it.toString()) } mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } minFaves?.let { put("minFaves", it.toString()) } minQuotes?.let { put("minQuotes", it.toString()) } minReplies?.let { put("minReplies", it.toString()) } minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } + nativeRetweets?.let { put("nativeRetweets", it.toString()) } + near?.let { put("near", it) } + news?.let { put("news", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } quotes?.let { put("quotes", it.toString()) } quotesOfTweetId?.let { put("quotesOfTweetId", it) } replies?.let { put("replies", it.toString()) } retweets?.let { put("retweets", it.toString()) } retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + safe?.let { put("safe", it.toString()) } sinceDate?.let { put("sinceDate", it.toString()) } + sinceId?.let { put("sinceId", it) } + source?.let { put("source", it) } toUser?.let { put("toUser", it) } untilDate?.let { put("untilDate", it.toString()) } url?.let { put("url", it) } verifiedOnly?.let { put("verifiedOnly", it.toString()) } + within?.let { put("within", it) } + withinTime?.let { put("withinTime", it) } putAll(additionalQueryParams) } .build() @@ -1278,34 +1631,53 @@ private constructor( return other is UserRetrieveTweetsParams && id == other.id && anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cardName == other.cardName && cashtags == other.cashtags && conversationId == other.conversationId && cursor == other.cursor && exactPhrase == other.exactPhrase && + excludeSource == other.excludeSource && excludeWords == other.excludeWords && fromUser == other.fromUser && + geocode == other.geocode && hashtags == other.hashtags && includeParentTweet == other.includeParentTweet && includeReplies == other.includeReplies && inReplyToTweetId == other.inReplyToTweetId && language == other.language && + maxFaves == other.maxFaves && + maxId == other.maxId && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && mediaType == other.mediaType && mentioning == other.mentioning && + minBookmarks == other.minBookmarks && minFaves == other.minFaves && minQuotes == other.minQuotes && minReplies == other.minReplies && minRetweets == other.minRetweets && + minViews == other.minViews && + nativeRetweets == other.nativeRetweets && + near == other.near && + news == other.news && pageSize == other.pageSize && quotes == other.quotes && quotesOfTweetId == other.quotesOfTweetId && replies == other.replies && retweets == other.retweets && retweetsOfTweetId == other.retweetsOfTweetId && + safe == other.safe && sinceDate == other.sinceDate && + sinceId == other.sinceId && + source == other.source && toUser == other.toUser && untilDate == other.untilDate && url == other.url && verifiedOnly == other.verifiedOnly && + within == other.within && + withinTime == other.withinTime && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } @@ -1314,38 +1686,57 @@ private constructor( Objects.hash( id, anyWords, + blueVerifiedOnly, + cardName, cashtags, conversationId, cursor, exactPhrase, + excludeSource, excludeWords, fromUser, + geocode, hashtags, includeParentTweet, includeReplies, inReplyToTweetId, language, + maxFaves, + maxId, + maxQuotes, + maxReplies, + maxRetweets, mediaType, mentioning, + minBookmarks, minFaves, minQuotes, minReplies, minRetweets, + minViews, + nativeRetweets, + near, + news, pageSize, quotes, quotesOfTweetId, replies, retweets, retweetsOfTweetId, + safe, sinceDate, + sinceId, + source, toUser, untilDate, url, verifiedOnly, + within, + withinTime, additionalHeaders, additionalQueryParams, ) override fun toString() = - "UserRetrieveTweetsParams{id=$id, anyWords=$anyWords, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, includeParentTweet=$includeParentTweet, includeReplies=$includeReplies, inReplyToTweetId=$inReplyToTweetId, language=$language, mediaType=$mediaType, mentioning=$mentioning, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveTweetsParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cardName=$cardName, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeSource=$excludeSource, excludeWords=$excludeWords, fromUser=$fromUser, geocode=$geocode, hashtags=$hashtags, includeParentTweet=$includeParentTweet, includeReplies=$includeReplies, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxId=$maxId, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, nativeRetweets=$nativeRetweets, near=$near, news=$news, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, safe=$safe, sinceDate=$sinceDate, sinceId=$sinceId, source=$source, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, within=$within, withinTime=$withinTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParams.kt index 3262a5f7..497c3a27 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParams.kt @@ -6,9 +6,13 @@ package com.x_twitter_scraper.api.models.x.users +import com.fasterxml.jackson.annotation.JsonCreator +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.Params import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -17,25 +21,100 @@ import kotlin.jvm.optionals.getOrNull class UserRetrieveVerifiedFollowersParams private constructor( private val id: String?, + private val after: String?, + private val bioContains: String?, private val cursor: String?, + private val hasLocation: Boolean?, + private val hasWebsite: Boolean?, + private val limit: Long?, + private val locationContains: String?, + private val maxFollowers: Long?, + private val maxFollowing: Long?, + private val maxStatuses: Long?, + private val minAccountAgeDays: Long?, + private val minFollowers: Long?, + private val minFollowing: Long?, + private val minStatuses: Long?, + private val mode: Mode?, private val pageSize: Long?, + private val usernameContains: String?, + private val verifiedOnly: Boolean?, + private val verifiedType: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) - /** Pagination cursor for verified followers */ + /** Legacy cursor alias. Prefer cursor. */ + fun after(): Optional = Optional.ofNullable(after) + + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(): Optional = Optional.ofNullable(bioContains) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Only return profiles with a location. */ + fun hasLocation(): Optional = Optional.ofNullable(hasLocation) + + /** Only return profiles with a website. */ + fun hasWebsite(): Optional = Optional.ofNullable(hasWebsite) + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit and - * count aliases remain accepted. + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ + fun limit(): Optional = Optional.ofNullable(limit) + + /** Match a location substring, ignoring case. */ + fun locationContains(): Optional = Optional.ofNullable(locationContains) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(): Optional = Optional.ofNullable(maxFollowers) + + /** Maximum following count. */ + fun maxFollowing(): Optional = Optional.ofNullable(maxFollowing) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(): Optional = Optional.ofNullable(maxStatuses) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(): Optional = Optional.ofNullable(minAccountAgeDays) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(): Optional = Optional.ofNullable(minFollowers) + + /** Minimum following count. */ + fun minFollowing(): Optional = Optional.ofNullable(minFollowing) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(): Optional = Optional.ofNullable(minStatuses) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + fun mode(): Optional = Optional.ofNullable(mode) + + /** + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. Continue + * with has_next_page. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Match a username substring, ignoring case. */ + fun usernameContains(): Optional = Optional.ofNullable(usernameContains) + + /** Only return verified profiles. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(): Optional = Optional.ofNullable(verifiedType) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -59,8 +138,25 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var after: String? = null + private var bioContains: String? = null private var cursor: String? = null + private var hasLocation: Boolean? = null + private var hasWebsite: Boolean? = null + private var limit: Long? = null + private var locationContains: String? = null + private var maxFollowers: Long? = null + private var maxFollowing: Long? = null + private var maxStatuses: Long? = null + private var minAccountAgeDays: Long? = null + private var minFollowers: Long? = null + private var minFollowing: Long? = null + private var minStatuses: Long? = null + private var mode: Mode? = null private var pageSize: Long? = null + private var usernameContains: String? = null + private var verifiedOnly: Boolean? = null + private var verifiedType: String? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @@ -69,8 +165,25 @@ private constructor( userRetrieveVerifiedFollowersParams: UserRetrieveVerifiedFollowersParams ) = apply { id = userRetrieveVerifiedFollowersParams.id + after = userRetrieveVerifiedFollowersParams.after + bioContains = userRetrieveVerifiedFollowersParams.bioContains cursor = userRetrieveVerifiedFollowersParams.cursor + hasLocation = userRetrieveVerifiedFollowersParams.hasLocation + hasWebsite = userRetrieveVerifiedFollowersParams.hasWebsite + limit = userRetrieveVerifiedFollowersParams.limit + locationContains = userRetrieveVerifiedFollowersParams.locationContains + maxFollowers = userRetrieveVerifiedFollowersParams.maxFollowers + maxFollowing = userRetrieveVerifiedFollowersParams.maxFollowing + maxStatuses = userRetrieveVerifiedFollowersParams.maxStatuses + minAccountAgeDays = userRetrieveVerifiedFollowersParams.minAccountAgeDays + minFollowers = userRetrieveVerifiedFollowersParams.minFollowers + minFollowing = userRetrieveVerifiedFollowersParams.minFollowing + minStatuses = userRetrieveVerifiedFollowersParams.minStatuses + mode = userRetrieveVerifiedFollowersParams.mode pageSize = userRetrieveVerifiedFollowersParams.pageSize + usernameContains = userRetrieveVerifiedFollowersParams.usernameContains + verifiedOnly = userRetrieveVerifiedFollowersParams.verifiedOnly + verifiedType = userRetrieveVerifiedFollowersParams.verifiedType additionalHeaders = userRetrieveVerifiedFollowersParams.additionalHeaders.toBuilder() additionalQueryParams = userRetrieveVerifiedFollowersParams.additionalQueryParams.toBuilder() @@ -81,17 +194,185 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) - /** Pagination cursor for verified followers */ + /** Legacy cursor alias. Prefer cursor. */ + fun after(after: String?) = apply { this.after = after } + + /** Alias for calling [Builder.after] with `after.orElse(null)`. */ + fun after(after: Optional) = after(after.getOrNull()) + + /** Match any comma-separated or line-separated bio term, ignoring case. */ + fun bioContains(bioContains: String?) = apply { this.bioContains = bioContains } + + /** Alias for calling [Builder.bioContains] with `bioContains.orElse(null)`. */ + fun bioContains(bioContains: Optional) = bioContains(bioContains.getOrNull()) + + /** + * Cursor from the previous response. Xquik cursors resume automatic coverage. Existing + * unprefixed cursors keep legacy standard behavior. + */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Only return profiles with a location. */ + fun hasLocation(hasLocation: Boolean?) = apply { this.hasLocation = hasLocation } + + /** + * Alias for [Builder.hasLocation]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasLocation(hasLocation: Boolean) = hasLocation(hasLocation as Boolean?) + + /** Alias for calling [Builder.hasLocation] with `hasLocation.orElse(null)`. */ + fun hasLocation(hasLocation: Optional) = hasLocation(hasLocation.getOrNull()) + + /** Only return profiles with a website. */ + fun hasWebsite(hasWebsite: Boolean?) = apply { this.hasWebsite = hasWebsite } + + /** + * Alias for [Builder.hasWebsite]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasWebsite(hasWebsite: Boolean) = hasWebsite(hasWebsite as Boolean?) + + /** Alias for calling [Builder.hasWebsite] with `hasWebsite.orElse(null)`. */ + fun hasWebsite(hasWebsite: Optional) = hasWebsite(hasWebsite.getOrNull()) + + /** + * Legacy page-size alias outside explicit coverage mode. Coverage accepts 1-10000. Prefer + * pageSize. + */ + fun limit(limit: Long?) = apply { this.limit = limit } + /** - * Maximum user profiles requested from this page (20-200, default 200). The response can - * contain fewer profiles because the source returned fewer or remaining credits cover fewer - * results. Keep requesting next_cursor while has_next_page is true. The deprecated limit - * and count aliases remain accepted. + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ + fun limit(limit: Optional) = limit(limit.getOrNull()) + + /** Match a location substring, ignoring case. */ + fun locationContains(locationContains: String?) = apply { + this.locationContains = locationContains + } + + /** Alias for calling [Builder.locationContains] with `locationContains.orElse(null)`. */ + fun locationContains(locationContains: Optional) = + locationContains(locationContains.getOrNull()) + + /** Maximum follower count. Missing counts pass this maximum. */ + fun maxFollowers(maxFollowers: Long?) = apply { this.maxFollowers = maxFollowers } + + /** + * Alias for [Builder.maxFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowers(maxFollowers: Long) = maxFollowers(maxFollowers as Long?) + + /** Alias for calling [Builder.maxFollowers] with `maxFollowers.orElse(null)`. */ + fun maxFollowers(maxFollowers: Optional) = maxFollowers(maxFollowers.getOrNull()) + + /** Maximum following count. */ + fun maxFollowing(maxFollowing: Long?) = apply { this.maxFollowing = maxFollowing } + + /** + * Alias for [Builder.maxFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFollowing(maxFollowing: Long) = maxFollowing(maxFollowing as Long?) + + /** Alias for calling [Builder.maxFollowing] with `maxFollowing.orElse(null)`. */ + fun maxFollowing(maxFollowing: Optional) = maxFollowing(maxFollowing.getOrNull()) + + /** Maximum post count. maxPosts is also accepted. */ + fun maxStatuses(maxStatuses: Long?) = apply { this.maxStatuses = maxStatuses } + + /** + * Alias for [Builder.maxStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxStatuses(maxStatuses: Long) = maxStatuses(maxStatuses as Long?) + + /** Alias for calling [Builder.maxStatuses] with `maxStatuses.orElse(null)`. */ + fun maxStatuses(maxStatuses: Optional) = maxStatuses(maxStatuses.getOrNull()) + + /** Minimum account age in whole days. */ + fun minAccountAgeDays(minAccountAgeDays: Long?) = apply { + this.minAccountAgeDays = minAccountAgeDays + } + + /** + * Alias for [Builder.minAccountAgeDays]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minAccountAgeDays(minAccountAgeDays: Long) = + minAccountAgeDays(minAccountAgeDays as Long?) + + /** Alias for calling [Builder.minAccountAgeDays] with `minAccountAgeDays.orElse(null)`. */ + fun minAccountAgeDays(minAccountAgeDays: Optional) = + minAccountAgeDays(minAccountAgeDays.getOrNull()) + + /** Minimum follower count. Filtering happens before billing. */ + fun minFollowers(minFollowers: Long?) = apply { this.minFollowers = minFollowers } + + /** + * Alias for [Builder.minFollowers]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowers(minFollowers: Long) = minFollowers(minFollowers as Long?) + + /** Alias for calling [Builder.minFollowers] with `minFollowers.orElse(null)`. */ + fun minFollowers(minFollowers: Optional) = minFollowers(minFollowers.getOrNull()) + + /** Minimum following count. */ + fun minFollowing(minFollowing: Long?) = apply { this.minFollowing = minFollowing } + + /** + * Alias for [Builder.minFollowing]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFollowing(minFollowing: Long) = minFollowing(minFollowing as Long?) + + /** Alias for calling [Builder.minFollowing] with `minFollowing.orElse(null)`. */ + fun minFollowing(minFollowing: Optional) = minFollowing(minFollowing.getOrNull()) + + /** Minimum post count. minPosts is also accepted. */ + fun minStatuses(minStatuses: Long?) = apply { this.minStatuses = minStatuses } + + /** + * Alias for [Builder.minStatuses]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minStatuses(minStatuses: Long) = minStatuses(minStatuses as Long?) + + /** Alias for calling [Builder.minStatuses] with `minStatuses.orElse(null)`. */ + fun minStatuses(minStatuses: Optional) = minStatuses(minStatuses.getOrNull()) + + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage + * returns diagnostics once and rejects cursors. + */ + fun mode(mode: Mode?) = apply { this.mode = mode } + + /** Alias for calling [Builder.mode] with `mode.orElse(null)`. */ + fun mode(mode: Optional) = mode(mode.getOrNull()) + + /** + * Maximum user profiles: automatic 300; standard 200. Sources return fewer profiles. + * Continue with has_next_page. */ fun pageSize(pageSize: Long?) = apply { this.pageSize = pageSize } @@ -105,6 +386,34 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Match a username substring, ignoring case. */ + fun usernameContains(usernameContains: String?) = apply { + this.usernameContains = usernameContains + } + + /** Alias for calling [Builder.usernameContains] with `usernameContains.orElse(null)`. */ + fun usernameContains(usernameContains: Optional) = + usernameContains(usernameContains.getOrNull()) + + /** Only return verified profiles. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + + /** Match the verification type exactly, ignoring case. */ + fun verifiedType(verifiedType: String?) = apply { this.verifiedType = verifiedType } + + /** Alias for calling [Builder.verifiedType] with `verifiedType.orElse(null)`. */ + fun verifiedType(verifiedType: Optional) = verifiedType(verifiedType.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -211,8 +520,25 @@ private constructor( fun build(): UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams( id, + after, + bioContains, cursor, + hasLocation, + hasWebsite, + limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, pageSize, + usernameContains, + verifiedOnly, + verifiedType, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -229,12 +555,169 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + after?.let { put("after", it) } + bioContains?.let { put("bioContains", it) } cursor?.let { put("cursor", it) } + hasLocation?.let { put("hasLocation", it.toString()) } + hasWebsite?.let { put("hasWebsite", it.toString()) } + limit?.let { put("limit", it.toString()) } + locationContains?.let { put("locationContains", it) } + maxFollowers?.let { put("maxFollowers", it.toString()) } + maxFollowing?.let { put("maxFollowing", it.toString()) } + maxStatuses?.let { put("maxStatuses", it.toString()) } + minAccountAgeDays?.let { put("minAccountAgeDays", it.toString()) } + minFollowers?.let { put("minFollowers", it.toString()) } + minFollowing?.let { put("minFollowing", it.toString()) } + minStatuses?.let { put("minStatuses", it.toString()) } + mode?.let { put("mode", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + usernameContains?.let { put("usernameContains", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } + verifiedType?.let { put("verifiedType", it) } putAll(additionalQueryParams) } .build() + /** + * Omit mode for resumable maximum coverage. Standard keeps legacy pagination. Coverage returns + * diagnostics once and rejects cursors. + */ + class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val STANDARD = of("standard") + + @JvmField val COVERAGE = of("coverage") + + @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) + } + + /** An enum containing [Mode]'s known values. */ + enum class Known { + STANDARD, + COVERAGE, + } + + /** + * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Mode] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + STANDARD, + COVERAGE, + /** An enum member indicating that [Mode] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + STANDARD -> Value.STANDARD + COVERAGE -> Value.COVERAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + STANDARD -> Known.STANDARD + COVERAGE -> Known.COVERAGE + else -> throw XTwitterScraperInvalidDataException("Unknown Mode: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Mode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Mode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -242,15 +725,55 @@ private constructor( return other is UserRetrieveVerifiedFollowersParams && id == other.id && + after == other.after && + bioContains == other.bioContains && cursor == other.cursor && + hasLocation == other.hasLocation && + hasWebsite == other.hasWebsite && + limit == other.limit && + locationContains == other.locationContains && + maxFollowers == other.maxFollowers && + maxFollowing == other.maxFollowing && + maxStatuses == other.maxStatuses && + minAccountAgeDays == other.minAccountAgeDays && + minFollowers == other.minFollowers && + minFollowing == other.minFollowing && + minStatuses == other.minStatuses && + mode == other.mode && pageSize == other.pageSize && + usernameContains == other.usernameContains && + verifiedOnly == other.verifiedOnly && + verifiedType == other.verifiedType && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + after, + bioContains, + cursor, + hasLocation, + hasWebsite, + limit, + locationContains, + maxFollowers, + maxFollowing, + maxStatuses, + minAccountAgeDays, + minFollowers, + minFollowing, + minStatuses, + mode, + pageSize, + usernameContains, + verifiedOnly, + verifiedType, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "UserRetrieveVerifiedFollowersParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "UserRetrieveVerifiedFollowersParams{id=$id, after=$after, bioContains=$bioContains, cursor=$cursor, hasLocation=$hasLocation, hasWebsite=$hasWebsite, limit=$limit, locationContains=$locationContains, maxFollowers=$maxFollowers, maxFollowing=$maxFollowing, maxStatuses=$maxStatuses, minAccountAgeDays=$minAccountAgeDays, minFollowers=$minFollowers, minFollowing=$minFollowing, minStatuses=$minStatuses, mode=$mode, pageSize=$pageSize, usernameContains=$usernameContains, verifiedOnly=$verifiedOnly, verifiedType=$verifiedType, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponse.kt new file mode 100644 index 00000000..79d174d8 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponse.kt @@ -0,0 +1,2232 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.BaseDeserializer +import com.x_twitter_scraper.api.core.BaseSerializer +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.ExcludeMissing +import com.x_twitter_scraper.api.core.JsonField +import com.x_twitter_scraper.api.core.JsonMissing +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.allMaxBy +import com.x_twitter_scraper.api.core.checkKnown +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.getOrThrow +import com.x_twitter_scraper.api.core.toImmutable +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy behavior. + * Follow next_cursor while has_next_page is true. + */ +@JsonDeserialize(using = UserRetrieveVerifiedFollowersResponse.Deserializer::class) +@JsonSerialize(using = UserRetrieveVerifiedFollowersResponse.Serializer::class) +class UserRetrieveVerifiedFollowersResponse +private constructor( + private val paginatedUsers: PaginatedUsers? = null, + private val listCoverage: UserListCoverageResponse? = null, + private val _json: JsonValue? = null, +) { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun paginatedUsers(): Optional = Optional.ofNullable(paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun listCoverage(): Optional = Optional.ofNullable(listCoverage) + + fun isPaginatedUsers(): Boolean = paginatedUsers != null + + fun isListCoverage(): Boolean = listCoverage != null + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asPaginatedUsers(): PaginatedUsers = paginatedUsers.getOrThrow("paginatedUsers") + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun asListCoverage(): UserListCoverageResponse = listCoverage.getOrThrow("listCoverage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.x_twitter_scraper.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = userRetrieveVerifiedFollowersResponse.accept(new UserRetrieveVerifiedFollowersResponse.Visitor>() { + * @Override + * public Optional visitPaginatedUsers(PaginatedUsers paginatedUsers) { + * return Optional.of(paginatedUsers.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws XTwitterScraperInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + paginatedUsers != null -> visitor.visitPaginatedUsers(paginatedUsers) + listCoverage != null -> visitor.visitListCoverage(listCoverage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UserRetrieveVerifiedFollowersResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) { + paginatedUsers.validate() + } + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) { + listCoverage.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitPaginatedUsers(paginatedUsers: PaginatedUsers) = + paginatedUsers.validity() + + override fun visitListCoverage(listCoverage: UserListCoverageResponse) = + listCoverage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserRetrieveVerifiedFollowersResponse && + paginatedUsers == other.paginatedUsers && + listCoverage == other.listCoverage + } + + override fun hashCode(): Int = Objects.hash(paginatedUsers, listCoverage) + + override fun toString(): String = + when { + paginatedUsers != null -> + "UserRetrieveVerifiedFollowersResponse{paginatedUsers=$paginatedUsers}" + listCoverage != null -> + "UserRetrieveVerifiedFollowersResponse{listCoverage=$listCoverage}" + _json != null -> "UserRetrieveVerifiedFollowersResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UserRetrieveVerifiedFollowersResponse") + } + + companion object { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofPaginatedUsers(paginatedUsers: PaginatedUsers) = + UserRetrieveVerifiedFollowersResponse(paginatedUsers = paginatedUsers) + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + @JvmStatic + fun ofListCoverage(listCoverage: UserListCoverageResponse) = + UserRetrieveVerifiedFollowersResponse(listCoverage = listCoverage) + } + + /** + * An interface that defines how to map each variant of [UserRetrieveVerifiedFollowersResponse] + * to a value of type [T]. + */ + interface Visitor { + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitPaginatedUsers(paginatedUsers: PaginatedUsers): T + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests + * merge independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + fun visitListCoverage(listCoverage: UserListCoverageResponse): T + + /** + * Maps an unknown variant of [UserRetrieveVerifiedFollowersResponse] to a value of type + * [T]. + * + * An instance of [UserRetrieveVerifiedFollowersResponse] can contain an unknown variant if + * it was deserialized from data that doesn't match any known variant. For example, if the + * SDK is on an older version than the API, then the API may respond with new variants that + * the SDK is unaware of. + * + * @throws XTwitterScraperInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw XTwitterScraperInvalidDataException( + "Unknown UserRetrieveVerifiedFollowersResponse: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer( + UserRetrieveVerifiedFollowersResponse::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): UserRetrieveVerifiedFollowersResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveVerifiedFollowersResponse(paginatedUsers = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UserRetrieveVerifiedFollowersResponse(listCoverage = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> UserRetrieveVerifiedFollowersResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer( + UserRetrieveVerifiedFollowersResponse::class + ) { + + override fun serialize( + value: UserRetrieveVerifiedFollowersResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.paginatedUsers != null -> generator.writeObject(value.paginatedUsers) + value.listCoverage != null -> generator.writeObject(value.listCoverage) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UserRetrieveVerifiedFollowersResponse") + } + } + } + + /** + * Paginated user profiles. No-mode follower, following, and verified follower requests merge + * independent views automatically. Response fields, page size, aliases, filters, and + * per-returned-profile billing stay unchanged. Existing unprefixed cursors retain legacy + * behavior. Follow next_cursor while has_next_page is true. + */ + class UserListCoverageResponse + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val hasNextPage: JsonField, + private val nextCursor: JsonField, + private val users: JsonField>, + private val diagnostic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("has_next_page") + @ExcludeMissing + hasNextPage: JsonField = JsonMissing.of(), + @JsonProperty("next_cursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("users") + @ExcludeMissing + users: JsonField> = JsonMissing.of(), + @JsonProperty("diagnostic") + @ExcludeMissing + diagnostic: JsonField = JsonMissing.of(), + ) : this(hasNextPage, nextCursor, users, diagnostic, mutableMapOf()) + + fun toPaginatedUsers(): PaginatedUsers = + PaginatedUsers.builder() + .hasNextPage(hasNextPage) + .nextCursor(nextCursor) + .users(users) + .build() + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun hasNextPage(): Boolean = hasNextPage.getRequired("has_next_page") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun nextCursor(): String = nextCursor.getRequired("next_cursor") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun users(): List = users.getRequired("users") + + /** + * Coverage evidence across parallel relationship strategies. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun diagnostic(): Diagnostic = diagnostic.getRequired("diagnostic") + + /** + * Returns the raw JSON value of [hasNextPage]. + * + * Unlike [hasNextPage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("has_next_page") + @ExcludeMissing + fun _hasNextPage(): JsonField = hasNextPage + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("next_cursor") + @ExcludeMissing + fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [users]. + * + * Unlike [users], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("users") @ExcludeMissing fun _users(): JsonField> = users + + /** + * Returns the raw JSON value of [diagnostic]. + * + * Unlike [diagnostic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("diagnostic") + @ExcludeMissing + fun _diagnostic(): JsonField = diagnostic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UserListCoverageResponse]. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UserListCoverageResponse]. */ + class Builder internal constructor() { + + private var hasNextPage: JsonField? = null + private var nextCursor: JsonField? = null + private var users: JsonField>? = null + private var diagnostic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(userListCoverageResponse: UserListCoverageResponse) = apply { + hasNextPage = userListCoverageResponse.hasNextPage + nextCursor = userListCoverageResponse.nextCursor + users = userListCoverageResponse.users.map { it.toMutableList() } + diagnostic = userListCoverageResponse.diagnostic + additionalProperties = userListCoverageResponse.additionalProperties.toMutableMap() + } + + fun hasNextPage(hasNextPage: Boolean) = hasNextPage(JsonField.of(hasNextPage)) + + /** + * Sets [Builder.hasNextPage] to an arbitrary JSON value. + * + * You should usually call [Builder.hasNextPage] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasNextPage(hasNextPage: JsonField) = apply { + this.hasNextPage = hasNextPage + } + + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + fun users(users: List) = users(JsonField.of(users)) + + /** + * Sets [Builder.users] to an arbitrary JSON value. + * + * You should usually call [Builder.users] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun users(users: JsonField>) = apply { + this.users = users.map { it.toMutableList() } + } + + /** + * Adds a single [UserProfile] to [users]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addUser(user: UserProfile) = apply { + users = + (users ?: JsonField.of(mutableListOf())).also { + checkKnown("users", it).add(user) + } + } + + /** Coverage evidence across parallel relationship strategies. */ + fun diagnostic(diagnostic: Diagnostic) = diagnostic(JsonField.of(diagnostic)) + + /** + * Sets [Builder.diagnostic] to an arbitrary JSON value. + * + * You should usually call [Builder.diagnostic] with a well-typed [Diagnostic] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun diagnostic(diagnostic: JsonField) = apply { + this.diagnostic = diagnostic + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UserListCoverageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .hasNextPage() + * .nextCursor() + * .users() + * .diagnostic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UserListCoverageResponse = + UserListCoverageResponse( + checkRequired("hasNextPage", hasNextPage), + checkRequired("nextCursor", nextCursor), + checkRequired("users", users).map { it.toImmutable() }, + checkRequired("diagnostic", diagnostic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): UserListCoverageResponse = apply { + if (validated) { + return@apply + } + + hasNextPage() + nextCursor() + users().forEach { it.validate() } + diagnostic().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (hasNextPage.asKnown().isPresent) 1 else 0) + + (if (nextCursor.asKnown().isPresent) 1 else 0) + + (users.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (diagnostic.asKnown().getOrNull()?.validity() ?: 0) + + /** Coverage evidence across parallel relationship strategies. */ + class Diagnostic + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val complete: JsonField, + private val cursorFailureCount: JsonField, + private val deadlineReached: JsonField, + private val duplicateCount: JsonField, + private val failedStrategyCount: JsonField, + private val malformedCount: JsonField, + private val pagesFetched: JsonField, + private val responseTruncated: JsonField, + private val resultLimitReached: JsonField, + private val returnedUsers: JsonField, + private val stalledStrategyCount: JsonField, + private val strategies: JsonField>, + private val strategyCount: JsonField, + private val uniqueUsers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("complete") + @ExcludeMissing + complete: JsonField = JsonMissing.of(), + @JsonProperty("cursorFailureCount") + @ExcludeMissing + cursorFailureCount: JsonField = JsonMissing.of(), + @JsonProperty("deadlineReached") + @ExcludeMissing + deadlineReached: JsonField = JsonMissing.of(), + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("failedStrategyCount") + @ExcludeMissing + failedStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("malformedCount") + @ExcludeMissing + malformedCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("responseTruncated") + @ExcludeMissing + responseTruncated: JsonField = JsonMissing.of(), + @JsonProperty("resultLimitReached") + @ExcludeMissing + resultLimitReached: JsonField = JsonMissing.of(), + @JsonProperty("returnedUsers") + @ExcludeMissing + returnedUsers: JsonField = JsonMissing.of(), + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + stalledStrategyCount: JsonField = JsonMissing.of(), + @JsonProperty("strategies") + @ExcludeMissing + strategies: JsonField> = JsonMissing.of(), + @JsonProperty("strategyCount") + @ExcludeMissing + strategyCount: JsonField = JsonMissing.of(), + @JsonProperty("uniqueUsers") + @ExcludeMissing + uniqueUsers: JsonField = JsonMissing.of(), + ) : this( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + mutableMapOf(), + ) + + /** + * True when every strategy exhausted its source. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun complete(): Boolean = complete.getRequired("complete") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun cursorFailureCount(): Long = cursorFailureCount.getRequired("cursorFailureCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun deadlineReached(): Boolean = deadlineReached.getRequired("deadlineReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun failedStrategyCount(): Long = failedStrategyCount.getRequired("failedStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun malformedCount(): Long = malformedCount.getRequired("malformedCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * Whether credits or the requested limit reduced output. + * + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun responseTruncated(): Boolean = responseTruncated.getRequired("responseTruncated") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun resultLimitReached(): Boolean = resultLimitReached.getRequired("resultLimitReached") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun returnedUsers(): Long = returnedUsers.getRequired("returnedUsers") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun stalledStrategyCount(): Long = + stalledStrategyCount.getRequired("stalledStrategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategies(): List = strategies.getRequired("strategies") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun strategyCount(): Long = strategyCount.getRequired("strategyCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun uniqueUsers(): Long = uniqueUsers.getRequired("uniqueUsers") + + /** + * Returns the raw JSON value of [complete]. + * + * Unlike [complete], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("complete") @ExcludeMissing fun _complete(): JsonField = complete + + /** + * Returns the raw JSON value of [cursorFailureCount]. + * + * Unlike [cursorFailureCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("cursorFailureCount") + @ExcludeMissing + fun _cursorFailureCount(): JsonField = cursorFailureCount + + /** + * Returns the raw JSON value of [deadlineReached]. + * + * Unlike [deadlineReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("deadlineReached") + @ExcludeMissing + fun _deadlineReached(): JsonField = deadlineReached + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [failedStrategyCount]. + * + * Unlike [failedStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("failedStrategyCount") + @ExcludeMissing + fun _failedStrategyCount(): JsonField = failedStrategyCount + + /** + * Returns the raw JSON value of [malformedCount]. + * + * Unlike [malformedCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("malformedCount") + @ExcludeMissing + fun _malformedCount(): JsonField = malformedCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [responseTruncated]. + * + * Unlike [responseTruncated], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("responseTruncated") + @ExcludeMissing + fun _responseTruncated(): JsonField = responseTruncated + + /** + * Returns the raw JSON value of [resultLimitReached]. + * + * Unlike [resultLimitReached], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("resultLimitReached") + @ExcludeMissing + fun _resultLimitReached(): JsonField = resultLimitReached + + /** + * Returns the raw JSON value of [returnedUsers]. + * + * Unlike [returnedUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("returnedUsers") + @ExcludeMissing + fun _returnedUsers(): JsonField = returnedUsers + + /** + * Returns the raw JSON value of [stalledStrategyCount]. + * + * Unlike [stalledStrategyCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stalledStrategyCount") + @ExcludeMissing + fun _stalledStrategyCount(): JsonField = stalledStrategyCount + + /** + * Returns the raw JSON value of [strategies]. + * + * Unlike [strategies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategies") + @ExcludeMissing + fun _strategies(): JsonField> = strategies + + /** + * Returns the raw JSON value of [strategyCount]. + * + * Unlike [strategyCount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategyCount") + @ExcludeMissing + fun _strategyCount(): JsonField = strategyCount + + /** + * Returns the raw JSON value of [uniqueUsers]. + * + * Unlike [uniqueUsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("uniqueUsers") + @ExcludeMissing + fun _uniqueUsers(): JsonField = uniqueUsers + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Diagnostic]. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Diagnostic]. */ + class Builder internal constructor() { + + private var complete: JsonField? = null + private var cursorFailureCount: JsonField? = null + private var deadlineReached: JsonField? = null + private var duplicateCount: JsonField? = null + private var failedStrategyCount: JsonField? = null + private var malformedCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var responseTruncated: JsonField? = null + private var resultLimitReached: JsonField? = null + private var returnedUsers: JsonField? = null + private var stalledStrategyCount: JsonField? = null + private var strategies: JsonField>? = null + private var strategyCount: JsonField? = null + private var uniqueUsers: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(diagnostic: Diagnostic) = apply { + complete = diagnostic.complete + cursorFailureCount = diagnostic.cursorFailureCount + deadlineReached = diagnostic.deadlineReached + duplicateCount = diagnostic.duplicateCount + failedStrategyCount = diagnostic.failedStrategyCount + malformedCount = diagnostic.malformedCount + pagesFetched = diagnostic.pagesFetched + responseTruncated = diagnostic.responseTruncated + resultLimitReached = diagnostic.resultLimitReached + returnedUsers = diagnostic.returnedUsers + stalledStrategyCount = diagnostic.stalledStrategyCount + strategies = diagnostic.strategies.map { it.toMutableList() } + strategyCount = diagnostic.strategyCount + uniqueUsers = diagnostic.uniqueUsers + additionalProperties = diagnostic.additionalProperties.toMutableMap() + } + + /** True when every strategy exhausted its source. */ + fun complete(complete: Boolean) = complete(JsonField.of(complete)) + + /** + * Sets [Builder.complete] to an arbitrary JSON value. + * + * You should usually call [Builder.complete] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun complete(complete: JsonField) = apply { this.complete = complete } + + fun cursorFailureCount(cursorFailureCount: Long) = + cursorFailureCount(JsonField.of(cursorFailureCount)) + + /** + * Sets [Builder.cursorFailureCount] to an arbitrary JSON value. + * + * You should usually call [Builder.cursorFailureCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun cursorFailureCount(cursorFailureCount: JsonField) = apply { + this.cursorFailureCount = cursorFailureCount + } + + fun deadlineReached(deadlineReached: Boolean) = + deadlineReached(JsonField.of(deadlineReached)) + + /** + * Sets [Builder.deadlineReached] to an arbitrary JSON value. + * + * You should usually call [Builder.deadlineReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun deadlineReached(deadlineReached: JsonField) = apply { + this.deadlineReached = deadlineReached + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun failedStrategyCount(failedStrategyCount: Long) = + failedStrategyCount(JsonField.of(failedStrategyCount)) + + /** + * Sets [Builder.failedStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.failedStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun failedStrategyCount(failedStrategyCount: JsonField) = apply { + this.failedStrategyCount = failedStrategyCount + } + + fun malformedCount(malformedCount: Long) = + malformedCount(JsonField.of(malformedCount)) + + /** + * Sets [Builder.malformedCount] to an arbitrary JSON value. + * + * You should usually call [Builder.malformedCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun malformedCount(malformedCount: JsonField) = apply { + this.malformedCount = malformedCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + /** Whether credits or the requested limit reduced output. */ + fun responseTruncated(responseTruncated: Boolean) = + responseTruncated(JsonField.of(responseTruncated)) + + /** + * Sets [Builder.responseTruncated] to an arbitrary JSON value. + * + * You should usually call [Builder.responseTruncated] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun responseTruncated(responseTruncated: JsonField) = apply { + this.responseTruncated = responseTruncated + } + + fun resultLimitReached(resultLimitReached: Boolean) = + resultLimitReached(JsonField.of(resultLimitReached)) + + /** + * Sets [Builder.resultLimitReached] to an arbitrary JSON value. + * + * You should usually call [Builder.resultLimitReached] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun resultLimitReached(resultLimitReached: JsonField) = apply { + this.resultLimitReached = resultLimitReached + } + + fun returnedUsers(returnedUsers: Long) = returnedUsers(JsonField.of(returnedUsers)) + + /** + * Sets [Builder.returnedUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.returnedUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun returnedUsers(returnedUsers: JsonField) = apply { + this.returnedUsers = returnedUsers + } + + fun stalledStrategyCount(stalledStrategyCount: Long) = + stalledStrategyCount(JsonField.of(stalledStrategyCount)) + + /** + * Sets [Builder.stalledStrategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.stalledStrategyCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun stalledStrategyCount(stalledStrategyCount: JsonField) = apply { + this.stalledStrategyCount = stalledStrategyCount + } + + fun strategies(strategies: List) = strategies(JsonField.of(strategies)) + + /** + * Sets [Builder.strategies] to an arbitrary JSON value. + * + * You should usually call [Builder.strategies] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun strategies(strategies: JsonField>) = apply { + this.strategies = strategies.map { it.toMutableList() } + } + + /** + * Adds a single [Strategy] to [strategies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addStrategy(strategy: Strategy) = apply { + strategies = + (strategies ?: JsonField.of(mutableListOf())).also { + checkKnown("strategies", it).add(strategy) + } + } + + fun strategyCount(strategyCount: Long) = strategyCount(JsonField.of(strategyCount)) + + /** + * Sets [Builder.strategyCount] to an arbitrary JSON value. + * + * You should usually call [Builder.strategyCount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun strategyCount(strategyCount: JsonField) = apply { + this.strategyCount = strategyCount + } + + fun uniqueUsers(uniqueUsers: Long) = uniqueUsers(JsonField.of(uniqueUsers)) + + /** + * Sets [Builder.uniqueUsers] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueUsers] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun uniqueUsers(uniqueUsers: JsonField) = apply { + this.uniqueUsers = uniqueUsers + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Diagnostic]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .complete() + * .cursorFailureCount() + * .deadlineReached() + * .duplicateCount() + * .failedStrategyCount() + * .malformedCount() + * .pagesFetched() + * .responseTruncated() + * .resultLimitReached() + * .returnedUsers() + * .stalledStrategyCount() + * .strategies() + * .strategyCount() + * .uniqueUsers() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Diagnostic = + Diagnostic( + checkRequired("complete", complete), + checkRequired("cursorFailureCount", cursorFailureCount), + checkRequired("deadlineReached", deadlineReached), + checkRequired("duplicateCount", duplicateCount), + checkRequired("failedStrategyCount", failedStrategyCount), + checkRequired("malformedCount", malformedCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("responseTruncated", responseTruncated), + checkRequired("resultLimitReached", resultLimitReached), + checkRequired("returnedUsers", returnedUsers), + checkRequired("stalledStrategyCount", stalledStrategyCount), + checkRequired("strategies", strategies).map { it.toImmutable() }, + checkRequired("strategyCount", strategyCount), + checkRequired("uniqueUsers", uniqueUsers), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Diagnostic = apply { + if (validated) { + return@apply + } + + complete() + cursorFailureCount() + deadlineReached() + duplicateCount() + failedStrategyCount() + malformedCount() + pagesFetched() + responseTruncated() + resultLimitReached() + returnedUsers() + stalledStrategyCount() + strategies().forEach { it.validate() } + strategyCount() + uniqueUsers() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (complete.asKnown().isPresent) 1 else 0) + + (if (cursorFailureCount.asKnown().isPresent) 1 else 0) + + (if (deadlineReached.asKnown().isPresent) 1 else 0) + + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (failedStrategyCount.asKnown().isPresent) 1 else 0) + + (if (malformedCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (if (responseTruncated.asKnown().isPresent) 1 else 0) + + (if (resultLimitReached.asKnown().isPresent) 1 else 0) + + (if (returnedUsers.asKnown().isPresent) 1 else 0) + + (if (stalledStrategyCount.asKnown().isPresent) 1 else 0) + + (strategies.asKnown().getOrNull()?.sumOf { it.validity() } ?: 0) + + (if (strategyCount.asKnown().isPresent) 1 else 0) + + (if (uniqueUsers.asKnown().isPresent) 1 else 0) + + class Strategy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val duplicateCount: JsonField, + private val pagesFetched: JsonField, + private val stopReason: JsonField, + private val strategy: JsonField, + private val uniqueAdded: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("duplicateCount") + @ExcludeMissing + duplicateCount: JsonField = JsonMissing.of(), + @JsonProperty("pagesFetched") + @ExcludeMissing + pagesFetched: JsonField = JsonMissing.of(), + @JsonProperty("stopReason") + @ExcludeMissing + stopReason: JsonField = JsonMissing.of(), + @JsonProperty("strategy") + @ExcludeMissing + strategy: JsonField = JsonMissing.of(), + @JsonProperty("uniqueAdded") + @ExcludeMissing + uniqueAdded: JsonField = JsonMissing.of(), + ) : this( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + mutableMapOf(), + ) + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun duplicateCount(): Long = duplicateCount.getRequired("duplicateCount") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun pagesFetched(): Long = pagesFetched.getRequired("pagesFetched") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun stopReason(): StopReason = stopReason.getRequired("stopReason") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun strategy(): Long = strategy.getRequired("strategy") + + /** + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun uniqueAdded(): Long = uniqueAdded.getRequired("uniqueAdded") + + /** + * Returns the raw JSON value of [duplicateCount]. + * + * Unlike [duplicateCount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("duplicateCount") + @ExcludeMissing + fun _duplicateCount(): JsonField = duplicateCount + + /** + * Returns the raw JSON value of [pagesFetched]. + * + * Unlike [pagesFetched], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("pagesFetched") + @ExcludeMissing + fun _pagesFetched(): JsonField = pagesFetched + + /** + * Returns the raw JSON value of [stopReason]. + * + * Unlike [stopReason], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("stopReason") + @ExcludeMissing + fun _stopReason(): JsonField = stopReason + + /** + * Returns the raw JSON value of [strategy]. + * + * Unlike [strategy], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strategy") + @ExcludeMissing + fun _strategy(): JsonField = strategy + + /** + * Returns the raw JSON value of [uniqueAdded]. + * + * Unlike [uniqueAdded], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("uniqueAdded") + @ExcludeMissing + fun _uniqueAdded(): JsonField = uniqueAdded + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Strategy]. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Strategy]. */ + class Builder internal constructor() { + + private var duplicateCount: JsonField? = null + private var pagesFetched: JsonField? = null + private var stopReason: JsonField? = null + private var strategy: JsonField? = null + private var uniqueAdded: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(strategy: Strategy) = apply { + duplicateCount = strategy.duplicateCount + pagesFetched = strategy.pagesFetched + stopReason = strategy.stopReason + this.strategy = strategy.strategy + uniqueAdded = strategy.uniqueAdded + additionalProperties = strategy.additionalProperties.toMutableMap() + } + + fun duplicateCount(duplicateCount: Long) = + duplicateCount(JsonField.of(duplicateCount)) + + /** + * Sets [Builder.duplicateCount] to an arbitrary JSON value. + * + * You should usually call [Builder.duplicateCount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun duplicateCount(duplicateCount: JsonField) = apply { + this.duplicateCount = duplicateCount + } + + fun pagesFetched(pagesFetched: Long) = pagesFetched(JsonField.of(pagesFetched)) + + /** + * Sets [Builder.pagesFetched] to an arbitrary JSON value. + * + * You should usually call [Builder.pagesFetched] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pagesFetched(pagesFetched: JsonField) = apply { + this.pagesFetched = pagesFetched + } + + fun stopReason(stopReason: StopReason) = stopReason(JsonField.of(stopReason)) + + /** + * Sets [Builder.stopReason] to an arbitrary JSON value. + * + * You should usually call [Builder.stopReason] with a well-typed [StopReason] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun stopReason(stopReason: JsonField) = apply { + this.stopReason = stopReason + } + + fun strategy(strategy: Long) = strategy(JsonField.of(strategy)) + + /** + * Sets [Builder.strategy] to an arbitrary JSON value. + * + * You should usually call [Builder.strategy] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun strategy(strategy: JsonField) = apply { this.strategy = strategy } + + fun uniqueAdded(uniqueAdded: Long) = uniqueAdded(JsonField.of(uniqueAdded)) + + /** + * Sets [Builder.uniqueAdded] to an arbitrary JSON value. + * + * You should usually call [Builder.uniqueAdded] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun uniqueAdded(uniqueAdded: JsonField) = apply { + this.uniqueAdded = uniqueAdded + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Strategy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .duplicateCount() + * .pagesFetched() + * .stopReason() + * .strategy() + * .uniqueAdded() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Strategy = + Strategy( + checkRequired("duplicateCount", duplicateCount), + checkRequired("pagesFetched", pagesFetched), + checkRequired("stopReason", stopReason), + checkRequired("strategy", strategy), + checkRequired("uniqueAdded", uniqueAdded), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Strategy = apply { + if (validated) { + return@apply + } + + duplicateCount() + pagesFetched() + stopReason().validate() + strategy() + uniqueAdded() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (duplicateCount.asKnown().isPresent) 1 else 0) + + (if (pagesFetched.asKnown().isPresent) 1 else 0) + + (stopReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (strategy.asKnown().isPresent) 1 else 0) + + (if (uniqueAdded.asKnown().isPresent) 1 else 0) + + class StopReason + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + @JvmField val CURSOR_FAILURE = of("cursor_failure") + + @JvmField val DEADLINE = of("deadline") + + @JvmField val EXHAUSTED = of("exhausted") + + @JvmField val FAILED = of("failed") + + @JvmField val PAGE_LIMIT = of("page_limit") + + @JvmField val RESULT_LIMIT = of("result_limit") + + @JvmField val STALLED = of("stalled") + + @JvmStatic fun of(value: String) = StopReason(JsonField.of(value)) + } + + /** An enum containing [StopReason]'s known values. */ + enum class Known { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + } + + /** + * An enum containing [StopReason]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [StopReason] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CURSOR_FAILURE, + DEADLINE, + EXHAUSTED, + FAILED, + PAGE_LIMIT, + RESULT_LIMIT, + STALLED, + /** + * An enum member indicating that [StopReason] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CURSOR_FAILURE -> Value.CURSOR_FAILURE + DEADLINE -> Value.DEADLINE + EXHAUSTED -> Value.EXHAUSTED + FAILED -> Value.FAILED + PAGE_LIMIT -> Value.PAGE_LIMIT + RESULT_LIMIT -> Value.RESULT_LIMIT + STALLED -> Value.STALLED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is + * a not a known member. + */ + fun known(): Known = + when (this) { + CURSOR_FAILURE -> Known.CURSOR_FAILURE + DEADLINE -> Known.DEADLINE + EXHAUSTED -> Known.EXHAUSTED + FAILED -> Known.FAILED + PAGE_LIMIT -> Known.PAGE_LIMIT + RESULT_LIMIT -> Known.RESULT_LIMIT + STALLED -> Known.STALLED + else -> + throw XTwitterScraperInvalidDataException( + "Unknown StopReason: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): StopReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StopReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Strategy && + duplicateCount == other.duplicateCount && + pagesFetched == other.pagesFetched && + stopReason == other.stopReason && + strategy == other.strategy && + uniqueAdded == other.uniqueAdded && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + duplicateCount, + pagesFetched, + stopReason, + strategy, + uniqueAdded, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Strategy{duplicateCount=$duplicateCount, pagesFetched=$pagesFetched, stopReason=$stopReason, strategy=$strategy, uniqueAdded=$uniqueAdded, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Diagnostic && + complete == other.complete && + cursorFailureCount == other.cursorFailureCount && + deadlineReached == other.deadlineReached && + duplicateCount == other.duplicateCount && + failedStrategyCount == other.failedStrategyCount && + malformedCount == other.malformedCount && + pagesFetched == other.pagesFetched && + responseTruncated == other.responseTruncated && + resultLimitReached == other.resultLimitReached && + returnedUsers == other.returnedUsers && + stalledStrategyCount == other.stalledStrategyCount && + strategies == other.strategies && + strategyCount == other.strategyCount && + uniqueUsers == other.uniqueUsers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + complete, + cursorFailureCount, + deadlineReached, + duplicateCount, + failedStrategyCount, + malformedCount, + pagesFetched, + responseTruncated, + resultLimitReached, + returnedUsers, + stalledStrategyCount, + strategies, + strategyCount, + uniqueUsers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Diagnostic{complete=$complete, cursorFailureCount=$cursorFailureCount, deadlineReached=$deadlineReached, duplicateCount=$duplicateCount, failedStrategyCount=$failedStrategyCount, malformedCount=$malformedCount, pagesFetched=$pagesFetched, responseTruncated=$responseTruncated, resultLimitReached=$resultLimitReached, returnedUsers=$returnedUsers, stalledStrategyCount=$stalledStrategyCount, strategies=$strategies, strategyCount=$strategyCount, uniqueUsers=$uniqueUsers, additionalProperties=$additionalProperties}" + } + + class HasNextPage @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val FALSE = of(false) + + @JvmStatic fun of(value: Boolean) = HasNextPage(JsonField.of(value)) + } + + /** An enum containing [HasNextPage]'s known values. */ + enum class Known { + FALSE + } + + /** + * An enum containing [HasNextPage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [HasNextPage] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FALSE, + /** + * An enum member indicating that [HasNextPage] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + FALSE -> Value.FALSE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + FALSE -> Known.FALSE + else -> throw XTwitterScraperInvalidDataException("Unknown HasNextPage: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asBoolean(): Boolean = + _value().asBoolean().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a Boolean") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): HasNextPage = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is HasNextPage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class NextCursor @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val EMPTY = of("") + + @JvmStatic fun of(value: String) = NextCursor(JsonField.of(value)) + } + + /** An enum containing [NextCursor]'s known values. */ + enum class Known { + EMPTY + } + + /** + * An enum containing [NextCursor]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [NextCursor] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + EMPTY, + /** + * An enum member indicating that [NextCursor] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + EMPTY -> Value.EMPTY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + EMPTY -> Known.EMPTY + else -> throw XTwitterScraperInvalidDataException("Unknown NextCursor: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): NextCursor = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NextCursor && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UserListCoverageResponse && + hasNextPage == other.hasNextPage && + nextCursor == other.nextCursor && + users == other.users && + diagnostic == other.diagnostic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(hasNextPage, nextCursor, users, diagnostic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UserListCoverageResponse{hasNextPage=$hasNextPage, nextCursor=$nextCursor, users=$users, diagnostic=$diagnostic, additionalProperties=$additionalProperties}" + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsync.kt index 69f112c9..86890c09 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsync.kt @@ -7,6 +7,7 @@ package com.x_twitter_scraper.api.services.async import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.services.async.support.AttachmentServiceAsync import com.x_twitter_scraper.api.services.async.support.TicketServiceAsync import java.util.function.Consumer @@ -24,6 +25,9 @@ interface SupportServiceAsync { */ fun withOptions(modifier: Consumer): SupportServiceAsync + /** Support ticket management */ + fun attachments(): AttachmentServiceAsync + /** Support ticket management */ fun tickets(): TicketServiceAsync @@ -41,6 +45,9 @@ interface SupportServiceAsync { modifier: Consumer ): SupportServiceAsync.WithRawResponse + /** Support ticket management */ + fun attachments(): AttachmentServiceAsync.WithRawResponse + /** Support ticket management */ fun tickets(): TicketServiceAsync.WithRawResponse } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsyncImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsyncImpl.kt index bea51f0f..e55573ab 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsyncImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/SupportServiceAsyncImpl.kt @@ -7,6 +7,8 @@ package com.x_twitter_scraper.api.services.async import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.services.async.support.AttachmentServiceAsync +import com.x_twitter_scraper.api.services.async.support.AttachmentServiceAsyncImpl import com.x_twitter_scraper.api.services.async.support.TicketServiceAsync import com.x_twitter_scraper.api.services.async.support.TicketServiceAsyncImpl import java.util.function.Consumer @@ -18,6 +20,10 @@ class SupportServiceAsyncImpl internal constructor(private val clientOptions: Cl WithRawResponseImpl(clientOptions) } + private val attachments: AttachmentServiceAsync by lazy { + AttachmentServiceAsyncImpl(clientOptions) + } + private val tickets: TicketServiceAsync by lazy { TicketServiceAsyncImpl(clientOptions) } override fun withRawResponse(): SupportServiceAsync.WithRawResponse = withRawResponse @@ -25,12 +31,19 @@ class SupportServiceAsyncImpl internal constructor(private val clientOptions: Cl override fun withOptions(modifier: Consumer): SupportServiceAsync = SupportServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** Support ticket management */ + override fun attachments(): AttachmentServiceAsync = attachments + /** Support ticket management */ override fun tickets(): TicketServiceAsync = tickets class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : SupportServiceAsync.WithRawResponse { + private val attachments: AttachmentServiceAsync.WithRawResponse by lazy { + AttachmentServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + private val tickets: TicketServiceAsync.WithRawResponse by lazy { TicketServiceAsyncImpl.WithRawResponseImpl(clientOptions) } @@ -42,6 +55,9 @@ class SupportServiceAsyncImpl internal constructor(private val clientOptions: Cl clientOptions.toBuilder().apply(modifier::accept).build() ) + /** Support ticket management */ + override fun attachments(): AttachmentServiceAsync.WithRawResponse = attachments + /** Support ticket management */ override fun tickets(): TicketServiceAsync.WithRawResponse = tickets } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsync.kt new file mode 100644 index 00000000..a89bdc73 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsync.kt @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.async.support + +import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.core.RequestOptions +import com.x_twitter_scraper.api.core.http.HttpResponse +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Support ticket management */ +interface AttachmentServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): AttachmentServiceAsync + + /** + * Streams an authenticated user's support image or video. Video requests support one standard + * byte range for seeking and resumable playback. + */ + fun download(id: String): CompletableFuture = + download(id, AttachmentDownloadParams.none()) + + /** @see download */ + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = download(params.toBuilder().id(id).build(), requestOptions) + + /** @see download */ + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + ): CompletableFuture = download(id, params, RequestOptions.none()) + + /** @see download */ + fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see download */ + fun download(params: AttachmentDownloadParams): CompletableFuture = + download(params, RequestOptions.none()) + + /** @see download */ + fun download(id: String, requestOptions: RequestOptions): CompletableFuture = + download(id, AttachmentDownloadParams.none(), requestOptions) + + /** + * A view of [AttachmentServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): AttachmentServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /support/attachments/{id}`, but is otherwise the + * same as [AttachmentServiceAsync.download]. + */ + fun download(id: String): CompletableFuture = + download(id, AttachmentDownloadParams.none()) + + /** @see download */ + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + download(params.toBuilder().id(id).build(), requestOptions) + + /** @see download */ + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + ): CompletableFuture = download(id, params, RequestOptions.none()) + + /** @see download */ + fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see download */ + fun download(params: AttachmentDownloadParams): CompletableFuture = + download(params, RequestOptions.none()) + + /** @see download */ + fun download(id: String, requestOptions: RequestOptions): CompletableFuture = + download(id, AttachmentDownloadParams.none(), requestOptions) + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncImpl.kt new file mode 100644 index 00000000..b4679968 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncImpl.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.async.support + +import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.core.RequestOptions +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.handlers.errorBodyHandler +import com.x_twitter_scraper.api.core.handlers.errorHandler +import com.x_twitter_scraper.api.core.http.HttpMethod +import com.x_twitter_scraper.api.core.http.HttpRequest +import com.x_twitter_scraper.api.core.http.HttpResponse +import com.x_twitter_scraper.api.core.http.HttpResponse.Handler +import com.x_twitter_scraper.api.core.prepareAsync +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** Support ticket management */ +class AttachmentServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + AttachmentServiceAsync { + + private val withRawResponse: AttachmentServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): AttachmentServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): AttachmentServiceAsync = + AttachmentServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // get /support/attachments/{id} + withRawResponse().download(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AttachmentServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): AttachmentServiceAsync.WithRawResponse = + AttachmentServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + override fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions, + ): CompletableFuture { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("support", "attachments", params._pathParam(0)) + .putHeader("Accept", "application/octet-stream") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> errorHandler.handle(response) } + } + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt index aff0000c..9fb6136e 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt @@ -25,6 +25,7 @@ import com.x_twitter_scraper.api.models.x.tweets.TweetListParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveResponse import com.x_twitter_scraper.api.models.x.tweets.TweetSearchParams +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse import com.x_twitter_scraper.api.services.async.x.tweets.LikeServiceAsync import com.x_twitter_scraper.api.services.async.x.tweets.RetweetServiceAsync import java.util.concurrent.CompletableFuture @@ -199,10 +200,9 @@ interface TweetServiceAsync { getQuotes(id, TweetGetQuotesParams.none(), requestOptions) /** - * Returns direct replies. Complete mode merges available timeline views, supported rankings, - * every forward cursor module, labeled hidden-content branches, exact-parent time partitions - * scaled to the reported reply count, and search. It separates nested replies and returns 424 - * below 80% coverage. + * Returns direct replies. Omit mode for automatic maximum coverage with resumable pagination. + * Complete mode returns nested replies, diagnostics, and 424 when direct coverage stays below + * 80%. */ fun getReplies(id: String): CompletableFuture = getReplies(id, TweetGetRepliesParams.none()) @@ -305,15 +305,15 @@ interface TweetServiceAsync { fun getThread(id: String, requestOptions: RequestOptions): CompletableFuture = getThread(id, TweetGetThreadParams.none(), requestOptions) - /** Search tweets by query, Tweet ID, X status URL, or account date window */ - fun search(params: TweetSearchParams): CompletableFuture = + /** No-mode search maximizes coverage. */ + fun search(params: TweetSearchParams): CompletableFuture = search(params, RequestOptions.none()) /** @see search */ fun search( params: TweetSearchParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture + ): CompletableFuture /** A view of [TweetServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -641,13 +641,15 @@ interface TweetServiceAsync { * Returns a raw HTTP response for `get /x/tweets/search`, but is otherwise the same as * [TweetServiceAsync.search]. */ - fun search(params: TweetSearchParams): CompletableFuture> = + fun search( + params: TweetSearchParams + ): CompletableFuture> = search(params, RequestOptions.none()) /** @see search */ fun search( params: TweetSearchParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture> } } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncImpl.kt index f28b1378..9f9a74a8 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncImpl.kt @@ -36,6 +36,7 @@ import com.x_twitter_scraper.api.models.x.tweets.TweetListParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveResponse import com.x_twitter_scraper.api.models.x.tweets.TweetSearchParams +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse import com.x_twitter_scraper.api.services.async.x.tweets.LikeServiceAsync import com.x_twitter_scraper.api.services.async.x.tweets.LikeServiceAsyncImpl import com.x_twitter_scraper.api.services.async.x.tweets.RetweetServiceAsync @@ -132,7 +133,7 @@ class TweetServiceAsyncImpl internal constructor(private val clientOptions: Clie override fun search( params: TweetSearchParams, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = // get /x/tweets/search withRawResponse().search(params, requestOptions).thenApply { it.parse() } @@ -456,13 +457,13 @@ class TweetServiceAsyncImpl internal constructor(private val clientOptions: Clie } } - private val searchHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val searchHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun search( params: TweetSearchParams, requestOptions: RequestOptions, - ): CompletableFuture> { + ): CompletableFuture> { val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsync.kt index 03d940e2..8320d70d 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsync.kt @@ -17,8 +17,10 @@ import com.x_twitter_scraper.api.models.x.users.UserRemoveFollowerResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersYouKnowParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveLikesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMediaParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMentionsParams @@ -27,6 +29,7 @@ import com.x_twitter_scraper.api.models.x.users.UserRetrieveRepliesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveSearchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveTweetsParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersResponse import com.x_twitter_scraper.api.services.async.x.users.FollowServiceAsync import java.util.concurrent.CompletableFuture import java.util.function.Consumer @@ -117,7 +120,7 @@ interface UserServiceAsync { ): CompletableFuture /** List followers of a user */ - fun retrieveFollowers(id: String): CompletableFuture = + fun retrieveFollowers(id: String): CompletableFuture = retrieveFollowers(id, UserRetrieveFollowersParams.none()) /** @see retrieveFollowers */ @@ -125,30 +128,33 @@ interface UserServiceAsync { id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture = + ): CompletableFuture = retrieveFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowers */ fun retrieveFollowers( id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), - ): CompletableFuture = retrieveFollowers(id, params, RequestOptions.none()) + ): CompletableFuture = + retrieveFollowers(id, params, RequestOptions.none()) /** @see retrieveFollowers */ fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture + ): CompletableFuture /** @see retrieveFollowers */ - fun retrieveFollowers(params: UserRetrieveFollowersParams): CompletableFuture = + fun retrieveFollowers( + params: UserRetrieveFollowersParams + ): CompletableFuture = retrieveFollowers(params, RequestOptions.none()) /** @see retrieveFollowers */ fun retrieveFollowers( id: String, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = retrieveFollowers(id, UserRetrieveFollowersParams.none(), requestOptions) /** List mutual followers between you and a user */ @@ -189,7 +195,7 @@ interface UserServiceAsync { retrieveFollowersYouKnow(id, UserRetrieveFollowersYouKnowParams.none(), requestOptions) /** List accounts a user follows */ - fun retrieveFollowing(id: String): CompletableFuture = + fun retrieveFollowing(id: String): CompletableFuture = retrieveFollowing(id, UserRetrieveFollowingParams.none()) /** @see retrieveFollowing */ @@ -197,30 +203,33 @@ interface UserServiceAsync { id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture = + ): CompletableFuture = retrieveFollowing(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowing */ fun retrieveFollowing( id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), - ): CompletableFuture = retrieveFollowing(id, params, RequestOptions.none()) + ): CompletableFuture = + retrieveFollowing(id, params, RequestOptions.none()) /** @see retrieveFollowing */ fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture + ): CompletableFuture /** @see retrieveFollowing */ - fun retrieveFollowing(params: UserRetrieveFollowingParams): CompletableFuture = + fun retrieveFollowing( + params: UserRetrieveFollowingParams + ): CompletableFuture = retrieveFollowing(params, RequestOptions.none()) /** @see retrieveFollowing */ fun retrieveFollowing( id: String, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = retrieveFollowing(id, UserRetrieveFollowingParams.none(), requestOptions) /** List tweets liked by a user */ @@ -328,7 +337,10 @@ interface UserServiceAsync { ): CompletableFuture = retrieveMentions(id, UserRetrieveMentionsParams.none(), requestOptions) - /** Returns the user's timeline with replies included by default. */ + /** + * Returns target-authored posts and replies. Omit mode for automatic maximum coverage. Pass + * next_cursor unchanged. Unprefixed cursors stay legacy. Excludes other-author context. + */ fun retrieveReplies(id: String): CompletableFuture = retrieveReplies(id, UserRetrieveRepliesParams.none()) @@ -373,7 +385,10 @@ interface UserServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture - /** List recent tweets posted by a user */ + /** + * Omit mode for automatic maximum coverage. Pass next_cursor unchanged. Unprefixed cursors use + * legacy pagination. Shape and billing stay the same. + */ fun retrieveTweets(id: String): CompletableFuture = retrieveTweets(id, UserRetrieveTweetsParams.none()) @@ -409,7 +424,9 @@ interface UserServiceAsync { retrieveTweets(id, UserRetrieveTweetsParams.none(), requestOptions) /** List verified followers of a user */ - fun retrieveVerifiedFollowers(id: String): CompletableFuture = + fun retrieveVerifiedFollowers( + id: String + ): CompletableFuture = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none()) /** @see retrieveVerifiedFollowers */ @@ -417,32 +434,33 @@ interface UserServiceAsync { id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture = + ): CompletableFuture = retrieveVerifiedFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), - ): CompletableFuture = + ): CompletableFuture = retrieveVerifiedFollowers(id, params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture + ): CompletableFuture /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams - ): CompletableFuture = retrieveVerifiedFollowers(params, RequestOptions.none()) + ): CompletableFuture = + retrieveVerifiedFollowers(params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( id: String, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none(), requestOptions) /** A view of [UserServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -546,7 +564,9 @@ interface UserServiceAsync { * Returns a raw HTTP response for `get /x/users/{id}/followers`, but is otherwise the same * as [UserServiceAsync.retrieveFollowers]. */ - fun retrieveFollowers(id: String): CompletableFuture> = + fun retrieveFollowers( + id: String + ): CompletableFuture> = retrieveFollowers(id, UserRetrieveFollowersParams.none()) /** @see retrieveFollowers */ @@ -554,33 +574,33 @@ interface UserServiceAsync { id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowers */ fun retrieveFollowers( id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowers(id, params, RequestOptions.none()) /** @see retrieveFollowers */ fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture> /** @see retrieveFollowers */ fun retrieveFollowers( params: UserRetrieveFollowersParams - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowers(params, RequestOptions.none()) /** @see retrieveFollowers */ fun retrieveFollowers( id: String, requestOptions: RequestOptions, - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowers(id, UserRetrieveFollowersParams.none(), requestOptions) /** @@ -630,7 +650,9 @@ interface UserServiceAsync { * Returns a raw HTTP response for `get /x/users/{id}/following`, but is otherwise the same * as [UserServiceAsync.retrieveFollowing]. */ - fun retrieveFollowing(id: String): CompletableFuture> = + fun retrieveFollowing( + id: String + ): CompletableFuture> = retrieveFollowing(id, UserRetrieveFollowingParams.none()) /** @see retrieveFollowing */ @@ -638,33 +660,33 @@ interface UserServiceAsync { id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowing(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowing */ fun retrieveFollowing( id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowing(id, params, RequestOptions.none()) /** @see retrieveFollowing */ fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture> /** @see retrieveFollowing */ fun retrieveFollowing( params: UserRetrieveFollowingParams - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowing(params, RequestOptions.none()) /** @see retrieveFollowing */ fun retrieveFollowing( id: String, requestOptions: RequestOptions, - ): CompletableFuture> = + ): CompletableFuture> = retrieveFollowing(id, UserRetrieveFollowingParams.none(), requestOptions) /** @@ -893,7 +915,7 @@ interface UserServiceAsync { */ fun retrieveVerifiedFollowers( id: String - ): CompletableFuture> = + ): CompletableFuture> = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none()) /** @see retrieveVerifiedFollowers */ @@ -902,7 +924,7 @@ interface UserServiceAsync { params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveVerifiedFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveVerifiedFollowers */ @@ -910,26 +932,26 @@ interface UserServiceAsync { id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), - ): CompletableFuture> = + ): CompletableFuture> = retrieveVerifiedFollowers(id, params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture> /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams - ): CompletableFuture> = + ): CompletableFuture> = retrieveVerifiedFollowers(params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( id: String, requestOptions: RequestOptions, - ): CompletableFuture> = + ): CompletableFuture> = retrieveVerifiedFollowers( id, UserRetrieveVerifiedFollowersParams.none(), diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncImpl.kt index e2064809..aeaf2942 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncImpl.kt @@ -28,8 +28,10 @@ import com.x_twitter_scraper.api.models.x.users.UserRemoveFollowerResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersYouKnowParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveLikesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMediaParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMentionsParams @@ -38,6 +40,7 @@ import com.x_twitter_scraper.api.models.x.users.UserRetrieveRepliesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveSearchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveTweetsParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersResponse import com.x_twitter_scraper.api.services.async.x.users.FollowServiceAsync import com.x_twitter_scraper.api.services.async.x.users.FollowServiceAsyncImpl import java.util.concurrent.CompletableFuture @@ -85,7 +88,7 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien override fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = // get /x/users/{id}/followers withRawResponse().retrieveFollowers(params, requestOptions).thenApply { it.parse() } @@ -99,7 +102,7 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien override fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = // get /x/users/{id}/following withRawResponse().retrieveFollowing(params, requestOptions).thenApply { it.parse() } @@ -148,7 +151,7 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien override fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions, - ): CompletableFuture = + ): CompletableFuture = // get /x/users/{id}/verified-followers withRawResponse().retrieveVerifiedFollowers(params, requestOptions).thenApply { it.parse() } @@ -269,13 +272,13 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien } } - private val retrieveFollowersHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveFollowersHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions, - ): CompletableFuture> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) @@ -335,13 +338,13 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien } } - private val retrieveFollowingHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveFollowingHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions, - ): CompletableFuture> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) @@ -563,13 +566,14 @@ class UserServiceAsyncImpl internal constructor(private val clientOptions: Clien } } - private val retrieveVerifiedFollowersHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveVerifiedFollowersHandler: + Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions, - ): CompletableFuture> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportService.kt index 9fbab4e9..23f29c7e 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportService.kt @@ -7,6 +7,7 @@ package com.x_twitter_scraper.api.services.blocking import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.services.blocking.support.AttachmentService import com.x_twitter_scraper.api.services.blocking.support.TicketService import java.util.function.Consumer @@ -24,6 +25,9 @@ interface SupportService { */ fun withOptions(modifier: Consumer): SupportService + /** Support ticket management */ + fun attachments(): AttachmentService + /** Support ticket management */ fun tickets(): TicketService @@ -37,6 +41,9 @@ interface SupportService { */ fun withOptions(modifier: Consumer): SupportService.WithRawResponse + /** Support ticket management */ + fun attachments(): AttachmentService.WithRawResponse + /** Support ticket management */ fun tickets(): TicketService.WithRawResponse } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportServiceImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportServiceImpl.kt index 1de102db..7ebc73a4 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportServiceImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/SupportServiceImpl.kt @@ -7,6 +7,8 @@ package com.x_twitter_scraper.api.services.blocking import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.services.blocking.support.AttachmentService +import com.x_twitter_scraper.api.services.blocking.support.AttachmentServiceImpl import com.x_twitter_scraper.api.services.blocking.support.TicketService import com.x_twitter_scraper.api.services.blocking.support.TicketServiceImpl import java.util.function.Consumer @@ -18,6 +20,8 @@ class SupportServiceImpl internal constructor(private val clientOptions: ClientO WithRawResponseImpl(clientOptions) } + private val attachments: AttachmentService by lazy { AttachmentServiceImpl(clientOptions) } + private val tickets: TicketService by lazy { TicketServiceImpl(clientOptions) } override fun withRawResponse(): SupportService.WithRawResponse = withRawResponse @@ -25,12 +29,19 @@ class SupportServiceImpl internal constructor(private val clientOptions: ClientO override fun withOptions(modifier: Consumer): SupportService = SupportServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** Support ticket management */ + override fun attachments(): AttachmentService = attachments + /** Support ticket management */ override fun tickets(): TicketService = tickets class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : SupportService.WithRawResponse { + private val attachments: AttachmentService.WithRawResponse by lazy { + AttachmentServiceImpl.WithRawResponseImpl(clientOptions) + } + private val tickets: TicketService.WithRawResponse by lazy { TicketServiceImpl.WithRawResponseImpl(clientOptions) } @@ -42,6 +53,9 @@ class SupportServiceImpl internal constructor(private val clientOptions: ClientO clientOptions.toBuilder().apply(modifier::accept).build() ) + /** Support ticket management */ + override fun attachments(): AttachmentService.WithRawResponse = attachments + /** Support ticket management */ override fun tickets(): TicketService.WithRawResponse = tickets } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentService.kt new file mode 100644 index 00000000..a34974f0 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentService.kt @@ -0,0 +1,117 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.blocking.support + +import com.google.errorprone.annotations.MustBeClosed +import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.core.RequestOptions +import com.x_twitter_scraper.api.core.http.HttpResponse +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import java.util.function.Consumer + +/** Support ticket management */ +interface AttachmentService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): AttachmentService + + /** + * Streams an authenticated user's support image or video. Video requests support one standard + * byte range for seeking and resumable playback. + */ + @MustBeClosed + fun download(id: String): HttpResponse = download(id, AttachmentDownloadParams.none()) + + /** @see download */ + @MustBeClosed + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = download(params.toBuilder().id(id).build(), requestOptions) + + /** @see download */ + @MustBeClosed + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + ): HttpResponse = download(id, params, RequestOptions.none()) + + /** @see download */ + @MustBeClosed + fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** @see download */ + @MustBeClosed + fun download(params: AttachmentDownloadParams): HttpResponse = + download(params, RequestOptions.none()) + + /** @see download */ + @MustBeClosed + fun download(id: String, requestOptions: RequestOptions): HttpResponse = + download(id, AttachmentDownloadParams.none(), requestOptions) + + /** A view of [AttachmentService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): AttachmentService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /support/attachments/{id}`, but is otherwise the + * same as [AttachmentService.download]. + */ + @MustBeClosed + fun download(id: String): HttpResponse = download(id, AttachmentDownloadParams.none()) + + /** @see download */ + @MustBeClosed + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = download(params.toBuilder().id(id).build(), requestOptions) + + /** @see download */ + @MustBeClosed + fun download( + id: String, + params: AttachmentDownloadParams = AttachmentDownloadParams.none(), + ): HttpResponse = download(id, params, RequestOptions.none()) + + /** @see download */ + @MustBeClosed + fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** @see download */ + @MustBeClosed + fun download(params: AttachmentDownloadParams): HttpResponse = + download(params, RequestOptions.none()) + + /** @see download */ + @MustBeClosed + fun download(id: String, requestOptions: RequestOptions): HttpResponse = + download(id, AttachmentDownloadParams.none(), requestOptions) + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceImpl.kt new file mode 100644 index 00000000..443fbf18 --- /dev/null +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceImpl.kt @@ -0,0 +1,72 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.blocking.support + +import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.core.RequestOptions +import com.x_twitter_scraper.api.core.checkRequired +import com.x_twitter_scraper.api.core.handlers.errorBodyHandler +import com.x_twitter_scraper.api.core.handlers.errorHandler +import com.x_twitter_scraper.api.core.http.HttpMethod +import com.x_twitter_scraper.api.core.http.HttpRequest +import com.x_twitter_scraper.api.core.http.HttpResponse +import com.x_twitter_scraper.api.core.http.HttpResponse.Handler +import com.x_twitter_scraper.api.core.prepare +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** Support ticket management */ +class AttachmentServiceImpl internal constructor(private val clientOptions: ClientOptions) : + AttachmentService { + + private val withRawResponse: AttachmentService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): AttachmentService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): AttachmentService = + AttachmentServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions, + ): HttpResponse = + // get /support/attachments/{id} + withRawResponse().download(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AttachmentService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): AttachmentService.WithRawResponse = + AttachmentServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + override fun download( + params: AttachmentDownloadParams, + requestOptions: RequestOptions, + ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("support", "attachments", params._pathParam(0)) + .putHeader("Accept", "application/octet-stream") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response) + } + } +} diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt index 50a7dbc5..f80c0f0e 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt @@ -26,6 +26,7 @@ import com.x_twitter_scraper.api.models.x.tweets.TweetListParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveResponse import com.x_twitter_scraper.api.models.x.tweets.TweetSearchParams +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse import com.x_twitter_scraper.api.services.blocking.x.tweets.LikeService import com.x_twitter_scraper.api.services.blocking.x.tweets.RetweetService import java.util.function.Consumer @@ -186,10 +187,9 @@ interface TweetService { getQuotes(id, TweetGetQuotesParams.none(), requestOptions) /** - * Returns direct replies. Complete mode merges available timeline views, supported rankings, - * every forward cursor module, labeled hidden-content branches, exact-parent time partitions - * scaled to the reported reply count, and search. It separates nested replies and returns 424 - * below 80% coverage. + * Returns direct replies. Omit mode for automatic maximum coverage with resumable pagination. + * Complete mode returns nested replies, diagnostics, and 424 when direct coverage stays below + * 80%. */ fun getReplies(id: String): TweetGetRepliesResponse = getReplies(id, TweetGetRepliesParams.none()) @@ -282,14 +282,15 @@ interface TweetService { fun getThread(id: String, requestOptions: RequestOptions): PaginatedTweets = getThread(id, TweetGetThreadParams.none(), requestOptions) - /** Search tweets by query, Tweet ID, X status URL, or account date window */ - fun search(params: TweetSearchParams): PaginatedTweets = search(params, RequestOptions.none()) + /** No-mode search maximizes coverage. */ + fun search(params: TweetSearchParams): TweetSearchResponse = + search(params, RequestOptions.none()) /** @see search */ fun search( params: TweetSearchParams, requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedTweets + ): TweetSearchResponse /** A view of [TweetService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -635,7 +636,7 @@ interface TweetService { * [TweetService.search]. */ @MustBeClosed - fun search(params: TweetSearchParams): HttpResponseFor = + fun search(params: TweetSearchParams): HttpResponseFor = search(params, RequestOptions.none()) /** @see search */ @@ -643,6 +644,6 @@ interface TweetService { fun search( params: TweetSearchParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor + ): HttpResponseFor } } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceImpl.kt index 05394bb1..0329d695 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceImpl.kt @@ -36,6 +36,7 @@ import com.x_twitter_scraper.api.models.x.tweets.TweetListParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveParams import com.x_twitter_scraper.api.models.x.tweets.TweetRetrieveResponse import com.x_twitter_scraper.api.models.x.tweets.TweetSearchParams +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse import com.x_twitter_scraper.api.services.blocking.x.tweets.LikeService import com.x_twitter_scraper.api.services.blocking.x.tweets.LikeServiceImpl import com.x_twitter_scraper.api.services.blocking.x.tweets.RetweetService @@ -128,7 +129,7 @@ class TweetServiceImpl internal constructor(private val clientOptions: ClientOpt override fun search( params: TweetSearchParams, requestOptions: RequestOptions, - ): PaginatedTweets = + ): TweetSearchResponse = // get /x/tweets/search withRawResponse().search(params, requestOptions).parse() @@ -425,13 +426,13 @@ class TweetServiceImpl internal constructor(private val clientOptions: ClientOpt } } - private val searchHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val searchHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun search( params: TweetSearchParams, requestOptions: RequestOptions, - ): HttpResponseFor { + ): HttpResponseFor { val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserService.kt index 3da59dbe..c7972ec6 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserService.kt @@ -18,8 +18,10 @@ import com.x_twitter_scraper.api.models.x.users.UserRemoveFollowerResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersYouKnowParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveLikesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMediaParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMentionsParams @@ -28,6 +30,7 @@ import com.x_twitter_scraper.api.models.x.users.UserRetrieveRepliesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveSearchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveTweetsParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersResponse import com.x_twitter_scraper.api.services.blocking.x.users.FollowService import java.util.function.Consumer @@ -108,7 +111,7 @@ interface UserService { ): UserRetrieveBatchResponse /** List followers of a user */ - fun retrieveFollowers(id: String): PaginatedUsers = + fun retrieveFollowers(id: String): UserRetrieveFollowersResponse = retrieveFollowers(id, UserRetrieveFollowersParams.none()) /** @see retrieveFollowers */ @@ -116,26 +119,30 @@ interface UserService { id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers = retrieveFollowers(params.toBuilder().id(id).build(), requestOptions) + ): UserRetrieveFollowersResponse = + retrieveFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowers */ fun retrieveFollowers( id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), - ): PaginatedUsers = retrieveFollowers(id, params, RequestOptions.none()) + ): UserRetrieveFollowersResponse = retrieveFollowers(id, params, RequestOptions.none()) /** @see retrieveFollowers */ fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers + ): UserRetrieveFollowersResponse /** @see retrieveFollowers */ - fun retrieveFollowers(params: UserRetrieveFollowersParams): PaginatedUsers = + fun retrieveFollowers(params: UserRetrieveFollowersParams): UserRetrieveFollowersResponse = retrieveFollowers(params, RequestOptions.none()) /** @see retrieveFollowers */ - fun retrieveFollowers(id: String, requestOptions: RequestOptions): PaginatedUsers = + fun retrieveFollowers( + id: String, + requestOptions: RequestOptions, + ): UserRetrieveFollowersResponse = retrieveFollowers(id, UserRetrieveFollowersParams.none(), requestOptions) /** List mutual followers between you and a user */ @@ -170,7 +177,7 @@ interface UserService { retrieveFollowersYouKnow(id, UserRetrieveFollowersYouKnowParams.none(), requestOptions) /** List accounts a user follows */ - fun retrieveFollowing(id: String): PaginatedUsers = + fun retrieveFollowing(id: String): UserRetrieveFollowingResponse = retrieveFollowing(id, UserRetrieveFollowingParams.none()) /** @see retrieveFollowing */ @@ -178,26 +185,30 @@ interface UserService { id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers = retrieveFollowing(params.toBuilder().id(id).build(), requestOptions) + ): UserRetrieveFollowingResponse = + retrieveFollowing(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowing */ fun retrieveFollowing( id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), - ): PaginatedUsers = retrieveFollowing(id, params, RequestOptions.none()) + ): UserRetrieveFollowingResponse = retrieveFollowing(id, params, RequestOptions.none()) /** @see retrieveFollowing */ fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers + ): UserRetrieveFollowingResponse /** @see retrieveFollowing */ - fun retrieveFollowing(params: UserRetrieveFollowingParams): PaginatedUsers = + fun retrieveFollowing(params: UserRetrieveFollowingParams): UserRetrieveFollowingResponse = retrieveFollowing(params, RequestOptions.none()) /** @see retrieveFollowing */ - fun retrieveFollowing(id: String, requestOptions: RequestOptions): PaginatedUsers = + fun retrieveFollowing( + id: String, + requestOptions: RequestOptions, + ): UserRetrieveFollowingResponse = retrieveFollowing(id, UserRetrieveFollowingParams.none(), requestOptions) /** List tweets liked by a user */ @@ -293,7 +304,10 @@ interface UserService { fun retrieveMentions(id: String, requestOptions: RequestOptions): PaginatedTweets = retrieveMentions(id, UserRetrieveMentionsParams.none(), requestOptions) - /** Returns the user's timeline with replies included by default. */ + /** + * Returns target-authored posts and replies. Omit mode for automatic maximum coverage. Pass + * next_cursor unchanged. Unprefixed cursors stay legacy. Excludes other-author context. + */ fun retrieveReplies(id: String): PaginatedTweets = retrieveReplies(id, UserRetrieveRepliesParams.none()) @@ -334,7 +348,10 @@ interface UserService { requestOptions: RequestOptions = RequestOptions.none(), ): PaginatedUsers - /** List recent tweets posted by a user */ + /** + * Omit mode for automatic maximum coverage. Pass next_cursor unchanged. Unprefixed cursors use + * legacy pagination. Shape and billing stay the same. + */ fun retrieveTweets(id: String): PaginatedTweets = retrieveTweets(id, UserRetrieveTweetsParams.none()) @@ -366,7 +383,7 @@ interface UserService { retrieveTweets(id, UserRetrieveTweetsParams.none(), requestOptions) /** List verified followers of a user */ - fun retrieveVerifiedFollowers(id: String): PaginatedUsers = + fun retrieveVerifiedFollowers(id: String): UserRetrieveVerifiedFollowersResponse = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none()) /** @see retrieveVerifiedFollowers */ @@ -374,26 +391,33 @@ interface UserService { id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers = retrieveVerifiedFollowers(params.toBuilder().id(id).build(), requestOptions) + ): UserRetrieveVerifiedFollowersResponse = + retrieveVerifiedFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), - ): PaginatedUsers = retrieveVerifiedFollowers(id, params, RequestOptions.none()) + ): UserRetrieveVerifiedFollowersResponse = + retrieveVerifiedFollowers(id, params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): PaginatedUsers + ): UserRetrieveVerifiedFollowersResponse /** @see retrieveVerifiedFollowers */ - fun retrieveVerifiedFollowers(params: UserRetrieveVerifiedFollowersParams): PaginatedUsers = + fun retrieveVerifiedFollowers( + params: UserRetrieveVerifiedFollowersParams + ): UserRetrieveVerifiedFollowersResponse = retrieveVerifiedFollowers(params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ - fun retrieveVerifiedFollowers(id: String, requestOptions: RequestOptions): PaginatedUsers = + fun retrieveVerifiedFollowers( + id: String, + requestOptions: RequestOptions, + ): UserRetrieveVerifiedFollowersResponse = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none(), requestOptions) /** A view of [UserService] that provides access to raw HTTP responses for each method. */ @@ -505,7 +529,7 @@ interface UserService { * as [UserService.retrieveFollowers]. */ @MustBeClosed - fun retrieveFollowers(id: String): HttpResponseFor = + fun retrieveFollowers(id: String): HttpResponseFor = retrieveFollowers(id, UserRetrieveFollowersParams.none()) /** @see retrieveFollowers */ @@ -514,7 +538,7 @@ interface UserService { id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor = + ): HttpResponseFor = retrieveFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowers */ @@ -522,27 +546,29 @@ interface UserService { fun retrieveFollowers( id: String, params: UserRetrieveFollowersParams = UserRetrieveFollowersParams.none(), - ): HttpResponseFor = retrieveFollowers(id, params, RequestOptions.none()) + ): HttpResponseFor = + retrieveFollowers(id, params, RequestOptions.none()) /** @see retrieveFollowers */ @MustBeClosed fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor + ): HttpResponseFor /** @see retrieveFollowers */ @MustBeClosed fun retrieveFollowers( params: UserRetrieveFollowersParams - ): HttpResponseFor = retrieveFollowers(params, RequestOptions.none()) + ): HttpResponseFor = + retrieveFollowers(params, RequestOptions.none()) /** @see retrieveFollowers */ @MustBeClosed fun retrieveFollowers( id: String, requestOptions: RequestOptions, - ): HttpResponseFor = + ): HttpResponseFor = retrieveFollowers(id, UserRetrieveFollowersParams.none(), requestOptions) /** @@ -596,7 +622,7 @@ interface UserService { * as [UserService.retrieveFollowing]. */ @MustBeClosed - fun retrieveFollowing(id: String): HttpResponseFor = + fun retrieveFollowing(id: String): HttpResponseFor = retrieveFollowing(id, UserRetrieveFollowingParams.none()) /** @see retrieveFollowing */ @@ -605,7 +631,7 @@ interface UserService { id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor = + ): HttpResponseFor = retrieveFollowing(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveFollowing */ @@ -613,27 +639,29 @@ interface UserService { fun retrieveFollowing( id: String, params: UserRetrieveFollowingParams = UserRetrieveFollowingParams.none(), - ): HttpResponseFor = retrieveFollowing(id, params, RequestOptions.none()) + ): HttpResponseFor = + retrieveFollowing(id, params, RequestOptions.none()) /** @see retrieveFollowing */ @MustBeClosed fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor + ): HttpResponseFor /** @see retrieveFollowing */ @MustBeClosed fun retrieveFollowing( params: UserRetrieveFollowingParams - ): HttpResponseFor = retrieveFollowing(params, RequestOptions.none()) + ): HttpResponseFor = + retrieveFollowing(params, RequestOptions.none()) /** @see retrieveFollowing */ @MustBeClosed fun retrieveFollowing( id: String, requestOptions: RequestOptions, - ): HttpResponseFor = + ): HttpResponseFor = retrieveFollowing(id, UserRetrieveFollowingParams.none(), requestOptions) /** @@ -876,7 +904,9 @@ interface UserService { * the same as [UserService.retrieveVerifiedFollowers]. */ @MustBeClosed - fun retrieveVerifiedFollowers(id: String): HttpResponseFor = + fun retrieveVerifiedFollowers( + id: String + ): HttpResponseFor = retrieveVerifiedFollowers(id, UserRetrieveVerifiedFollowersParams.none()) /** @see retrieveVerifiedFollowers */ @@ -886,7 +916,7 @@ interface UserService { params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor = + ): HttpResponseFor = retrieveVerifiedFollowers(params.toBuilder().id(id).build(), requestOptions) /** @see retrieveVerifiedFollowers */ @@ -895,7 +925,7 @@ interface UserService { id: String, params: UserRetrieveVerifiedFollowersParams = UserRetrieveVerifiedFollowersParams.none(), - ): HttpResponseFor = + ): HttpResponseFor = retrieveVerifiedFollowers(id, params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ @@ -903,13 +933,13 @@ interface UserService { fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor + ): HttpResponseFor /** @see retrieveVerifiedFollowers */ @MustBeClosed fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams - ): HttpResponseFor = + ): HttpResponseFor = retrieveVerifiedFollowers(params, RequestOptions.none()) /** @see retrieveVerifiedFollowers */ @@ -917,7 +947,7 @@ interface UserService { fun retrieveVerifiedFollowers( id: String, requestOptions: RequestOptions, - ): HttpResponseFor = + ): HttpResponseFor = retrieveVerifiedFollowers( id, UserRetrieveVerifiedFollowersParams.none(), diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceImpl.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceImpl.kt index 57f1e5e3..3a468cb3 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceImpl.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceImpl.kt @@ -28,8 +28,10 @@ import com.x_twitter_scraper.api.models.x.users.UserRemoveFollowerResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveBatchResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowersYouKnowParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveFollowingResponse import com.x_twitter_scraper.api.models.x.users.UserRetrieveLikesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMediaParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveMentionsParams @@ -38,6 +40,7 @@ import com.x_twitter_scraper.api.models.x.users.UserRetrieveRepliesParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveSearchParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveTweetsParams import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersParams +import com.x_twitter_scraper.api.models.x.users.UserRetrieveVerifiedFollowersResponse import com.x_twitter_scraper.api.services.blocking.x.users.FollowService import com.x_twitter_scraper.api.services.blocking.x.users.FollowServiceImpl import java.util.function.Consumer @@ -80,7 +83,7 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti override fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions, - ): PaginatedUsers = + ): UserRetrieveFollowersResponse = // get /x/users/{id}/followers withRawResponse().retrieveFollowers(params, requestOptions).parse() @@ -94,7 +97,7 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti override fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions, - ): PaginatedUsers = + ): UserRetrieveFollowingResponse = // get /x/users/{id}/following withRawResponse().retrieveFollowing(params, requestOptions).parse() @@ -143,7 +146,7 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti override fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions, - ): PaginatedUsers = + ): UserRetrieveVerifiedFollowersResponse = // get /x/users/{id}/verified-followers withRawResponse().retrieveVerifiedFollowers(params, requestOptions).parse() @@ -255,13 +258,13 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti } } - private val retrieveFollowersHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveFollowersHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveFollowers( params: UserRetrieveFollowersParams, requestOptions: RequestOptions, - ): HttpResponseFor { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) @@ -315,13 +318,13 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti } } - private val retrieveFollowingHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveFollowingHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveFollowing( params: UserRetrieveFollowingParams, requestOptions: RequestOptions, - ): HttpResponseFor { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) @@ -522,13 +525,14 @@ class UserServiceImpl internal constructor(private val clientOptions: ClientOpti } } - private val retrieveVerifiedFollowersHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val retrieveVerifiedFollowersHandler: + Handler = + jsonHandler(clientOptions.jsonMapper) override fun retrieveVerifiedFollowers( params: UserRetrieveVerifiedFollowersParams, requestOptions: RequestOptions, - ): HttpResponseFor { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. checkRequired("id", params.id().getOrNull()) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/GeneratedModelContractTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/GeneratedModelContractTest.kt index 82734bb1..5a5964d0 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/GeneratedModelContractTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/GeneratedModelContractTest.kt @@ -189,7 +189,7 @@ internal class GeneratedModelContractTest { assertThat(validityOf(knownValue)).isEqualTo(1) assertThat(reconstructed).isEqualTo(knownValue) assertThat(reconstructed.hashCode()).isEqualTo(knownValue.hashCode()) - assertThat(knownValue.toString()).isNotBlank() + assertThat(knownValue.toString()).isEqualTo(wireValue.toString()) } if (knownValues.size > 1) { @@ -231,6 +231,13 @@ internal class GeneratedModelContractTest { ): Any = when (wireType) { String::class.java -> UNKNOWN_ENUM_VALUE + Boolean::class.javaPrimitiveType, + Boolean::class.javaObjectType -> + listOf(false, true).firstOrNull { candidate -> + knownValues.none { + (rawValue.invoke(it) as JsonField<*>).asBoolean().orElse(null) == candidate + } + } ?: error("Boolean enum has no unknown wire value") Long::class.javaPrimitiveType, Long::class.javaObjectType -> knownValues @@ -532,8 +539,32 @@ internal class GeneratedModelContractTest { if (hasBuilder(modelClass)) { populatedModel(modelClass, ancestors) } else { - defaultModelValue(modelClass) + factoryModelValue(modelClass, ancestors) ?: defaultModelValue(modelClass) + } + + private fun factoryModelValue(modelClass: Class<*>, ancestors: Set>): Any? { + val factories = + modelClass.methods + .filter { method -> + method.name.startsWith("of") && + method.parameterCount == 1 && + modelClass.isAssignableFrom(method.returnType) + } + .sortedBy { method -> method.name } + val factoryAncestors = ancestors + modelClass + + return factories.firstNotNullOfOrNull { factory -> + if (referencesAncestor(factory.genericParameterTypes.single(), factoryAncestors)) { + return@firstNotNullOfOrNull null + } + + val argument = + sampleValue(factory.genericParameterTypes.single()) { nestedModel -> + validModelValue(nestedModel, factoryAncestors) + } ?: return@firstNotNullOfOrNull null + runCatching { factory.invoke(null, argument) }.getOrNull() } + } private fun defaultModelValue(modelClass: Class<*>): Any? { val deserialized = runCatching { MAPPER.readValue("{}", modelClass) }.getOrNull() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/events/EventListParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/events/EventListParamsTest.kt index 3a9cc9a1..bb76ca96 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/events/EventListParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/events/EventListParamsTest.kt @@ -18,6 +18,7 @@ internal class EventListParamsTest { EventListParams.builder() .cursor("cursor") .eventType(EventType.TWEET_NEW) + .keywordMonitorId("keywordMonitorId") .limit(1L) .monitorId("monitorId") .build() @@ -29,6 +30,7 @@ internal class EventListParamsTest { EventListParams.builder() .cursor("cursor") .eventType(EventType.TWEET_NEW) + .keywordMonitorId("keywordMonitorId") .limit(1L) .monitorId("monitorId") .build() @@ -40,6 +42,7 @@ internal class EventListParamsTest { QueryParams.builder() .put("cursor", "cursor") .put("eventType", "tweet.new") + .put("keywordMonitorId", "keywordMonitorId") .put("limit", "1") .put("monitorId", "monitorId") .build() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParamsTest.kt index 3d0c5a9a..98d9df1b 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionEstimateCostParamsTest.kt @@ -7,6 +7,8 @@ package com.x_twitter_scraper.api.models.extractions import java.time.LocalDate +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,42 +20,106 @@ internal class ExtractionEstimateCostParamsTest { .toolType(ExtractionEstimateCostParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionEstimateCostParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionEstimateCostParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionEstimateCostParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionEstimateCostParams.QueryType.LATEST) .quotes(ExtractionEstimateCostParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionEstimateCostParams.RelationTarget.builder() + .relation( + ExtractionEstimateCostParams.RelationTarget.Relation.COMMUNITY_MEMBERS + ) + .value("x") + .build() + ) .replies(ExtractionEstimateCostParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionEstimateCostParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionEstimateCostParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionEstimateCostParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() } @@ -64,42 +130,106 @@ internal class ExtractionEstimateCostParamsTest { .toolType(ExtractionEstimateCostParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionEstimateCostParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionEstimateCostParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionEstimateCostParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionEstimateCostParams.QueryType.LATEST) .quotes(ExtractionEstimateCostParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionEstimateCostParams.RelationTarget.builder() + .relation( + ExtractionEstimateCostParams.RelationTarget.Relation.COMMUNITY_MEMBERS + ) + .value("x") + .build() + ) .replies(ExtractionEstimateCostParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionEstimateCostParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionEstimateCostParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionEstimateCostParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() val body = params._body() @@ -108,42 +238,119 @@ internal class ExtractionEstimateCostParamsTest { .isEqualTo(ExtractionEstimateCostParams.ToolType.FOLLOWER_EXPLORER) assertThat(body.advancedQuery()).contains("min_faves:100") assertThat(body.anyWords()).contains("ChatGPT AI model") + assertThat(body.bioContains()).contains("bioContains") + assertThat(body.blueVerifiedOnly()).contains(true) assertThat(body.boundingBox()).contains("-74.1 40.6 -73.9 40.8") + assertThat(body.cardName()).contains("cardName") assertThat(body.cashtags()).contains("\$TSLA \$NVDA") + assertThat(body.collectionStrategy()) + .contains(ExtractionEstimateCostParams.CollectionStrategy.AUTO) assertThat(body.conversationId()).contains("1234567890") + assertThat(body.dedupeAcrossTargets()).contains(true) + assertThat(body.dedupeMode()).contains(ExtractionEstimateCostParams.DedupeMode.NONE) assertThat(body.exactPhrase()).contains("artificial intelligence") + assertThat(body.excludeOriginalAuthor()).contains(true) + assertThat(body.excludeSource()).contains("excludeSource") assertThat(body.excludeWords()).contains("spam") assertThat(body.fromUser()).contains("nasa") + assertThat(body.geocode()).contains("geocode") assertThat(body.hashtags()).contains("#AI startups") + assertThat(body.hasLocation()).contains(true) + assertThat(body.hasMediaOnly()).contains(true) + assertThat(body.hasWebsite()).contains(true) + assertThat(body.includeOriginalPost()).contains(true) + assertThat(body.includeSearchTerms()).contains(true) + assertThat(body.includeTargetMetadata()).contains(true) assertThat(body.inReplyToTweetId()).contains("1234567890") assertThat(body.language()).contains("en") assertThat(body.listId()).contains("1234567890") + assertThat(body.locationContains()).contains("locationContains") + assertThat(body.maxDepth()).contains(1L) + assertThat(body.maxFollowers()).contains(0L) + assertThat(body.maxFollowing()).contains(0L) + assertThat(body.maxId()).contains("maxId") + assertThat(body.maxItemsPerTarget()).contains(1L) + assertThat(body.maxLikes()).contains(0L) + assertThat(body.maxPagesPerTarget()).contains(1L) + assertThat(body.maxPosts()).contains(0L) + assertThat(body.maxQuotes()).contains(0L) + assertThat(body.maxReplies()).contains(0L) + assertThat(body.maxRetweets()).contains(0L) assertThat(body.mediaType()).contains(ExtractionEstimateCostParams.MediaType.IMAGES) assertThat(body.mentioning()).contains("example_user") + assertThat(body.minAccountAgeDays()).contains(0L) + assertThat(body.minBookmarks()).contains(0L) assertThat(body.minFaves()).contains(10L) + assertThat(body.minFollowers()).contains(0L) + assertThat(body.minFollowing()).contains(0L) + assertThat(body.minPosts()).contains(0L) assertThat(body.minQuotes()).contains(2L) assertThat(body.minReplies()).contains(3L) assertThat(body.minRetweets()).contains(5L) + assertThat(body.minViews()).contains(0L) + assertThat(body.nativeRetweets()).contains(true) + assertThat(body.near()).contains("near") + assertThat(body.news()).contains(true) + assertThat(body.overlapMode()).contains(true) assertThat(body.place()).contains("96683cc9126741d1") assertThat(body.placeCountry()).contains("US") assertThat(body.pointRadius()).contains("-73.99 40.73 25mi") + assertThat(body.queryType()).contains(ExtractionEstimateCostParams.QueryType.LATEST) assertThat(body.quotes()).contains(ExtractionEstimateCostParams.Quotes.INCLUDE) assertThat(body.quotesOfTweetId()).contains("1234567890") + assertThat(body.relationTargets().getOrNull()) + .containsExactly( + ExtractionEstimateCostParams.RelationTarget.builder() + .relation( + ExtractionEstimateCostParams.RelationTarget.Relation.COMMUNITY_MEMBERS + ) + .value("x") + .build() + ) assertThat(body.replies()).contains(ExtractionEstimateCostParams.Replies.INCLUDE) assertThat(body.resultsLimit()).contains(1000L) assertThat(body.retweets()).contains(ExtractionEstimateCostParams.Retweets.EXCLUDE) assertThat(body.retweetsOfTweetId()).contains("1234567890") + assertThat(body.safe()).contains(true) + assertThat(body.scope()).contains(ExtractionEstimateCostParams.Scope.ALL) + assertThat(body.searchQueries().getOrNull()).containsExactly("string") assertThat(body.searchQuery()).contains("AI trends 2025") assertThat(body.sinceDate()).contains(LocalDate.parse("2025-01-01")) + assertThat(body.sinceId()).contains("sinceId") + assertThat(body.sinceTime()) + .contains( + ExtractionEstimateCostParams.SinceTime.ofOffsetDate( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + ) + assertThat(body.sort()).contains(ExtractionEstimateCostParams.Sort.RELEVANCE) + assertThat(body.source()).contains("source") + assertThat(body.startCursor()).contains("x") assertThat(body.targetCommunityId()).contains("1500000000000000000") + assertThat(body.targetCommunityIds().getOrNull()).containsExactly("string") assertThat(body.targetListId()).contains("1234567890") + assertThat(body.targetListIds().getOrNull()).containsExactly("string") + assertThat(body.targets().getOrNull()) + .containsExactly(ExtractionEstimateCostParams.Target.ofString("string")) assertThat(body.targetSpaceId()).contains("1vOGwMdBqpwGB") assertThat(body.targetTweetId()).contains("1234567890") + assertThat(body.targetTweetIds().getOrNull()).containsExactly("string") assertThat(body.targetUsername()).contains("elonmusk") + assertThat(body.targetUsernames().getOrNull()).containsExactly("string") assertThat(body.toUser()).contains("openai") assertThat(body.untilDate()).contains(LocalDate.parse("2025-12-31")) + assertThat(body.untilTime()) + .contains( + ExtractionEstimateCostParams.UntilTime.ofOffsetDate( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + ) assertThat(body.url()).contains("example.com") + assertThat(body.usernameContains()).contains("usernameContains") assertThat(body.verifiedOnly()).contains(false) + assertThat(body.verifiedType()).contains("verifiedType") + assertThat(body.within()).contains("within") + assertThat(body.withinTime()).contains("withinTime") } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParamsTest.kt index 5cdcd10b..c13faba0 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionExportResultsParamsTest.kt @@ -7,6 +7,7 @@ package com.x_twitter_scraper.api.models.extractions import com.x_twitter_scraper.api.core.http.QueryParams +import java.time.LocalDate import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -17,6 +18,24 @@ internal class ExtractionExportResultsParamsTest { ExtractionExportResultsParams.builder() .id("id") .format(ExtractionExportResultsParams.Format.CSV) + .hasDescription(true) + .hasLocation(true) + .hasMedia(true) + .lang("lang") + .maxFollowers(0L) + .maxFollowing(0L) + .maxPosts(0L) + .minFollowers(0L) + .minFollowing(0L) + .minLikes(0L) + .minPosts(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .search("search") + .sinceDate(LocalDate.parse("2019-12-27")) + .untilDate(LocalDate.parse("2019-12-27")) + .verified(true) .build() } @@ -35,6 +54,60 @@ internal class ExtractionExportResultsParamsTest { @Test fun queryParams() { + val params = + ExtractionExportResultsParams.builder() + .id("id") + .format(ExtractionExportResultsParams.Format.CSV) + .hasDescription(true) + .hasLocation(true) + .hasMedia(true) + .lang("lang") + .maxFollowers(0L) + .maxFollowing(0L) + .maxPosts(0L) + .minFollowers(0L) + .minFollowing(0L) + .minLikes(0L) + .minPosts(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .search("search") + .sinceDate(LocalDate.parse("2019-12-27")) + .untilDate(LocalDate.parse("2019-12-27")) + .verified(true) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("format", "csv") + .put("hasDescription", "true") + .put("hasLocation", "true") + .put("hasMedia", "true") + .put("lang", "lang") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxPosts", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minLikes", "0") + .put("minPosts", "0") + .put("minReplies", "0") + .put("minRetweets", "0") + .put("minViews", "0") + .put("search", "search") + .put("sinceDate", "2019-12-27") + .put("untilDate", "2019-12-27") + .put("verified", "true") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { val params = ExtractionExportResultsParams.builder() .id("id") diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParamsTest.kt index 257f6b17..52c6711b 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRetrieveParamsTest.kt @@ -14,7 +14,15 @@ internal class ExtractionRetrieveParamsTest { @Test fun create() { - ExtractionRetrieveParams.builder().id("id").cursor("cursor").limit(1L).build() + ExtractionRetrieveParams.builder() + .id("id") + .cursor("cursor") + .fieldStyle(ExtractionRetrieveParams.FieldStyle.SOURCE) + .includeRaw(true) + .limit(1L) + .outputMode(ExtractionRetrieveParams.OutputMode.COMPACT) + .outputPreset(ExtractionRetrieveParams.OutputPreset.NESTED) + .build() } @Test @@ -28,12 +36,30 @@ internal class ExtractionRetrieveParamsTest { @Test fun queryParams() { - val params = ExtractionRetrieveParams.builder().id("id").cursor("cursor").limit(1L).build() + val params = + ExtractionRetrieveParams.builder() + .id("id") + .cursor("cursor") + .fieldStyle(ExtractionRetrieveParams.FieldStyle.SOURCE) + .includeRaw(true) + .limit(1L) + .outputMode(ExtractionRetrieveParams.OutputMode.COMPACT) + .outputPreset(ExtractionRetrieveParams.OutputPreset.NESTED) + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("limit", "1").build()) + .isEqualTo( + QueryParams.builder() + .put("cursor", "cursor") + .put("fieldStyle", "source") + .put("includeRaw", "true") + .put("limit", "1") + .put("outputMode", "compact") + .put("outputPreset", "nested") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParamsTest.kt index 1c2ea11f..a30f4b9a 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunParamsTest.kt @@ -6,7 +6,10 @@ package com.x_twitter_scraper.api.models.extractions +import com.x_twitter_scraper.api.core.http.QueryParams import java.time.LocalDate +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,91 +18,342 @@ internal class ExtractionRunParamsTest { @Test fun create() { ExtractionRunParams.builder() + .dryRun(true) .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionRunParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionRunParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionRunParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionRunParams.QueryType.LATEST) .quotes(ExtractionRunParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) .replies(ExtractionRunParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionRunParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionRunParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionRunParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() } + @Test + fun queryParams() { + val params = + ExtractionRunParams.builder() + .dryRun(true) + .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) + .advancedQuery("min_faves:100") + .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) + .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") + .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionRunParams.CollectionStrategy.AUTO) + .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionRunParams.DedupeMode.NONE) + .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") + .excludeWords("spam") + .fromUser("nasa") + .geocode("geocode") + .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) + .inReplyToTweetId("1234567890") + .language("en") + .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) + .mediaType(ExtractionRunParams.MediaType.IMAGES) + .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) + .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) + .minQuotes(2L) + .minReplies(3L) + .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) + .place("96683cc9126741d1") + .placeCountry("US") + .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionRunParams.QueryType.LATEST) + .quotes(ExtractionRunParams.Quotes.INCLUDE) + .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) + .replies(ExtractionRunParams.Replies.INCLUDE) + .resultsLimit(1000L) + .retweets(ExtractionRunParams.Retweets.EXCLUDE) + .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionRunParams.Scope.ALL) + .addSearchQuery("string") + .searchQuery("AI trends 2025") + .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionRunParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") + .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") + .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") + .targetSpaceId("1vOGwMdBqpwGB") + .targetTweetId("1234567890") + .addTargetTweetId("string") + .targetUsername("elonmusk") + .addTargetUsername("string") + .toUser("openai") + .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .url("example.com") + .usernameContains("usernameContains") + .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().put("dry_run", "true").build()) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = + ExtractionRunParams.builder() + .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } + @Test fun body() { val params = ExtractionRunParams.builder() + .dryRun(true) .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionRunParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionRunParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionRunParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionRunParams.QueryType.LATEST) .quotes(ExtractionRunParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) .replies(ExtractionRunParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionRunParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionRunParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionRunParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() val body = params._body() @@ -107,42 +361,116 @@ internal class ExtractionRunParamsTest { assertThat(body.toolType()).isEqualTo(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) assertThat(body.advancedQuery()).contains("min_faves:100") assertThat(body.anyWords()).contains("ChatGPT AI model") + assertThat(body.bioContains()).contains("bioContains") + assertThat(body.blueVerifiedOnly()).contains(true) assertThat(body.boundingBox()).contains("-74.1 40.6 -73.9 40.8") + assertThat(body.cardName()).contains("cardName") assertThat(body.cashtags()).contains("\$TSLA \$NVDA") + assertThat(body.collectionStrategy()).contains(ExtractionRunParams.CollectionStrategy.AUTO) assertThat(body.conversationId()).contains("1234567890") + assertThat(body.dedupeAcrossTargets()).contains(true) + assertThat(body.dedupeMode()).contains(ExtractionRunParams.DedupeMode.NONE) assertThat(body.exactPhrase()).contains("artificial intelligence") + assertThat(body.excludeOriginalAuthor()).contains(true) + assertThat(body.excludeSource()).contains("excludeSource") assertThat(body.excludeWords()).contains("spam") assertThat(body.fromUser()).contains("nasa") + assertThat(body.geocode()).contains("geocode") assertThat(body.hashtags()).contains("#AI startups") + assertThat(body.hasLocation()).contains(true) + assertThat(body.hasMediaOnly()).contains(true) + assertThat(body.hasWebsite()).contains(true) + assertThat(body.includeOriginalPost()).contains(true) + assertThat(body.includeSearchTerms()).contains(true) + assertThat(body.includeTargetMetadata()).contains(true) assertThat(body.inReplyToTweetId()).contains("1234567890") assertThat(body.language()).contains("en") assertThat(body.listId()).contains("1234567890") + assertThat(body.locationContains()).contains("locationContains") + assertThat(body.maxDepth()).contains(1L) + assertThat(body.maxFollowers()).contains(0L) + assertThat(body.maxFollowing()).contains(0L) + assertThat(body.maxId()).contains("maxId") + assertThat(body.maxItemsPerTarget()).contains(1L) + assertThat(body.maxLikes()).contains(0L) + assertThat(body.maxPagesPerTarget()).contains(1L) + assertThat(body.maxPosts()).contains(0L) + assertThat(body.maxQuotes()).contains(0L) + assertThat(body.maxReplies()).contains(0L) + assertThat(body.maxRetweets()).contains(0L) assertThat(body.mediaType()).contains(ExtractionRunParams.MediaType.IMAGES) assertThat(body.mentioning()).contains("example_user") + assertThat(body.minAccountAgeDays()).contains(0L) + assertThat(body.minBookmarks()).contains(0L) assertThat(body.minFaves()).contains(10L) + assertThat(body.minFollowers()).contains(0L) + assertThat(body.minFollowing()).contains(0L) + assertThat(body.minPosts()).contains(0L) assertThat(body.minQuotes()).contains(2L) assertThat(body.minReplies()).contains(3L) assertThat(body.minRetweets()).contains(5L) + assertThat(body.minViews()).contains(0L) + assertThat(body.nativeRetweets()).contains(true) + assertThat(body.near()).contains("near") + assertThat(body.news()).contains(true) + assertThat(body.overlapMode()).contains(true) assertThat(body.place()).contains("96683cc9126741d1") assertThat(body.placeCountry()).contains("US") assertThat(body.pointRadius()).contains("-73.99 40.73 25mi") + assertThat(body.queryType()).contains(ExtractionRunParams.QueryType.LATEST) assertThat(body.quotes()).contains(ExtractionRunParams.Quotes.INCLUDE) assertThat(body.quotesOfTweetId()).contains("1234567890") + assertThat(body.relationTargets().getOrNull()) + .containsExactly( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) assertThat(body.replies()).contains(ExtractionRunParams.Replies.INCLUDE) assertThat(body.resultsLimit()).contains(1000L) assertThat(body.retweets()).contains(ExtractionRunParams.Retweets.EXCLUDE) assertThat(body.retweetsOfTweetId()).contains("1234567890") + assertThat(body.safe()).contains(true) + assertThat(body.scope()).contains(ExtractionRunParams.Scope.ALL) + assertThat(body.searchQueries().getOrNull()).containsExactly("string") assertThat(body.searchQuery()).contains("AI trends 2025") assertThat(body.sinceDate()).contains(LocalDate.parse("2025-01-01")) + assertThat(body.sinceId()).contains("sinceId") + assertThat(body.sinceTime()) + .contains( + ExtractionRunParams.SinceTime.ofOffsetDate( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + ) + assertThat(body.sort()).contains(ExtractionRunParams.Sort.RELEVANCE) + assertThat(body.source()).contains("source") + assertThat(body.startCursor()).contains("x") assertThat(body.targetCommunityId()).contains("1500000000000000000") + assertThat(body.targetCommunityIds().getOrNull()).containsExactly("string") assertThat(body.targetListId()).contains("1234567890") + assertThat(body.targetListIds().getOrNull()).containsExactly("string") + assertThat(body.targets().getOrNull()) + .containsExactly(ExtractionRunParams.Target.ofString("string")) assertThat(body.targetSpaceId()).contains("1vOGwMdBqpwGB") assertThat(body.targetTweetId()).contains("1234567890") + assertThat(body.targetTweetIds().getOrNull()).containsExactly("string") assertThat(body.targetUsername()).contains("elonmusk") + assertThat(body.targetUsernames().getOrNull()).containsExactly("string") assertThat(body.toUser()).contains("openai") assertThat(body.untilDate()).contains(LocalDate.parse("2025-12-31")) + assertThat(body.untilTime()) + .contains( + ExtractionRunParams.UntilTime.ofOffsetDate( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + ) assertThat(body.url()).contains("example.com") + assertThat(body.usernameContains()).contains("usernameContains") assertThat(body.verifiedOnly()).contains(false) + assertThat(body.verifiedType()).contains("verifiedType") + assertThat(body.within()).contains("within") + assertThat(body.withinTime()).contains("withinTime") } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponseTest.kt index 4aa043a6..35356fbd 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponseTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/extractions/ExtractionRunResponseTest.kt @@ -17,13 +17,20 @@ internal class ExtractionRunResponseTest { fun create() { val extractionRunResponse = ExtractionRunResponse.builder() - .id("a1b2c3d4-e5f6-7890-abcd-ef1234567890") - .toolType(ExtractionRunResponse.ToolType.FOLLOWER_EXPLORER) + .allowed(true) + .creditsAvailable("creditsAvailable") + .creditsRequired("creditsRequired") + .estimatedResults(0L) + .source("source") + .resolvedXUserId("resolvedXUserId") .build() - assertThat(extractionRunResponse.id()).isEqualTo("a1b2c3d4-e5f6-7890-abcd-ef1234567890") - assertThat(extractionRunResponse.toolType()) - .isEqualTo(ExtractionRunResponse.ToolType.FOLLOWER_EXPLORER) + assertThat(extractionRunResponse.allowed()).isEqualTo(true) + assertThat(extractionRunResponse.creditsAvailable()).isEqualTo("creditsAvailable") + assertThat(extractionRunResponse.creditsRequired()).isEqualTo("creditsRequired") + assertThat(extractionRunResponse.estimatedResults()).isEqualTo(0L) + assertThat(extractionRunResponse.source()).isEqualTo("source") + assertThat(extractionRunResponse.resolvedXUserId()).contains("resolvedXUserId") } @Test @@ -31,8 +38,12 @@ internal class ExtractionRunResponseTest { val jsonMapper = jsonMapper() val extractionRunResponse = ExtractionRunResponse.builder() - .id("a1b2c3d4-e5f6-7890-abcd-ef1234567890") - .toolType(ExtractionRunResponse.ToolType.FOLLOWER_EXPLORER) + .allowed(true) + .creditsAvailable("creditsAvailable") + .creditsRequired("creditsRequired") + .estimatedResults(0L) + .source("source") + .resolvedXUserId("resolvedXUserId") .build() val roundtrippedExtractionRunResponse = diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorListResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorListResponseTest.kt index ce89bdf4..176b6169 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorListResponseTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorListResponseTest.kt @@ -29,6 +29,8 @@ internal class MonitorListResponseTest { .nextBillingAt(OffsetDateTime.parse("2025-01-15T12:00:00Z")) .username("elonmusk") .xUserId("9876543210") + .pausedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .pausedReason(Monitor.PausedReason.X_USER_NOT_FOUND) .build() ) .total(0L) @@ -45,6 +47,8 @@ internal class MonitorListResponseTest { .nextBillingAt(OffsetDateTime.parse("2025-01-15T12:00:00Z")) .username("elonmusk") .xUserId("9876543210") + .pausedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .pausedReason(Monitor.PausedReason.X_USER_NOT_FOUND) .build() ) assertThat(monitorListResponse.total()).isEqualTo(0L) @@ -65,6 +69,8 @@ internal class MonitorListResponseTest { .nextBillingAt(OffsetDateTime.parse("2025-01-15T12:00:00Z")) .username("elonmusk") .xUserId("9876543210") + .pausedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .pausedReason(Monitor.PausedReason.X_USER_NOT_FOUND) .build() ) .total(0L) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorTest.kt index f349f448..a30f92e7 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/monitors/MonitorTest.kt @@ -27,6 +27,8 @@ internal class MonitorTest { .nextBillingAt(OffsetDateTime.parse("2025-01-15T12:00:00Z")) .username("elonmusk") .xUserId("9876543210") + .pausedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .pausedReason(Monitor.PausedReason.X_USER_NOT_FOUND) .build() assertThat(monitor.id()).isEqualTo("42") @@ -36,6 +38,8 @@ internal class MonitorTest { assertThat(monitor.nextBillingAt()).isEqualTo(OffsetDateTime.parse("2025-01-15T12:00:00Z")) assertThat(monitor.username()).isEqualTo("elonmusk") assertThat(monitor.xUserId()).isEqualTo("9876543210") + assertThat(monitor.pausedAt()).contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(monitor.pausedReason()).contains(Monitor.PausedReason.X_USER_NOT_FOUND) } @Test @@ -51,6 +55,8 @@ internal class MonitorTest { .nextBillingAt(OffsetDateTime.parse("2025-01-15T12:00:00Z")) .username("elonmusk") .xUserId("9876543210") + .pausedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .pausedReason(Monitor.PausedReason.X_USER_NOT_FOUND) .build() val roundtrippedMonitor = diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParamsTest.kt new file mode 100644 index 00000000..981da744 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/support/attachments/AttachmentDownloadParamsTest.kt @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.support.attachments + +import com.x_twitter_scraper.api.core.http.Headers +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AttachmentDownloadParamsTest { + + @Test + fun create() { + AttachmentDownloadParams.builder() + .id("att_a1b2c3d4e5f6a1b2c3d4e5f6") + .range("bytes=0-1048575") + .build() + } + + @Test + fun pathParams() { + val params = AttachmentDownloadParams.builder().id("att_a1b2c3d4e5f6a1b2c3d4e5f6").build() + + assertThat(params._pathParam(0)).isEqualTo("att_a1b2c3d4e5f6a1b2c3d4e5f6") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun headers() { + val params = + AttachmentDownloadParams.builder() + .id("att_a1b2c3d4e5f6a1b2c3d4e5f6") + .range("bytes=0-1048575") + .build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().put("Range", "bytes=0-1048575").build()) + } + + @Test + fun headersWithoutOptionalFields() { + val params = AttachmentDownloadParams.builder().id("att_a1b2c3d4e5f6a1b2c3d4e5f6").build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParamsTest.kt index 5361f1cd..ce9d0eb9 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveMembersParamsTest.kt @@ -14,7 +14,25 @@ internal class CommunityRetrieveMembersParamsTest { @Test fun create() { - CommunityRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + CommunityRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -29,12 +47,49 @@ internal class CommunityRetrieveMembersParamsTest { @Test fun queryParams() { val params = - CommunityRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + CommunityRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParamsTest.kt index f352b852..4f3e731f 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/communities/CommunityRetrieveModeratorsParamsTest.kt @@ -14,7 +14,24 @@ internal class CommunityRetrieveModeratorsParamsTest { @Test fun create() { - CommunityRetrieveModeratorsParams.builder().id("id").cursor("cursor").build() + CommunityRetrieveModeratorsParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -28,11 +45,48 @@ internal class CommunityRetrieveModeratorsParamsTest { @Test fun queryParams() { - val params = CommunityRetrieveModeratorsParams.builder().id("id").cursor("cursor").build() + val params = + CommunityRetrieveModeratorsParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() - assertThat(queryParams).isEqualTo(QueryParams.builder().put("cursor", "cursor").build()) + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParamsTest.kt index 31805722..1bc9bddb 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveFollowersParamsTest.kt @@ -14,7 +14,25 @@ internal class ListRetrieveFollowersParamsTest { @Test fun create() { - ListRetrieveFollowersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveFollowersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -29,12 +47,49 @@ internal class ListRetrieveFollowersParamsTest { @Test fun queryParams() { val params = - ListRetrieveFollowersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveFollowersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParamsTest.kt index f53fb0ad..e721c53b 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/lists/ListRetrieveMembersParamsTest.kt @@ -14,7 +14,25 @@ internal class ListRetrieveMembersParamsTest { @Test fun create() { - ListRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -29,12 +47,49 @@ internal class ListRetrieveMembersParamsTest { @Test fun queryParams() { val params = - ListRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParamsTest.kt index 520dde39..9cc431eb 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetFavoritersParamsTest.kt @@ -14,7 +14,25 @@ internal class TweetGetFavoritersParamsTest { @Test fun create() { - TweetGetFavoritersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetFavoritersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -29,12 +47,49 @@ internal class TweetGetFavoritersParamsTest { @Test fun queryParams() { val params = - TweetGetFavoritersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetFavoritersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParamsTest.kt index dfdbfcc0..3c657082 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParamsTest.kt @@ -18,35 +18,54 @@ internal class TweetGetQuotesParamsTest { TweetGetQuotesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetQuotesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetQuotesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetQuotesParams.Replies.INCLUDE) .retweets(TweetGetQuotesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -65,35 +84,54 @@ internal class TweetGetQuotesParamsTest { TweetGetQuotesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetQuotesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetQuotesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetQuotesParams.Replies.INCLUDE) .retweets(TweetGetQuotesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -102,35 +140,54 @@ internal class TweetGetQuotesParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("includeReplies", "true") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") .put("sinceTime", "sinceTime") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("untilTime", "untilTime") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParamsTest.kt index 0c951397..a48a6991 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParamsTest.kt @@ -18,36 +18,61 @@ internal class TweetGetRepliesParamsTest { TweetGetRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") + .hasMediaOnly(true) + .includeOriginalPost(true) .inReplyToTweetId("inReplyToTweetId") .language("language") .limit(1L) + .maxDepth(1L) + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) - .mode(TweetGetRepliesParams.Mode.COMPLETE) + .minViews(0L) + .mode(TweetGetRepliesParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetRepliesParams.Replies.INCLUDE) .retweets(TweetGetRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) + .scope(TweetGetRepliesParams.Scope.ALL) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .sort(TweetGetRepliesParams.Sort.RELEVANCE) + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -66,36 +91,61 @@ internal class TweetGetRepliesParamsTest { TweetGetRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") + .hasMediaOnly(true) + .includeOriginalPost(true) .inReplyToTweetId("inReplyToTweetId") .language("language") .limit(1L) + .maxDepth(1L) + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) - .mode(TweetGetRepliesParams.Mode.COMPLETE) + .minViews(0L) + .mode(TweetGetRepliesParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetRepliesParams.Replies.INCLUDE) .retweets(TweetGetRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) + .scope(TweetGetRepliesParams.Scope.ALL) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .sort(TweetGetRepliesParams.Sort.RELEVANCE) + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -104,36 +154,61 @@ internal class TweetGetRepliesParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeOriginalAuthor", "true") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") + .put("hasMediaOnly", "true") + .put("includeOriginalPost", "true") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") .put("limit", "1") + .put("maxDepth", "1") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") - .put("mode", "complete") + .put("minViews", "0") + .put("mode", "standard") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") + .put("scope", "all") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") .put("sinceTime", "sinceTime") + .put("sort", "relevance") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("untilTime", "untilTime") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParamsTest.kt index b82f5e90..a777b632 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRetweetersParamsTest.kt @@ -14,7 +14,25 @@ internal class TweetGetRetweetersParamsTest { @Test fun create() { - TweetGetRetweetersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetRetweetersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -29,12 +47,49 @@ internal class TweetGetRetweetersParamsTest { @Test fun queryParams() { val params = - TweetGetRetweetersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetRetweetersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParamsTest.kt index cfa34e02..2a7c2495 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParamsTest.kt @@ -19,24 +19,39 @@ internal class TweetSearchParamsTest { .q("q") .advancedQuery("advancedQuery") .anyWords("anyWords") + .blueVerifiedOnly(true) .boundingBox("boundingBox") + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") - .limit(200L) + .limit(1L) .listId("listId") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetSearchParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .mode(TweetSearchParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .place("place") .placeCountry("placeCountry") .pointRadius("pointRadius") @@ -46,13 +61,18 @@ internal class TweetSearchParamsTest { .replies(TweetSearchParams.Replies.INCLUDE) .retweets(TweetSearchParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -63,24 +83,39 @@ internal class TweetSearchParamsTest { .q("q") .advancedQuery("advancedQuery") .anyWords("anyWords") + .blueVerifiedOnly(true) .boundingBox("boundingBox") + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") - .limit(200L) + .limit(1L) .listId("listId") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetSearchParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .mode(TweetSearchParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .place("place") .placeCountry("placeCountry") .pointRadius("pointRadius") @@ -90,13 +125,18 @@ internal class TweetSearchParamsTest { .replies(TweetSearchParams.Replies.INCLUDE) .retweets(TweetSearchParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -107,24 +147,39 @@ internal class TweetSearchParamsTest { .put("q", "q") .put("advancedQuery", "advancedQuery") .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") .put("boundingBox", "boundingBox") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") - .put("limit", "200") + .put("limit", "1") .put("listId", "listId") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("mode", "standard") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("place", "place") .put("placeCountry", "placeCountry") .put("pointRadius", "pointRadius") @@ -134,13 +189,18 @@ internal class TweetSearchParamsTest { .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") .put("sinceTime", "sinceTime") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("untilTime", "untilTime") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponseTest.kt new file mode 100644 index 00000000..5a241558 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchResponseTest.kt @@ -0,0 +1,3597 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.tweets + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.jsonMapper +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.ContentDisclosure +import com.x_twitter_scraper.api.models.EmbeddedTweet +import com.x_twitter_scraper.api.models.PaginatedTweets +import com.x_twitter_scraper.api.models.SearchTweet +import com.x_twitter_scraper.api.models.TweetMedia +import com.x_twitter_scraper.api.models.UserProfile +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class TweetSearchResponseTest { + + @Test + fun ofPaginatedTweets() { + val paginatedTweets = + PaginatedTweets.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addTweet( + SearchTweet.builder() + .id("1234567890") + .bookmarkCount(2L) + .likeCount(42L) + .quoteCount(1L) + .replyCount(3L) + .retweetCount(5L) + .text("Just launched our new feature!") + .viewCount(1500L) + .article( + SearchTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + SearchTweet.Card.builder() + .id("id") + .bindingValues( + SearchTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + SearchTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("1234567890") + .createdAt("2025-01-15T12:00:00Z") + .addDisplayTextRange(0L) + .addDisplayTextRange(31L) + .edit( + SearchTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + SearchTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("1234567890") + .inReplyToUserId("9876543210") + .inReplyToUsername("example_user") + .isLimitedReply(false) + .isNoteTweet(false) + .isQuoteStatus(false) + .isReply(false) + .isTranslatable(true) + .lang("en") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf("h" to 0, "w" to 0, "x" to 0, "y" to 0) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder().h(0L).w(0L).x(0L).y(0L).build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf("h" to 0, "resize" to "resize", "w" to 0) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + SearchTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + SearchTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + SearchTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + SearchTweet.Place.builder() + .id("id") + .boundingBox( + SearchTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + SearchTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .quotedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .retweetedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .source("Twitter Web App") + .type("tweet") + .url("https://x.com/example_user/status/1234567890") + .viewState("viewState") + .build() + ) + .build() + + val tweetSearchResponse = TweetSearchResponse.ofPaginatedTweets(paginatedTweets) + + assertThat(tweetSearchResponse.paginatedTweets()).contains(paginatedTweets) + assertThat(tweetSearchResponse.coverage()).isEmpty + } + + @Test + fun ofPaginatedTweetsRoundtrip() { + val jsonMapper = jsonMapper() + val tweetSearchResponse = + TweetSearchResponse.ofPaginatedTweets( + PaginatedTweets.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addTweet( + SearchTweet.builder() + .id("1234567890") + .bookmarkCount(2L) + .likeCount(42L) + .quoteCount(1L) + .replyCount(3L) + .retweetCount(5L) + .text("Just launched our new feature!") + .viewCount(1500L) + .article( + SearchTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + SearchTweet.Card.builder() + .id("id") + .bindingValues( + SearchTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + SearchTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("1234567890") + .createdAt("2025-01-15T12:00:00Z") + .addDisplayTextRange(0L) + .addDisplayTextRange(31L) + .edit( + SearchTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + SearchTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("1234567890") + .inReplyToUserId("9876543210") + .inReplyToUsername("example_user") + .isLimitedReply(false) + .isNoteTweet(false) + .isQuoteStatus(false) + .isReply(false) + .isTranslatable(true) + .lang("en") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf("h" to 0, "resize" to "resize", "w" to 0) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + SearchTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + SearchTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + SearchTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + SearchTweet.Place.builder() + .id("id") + .boundingBox( + SearchTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + SearchTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .quotedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .retweetedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .source("Twitter Web App") + .type("tweet") + .url("https://x.com/example_user/status/1234567890") + .viewState("viewState") + .build() + ) + .build() + ) + + val roundtrippedTweetSearchResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(tweetSearchResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTweetSearchResponse).isEqualTo(tweetSearchResponse) + } + + @Test + fun ofCoverage() { + val coverage = + TweetSearchResponse.TweetSearchCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addTweet( + SearchTweet.builder() + .id("1234567890") + .bookmarkCount(2L) + .likeCount(42L) + .quoteCount(1L) + .replyCount(3L) + .retweetCount(5L) + .text("Just launched our new feature!") + .viewCount(1500L) + .article( + SearchTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + SearchTweet.Card.builder() + .id("id") + .bindingValues( + SearchTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + SearchTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("1234567890") + .createdAt("2025-01-15T12:00:00Z") + .addDisplayTextRange(0L) + .addDisplayTextRange(31L) + .edit( + SearchTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + SearchTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("1234567890") + .inReplyToUserId("9876543210") + .inReplyToUsername("example_user") + .isLimitedReply(false) + .isNoteTweet(false) + .isQuoteStatus(false) + .isReply(false) + .isTranslatable(true) + .lang("en") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf("h" to 0, "w" to 0, "x" to 0, "y" to 0) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder().h(0L).w(0L).x(0L).y(0L).build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf("h" to 0, "resize" to "resize", "w" to 0) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + SearchTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + SearchTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + SearchTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + SearchTweet.Place.builder() + .id("id") + .boundingBox( + SearchTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + SearchTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .quotedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .retweetedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .source("Twitter Web App") + .type("tweet") + .url("https://x.com/example_user/status/1234567890") + .viewState("viewState") + .build() + ) + .diagnostic( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .partitioned(true) + .responseTruncated(true) + .resultLimitReached(true) + .returnedTweets(0L) + .stalledStrategyCount(0L) + .addStrategy( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic.Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .queryType( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .QueryType + .LATEST + ) + .stopReason( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .window( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .Window + .builder() + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + ) + .build() + ) + .strategyCount(1L) + .uniqueTweets(0L) + .build() + ) + .build() + + val tweetSearchResponse = TweetSearchResponse.ofCoverage(coverage) + + assertThat(tweetSearchResponse.paginatedTweets()).isEmpty + assertThat(tweetSearchResponse.coverage()).contains(coverage) + } + + @Test + fun ofCoverageRoundtrip() { + val jsonMapper = jsonMapper() + val tweetSearchResponse = + TweetSearchResponse.ofCoverage( + TweetSearchResponse.TweetSearchCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addTweet( + SearchTweet.builder() + .id("1234567890") + .bookmarkCount(2L) + .likeCount(42L) + .quoteCount(1L) + .replyCount(3L) + .retweetCount(5L) + .text("Just launched our new feature!") + .viewCount(1500L) + .article( + SearchTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + SearchTweet.Card.builder() + .id("id") + .bindingValues( + SearchTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + SearchTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("1234567890") + .createdAt("2025-01-15T12:00:00Z") + .addDisplayTextRange(0L) + .addDisplayTextRange(31L) + .edit( + SearchTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + SearchTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("1234567890") + .inReplyToUserId("9876543210") + .inReplyToUsername("example_user") + .isLimitedReply(false) + .isNoteTweet(false) + .isQuoteStatus(false) + .isReply(false) + .isTranslatable(true) + .lang("en") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf("h" to 0, "resize" to "resize", "w" to 0) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + SearchTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + SearchTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + SearchTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + SearchTweet.Place.builder() + .id("id") + .boundingBox( + SearchTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + SearchTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .quotedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .retweetedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .source("Twitter Web App") + .type("tweet") + .url("https://x.com/example_user/status/1234567890") + .viewState("viewState") + .build() + ) + .diagnostic( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .partitioned(true) + .responseTruncated(true) + .resultLimitReached(true) + .returnedTweets(0L) + .stalledStrategyCount(0L) + .addStrategy( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic.Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .queryType( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .QueryType + .LATEST + ) + .stopReason( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .window( + TweetSearchResponse.TweetSearchCoverageResponse.Diagnostic + .Strategy + .Window + .builder() + .sinceTime( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + .untilTime( + OffsetDateTime.parse("2019-12-27T18:11:19.117Z") + ) + .build() + ) + .build() + ) + .strategyCount(1L) + .uniqueTweets(0L) + .build() + ) + .build() + ) + + val roundtrippedTweetSearchResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(tweetSearchResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTweetSearchResponse).isEqualTo(tweetSearchResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val tweetSearchResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { tweetSearchResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParamsTest.kt index da001cf7..97b53c38 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersParamsTest.kt @@ -17,9 +17,24 @@ internal class UserRetrieveFollowersParamsTest { UserRetrieveFollowersParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() } @@ -38,9 +53,24 @@ internal class UserRetrieveFollowersParamsTest { UserRetrieveFollowersParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() val queryParams = params._queryParams() @@ -49,9 +79,24 @@ internal class UserRetrieveFollowersParamsTest { .isEqualTo( QueryParams.builder() .put("after", "after") + .put("bioContains", "bioContains") .put("cursor", "cursor") - .put("limit", "0") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("limit", "1") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("mode", "standard") .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponseTest.kt new file mode 100644 index 00000000..a02bfb95 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersResponseTest.kt @@ -0,0 +1,480 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.jsonMapper +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class UserRetrieveFollowersResponseTest { + + @Test + fun ofPaginatedUsers() { + val paginatedUsers = + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + + val userRetrieveFollowersResponse = + UserRetrieveFollowersResponse.ofPaginatedUsers(paginatedUsers) + + assertThat(userRetrieveFollowersResponse.paginatedUsers()).contains(paginatedUsers) + assertThat(userRetrieveFollowersResponse.listCoverage()).isEmpty + } + + @Test + fun ofPaginatedUsersRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveFollowersResponse = + UserRetrieveFollowersResponse.ofPaginatedUsers( + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveFollowersResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveFollowersResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveFollowersResponse) + .isEqualTo(userRetrieveFollowersResponse) + } + + @Test + fun ofListCoverage() { + val listCoverage = + UserRetrieveFollowersResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveFollowersResponse.UserListCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveFollowersResponse.UserListCoverageResponse.Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveFollowersResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + + val userRetrieveFollowersResponse = + UserRetrieveFollowersResponse.ofListCoverage(listCoverage) + + assertThat(userRetrieveFollowersResponse.paginatedUsers()).isEmpty + assertThat(userRetrieveFollowersResponse.listCoverage()).contains(listCoverage) + } + + @Test + fun ofListCoverageRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveFollowersResponse = + UserRetrieveFollowersResponse.ofListCoverage( + UserRetrieveFollowersResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveFollowersResponse.UserListCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveFollowersResponse.UserListCoverageResponse.Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveFollowersResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveFollowersResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveFollowersResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveFollowersResponse) + .isEqualTo(userRetrieveFollowersResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val userRetrieveFollowersResponse = + jsonMapper() + .convertValue(testCase.value, jacksonTypeRef()) + + val e = + assertThrows { + userRetrieveFollowersResponse.validate() + } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParamsTest.kt index 936b02f7..8209e4a0 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowersYouKnowParamsTest.kt @@ -14,7 +14,25 @@ internal class UserRetrieveFollowersYouKnowParamsTest { @Test fun create() { - UserRetrieveFollowersYouKnowParams.builder().id("id").cursor("cursor").pageSize(20L).build() + UserRetrieveFollowersYouKnowParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test @@ -31,14 +49,47 @@ internal class UserRetrieveFollowersYouKnowParamsTest { val params = UserRetrieveFollowersYouKnowParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParamsTest.kt index e5379890..59294ec9 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingParamsTest.kt @@ -17,9 +17,24 @@ internal class UserRetrieveFollowingParamsTest { UserRetrieveFollowingParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowingParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() } @@ -38,9 +53,24 @@ internal class UserRetrieveFollowingParamsTest { UserRetrieveFollowingParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowingParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() val queryParams = params._queryParams() @@ -49,9 +79,24 @@ internal class UserRetrieveFollowingParamsTest { .isEqualTo( QueryParams.builder() .put("after", "after") + .put("bioContains", "bioContains") .put("cursor", "cursor") - .put("limit", "0") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("limit", "1") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("mode", "standard") .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponseTest.kt new file mode 100644 index 00000000..90e4d7a9 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveFollowingResponseTest.kt @@ -0,0 +1,480 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.jsonMapper +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class UserRetrieveFollowingResponseTest { + + @Test + fun ofPaginatedUsers() { + val paginatedUsers = + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + + val userRetrieveFollowingResponse = + UserRetrieveFollowingResponse.ofPaginatedUsers(paginatedUsers) + + assertThat(userRetrieveFollowingResponse.paginatedUsers()).contains(paginatedUsers) + assertThat(userRetrieveFollowingResponse.listCoverage()).isEmpty + } + + @Test + fun ofPaginatedUsersRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveFollowingResponse = + UserRetrieveFollowingResponse.ofPaginatedUsers( + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveFollowingResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveFollowingResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveFollowingResponse) + .isEqualTo(userRetrieveFollowingResponse) + } + + @Test + fun ofListCoverage() { + val listCoverage = + UserRetrieveFollowingResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveFollowingResponse.UserListCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveFollowingResponse.UserListCoverageResponse.Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveFollowingResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + + val userRetrieveFollowingResponse = + UserRetrieveFollowingResponse.ofListCoverage(listCoverage) + + assertThat(userRetrieveFollowingResponse.paginatedUsers()).isEmpty + assertThat(userRetrieveFollowingResponse.listCoverage()).contains(listCoverage) + } + + @Test + fun ofListCoverageRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveFollowingResponse = + UserRetrieveFollowingResponse.ofListCoverage( + UserRetrieveFollowingResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveFollowingResponse.UserListCoverageResponse.Diagnostic.builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveFollowingResponse.UserListCoverageResponse.Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveFollowingResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveFollowingResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveFollowingResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveFollowingResponse) + .isEqualTo(userRetrieveFollowingResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val userRetrieveFollowingResponse = + jsonMapper() + .convertValue(testCase.value, jacksonTypeRef()) + + val e = + assertThrows { + userRetrieveFollowingResponse.validate() + } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParamsTest.kt index 3184001c..e26b7a31 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParamsTest.kt @@ -18,32 +18,51 @@ internal class UserRetrieveLikesParamsTest { UserRetrieveLikesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveLikesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveLikesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveLikesParams.Replies.INCLUDE) .retweets(UserRetrieveLikesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -62,32 +81,51 @@ internal class UserRetrieveLikesParamsTest { UserRetrieveLikesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveLikesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveLikesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveLikesParams.Replies.INCLUDE) .retweets(UserRetrieveLikesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -96,32 +134,51 @@ internal class UserRetrieveLikesParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParamsTest.kt index 11fb519c..cdf6ddba 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParamsTest.kt @@ -18,32 +18,51 @@ internal class UserRetrieveMediaParamsTest { UserRetrieveMediaParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMediaParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMediaParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMediaParams.Replies.INCLUDE) .retweets(UserRetrieveMediaParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -62,32 +81,51 @@ internal class UserRetrieveMediaParamsTest { UserRetrieveMediaParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMediaParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMediaParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMediaParams.Replies.INCLUDE) .retweets(UserRetrieveMediaParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -96,32 +134,51 @@ internal class UserRetrieveMediaParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParamsTest.kt index 823b4662..f631b132 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParamsTest.kt @@ -18,34 +18,53 @@ internal class UserRetrieveMentionsParamsTest { UserRetrieveMentionsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMentionsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMentionsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMentionsParams.Replies.INCLUDE) .retweets(UserRetrieveMentionsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -64,34 +83,53 @@ internal class UserRetrieveMentionsParamsTest { UserRetrieveMentionsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMentionsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMentionsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMentionsParams.Replies.INCLUDE) .retweets(UserRetrieveMentionsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -100,34 +138,53 @@ internal class UserRetrieveMentionsParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") .put("sinceTime", "sinceTime") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("untilTime", "untilTime") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParamsTest.kt index 8e00c7fd..7286ffb6 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParamsTest.kt @@ -18,33 +18,52 @@ internal class UserRetrieveRepliesParamsTest { UserRetrieveRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveRepliesParams.Replies.INCLUDE) .retweets(UserRetrieveRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -63,33 +82,52 @@ internal class UserRetrieveRepliesParamsTest { UserRetrieveRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveRepliesParams.Replies.INCLUDE) .retweets(UserRetrieveRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -98,33 +136,52 @@ internal class UserRetrieveRepliesParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("includeParentTweet", "true") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParamsTest.kt index 3654ea03..85f5c793 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveSearchParamsTest.kt @@ -14,17 +14,71 @@ internal class UserRetrieveSearchParamsTest { @Test fun create() { - UserRetrieveSearchParams.builder().q("q").cursor("cursor").build() + UserRetrieveSearchParams.builder() + .q("q") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() } @Test fun queryParams() { - val params = UserRetrieveSearchParams.builder().q("q").cursor("cursor").build() + val params = + UserRetrieveSearchParams.builder() + .q("q") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("q", "q").put("cursor", "cursor").build()) + .isEqualTo( + QueryParams.builder() + .put("q", "q") + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParamsTest.kt index 805a46cb..30de893d 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParamsTest.kt @@ -18,34 +18,53 @@ internal class UserRetrieveTweetsParamsTest { UserRetrieveTweetsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveTweetsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveTweetsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveTweetsParams.Replies.INCLUDE) .retweets(UserRetrieveTweetsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() } @@ -64,34 +83,53 @@ internal class UserRetrieveTweetsParamsTest { UserRetrieveTweetsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveTweetsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveTweetsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveTweetsParams.Replies.INCLUDE) .retweets(UserRetrieveTweetsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() val queryParams = params._queryParams() @@ -100,34 +138,53 @@ internal class UserRetrieveTweetsParamsTest { .isEqualTo( QueryParams.builder() .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cardName", "cardName") .put("cashtags", "cashtags") .put("conversationId", "conversationId") .put("cursor", "cursor") .put("exactPhrase", "exactPhrase") + .put("excludeSource", "excludeSource") .put("excludeWords", "excludeWords") .put("fromUser", "fromUser") + .put("geocode", "geocode") .put("hashtags", "hashtags") .put("includeParentTweet", "true") .put("includeReplies", "true") .put("inReplyToTweetId", "inReplyToTweetId") .put("language", "language") + .put("maxFaves", "0") + .put("maxId", "maxId") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") .put("mediaType", "images") .put("mentioning", "mentioning") + .put("minBookmarks", "0") .put("minFaves", "0") .put("minQuotes", "0") .put("minReplies", "0") .put("minRetweets", "0") + .put("minViews", "0") + .put("nativeRetweets", "true") + .put("near", "near") + .put("news", "true") .put("pageSize", "1") .put("quotes", "include") .put("quotesOfTweetId", "quotesOfTweetId") .put("replies", "include") .put("retweets", "include") .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("safe", "true") .put("sinceDate", "2019-12-27") + .put("sinceId", "sinceId") + .put("source", "source") .put("toUser", "toUser") .put("untilDate", "2019-12-27") .put("url", "url") .put("verifiedOnly", "true") + .put("within", "within") + .put("withinTime", "withinTime") .build() ) } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParamsTest.kt index d5f6bf33..663f0e71 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersParamsTest.kt @@ -16,8 +16,25 @@ internal class UserRetrieveVerifiedFollowersParamsTest { fun create() { UserRetrieveVerifiedFollowersParams.builder() .id("id") + .after("after") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveVerifiedFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() } @@ -35,14 +52,53 @@ internal class UserRetrieveVerifiedFollowersParamsTest { val params = UserRetrieveVerifiedFollowersParams.builder() .id("id") + .after("after") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveVerifiedFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "20").build()) + .isEqualTo( + QueryParams.builder() + .put("after", "after") + .put("bioContains", "bioContains") + .put("cursor", "cursor") + .put("hasLocation", "true") + .put("hasWebsite", "true") + .put("limit", "1") + .put("locationContains", "locationContains") + .put("maxFollowers", "0") + .put("maxFollowing", "0") + .put("maxStatuses", "0") + .put("minAccountAgeDays", "0") + .put("minFollowers", "0") + .put("minFollowing", "0") + .put("minStatuses", "0") + .put("mode", "standard") + .put("pageSize", "20") + .put("usernameContains", "usernameContains") + .put("verifiedOnly", "true") + .put("verifiedType", "verifiedType") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponseTest.kt new file mode 100644 index 00000000..83df2edc --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveVerifiedFollowersResponseTest.kt @@ -0,0 +1,488 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.models.x.users + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.x_twitter_scraper.api.core.JsonValue +import com.x_twitter_scraper.api.core.jsonMapper +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import com.x_twitter_scraper.api.models.PaginatedUsers +import com.x_twitter_scraper.api.models.UserProfile +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class UserRetrieveVerifiedFollowersResponseTest { + + @Test + fun ofPaginatedUsers() { + val paginatedUsers = + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + + val userRetrieveVerifiedFollowersResponse = + UserRetrieveVerifiedFollowersResponse.ofPaginatedUsers(paginatedUsers) + + assertThat(userRetrieveVerifiedFollowersResponse.paginatedUsers()).contains(paginatedUsers) + assertThat(userRetrieveVerifiedFollowersResponse.listCoverage()).isEmpty + } + + @Test + fun ofPaginatedUsersRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveVerifiedFollowersResponse = + UserRetrieveVerifiedFollowersResponse.ofPaginatedUsers( + PaginatedUsers.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveVerifiedFollowersResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveVerifiedFollowersResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveVerifiedFollowersResponse) + .isEqualTo(userRetrieveVerifiedFollowersResponse) + } + + @Test + fun ofListCoverage() { + val listCoverage = + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse.Diagnostic + .builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + + val userRetrieveVerifiedFollowersResponse = + UserRetrieveVerifiedFollowersResponse.ofListCoverage(listCoverage) + + assertThat(userRetrieveVerifiedFollowersResponse.paginatedUsers()).isEmpty + assertThat(userRetrieveVerifiedFollowersResponse.listCoverage()).contains(listCoverage) + } + + @Test + fun ofListCoverageRoundtrip() { + val jsonMapper = jsonMapper() + val userRetrieveVerifiedFollowersResponse = + UserRetrieveVerifiedFollowersResponse.ofListCoverage( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse.builder() + .hasNextPage(false) + .nextCursor("") + .addUser( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture("https://pbs.twimg.com/profile_banners/example.jpg") + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty("description", JsonValue.from("bar")) + .putAdditionalProperty("entities", JsonValue.from("bar")) + .build() + ) + .profileBannerUrl("https://pbs.twimg.com/profile_banners/example.jpg") + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture("https://pbs.twimg.com/profile_images/example.jpg") + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .diagnostic( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse.Diagnostic + .builder() + .complete(true) + .cursorFailureCount(0L) + .deadlineReached(true) + .duplicateCount(0L) + .failedStrategyCount(0L) + .malformedCount(0L) + .pagesFetched(0L) + .responseTruncated(true) + .resultLimitReached(true) + .returnedUsers(0L) + .stalledStrategyCount(0L) + .addStrategy( + UserRetrieveVerifiedFollowersResponse.UserListCoverageResponse + .Diagnostic + .Strategy + .builder() + .duplicateCount(0L) + .pagesFetched(0L) + .stopReason( + UserRetrieveVerifiedFollowersResponse + .UserListCoverageResponse + .Diagnostic + .Strategy + .StopReason + .CURSOR_FAILURE + ) + .strategy(1L) + .uniqueAdded(0L) + .build() + ) + .strategyCount(1L) + .uniqueUsers(0L) + .build() + ) + .build() + ) + + val roundtrippedUserRetrieveVerifiedFollowersResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(userRetrieveVerifiedFollowersResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUserRetrieveVerifiedFollowersResponse) + .isEqualTo(userRetrieveVerifiedFollowersResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val userRetrieveVerifiedFollowersResponse = + jsonMapper() + .convertValue( + testCase.value, + jacksonTypeRef(), + ) + + val e = + assertThrows { + userRetrieveVerifiedFollowersResponse.validate() + } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/ServiceParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/ServiceParamsTest.kt index 998a8f14..cf4b5ffb 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/ServiceParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/ServiceParamsTest.kt @@ -69,24 +69,39 @@ internal class ServiceParamsTest { .q("q") .advancedQuery("advancedQuery") .anyWords("anyWords") + .blueVerifiedOnly(true) .boundingBox("boundingBox") + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") - .limit(200L) + .limit(1L) .listId("listId") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetSearchParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .mode(TweetSearchParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .place("place") .placeCountry("placeCountry") .pointRadius("pointRadius") @@ -96,13 +111,18 @@ internal class ServiceParamsTest { .replies(TweetSearchParams.Replies.INCLUDE) .retweets(TweetSearchParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .putAdditionalHeader("Secret-Header", "42") .putAdditionalQueryParam("secret_query_param", "42") .build() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/EventServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/EventServiceAsyncTest.kt index 1718b20c..9089ccdb 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/EventServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/EventServiceAsyncTest.kt @@ -43,6 +43,7 @@ internal class EventServiceAsyncTest { EventListParams.builder() .cursor("cursor") .eventType(EventType.TWEET_NEW) + .keywordMonitorId("keywordMonitorId") .limit(1L) .monitorId("monitorId") .build() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/ExtractionServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/ExtractionServiceAsyncTest.kt index 1f7fdc9c..ec8f960a 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/ExtractionServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/ExtractionServiceAsyncTest.kt @@ -19,6 +19,7 @@ import com.x_twitter_scraper.api.models.extractions.ExtractionListParams import com.x_twitter_scraper.api.models.extractions.ExtractionRetrieveParams import com.x_twitter_scraper.api.models.extractions.ExtractionRunParams import java.time.LocalDate +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.parallel.ResourceLock @@ -38,7 +39,15 @@ internal class ExtractionServiceAsyncTest { val extractionFuture = extractionServiceAsync.retrieve( - ExtractionRetrieveParams.builder().id("id").cursor("cursor").limit(1L).build() + ExtractionRetrieveParams.builder() + .id("id") + .cursor("cursor") + .fieldStyle(ExtractionRetrieveParams.FieldStyle.SOURCE) + .includeRaw(true) + .limit(1L) + .outputMode(ExtractionRetrieveParams.OutputMode.COMPACT) + .outputPreset(ExtractionRetrieveParams.OutputPreset.NESTED) + .build() ) val extraction = extractionFuture.get() @@ -85,42 +94,107 @@ internal class ExtractionServiceAsyncTest { .toolType(ExtractionEstimateCostParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionEstimateCostParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionEstimateCostParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionEstimateCostParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionEstimateCostParams.QueryType.LATEST) .quotes(ExtractionEstimateCostParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionEstimateCostParams.RelationTarget.builder() + .relation( + ExtractionEstimateCostParams.RelationTarget.Relation + .COMMUNITY_MEMBERS + ) + .value("x") + .build() + ) .replies(ExtractionEstimateCostParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionEstimateCostParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionEstimateCostParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionEstimateCostParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() ) @@ -144,6 +218,24 @@ internal class ExtractionServiceAsyncTest { ExtractionExportResultsParams.builder() .id("id") .format(ExtractionExportResultsParams.Format.CSV) + .hasDescription(true) + .hasLocation(true) + .hasMedia(true) + .lang("lang") + .maxFollowers(0L) + .maxFollowing(0L) + .maxPosts(0L) + .minFollowers(0L) + .minFollowing(0L) + .minLikes(0L) + .minPosts(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .search("search") + .sinceDate(LocalDate.parse("2019-12-27")) + .untilDate(LocalDate.parse("2019-12-27")) + .verified(true) .build() ) @@ -164,45 +256,108 @@ internal class ExtractionServiceAsyncTest { val responseFuture = extractionServiceAsync.run( ExtractionRunParams.builder() + .dryRun(true) .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionRunParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionRunParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionRunParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionRunParams.QueryType.LATEST) .quotes(ExtractionRunParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) .replies(ExtractionRunParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionRunParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionRunParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionRunParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() ) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncTest.kt new file mode 100644 index 00000000..8a4accc6 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/support/AttachmentServiceAsyncTest.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.async.support + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.x_twitter_scraper.api.client.okhttp.XTwitterScraperOkHttpClientAsync +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class AttachmentServiceAsyncTest { + + @Test + fun download(wmRuntimeInfo: WireMockRuntimeInfo) { + val client = + XTwitterScraperOkHttpClientAsync.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .bearerToken("My Bearer Token") + .build() + val attachmentServiceAsync = client.support().attachments() + stubFor(get(anyUrl()).willReturn(ok().withBody("abc"))) + + val responseFuture = + attachmentServiceAsync.download( + AttachmentDownloadParams.builder() + .id("att_a1b2c3d4e5f6a1b2c3d4e5f6") + .range("bytes=0-1048575") + .build() + ) + + val response = responseFuture.get() + assertThat(response.body()).hasContent("abc") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/CommunityServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/CommunityServiceAsyncTest.kt index f0f7779f..5d47dc4e 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/CommunityServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/CommunityServiceAsyncTest.kt @@ -93,8 +93,22 @@ internal class CommunityServiceAsyncTest { communityServiceAsync.retrieveMembers( CommunityRetrieveMembersParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -114,7 +128,24 @@ internal class CommunityServiceAsyncTest { val paginatedUsersFuture = communityServiceAsync.retrieveModerators( - CommunityRetrieveModeratorsParams.builder().id("id").cursor("cursor").build() + CommunityRetrieveModeratorsParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) val paginatedUsers = paginatedUsersFuture.get() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/ListServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/ListServiceAsyncTest.kt index 6db1eb70..cb703516 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/ListServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/ListServiceAsyncTest.kt @@ -27,8 +27,22 @@ internal class ListServiceAsyncTest { listServiceAsync.retrieveFollowers( ListRetrieveFollowersParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -48,7 +62,25 @@ internal class ListServiceAsyncTest { val paginatedUsersFuture = listServiceAsync.retrieveMembers( - ListRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) val paginatedUsers = paginatedUsersFuture.get() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt index 9bce81d5..db0b0765 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt @@ -115,7 +115,25 @@ internal class TweetServiceAsyncTest { val paginatedUsersFuture = tweetServiceAsync.getFavoriters( - TweetGetFavoritersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetFavoritersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) val paginatedUsers = paginatedUsersFuture.get() @@ -137,35 +155,54 @@ internal class TweetServiceAsyncTest { TweetGetQuotesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetQuotesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetQuotesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetQuotesParams.Replies.INCLUDE) .retweets(TweetGetQuotesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -188,36 +225,61 @@ internal class TweetServiceAsyncTest { TweetGetRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") + .hasMediaOnly(true) + .includeOriginalPost(true) .inReplyToTweetId("inReplyToTweetId") .language("language") .limit(1L) + .maxDepth(1L) + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) - .mode(TweetGetRepliesParams.Mode.COMPLETE) + .minViews(0L) + .mode(TweetGetRepliesParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetRepliesParams.Replies.INCLUDE) .retweets(TweetGetRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) + .scope(TweetGetRepliesParams.Scope.ALL) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .sort(TweetGetRepliesParams.Sort.RELEVANCE) + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -237,7 +299,25 @@ internal class TweetServiceAsyncTest { val paginatedUsersFuture = tweetServiceAsync.getRetweeters( - TweetGetRetweetersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetRetweetersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) val paginatedUsers = paginatedUsersFuture.get() @@ -273,30 +353,45 @@ internal class TweetServiceAsyncTest { .build() val tweetServiceAsync = client.x().tweets() - val paginatedTweetsFuture = + val responseFuture = tweetServiceAsync.search( TweetSearchParams.builder() .q("q") .advancedQuery("advancedQuery") .anyWords("anyWords") + .blueVerifiedOnly(true) .boundingBox("boundingBox") + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") - .limit(200L) + .limit(1L) .listId("listId") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetSearchParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .mode(TweetSearchParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .place("place") .placeCountry("placeCountry") .pointRadius("pointRadius") @@ -306,17 +401,22 @@ internal class TweetServiceAsyncTest { .replies(TweetSearchParams.Replies.INCLUDE) .retweets(TweetSearchParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) - val paginatedTweets = paginatedTweetsFuture.get() - kotlin.test.assertNotNull(paginatedTweets) + val response = responseFuture.get() + kotlin.test.assertNotNull(response) } } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncTest.kt index ea1c7350..cb10acb3 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/UserServiceAsyncTest.kt @@ -89,19 +89,34 @@ internal class UserServiceAsyncTest { .build() val userServiceAsync = client.x().users() - val paginatedUsersFuture = + val responseFuture = userServiceAsync.retrieveFollowers( UserRetrieveFollowersParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - val paginatedUsers = paginatedUsersFuture.get() - kotlin.test.assertNotNull(paginatedUsers) + val response = responseFuture.get() + kotlin.test.assertNotNull(response) } @Test @@ -118,8 +133,22 @@ internal class UserServiceAsyncTest { userServiceAsync.retrieveFollowersYouKnow( UserRetrieveFollowersYouKnowParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -137,19 +166,34 @@ internal class UserServiceAsyncTest { .build() val userServiceAsync = client.x().users() - val paginatedUsersFuture = + val responseFuture = userServiceAsync.retrieveFollowing( UserRetrieveFollowingParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowingParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - val paginatedUsers = paginatedUsersFuture.get() - kotlin.test.assertNotNull(paginatedUsers) + val response = responseFuture.get() + kotlin.test.assertNotNull(response) } @Test @@ -167,32 +211,51 @@ internal class UserServiceAsyncTest { UserRetrieveLikesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveLikesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveLikesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveLikesParams.Replies.INCLUDE) .retweets(UserRetrieveLikesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -215,32 +278,51 @@ internal class UserServiceAsyncTest { UserRetrieveMediaParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMediaParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMediaParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMediaParams.Replies.INCLUDE) .retweets(UserRetrieveMediaParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -263,34 +345,53 @@ internal class UserServiceAsyncTest { UserRetrieveMentionsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMentionsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMentionsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMentionsParams.Replies.INCLUDE) .retweets(UserRetrieveMentionsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -313,33 +414,52 @@ internal class UserServiceAsyncTest { UserRetrieveRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveRepliesParams.Replies.INCLUDE) .retweets(UserRetrieveRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -359,7 +479,24 @@ internal class UserServiceAsyncTest { val paginatedUsersFuture = userServiceAsync.retrieveSearch( - UserRetrieveSearchParams.builder().q("q").cursor("cursor").build() + UserRetrieveSearchParams.builder() + .q("q") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) val paginatedUsers = paginatedUsersFuture.get() @@ -381,34 +518,53 @@ internal class UserServiceAsyncTest { UserRetrieveTweetsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveTweetsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveTweetsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveTweetsParams.Replies.INCLUDE) .retweets(UserRetrieveTweetsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -426,16 +582,33 @@ internal class UserServiceAsyncTest { .build() val userServiceAsync = client.x().users() - val paginatedUsersFuture = + val responseFuture = userServiceAsync.retrieveVerifiedFollowers( UserRetrieveVerifiedFollowersParams.builder() .id("id") + .after("after") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveVerifiedFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - val paginatedUsers = paginatedUsersFuture.get() - kotlin.test.assertNotNull(paginatedUsers) + val response = responseFuture.get() + kotlin.test.assertNotNull(response) } } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/EventServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/EventServiceTest.kt index e460d4f6..25d18a83 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/EventServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/EventServiceTest.kt @@ -42,6 +42,7 @@ internal class EventServiceTest { EventListParams.builder() .cursor("cursor") .eventType(EventType.TWEET_NEW) + .keywordMonitorId("keywordMonitorId") .limit(1L) .monitorId("monitorId") .build() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/ExtractionServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/ExtractionServiceTest.kt index 038545c8..86227f34 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/ExtractionServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/ExtractionServiceTest.kt @@ -19,6 +19,7 @@ import com.x_twitter_scraper.api.models.extractions.ExtractionListParams import com.x_twitter_scraper.api.models.extractions.ExtractionRetrieveParams import com.x_twitter_scraper.api.models.extractions.ExtractionRunParams import java.time.LocalDate +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.parallel.ResourceLock @@ -38,7 +39,15 @@ internal class ExtractionServiceTest { val extraction = extractionService.retrieve( - ExtractionRetrieveParams.builder().id("id").cursor("cursor").limit(1L).build() + ExtractionRetrieveParams.builder() + .id("id") + .cursor("cursor") + .fieldStyle(ExtractionRetrieveParams.FieldStyle.SOURCE) + .includeRaw(true) + .limit(1L) + .outputMode(ExtractionRetrieveParams.OutputMode.COMPACT) + .outputPreset(ExtractionRetrieveParams.OutputPreset.NESTED) + .build() ) kotlin.test.assertNotNull(extraction) @@ -83,42 +92,107 @@ internal class ExtractionServiceTest { .toolType(ExtractionEstimateCostParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionEstimateCostParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionEstimateCostParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionEstimateCostParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionEstimateCostParams.QueryType.LATEST) .quotes(ExtractionEstimateCostParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionEstimateCostParams.RelationTarget.builder() + .relation( + ExtractionEstimateCostParams.RelationTarget.Relation + .COMMUNITY_MEMBERS + ) + .value("x") + .build() + ) .replies(ExtractionEstimateCostParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionEstimateCostParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionEstimateCostParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionEstimateCostParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() ) @@ -141,6 +215,24 @@ internal class ExtractionServiceTest { ExtractionExportResultsParams.builder() .id("id") .format(ExtractionExportResultsParams.Format.CSV) + .hasDescription(true) + .hasLocation(true) + .hasMedia(true) + .lang("lang") + .maxFollowers(0L) + .maxFollowing(0L) + .maxPosts(0L) + .minFollowers(0L) + .minFollowing(0L) + .minLikes(0L) + .minPosts(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .search("search") + .sinceDate(LocalDate.parse("2019-12-27")) + .untilDate(LocalDate.parse("2019-12-27")) + .verified(true) .build() ) @@ -160,45 +252,108 @@ internal class ExtractionServiceTest { val response = extractionService.run( ExtractionRunParams.builder() + .dryRun(true) .toolType(ExtractionRunParams.ToolType.FOLLOWER_EXPLORER) .advancedQuery("min_faves:100") .anyWords("ChatGPT AI model") + .bioContains("bioContains") + .blueVerifiedOnly(true) .boundingBox("-74.1 40.6 -73.9 40.8") + .cardName("cardName") .cashtags("\$TSLA \$NVDA") + .collectionStrategy(ExtractionRunParams.CollectionStrategy.AUTO) .conversationId("1234567890") + .dedupeAcrossTargets(true) + .dedupeMode(ExtractionRunParams.DedupeMode.NONE) .exactPhrase("artificial intelligence") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("spam") .fromUser("nasa") + .geocode("geocode") .hashtags("#AI startups") + .hasLocation(true) + .hasMediaOnly(true) + .hasWebsite(true) + .includeOriginalPost(true) + .includeSearchTerms(true) + .includeTargetMetadata(true) .inReplyToTweetId("1234567890") .language("en") .listId("1234567890") + .locationContains("locationContains") + .maxDepth(1L) + .maxFollowers(0L) + .maxFollowing(0L) + .maxId("maxId") + .maxItemsPerTarget(1L) + .maxLikes(0L) + .maxPagesPerTarget(1L) + .maxPosts(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(ExtractionRunParams.MediaType.IMAGES) .mentioning("example_user") + .minAccountAgeDays(0L) + .minBookmarks(0L) .minFaves(10L) + .minFollowers(0L) + .minFollowing(0L) + .minPosts(0L) .minQuotes(2L) .minReplies(3L) .minRetweets(5L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) + .overlapMode(true) .place("96683cc9126741d1") .placeCountry("US") .pointRadius("-73.99 40.73 25mi") + .queryType(ExtractionRunParams.QueryType.LATEST) .quotes(ExtractionRunParams.Quotes.INCLUDE) .quotesOfTweetId("1234567890") + .addRelationTarget( + ExtractionRunParams.RelationTarget.builder() + .relation(ExtractionRunParams.RelationTarget.Relation.COMMUNITY_MEMBERS) + .value("x") + .build() + ) .replies(ExtractionRunParams.Replies.INCLUDE) .resultsLimit(1000L) .retweets(ExtractionRunParams.Retweets.EXCLUDE) .retweetsOfTweetId("1234567890") + .safe(true) + .scope(ExtractionRunParams.Scope.ALL) + .addSearchQuery("string") .searchQuery("AI trends 2025") .sinceDate(LocalDate.parse("2025-01-01")) + .sinceId("sinceId") + .sinceTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .sort(ExtractionRunParams.Sort.RELEVANCE) + .source("source") + .startCursor("x") .targetCommunityId("1500000000000000000") + .addTargetCommunityId("string") .targetListId("1234567890") + .addTargetListId("string") + .addTarget("string") .targetSpaceId("1vOGwMdBqpwGB") .targetTweetId("1234567890") + .addTargetTweetId("string") .targetUsername("elonmusk") + .addTargetUsername("string") .toUser("openai") .untilDate(LocalDate.parse("2025-12-31")) + .untilTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .url("example.com") + .usernameContains("usernameContains") .verifiedOnly(false) + .verifiedType("verifiedType") + .within("within") + .withinTime("withinTime") .build() ) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceTest.kt new file mode 100644 index 00000000..256a21d5 --- /dev/null +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/support/AttachmentServiceTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.x_twitter_scraper.api.services.blocking.support + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.x_twitter_scraper.api.client.okhttp.XTwitterScraperOkHttpClient +import com.x_twitter_scraper.api.models.support.attachments.AttachmentDownloadParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class AttachmentServiceTest { + + @Test + fun download(wmRuntimeInfo: WireMockRuntimeInfo) { + val client = + XTwitterScraperOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .bearerToken("My Bearer Token") + .build() + val attachmentService = client.support().attachments() + stubFor(get(anyUrl()).willReturn(ok().withBody("abc"))) + + val response = + attachmentService.download( + AttachmentDownloadParams.builder() + .id("att_a1b2c3d4e5f6a1b2c3d4e5f6") + .range("bytes=0-1048575") + .build() + ) + + assertThat(response.body()).hasContent("abc") + } +} diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/CommunityServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/CommunityServiceTest.kt index aa116a24..adfae772 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/CommunityServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/CommunityServiceTest.kt @@ -90,8 +90,22 @@ internal class CommunityServiceTest { communityService.retrieveMembers( CommunityRetrieveMembersParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -110,7 +124,24 @@ internal class CommunityServiceTest { val paginatedUsers = communityService.retrieveModerators( - CommunityRetrieveModeratorsParams.builder().id("id").cursor("cursor").build() + CommunityRetrieveModeratorsParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) kotlin.test.assertNotNull(paginatedUsers) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/ListServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/ListServiceTest.kt index 8fb6353a..efb5ce49 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/ListServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/ListServiceTest.kt @@ -27,8 +27,22 @@ internal class ListServiceTest { listService.retrieveFollowers( ListRetrieveFollowersParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -47,7 +61,25 @@ internal class ListServiceTest { val paginatedUsers = listService.retrieveMembers( - ListRetrieveMembersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + ListRetrieveMembersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) kotlin.test.assertNotNull(paginatedUsers) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt index 5c3eaae0..cb1ed14e 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt @@ -110,7 +110,25 @@ internal class TweetServiceTest { val paginatedUsers = tweetService.getFavoriters( - TweetGetFavoritersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetFavoritersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) kotlin.test.assertNotNull(paginatedUsers) @@ -131,35 +149,54 @@ internal class TweetServiceTest { TweetGetQuotesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetQuotesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetQuotesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetQuotesParams.Replies.INCLUDE) .retweets(TweetGetQuotesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -181,36 +218,61 @@ internal class TweetServiceTest { TweetGetRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeOriginalAuthor(true) + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") + .hasMediaOnly(true) + .includeOriginalPost(true) .inReplyToTweetId("inReplyToTweetId") .language("language") .limit(1L) + .maxDepth(1L) + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetGetRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) - .mode(TweetGetRepliesParams.Mode.COMPLETE) + .minViews(0L) + .mode(TweetGetRepliesParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(TweetGetRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(TweetGetRepliesParams.Replies.INCLUDE) .retweets(TweetGetRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) + .scope(TweetGetRepliesParams.Scope.ALL) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .sort(TweetGetRepliesParams.Sort.RELEVANCE) + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -229,7 +291,25 @@ internal class TweetServiceTest { val paginatedUsers = tweetService.getRetweeters( - TweetGetRetweetersParams.builder().id("id").cursor("cursor").pageSize(20L).build() + TweetGetRetweetersParams.builder() + .id("id") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) kotlin.test.assertNotNull(paginatedUsers) @@ -263,30 +343,45 @@ internal class TweetServiceTest { .build() val tweetService = client.x().tweets() - val paginatedTweets = + val response = tweetService.search( TweetSearchParams.builder() .q("q") .advancedQuery("advancedQuery") .anyWords("anyWords") + .blueVerifiedOnly(true) .boundingBox("boundingBox") + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") - .limit(200L) + .limit(1L) .listId("listId") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(TweetSearchParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .mode(TweetSearchParams.Mode.STANDARD) + .nativeRetweets(true) + .near("near") + .news(true) .place("place") .placeCountry("placeCountry") .pointRadius("pointRadius") @@ -296,16 +391,21 @@ internal class TweetServiceTest { .replies(TweetSearchParams.Replies.INCLUDE) .retweets(TweetSearchParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) - kotlin.test.assertNotNull(paginatedTweets) + kotlin.test.assertNotNull(response) } } diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceTest.kt index 9480d53a..5eb7fb4b 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/UserServiceTest.kt @@ -86,18 +86,33 @@ internal class UserServiceTest { .build() val userService = client.x().users() - val paginatedUsers = + val response = userService.retrieveFollowers( UserRetrieveFollowersParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - kotlin.test.assertNotNull(paginatedUsers) + kotlin.test.assertNotNull(response) } @Test @@ -114,8 +129,22 @@ internal class UserServiceTest { userService.retrieveFollowersYouKnow( UserRetrieveFollowersYouKnowParams.builder() .id("id") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) @@ -132,18 +161,33 @@ internal class UserServiceTest { .build() val userService = client.x().users() - val paginatedUsers = + val response = userService.retrieveFollowing( UserRetrieveFollowingParams.builder() .id("id") .after("after") + .bioContains("bioContains") .cursor("cursor") - .limit(0L) + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveFollowingParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - kotlin.test.assertNotNull(paginatedUsers) + kotlin.test.assertNotNull(response) } @Test @@ -161,32 +205,51 @@ internal class UserServiceTest { UserRetrieveLikesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveLikesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveLikesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveLikesParams.Replies.INCLUDE) .retweets(UserRetrieveLikesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -208,32 +271,51 @@ internal class UserServiceTest { UserRetrieveMediaParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMediaParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMediaParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMediaParams.Replies.INCLUDE) .retweets(UserRetrieveMediaParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -255,34 +337,53 @@ internal class UserServiceTest { UserRetrieveMentionsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveMentionsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveMentionsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveMentionsParams.Replies.INCLUDE) .retweets(UserRetrieveMentionsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") .sinceTime("sinceTime") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .untilTime("untilTime") .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -304,33 +405,52 @@ internal class UserServiceTest { UserRetrieveRepliesParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveRepliesParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveRepliesParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveRepliesParams.Replies.INCLUDE) .retweets(UserRetrieveRepliesParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -349,7 +469,24 @@ internal class UserServiceTest { val paginatedUsers = userService.retrieveSearch( - UserRetrieveSearchParams.builder().q("q").cursor("cursor").build() + UserRetrieveSearchParams.builder() + .q("q") + .bioContains("bioContains") + .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") + .build() ) kotlin.test.assertNotNull(paginatedUsers) @@ -370,34 +507,53 @@ internal class UserServiceTest { UserRetrieveTweetsParams.builder() .id("id") .anyWords("anyWords") + .blueVerifiedOnly(true) + .cardName("cardName") .cashtags("cashtags") .conversationId("conversationId") .cursor("cursor") .exactPhrase("exactPhrase") + .excludeSource("excludeSource") .excludeWords("excludeWords") .fromUser("fromUser") + .geocode("geocode") .hashtags("hashtags") .includeParentTweet(true) .includeReplies(true) .inReplyToTweetId("inReplyToTweetId") .language("language") + .maxFaves(0L) + .maxId("maxId") + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) .mediaType(UserRetrieveTweetsParams.MediaType.IMAGES) .mentioning("mentioning") + .minBookmarks(0L) .minFaves(0L) .minQuotes(0L) .minReplies(0L) .minRetweets(0L) + .minViews(0L) + .nativeRetweets(true) + .near("near") + .news(true) .pageSize(1L) .quotes(UserRetrieveTweetsParams.Quotes.INCLUDE) .quotesOfTweetId("quotesOfTweetId") .replies(UserRetrieveTweetsParams.Replies.INCLUDE) .retweets(UserRetrieveTweetsParams.Retweets.INCLUDE) .retweetsOfTweetId("retweetsOfTweetId") + .safe(true) .sinceDate(LocalDate.parse("2019-12-27")) + .sinceId("sinceId") + .source("source") .toUser("toUser") .untilDate(LocalDate.parse("2019-12-27")) .url("url") .verifiedOnly(true) + .within("within") + .withinTime("withinTime") .build() ) @@ -414,15 +570,32 @@ internal class UserServiceTest { .build() val userService = client.x().users() - val paginatedUsers = + val response = userService.retrieveVerifiedFollowers( UserRetrieveVerifiedFollowersParams.builder() .id("id") + .after("after") + .bioContains("bioContains") .cursor("cursor") + .hasLocation(true) + .hasWebsite(true) + .limit(1L) + .locationContains("locationContains") + .maxFollowers(0L) + .maxFollowing(0L) + .maxStatuses(0L) + .minAccountAgeDays(0L) + .minFollowers(0L) + .minFollowing(0L) + .minStatuses(0L) + .mode(UserRetrieveVerifiedFollowersParams.Mode.STANDARD) .pageSize(20L) + .usernameContains("usernameContains") + .verifiedOnly(true) + .verifiedType("verifiedType") .build() ) - kotlin.test.assertNotNull(paginatedUsers) + kotlin.test.assertNotNull(response) } } diff --git a/x-twitter-scraper-java-proguard-test/src/test/kotlin/com/x_twitter_scraper/api/proguard/ProGuardCompatibilityTest.kt b/x-twitter-scraper-java-proguard-test/src/test/kotlin/com/x_twitter_scraper/api/proguard/ProGuardCompatibilityTest.kt index 3336f1ab..94c3e4d2 100644 --- a/x-twitter-scraper-java-proguard-test/src/test/kotlin/com/x_twitter_scraper/api/proguard/ProGuardCompatibilityTest.kt +++ b/x-twitter-scraper-java-proguard-test/src/test/kotlin/com/x_twitter_scraper/api/proguard/ProGuardCompatibilityTest.kt @@ -11,15 +11,22 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.x_twitter_scraper.api.client.XTwitterScraperClientImpl import com.x_twitter_scraper.api.client.okhttp.XTwitterScraperOkHttpClient import com.x_twitter_scraper.api.core.ClientOptions +import com.x_twitter_scraper.api.core.JsonValue import com.x_twitter_scraper.api.core.RequestOptions import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.HttpClient import com.x_twitter_scraper.api.core.http.HttpRequest import com.x_twitter_scraper.api.core.http.HttpResponse import com.x_twitter_scraper.api.core.jsonMapper +import com.x_twitter_scraper.api.models.ContentDisclosure +import com.x_twitter_scraper.api.models.EmbeddedTweet import com.x_twitter_scraper.api.models.EventType -import com.x_twitter_scraper.api.models.compose.ComposeCreateResponse +import com.x_twitter_scraper.api.models.PaginatedTweets +import com.x_twitter_scraper.api.models.SearchTweet +import com.x_twitter_scraper.api.models.TweetMedia +import com.x_twitter_scraper.api.models.UserProfile import com.x_twitter_scraper.api.models.x.tweets.TweetAuthor +import com.x_twitter_scraper.api.models.x.tweets.TweetSearchResponse import java.io.ByteArrayInputStream import java.io.InputStream import java.util.concurrent.CompletableFuture @@ -39,7 +46,7 @@ internal class ProGuardCompatibilityTest { test.client() test.productionDeserialization() test.tweetAuthorRoundtrip() - test.composeCreateResponseRoundtrip() + test.tweetSearchResponseRoundtrip() test.eventTypeRoundtrip() } } @@ -116,328 +123,892 @@ internal class ProGuardCompatibilityTest { } } - fun composeCreateResponseRoundtrip() { + fun tweetSearchResponseRoundtrip() { val jsonMapper = jsonMapper() - val composeCreateResponse = - ComposeCreateResponse.ofPrepareResult( - ComposeCreateResponse.ComposePrepareResult.builder() - .contentRules( - listOf( - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ComposeCreateResponse.ComposePrepareResult.ContentRule.builder() - .rule("rule") - .build(), - ) - ) - .engagementMultipliers( - listOf( - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ComposeCreateResponse.ComposePrepareResult.EngagementMultiplier - .builder() - .action("action") - .build(), - ) - ) - .engagementVelocity("engagementVelocity") - .followUpQuestions(listOf("string", "string", "string", "string")) - .intentUrl("https://example.com") - .nextStep("nextStep") - .radarRecommendations( - listOf( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation.builder() - .endpoint("endpoint") - .guidance("guidance") - .source( - ComposeCreateResponse.ComposePrepareResult.RadarRecommendation - .Source - .REDDIT - ) - .useFor("useFor") - .build(), - ) - ) - .scorerWeights( - listOf( - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ComposeCreateResponse.ComposePrepareResult.ScorerWeight.builder() - .context("context") - .signal("signal") - .build(), - ) - ) - .source("source") - .topPenalties(listOf("string", "string", "string", "string")) - .addSavedStyle( - ComposeCreateResponse.ComposePrepareResult.SavedStyle.builder() - .tweetCount(0L) - .username("username") + val tweetSearchResponse = + TweetSearchResponse.ofPaginatedTweets( + PaginatedTweets.builder() + .hasNextPage(true) + .nextCursor("DAACCgACGRElMJcAAA") + .addTweet( + SearchTweet.builder() + .id("1234567890") + .bookmarkCount(2L) + .likeCount(42L) + .quoteCount(1L) + .replyCount(3L) + .retweetCount(5L) + .text("Just launched our new feature!") + .viewCount(1500L) + .article( + SearchTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage("profileDescriptionLanguage") + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + SearchTweet.Card.builder() + .id("id") + .bindingValues( + SearchTweet.Card.BindingValues.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + SearchTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("1234567890") + .createdAt("2025-01-15T12:00:00Z") + .addDisplayTextRange(0L) + .addDisplayTextRange(31L) + .edit( + SearchTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + SearchTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("1234567890") + .inReplyToUserId("9876543210") + .inReplyToUsername("example_user") + .isLimitedReply(false) + .isNoteTweet(false) + .isQuoteStatus(false) + .isReply(false) + .isTranslatable(true) + .lang("en") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf("h" to 0, "resize" to "resize", "w" to 0) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + SearchTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + SearchTweet.NoteTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .isExpandable(true) + .addRichtextTag( + SearchTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + SearchTweet.Place.builder() + .id("id") + .boundingBox( + SearchTweet.Place.BoundingBox.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + SearchTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .quotedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .retweetedTweet( + EmbeddedTweet.builder() + .id("id") + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .text("text") + .viewCount(0L) + .article( + EmbeddedTweet.Article.builder() + .id("id") + .coverMediaUrl("coverMediaUrl") + .previewText("previewText") + .title("title") + .build() + ) + .author( + UserProfile.builder() + .id("9876543210") + .name("Elon Musk") + .username("elonmusk") + .affiliatesHighlightedLabel( + UserProfile.AffiliatesHighlightedLabel.builder() + .badgeUrl("badgeUrl") + .description("description") + .url("url") + .urlType("urlType") + .userLabelDisplayType("userLabelDisplayType") + .userLabelType("userLabelType") + .build() + ) + .automatedBy("example_user") + .businessAccountAffiliatesCount(0L) + .communityRole("Member") + .coverPicture( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .createdAt("2009-06-02T20:12:29Z") + .creatorSubscriptionsCount(0L) + .description("CEO of Tesla, SpaceX, and X") + .favouritesCount(18000L) + .followers(150000000L) + .following(500L) + .hasCustomTimelines(true) + .hasGraduatedAccess(true) + .hasHiddenSubscriptionsOnProfile(true) + .highlightsInfo( + UserProfile.HighlightsInfo.builder() + .canHighlightTweets(true) + .highlightedTweets("highlightedTweets") + .build() + ) + .identityVerification( + UserProfile.IdentityVerification.builder() + .description("description") + .isIdentityVerified(true) + .verifiedSinceMsec("verifiedSinceMsec") + .build() + ) + .isAutomated(false) + .isBlueVerified(true) + .isProfileTranslatable(true) + .isTranslator(false) + .isVerified(true) + .location("Austin, TX") + .mediaCount(1200L) + .parodyCommentaryFanLabel("parodyCommentaryFanLabel") + .addPinnedTweetId("1234567890") + .possiblySensitive(false) + .profileBio( + UserProfile.ProfileBio.builder() + .putAdditionalProperty( + "description", + JsonValue.from("bar"), + ) + .putAdditionalProperty( + "entities", + JsonValue.from("bar"), + ) + .build() + ) + .profileBannerUrl( + "https://pbs.twimg.com/profile_banners/example.jpg" + ) + .profileDescriptionLanguage( + "profileDescriptionLanguage" + ) + .profileImageShape("profileImageShape") + .profileInterstitialType("profileInterstitialType") + .profilePicture( + "https://pbs.twimg.com/profile_images/example.jpg" + ) + .profileSortEnabled(true) + .profileTranslatorType("profileTranslatorType") + .protected_(false) + .statusesCount(35000L) + .superFollowEligible(true) + .unavailable(false) + .unavailableReason("suspended") + .url("https://xquik.com") + .verified(true) + .verifiedType("Business") + .addWithheldInCountry("DE") + .build() + ) + .card( + EmbeddedTweet.Card.builder() + .id("id") + .bindingValues( + EmbeddedTweet.Card.BindingValues.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .name("name") + .url("url") + .build() + ) + .communityNote( + EmbeddedTweet.CommunityNote.builder() + .id("id") + .destinationUrl("destinationUrl") + .footer("footer") + .shortTitle("shortTitle") + .subtitle("subtitle") + .title("title") + .visualStyle("visualStyle") + .build() + ) + .contentDisclosure( + ContentDisclosure.builder() + .advertising( + ContentDisclosure.Advertising.builder() + .isPaidPromotion(true) + .build() + ) + .aiGenerated( + ContentDisclosure.AiGenerated.builder() + .detectionSource("UserDeclared") + .hasAiGeneratedMedia(true) + .build() + ) + .build() + ) + .conversationId("conversationId") + .createdAt("createdAt") + .addDisplayTextRange(0L) + .edit( + EmbeddedTweet.Edit.builder() + .editableUntilMsecs("editableUntilMsecs") + .addEditTweetId("string") + .build() + ) + .entities( + EmbeddedTweet.Entities.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .inReplyToId("inReplyToId") + .inReplyToUserId("inReplyToUserId") + .inReplyToUsername("inReplyToUsername") + .isLimitedReply(true) + .isNoteTweet(true) + .isQuoteStatus(true) + .isReply(true) + .isTranslatable(true) + .lang("lang") + .addMedia( + TweetMedia.builder() + .mediaUrl("mediaUrl") + .type(TweetMedia.Type.PHOTO) + .url("url") + .id("id") + .allowDownload(true) + .altText("altText") + .addAspectRatio(0L) + .availabilityStatus("availabilityStatus") + .displayUrl("displayUrl") + .durationMillis(0L) + .expandedUrl("expandedUrl") + .faceRects( + TweetMedia.FaceRects.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + listOf( + mapOf( + "h" to 0, + "w" to 0, + "x" to 0, + "y" to 0, + ) + ) + ), + ) + .build() + ) + .addFocusRect( + TweetMedia.FocusRect.builder() + .h(0L) + .w(0L) + .x(0L) + .y(0L) + .build() + ) + .height(0L) + .addIndex(0L) + .mediaKey("mediaKey") + .monetizable(true) + .sizes( + TweetMedia.Sizes.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "h" to 0, + "resize" to "resize", + "w" to 0, + ) + ), + ) + .build() + ) + .addVideoVariant( + TweetMedia.VideoVariant.builder() + .contentType("contentType") + .url("url") + .bitrate(0L) + .build() + ) + .width(0L) + .build() + ) + .noteTweet( + EmbeddedTweet.NoteTweet.builder() + .text("text") + .id("id") + .entities( + EmbeddedTweet.NoteTweet.Entities.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .isExpandable(true) + .addRichtextTag( + EmbeddedTweet.NoteTweet.RichtextTag.builder() + .fromIndex(0L) + .toIndex(0L) + .addType("string") + .build() + ) + .build() + ) + .place( + EmbeddedTweet.Place.builder() + .id("id") + .boundingBox( + EmbeddedTweet.Place.BoundingBox.builder() + .putAdditionalProperty( + "foo", + JsonValue.from("bar"), + ) + .build() + ) + .country("country") + .countryCode("countryCode") + .fullName("fullName") + .name("name") + .placeType("placeType") + .url("url") + .build() + ) + .possiblySensitive(true) + .previousCounts( + EmbeddedTweet.PreviousCounts.builder() + .bookmarkCount(0L) + .likeCount(0L) + .quoteCount(0L) + .replyCount(0L) + .retweetCount(0L) + .build() + ) + .source("source") + .type("type") + .url("url") + .viewState("viewState") + .build() + ) + .source("Twitter Web App") + .type("tweet") + .url("https://x.com/example_user/status/1234567890") + .viewState("viewState") .build() ) - .styleNote("styleNote") - .addStyleTweet("string") .build() ) - val roundtrippedComposeCreateResponse = + val roundtrippedTweetSearchResponse = jsonMapper.readValue( - jsonMapper.writeValueAsString(composeCreateResponse), - jacksonTypeRef(), + jsonMapper.writeValueAsString(tweetSearchResponse), + jacksonTypeRef(), ) - check(roundtrippedComposeCreateResponse == composeCreateResponse) { - "ComposeCreateResponse roundtrip changed" + check(roundtrippedTweetSearchResponse == tweetSearchResponse) { + "TweetSearchResponse roundtrip changed" } } From d9ec6821d543fce1a888f4d0582c3ec24f3c0564 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:56:18 +0000 Subject: [PATCH 2/8] feat(api): api update --- .stats.yml | 4 +- .../credits/CreditTopupBalanceParams.kt | 4 +- .../credits/CreditTopupBalanceResponse.kt | 12 +-- .../guestwallets/GuestWalletCreateParams.kt | 6 +- .../guestwallets/GuestWalletCreateResponse.kt | 77 ++++++++---------- .../GuestWalletRetrieveStatusParams.kt | 6 +- .../guestwallets/GuestWalletTopupParams.kt | 6 +- .../guestwallets/GuestWalletTopupResponse.kt | 79 ++++++++----------- .../api/services/async/CreditServiceAsync.kt | 4 +- .../services/async/GuestWalletServiceAsync.kt | 18 ++--- .../api/services/blocking/CreditService.kt | 4 +- .../services/blocking/GuestWalletService.kt | 18 ++--- .../credits/CreditTopupBalanceResponseTest.kt | 16 ++-- .../GuestWalletCreateResponseTest.kt | 9 ++- .../GuestWalletTopupResponseTest.kt | 9 ++- 15 files changed, 127 insertions(+), 145 deletions(-) diff --git a/.stats.yml b/.stats.yml index f192b6e9..7f3e8c1a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 124 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-7bcac1557175956021a62532c55ce29b8822b5a41d0d59ae67787a6326c0ef8a.yml -openapi_spec_hash: 04e28f19097d9b59119101492100c4ab +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-f688ec225c65653bb6ba3f51fe643fc69e329fa93cdc13117060d56de13823cc.yml +openapi_spec_hash: 4ad185ef874409f7cd359beed23f448f config_hash: 217d68ca6999ad03cfc4922e9658297e diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceParams.kt index 9def0502..d14c1818 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceParams.kt @@ -24,8 +24,8 @@ import java.util.Objects import java.util.Optional /** - * Create a Stripe Checkout session only after the user confirms. The request never completes - * payment or adds credits by itself. + * Create a hosted checkout only after the user confirms. The request never completes payment or + * adds credits. */ class CreditTopupBalanceParams private constructor( diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponse.kt index 65e45086..32e1b602 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponse.kt @@ -36,7 +36,7 @@ private constructor( ) : this(redirectUrl, url, mutableMapOf()) /** - * Stable first-party Xquik redirect URL for the active Stripe Checkout session. + * Stable Xquik redirect URL for the active checkout. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -44,8 +44,8 @@ private constructor( fun redirectUrl(): String = redirectUrl.getRequired("redirect_url") /** - * Same stable first-party Xquik redirect URL as redirect_url. The response never exposes a raw - * Stripe Checkout URL. + * Same stable Xquik redirect URL as redirect_url. The response never exposes the hosted + * checkout URL. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -108,7 +108,7 @@ private constructor( additionalProperties = creditTopupBalanceResponse.additionalProperties.toMutableMap() } - /** Stable first-party Xquik redirect URL for the active Stripe Checkout session. */ + /** Stable Xquik redirect URL for the active checkout. */ fun redirectUrl(redirectUrl: String) = redirectUrl(JsonField.of(redirectUrl)) /** @@ -121,8 +121,8 @@ private constructor( fun redirectUrl(redirectUrl: JsonField) = apply { this.redirectUrl = redirectUrl } /** - * Same stable first-party Xquik redirect URL as redirect_url. The response never exposes a - * raw Stripe Checkout URL. + * Same stable Xquik redirect URL as redirect_url. The response never exposes the hosted + * checkout URL. */ fun url(url: String) = url(JsonField.of(url)) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt index e98e8c97..cc4884a3 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt @@ -23,10 +23,8 @@ import java.util.Collections import java.util.Objects /** - * Create a one-use Stripe-hosted checkout after the user explicitly confirms a $10-$250 USD amount. - * This request creates no charge by itself. The user opens checkout_url on Stripe. This endpoint - * returns the paid-read API key without requiring an Xquik account, email, dashboard, or Xquik web - * page. An idempotent replay returns the same key. + * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no + * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. */ class GuestWalletCreateParams private constructor( diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponse.kt index 90be4209..220d4c66 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponse.kt @@ -34,7 +34,7 @@ private constructor( private val credentialNotice: JsonValue, private val credits: JsonField, private val expiresAt: JsonField, - private val instructions: JsonValue, + private val instructions: JsonField, private val pollAfterSeconds: JsonValue, private val purchaseId: JsonField, private val requiresUserInteraction: JsonValue, @@ -66,7 +66,9 @@ private constructor( @JsonProperty("expires_at") @ExcludeMissing expiresAt: JsonField = JsonMissing.of(), - @JsonProperty("instructions") @ExcludeMissing instructions: JsonValue = JsonMissing.of(), + @JsonProperty("instructions") + @ExcludeMissing + instructions: JsonField = JsonMissing.of(), @JsonProperty("poll_after_seconds") @ExcludeMissing pollAfterSeconds: JsonValue = JsonMissing.of(), @@ -135,7 +137,7 @@ private constructor( fun authorization(): Authorization = authorization.getRequired("authorization") /** - * Raw Stripe-hosted checkout URL for user interaction. + * Hosted checkout URL for user interaction. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -172,15 +174,12 @@ private constructor( fun expiresAt(): OffsetDateTime = expiresAt.getRequired("expires_at") /** - * Expected to always return the following: - * ```java - * JsonValue.from("Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending.") - * ``` + * Hosted checkout and status polling instructions. * - * However, this method can be useful for debugging and logging (e.g. if the server responded - * with an unexpected value). + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - @JsonProperty("instructions") @ExcludeMissing fun _instructions(): JsonValue = instructions + fun instructions(): String = instructions.getRequired("instructions") /** * Wait at least this long before polling status_url. @@ -287,6 +286,15 @@ private constructor( @ExcludeMissing fun _expiresAt(): JsonField = expiresAt + /** + * Returns the raw JSON value of [instructions]. + * + * Unlike [instructions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("instructions") + @ExcludeMissing + fun _instructions(): JsonField = instructions + /** * Returns the raw JSON value of [purchaseId]. * @@ -333,6 +341,7 @@ private constructor( * .checkoutUrl() * .credits() * .expiresAt() + * .instructions() * .purchaseId() * .status() * .walletId() @@ -355,10 +364,7 @@ private constructor( ) private var credits: JsonField? = null private var expiresAt: JsonField? = null - private var instructions: JsonValue = - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) + private var instructions: JsonField? = null private var pollAfterSeconds: JsonValue = JsonValue.from(2) private var purchaseId: JsonField? = null private var requiresUserInteraction: JsonValue = JsonValue.from(true) @@ -443,7 +449,7 @@ private constructor( this.authorization = authorization } - /** Raw Stripe-hosted checkout URL for user interaction. */ + /** Hosted checkout URL for user interaction. */ fun checkoutUrl(checkoutUrl: String) = checkoutUrl(JsonField.of(checkoutUrl)) /** @@ -494,19 +500,19 @@ private constructor( */ fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + /** Hosted checkout and status polling instructions. */ + fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending.") - * ``` + * Sets [Builder.instructions] to an arbitrary JSON value. * + * You should usually call [Builder.instructions] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun instructions(instructions: JsonValue) = apply { this.instructions = instructions } + fun instructions(instructions: JsonField) = apply { + this.instructions = instructions + } /** * Sets the field to an arbitrary JSON value. @@ -617,6 +623,7 @@ private constructor( * .checkoutUrl() * .credits() * .expiresAt() + * .instructions() * .purchaseId() * .status() * .walletId() @@ -634,7 +641,7 @@ private constructor( credentialNotice, checkRequired("credits", credits), checkRequired("expiresAt", expiresAt), - instructions, + checkRequired("instructions", instructions), pollAfterSeconds, checkRequired("purchaseId", purchaseId), requiresUserInteraction, @@ -685,16 +692,7 @@ private constructor( } credits() expiresAt() - _instructions().let { - if ( - it != - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) - ) { - throw XTwitterScraperInvalidDataException("'instructions' is invalid, received $it") - } - } + instructions() _pollAfterSeconds().let { if (it != JsonValue.from(2)) { throw XTwitterScraperInvalidDataException( @@ -752,16 +750,7 @@ private constructor( } + (if (credits.asKnown().isPresent) 1 else 0) + (if (expiresAt.asKnown().isPresent) 1 else 0) + - instructions.let { - if ( - it == - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) - ) - 1 - else 0 - } + + (if (instructions.asKnown().isPresent) 1 else 0) + pollAfterSeconds.let { if (it == JsonValue.from(2)) 1 else 0 } + (if (purchaseId.asKnown().isPresent) 1 else 0) + requiresUserInteraction.let { if (it == JsonValue.from(true)) 1 else 0 } + diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletRetrieveStatusParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletRetrieveStatusParams.kt index b49862ce..cd686a23 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletRetrieveStatusParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletRetrieveStatusParams.kt @@ -12,9 +12,9 @@ import com.x_twitter_scraper.api.core.http.QueryParams import java.util.Objects /** - * Poll after Stripe payment. Use usable to decide whether paid reads can run. An active wallet can - * remain usable while a top-up is pending. A new wallet becomes usable only after verified webhook - * fulfillment. Send the guest key as Authorization: Bearer. + * Poll after payment. Use usable to decide whether paid reads can run. An active wallet can remain + * usable while a top-up is pending. A new wallet becomes usable only after payment is verified. + * Send the guest key as Authorization: Bearer. */ class GuestWalletRetrieveStatusParams private constructor( diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupParams.kt index f11eee86..197c9c8c 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupParams.kt @@ -23,9 +23,9 @@ import java.util.Collections import java.util.Objects /** - * Create a one-use Stripe-hosted checkout for an existing paid-read guest key after the user - * explicitly confirms a $10-$250 USD amount. The key remains the same. This request creates no - * charge by itself and never redirects through an Xquik web page. + * Create a one-use hosted checkout after the user confirms a $10-$250 USD amount for an existing + * paid-read guest key. The key remains the same. This request creates no charge and never redirects + * through Xquik. */ class GuestWalletTopupParams private constructor( diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponse.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponse.kt index b61f2355..ef82dc20 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponse.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponse.kt @@ -23,7 +23,7 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Pending Stripe checkout and guest wallet purchase details. */ +/** Pending hosted checkout and guest wallet purchase details. */ class GuestWalletTopupResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -32,7 +32,7 @@ private constructor( private val checkoutUrl: JsonField, private val credits: JsonField, private val expiresAt: JsonField, - private val instructions: JsonValue, + private val instructions: JsonField, private val pollAfterSeconds: JsonValue, private val purchaseId: JsonField, private val requiresUserInteraction: JsonValue, @@ -60,7 +60,9 @@ private constructor( @JsonProperty("expires_at") @ExcludeMissing expiresAt: JsonField = JsonMissing.of(), - @JsonProperty("instructions") @ExcludeMissing instructions: JsonValue = JsonMissing.of(), + @JsonProperty("instructions") + @ExcludeMissing + instructions: JsonField = JsonMissing.of(), @JsonProperty("poll_after_seconds") @ExcludeMissing pollAfterSeconds: JsonValue = JsonMissing.of(), @@ -121,7 +123,7 @@ private constructor( fun amount(): GuestWalletAmount = amount.getRequired("amount") /** - * Raw Stripe-hosted checkout URL for user interaction. + * Hosted checkout URL for user interaction. * * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -145,15 +147,12 @@ private constructor( fun expiresAt(): OffsetDateTime = expiresAt.getRequired("expires_at") /** - * Expected to always return the following: - * ```java - * JsonValue.from("Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending.") - * ``` + * Hosted checkout and status polling instructions. * - * However, this method can be useful for debugging and logging (e.g. if the server responded - * with an unexpected value). + * @throws XTwitterScraperInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - @JsonProperty("instructions") @ExcludeMissing fun _instructions(): JsonValue = instructions + fun instructions(): String = instructions.getRequired("instructions") /** * Wait at least this long before polling status_url. @@ -266,6 +265,15 @@ private constructor( @ExcludeMissing fun _expiresAt(): JsonField = expiresAt + /** + * Returns the raw JSON value of [instructions]. + * + * Unlike [instructions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("instructions") + @ExcludeMissing + fun _instructions(): JsonField = instructions + /** * Returns the raw JSON value of [purchaseId]. * @@ -336,6 +344,7 @@ private constructor( * .checkoutUrl() * .credits() * .expiresAt() + * .instructions() * .purchaseId() * .status() * .walletId() @@ -352,10 +361,7 @@ private constructor( private var checkoutUrl: JsonField? = null private var credits: JsonField? = null private var expiresAt: JsonField? = null - private var instructions: JsonValue = - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) + private var instructions: JsonField? = null private var pollAfterSeconds: JsonValue = JsonValue.from(2) private var purchaseId: JsonField? = null private var requiresUserInteraction: JsonValue = JsonValue.from(true) @@ -416,7 +422,7 @@ private constructor( */ fun amount(amount: JsonField) = apply { this.amount = amount } - /** Raw Stripe-hosted checkout URL for user interaction. */ + /** Hosted checkout URL for user interaction. */ fun checkoutUrl(checkoutUrl: String) = checkoutUrl(JsonField.of(checkoutUrl)) /** @@ -451,19 +457,19 @@ private constructor( */ fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + /** Hosted checkout and status polling instructions. */ + fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending.") - * ``` + * Sets [Builder.instructions] to an arbitrary JSON value. * + * You should usually call [Builder.instructions] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun instructions(instructions: JsonValue) = apply { this.instructions = instructions } + fun instructions(instructions: JsonField) = apply { + this.instructions = instructions + } /** * Sets the field to an arbitrary JSON value. @@ -613,6 +619,7 @@ private constructor( * .checkoutUrl() * .credits() * .expiresAt() + * .instructions() * .purchaseId() * .status() * .walletId() @@ -627,7 +634,7 @@ private constructor( checkRequired("checkoutUrl", checkoutUrl), checkRequired("credits", credits), checkRequired("expiresAt", expiresAt), - instructions, + checkRequired("instructions", instructions), pollAfterSeconds, checkRequired("purchaseId", purchaseId), requiresUserInteraction, @@ -667,16 +674,7 @@ private constructor( checkoutUrl() credits() expiresAt() - _instructions().let { - if ( - it != - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) - ) { - throw XTwitterScraperInvalidDataException("'instructions' is invalid, received $it") - } - } + instructions() _pollAfterSeconds().let { if (it != JsonValue.from(2)) { throw XTwitterScraperInvalidDataException( @@ -725,16 +723,7 @@ private constructor( (if (checkoutUrl.asKnown().isPresent) 1 else 0) + (if (credits.asKnown().isPresent) 1 else 0) + (if (expiresAt.asKnown().isPresent) 1 else 0) + - instructions.let { - if ( - it == - JsonValue.from( - "Give checkout_url to the user. They must complete payment on Stripe. Never submit payment for them. After payment, poll status_url every poll_after_seconds until latest_purchase.status is no longer pending." - ) - ) - 1 - else 0 - } + + (if (instructions.asKnown().isPresent) 1 else 0) + pollAfterSeconds.let { if (it == JsonValue.from(2)) 1 else 0 } + (if (purchaseId.asKnown().isPresent) 1 else 0) + requiresUserInteraction.let { if (it == JsonValue.from(true)) 1 else 0 } + diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/CreditServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/CreditServiceAsync.kt index 2e84bbf1..70705215 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/CreditServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/CreditServiceAsync.kt @@ -80,8 +80,8 @@ interface CreditServiceAsync { ): CompletableFuture /** - * Create a Stripe Checkout session only after the user confirms. The request never completes - * payment or adds credits by itself. + * Create a hosted checkout only after the user confirms. The request never completes payment or + * adds credits. */ fun topupBalance( params: CreditTopupBalanceParams diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt index ff8a5870..5e74699b 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt @@ -34,10 +34,8 @@ interface GuestWalletServiceAsync { fun withOptions(modifier: Consumer): GuestWalletServiceAsync /** - * Create a one-use Stripe-hosted checkout after the user explicitly confirms a $10-$250 USD - * amount. This request creates no charge by itself. The user opens checkout_url on Stripe. This - * endpoint returns the paid-read API key without requiring an Xquik account, email, dashboard, - * or Xquik web page. An idempotent replay returns the same key. + * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no + * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. */ fun create(params: GuestWalletCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -49,9 +47,9 @@ interface GuestWalletServiceAsync { ): CompletableFuture /** - * Poll after Stripe payment. Use usable to decide whether paid reads can run. An active wallet - * can remain usable while a top-up is pending. A new wallet becomes usable only after verified - * webhook fulfillment. Send the guest key as Authorization: Bearer. + * Poll after payment. Use usable to decide whether paid reads can run. An active wallet can + * remain usable while a top-up is pending. A new wallet becomes usable only after payment is + * verified. Send the guest key as Authorization: Bearer. */ fun retrieveStatus(): CompletableFuture = retrieveStatus(GuestWalletRetrieveStatusParams.none()) @@ -75,9 +73,9 @@ interface GuestWalletServiceAsync { retrieveStatus(GuestWalletRetrieveStatusParams.none(), requestOptions) /** - * Create a one-use Stripe-hosted checkout for an existing paid-read guest key after the user - * explicitly confirms a $10-$250 USD amount. The key remains the same. This request creates no - * charge by itself and never redirects through an Xquik web page. + * Create a one-use hosted checkout after the user confirms a $10-$250 USD amount for an + * existing paid-read guest key. The key remains the same. This request creates no charge and + * never redirects through Xquik. */ fun topup(params: GuestWalletTopupParams): CompletableFuture = topup(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/CreditService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/CreditService.kt index 397d7250..07dcb1f8 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/CreditService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/CreditService.kt @@ -76,8 +76,8 @@ interface CreditService { ): CreditRetrieveTopupStatusResponse /** - * Create a Stripe Checkout session only after the user confirms. The request never completes - * payment or adds credits by itself. + * Create a hosted checkout only after the user confirms. The request never completes payment or + * adds credits. */ fun topupBalance(params: CreditTopupBalanceParams): CreditTopupBalanceResponse = topupBalance(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt index 85af7b18..1c949642 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt @@ -34,10 +34,8 @@ interface GuestWalletService { fun withOptions(modifier: Consumer): GuestWalletService /** - * Create a one-use Stripe-hosted checkout after the user explicitly confirms a $10-$250 USD - * amount. This request creates no charge by itself. The user opens checkout_url on Stripe. This - * endpoint returns the paid-read API key without requiring an Xquik account, email, dashboard, - * or Xquik web page. An idempotent replay returns the same key. + * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no + * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. */ fun create(params: GuestWalletCreateParams): GuestWalletCreateResponse = create(params, RequestOptions.none()) @@ -49,9 +47,9 @@ interface GuestWalletService { ): GuestWalletCreateResponse /** - * Poll after Stripe payment. Use usable to decide whether paid reads can run. An active wallet - * can remain usable while a top-up is pending. A new wallet becomes usable only after verified - * webhook fulfillment. Send the guest key as Authorization: Bearer. + * Poll after payment. Use usable to decide whether paid reads can run. An active wallet can + * remain usable while a top-up is pending. A new wallet becomes usable only after payment is + * verified. Send the guest key as Authorization: Bearer. */ fun retrieveStatus(): GuestWalletRetrieveStatusResponse = retrieveStatus(GuestWalletRetrieveStatusParams.none()) @@ -72,9 +70,9 @@ interface GuestWalletService { retrieveStatus(GuestWalletRetrieveStatusParams.none(), requestOptions) /** - * Create a one-use Stripe-hosted checkout for an existing paid-read guest key after the user - * explicitly confirms a $10-$250 USD amount. The key remains the same. This request creates no - * charge by itself and never redirects through an Xquik web page. + * Create a one-use hosted checkout after the user confirms a $10-$250 USD amount for an + * existing paid-read guest key. The key remains the same. This request creates no charge and + * never redirects through Xquik. */ fun topup(params: GuestWalletTopupParams): GuestWalletTopupResponse = topup(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponseTest.kt index 56edb8f8..a828602b 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponseTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/credits/CreditTopupBalanceResponseTest.kt @@ -18,15 +18,19 @@ internal class CreditTopupBalanceResponseTest { val creditTopupBalanceResponse = CreditTopupBalanceResponse.builder() .redirectUrl( - "https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123" + "https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example" ) - .url("https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123") + .url("https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example") .build() assertThat(creditTopupBalanceResponse.redirectUrl()) - .isEqualTo("https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123") + .isEqualTo( + "https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example" + ) assertThat(creditTopupBalanceResponse.url()) - .isEqualTo("https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123") + .isEqualTo( + "https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example" + ) } @Test @@ -35,9 +39,9 @@ internal class CreditTopupBalanceResponseTest { val creditTopupBalanceResponse = CreditTopupBalanceResponse.builder() .redirectUrl( - "https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123" + "https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example" ) - .url("https://xquik.com/api/v1/credits/topup/redirect?session_id=cs_test_123") + .url("https://xquik.com/api/v1/credits/topup/redirect?session_id=checkout_example") .build() val roundtrippedCreditTopupBalanceResponse = diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponseTest.kt index 92bef599..26af08b5 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponseTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateResponseTest.kt @@ -26,9 +26,10 @@ internal class GuestWalletCreateResponseTest { .scheme(GuestWalletCreateResponse.Authorization.Scheme.BEARER) .build() ) - .checkoutUrl("https://buy.stripe.com/example") + .checkoutUrl("https://checkout.example/guest/example") .credits("66666") .expiresAt(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + .instructions("instructions") .purchaseId("gp_example") .status(GuestWalletCreateResponse.Status.PENDING) .walletId("gw_example") @@ -45,10 +46,11 @@ internal class GuestWalletCreateResponseTest { .build() ) assertThat(guestWalletCreateResponse.checkoutUrl()) - .isEqualTo("https://buy.stripe.com/example") + .isEqualTo("https://checkout.example/guest/example") assertThat(guestWalletCreateResponse.credits()).isEqualTo("66666") assertThat(guestWalletCreateResponse.expiresAt()) .isEqualTo(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + assertThat(guestWalletCreateResponse.instructions()).isEqualTo("instructions") assertThat(guestWalletCreateResponse.purchaseId()).isEqualTo("gp_example") assertThat(guestWalletCreateResponse.status()) .isEqualTo(GuestWalletCreateResponse.Status.PENDING) @@ -68,9 +70,10 @@ internal class GuestWalletCreateResponseTest { .scheme(GuestWalletCreateResponse.Authorization.Scheme.BEARER) .build() ) - .checkoutUrl("https://buy.stripe.com/example") + .checkoutUrl("https://checkout.example/guest/example") .credits("66666") .expiresAt(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + .instructions("instructions") .purchaseId("gp_example") .status(GuestWalletCreateResponse.Status.PENDING) .walletId("gw_example") diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponseTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponseTest.kt index 03995031..7828604e 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponseTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletTopupResponseTest.kt @@ -19,9 +19,10 @@ internal class GuestWalletTopupResponseTest { val guestWalletTopupResponse = GuestWalletTopupResponse.builder() .amount(GuestWalletAmount.builder().amountMinor(1000L).build()) - .checkoutUrl("https://buy.stripe.com/example") + .checkoutUrl("https://checkout.example/guest/example") .credits("66666") .expiresAt(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + .instructions("instructions") .purchaseId("gp_example") .status(GuestWalletTopupResponse.Status.PENDING) .walletId("gw_example") @@ -41,10 +42,11 @@ internal class GuestWalletTopupResponseTest { assertThat(guestWalletTopupResponse.amount()) .isEqualTo(GuestWalletAmount.builder().amountMinor(1000L).build()) assertThat(guestWalletTopupResponse.checkoutUrl()) - .isEqualTo("https://buy.stripe.com/example") + .isEqualTo("https://checkout.example/guest/example") assertThat(guestWalletTopupResponse.credits()).isEqualTo("66666") assertThat(guestWalletTopupResponse.expiresAt()) .isEqualTo(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + assertThat(guestWalletTopupResponse.instructions()).isEqualTo("instructions") assertThat(guestWalletTopupResponse.purchaseId()).isEqualTo("gp_example") assertThat(guestWalletTopupResponse.status()) .isEqualTo(GuestWalletTopupResponse.Status.PENDING) @@ -70,9 +72,10 @@ internal class GuestWalletTopupResponseTest { val guestWalletTopupResponse = GuestWalletTopupResponse.builder() .amount(GuestWalletAmount.builder().amountMinor(1000L).build()) - .checkoutUrl("https://buy.stripe.com/example") + .checkoutUrl("https://checkout.example/guest/example") .credits("66666") .expiresAt(OffsetDateTime.parse("2026-07-13T13:00:00.000Z")) + .instructions("instructions") .purchaseId("gp_example") .status(GuestWalletTopupResponse.Status.PENDING) .walletId("gw_example") From 912f5a17b1f266c1bb1da3af38dc58d24e48cc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Bay=C4=B1r?= Date: Wed, 12 Aug 2026 01:33:51 +0300 Subject: [PATCH 3/8] release: 0.9.0 (#36) * release: 0.9.0 * fix(docs): align generated Javadoc release link --------- Co-authored-by: kriptoburak --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ README.md | 8 ++++---- build.gradle.kts | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 74674555..76d5538a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.4" + ".": "0.9.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index a85650bf..440061d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.9.0](https://github.com/stainless-sdks/x-twitter-scraper-java/compare/v0.8.4...v0.9.0) (2026-08-11) + + +### Features + +* **api:** api update ([d9ec682](https://github.com/stainless-sdks/x-twitter-scraper-java/commit/d9ec6821d543fce1a888f4d0582c3ec24f3c0564)) +* **api:** api update ([aa06dd7](https://github.com/stainless-sdks/x-twitter-scraper-java/commit/aa06dd748fa884ccfa058fe5ed576d97fae978ef)) +* **api:** update generated API surface ([9aa7a46](https://github.com/stainless-sdks/x-twitter-scraper-java/commit/9aa7a469545701d567c0a74e5ad7eea123c333f1)) + ## 0.8.4 (2026-08-11) Full Changelog: [v0.8.3...v0.8.4](https://github.com/Xquik-dev/x-twitter-scraper-java/compare/v0.8.3...v0.8.4) diff --git a/README.md b/README.md index 75e4a5ba..d2b70f0d 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Use it as a typed X API or Twitter API alternative. -[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.8.4) -[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.8.4/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.8.4) +[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.9.0) +[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.9.0/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.9.0) @@ -64,7 +64,7 @@ Use [API reference](https://docs.xquik.com/api-reference/overview) for request c Gradle: ```kotlin -implementation("com.xquik.api:x-twitter-scraper-java:0.8.4") +implementation("com.xquik.api:x-twitter-scraper-java:0.9.0") ``` Maven: @@ -73,7 +73,7 @@ Maven: com.xquik.api x-twitter-scraper-java - 0.8.4 + 0.9.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 96cf25fb..b2c37382 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ licenseReport { allprojects { group = "com.xquik.api" - version = "0.8.4" // x-release-please-version + version = "0.9.0" // x-release-please-version dependencyLocking { lockAllConfigurations() From e75a7919e8ff8da00ddf41ae4111e0f560c6e54a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:35:25 +0000 Subject: [PATCH 4/8] feat(api): api update --- .stats.yml | 4 ++-- .../api/models/guestwallets/GuestWalletCreateParams.kt | 3 ++- .../api/services/async/GuestWalletServiceAsync.kt | 3 ++- .../api/services/blocking/GuestWalletService.kt | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7f3e8c1a..61f7c4d3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 124 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-f688ec225c65653bb6ba3f51fe643fc69e329fa93cdc13117060d56de13823cc.yml -openapi_spec_hash: 4ad185ef874409f7cd359beed23f448f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-d5ea5609c8180c1340dd312fcb526d52953040de479617d4755673705d7c1334.yml +openapi_spec_hash: 9b5ddbbd9ca6b21d24051d6b7cec9123 config_hash: 217d68ca6999ad03cfc4922e9658297e diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt index cc4884a3..d5849098 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/guestwallets/GuestWalletCreateParams.kt @@ -24,7 +24,8 @@ import java.util.Objects /** * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no - * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. + * charge. It returns a paid-read API key without an Xquik account. Idempotent replays return the + * same key. */ class GuestWalletCreateParams private constructor( diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt index 5e74699b..44e2dfee 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/GuestWalletServiceAsync.kt @@ -35,7 +35,8 @@ interface GuestWalletServiceAsync { /** * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no - * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. + * charge. It returns a paid-read API key without an Xquik account. Idempotent replays return + * the same key. */ fun create(params: GuestWalletCreateParams): CompletableFuture = create(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt index 1c949642..ce814d1e 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/GuestWalletService.kt @@ -35,7 +35,8 @@ interface GuestWalletService { /** * Create a one-use hosted checkout after the user confirms $10-$250 USD. The request creates no - * charge. It returns a paid-read API key without an Xquik account. Replays return the same key. + * charge. It returns a paid-read API key without an Xquik account. Idempotent replays return + * the same key. */ fun create(params: GuestWalletCreateParams): GuestWalletCreateResponse = create(params, RequestOptions.none()) From e15cf7f3209954ba0a7ae9755a78288263b85a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Bay=C4=B1r?= Date: Wed, 12 Aug 2026 02:00:57 +0300 Subject: [PATCH 5/8] release: 0.9.1 (#38) * release: 0.9.1 * fix(docs): align patch release references --------- Co-authored-by: kriptoburak --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ README.md | 8 ++++---- build.gradle.kts | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 76d5538a..b28fea99 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.9.0" + ".": "0.9.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 440061d1..cf32378f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.9.1](https://github.com/stainless-sdks/x-twitter-scraper-java/compare/v0.9.0...v0.9.1) (2026-08-11) + +### Features + +* **api:** api update ([e75a791](https://github.com/stainless-sdks/x-twitter-scraper-java/commit/e75a7919e8ff8da00ddf41ae4111e0f560c6e54a)) +* **api:** clarify idempotent guest checkout replays ([60ae884](https://github.com/stainless-sdks/x-twitter-scraper-java/commit/60ae88467fd1d10f2c33a7f2a6575e3dd6087ff8)) + ## [0.9.0](https://github.com/stainless-sdks/x-twitter-scraper-java/compare/v0.8.4...v0.9.0) (2026-08-11) diff --git a/README.md b/README.md index d2b70f0d..413b6bf9 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Use it as a typed X API or Twitter API alternative. -[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.9.0) -[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.9.0/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.9.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.9.1) +[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.9.1/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.9.1) @@ -64,7 +64,7 @@ Use [API reference](https://docs.xquik.com/api-reference/overview) for request c Gradle: ```kotlin -implementation("com.xquik.api:x-twitter-scraper-java:0.9.0") +implementation("com.xquik.api:x-twitter-scraper-java:0.9.1") ``` Maven: @@ -73,7 +73,7 @@ Maven: com.xquik.api x-twitter-scraper-java - 0.9.0 + 0.9.1 ``` diff --git a/build.gradle.kts b/build.gradle.kts index b2c37382..02fe6e12 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ licenseReport { allprojects { group = "com.xquik.api" - version = "0.9.0" // x-release-please-version + version = "0.9.1" // x-release-please-version dependencyLocking { lockAllConfigurations() From ddda594ba95292a599c2d3352e3130d63ddf6a74 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:03:01 +0000 Subject: [PATCH 6/8] feat(api): sync public contracts --- .stats.yml | 6 +- .../com/x_twitter_scraper/api/models/Error.kt | 74 + .../models/x/tweets/TweetGetQuotesParams.kt | 4 +- .../models/x/tweets/TweetGetRepliesParams.kt | 4 +- .../models/x/tweets/TweetGetThreadParams.kt | 1219 ++++++++++++++++- .../api/models/x/tweets/TweetSearchParams.kt | 9 +- .../models/x/users/UserRetrieveLikesParams.kt | 4 +- .../models/x/users/UserRetrieveMediaParams.kt | 4 +- .../x/users/UserRetrieveMentionsParams.kt | 4 +- .../x/users/UserRetrieveRepliesParams.kt | 4 +- .../x/users/UserRetrieveTweetsParams.kt | 4 +- .../api/services/async/x/TweetServiceAsync.kt | 5 +- .../api/services/blocking/x/TweetService.kt | 5 +- .../x/tweets/TweetGetThreadParamsTest.kt | 117 +- .../services/async/x/TweetServiceAsyncTest.kt | 38 +- .../services/blocking/x/TweetServiceTest.kt | 38 +- 16 files changed, 1510 insertions(+), 29 deletions(-) diff --git a/.stats.yml b/.stats.yml index 61f7c4d3..a89002f5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 124 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-d5ea5609c8180c1340dd312fcb526d52953040de479617d4755673705d7c1334.yml -openapi_spec_hash: 9b5ddbbd9ca6b21d24051d6b7cec9123 -config_hash: 217d68ca6999ad03cfc4922e9658297e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik/x-twitter-scraper-878138bbf57021047ac2af87a490a147b1d41367ee7807cc188d3247cc4b255d.yml +openapi_spec_hash: 1f50b417a17776f9f361f34552879e64 +config_hash: a4624963c7e5bdbdf085e64f3101d3b0 diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt index 208f3c2c..65f5af1b 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/Error.kt @@ -674,18 +674,29 @@ private constructor( @JvmField val INVALID_COMMUNITY_ID = of("invalid_community_id") + @JvmField + val INVALID_COMPLETE_REPLIES_REQUEST = of("invalid_complete_replies_request") + @JvmField val INVALID_COVERAGE_CURSOR = of("invalid_coverage_cursor") + @JvmField val INVALID_COVERAGE_REQUEST = of("invalid_coverage_request") + @JvmField val INVALID_IDEMPOTENCY_KEY = of("invalid_idempotency_key") @JvmField val INVALID_LIST_ID = of("invalid_list_id") + @JvmField val INVALID_OUTPUT_OPTIONS = of("invalid_output_options") + @JvmField val INVALID_PAYMENT_AMOUNT = of("invalid_payment_amount") @JvmField val INVALID_RANGE = of("invalid_range") + @JvmField val INVALID_REPLY_OPTIONS = of("invalid_reply_options") + @JvmField val LOGIN_RATE_LIMITED = of("login_rate_limited") + @JvmField val LOGIN_SERVICE_UNAVAILABLE = of("login_service_unavailable") + @JvmField val MISSING_IDEMPOTENCY_KEY = of("missing_idempotency_key") @JvmField val MISSING_IDS = of("missing_ids") @@ -706,6 +717,8 @@ private constructor( @JvmField val TOO_MANY_IDS = of("too_many_ids") + @JvmField val TOO_MANY_TWEETS = of("too_many_tweets") + @JvmField val UNKNOWN_FIELD = of("unknown_field") @JvmField val UNSUPPORTED_MEDIA_TYPE = of("unsupported_media_type") @@ -829,12 +842,17 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COMPLETE_REPLIES_REQUEST, INVALID_COVERAGE_CURSOR, + INVALID_COVERAGE_REQUEST, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, + INVALID_OUTPUT_OPTIONS, INVALID_PAYMENT_AMOUNT, INVALID_RANGE, + INVALID_REPLY_OPTIONS, LOGIN_RATE_LIMITED, + LOGIN_SERVICE_UNAVAILABLE, MISSING_IDEMPOTENCY_KEY, MISSING_IDS, NO_CACHED_STYLE, @@ -845,6 +863,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT, SUPPORT_REQUEST_RATE_LIMIT, TOO_MANY_IDS, + TOO_MANY_TWEETS, UNKNOWN_FIELD, UNSUPPORTED_MEDIA_TYPE, WEBHOOK_INACTIVE, @@ -944,12 +963,17 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COMPLETE_REPLIES_REQUEST, INVALID_COVERAGE_CURSOR, + INVALID_COVERAGE_REQUEST, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, + INVALID_OUTPUT_OPTIONS, INVALID_PAYMENT_AMOUNT, INVALID_RANGE, + INVALID_REPLY_OPTIONS, LOGIN_RATE_LIMITED, + LOGIN_SERVICE_UNAVAILABLE, MISSING_IDEMPOTENCY_KEY, MISSING_IDS, NO_CACHED_STYLE, @@ -960,6 +984,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT, SUPPORT_REQUEST_RATE_LIMIT, TOO_MANY_IDS, + TOO_MANY_TWEETS, UNKNOWN_FIELD, UNSUPPORTED_MEDIA_TYPE, WEBHOOK_INACTIVE, @@ -1063,12 +1088,17 @@ private constructor( IDEMPOTENCY_CONFLICT -> Value.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Value.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Value.INVALID_COMMUNITY_ID + INVALID_COMPLETE_REPLIES_REQUEST -> Value.INVALID_COMPLETE_REPLIES_REQUEST INVALID_COVERAGE_CURSOR -> Value.INVALID_COVERAGE_CURSOR + INVALID_COVERAGE_REQUEST -> Value.INVALID_COVERAGE_REQUEST INVALID_IDEMPOTENCY_KEY -> Value.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Value.INVALID_LIST_ID + INVALID_OUTPUT_OPTIONS -> Value.INVALID_OUTPUT_OPTIONS INVALID_PAYMENT_AMOUNT -> Value.INVALID_PAYMENT_AMOUNT INVALID_RANGE -> Value.INVALID_RANGE + INVALID_REPLY_OPTIONS -> Value.INVALID_REPLY_OPTIONS LOGIN_RATE_LIMITED -> Value.LOGIN_RATE_LIMITED + LOGIN_SERVICE_UNAVAILABLE -> Value.LOGIN_SERVICE_UNAVAILABLE MISSING_IDEMPOTENCY_KEY -> Value.MISSING_IDEMPOTENCY_KEY MISSING_IDS -> Value.MISSING_IDS NO_CACHED_STYLE -> Value.NO_CACHED_STYLE @@ -1079,6 +1109,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT -> Value.SUPPORT_MEDIA_RATE_LIMIT SUPPORT_REQUEST_RATE_LIMIT -> Value.SUPPORT_REQUEST_RATE_LIMIT TOO_MANY_IDS -> Value.TOO_MANY_IDS + TOO_MANY_TWEETS -> Value.TOO_MANY_TWEETS UNKNOWN_FIELD -> Value.UNKNOWN_FIELD UNSUPPORTED_MEDIA_TYPE -> Value.UNSUPPORTED_MEDIA_TYPE WEBHOOK_INACTIVE -> Value.WEBHOOK_INACTIVE @@ -1180,12 +1211,17 @@ private constructor( IDEMPOTENCY_CONFLICT -> Known.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Known.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Known.INVALID_COMMUNITY_ID + INVALID_COMPLETE_REPLIES_REQUEST -> Known.INVALID_COMPLETE_REPLIES_REQUEST INVALID_COVERAGE_CURSOR -> Known.INVALID_COVERAGE_CURSOR + INVALID_COVERAGE_REQUEST -> Known.INVALID_COVERAGE_REQUEST INVALID_IDEMPOTENCY_KEY -> Known.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Known.INVALID_LIST_ID + INVALID_OUTPUT_OPTIONS -> Known.INVALID_OUTPUT_OPTIONS INVALID_PAYMENT_AMOUNT -> Known.INVALID_PAYMENT_AMOUNT INVALID_RANGE -> Known.INVALID_RANGE + INVALID_REPLY_OPTIONS -> Known.INVALID_REPLY_OPTIONS LOGIN_RATE_LIMITED -> Known.LOGIN_RATE_LIMITED + LOGIN_SERVICE_UNAVAILABLE -> Known.LOGIN_SERVICE_UNAVAILABLE MISSING_IDEMPOTENCY_KEY -> Known.MISSING_IDEMPOTENCY_KEY MISSING_IDS -> Known.MISSING_IDS NO_CACHED_STYLE -> Known.NO_CACHED_STYLE @@ -1196,6 +1232,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT -> Known.SUPPORT_MEDIA_RATE_LIMIT SUPPORT_REQUEST_RATE_LIMIT -> Known.SUPPORT_REQUEST_RATE_LIMIT TOO_MANY_IDS -> Known.TOO_MANY_IDS + TOO_MANY_TWEETS -> Known.TOO_MANY_TWEETS UNKNOWN_FIELD -> Known.UNKNOWN_FIELD UNSUPPORTED_MEDIA_TYPE -> Known.UNSUPPORTED_MEDIA_TYPE WEBHOOK_INACTIVE -> Known.WEBHOOK_INACTIVE @@ -1647,18 +1684,29 @@ private constructor( @JvmField val INVALID_COMMUNITY_ID = of("invalid_community_id") + @JvmField + val INVALID_COMPLETE_REPLIES_REQUEST = of("invalid_complete_replies_request") + @JvmField val INVALID_COVERAGE_CURSOR = of("invalid_coverage_cursor") + @JvmField val INVALID_COVERAGE_REQUEST = of("invalid_coverage_request") + @JvmField val INVALID_IDEMPOTENCY_KEY = of("invalid_idempotency_key") @JvmField val INVALID_LIST_ID = of("invalid_list_id") + @JvmField val INVALID_OUTPUT_OPTIONS = of("invalid_output_options") + @JvmField val INVALID_PAYMENT_AMOUNT = of("invalid_payment_amount") @JvmField val INVALID_RANGE = of("invalid_range") + @JvmField val INVALID_REPLY_OPTIONS = of("invalid_reply_options") + @JvmField val LOGIN_RATE_LIMITED = of("login_rate_limited") + @JvmField val LOGIN_SERVICE_UNAVAILABLE = of("login_service_unavailable") + @JvmField val MISSING_IDEMPOTENCY_KEY = of("missing_idempotency_key") @JvmField val MISSING_IDS = of("missing_ids") @@ -1679,6 +1727,8 @@ private constructor( @JvmField val TOO_MANY_IDS = of("too_many_ids") + @JvmField val TOO_MANY_TWEETS = of("too_many_tweets") + @JvmField val UNKNOWN_FIELD = of("unknown_field") @JvmField val UNSUPPORTED_MEDIA_TYPE = of("unsupported_media_type") @@ -1802,12 +1852,17 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COMPLETE_REPLIES_REQUEST, INVALID_COVERAGE_CURSOR, + INVALID_COVERAGE_REQUEST, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, + INVALID_OUTPUT_OPTIONS, INVALID_PAYMENT_AMOUNT, INVALID_RANGE, + INVALID_REPLY_OPTIONS, LOGIN_RATE_LIMITED, + LOGIN_SERVICE_UNAVAILABLE, MISSING_IDEMPOTENCY_KEY, MISSING_IDS, NO_CACHED_STYLE, @@ -1818,6 +1873,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT, SUPPORT_REQUEST_RATE_LIMIT, TOO_MANY_IDS, + TOO_MANY_TWEETS, UNKNOWN_FIELD, UNSUPPORTED_MEDIA_TYPE, WEBHOOK_INACTIVE, @@ -1917,12 +1973,17 @@ private constructor( IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_CONFLICT, INVALID_COMMUNITY_ID, + INVALID_COMPLETE_REPLIES_REQUEST, INVALID_COVERAGE_CURSOR, + INVALID_COVERAGE_REQUEST, INVALID_IDEMPOTENCY_KEY, INVALID_LIST_ID, + INVALID_OUTPUT_OPTIONS, INVALID_PAYMENT_AMOUNT, INVALID_RANGE, + INVALID_REPLY_OPTIONS, LOGIN_RATE_LIMITED, + LOGIN_SERVICE_UNAVAILABLE, MISSING_IDEMPOTENCY_KEY, MISSING_IDS, NO_CACHED_STYLE, @@ -1933,6 +1994,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT, SUPPORT_REQUEST_RATE_LIMIT, TOO_MANY_IDS, + TOO_MANY_TWEETS, UNKNOWN_FIELD, UNSUPPORTED_MEDIA_TYPE, WEBHOOK_INACTIVE, @@ -2035,12 +2097,17 @@ private constructor( IDEMPOTENCY_CONFLICT -> Value.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Value.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Value.INVALID_COMMUNITY_ID + INVALID_COMPLETE_REPLIES_REQUEST -> Value.INVALID_COMPLETE_REPLIES_REQUEST INVALID_COVERAGE_CURSOR -> Value.INVALID_COVERAGE_CURSOR + INVALID_COVERAGE_REQUEST -> Value.INVALID_COVERAGE_REQUEST INVALID_IDEMPOTENCY_KEY -> Value.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Value.INVALID_LIST_ID + INVALID_OUTPUT_OPTIONS -> Value.INVALID_OUTPUT_OPTIONS INVALID_PAYMENT_AMOUNT -> Value.INVALID_PAYMENT_AMOUNT INVALID_RANGE -> Value.INVALID_RANGE + INVALID_REPLY_OPTIONS -> Value.INVALID_REPLY_OPTIONS LOGIN_RATE_LIMITED -> Value.LOGIN_RATE_LIMITED + LOGIN_SERVICE_UNAVAILABLE -> Value.LOGIN_SERVICE_UNAVAILABLE MISSING_IDEMPOTENCY_KEY -> Value.MISSING_IDEMPOTENCY_KEY MISSING_IDS -> Value.MISSING_IDS NO_CACHED_STYLE -> Value.NO_CACHED_STYLE @@ -2051,6 +2118,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT -> Value.SUPPORT_MEDIA_RATE_LIMIT SUPPORT_REQUEST_RATE_LIMIT -> Value.SUPPORT_REQUEST_RATE_LIMIT TOO_MANY_IDS -> Value.TOO_MANY_IDS + TOO_MANY_TWEETS -> Value.TOO_MANY_TWEETS UNKNOWN_FIELD -> Value.UNKNOWN_FIELD UNSUPPORTED_MEDIA_TYPE -> Value.UNSUPPORTED_MEDIA_TYPE WEBHOOK_INACTIVE -> Value.WEBHOOK_INACTIVE @@ -2152,12 +2220,17 @@ private constructor( IDEMPOTENCY_CONFLICT -> Known.IDEMPOTENCY_CONFLICT IDEMPOTENCY_KEY_CONFLICT -> Known.IDEMPOTENCY_KEY_CONFLICT INVALID_COMMUNITY_ID -> Known.INVALID_COMMUNITY_ID + INVALID_COMPLETE_REPLIES_REQUEST -> Known.INVALID_COMPLETE_REPLIES_REQUEST INVALID_COVERAGE_CURSOR -> Known.INVALID_COVERAGE_CURSOR + INVALID_COVERAGE_REQUEST -> Known.INVALID_COVERAGE_REQUEST INVALID_IDEMPOTENCY_KEY -> Known.INVALID_IDEMPOTENCY_KEY INVALID_LIST_ID -> Known.INVALID_LIST_ID + INVALID_OUTPUT_OPTIONS -> Known.INVALID_OUTPUT_OPTIONS INVALID_PAYMENT_AMOUNT -> Known.INVALID_PAYMENT_AMOUNT INVALID_RANGE -> Known.INVALID_RANGE + INVALID_REPLY_OPTIONS -> Known.INVALID_REPLY_OPTIONS LOGIN_RATE_LIMITED -> Known.LOGIN_RATE_LIMITED + LOGIN_SERVICE_UNAVAILABLE -> Known.LOGIN_SERVICE_UNAVAILABLE MISSING_IDEMPOTENCY_KEY -> Known.MISSING_IDEMPOTENCY_KEY MISSING_IDS -> Known.MISSING_IDS NO_CACHED_STYLE -> Known.NO_CACHED_STYLE @@ -2168,6 +2241,7 @@ private constructor( SUPPORT_MEDIA_RATE_LIMIT -> Known.SUPPORT_MEDIA_RATE_LIMIT SUPPORT_REQUEST_RATE_LIMIT -> Known.SUPPORT_REQUEST_RATE_LIMIT TOO_MANY_IDS -> Known.TOO_MANY_IDS + TOO_MANY_TWEETS -> Known.TOO_MANY_TWEETS UNKNOWN_FIELD -> Known.UNKNOWN_FIELD UNSUPPORTED_MEDIA_TYPE -> Known.UNSUPPORTED_MEDIA_TYPE WEBHOOK_INACTIVE -> Known.WEBHOOK_INACTIVE diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt index 720b0a10..9c04cb1c 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetQuotesParams.kt @@ -146,7 +146,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -561,7 +561,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt index d5354110..04d5f5e2 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetRepliesParams.kt @@ -175,7 +175,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -686,7 +686,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParams.kt index 58cdba8f..bcd9947d 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParams.kt @@ -6,9 +6,14 @@ package com.x_twitter_scraper.api.models.x.tweets +import com.fasterxml.jackson.annotation.JsonCreator +import com.x_twitter_scraper.api.core.Enum +import com.x_twitter_scraper.api.core.JsonField import com.x_twitter_scraper.api.core.Params import com.x_twitter_scraper.api.core.http.Headers import com.x_twitter_scraper.api.core.http.QueryParams +import com.x_twitter_scraper.api.errors.XTwitterScraperInvalidDataException +import java.time.LocalDate import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -17,23 +22,151 @@ import kotlin.jvm.optionals.getOrNull class TweetGetThreadParams private constructor( private val id: String?, + private val anyWords: String?, + private val blueVerifiedOnly: Boolean?, + private val cashtags: String?, + private val conversationId: String?, private val cursor: String?, + private val exactPhrase: String?, + private val excludeWords: String?, + private val fromUser: String?, + private val hashtags: String?, + private val inReplyToTweetId: String?, + private val language: String?, + private val maxFaves: Long?, + private val maxQuotes: Long?, + private val maxReplies: Long?, + private val maxRetweets: Long?, + private val mediaType: MediaType?, + private val mentioning: String?, + private val minBookmarks: Long?, + private val minFaves: Long?, + private val minQuotes: Long?, + private val minReplies: Long?, + private val minRetweets: Long?, + private val minViews: Long?, private val pageSize: Long?, + private val quotes: Quotes?, + private val quotesOfTweetId: String?, + private val replies: Replies?, + private val retweets: Retweets?, + private val retweetsOfTweetId: String?, + private val sinceDate: LocalDate?, + private val toUser: String?, + private val untilDate: LocalDate?, + private val url: String?, + private val verifiedOnly: Boolean?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun id(): Optional = Optional.ofNullable(id) + /** Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. */ + fun anyWords(): Optional = Optional.ofNullable(anyWords) + + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(): Optional = Optional.ofNullable(blueVerifiedOnly) + + /** Cashtags separated by spaces, commas, or lines. */ + fun cashtags(): Optional = Optional.ofNullable(cashtags) + + /** Conversation ID filter. */ + fun conversationId(): Optional = Optional.ofNullable(conversationId) + /** Pagination cursor for thread tweets */ fun cursor(): Optional = Optional.ofNullable(cursor) + /** Exact phrase to match. */ + fun exactPhrase(): Optional = Optional.ofNullable(exactPhrase) + + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ + fun excludeWords(): Optional = Optional.ofNullable(excludeWords) + + /** Filter by author username. */ + fun fromUser(): Optional = Optional.ofNullable(fromUser) + + /** Hashtags separated by spaces, commas, or lines. */ + fun hashtags(): Optional = Optional.ofNullable(hashtags) + + /** Only replies to this tweet ID. */ + fun inReplyToTweetId(): Optional = Optional.ofNullable(inReplyToTweetId) + + /** Language code filter, e.g. en or tr. */ + fun language(): Optional = Optional.ofNullable(language) + + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(): Optional = Optional.ofNullable(maxFaves) + + /** Maximum quotes threshold. */ + fun maxQuotes(): Optional = Optional.ofNullable(maxQuotes) + + /** Maximum replies threshold. */ + fun maxReplies(): Optional = Optional.ofNullable(maxReplies) + + /** Maximum retweets threshold. */ + fun maxRetweets(): Optional = Optional.ofNullable(maxRetweets) + + /** Filter by media type. */ + fun mediaType(): Optional = Optional.ofNullable(mediaType) + + /** Filter tweets mentioning a username. */ + fun mentioning(): Optional = Optional.ofNullable(mentioning) + + /** Minimum bookmark count threshold. */ + fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) + + /** Minimum likes threshold. minLikes is also accepted. */ + fun minFaves(): Optional = Optional.ofNullable(minFaves) + + /** Minimum quote count threshold. */ + fun minQuotes(): Optional = Optional.ofNullable(minQuotes) + + /** Minimum replies threshold. */ + fun minReplies(): Optional = Optional.ofNullable(minReplies) + + /** Minimum retweets threshold. */ + fun minRetweets(): Optional = Optional.ofNullable(minRetweets) + + /** Minimum view count threshold. */ + fun minViews(): Optional = Optional.ofNullable(minViews) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. */ fun pageSize(): Optional = Optional.ofNullable(pageSize) + /** Quote mode. */ + fun quotes(): Optional = Optional.ofNullable(quotes) + + /** Only quotes of this tweet ID. */ + fun quotesOfTweetId(): Optional = Optional.ofNullable(quotesOfTweetId) + + /** Reply mode. */ + fun replies(): Optional = Optional.ofNullable(replies) + + /** Retweet mode. */ + fun retweets(): Optional = Optional.ofNullable(retweets) + + /** Only retweets of this tweet ID. */ + fun retweetsOfTweetId(): Optional = Optional.ofNullable(retweetsOfTweetId) + + /** Start date in YYYY-MM-DD format. */ + fun sinceDate(): Optional = Optional.ofNullable(sinceDate) + + /** Filter replies sent to a username. */ + fun toUser(): Optional = Optional.ofNullable(toUser) + + /** End date in YYYY-MM-DD format. */ + fun untilDate(): Optional = Optional.ofNullable(untilDate) + + /** URL substring or domain filter. */ + fun url(): Optional = Optional.ofNullable(url) + + /** Only return tweets from verified authors. */ + fun verifiedOnly(): Optional = Optional.ofNullable(verifiedOnly) + /** Additional headers to send with the request. */ fun _additionalHeaders(): Headers = additionalHeaders @@ -54,16 +187,80 @@ private constructor( class Builder internal constructor() { private var id: String? = null + private var anyWords: String? = null + private var blueVerifiedOnly: Boolean? = null + private var cashtags: String? = null + private var conversationId: String? = null private var cursor: String? = null + private var exactPhrase: String? = null + private var excludeWords: String? = null + private var fromUser: String? = null + private var hashtags: String? = null + private var inReplyToTweetId: String? = null + private var language: String? = null + private var maxFaves: Long? = null + private var maxQuotes: Long? = null + private var maxReplies: Long? = null + private var maxRetweets: Long? = null + private var mediaType: MediaType? = null + private var mentioning: String? = null + private var minBookmarks: Long? = null + private var minFaves: Long? = null + private var minQuotes: Long? = null + private var minReplies: Long? = null + private var minRetweets: Long? = null + private var minViews: Long? = null private var pageSize: Long? = null + private var quotes: Quotes? = null + private var quotesOfTweetId: String? = null + private var replies: Replies? = null + private var retweets: Retweets? = null + private var retweetsOfTweetId: String? = null + private var sinceDate: LocalDate? = null + private var toUser: String? = null + private var untilDate: LocalDate? = null + private var url: String? = null + private var verifiedOnly: Boolean? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(tweetGetThreadParams: TweetGetThreadParams) = apply { id = tweetGetThreadParams.id + anyWords = tweetGetThreadParams.anyWords + blueVerifiedOnly = tweetGetThreadParams.blueVerifiedOnly + cashtags = tweetGetThreadParams.cashtags + conversationId = tweetGetThreadParams.conversationId cursor = tweetGetThreadParams.cursor + exactPhrase = tweetGetThreadParams.exactPhrase + excludeWords = tweetGetThreadParams.excludeWords + fromUser = tweetGetThreadParams.fromUser + hashtags = tweetGetThreadParams.hashtags + inReplyToTweetId = tweetGetThreadParams.inReplyToTweetId + language = tweetGetThreadParams.language + maxFaves = tweetGetThreadParams.maxFaves + maxQuotes = tweetGetThreadParams.maxQuotes + maxReplies = tweetGetThreadParams.maxReplies + maxRetweets = tweetGetThreadParams.maxRetweets + mediaType = tweetGetThreadParams.mediaType + mentioning = tweetGetThreadParams.mentioning + minBookmarks = tweetGetThreadParams.minBookmarks + minFaves = tweetGetThreadParams.minFaves + minQuotes = tweetGetThreadParams.minQuotes + minReplies = tweetGetThreadParams.minReplies + minRetweets = tweetGetThreadParams.minRetweets + minViews = tweetGetThreadParams.minViews pageSize = tweetGetThreadParams.pageSize + quotes = tweetGetThreadParams.quotes + quotesOfTweetId = tweetGetThreadParams.quotesOfTweetId + replies = tweetGetThreadParams.replies + retweets = tweetGetThreadParams.retweets + retweetsOfTweetId = tweetGetThreadParams.retweetsOfTweetId + sinceDate = tweetGetThreadParams.sinceDate + toUser = tweetGetThreadParams.toUser + untilDate = tweetGetThreadParams.untilDate + url = tweetGetThreadParams.url + verifiedOnly = tweetGetThreadParams.verifiedOnly additionalHeaders = tweetGetThreadParams.additionalHeaders.toBuilder() additionalQueryParams = tweetGetThreadParams.additionalQueryParams.toBuilder() } @@ -73,12 +270,231 @@ private constructor( /** Alias for calling [Builder.id] with `id.orElse(null)`. */ fun id(id: Optional) = id(id.getOrNull()) + /** + * Words or quoted phrases where any one can match. Separate with spaces, commas, or lines. + */ + fun anyWords(anyWords: String?) = apply { this.anyWords = anyWords } + + /** Alias for calling [Builder.anyWords] with `anyWords.orElse(null)`. */ + fun anyWords(anyWords: Optional) = anyWords(anyWords.getOrNull()) + + /** Only return tweets from Blue-verified authors. */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean?) = apply { + this.blueVerifiedOnly = blueVerifiedOnly + } + + /** + * Alias for [Builder.blueVerifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun blueVerifiedOnly(blueVerifiedOnly: Boolean) = + blueVerifiedOnly(blueVerifiedOnly as Boolean?) + + /** Alias for calling [Builder.blueVerifiedOnly] with `blueVerifiedOnly.orElse(null)`. */ + fun blueVerifiedOnly(blueVerifiedOnly: Optional) = + blueVerifiedOnly(blueVerifiedOnly.getOrNull()) + + /** Cashtags separated by spaces, commas, or lines. */ + fun cashtags(cashtags: String?) = apply { this.cashtags = cashtags } + + /** Alias for calling [Builder.cashtags] with `cashtags.orElse(null)`. */ + fun cashtags(cashtags: Optional) = cashtags(cashtags.getOrNull()) + + /** Conversation ID filter. */ + fun conversationId(conversationId: String?) = apply { this.conversationId = conversationId } + + /** Alias for calling [Builder.conversationId] with `conversationId.orElse(null)`. */ + fun conversationId(conversationId: Optional) = + conversationId(conversationId.getOrNull()) + /** Pagination cursor for thread tweets */ fun cursor(cursor: String?) = apply { this.cursor = cursor } /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */ fun cursor(cursor: Optional) = cursor(cursor.getOrNull()) + /** Exact phrase to match. */ + fun exactPhrase(exactPhrase: String?) = apply { this.exactPhrase = exactPhrase } + + /** Alias for calling [Builder.exactPhrase] with `exactPhrase.orElse(null)`. */ + fun exactPhrase(exactPhrase: Optional) = exactPhrase(exactPhrase.getOrNull()) + + /** Words or quoted phrases to exclude. Separate with spaces, commas, or lines. */ + fun excludeWords(excludeWords: String?) = apply { this.excludeWords = excludeWords } + + /** Alias for calling [Builder.excludeWords] with `excludeWords.orElse(null)`. */ + fun excludeWords(excludeWords: Optional) = excludeWords(excludeWords.getOrNull()) + + /** Filter by author username. */ + fun fromUser(fromUser: String?) = apply { this.fromUser = fromUser } + + /** Alias for calling [Builder.fromUser] with `fromUser.orElse(null)`. */ + fun fromUser(fromUser: Optional) = fromUser(fromUser.getOrNull()) + + /** Hashtags separated by spaces, commas, or lines. */ + fun hashtags(hashtags: String?) = apply { this.hashtags = hashtags } + + /** Alias for calling [Builder.hashtags] with `hashtags.orElse(null)`. */ + fun hashtags(hashtags: Optional) = hashtags(hashtags.getOrNull()) + + /** Only replies to this tweet ID. */ + fun inReplyToTweetId(inReplyToTweetId: String?) = apply { + this.inReplyToTweetId = inReplyToTweetId + } + + /** Alias for calling [Builder.inReplyToTweetId] with `inReplyToTweetId.orElse(null)`. */ + fun inReplyToTweetId(inReplyToTweetId: Optional) = + inReplyToTweetId(inReplyToTweetId.getOrNull()) + + /** Language code filter, e.g. en or tr. */ + fun language(language: String?) = apply { this.language = language } + + /** Alias for calling [Builder.language] with `language.orElse(null)`. */ + fun language(language: Optional) = language(language.getOrNull()) + + /** Maximum likes threshold. maxLikes is also accepted. */ + fun maxFaves(maxFaves: Long?) = apply { this.maxFaves = maxFaves } + + /** + * Alias for [Builder.maxFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxFaves(maxFaves: Long) = maxFaves(maxFaves as Long?) + + /** Alias for calling [Builder.maxFaves] with `maxFaves.orElse(null)`. */ + fun maxFaves(maxFaves: Optional) = maxFaves(maxFaves.getOrNull()) + + /** Maximum quotes threshold. */ + fun maxQuotes(maxQuotes: Long?) = apply { this.maxQuotes = maxQuotes } + + /** + * Alias for [Builder.maxQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxQuotes(maxQuotes: Long) = maxQuotes(maxQuotes as Long?) + + /** Alias for calling [Builder.maxQuotes] with `maxQuotes.orElse(null)`. */ + fun maxQuotes(maxQuotes: Optional) = maxQuotes(maxQuotes.getOrNull()) + + /** Maximum replies threshold. */ + fun maxReplies(maxReplies: Long?) = apply { this.maxReplies = maxReplies } + + /** + * Alias for [Builder.maxReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxReplies(maxReplies: Long) = maxReplies(maxReplies as Long?) + + /** Alias for calling [Builder.maxReplies] with `maxReplies.orElse(null)`. */ + fun maxReplies(maxReplies: Optional) = maxReplies(maxReplies.getOrNull()) + + /** Maximum retweets threshold. */ + fun maxRetweets(maxRetweets: Long?) = apply { this.maxRetweets = maxRetweets } + + /** + * Alias for [Builder.maxRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxRetweets(maxRetweets: Long) = maxRetweets(maxRetweets as Long?) + + /** Alias for calling [Builder.maxRetweets] with `maxRetweets.orElse(null)`. */ + fun maxRetweets(maxRetweets: Optional) = maxRetweets(maxRetweets.getOrNull()) + + /** Filter by media type. */ + fun mediaType(mediaType: MediaType?) = apply { this.mediaType = mediaType } + + /** Alias for calling [Builder.mediaType] with `mediaType.orElse(null)`. */ + fun mediaType(mediaType: Optional) = mediaType(mediaType.getOrNull()) + + /** Filter tweets mentioning a username. */ + fun mentioning(mentioning: String?) = apply { this.mentioning = mentioning } + + /** Alias for calling [Builder.mentioning] with `mentioning.orElse(null)`. */ + fun mentioning(mentioning: Optional) = mentioning(mentioning.getOrNull()) + + /** Minimum bookmark count threshold. */ + fun minBookmarks(minBookmarks: Long?) = apply { this.minBookmarks = minBookmarks } + + /** + * Alias for [Builder.minBookmarks]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minBookmarks(minBookmarks: Long) = minBookmarks(minBookmarks as Long?) + + /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ + fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) + + /** Minimum likes threshold. minLikes is also accepted. */ + fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } + + /** + * Alias for [Builder.minFaves]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minFaves(minFaves: Long) = minFaves(minFaves as Long?) + + /** Alias for calling [Builder.minFaves] with `minFaves.orElse(null)`. */ + fun minFaves(minFaves: Optional) = minFaves(minFaves.getOrNull()) + + /** Minimum quote count threshold. */ + fun minQuotes(minQuotes: Long?) = apply { this.minQuotes = minQuotes } + + /** + * Alias for [Builder.minQuotes]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minQuotes(minQuotes: Long) = minQuotes(minQuotes as Long?) + + /** Alias for calling [Builder.minQuotes] with `minQuotes.orElse(null)`. */ + fun minQuotes(minQuotes: Optional) = minQuotes(minQuotes.getOrNull()) + + /** Minimum replies threshold. */ + fun minReplies(minReplies: Long?) = apply { this.minReplies = minReplies } + + /** + * Alias for [Builder.minReplies]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minReplies(minReplies: Long) = minReplies(minReplies as Long?) + + /** Alias for calling [Builder.minReplies] with `minReplies.orElse(null)`. */ + fun minReplies(minReplies: Optional) = minReplies(minReplies.getOrNull()) + + /** Minimum retweets threshold. */ + fun minRetweets(minRetweets: Long?) = apply { this.minRetweets = minRetweets } + + /** + * Alias for [Builder.minRetweets]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minRetweets(minRetweets: Long) = minRetweets(minRetweets as Long?) + + /** Alias for calling [Builder.minRetweets] with `minRetweets.orElse(null)`. */ + fun minRetweets(minRetweets: Optional) = minRetweets(minRetweets.getOrNull()) + + /** Minimum view count threshold. */ + fun minViews(minViews: Long?) = apply { this.minViews = minViews } + + /** + * Alias for [Builder.minViews]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun minViews(minViews: Long) = minViews(minViews as Long?) + + /** Alias for calling [Builder.minViews] with `minViews.orElse(null)`. */ + fun minViews(minViews: Optional) = minViews(minViews.getOrNull()) + /** * Maximum page items (1-100, default 20). Source, filters, or credits can reduce results. * Continue while has_next_page is true. Deprecated limit and count aliases remain accepted. @@ -95,6 +511,79 @@ private constructor( /** Alias for calling [Builder.pageSize] with `pageSize.orElse(null)`. */ fun pageSize(pageSize: Optional) = pageSize(pageSize.getOrNull()) + /** Quote mode. */ + fun quotes(quotes: Quotes?) = apply { this.quotes = quotes } + + /** Alias for calling [Builder.quotes] with `quotes.orElse(null)`. */ + fun quotes(quotes: Optional) = quotes(quotes.getOrNull()) + + /** Only quotes of this tweet ID. */ + fun quotesOfTweetId(quotesOfTweetId: String?) = apply { + this.quotesOfTweetId = quotesOfTweetId + } + + /** Alias for calling [Builder.quotesOfTweetId] with `quotesOfTweetId.orElse(null)`. */ + fun quotesOfTweetId(quotesOfTweetId: Optional) = + quotesOfTweetId(quotesOfTweetId.getOrNull()) + + /** Reply mode. */ + fun replies(replies: Replies?) = apply { this.replies = replies } + + /** Alias for calling [Builder.replies] with `replies.orElse(null)`. */ + fun replies(replies: Optional) = replies(replies.getOrNull()) + + /** Retweet mode. */ + fun retweets(retweets: Retweets?) = apply { this.retweets = retweets } + + /** Alias for calling [Builder.retweets] with `retweets.orElse(null)`. */ + fun retweets(retweets: Optional) = retweets(retweets.getOrNull()) + + /** Only retweets of this tweet ID. */ + fun retweetsOfTweetId(retweetsOfTweetId: String?) = apply { + this.retweetsOfTweetId = retweetsOfTweetId + } + + /** Alias for calling [Builder.retweetsOfTweetId] with `retweetsOfTweetId.orElse(null)`. */ + fun retweetsOfTweetId(retweetsOfTweetId: Optional) = + retweetsOfTweetId(retweetsOfTweetId.getOrNull()) + + /** Start date in YYYY-MM-DD format. */ + fun sinceDate(sinceDate: LocalDate?) = apply { this.sinceDate = sinceDate } + + /** Alias for calling [Builder.sinceDate] with `sinceDate.orElse(null)`. */ + fun sinceDate(sinceDate: Optional) = sinceDate(sinceDate.getOrNull()) + + /** Filter replies sent to a username. */ + fun toUser(toUser: String?) = apply { this.toUser = toUser } + + /** Alias for calling [Builder.toUser] with `toUser.orElse(null)`. */ + fun toUser(toUser: Optional) = toUser(toUser.getOrNull()) + + /** End date in YYYY-MM-DD format. */ + fun untilDate(untilDate: LocalDate?) = apply { this.untilDate = untilDate } + + /** Alias for calling [Builder.untilDate] with `untilDate.orElse(null)`. */ + fun untilDate(untilDate: Optional) = untilDate(untilDate.getOrNull()) + + /** URL substring or domain filter. */ + fun url(url: String?) = apply { this.url = url } + + /** Alias for calling [Builder.url] with `url.orElse(null)`. */ + fun url(url: Optional) = url(url.getOrNull()) + + /** Only return tweets from verified authors. */ + fun verifiedOnly(verifiedOnly: Boolean?) = apply { this.verifiedOnly = verifiedOnly } + + /** + * Alias for [Builder.verifiedOnly]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun verifiedOnly(verifiedOnly: Boolean) = verifiedOnly(verifiedOnly as Boolean?) + + /** Alias for calling [Builder.verifiedOnly] with `verifiedOnly.orElse(null)`. */ + fun verifiedOnly(verifiedOnly: Optional) = verifiedOnly(verifiedOnly.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -201,8 +690,40 @@ private constructor( fun build(): TweetGetThreadParams = TweetGetThreadParams( id, + anyWords, + blueVerifiedOnly, + cashtags, + conversationId, cursor, + exactPhrase, + excludeWords, + fromUser, + hashtags, + inReplyToTweetId, + language, + maxFaves, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minBookmarks, + minFaves, + minQuotes, + minReplies, + minRetweets, + minViews, pageSize, + quotes, + quotesOfTweetId, + replies, + retweets, + retweetsOfTweetId, + sinceDate, + toUser, + untilDate, + url, + verifiedOnly, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -219,12 +740,636 @@ private constructor( override fun _queryParams(): QueryParams = QueryParams.builder() .apply { + anyWords?.let { put("anyWords", it) } + blueVerifiedOnly?.let { put("blueVerifiedOnly", it.toString()) } + cashtags?.let { put("cashtags", it) } + conversationId?.let { put("conversationId", it) } cursor?.let { put("cursor", it) } + exactPhrase?.let { put("exactPhrase", it) } + excludeWords?.let { put("excludeWords", it) } + fromUser?.let { put("fromUser", it) } + hashtags?.let { put("hashtags", it) } + inReplyToTweetId?.let { put("inReplyToTweetId", it) } + language?.let { put("language", it) } + maxFaves?.let { put("maxFaves", it.toString()) } + maxQuotes?.let { put("maxQuotes", it.toString()) } + maxReplies?.let { put("maxReplies", it.toString()) } + maxRetweets?.let { put("maxRetweets", it.toString()) } + mediaType?.let { put("mediaType", it.toString()) } + mentioning?.let { put("mentioning", it) } + minBookmarks?.let { put("minBookmarks", it.toString()) } + minFaves?.let { put("minFaves", it.toString()) } + minQuotes?.let { put("minQuotes", it.toString()) } + minReplies?.let { put("minReplies", it.toString()) } + minRetweets?.let { put("minRetweets", it.toString()) } + minViews?.let { put("minViews", it.toString()) } pageSize?.let { put("pageSize", it.toString()) } + quotes?.let { put("quotes", it.toString()) } + quotesOfTweetId?.let { put("quotesOfTweetId", it) } + replies?.let { put("replies", it.toString()) } + retweets?.let { put("retweets", it.toString()) } + retweetsOfTweetId?.let { put("retweetsOfTweetId", it) } + sinceDate?.let { put("sinceDate", it.toString()) } + toUser?.let { put("toUser", it) } + untilDate?.let { put("untilDate", it.toString()) } + url?.let { put("url", it) } + verifiedOnly?.let { put("verifiedOnly", it.toString()) } putAll(additionalQueryParams) } .build() + /** Filter by media type. */ + class MediaType @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IMAGES = of("images") + + @JvmField val VIDEOS = of("videos") + + @JvmField val GIFS = of("gifs") + + @JvmField val MEDIA = of("media") + + @JvmField val LINKS = of("links") + + @JvmField val NONE = of("none") + + @JvmStatic fun of(value: String) = MediaType(JsonField.of(value)) + } + + /** An enum containing [MediaType]'s known values. */ + enum class Known { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, + } + + /** + * An enum containing [MediaType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [MediaType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IMAGES, + VIDEOS, + GIFS, + MEDIA, + LINKS, + NONE, + /** + * An enum member indicating that [MediaType] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IMAGES -> Value.IMAGES + VIDEOS -> Value.VIDEOS + GIFS -> Value.GIFS + MEDIA -> Value.MEDIA + LINKS -> Value.LINKS + NONE -> Value.NONE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + IMAGES -> Known.IMAGES + VIDEOS -> Known.VIDEOS + GIFS -> Known.GIFS + MEDIA -> Known.MEDIA + LINKS -> Known.LINKS + NONE -> Known.NONE + else -> throw XTwitterScraperInvalidDataException("Unknown MediaType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): MediaType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is MediaType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Quote mode. */ + class Quotes @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val INCLUDE = of("include") + + @JvmField val EXCLUDE = of("exclude") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Quotes(JsonField.of(value)) + } + + /** An enum containing [Quotes]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } + + /** + * An enum containing [Quotes]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Quotes] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Quotes] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Quotes: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Quotes = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Quotes && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Reply mode. */ + class Replies @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val INCLUDE = of("include") + + @JvmField val EXCLUDE = of("exclude") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Replies(JsonField.of(value)) + } + + /** An enum containing [Replies]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } + + /** + * An enum containing [Replies]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Replies] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Replies] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Replies: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Replies = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Replies && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Retweet mode. */ + class Retweets @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val INCLUDE = of("include") + + @JvmField val EXCLUDE = of("exclude") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Retweets(JsonField.of(value)) + } + + /** An enum containing [Retweets]'s known values. */ + enum class Known { + INCLUDE, + EXCLUDE, + ONLY, + } + + /** + * An enum containing [Retweets]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Retweets] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCLUDE, + EXCLUDE, + ONLY, + /** An enum member indicating that [Retweets] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCLUDE -> Value.INCLUDE + EXCLUDE -> Value.EXCLUDE + ONLY -> Value.ONLY + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + INCLUDE -> Known.INCLUDE + EXCLUDE -> Known.EXCLUDE + ONLY -> Known.ONLY + else -> throw XTwitterScraperInvalidDataException("Unknown Retweets: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws XTwitterScraperInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + XTwitterScraperInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws XTwitterScraperInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Retweets = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: XTwitterScraperInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Retweets && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -232,15 +1377,85 @@ private constructor( return other is TweetGetThreadParams && id == other.id && + anyWords == other.anyWords && + blueVerifiedOnly == other.blueVerifiedOnly && + cashtags == other.cashtags && + conversationId == other.conversationId && cursor == other.cursor && + exactPhrase == other.exactPhrase && + excludeWords == other.excludeWords && + fromUser == other.fromUser && + hashtags == other.hashtags && + inReplyToTweetId == other.inReplyToTweetId && + language == other.language && + maxFaves == other.maxFaves && + maxQuotes == other.maxQuotes && + maxReplies == other.maxReplies && + maxRetweets == other.maxRetweets && + mediaType == other.mediaType && + mentioning == other.mentioning && + minBookmarks == other.minBookmarks && + minFaves == other.minFaves && + minQuotes == other.minQuotes && + minReplies == other.minReplies && + minRetweets == other.minRetweets && + minViews == other.minViews && pageSize == other.pageSize && + quotes == other.quotes && + quotesOfTweetId == other.quotesOfTweetId && + replies == other.replies && + retweets == other.retweets && + retweetsOfTweetId == other.retweetsOfTweetId && + sinceDate == other.sinceDate && + toUser == other.toUser && + untilDate == other.untilDate && + url == other.url && + verifiedOnly == other.verifiedOnly && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams } override fun hashCode(): Int = - Objects.hash(id, cursor, pageSize, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + anyWords, + blueVerifiedOnly, + cashtags, + conversationId, + cursor, + exactPhrase, + excludeWords, + fromUser, + hashtags, + inReplyToTweetId, + language, + maxFaves, + maxQuotes, + maxReplies, + maxRetweets, + mediaType, + mentioning, + minBookmarks, + minFaves, + minQuotes, + minReplies, + minRetweets, + minViews, + pageSize, + quotes, + quotesOfTweetId, + replies, + retweets, + retweetsOfTweetId, + sinceDate, + toUser, + untilDate, + url, + verifiedOnly, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "TweetGetThreadParams{id=$id, cursor=$cursor, pageSize=$pageSize, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "TweetGetThreadParams{id=$id, anyWords=$anyWords, blueVerifiedOnly=$blueVerifiedOnly, cashtags=$cashtags, conversationId=$conversationId, cursor=$cursor, exactPhrase=$exactPhrase, excludeWords=$excludeWords, fromUser=$fromUser, hashtags=$hashtags, inReplyToTweetId=$inReplyToTweetId, language=$language, maxFaves=$maxFaves, maxQuotes=$maxQuotes, maxReplies=$maxReplies, maxRetweets=$maxRetweets, mediaType=$mediaType, mentioning=$mentioning, minBookmarks=$minBookmarks, minFaves=$minFaves, minQuotes=$minQuotes, minReplies=$minReplies, minRetweets=$minRetweets, minViews=$minViews, pageSize=$pageSize, quotes=$quotes, quotesOfTweetId=$quotesOfTweetId, replies=$replies, retweets=$retweets, retweetsOfTweetId=$retweetsOfTweetId, sinceDate=$sinceDate, toUser=$toUser, untilDate=$untilDate, url=$url, verifiedOnly=$verifiedOnly, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt index 3c120d7f..3a0dd902 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetSearchParams.kt @@ -19,7 +19,10 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** No-mode search maximizes coverage. */ +/** + * No-mode search maximizes coverage. New cursorless `Latest` sessions return rows newest-first + * across cursor pages. Existing cursors preserve their established ordering. + */ class TweetSearchParams private constructor( private val q: String, @@ -171,7 +174,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -638,7 +641,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt index eb2aad91..70adf121 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveLikesParams.kt @@ -140,7 +140,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -527,7 +527,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt index a8666866..ea433342 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMediaParams.kt @@ -140,7 +140,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -527,7 +527,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt index e273d259..ab432a46 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveMentionsParams.kt @@ -142,7 +142,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -541,7 +541,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt index 06cb8e45..136bb19c 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveRepliesParams.kt @@ -150,7 +150,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -563,7 +563,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt index 757c0f46..4686939f 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/models/x/users/UserRetrieveTweetsParams.kt @@ -154,7 +154,7 @@ private constructor( /** Minimum bookmark count threshold. */ fun minBookmarks(): Optional = Optional.ofNullable(minBookmarks) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(): Optional = Optional.ofNullable(minFaves) /** Minimum quote count threshold. */ @@ -583,7 +583,7 @@ private constructor( /** Alias for calling [Builder.minBookmarks] with `minBookmarks.orElse(null)`. */ fun minBookmarks(minBookmarks: Optional) = minBookmarks(minBookmarks.getOrNull()) - /** Minimum likes threshold. */ + /** Minimum likes threshold. minLikes is also accepted. */ fun minFaves(minFaves: Long?) = apply { this.minFaves = minFaves } /** diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt index 9fb6136e..69775ad6 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsync.kt @@ -305,7 +305,10 @@ interface TweetServiceAsync { fun getThread(id: String, requestOptions: RequestOptions): CompletableFuture = getThread(id, TweetGetThreadParams.none(), requestOptions) - /** No-mode search maximizes coverage. */ + /** + * No-mode search maximizes coverage. New cursorless `Latest` sessions return rows newest-first + * across cursor pages. Existing cursors preserve their established ordering. + */ fun search(params: TweetSearchParams): CompletableFuture = search(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt index f80c0f0e..a425d34a 100644 --- a/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt +++ b/x-twitter-scraper-java-core/src/main/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetService.kt @@ -282,7 +282,10 @@ interface TweetService { fun getThread(id: String, requestOptions: RequestOptions): PaginatedTweets = getThread(id, TweetGetThreadParams.none(), requestOptions) - /** No-mode search maximizes coverage. */ + /** + * No-mode search maximizes coverage. New cursorless `Latest` sessions return rows newest-first + * across cursor pages. Existing cursors preserve their established ordering. + */ fun search(params: TweetSearchParams): TweetSearchResponse = search(params, RequestOptions.none()) diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParamsTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParamsTest.kt index d17af447..0bee2879 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParamsTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/models/x/tweets/TweetGetThreadParamsTest.kt @@ -7,6 +7,7 @@ package com.x_twitter_scraper.api.models.x.tweets import com.x_twitter_scraper.api.core.http.QueryParams +import java.time.LocalDate import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,7 +15,43 @@ internal class TweetGetThreadParamsTest { @Test fun create() { - TweetGetThreadParams.builder().id("id").cursor("cursor").pageSize(1L).build() + TweetGetThreadParams.builder() + .id("id") + .anyWords("anyWords") + .blueVerifiedOnly(true) + .cashtags("cashtags") + .conversationId("conversationId") + .cursor("cursor") + .exactPhrase("exactPhrase") + .excludeWords("excludeWords") + .fromUser("fromUser") + .hashtags("hashtags") + .inReplyToTweetId("inReplyToTweetId") + .language("language") + .maxFaves(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) + .mediaType(TweetGetThreadParams.MediaType.IMAGES) + .mentioning("mentioning") + .minBookmarks(0L) + .minFaves(0L) + .minQuotes(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .pageSize(1L) + .quotes(TweetGetThreadParams.Quotes.INCLUDE) + .quotesOfTweetId("quotesOfTweetId") + .replies(TweetGetThreadParams.Replies.INCLUDE) + .retweets(TweetGetThreadParams.Retweets.INCLUDE) + .retweetsOfTweetId("retweetsOfTweetId") + .sinceDate(LocalDate.parse("2019-12-27")) + .toUser("toUser") + .untilDate(LocalDate.parse("2019-12-27")) + .url("url") + .verifiedOnly(true) + .build() } @Test @@ -28,12 +65,86 @@ internal class TweetGetThreadParamsTest { @Test fun queryParams() { - val params = TweetGetThreadParams.builder().id("id").cursor("cursor").pageSize(1L).build() + val params = + TweetGetThreadParams.builder() + .id("id") + .anyWords("anyWords") + .blueVerifiedOnly(true) + .cashtags("cashtags") + .conversationId("conversationId") + .cursor("cursor") + .exactPhrase("exactPhrase") + .excludeWords("excludeWords") + .fromUser("fromUser") + .hashtags("hashtags") + .inReplyToTweetId("inReplyToTweetId") + .language("language") + .maxFaves(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) + .mediaType(TweetGetThreadParams.MediaType.IMAGES) + .mentioning("mentioning") + .minBookmarks(0L) + .minFaves(0L) + .minQuotes(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .pageSize(1L) + .quotes(TweetGetThreadParams.Quotes.INCLUDE) + .quotesOfTweetId("quotesOfTweetId") + .replies(TweetGetThreadParams.Replies.INCLUDE) + .retweets(TweetGetThreadParams.Retweets.INCLUDE) + .retweetsOfTweetId("retweetsOfTweetId") + .sinceDate(LocalDate.parse("2019-12-27")) + .toUser("toUser") + .untilDate(LocalDate.parse("2019-12-27")) + .url("url") + .verifiedOnly(true) + .build() val queryParams = params._queryParams() assertThat(queryParams) - .isEqualTo(QueryParams.builder().put("cursor", "cursor").put("pageSize", "1").build()) + .isEqualTo( + QueryParams.builder() + .put("anyWords", "anyWords") + .put("blueVerifiedOnly", "true") + .put("cashtags", "cashtags") + .put("conversationId", "conversationId") + .put("cursor", "cursor") + .put("exactPhrase", "exactPhrase") + .put("excludeWords", "excludeWords") + .put("fromUser", "fromUser") + .put("hashtags", "hashtags") + .put("inReplyToTweetId", "inReplyToTweetId") + .put("language", "language") + .put("maxFaves", "0") + .put("maxQuotes", "0") + .put("maxReplies", "0") + .put("maxRetweets", "0") + .put("mediaType", "images") + .put("mentioning", "mentioning") + .put("minBookmarks", "0") + .put("minFaves", "0") + .put("minQuotes", "0") + .put("minReplies", "0") + .put("minRetweets", "0") + .put("minViews", "0") + .put("pageSize", "1") + .put("quotes", "include") + .put("quotesOfTweetId", "quotesOfTweetId") + .put("replies", "include") + .put("retweets", "include") + .put("retweetsOfTweetId", "retweetsOfTweetId") + .put("sinceDate", "2019-12-27") + .put("toUser", "toUser") + .put("untilDate", "2019-12-27") + .put("url", "url") + .put("verifiedOnly", "true") + .build() + ) } @Test diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt index db0b0765..ade48936 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/async/x/TweetServiceAsyncTest.kt @@ -336,7 +336,43 @@ internal class TweetServiceAsyncTest { val paginatedTweetsFuture = tweetServiceAsync.getThread( - TweetGetThreadParams.builder().id("id").cursor("cursor").pageSize(1L).build() + TweetGetThreadParams.builder() + .id("id") + .anyWords("anyWords") + .blueVerifiedOnly(true) + .cashtags("cashtags") + .conversationId("conversationId") + .cursor("cursor") + .exactPhrase("exactPhrase") + .excludeWords("excludeWords") + .fromUser("fromUser") + .hashtags("hashtags") + .inReplyToTweetId("inReplyToTweetId") + .language("language") + .maxFaves(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) + .mediaType(TweetGetThreadParams.MediaType.IMAGES) + .mentioning("mentioning") + .minBookmarks(0L) + .minFaves(0L) + .minQuotes(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .pageSize(1L) + .quotes(TweetGetThreadParams.Quotes.INCLUDE) + .quotesOfTweetId("quotesOfTweetId") + .replies(TweetGetThreadParams.Replies.INCLUDE) + .retweets(TweetGetThreadParams.Retweets.INCLUDE) + .retweetsOfTweetId("retweetsOfTweetId") + .sinceDate(LocalDate.parse("2019-12-27")) + .toUser("toUser") + .untilDate(LocalDate.parse("2019-12-27")) + .url("url") + .verifiedOnly(true) + .build() ) val paginatedTweets = paginatedTweetsFuture.get() diff --git a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt index cb1ed14e..3d13f368 100644 --- a/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt +++ b/x-twitter-scraper-java-core/src/test/kotlin/com/x_twitter_scraper/api/services/blocking/x/TweetServiceTest.kt @@ -327,7 +327,43 @@ internal class TweetServiceTest { val paginatedTweets = tweetService.getThread( - TweetGetThreadParams.builder().id("id").cursor("cursor").pageSize(1L).build() + TweetGetThreadParams.builder() + .id("id") + .anyWords("anyWords") + .blueVerifiedOnly(true) + .cashtags("cashtags") + .conversationId("conversationId") + .cursor("cursor") + .exactPhrase("exactPhrase") + .excludeWords("excludeWords") + .fromUser("fromUser") + .hashtags("hashtags") + .inReplyToTweetId("inReplyToTweetId") + .language("language") + .maxFaves(0L) + .maxQuotes(0L) + .maxReplies(0L) + .maxRetweets(0L) + .mediaType(TweetGetThreadParams.MediaType.IMAGES) + .mentioning("mentioning") + .minBookmarks(0L) + .minFaves(0L) + .minQuotes(0L) + .minReplies(0L) + .minRetweets(0L) + .minViews(0L) + .pageSize(1L) + .quotes(TweetGetThreadParams.Quotes.INCLUDE) + .quotesOfTweetId("quotesOfTweetId") + .replies(TweetGetThreadParams.Replies.INCLUDE) + .retweets(TweetGetThreadParams.Retweets.INCLUDE) + .retweetsOfTweetId("retweetsOfTweetId") + .sinceDate(LocalDate.parse("2019-12-27")) + .toUser("toUser") + .untilDate(LocalDate.parse("2019-12-27")) + .url("url") + .verifiedOnly(true) + .build() ) kotlin.test.assertNotNull(paginatedTweets) From e4d9217b1a93cd4f03ff15d0f7127ecaaf45f8f3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:03:55 +0000 Subject: [PATCH 7/8] release: 0.10.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 57 +++++++++++++++++++++++++++++++++++ README.md | 8 ++--- build.gradle.kts | 2 +- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b28fea99..7d9b009d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.9.1" + ".": "0.10.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index cf32378f..e15d7b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,62 @@ # Changelog +## 0.10.0 (2026-08-12) + +Full Changelog: [v0.9.1...v0.10.0](https://github.com/Xquik-dev/x-twitter-scraper-java/compare/v0.9.1...v0.10.0) + +### Features + +* add OpenSSF Gold quality gates ([f0fb44d](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/f0fb44d9b07c3c612ff9b005a694e6f838081f3a)) +* **api:** align reply extraction contracts ([#30](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/30)) ([7702591](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/7702591c9b20cabdef63849eb98419cec0ef356a)) +* **api:** api update ([e75a791](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/e75a7919e8ff8da00ddf41ae4111e0f560c6e54a)) +* **api:** api update ([d9ec682](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/d9ec6821d543fce1a888f4d0582c3ec24f3c0564)) +* **api:** api update ([aa06dd7](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/aa06dd748fa884ccfa058fe5ed576d97fae978ef)) +* **api:** api update ([30c1879](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/30c1879055bd1a7fb925a8dc674fbbd932c29f9f)) +* **api:** api update ([5e2cc73](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/5e2cc73283addfe9ac177efb47623c0f51dedf8e)) +* **api:** api update ([6022295](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/6022295c6a25bd0b0797c85ae96304cc3eeb2103)) +* **api:** api update ([d4c753a](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/d4c753ab0a9ee134dc567e576dac2892407e7e29)) +* **api:** api update ([785b4f7](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/785b4f7e00571e41ccd6b1a53ffca6d75ab6f58f)) +* **api:** api update ([a261d36](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/a261d36ba03b9eb2969ca9758694f969bf374ec7)) +* **api:** sync public contracts ([ddda594](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/ddda594ba95292a599c2d3352e3130d63ddf6a74)) + + +### Bug Fixes + +* align SLF4J test provider ([e760ae0](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/e760ae0bc9ff0ecb402e6f989c319908d1282875)) +* escape ampersand in OpenAPI summaries for C# XML docs ([fb78c9a](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/fb78c9aea1252be7535e3b5efcc5f916805831d4)) +* **release:** allow Maven publication propagation ([#32](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/32)) ([efd0ddc](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/efd0ddc053c497054b01d765e7c1fbbddf5d6106)) +* remove unused async lock helper ([bf2e7a9](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/bf2e7a9d5bbfd1b53ce9bf34386a6fe0194ba73c)) +* trust verified Gradle metadata ([e0e3e87](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/e0e3e8783d7b3077af70647d8e5f67565da4bc3f)) + + +### Chores + +* add Context7 refresh workflow ([ce2a6d7](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/ce2a6d7bbf0b3818343823564632b9663deef0ef)) +* add FUNDING.yml pointing to xquik.com ([b8a37d9](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/b8a37d9d5eff62c825d045e7549cbbd97b8e044a)) +* **deps:** bump actions/setup-java from 5.6.0 to 5.7.0 ([#49](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/49)) ([ca9c316](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/ca9c316f84afa298a5df6e5c06da2acf1da64541)) +* **deps:** bump github/codeql-action/analyze from 4.37.1 to 4.37.2 ([#22](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/22)) ([d307843](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/d3078435464e3144075d865d972e38d5bbd59482)) +* **deps:** bump github/codeql-action/upload-sarif ([#23](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/23)) ([977ccd8](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/977ccd80c70faa598845c08f1a776ea48d198a51)) +* **deps:** bump github/codeql-action/upload-sarif from 4.37.2 to 4.37.6 ([#46](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/46)) ([d1614e9](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/d1614e9d52d5fb6d78cbd7178ceaf7a54d924816)) +* **deps:** bump org.apache.logging.log4j:log4j-api ([#20](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/20)) ([13ec838](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/13ec8385e24b81e280f66f6897398b1385c8aec6)) +* **internal:** codegen related update ([9ff57d2](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/9ff57d291ace852b3fcea07c5db1444dd7ec5115)) +* merge main into release branch ([cb52b50](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/cb52b508a62ee885b19897c8b4661c6f0609601d)) +* neutralize coverage source classification ([0089073](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/0089073842fc8fe912ae99232b163a3be11313de)) +* preserve coverage gates with SPDX headers ([873219a](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/873219ac82c57fdceb4fef37c8393d447a03781f)) +* quiet Gradle cache cleanup warnings ([6b77330](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/6b77330a7fdf7e9c8ea84dee354e57e613564be3)) +* sync OpenAPI spec ([c225ad8](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/c225ad8eb377c71093ce3f9b5292257ce191ed3a)) +* update Gradle actions for Node 24 ([9c8a3a0](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/9c8a3a0da540d688aac6f5d9b128962c1d222876)) +* wire production_repo for all targets ([1a3de89](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/1a3de8994dc023e9602bc359a0f5067f03082e9a)) + + +### Documentation + +* add Contributor Covenant 2.1 Code of Conduct ([#2192](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/2192)) ([afb97b0](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/afb97b0d326a8ff3602f871cd12daa63c7a3d847)) +* add DeepWiki badge ([3b366af](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/3b366af5ff33de6e029885af3c7fedbf7c0a6b9a)) +* clarify repository discovery ([e6c115b](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/e6c115b50205ce8720f5d30c7cfdf48f780537b3)) +* correct Java test evidence count ([6e6a171](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/6e6a171d428896d32833fa94e5860c68ad7e2739)) +* map common X data tasks ([#12](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/12)) ([f3438ea](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/f3438ea480f091967958c158a10fc7722d6298fd)) +* mark Maven publication pending ([2aad6f6](https://github.com/Xquik-dev/x-twitter-scraper-java/commit/2aad6f6d7208d90eaffbb483677cd50a34f55559)) + ## [0.9.1](https://github.com/stainless-sdks/x-twitter-scraper-java/compare/v0.9.0...v0.9.1) (2026-08-11) ### Features diff --git a/README.md b/README.md index 413b6bf9..c00fcef0 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Use it as a typed X API or Twitter API alternative. -[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.9.1) -[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.9.1/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.9.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.xquik.api/x-twitter-scraper-java)](https://central.sonatype.com/artifact/com.xquik.api/x-twitter-scraper-java/0.10.0) +[![Javadocs](https://javadoc.io/badge2/com.xquik.api/x-twitter-scraper-java/0.10.0/javadoc.svg)](https://javadoc.io/doc/com.xquik.api/x-twitter-scraper-java/0.10.0) @@ -64,7 +64,7 @@ Use [API reference](https://docs.xquik.com/api-reference/overview) for request c Gradle: ```kotlin -implementation("com.xquik.api:x-twitter-scraper-java:0.9.1") +implementation("com.xquik.api:x-twitter-scraper-java:0.10.0") ``` Maven: @@ -73,7 +73,7 @@ Maven: com.xquik.api x-twitter-scraper-java - 0.9.1 + 0.10.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 02fe6e12..243958c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ licenseReport { allprojects { group = "com.xquik.api" - version = "0.9.1" // x-release-please-version + version = "0.10.0" // x-release-please-version dependencyLocking { lockAllConfigurations() From 5a00a553b0504032e7517266b4ad2553dc163e4e Mon Sep 17 00:00:00 2001 From: kriptoburak Date: Wed, 12 Aug 2026 17:57:10 +0300 Subject: [PATCH 8/8] docs: fix release comparison baseline --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e15d7b6d..311f4d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 0.10.0 (2026-08-12) -Full Changelog: [v0.9.1...v0.10.0](https://github.com/Xquik-dev/x-twitter-scraper-java/compare/v0.9.1...v0.10.0) +Full Changelog: [v0.8.4...v0.10.0](https://github.com/Xquik-dev/x-twitter-scraper-java/compare/v0.8.4...v0.10.0) ### Features