twitter-api-java-sdk's People
Forkers
ctkqiang thom-bahm yechanpark thananchseyan chengs2035 sideeffffect officialmutazawad zakichanu mrwhywu quangson91 totetmatt aliberkyucel kisaga nikitakoselev slvin gotoneshot nrv peymang tanbinh123 hizumiaoba beaelf mutazawadorg www-mutazawad-org marcelomrwin legend9999 junyang100 saunders-d meuman redracer93 ttldtor michael21zzz dlxwang bubu-nono siinfante smoodi danielbartl oriarditi tmoreira2020 starmiiz bugfree66 onurkoocc mjalvarez joelforjava hiro-matsuno agienka ryudongjae ninjayoto bernardogiordano ravidevk jdpgrailsdev mason0510 ai-awe mreenals yrlish vishvesh-jain sliifey swapnilnakate7 workmansir terrorizer1980 desintegrators lysiya magix-bugs bws9000 mkavanagh ku-nal tangwang9527 dhruv-bansal bodong-chips trediggs bobbyjow92 kryslynn93 dshetranjiwala-ontic luafanti livereach himanshiverma05 xianlaizhang huijunwutwitter-api-java-sdk's Issues
Unmanaged JsonSyntaxException on TweetsApi
In TweetsApi all methods xxxxWithHttpInfo doesn't manage Json exception.
For example, to have recent tweets, the flow goes into tweetsRecentSearchWithHttpInfo
that has this code (some parts omitted to help reading)
private ApiResponse<Get2TweetsSearchRecentResponse> tweetsRecentSearchWithHttpInfo(......) throws ApiException {
okhttp3.Call localVarCall = tweetsRecentSearchValidateBeforeCall(.....);
try {
Type localVarReturnType = new TypeToken<Get2TweetsSearchRecentResponse>(){}.getType();
return localVarApiClient.execute(localVarCall, localVarReturnType);
} catch (ApiException e) {
e.setErrorObject(localVarApiClient.getJSON().getGson().fromJson(e.getResponseBody(), new TypeToken<com.twitter.clientlib.model.ProblemOrError>(){}.getType()));
throw e;
}
}
For some calls I receive this error
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Not a JSON Object: "<!DOCTYPE"
at com.google.gson.Gson.fromJson(Gson.java:1003)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.twitter.clientlib.api.TweetsApi.tweetsRecentSearchWithHttpInfo(TweetsApi.java:5140)
at com.twitter.clientlib.api.TweetsApi.access$7300(TweetsApi.java:89)
at com.twitter.clientlib.api.TweetsApi$APItweetsRecentSearchRequest.execute(TweetsApi.java:5362)
......
Caused by: java.lang.IllegalStateException: Not a JSON Object: "<!DOCTYPE"
at com.google.gson.JsonElement.getAsJsonObject(JsonElement.java:91)
at com.twitter.clientlib.model.ProblemOrError$CustomTypeAdapterFactory$1.read(ProblemOrError.java:114)
at com.twitter.clientlib.model.ProblemOrError$CustomTypeAdapterFactory$1.read(ProblemOrError.java:86)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199
as you see in the catch part the setErrorObject assumes the response body is a valid JSON. But it isn't
Expected behavior
The setErrorObject part is surrounded by another try...catch
so the real ApiException with headers and body will be accessible by the caller class
Actual behavior
JsonSyntaxException completely hide the original exception
Steps to reproduce the behavior
IMHO this is a 'design' bug.
It happens randomly and without the original exception I'm unable to identify the cause
ReplaySetting enum not support "verified" value
I got an error Unexpected value verified
when I tried to get Tweets because ReplySettings isn't support value "reply_settings": "verified"
This kind of reply_settings
I received using below request to Twitter API
Below response payload
{
"data": [
{
"id": "1752384295568851027",
"conversation_id": "1752384295568851027",
"author_id": "3220664394",
"possibly_sensitive": false,
"created_at": "2024-01-30T17:32:28.000Z",
"lang": "en",
"entities": {
"mentions": [
{
"start": 169,
"end": 181,
"username": "AirbusSpace",
"id": "44324383"
}
],
"annotations": [
{
"start": 0,
"end": 5,
"probability": 0.9646,
"type": "Organization",
"normalized_text": "Airbus"
},
{
"start": 29,
"end": 43,
"probability": 0.4938,
"type": "Other",
"normalized_text": "Eutelsat OneWeb"
},
{
"start": 150,
"end": 155,
"probability": 0.8508,
"type": "Organization",
"normalized_text": "Airbus"
}
],
"urls": [
{
"start": 125,
"end": 148,
"url": "https://t.co/N54G8o791s",
"expanded_url": "https://bit.ly/48QZtaN",
"display_url": "bit.ly/48QZtaN",
"images": [
{
"url": "https://pbs.twimg.com/news_img/1752384308810268672/JDKjOTxF?format=jpg&name=orig",
"width": 1024,
"height": 512
},
{
"url": "https://pbs.twimg.com/news_img/1752384308810268672/JDKjOTxF?format=jpg&name=150x150",
"width": 150,
"height": 150
}
],
"status": 200,
"title": "Airbus Announces Purchase of Eutelsat OneWeb’s Stake in Satellite Constellation Manufacturing Joint Venture",
"description": "Washington D.C., USA, 29 January 2024 – Airbus U.S. Space & Defense, Inc. announced completion of a deal with Eutelsat OneWeb to purchase its 50% share of the Airbus OneWeb Satellites (AOS) joint venture.Airbus is now the sole owner of AOS and the satellite manufacturing facility in Merritt Island, Florida.The new structure is expected to provide maximum efficiency and increased competitiveness for commercial, institutional and national security space customers.“This agreement furthers our position as a market leader in the small satellite constellation business, building on our successful partnership with OneWeb,” said Robert Geckle, Chairman and CEO, Airbus U.S. Space & Defense, Inc. “We will continue mass producing small satellites for our customers and are excited for what the future holds for us on Florida’s Space Coast as we move forward,” he added.Airbus U.S. Space & Defense recently retooled the Merritt Island factory to accommodate the Arrow450 production line and is starting ",
"unwound_url": "https://airbus-ds-gs.com/2024/01/29/airbus-announces-purchase-of-eutelsat-onewebs-stake-in-satellite-constellation-manufacturing-joint-venture/"
}
],
"hashtags": [
{
"start": 149,
"end": 156,
"tag": "Airbus"
},
{
"start": 157,
"end": 168,
"tag": "Satellites"
}
]
},
"edit_controls": {
"edits_remaining": 5,
"is_edit_eligible": true,
"editable_until": "2024-01-30T18:32:28.000Z"
},
"public_metrics": {
"retweet_count": 0,
"reply_count": 0,
"like_count": 0,
"quote_count": 0,
"bookmark_count": 0,
"impression_count": 22
},
"reply_settings": "verified",
"edit_history_tweet_ids": [
"1752384295568851027"
],
"text": "Airbus Announces Purchase of Eutelsat OneWeb’s Stake in Satellite Constellation Manufacturing Joint Venture. Read more here: https://t.co/N54G8o791s\n#Airbus #Satellites @AirbusSpace",
"card_uri": "https://t.co/N54G8o791s"
}
],
"includes": {
"users": [
{
"description": "#AirbusDSGS is an industry leader in #satcom & networking systems. This is the official account of Airbus DS Government Solutions, Inc.",
"verified_type": "blue",
"name": "Airbus DS GS",
"public_metrics": {
"followers_count": 697,
"following_count": 490,
"tweet_count": 353,
"listed_count": 17,
"like_count": 161
},
"subscription_type": "None",
"most_recent_tweet_id": "1752384295568851027",
"receives_your_dm": false,
"location": "Plano, TX",
"protected": false,
"verified": true,
"created_at": "2015-05-19T20:12:05.000Z",
"id": "3220664394",
"profile_image_url": "https://pbs.twimg.com/profile_images/821232055929294848/2927oAMo_normal.jpg",
"url": "https://t.co/r2uPduoD3w",
"username": "airbusdsgs"
}
]
}
}
Deserialization of TweetSearchResponse throws an NPE in an environment where gson conflicts with other deps
NPE when deserializing TweetSearchResponse
in a web app environment with many dependencies (not in a simple Java SE environment with few dependencies)
Expected behavior
Deserializing a json string into a TweetSearchResponse
object
Actual behavior
The deserialization throws an NPE
Steps to reproduce the behavior
Create a String
example with the text in tyhe attached file, which is the serialization of a TweetSearchResponse
tweetRecentSearch.txt
Then do:
TweetSearchResponse tweetSearchResponse = TweetSearchResponse.fromJson(example);
In the context of a Java SE app with few dependencies, no exception is thrown.
The pom.xml of this simple app:
https://github.com/seinecle/nocodefunctions-as-api/blob/master/pom.xml
In the context of a web app with possibly conflicting dependencies (Google librairies relying on GSON as well), an NPE is thrown. The pom.xml of this bigger app:
https://github.com/seinecle/nocodefunctions/blob/master/pom.xml
Proposed solution
Do not rely on the TweetSearchResponse.fromJson()
method and use the Json Binding API instead:
dependencies: https://javaee.github.io/jsonb-spec/users-guide.html
code:
Jsonb jsonb = JsonbBuilder.create();
TweetSearchResponse tweetSearchResponse = jsonb.fromJson(example, TweetSearchResponse.class);
Broader suggestion
It could be interesting to drop the dependency on gson and rely on a more conventional json-b API and implementation:
FilteredStreamingTweet.fromJson deserialization issue
Trying to use Twitter Stream Stream. When parsing stream buffer response with FilteredStreamingTweet.fromJson
got this error :
com.google.gson.JsonSyntaxException: java.io.IOException: Failed deserialization for FilteredStreamingTweet: 2 classes match result, expected 1. (Full log at the end)
Expected behavior
When using FilteredStreamingTweet.fromJson, expecing to have most of the response parsed
Actual behavior
Failing
Steps to reproduce the behavior
Get HelloWorldStreaming example,
Replace sample stream to have a valid search stream
Replace StreamingTweet
to FilteredStreamingTweet
Run the example.
Extra data
Full log dump
com.google.gson.JsonSyntaxException: java.io.IOException: Failed deserialization for FilteredStreamingTweet: 2 classes match result, expected 1. JSON: {"data":{"attachments":{"media_keys":["3_1529016941515378688","3_1529016942450774021"]},"author_id":"2354994440","created_at":"2022-05-24T08:30:23.000Z","entities":{"mentions":[{"start":0,"end":12,"username":"taegiveaway","id":"1353847968919547904"},{"start":13,"end":26,"username":"ZelenskiyNFT","id":"1509950611483156488"}],"urls":[{"start":41,"end":64,"url":"https://t.co/sT4XPjBilR","expanded_url":"https://twitter.com/seokshuu/status/1529016950554099713/photo/1","display_url":"pic.twitter.com/sT4XPjBilR","media_key":"3_1529016941515378688"},{"start":41,"end":64,"url":"https://t.co/sT4XPjBilR","expanded_url":"https://twitter.com/seokshuu/status/1529016950554099713/photo/1","display_url":"pic.twitter.com/sT4XPjBilR","media_key":"3_1529016942450774021"}]},"geo":{},"id":"1529016950554099713","in_reply_to_user_id":"1353847968919547904","lang":"en","referenced_tweets":[{"type":"replied_to","id":"1527450748275290112"}],"text":"@taegiveaway @ZelenskiyNFT acc suspended https://t.co/sT4XPjBilR"},"includes":{"media":[{"media_key":"3_1529016941515378688","type":"photo","url":"https://pbs.twimg.com/media/FTgozqaUEAAhmgJ.jpg"},{"media_key":"3_1529016942450774021","type":"photo","url":"https://pbs.twimg.com/media/FTgozt5VEAU9qVA.jpg"}],"users":[{"created_at":"2014-02-21T15:52:24.000Z","description":"broke","id":"2354994440","location":"mea","name":"diane","profile_image_url":"https://pbs.twimg.com/profile_images/1526821401831026688/YVme9vgY_normal.jpg","protected":false,"url":"","username":"seokshuu","verified":false},{"created_at":"2021-01-25T23:31:33.000Z","description":"proofs winner #taefairy","entities":{"url":{"urls":[{"start":0,"end":23,"url":"https://t.co/vVo16A5mXM","expanded_url":"https://twitter.com/i/events/1483286885791375365","display_url":"twitter.com/i/events/14832…"}]},"description":{"hashtags":[{"start":14,"end":23,"tag":"taefairy"}]}},"id":"1353847968919547904","location":"check list active giveaways ↓","name":"taeby","profile_image_url":"https://pbs.twimg.com/profile_images/1433287575741943811/yGxUdigL_normal.png","protected":false,"url":"https://t.co/vVo16A5mXM","username":"taegiveaway","verified":false}],"tweets":[{"attachments":{},"author_id":"1353847968919547904","context_annotations":[{"domain":{"id":"65","name":"Interests and Hobbies Vertical","description":"Top level interests and hobbies groupings, like Food or Travel"},"entity":{"id":"1280550787207147521","name":"Arts & culture"}},{"domain":{"id":"66","name":"Interests and Hobbies Category","description":"A grouping of interests and hobbies entities, like Novelty Food or Destinations"},"entity":{"id":"913142676819648512","name":"Cryptocurrencies","description":"Cryptocurrency"}},{"domain":{"id":"66","name":"Interests and Hobbies Category","description":"A grouping of interests and hobbies entities, like Novelty Food or Destinations"},"entity":{"id":"1046577790353428480","name":"Visual arts","description":"Visual Arts"}},{"domain":{"id":"123","name":"Ongoing News Story","description":"Ongoing News Stories like 'Brexit'"},"entity":{"id":"1484601166080081920","name":"Russo-Ukrainian conflict"}},{"domain":{"id":"46","name":"Brand Category","description":"Categories within Brand Verticals that narrow down the scope of Brands"},"entity":{"id":"781974596752842752","name":"Services"}},{"domain":{"id":"47","name":"Brand","description":"Brands and Companies"},"entity":{"id":"10045225402","name":"Twitter"}}],"created_at":"2022-05-20T00:46:51.000Z","entities":{"annotations":[{"start":149,"end":155,"probability":0.8075,"type":"Place","normalized_text":"Ukraine"}],"hashtags":[{"start":174,"end":191,"tag":"StandWithUkraine"},{"start":192,"end":204,"tag":"HelpUkraine"},{"start":205,"end":210,"tag":"NFTs"},{"start":211,"end":215,"tag":"NFT"},{"start":216,"end":229,"tag":"NFTCommunity"},{"start":230,"end":237,"tag":"crypto"},{"start":238,"end":246,"tag":"Ukraine"},{"start":247,"end":251,"tag":"art"}],"mentions":[{"start":56,"end":69,"username":"ZelenskiyNFT","id":"1509950611483156488"},{"start":126,"end":139,"username":"ZelenskiyNFT","id":"1509950611483156488"}],"urls":[{"start":254,"end":277,"url":"https://t.co/mHrgcY1abQ","expanded_url":"https://twitter.com/zelenskiynft/status/1526282601991897088","display_url":"twitter.com/zelenskiynft/s…"}]},"geo":{},"id":"1527450748275290112","lang":"en","text":"$500 | 7.000.000 IDR • 7 DAYS 🌷\n\n$250\n- RT & Follow @ZelenskiyNFT\n\n(+) $250\n- QRT attached tweet + Tag 3 friends with👇\n\n\" @ZelenskiyNFT Stands For Ukraine 🇺🇦� WL Opened! #StandWithUkraine #HelpUkraine #NFTs #NFT #NFTCommunity #crypto #Ukraine #art \" https://t.co/mHrgcY1abQ"}]},"errors":[{"parameter":"entities.mentions.username","resource_id":"ZelenskiyNFT","value":"ZelenskiyNFT","detail":"User has been suspended: [ZelenskiyNFT].","title":"Forbidden","resource_type":"user","type":"https://api.twitter.com/2/problems/resource-not-found"}],"matching_rules":[{"id":"1527970068608561153","tag":"ukraine"}]}
at com.google.gson.Gson.fromJson(Gson.java:1006)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.google.gson.Gson.fromJson(Gson.java:876)
at com.twitter.clientlib.model.FilteredStreamingTweet.fromJson(FilteredStreamingTweet.java:283)
at fr.totetmatt.gephi.twitter.utils.listener.filtered.TweetsStreamListenersExecutor$TweetsQueuer.queueTweets(TweetsStreamListenersExecutor.java:124)
at fr.totetmatt.gephi.twitter.utils.listener.filtered.TweetsStreamListenersExecutor$TweetsQueuer.run(TweetsStreamListenersExecutor.java:109)
Caused by: java.io.IOException: Failed deserialization for FilteredStreamingTweet: 2 classes match result, expected 1. JSON: {"data":{"attachments":{"media_keys":["3_1529016941515378688","3_1529016942450774021"]},"author_id":"2354994440","created_at":"2022-05-24T08:30:23.000Z","entities":{"mentions":[{"start":0,"end":12,"username":"taegiveaway","id":"1353847968919547904"},{"start":13,"end":26,"username":"ZelenskiyNFT","id":"1509950611483156488"}],"urls":[{"start":41,"end":64,"url":"https://t.co/sT4XPjBilR","expanded_url":"https://twitter.com/seokshuu/status/1529016950554099713/photo/1","display_url":"pic.twitter.com/sT4XPjBilR","media_key":"3_1529016941515378688"},{"start":41,"end":64,"url":"https://t.co/sT4XPjBilR","expanded_url":"https://twitter.com/seokshuu/status/1529016950554099713/photo/1","display_url":"pic.twitter.com/sT4XPjBilR","media_key":"3_1529016942450774021"}]},"geo":{},"id":"1529016950554099713","in_reply_to_user_id":"1353847968919547904","lang":"en","referenced_tweets":[{"type":"replied_to","id":"1527450748275290112"}],"text":"@taegiveaway @ZelenskiyNFT acc suspended https://t.co/sT4XPjBilR"},"includes":{"media":[{"media_key":"3_1529016941515378688","type":"photo","url":"https://pbs.twimg.com/media/FTgozqaUEAAhmgJ.jpg"},{"media_key":"3_1529016942450774021","type":"photo","url":"https://pbs.twimg.com/media/FTgozt5VEAU9qVA.jpg"}],"users":[{"created_at":"2014-02-21T15:52:24.000Z","description":"broke","id":"2354994440","location":"mea","name":"diane","profile_image_url":"https://pbs.twimg.com/profile_images/1526821401831026688/YVme9vgY_normal.jpg","protected":false,"url":"","username":"seokshuu","verified":false},{"created_at":"2021-01-25T23:31:33.000Z","description":"proofs winner #taefairy","entities":{"url":{"urls":[{"start":0,"end":23,"url":"https://t.co/vVo16A5mXM","expanded_url":"https://twitter.com/i/events/1483286885791375365","display_url":"twitter.com/i/events/14832…"}]},"description":{"hashtags":[{"start":14,"end":23,"tag":"taefairy"}]}},"id":"1353847968919547904","location":"check list active giveaways ↓","name":"taeby","profile_image_url":"https://pbs.twimg.com/profile_images/1433287575741943811/yGxUdigL_normal.png","protected":false,"url":"https://t.co/vVo16A5mXM","username":"taegiveaway","verified":false}],"tweets":[{"attachments":{},"author_id":"1353847968919547904","context_annotations":[{"domain":{"id":"65","name":"Interests and Hobbies Vertical","description":"Top level interests and hobbies groupings, like Food or Travel"},"entity":{"id":"1280550787207147521","name":"Arts & culture"}},{"domain":{"id":"66","name":"Interests and Hobbies Category","description":"A grouping of interests and hobbies entities, like Novelty Food or Destinations"},"entity":{"id":"913142676819648512","name":"Cryptocurrencies","description":"Cryptocurrency"}},{"domain":{"id":"66","name":"Interests and Hobbies Category","description":"A grouping of interests and hobbies entities, like Novelty Food or Destinations"},"entity":{"id":"1046577790353428480","name":"Visual arts","description":"Visual Arts"}},{"domain":{"id":"123","name":"Ongoing News Story","description":"Ongoing News Stories like 'Brexit'"},"entity":{"id":"1484601166080081920","name":"Russo-Ukrainian conflict"}},{"domain":{"id":"46","name":"Brand Category","description":"Categories within Brand Verticals that narrow down the scope of Brands"},"entity":{"id":"781974596752842752","name":"Services"}},{"domain":{"id":"47","name":"Brand","description":"Brands and Companies"},"entity":{"id":"10045225402","name":"Twitter"}}],"created_at":"2022-05-20T00:46:51.000Z","entities":{"annotations":[{"start":149,"end":155,"probability":0.8075,"type":"Place","normalized_text":"Ukraine"}],"hashtags":[{"start":174,"end":191,"tag":"StandWithUkraine"},{"start":192,"end":204,"tag":"HelpUkraine"},{"start":205,"end":210,"tag":"NFTs"},{"start":211,"end":215,"tag":"NFT"},{"start":216,"end":229,"tag":"NFTCommunity"},{"start":230,"end":237,"tag":"crypto"},{"start":238,"end":246,"tag":"Ukraine"},{"start":247,"end":251,"tag":"art"}],"mentions":[{"start":56,"end":69,"username":"ZelenskiyNFT","id":"1509950611483156488"},{"start":126,"end":139,"username":"ZelenskiyNFT","id":"1509950611483156488"}],"urls":[{"start":254,"end":277,"url":"https://t.co/mHrgcY1abQ","expanded_url":"https://twitter.com/zelenskiynft/status/1526282601991897088","display_url":"twitter.com/zelenskiynft/s…"}]},"geo":{},"id":"1527450748275290112","lang":"en","text":"$500 | 7.000.000 IDR • 7 DAYS 🌷\n\n$250\n- RT & Follow @ZelenskiyNFT\n\n(+) $250\n- QRT attached tweet + Tag 3 friends with👇\n\n\" @ZelenskiyNFT Stands For Ukraine 🇺🇦� WL Opened! #StandWithUkraine #HelpUkraine #NFTs #NFT #NFTCommunity #crypto #Ukraine #art \" https://t.co/mHrgcY1abQ"}]},"errors":[{"parameter":"entities.mentions.username","resource_id":"ZelenskiyNFT","value":"ZelenskiyNFT","detail":"User has been suspended: [ZelenskiyNFT].","title":"Forbidden","resource_type":"user","type":"https://api.twitter.com/2/problems/resource-not-found"}],"matching_rules":[{"id":"1527970068608561153","tag":"ukraine"}]}
at com.twitter.clientlib.model.FilteredStreamingTweet$CustomTypeAdapterFactory$1.read(FilteredStreamingTweet.java:155)
at com.twitter.clientlib.model.FilteredStreamingTweet$CustomTypeAdapterFactory$1.read(FilteredStreamingTweet.java:92)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:991)
... 6 more
Method to retrieve the embeddable version of tweet?
I am looking for a method to retrieve the html code that corresponds to a tweet's embeddable version?
For refernce, Twitter4J provides such a set of methods with the OEmbedRequest
and OEmbed
classes:
OEmbedRequest oEmbedRequest = new OEmbedRequest(Long.valueOf(tweet.getId()), "");
oEmbedRequest.setMaxWidth(550);
oEmbedRequest.setOmitScript(true);
twitter = TwitterFactory.getInstance();
OEmbed embed = twitter.getOEmbed(oEmbedRequest);
String tweetAsHtml = embed.getHtml();
https://twitter4j.org/oldjavadocs/3.0.2/twitter4j/OEmbedRequest.html
Thank you.
Adding tweets usage endpoint
Hey guys, do you plan to add to your SDK https://api.twitter.com/2/usage/tweets
endpoint handling?
https://developer.twitter.com/en/docs/twitter-api/usage/tweets/introduction
IMO it'll be very helpful to check for limits before executing other actions.
tweets().searchStream().execute() throws 400 error with no response body if additional parameters are included
TwitterApi.tweets().searchStream().execute() throws 400 error with no response body if additional parameters are included
Expected behavior
defining the call as
Integer backfillMinutes = 1;
OffsetDateTime startTime = OffsetDateTime.parse("2022-10-01T18:40:40.000Z"); // OffsetDateTime | YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Tweets will be provided.
OffsetDateTime endTime = OffsetDateTime.parse("2022-10-14T18:40:40.000Z"); // OffsetDateTime | YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Tweets will be provided.
Set<String> tweetFields = new HashSet<>(Arrays.asList("conversation_id", "author_id")); // Set<String> | A comma separated list of Tweet fields to display.
apiInstance.tweets().searchStream()
.tweetFields(tweetFields)
.backfillMinutes()
.startTime(startTime)
.endTime(endTime)
.execute();
enables the additional functionality of those fields within the searchStream.
Actual behavior
when .execute() is called, a 400 APIException is thrown with a null message body
Steps to reproduce the behavior
As per the code above will reproduce this error.
It is also discovered that .tweetFields() does not trigger this error, as does excluding all optional fields.
No updates in months
Although the API works now, the project has not had updates in months. Does anybody know if it is going to be maintained or just archived.
json serialization error
Error with Json deserialization
Expected behavior
Json serialization should run fine when fetching tweets
Actual behavior
A json serialization error occurs. I don't have the corresponding tweet. The trace is:
javax.json.bind.JsonbException: Internal error: No enum constant com.twitter.clientlib.model.Point.TypeEnum.Point
Steps to reproduce the behavior
Fetch tweets
always give the forbidden error
it always give this error, and not have any runnable offical demo
{
"client_id": "27988566",
"detail": "When authenticating requests to the Twitter API v2 endpoints, you must use keys and tokens from a Twitter developer App that is attached to a Project. You can create a project via the developer portal.",
"registration_url": "https://developer.twitter.com/en/docs/projects/overview",
"title": "Client Forbidden",
"required_enrollment": "Appropriate Level of API Access",
"reason": "client-not-enrolled",
"type": "https://api.twitter.com/2/problems/client-forbidden"
}
Is the SDK support POST follow request
I go try the documentation and I didn't see oauth1 method as I know POST follow request for example use that auth method also from the href for the endpoint its list all endpoints is that mean is support all of them
RulesResponseMetadata throwing exeption on new field from Twitter
OAuth 2 error
Using createTweet method that requires OAuth.
As per the example TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN")); is giving 401 error so tried creating OAuth 2 credential using
TwitterCredentialsOAuth2 credentials = new TwitterCredentialsOAuth2(System.getenv("TWITTER_OAUTH2_CLIENT_ID"),
System.getenv("TWITTER_OAUTH2_CLIENT_SECRET"),
System.getenv("TWITTER_OAUTH2_ACCESS_TOKEN"),
System.getenv("TWITTER_OAUTH2_REFRESH_TOKEN"));
Used the credentials in developer's portal
OAuth 2.0 Client ID and Client Secret
Access Token used
Generated May 9, 2022
For @
Created with Read and Write permissions
I have elevated access. However getting the error
Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint. Supported authentication types are [OAuth 1.0a User Context, OAuth 2.0 User Context].
No url property in media object
One line summary of the issue here.
There is no intuitive way to extract media url from any tweet response.
Expected behavior
Presence of url property in media object
Actual behavior
Url of the media object is shown in the text of data property of tweet object
Steps to reproduce the behavior
Please list all relevant steps to reproduce the observed behavior.
V2 API - Unauthorized 401
As described in this thread https://twittercommunity.com/t/v2-api-unauthorized-401/188899 we are having the same problem, the library version we are using is 2.0.2 but this also happens with 2.0.3.
Has anyone managed to get this library to work with OAuth 1.0a in order to allow us to migrate temporarily?
Filtered Stream
Can you update StreamingTweet type to add matching_rules variable?
Is searchStream can wait data on stream infinitly? What do you advice me to wait and write stream infinitly?
How to generate refresh token?
I have client id, client secret, access token, access token secret, bearer token, consumer key, consumer secret.
But I need to get refresh token to make this
TwitterApi twitter = new TwitterApi(
new TwitterCredentialsOAuth2(
clientId, clientSecret, accessToken,refreshToken
)
);
How can I generate this refresh token?
Building a bot
Hello zacmos,
Thank you for sharing this comprehensive library.
Please pardon my ignorance but how would you recommend building a bot for replying/acting everytime that someone @'s its name?
There exists the streaming API but how would one use this reacting to being called instead of pooling twitter every few seconds.
Perhaps possible to provide a sample example? Many thanks!!
OAuth 1.0a User Context authentication
Is it possible / will it be possible to use this library with OAuth 1.0a User Context in the future?
In the API v2 documentation I read that you can access:
- OAuth 1.0a User Context
- OAuth 2.0 App-Only
- OAuth 2.0 Authorization code with PKCE
`source` of tweets retrieved by stream API is null for about two days now.
source
of tweets retrieved by stream API is null for about two days now.
Expected behavior
tweet.getSource()
returns the source.
Actual behavior
tweet.getSource()
returns null
Steps to reproduce the behavior
Normally using stream API.
TwitterApi apiInstance = new TwitterApi(
new TwitterCredentialsBearer(
"XXXX"//dummy
));
// Set the params values
Set<String> tweetFields = new HashSet<>(Arrays.asList("id", "author_id", "created_at", "lang", "source", "text", "entities", "public_metrics")); // Set<String> | A comma separated list of Tweet fields to display.
Set<String> expansions = new HashSet<>(Arrays.asList()); // Set<String> | A comma separated list of fields to expand.
Set<String> mediaFields = new HashSet<>(Arrays.asList()); // Set<String> | A comma separated list of Media fields to display.
Set<String> pollFields = new HashSet<>(Arrays.asList()); // Set<String> | A comma separated list of Poll fields to display.
Set<String> userFields = new HashSet<>(Arrays.asList("created_at", "description", "public_metrics", "verified")); // Set<String> | A comma separated list of User fields to display.
Set<String> placeFields = new HashSet<>(Arrays.asList("country_code")); // Set<String> | A comma separated list of Place fields to display.
try {
InputStream result = apiInstance.tweets().sampleStream()
.tweetFields(tweetFields)
.expansions(expansions)
.mediaFields(mediaFields)
.pollFields(pollFields)
.userFields(userFields)
.placeFields(placeFields)
.execute();
try {
Type localVarReturnType = new TypeToken<StreamingTweetResponse>() {
}.getType();
BufferedReader reader = new BufferedReader(new InputStreamReader(result, "UTF-8"));
String line = reader.readLine();
while (line != null) {
if (line.isEmpty()) {
//System.out.println("==> Empty line");
line = reader.readLine();
continue;
}
//System.out.println(response != null ? response.toString() : "Null object");
try {
StreamingTweetResponse response = JSON.getGson().fromJson(line, localVarReturnType);
if (response != null) {
Tweet tweet = response.getData();
//------------------------------------
//------------------------------------
//------------------------------------
//Place which problem happens.
if (tweet != null) System.out.println(tweet.getSource());//null
//------------------------------------
//------------------------------------
//------------------------------------
}
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage() + ": " + line);
}
line = reader.readLine();
}
} catch (StreamResetException e) {
e.printStackTrace();
String[] tmp = new String[0];
main(tmp);
} catch (Exception e) {
e.printStackTrace();
}
} catch (ApiException e) {
System.err.println("Exception when calling TweetsApi#sampleStream");
System.err.println("Status code: " + e.getCode());
System.err.println("Reason: " + e.getResponseBody());
System.err.println("Response headers: " + e.getResponseHeaders());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
How to retrieve fully hydrated Tweet objects (including a User, ...) ?
Hi all,
thanks for providing an official Twitter API SDK.
I am currently in the process of migrating away from Twitter4J (API v1). Therefore, I need to update our adapters / bridges to use the SDK. While working on this migration, I developed some questions:
From my previous experiments with API v2 and an homecrown Twitter4J v2 adapter, I am remembering, that I could use expansions to get fully hydrated response objects like
//https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/tweet
params.add(new HttpParameter("tweet.fields", "public_metrics,created_at,entities,geo,lang,source,referenced_tweets,in_reply_to_user_id"));
params.add(new HttpParameter("expansions", "author_id,attachments.media_keys,geo.place_id"));
params.add(new HttpParameter("media.fields", "duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width"));
params.add(new HttpParameter("place.fields", "country,geo,name,place_type,contained_within"));
Plain JSON responses then contain an includes
field (i.,e. for Place
or Media
), which can be used to map keys
or ids
to the correct hydrated
elements and bridge this gap. Also it can be used to retrieve additional information.
My question is:
How can I get a fully hydrated Tweet
object, which contains hydrated Media
, Place
and User
ids rather than only the key / id for it?
I couldn't find any fields / getters for the Tweet
object and I don't think, that using additional requests to look them up are a intended behaviour?
Happy to hear from you.
Thanks and Gruß
Richard
IllegalAccessError exception
I used TweetsStreamListenersExecutor example to fetch tweets and got the below exception.
Exception
Should throw the exception
Exception in thread "Thread-118" java.lang.IllegalAccessError: class io.gsonfire.gson.HooksTypeAdapter (in unnamed module @0x3f64b8f4) cannot access class com.google.gson.internal.bind.JsonTreeReader (in module com.google.gson) because module com.google.gson does not export com.google.gson.internal.bind to unnamed module @0x3f64b8f4
at io.gsonfire.gson.HooksTypeAdapter.deserialize(HooksTypeAdapter.java:84)
at io.gsonfire.gson.HooksTypeAdapter.read(HooksTypeAdapter.java:54)
at [email protected]/com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at [email protected]/com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at [email protected]/com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at [email protected]/com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at [email protected]/com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at [email protected]/com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at [email protected]/com.twitter.clientlib.model.FilteredStreamingTweet$CustomTypeAdapterFactory$1.read(FilteredStreamingTweet.java:326)
at [email protected]/com.twitter.clientlib.model.FilteredStreamingTweet$CustomTypeAdapterFactory$1.read(FilteredStreamingTweet.java:315)
at [email protected]/com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at [email protected]/com.google.gson.Gson.fromJson(Gson.java:991)
at [email protected]/com.google.gson.Gson.fromJson(Gson.java:956)
at [email protected]/com.google.gson.Gson.fromJson(Gson.java:905)
at [email protected]/com.google.gson.Gson.fromJson(Gson.java:876)
at [email protected]/com.twitter.clientlib.model.FilteredStreamingTweet.fromJson(FilteredStreamingTweet.java:341)
at com.numex/com.numex.util.twitterclient.TweetsStreamListenersExecutor$TweetsQueuer.queueTweets(TweetsStreamListenersExecutor.java:101)
at com.numex/com.numex.util.twitterclient.TweetsStreamListenersExecutor$TweetsQueuer.run(TweetsStreamListenersExecutor.java:87)
javax.ws.rs:javax.ws.rs-api issue
When I included the SDK into my project and rebuild project, it make error as below:
Execution failed for task ':app:mergeExtDexAppDebug'.
Could not resolve all files for configuration ':app:AppDebugRuntimeClasspath'.
Failed to transform twitter-api-java-sdk-2.0.3.jar (com.twitter:twitter-api-java-sdk:2.0.3) to match attributes {artifactType=android-dex, asm-transformed-variant=NONE, dexing-enable-desugaring=true, dexing-enable-jacoco-instrumentation=false, dexing-is-debuggable=true, dexing-min-sdk=23, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-runtime}.
> Could not resolve all files for configuration ':app:AppDebugRuntimeClasspath'.
> No variants of javax.ws.rs:javax.ws.rs-api:2.1.1 match the consumer attributes:
- javax.ws.rs:javax.ws.rs-api:2.1.1 configuration runtime declares a component for use during runtime:
- Incompatible because this component declares a component, as well as attribute 'artifactType' with value '${packaging.type}' and the consumer needed a component, as well as attribute 'artifactType' with value 'android-classes-jar'
- Other compatible attributes:
- Doesn't say anything about asm-transformed-variant (required 'NONE')
- Doesn't say anything about com.android.build.api.attributes.AgpVersionAttr (required '8.1.0')
- Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
- Doesn't say anything about com.android.build.api.attributes.ProductFlavor:project (required 'Qubii_Pro')
- Doesn't say anything about dexing-enable-desugaring (required 'true')
- Doesn't say anything about dexing-enable-jacoco-instrumentation (required 'false')
- Doesn't say anything about dexing-is-debuggable (required 'true')
- Doesn't say anything about dexing-min-sdk (required '23')
- Doesn't say anything about its target Java environment (preferred optimized for Android)
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
parameter missing from Media Object
Hello everyone,
i am trying to collect images from tweets with the v2 timeline API adding attachments and mediaUrls.
?expansions=attachments.media_keys&media.fields=url
Expected behavior
I was expecting in the "Media" object an "url" parameter
Actual behavior
"url" parameter is missing from the "Media" object
Steps to reproduce the behavior
using the method:
usersIdTweets().getIncludes.getMedia
Thanks in advance for the Help.
When authenticating requests to the Twitter API v2 endpoints
TwitterCredentialsOAuth2 twitterCredentialsOAuth2 = new TwitterCredentialsOAuth2(xxx, xxx, xxx, xxx);
TwitterApi apiInstance = new TwitterApi(twitterCredentialsOAuth2);
apiInstance.tweets().createTweet(new TweetCreateRequest().text("hellow world"));
{"client_id":"27512824","detail":"When authenticating requests to the Twitter API v2 endpoints, you must use keys and tokens from a Twitter developer App that is attached to a Project. You can create a project via the developer portal.","registration_url":"https://developer.twitter.com/en/docs/projects/overview","title":"Client Forbidden","required_enrollment":"Appropriate Level of API Access","reason":"client-not-enrolled","type":"https://api.twitter.com/2/problems/client-forbidden"}
When I push Twitter, I report an error and I guarantee that my app is not a standalone app in the project 。 What permissions do I need to activate。
where can i download compiled .jar?
as title says
java.lang.IllegalArgumentException: The required field `id` is not found in the JSON string
抓用户信息的时候,mention 字段并没有 id 这个字段,但是openapiRequiredFields.add("id");将他加上去了,请检查 MentionEntity.java
Expected behavior
java.lang.IllegalArgumentException: The required field id
is not found in the JSON string
Actual behavior
public static Set<String> userFields = Set.of(
"created_at", "description", "entities", "id", "location", "name", "pinned_tweet_id", "profile_image_url", "protected", "public_metrics", "url", "username", "verified", "withheld"
);
try {
Get2UsersIdResponse userById = instance.users().findUserById("276372500").userFields(TwitterFields.userFields).execute();
User user = userById.getData();
Assert.assertNotNull(user);
} catch (ApiException e) {
throw new RuntimeException(e);
}
当添加了entities字段时,就会因检测id字段而出错
完整报错如下:
java.lang.IllegalArgumentException: The required field `id` is not found in the JSON string: {"start":31,"end":43,"username":"MCNVentures"}
at com.twitter.clientlib.model.MentionEntity.validateJsonObject(MentionEntity.java:259)
at com.twitter.clientlib.model.FullTextEntities.validateJsonObject(FullTextEntities.java:369)
at com.twitter.clientlib.model.UserEntities.validateJsonObject(UserEntities.java:192)
at com.twitter.clientlib.model.User.validateJsonObject(User.java:568)
at com.twitter.clientlib.model.Get2UsersIdResponse.validateJsonObject(Get2UsersIdResponse.java:232)
at com.twitter.clientlib.model.Get2UsersIdResponse$CustomTypeAdapterFactory$1.read(Get2UsersIdResponse.java:273)
at com.twitter.clientlib.model.Get2UsersIdResponse$CustomTypeAdapterFactory$1.read(Get2UsersIdResponse.java:263)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:963)
at com.google.gson.Gson.fromJson(Gson.java:928)
at com.google.gson.Gson.fromJson(Gson.java:877)
at com.twitter.clientlib.JSON.deserialize(JSON.java:944)
at com.twitter.clientlib.ApiClient.deserialize(ApiClient.java:959)
at com.twitter.clientlib.ApiClient.handleResponse(ApiClient.java:1190)
at com.twitter.clientlib.ApiClient.execute(ApiClient.java:1091)
at com.twitter.clientlib.api.UsersApi.findUserByIdWithHttpInfo(UsersApi.java:347)
at com.twitter.clientlib.api.UsersApi.access$500(UsersApi.java:76)
at com.twitter.clientlib.api.UsersApi$APIfindUserByIdRequest.execute(UsersApi.java:430)
at com.mifengcha.core.client.TwitterClientTest.testTwitterUser(TwitterClientTest.java:55)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
how to update tweet with this SDK?
Expected behavior
Must update tweet once created by user
Actual behavior
Not mention any endpoint for this
message "java.io.FileNotFoundException: sdk.properties"
why?
how to upload meida with this SDK?
Incompatibility with spring-boot-starter-parent: okhttp3.RequestBody.create not found
Expected behavior
I'm trying to set up rules to filter stream request using the method:
https://github.com/twitterdev/twitter-api-java-sdk/blob/main/docs/TweetsApi.md#addOrDeleteRules
But adding a rules to the AddOrDeleteRulesRequest.
here my code: (simplified to isolate the problem)
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
TwitterCredentialsBearer credentials = new TwitterCredentialsBearer("*************");
TwitterApi apiInstance = new TwitterApi();
apiInstance.setTwitterCredentials(credentials);
// create new rules from a keywords array
String[] keywords = {"Java", "Microservices", "Spring", "Kafka", "Elasticseach"};
AddRulesRequest addRulesRequest = new AddRulesRequest();
for (String keyword : keywords) {
addRulesRequest.addAddItem(new RuleNoId().value(keyword).tag(keyword));
}
// Set the params values
AddOrDeleteRulesRequest addOrDeleteRulesRequest = new AddOrDeleteRulesRequest(
addRulesRequest); // AddOrDeleteRulesRequest |
Boolean dryRun = true; // Boolean | Dry Run can be used with both the add and delete action, with the expected result given, but without actually taking any action in the system (meaning the end state will always be as it was when the request was submitted). This is particularly useful to validate rule changes.
try {
AddOrDeleteRulesResponse result = apiInstance.tweets()
.addOrDeleteRules(addOrDeleteRulesRequest, dryRun);
System.out.println(result);
} catch (ApiException e) {
System.err.println("Exception when calling TweetsApi#addOrDeleteRules");
System.err.println("Status code: " + e.getCode());
System.err.println("Reason: " + e.getResponseBody());
System.err.println("Response headers: " + e.getResponseHeaders());
e.printStackTrace();
}
}
}
Actual behavior
But when I run the application I received this error:
Exception in thread "main" java.lang.NoSuchMethodError: 'okhttp3.RequestBody okhttp3.RequestBody.create(java.lang.String, okhttp3.MediaType)'
at com.twitter.clientlib.ApiClient.serialize(ApiClient.java:956)
at com.twitter.clientlib.ApiClient.buildRequest(ApiClient.java:1227)
at com.twitter.clientlib.ApiClient.buildCall(ApiClient.java:1173)
at com.twitter.clientlib.api.TweetsApi.addOrDeleteRulesCall(TweetsApi.java:132)
at com.twitter.clientlib.api.TweetsApi.addOrDeleteRulesValidateBeforeCall(TweetsApi.java:144)
at com.twitter.clientlib.api.TweetsApi.addOrDeleteRulesWithHttpInfo(TweetsApi.java:211)
at com.twitter.clientlib.api.TweetsApi.addOrDeleteRules(TweetsApi.java:166)
at com.example.demo.Example.main(Example.java:28)
Or if I run inside @SpringBootApplication
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
com.twitter.clientlib.ApiClient.serialize(ApiClient.java:956)
The following method did not exist:
'okhttp3.RequestBody okhttp3.RequestBody.create(java.lang.String, okhttp3.MediaType)'
The calling method's class, com.twitter.clientlib.ApiClient, was loaded from the following location:
jar:file:/Users/gerard.puig/.m2/repository/com/twitter/twitter-api-java-sdk/1.1.3/twitter-api-java-sdk-1.1.3.jar!/com/twitter/clientlib/ApiClient.class
The called method's class, okhttp3.RequestBody, is available from the following locations:
jar:file:/Users/gerard.puig/.m2/repository/com/squareup/okhttp3/okhttp/3.14.9/okhttp-3.14.9.jar!/okhttp3/RequestBody.class
The called method's class hierarchy was loaded from the following locations:
okhttp3.RequestBody: file:/Users/gerard.puig/.m2/repository/com/squareup/okhttp3/okhttp/3.14.9/okhttp-3.14.9.jar
Action:
Correct the classpath of your application so that it contains compatible versions of the classes com.twitter.clientlib.ApiClient and okhttp3.RequestBody
I think the problem comes from use spring-boot-starter-parent created with: https://start.spring.io/
I think spring boot starter parent loads a version 4.9.2 of okhttp library and that makes that the ApiClient don't found the okhttp version 3.14.9
Steps to reproduce the behavior
try to execute the code above with POM.xml from spring Initializr
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>twitter-api-java-sdk</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
why every damn java api are abandonned
title
Systematic 401 unauthorized with tweetsRecentSearch and bearer token
I do not manage to extract the recent tweets of a specific Twitter account with the method tweetsRecentSearch
, but the same request with curl
works.
The curl
request:
curl --request GET 'https://api.twitter.com/2/tweets/search/recent?query=from:elonmusk' --header 'Authorization: Bearer $bearer'
{"data":[{"id":"1547102805600632832","text":"@cb_doge Ancient times"},{"id":"1547094594466332672","text":"Was just up in the booster propulsion section. Damage appears to be minor, but we need to inspect all the engines. Best to do this in the high bay."},{"id":"1547093032301985794","text":"Starship launch site tonight https://t.co/Len70RGCNf"},{"id":"1547081728979869697","text":"@Teslarati @ResidentSponge Join our underground movement!"},{"id":"1547028854161788929","text":"Excited about @NASAWebb potential!"},{"id":"1547002861942448128","text":"@ashleevance https://t.co/fEyGfy1EaX"},{"id":"1547001325602443264","text":"@ashleevance Lmaooo"},{"id":"1546980241494745100","text":"Oh the irony lol"},{"id":"1546957550670815234","text":"@ajtourville @Tesla No problem to recycle the 4680 pack. Just think of any battery pack as super high grade ore – it is always better to start with high grade ore than low grade!"},{"id":"1546733355336716289","text":"@cb_doge But Fifth Element was great"}],"meta":{"newest_id":"1547102805600632832","oldest_id":"1546733355336716289","result_count":10,"next_token":"b26v89c19zqg8o3fpz2mviz0d9x8fxli8oqobs81ze5bx"}}
The same request with tweetsRecentSearch
systematically returns a status code 401. My code:
TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("$bearer"));
TwitterApi apiInstance = new TwitterApi(credentials);
String query = "from:elonmusk";
try {
Get2TweetsSearchRecentResponse result = apiInstance.tweets().tweetsRecentSearch(query).execute();
System.out.println(result);
} catch (ApiException e) {
System.err.println("Exception when calling UsersApi#findUsersById");
System.err.println("Status code: " + e.getCode());
System.err.println("Reason: " + e.getResponseBody());
System.err.println("Response headers: " + e.getResponseHeaders());
e.printStackTrace();
}
The output:
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: Exception when calling UsersApi#findUsersById
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: Status code: 401
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: Reason: {
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: "title": "Unauthorized",
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: "type": "about:blank",
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: "status": 401,
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: "detail": "Unauthorized"
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: }
2022-07-13 15:38:35.598 6065-6276/com.example.trackmycrypto W/System.err: Response headers: {cache-control=[no-cache, no-store, max-age=0], content-length=[99], content-type=[application/problem+json], date=[Wed, 13 Jul 2022 13:38:35 GMT], server=[tsa_o], x-connection-hash=[5a5e0270ede245b52cb3e53de7617bce341005b413a746f711b9096d54a2e80b], x-response-time=[107]}
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: com.twitter.clientlib.ApiException: Message:
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: HTTP response code: 401
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: HTTP response body: {
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: "title": "Unauthorized",
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: "type": "about:blank",
2022-07-13 15:38:35.599 6065-6276/com.example.trackmycrypto W/System.err: "status": 401,
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: "detail": "Unauthorized"
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: }
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: HTTP response headers: {cache-control=[no-cache, no-store, max-age=0], content-length=[99], content-type=[application/problem+json], date=[Wed, 13 Jul 2022 13:38:35 GMT], server=[tsa_o], x-connection-hash=[5a5e0270ede245b52cb3e53de7617bce341005b413a746f711b9096d54a2e80b], x-response-time=[107]}
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.twitter.clientlib.ApiClient.handleResponse(ApiClient.java:1201)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.twitter.clientlib.ApiClient.execute(ApiClient.java:1091)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.twitter.clientlib.api.TweetsApi.tweetsRecentSearchWithHttpInfo(TweetsApi.java:4235)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.twitter.clientlib.api.TweetsApi.access$6500(TweetsApi.java:87)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.twitter.clientlib.api.TweetsApi$APItweetsRecentSearchRequest.execute(TweetsApi.java:4439)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.example.trackmycrypto.Utils.getTwitterNews(Utils.java:736)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at com.example.trackmycrypto.NewsAndMediaActivity$2.run(NewsAndMediaActivity.java:86)
2022-07-13 15:38:35.600 6065-6276/com.example.trackmycrypto W/System.err: at java.lang.Thread.run(Thread.java:1012)
This code is actually part of an Android application.
I have honestly no idea how to fix this issue, as the error is unfortunately not indicative. Would you have any suggestion on how to investigate this issue or on anything that could help ?
Unexpected FileNotFoundException while upgrading from 2.0.2 to 2.0.3
If no sdk.properties
is present, the following stacktrace is printed:
java.io.FileNotFoundException: sdk.properties (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:111)
at com.twitter.clientlib.SDKConfig.<clinit>(SDKConfig.java:38)
at com.twitter.clientlib.api.TwitterApi.init(TwitterApi.java:75)
at com.twitter.clientlib.api.TwitterApi.<init>(TwitterApi.java:50)
The change was introduced with #32.
Can we simply ignore the exception in https://github.com/twitterdev/twitter-api-java-sdk/blob/main/src/main/java/com/twitter/clientlib/SDKConfig.java#L40 instead of printing the stacktrace, which might confuse users?
AFAIK the sdk.properties
aren't mandatory, so perhaps it would be best to just ignore the exception instead of printing the stacktrace?
In addition, I am wondering, if this snippet shouldn't use the classloader in order to load the properties file?
FilteredStream gets "stale" after a certain period of time
Hi all,
I am migrating our code from twitter4j to this v2 capable library, so I am more than happy, that a official sdk is provided via GitHub!
I am using a slightly modified version of the Streamlistener example, i.e. it uses a fixed thread pool, has multiple consumers and can handle disconnects of the client as well as rate limiting.
However, I am encountering some weird behaviour, which also happens if I am using the streaming example without my modifications.
My setup is as follows:
- I have a Java application running on Ubuntu linux with an OpenJDK 17 from the package manager
- I am using a normal developer account (no academic access level or sth. else)
- I am using the filtered streaming endpoint.
- I am creating two rules: (a) follows some trending hashtags, and (b) follows some dedicated news outlets.
This setup works quite well. I am receiving lots of tweets, which are subsequently processed by my system.
However, after a certain period of time (some hours), the stream becomes "silent". I do not receive any new tweets and no exception is thrown.
If I am activley restarting the application or conduct an automatic reconnect, the tweets start flowing again.
According to this article from the documentation, disconnects may occur. However, I would expect an exception in case a disconnect happens, so the application can handle it in a nice way, i.e. conduct a reconnect.
Basically, I am wondering, if I am hitting
Full Buffer: Your App is not reading the data fast enough, or a network bottleneck is slowing data flow.
which leads silently (?) to the behaviour described above?
Any ideas / hints or pointers to related documentation are more than welcome.
Thanks in advance!
java.io.IOException: Failed deserialization for ProblemOrError
When calling twitter v2 api by twitterApi.users().findMyUser() when user's accessToken, the user's twitter account is suspened, but the api does not deserialize the ApiException properly.
TwitterCredentialsOAuth2 oAuth2Credential = new TwitterCredentialsOAuth2("devClientId", "", request.getAccessToken(), "", false);
TwitterApi twitterApi = new TwitterApi(oAuth2Credential);
Get2UsersMeResponse response = twitterApi.users()
.findMyUser()
.userFields(userFields)
.tweetFields(tweetFields)
.expansions(expansions)
.execute();
then, it catches the Exception
com.google.gson.JsonSyntaxException: java.io.IOException: Failed deserialization for ProblemOrError: 0 classes match result, expected 1. Detailed failure message for oneOf schemas: [Deserialization for Error failed with `The required field `code` is not found in the JSON string: {"detail":"The user used for authentication is suspended","title":"Forbidden","status":403,"type":"https://api.twitter.com/2/problems/user-suspended"}`., Deserialization for Problem failed with `The value of the `type` field `https://api.twitter.com/2/problems/user-suspended` does not match any key defined in the discriminator's mapping.`.]. JSON: {"detail":"The user used for authentication is suspended","title":"Forbidden","status":403,"type":"https://api.twitter.com/2/problems/user-suspended"}
It does not handle the type of Problem or Error properly.
NullPointerException Exception - TweetCreateRequest
As per this code to post a tweet https://github.com/twitterdev/twitter-api-java-sdk/blob/main/docs/TweetsApi.md#createTweet, it is throwing a NullPointer error.
Expected behavior
A given text should be created or posted.
Actual behavior
With all latest jar files and JDK, getting below error, that has no clue to fix:
Exception in thread "main" java.lang.NullPointerException
at com.twitter.clientlib.model.TweetCreateRequest$ReplySettingsEnum$Adapter.write(TweetCreateRequest.java:129)
at com.twitter.clientlib.model.TweetCreateRequest$ReplySettingsEnum$Adapter.write(TweetCreateRequest.java:126)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:99)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:219)
at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:230)
at com.twitter.clientlib.model.TweetCreateRequest$CustomTypeAdapterFactory$1.write(TweetCreateRequest.java:492)
at com.twitter.clientlib.model.TweetCreateRequest$CustomTypeAdapterFactory$1.write(TweetCreateRequest.java:489)
at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
at com.google.gson.Gson.toJson(Gson.java:600)
at com.google.gson.Gson.toJson(Gson.java:579)
at com.google.gson.Gson.toJson(Gson.java:534)
at com.google.gson.Gson.toJson(Gson.java:514)
at com.twitter.clientlib.JSON.serialize(JSON.java:958)
at com.twitter.clientlib.ApiClient.serialize(ApiClient.java:993)
at com.twitter.clientlib.ApiClient.buildRequest(ApiClient.java:1269)
at com.twitter.clientlib.ApiClient.buildCall(ApiClient.java:1222)
at com.twitter.clientlib.api.TweetsApi.createTweetCall(TweetsApi.java:308)
at com.twitter.clientlib.api.TweetsApi.createTweetValidateBeforeCall(TweetsApi.java:320)
at com.twitter.clientlib.api.TweetsApi.createTweetWithHttpInfo(TweetsApi.java:327)
at com.twitter.clientlib.api.TweetsApi.access$500(TweetsApi.java:89)
at com.twitter.clientlib.api.TweetsApi$APIcreateTweetRequest.execute(TweetsApi.java:383)
at com.veer.social.twitter.test.TwitterTestPost1.main(TwitterTestPost1.java:42)
Steps to reproduce the behavior
Just run that code and it gives the above error.
Please help me out.
TweetPublicMetrics model miss "impression_count" param
In the API doc:
public_metrics.impression_count | integer | Number of times this Tweet has been viewed.
But when use TweetsApi.findTweetById response miss "impression_count". See TweetPublicMetrics model miss "impression_count" param.
the class UrlFields UrlImage is not the same as the file name UrlImage.java URLFields.java
the class UrlFields UrlImage is not the same as the file name UrlImage.java URLFields.java
Expected behavior
When building the code, the result is a failure.
Actual behavior
Steps to reproduce the behavior
twitter-api-java-sdk\src\main\java\com\twitter\clientlib\model\URLImage.java:62:8
java: class UrlImage is public, should be declared in a file named UrlImage.java
"Unable to give access to the App" when following steps from example
Trying to authenticate a user of the app with TwitterOAuth20Service
. The auth url returns "Unable to give access to the App"
Expected behavior
The auth url should present the user with the choice to authorize the app.
Actual behavior
Steps to reproduce the behavior
These are the steps I follow, directly taken from the code example. A screenshot just below shows where I take the tokens from. I suspect I do something silly here, your advice is welcome.
Properties props = new Properties();
props.load(new FileInputStream("private/props.properties"));
TwitterCredentialsOAuth2 credentials = new TwitterCredentialsOAuth2(
props.getProperty("twitter_client_id"), // see A in the screenshot
props.getProperty("twitter_client_secret"), // see B in the screenshot
props.getProperty("twitter_access_token"), // see C in the screenshot
System.getenv("TWITTER_OAUTH2_REFRESH_TOKEN") // I don't see such a refresh token in the dev console ??
);
TwitterOAuth20Service service = new TwitterOAuth20Service(
credentials.getTwitterOauth2ClientId(),
credentials.getTwitterOAuth2ClientSecret(),
"http://twitter.com",
"offline.access tweet.read");
OAuth2AccessToken accessToken = null;
try {
final Scanner in = new Scanner(System.in, "UTF-8");
System.out.println("Fetching the Authorization URL...");
final String secretState = "state";
PKCE pkce = new PKCE();
pkce.setCodeChallenge("challenge");
pkce.setCodeChallengeMethod(PKCECodeChallengeMethod.PLAIN);
pkce.setCodeVerifier("challenge");
String authorizationUrl = service.getAuthorizationUrl(pkce, secretState);
System.out.println("Go to the Authorization URL and authorize your App:\n"
+ authorizationUrl + "\nAfter that paste the authorization code here\n>>");
// at this step, I copy paste the URL in a browser and this is where the error msg is returned
final String code = in.nextLine();
System.out.println("\nTrading the Authorization Code for an Access Token...");
accessToken = service.getAccessToken(pkce, code);
System.out.println("Access token: " + accessToken.getAccessToken());
System.out.println("Refresh token: " + accessToken.getRefreshToken());
} catch (Exception e) {
System.err.println("Error while getting the access token:\n " + e);
e.printStackTrace();
}
Screenshot showing where I take the tokens used in the credentials:
IllegalArgumentException when no rules returned
When I call TwitterApi.tweets().getRules().execute() and the API doesn't have rules to return, the SDK fails parsing the result:
java.lang.IllegalArgumentException: The required field data
is not found in the JSON string: {"meta":{"sent":"2022-07-08T14:41:31.273Z","result_count":0}}
at com.twitter.clientlib.model.RulesLookupResponse.validateJsonObject(RulesLookupResponse.java:202)
at com.twitter.clientlib.model.RulesLookupResponse$CustomTypeAdapterFactory$1.read(RulesLookupResponse.java:244)
at com.twitter.clientlib.model.RulesLookupResponse$CustomTypeAdapterFactory$1.read(RulesLookupResponse.java:234)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:991)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.twitter.clientlib.JSON.deserialize(JSON.java:944)
at com.twitter.clientlib.ApiClient.deserialize(ApiClient.java:959)
at com.twitter.clientlib.ApiClient.handleResponse(ApiClient.java:1190)
at com.twitter.clientlib.ApiClient.execute(ApiClient.java:1091)
at com.twitter.clientlib.api.TweetsApi.getRulesWithHttpInfo(TweetsApi.java:1492)
at com.twitter.clientlib.api.TweetsApi.access$2500(TweetsApi.java:87)
at com.twitter.clientlib.api.TweetsApi$APIgetRulesRequest.execute(TweetsApi.java:1573)
... 9 more
It seems that the API not serializing empty fields causes the ApiClient to fail.
Getting an IllegalArgumentException when deserializing JSON response to create tweet
Getting an IllegalArgumentException when deserializing JSON response to create tweet. It looks like the TweetCreateResponseData class doesn't contain fields present in the create tweet response JSON since Sept 20, 2022:
https://developer.twitter.com/en/docs/twitter-api/fields
By default, the Tweet object only returns the id and the text fields, and for Tweets created since September 29, 2022, the edit_history_tweet_ids field.
Expected behavior
CreateTweetRequest instance is created and populated with tweet information from the JSON response to create a tweet.
Actual behavior
Getting an IllegalArgumentException when deserializing JSON response to create tweet. The error seems to be related to a newish JSON field that was added to the response to create tweets via the TwitterApi.tweets().createTweet() api call. The exact error is:
java.lang.IllegalArgumentException: The field edit_history_tweet_ids
in the JSON string is not defined in the TweetCreateResponseData
properties. JSON: {"edit_history_tweet_ids":["1671964017076883456"],"id":"1671964017076883456","text":""Ahoy!""}
Steps to reproduce the behavior
Use the twitter api to create a tweet and attempt to deserialize the response directly to a TweetCreateResponse object.
TweetCreateResponse result = apiInstance.tweets().createTweet(createTweetRequest);
'sdkProperties' has private access in 'com.twitter.clientlib.SDKConfig' .
One line summary of the issue here.
Expected behavior
As concisely as possible, describe the expected behavior.
Actual behavior
As concisely as possible, describe the observed behavior.
Steps to reproduce the behavior
Please list all relevant steps to reproduce the observed behavior.
No way to retweet?
There doesnt seem to be a way to configure what tweet to retweet
Expected behavior
There should be a parameter or method that allows for specifying what tweet to retweet
Actual behavior
All methods that expose the ability to provide a tweet ID are private, including the method given from the example docs which appears to be missing entirely https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets#tab1
Steps to reproduce the behavior
Download the latest version of the SDK and attempt to write code to retweet, the required method seems to have been removed in a7d8da3
Unable to avoid sporadic 429 too many connections on stream
Currently I have a elevated
account on twitter.
As per my understanding twitter only allows 1 stream connection per 1 app.
As I understand this I am making sure I am not running multiple instances of my app, and of course not sharing the bearer token to anyone else.
However I am unable to make my stream application running stably.
The error starts by suddenly receiving a Socket.Exception connection reset
on my stream app.
After that when I start my app I receive a 429 too many connection error. I am making sure my previous connection has been cleaned during shutdown, but there seems to be a connection which I am unable to identify.
I have already tried multiple things which did not participate to help this issue
- Do not use connection pools
- Do not retry on failure (as the retry option seems to be unhelpful on 429 too many connections errors)
- Use HTTP 1.1
- Clean up connection on shutdown
The following is the code fragments.
Expected behavior
Stable and long live stream connections without sudden disconnect
Actual behavior
Sudden disconnect with 429 too many connection, even with several actions to clean connections on shutdown.
Steps to reproduce the behavior
Run a stream application with "elevated" account.
Caused by: java.net.SocketTimeoutException: timeout
I'm running into a timeout issue frequently. I guess, it happens when there is currently no item to stream. However, I'm basically using the implementation of the documentation.
BufferedReader reader = new BufferedReader(new InputStreamReader(twitterApi.tweets()
.searchStream(5,
Set.of("author_id", "referenced_tweets.id", "in_reply_to_user_id", "geo.place_id", "attachments.media_keys", "attachments.poll_ids", "entities.mentions.username", "referenced_tweets.id.author_id"),
Set.of("id", "created_at", "text", "author_id", "in_reply_to_user_id", "referenced_tweets", "attachments",
"withheld", "geo", "entities", "public_metrics", "possibly_sensitive", "source", "lang", "context_annotations", "non_public_metrics",
"promoted_metrics", "organic_metrics", "conversation_id", "reply_settings"),
Set.of("id", "created_at", "name", "username", "protected", "verified", "withheld", "profile_image_url", "location", "url", "description", "entities", "pinned_tweet_id", "public_metrics"),
Set.of("media_key", "duration_ms", "height", "preview_image_url", "type", "url", "width", "public_metrics", "non_public_metrics", "organic_metrics", "promoted_metrics", "alt_text"),
Set.of("id", "name", "country_code", "place_type", "full_name", "country", "contained_within", "geo"),
null, 0))
);
String line = reader.readLine();
while (line != null) {
log.info(line);
//emitter.emit(convert(gson, line));
line = reader.readLine();
}
Caused by: java.net.SocketTimeoutException: timeout
at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:678)
at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:686)
at okhttp3.internal.http2.Http2Stream$FramingSource.read(Http2Stream.java:409)
at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.java:286)
at okio.RealBufferedSource.read(RealBufferedSource.java:51)
at okio.RealBufferedSource.request(RealBufferedSource.java:72)
at okio.RealBufferedSource.require(RealBufferedSource.java:65)
at okio.GzipSource.consumeHeader(GzipSource.java:114)
at okio.GzipSource.read(GzipSource.java:73)
at okio.RealBufferedSource$1.read(RealBufferedSource.java:447)
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:270)
at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:313)
at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:188)
at java.base/java.io.InputStreamReader.read(InputStreamReader.java:177)
at java.base/java.io.BufferedReader.fill(BufferedReader.java:162)
at java.base/java.io.BufferedReader.readLine(BufferedReader.java:329)
at java.base/java.io.BufferedReader.readLine(BufferedReader.java:396)
How can I prevent that?
tweetsIdLikingUsers returns 403
Call to api.users().tweetsIdLikingUsers("...").execute()
returns Status Code 403
Expected behavior
I would expect that the call returns with a 200.
I checked the API if it should only work with my own tweets but as of my interpretation it should work with any tweet.
Actual behavior
I initialize the API, test the validity of the object with a call to findMyUser()
(which does not throw an exception).
Then I call
api.users().tweetsIdLikingUsers("1565414244052508673").execute();
(Which should list the Users of the tweet https://twitter.com/TwitterDev/status/1565414244052508673 .)
This call results in a ApiException
Response headers: com.twitter.clientlib.ApiException: Message:
HTTP response code: 403
HTTP response body: {
"title": "Forbidden",
"type": "about:blank",
"status": 403,
"detail": "Forbidden"
}
Steps to reproduce the behavior
Just initialize the api and call
api.users().tweetsIdLikingUsers("1565414244052508673").execute();
java.net.MalformedURLException: no protocol
When retrieving tweets with the full archive endpoint, asking for all expansions, I have the following error. It does not appear immediately, I'm able to retrieve a bunch of tweets before it is thrown, so it must be linked to a specific tweet/user data. I'm using the 2.0.1 sdk.
com.google.gson.JsonIOException: java.net.MalformedURLException: no protocol:
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:287)
at com.twitter.clientlib.model.User$CustomTypeAdapterFactory$1.read(User.java:623)
at com.twitter.clientlib.model.User$CustomTypeAdapterFactory$1.read(User.java:612)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.Expansions$CustomTypeAdapterFactory$1.read(Expansions.java:459)
at com.twitter.clientlib.model.Expansions$CustomTypeAdapterFactory$1.read(Expansions.java:448)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.Get2TweetsSearchAllResponse$CustomTypeAdapterFactory$1.read(Get2TweetsSearchAllResponse.java:325)
at com.twitter.clientlib.model.Get2TweetsSearchAllResponse$CustomTypeAdapterFactory$1.read(Get2TweetsSearchAllResponse.java:314)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:991)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.twitter.clientlib.JSON.deserialize(JSON.java:944)
at com.twitter.clientlib.ApiClient.deserialize(ApiClient.java:959)
at com.twitter.clientlib.ApiClient.handleResponse(ApiClient.java:1190)
at com.twitter.clientlib.ApiClient.execute(ApiClient.java:1091)
at com.twitter.clientlib.api.TweetsApi.tweetsFullarchiveSearchWithHttpInfo(TweetsApi.java:3853)
at com.twitter.clientlib.api.TweetsApi.access$6100(TweetsApi.java:87)
at com.twitter.clientlib.api.TweetsApi$APItweetsFullarchiveSearchRequest.execute(TweetsApi.java:4057)
Error is thrown when deserializing a tweet containing a url with curly brackets
URISyntaxException when deserializing a String containing curly brackets
Expected behavior
Deserialization should work
Actual behavior
An exception is thrown:
com.google.gson.JsonIOException: java.net.URISyntaxException: Illegal character in query at index 32: http://lp.ixd.dmm.com/lp/?lpurl={lurl}
at com.google.gson.internal.bind.TypeAdapters$22.read(TypeAdapters.java:523)
at com.google.gson.internal.bind.TypeAdapters$22.read(TypeAdapters.java:512)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.UrlEntity$CustomTypeAdapterFactory$1.read(UrlEntity.java:543)
at com.twitter.clientlib.model.UrlEntity$CustomTypeAdapterFactory$1.read(UrlEntity.java:532)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.FullTextEntities$CustomTypeAdapterFactory$1.read(FullTextEntities.java:357)
at com.twitter.clientlib.model.FullTextEntities$CustomTypeAdapterFactory$1.read(FullTextEntities.java:346)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.Tweet$CustomTypeAdapterFactory$1.read(Tweet.java:867)
at com.twitter.clientlib.model.Tweet$CustomTypeAdapterFactory$1.read(Tweet.java:856)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
at com.google.gson.TypeAdapter.fromJsonTree(TypeAdapter.java:285)
at com.twitter.clientlib.model.TweetSearchResponse$CustomTypeAdapterFactory$1.read(TweetSearchResponse.java:325)
at com.twitter.clientlib.model.TweetSearchResponse$CustomTypeAdapterFactory$1.read(TweetSearchResponse.java:314)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:991)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.twitter.clientlib.JSON.deserialize(JSON.java:921)
Steps to reproduce the behavior
Query recent tweets where one tweet includes a url with curly braces (see the first line of the error trace). This happened when I did a test with a query on "Obama". Can't redo the query as the tweet is no longer included in the nth most recent tweets
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.