feat: x.com migration + public API for cookie-based auth

- migrate all API URLs from twitter.com to x.com
- add SetBearerToken, SetHTTPClient, SetLoggedIn public methods
- accept 202/204 status codes in handleResponse
- module path -> src.cultist.club/lain/twitter-scrapper
This commit is contained in:
Lain Iwakura 2026-05-21 18:54:10 +03:00
parent 76cb95cd3e
commit 34db837a9e
Signed by: lain
GPG key ID: 8160466B2E8D1441
19 changed files with 75 additions and 63 deletions

View file

@ -46,7 +46,7 @@ func (s *Scraper) FetchTweetsAndRepliesByUserID(userID string, maxReplysNbr int,
maxReplysNbr = 200
}
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/bt4TKuFz4T7Ckk-VvQVSow/UserTweetsAndReplies")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/bt4TKuFz4T7Ckk-VvQVSow/UserTweetsAndReplies")
if err != nil {
return nil, "", err
}
@ -111,7 +111,7 @@ func (s *Scraper) FetchTweetsByUserID(userID string, maxTweetsNbr int, cursor st
maxTweetsNbr = 200
}
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/UGi7tjRPr-d_U3bCPIko5Q/UserTweets")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/UGi7tjRPr-d_U3bCPIko5Q/UserTweets")
if err != nil {
return nil, "", err
}
@ -173,7 +173,7 @@ func (s *Scraper) FetchTweetsByUserIDLegacy(userID string, maxTweetsNbr int, cur
maxTweetsNbr = 200
}
req, err := s.newRequest("GET", "https://api.twitter.com/2/timeline/profile/"+userID+".json")
req, err := s.newRequest("GET", "https://api.x.com/2/timeline/profile/"+userID+".json")
if err != nil {
return nil, "", err
}
@ -199,7 +199,7 @@ func (s *Scraper) FetchTweetsByUserIDLegacy(userID string, maxTweetsNbr int, cur
// GetTweet get a single tweet by ID.
func (s *Scraper) GetTweet(id string) (*Tweet, error) {
if s.isOpenAccount {
req, err := s.newRequest("GET", "https://api.twitter.com/2/timeline/conversation/"+id+".json")
req, err := s.newRequest("GET", "https://api.x.com/2/timeline/conversation/"+id+".json")
if err != nil {
return nil, err
}
@ -217,7 +217,7 @@ func (s *Scraper) GetTweet(id string) (*Tweet, error) {
}
}
} else if s.isLogged {
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/VWFGPVAGkZMGRKGe3GFFnA/TweetDetail")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/VWFGPVAGkZMGRKGe3GFFnA/TweetDetail")
if err != nil {
return nil, err
}
@ -263,7 +263,7 @@ func (s *Scraper) GetTweet(id string) (*Tweet, error) {
// Surprisingly, if bearerToken2 is not set, then animated GIFs are not
// present in the response for tweets with a GIF + a photo like this one:
// https://twitter.com/Twitter/status/1580661436132757506
// https://x.com/Twitter/status/1580661436132757506
curBearerToken := s.bearerToken
if curBearerToken != bearerToken2 {
s.setBearerToken(bearerToken2)
@ -286,7 +286,7 @@ func (s *Scraper) GetTweet(id string) (*Tweet, error) {
}
}
} else {
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/xBtHv5-Xsk268T5ng_OGNg/TweetResultByRestId")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/xBtHv5-Xsk268T5ng_OGNg/TweetResultByRestId")
if err != nil {
return nil, err
}
@ -332,7 +332,7 @@ func (s *Scraper) GetTweet(id string) (*Tweet, error) {
// Surprisingly, if bearerToken2 is not set, then animated GIFs are not
// present in the response for tweets with a GIF + a photo like this one:
// https://twitter.com/Twitter/status/1580661436132757506
// https://x.com/Twitter/status/1580661436132757506
curBearerToken := s.bearerToken
if curBearerToken != bearerToken2 {
s.setBearerToken(bearerToken2)
@ -421,7 +421,7 @@ func (s *Scraper) fetchHomeTweets(_ string, maxTweetsNbr int, cursor string) ([]
maxTweetsNbr = 200
}
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/9EwYy8pLBOSFlEoSP2STiQ/HomeLatestTimeline")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/9EwYy8pLBOSFlEoSP2STiQ/HomeLatestTimeline")
if err != nil {
return nil, "", err
}
@ -496,7 +496,7 @@ func (s *Scraper) fetchForYouTweets(_ string, maxTweetsNbr int, cursor string) (
maxTweetsNbr = 200
}
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/1u0Wlkw6Ru1NwBUD-pDiww/HomeTimeline")
req, err := s.newRequest("GET", "https://x.com/i/api/graphql/1u0Wlkw6Ru1NwBUD-pDiww/HomeTimeline")
if err != nil {
return nil, "", err
}