diff --git a/tweets.go b/tweets.go index e01dedd..24f9739 100644 --- a/tweets.go +++ b/tweets.go @@ -25,6 +25,71 @@ func (s *Scraper) FetchTweets(user string, maxTweetsNbr int, cursor string) ([]* return s.FetchTweetsByUserID(userID, maxTweetsNbr, cursor) } +// FetchTweetsAndRepliesByUserID gets tweets and replies for a given userID, via the Twitter frontend GraphQL API. +func (s *Scraper) FetchTweetsAndRepliesByUserID(userID string, maxReplysNbr int, cursor string) ([]*Tweet, string, error) { + if maxReplysNbr > 200 { + maxReplysNbr = 200 + } + + req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/bt4TKuFz4T7Ckk-VvQVSow/UserTweetsAndReplies") + if err != nil { + return nil, "", err + } + + variables := map[string]interface{}{ + "userId": userID, + "count": maxReplysNbr, + "includePromotedContent": false, + "withQuickPromoteEligibilityTweetFields": false, + "withVoice": true, + "withV2Timeline": true, + } + + features := map[string]interface{}{ + "rweb_tipjar_consumption_enabled": true, + "responsive_web_graphql_exclude_directive_enabled": true, + "verified_phone_label_enabled": false, + "creator_subscriptions_tweet_preview_api_enabled": true, + "responsive_web_graphql_timeline_navigation_enabled": true, + "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, + "communities_web_enable_tweet_community_results_fetch": true, + "c9s_tweet_anatomy_moderator_badge_enabled": true, + "articles_preview_enabled": true, + "responsive_web_edit_tweet_api_enabled": true, + "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true, + "view_counts_everywhere_api_enabled": true, + "longform_notetweets_consumption_enabled": true, + "responsive_web_twitter_article_tweet_consumption_enabled": true, + "tweet_awards_web_tipping_enabled": false, + "creator_subscriptions_quote_tweet_preview_enabled": false, + "freedom_of_speech_not_reach_fetch_enabled": true, + "standardized_nudges_misinfo": true, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true, + "rweb_video_timestamps_enabled": true, + "longform_notetweets_rich_text_read_enabled": true, + "longform_notetweets_inline_media_enabled": true, + "responsive_web_enhance_cards_enabled": false, + } + + if cursor != "" { + variables["cursor"] = cursor + } + + query := url.Values{} + query.Set("variables", mapToJSONString(variables)) + query.Set("features", mapToJSONString(features)) + req.URL.RawQuery = query.Encode() + + var timeline timelineV2 + err = s.RequestAPI(req, &timeline) + if err != nil { + return nil, "", err + } + + tweets, nextCursor := timeline.parseTweets() + return tweets, nextCursor, nil +} + // FetchTweetsByUserID gets tweets for a given userID, via the Twitter frontend GraphQL API. func (s *Scraper) FetchTweetsByUserID(userID string, maxTweetsNbr int, cursor string) ([]*Tweet, string, error) { if maxTweetsNbr > 200 {