diff --git a/cmd/tests.sh b/cmd/tests.sh index 1fa264a..5356ab5 100755 --- a/cmd/tests.sh +++ b/cmd/tests.sh @@ -247,6 +247,14 @@ test $(sqlite3 twitter.db "select count(*) from users where handle like '9Monsie test $(sqlite3 twitter.db "select count(*) from users where handle like '9MonsieurChat9' and not is_deleted") = "1" test $(sqlite3 twitter.db "select is_deleted from users where id = 1615394007961731072") = "1" +# Fetch a tweet from such a new account with the same handle as an old one +sqlite3 twitter.db "delete from users where handle like '9MonsieurChat9' and not is_deleted" +test $(sqlite3 twitter.db "select count(*) from users where handle like '9MonsieurChat9'") = "1" +tw fetch_tweet https://x.com/9MonsieurChat9/status/1834121200584589600 # Should update the user as well +test $(sqlite3 twitter.db "select count(*) from users where handle like '9MonsieurChat9'") = "2" +test $(sqlite3 twitter.db "select count(*) from users where handle like '9MonsieurChat9' and not is_deleted") = "1" +test $(sqlite3 twitter.db "select is_deleted from users where id = 1615394007961731072") = "1" + # Test tweets with URLs tw fetch_user RoninGreg diff --git a/internal/webserver/handler_login.go b/internal/webserver/handler_login.go index eb6755a..f4dc1da 100644 --- a/internal/webserver/handler_login.go +++ b/internal/webserver/handler_login.go @@ -74,7 +74,7 @@ func (app *Application) after_login(w http.ResponseWriter, r *http.Request, api app.error_404(w, r) return } - panic_if(app.Profile.SaveUser(&user)) + panic_if(app.Profile.SaveUser(&user)) // TODO: handle conflicting users panic_if(app.Profile.DownloadUserContentFor(&user, &app.API)) // Now that the user is scraped for sure, set them as the logged-in user diff --git a/internal/webserver/handler_user_feed.go b/internal/webserver/handler_user_feed.go index 1d75b51..abcc7d1 100644 --- a/internal/webserver/handler_user_feed.go +++ b/internal/webserver/handler_user_feed.go @@ -24,7 +24,7 @@ func (app *Application) UserFeed(w http.ResponseWriter, r *http.Request) { app.error_404(w, r) return } - panic_if(app.Profile.SaveUser(&user)) + panic_if(app.Profile.SaveUser(&user)) // TODO: handle conflicting users panic_if(app.Profile.DownloadUserContentFor(&user, &app.API)) } else if err != nil { panic(err) diff --git a/pkg/persistence/tweet_trove_queries.go b/pkg/persistence/tweet_trove_queries.go index d0a55c8..c39806b 100644 --- a/pkg/persistence/tweet_trove_queries.go +++ b/pkg/persistence/tweet_trove_queries.go @@ -13,7 +13,28 @@ import ( func (p Profile) SaveTweetTrove(trove TweetTrove, should_download bool, api *API) { for i, u := range trove.Users { err := p.SaveUser(&u) - if err != nil { + // Check for handle conflicts and handle them in place + // TODO: this is hacky, it doesn't go here. We should return a list of conflicting users + // who were marked as deleted, and then let the callee re-scrape and re-save them. + var conflict_err ErrConflictingUserHandle + if errors.As(err, &conflict_err) { + fmt.Printf( + "Conflicting user handle found (ID %d); old user has been marked deleted. Rescraping them\n", + conflict_err.ConflictingUserID, + ) + user, err := GetUserByID(conflict_err.ConflictingUserID) + if errors.Is(err, ErrDoesntExist) { + // Mark them as deleted. + // Handle and display name won't be updated if the user exists. + user = User{ID: conflict_err.ConflictingUserID, DisplayName: "", Handle: "", IsDeleted: true} + } else if err != nil { + panic(fmt.Errorf("error scraping conflicting user (ID %d): %w", conflict_err.ConflictingUserID, err)) + } + err = p.SaveUser(&user) + if err != nil { + panic(fmt.Errorf("error saving rescraped conflicting user with ID %d and handle %q: %w", user.ID, user.Handle, err)) + } + } else if err != nil { panic(fmt.Errorf("Error saving user with ID %d and handle %s:\n %w", u.ID, u.Handle, err)) } fmt.Println(u.Handle, u.ID)