Add scraper function to get user by ID

This commit is contained in:
Alessio 2024-09-15 22:56:59 -07:00
parent 210328bcc9
commit 4ea15f10af
6 changed files with 74 additions and 1 deletions

View File

@ -163,6 +163,12 @@ func main() {
login(target, password) login(target, password)
case "fetch_user": case "fetch_user":
fetch_user(scraper.UserHandle(target)) fetch_user(scraper.UserHandle(target))
case "fetch_user_by_id":
id, err := strconv.Atoi(target)
if err != nil {
panic(err)
}
fetch_user_by_id(scraper.UserID(id))
case "download_user_content": case "download_user_content":
download_user_content(scraper.UserHandle(target)) download_user_content(scraper.UserHandle(target))
case "fetch_tweet_only": case "fetch_tweet_only":
@ -310,6 +316,26 @@ func fetch_user(handle scraper.UserHandle) {
happy_exit("Saved the user", nil) happy_exit("Saved the user", nil)
} }
func fetch_user_by_id(id scraper.UserID) {
session, err := scraper.NewGuestSession() // This endpoint works better if you're not logged in
if err != nil {
panic(err)
}
user, err := session.GetUserByID(id)
if err != nil {
panic(err)
}
log.Debug(user)
err = profile.SaveUser(&user)
if err != nil {
die(fmt.Sprintf("Error saving user: %s", err.Error()), false, 4)
}
download_user_content(user.Handle)
happy_exit("Saved the user", nil)
}
/** /**
* Scrape a single tweet and save it in the database. * Scrape a single tweet and save it in the database.
* *

View File

@ -139,6 +139,8 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/messages", http.HandlerFunc(app.Messages)).ServeHTTP(w, r) http.StripPrefix("/messages", http.HandlerFunc(app.Messages)).ServeHTTP(w, r)
case "nav-sidebar-poll-updates": case "nav-sidebar-poll-updates":
app.NavSidebarPollUpdates(w, r) app.NavSidebarPollUpdates(w, r)
case "communities":
panic("not implemented")
default: default:
app.UserFeed(w, r) app.UserFeed(w, r)
} }

View File

@ -51,7 +51,7 @@
<label class="nav-sidebar__button-label">Bookmarks</label> <label class="nav-sidebar__button-label">Bookmarks</label>
</li> </li>
</a> </a>
<a href="#"> <a hx-get="/communities">
<li class="button labelled-icon"> <li class="button labelled-icon">
<img class="svg-icon" src="/static/icons/communities.svg" width="24" height="24" /> <img class="svg-icon" src="/static/icons/communities.svg" width="24" height="24" />
<label class="nav-sidebar__button-label">Communities</label> <label class="nav-sidebar__button-label">Communities</label>

View File

@ -7,6 +7,7 @@ import (
var ( var (
END_OF_FEED = errors.New("End of feed") END_OF_FEED = errors.New("End of feed")
ErrDoesntExist = errors.New("Doesn't exist") ErrDoesntExist = errors.New("Doesn't exist")
ErrUserIsBanned = errors.New("user is banned")
EXTERNAL_API_ERROR = errors.New("Unexpected result from external API") EXTERNAL_API_ERROR = errors.New("Unexpected result from external API")
ErrorIsTombstone = errors.New("tweet is a tombstone") ErrorIsTombstone = errors.New("tweet is a tombstone")
ErrRateLimited = errors.New("rate limited") ErrRateLimited = errors.New("rate limited")

View File

@ -63,6 +63,7 @@ type GraphqlFeatures struct {
ResponsiveWebTextConversationsEnabled bool `json:"responsive_web_text_conversations_enabled"` ResponsiveWebTextConversationsEnabled bool `json:"responsive_web_text_conversations_enabled"`
ResponsiveWebTwitterArticleTweetConsumptionEnabled bool `json:"responsive_web_twitter_article_tweet_consumption_enabled"` ResponsiveWebTwitterArticleTweetConsumptionEnabled bool `json:"responsive_web_twitter_article_tweet_consumption_enabled"`
ResponsiveWebMediaDownloadVideoEnabled bool `json:"responsive_web_media_download_video_enabled"` ResponsiveWebMediaDownloadVideoEnabled bool `json:"responsive_web_media_download_video_enabled"`
ResponsiveWebTwitterArticleNotesTabEnabled bool `json:"responsive_web_twitter_article_notes_tab_enabled"`
SubscriptionsVerificationInfoVerifiedSinceEnabled bool `json:"subscriptions_verification_info_verified_since_enabled"` SubscriptionsVerificationInfoVerifiedSinceEnabled bool `json:"subscriptions_verification_info_verified_since_enabled"`
HiddenProfileLikesEnabled bool `json:"hidden_profile_likes_enabled"` HiddenProfileLikesEnabled bool `json:"hidden_profile_likes_enabled"`
HiddenProfileSubscriptionsEnabled bool `json:"hidden_profile_subscriptions_enabled"` HiddenProfileSubscriptionsEnabled bool `json:"hidden_profile_subscriptions_enabled"`
@ -81,6 +82,7 @@ type GraphqlFeatures struct {
ArticlesPreviewEnabled bool `json:"articles_preview_enabled,omitempty"` ArticlesPreviewEnabled bool `json:"articles_preview_enabled,omitempty"`
GraphqlTimelineV2BookmarkTimeline bool `json:"graphql_timeline_v2_bookmark_timeline,omitempty"` GraphqlTimelineV2BookmarkTimeline bool `json:"graphql_timeline_v2_bookmark_timeline,omitempty"`
CreatorSubscriptionsQuoteTweetPreviewEnabled bool `json:"creator_subscriptions_quote_tweet_preview_enabled"` CreatorSubscriptionsQuoteTweetPreviewEnabled bool `json:"creator_subscriptions_quote_tweet_preview_enabled"`
SubscriptionsFeatureCanGiftPremium bool `json:"subscriptions_feature_can_gift_premium,omitempty"`
} }
type GraphqlURL struct { type GraphqlURL struct {

View File

@ -1384,6 +1384,48 @@ func (api API) GetUser(handle UserHandle) (User, error) {
return ParseSingleUser(apiUser) return ParseSingleUser(apiUser)
} }
func (api API) GetUserByID(u_id UserID) (User, error) {
url, err := url.Parse(GraphqlURL{
BaseUrl: "https://x.com/i/api/graphql/Qw77dDjp9xCpUY-AXwt-yQ/UserByRestId",
Variables: GraphqlVariables{
UserID: u_id,
},
Features: GraphqlFeatures{
RWebTipjarConsumptionEnabled: true,
ResponsiveWebGraphqlExcludeDirectiveEnabled: true,
VerifiedPhoneLabelEnabled: false,
ResponsiveWebGraphqlSkipUserProfileImageExtensionsEnabled: false,
ResponsiveWebGraphqlTimelineNavigationEnabled: true,
SubscriptionsFeatureCanGiftPremium: true,
ResponsiveWebTwitterArticleNotesTabEnabled: true,
},
}.String())
if err != nil {
panic(err)
}
var response UserResponse
err = api.do_http(url.String(), "", &response)
if err != nil {
return User{}, err
}
apiUser, err := response.ConvertToAPIUser()
if errors.Is(err, ErrDoesntExist) {
return User{}, err
}
if apiUser.ScreenName == "" {
if apiUser.IsBanned {
return User{}, ErrUserIsBanned
} else {
return User{}, ErrDoesntExist
}
}
if err != nil {
return User{}, fmt.Errorf("Error fetching user ID %d:\n %w", u_id, err)
}
return ParseSingleUser(apiUser)
}
// Paginated Search // Paginated Search
// ---------------- // ----------------