Coder Social home page Coder Social logo

twitter-api-java-sdk's People

Contributors

foundingnimo avatar zacmos avatar zakichanu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

twitter-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

https://api.twitter.com/2/tweets?media.fields=alt_text,duration_ms,height,media_key,preview_image_url,public_metrics,type,url,variants,width&expansions=author_id,attachments.media_keys,geo.place_id&tweet.fields=attachments,author_id,card_uri,context_annotations,conversation_id,created_at,edit_controls,edit_history_tweet_ids,entities,geo,id,in_reply_to_user_id,lang,note_tweet,possibly_sensitive,public_metrics,referenced_tweets,reply_settings,source,text,withheld&user.fields=created_at,description,id,location,most_recent_tweet_id,name,pinned_tweet_id,profile_image_url,protected,public_metrics,receives_your_dm,subscription_type,url,username,verified,verified_type&ids=1752384295568851027

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:

https://javaee.github.io/jsonb-spec/index.html

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 &amp; 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 &amp; 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

Project context : https://github.com/totetmatt/gephi-plugins/blob/twitter_v2/modules/TwitterV2/src/main/java/fr/totetmatt/gephi/twitter/utils/listener/filtered/TweetsStreamListenersExecutor.java#L124

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.

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

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.

Filtered Stream

Can you update StreamingTweet type to add matching_rules variable?
image

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。

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)


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>

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

image

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:

image

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);

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.

https://github.com/tanzu-japan/twitter-wordcloud-demo/blob/main/src/main/java/jp/vmware/tanzu/twitterwordclouddemo/utils/TwitterStreamClientImpl.java#L66-L77

https://github.com/tanzu-japan/twitter-wordcloud-demo/blob/main/src/main/java/jp/vmware/tanzu/twitterwordclouddemo/utils/TwitterStreamClientImpl.java#L209-L220

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 photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.