diff --git a/scraper/api_types.go b/scraper/api_types.go index 3940e49..90e0092 100644 --- a/scraper/api_types.go +++ b/scraper/api_types.go @@ -232,6 +232,7 @@ type APIUser struct { ScreenName string `json:"screen_name"` StatusesCount int `json:"statuses_count"` Verified bool `json:"verified"` + IsBanned bool } @@ -250,6 +251,16 @@ type UserResponse struct { func (u UserResponse) ConvertToAPIUser() APIUser { ret := u.Data.User.Legacy ret.ID = u.Data.User.ID + + // Banned users + for _, api_error := range u.Errors { + if api_error.Message == "Authorization: User has been suspended. (63)" { + ret.IsBanned = true + } else { + panic(fmt.Sprintf("Unknown api error: %q", api_error.Message)) + } + } + return ret } diff --git a/scraper/test_responses/suspended_user.json b/scraper/test_responses/suspended_user.json new file mode 100644 index 0000000..457750c --- /dev/null +++ b/scraper/test_responses/suspended_user.json @@ -0,0 +1 @@ +{"errors":[{"message":"Authorization: User has been suspended. (63)","locations":[{"line":17,"column":3}],"path":["user","legacy"],"extensions":{"name":"AuthorizationError","source":"Client","code":63,"kind":"Permissions","tracing":{"trace_id":"c9f5c3bc1afaab46"}},"code":63,"kind":"Permissions","name":"AuthorizationError","source":"Client","tracing":{"trace_id":"c9f5c3bc1afaab46"}}],"data":{"user":{"id":"VXNlcjoxOTM5MTg1NTA=","rest_id":"193918550","affiliates_highlighted_label":{},"legacy_extended_profile":{},"is_profile_translatable":false}}} diff --git a/scraper/user.go b/scraper/user.go index f15756c..72f708c 100644 --- a/scraper/user.go +++ b/scraper/user.go @@ -33,6 +33,7 @@ type User struct { JoinDate time.Time IsPrivate bool IsVerified bool + IsBanned bool ProfileImageUrl string ProfileImageLocalPath string BannerImageUrl string @@ -95,6 +96,11 @@ func ParseHandleFromTweetUrl(tweet_url string) (UserHandle, error) { // Turn an APIUser, as returned from the scraper, into a properly structured User object func ParseSingleUser(apiUser APIUser) (ret User, err error) { ret.ID = UserID(apiUser.ID) + if apiUser.IsBanned { + // Banned users won't have any further info, so just return here + ret.IsBanned = true + return + } ret.DisplayName = apiUser.Name ret.Handle = UserHandle(apiUser.ScreenName) ret.Bio = apiUser.Description diff --git a/scraper/user_test.go b/scraper/user_test.go index 82ed23c..6bafed7 100644 --- a/scraper/user_test.go +++ b/scraper/user_test.go @@ -60,6 +60,9 @@ func TestParseSingleUser(t *testing.T) { if user.IsVerified != true { t.Errorf("Expected %v, got %v", true, user.IsPrivate) } + if user.IsBanned != false { + t.Errorf("User should not be banned") + } expectedProfileImage := "https://pbs.twimg.com/profile_images/1064051934812913664/Lbwdb_C9.jpg" if user.ProfileImageUrl != expectedProfileImage { t.Errorf("Expected %q, got %q", expectedProfileImage, user.ProfileImageUrl) @@ -86,6 +89,34 @@ func TestParseSingleUser(t *testing.T) { } } +/** + * Should correctly parse a banned user + */ +func TestParseBannedUser(t *testing.T) { + data, err := ioutil.ReadFile("test_responses/suspended_user.json") + if err != nil { + panic(err) + } + var user_resp scraper.UserResponse + err = json.Unmarshal(data, &user_resp) + if err != nil { + t.Fatalf(err.Error()) + } + apiUser := user_resp.ConvertToAPIUser() + + user, err := scraper.ParseSingleUser(apiUser) + if err != nil { + t.Fatalf(err.Error()) + } + + if user.ID != 193918550 { + t.Errorf("Expected id %d, got %d", 193918550, user.ID) + } + if user.IsBanned != true { + t.Errorf("Expected user to be banned") + } +} + /** * Should extract a user handle from a tweet URL, or fail if URL is invalid */