2025-02-14 15:54:36 -08:00
|
|
|
package persistence
|
2021-05-22 18:20:18 -04:00
|
|
|
|
2021-06-13 14:34:20 -07:00
|
|
|
import (
|
2022-03-13 17:09:43 -07:00
|
|
|
"fmt"
|
|
|
|
"path"
|
|
|
|
"regexp"
|
2021-06-13 14:34:20 -07:00
|
|
|
)
|
|
|
|
|
2022-01-06 15:21:27 -05:00
|
|
|
const DEFAULT_PROFILE_IMAGE_URL = "https://abs.twimg.com/sticky/default_profile_images/default_profile.png"
|
2023-08-13 15:55:04 -03:00
|
|
|
const DEFAULT_PROFILE_IMAGE = "default_profile.png"
|
2022-01-06 15:21:27 -05:00
|
|
|
|
2021-08-04 02:00:58 -07:00
|
|
|
type UserID int64
|
2021-06-16 13:14:56 -07:00
|
|
|
type UserHandle string
|
2021-06-13 14:34:20 -07:00
|
|
|
|
|
|
|
type User struct {
|
2023-06-27 21:56:29 -03:00
|
|
|
ID UserID `db:"id"`
|
|
|
|
DisplayName string `db:"display_name"`
|
|
|
|
Handle UserHandle `db:"handle"`
|
|
|
|
Bio string `db:"bio"`
|
|
|
|
FollowingCount int `db:"following_count"`
|
|
|
|
FollowersCount int `db:"followers_count"`
|
|
|
|
Location string `db:"location"`
|
|
|
|
Website string `db:"website"`
|
|
|
|
JoinDate Timestamp `db:"join_date"`
|
|
|
|
IsPrivate bool `db:"is_private"`
|
|
|
|
IsVerified bool `db:"is_verified"`
|
|
|
|
IsBanned bool `db:"is_banned"`
|
|
|
|
IsDeleted bool `db:"is_deleted"`
|
|
|
|
ProfileImageUrl string `db:"profile_image_url"`
|
|
|
|
ProfileImageLocalPath string `db:"profile_image_local_path"`
|
|
|
|
BannerImageUrl string `db:"banner_image_url"`
|
|
|
|
BannerImageLocalPath string `db:"banner_image_local_path"`
|
2022-03-13 17:09:43 -07:00
|
|
|
|
2023-06-27 21:56:29 -03:00
|
|
|
PinnedTweetID TweetID `db:"pinned_tweet_id"`
|
2022-03-13 17:09:43 -07:00
|
|
|
PinnedTweet *Tweet
|
|
|
|
|
2023-06-27 21:56:29 -03:00
|
|
|
IsFollowed bool `db:"is_followed"`
|
|
|
|
IsContentDownloaded bool `db:"is_content_downloaded"`
|
2022-03-13 17:09:43 -07:00
|
|
|
IsNeedingFakeID bool
|
2023-06-27 21:56:29 -03:00
|
|
|
IsIdFake bool `db:"is_id_fake"`
|
2021-06-13 14:34:20 -07:00
|
|
|
}
|
|
|
|
|
2023-06-19 21:25:20 -03:00
|
|
|
func GetUnknownUser() User {
|
|
|
|
return User{
|
|
|
|
ID: UserID(0x4000000000000000), // 2^62
|
|
|
|
DisplayName: "<Unknown User>",
|
|
|
|
Handle: UserHandle("<UNKNOWN USER>"),
|
|
|
|
Bio: "<blank>",
|
|
|
|
FollowersCount: 0,
|
|
|
|
FollowingCount: 0,
|
|
|
|
Location: "<blank>",
|
|
|
|
Website: "<blank>",
|
|
|
|
JoinDate: TimestampFromUnix(0),
|
|
|
|
IsVerified: false,
|
|
|
|
IsPrivate: false,
|
|
|
|
IsNeedingFakeID: false,
|
|
|
|
IsIdFake: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 16:06:58 -08:00
|
|
|
/**
|
|
|
|
* Unknown Users with handles are only created by direct GetUser calls (either `twitter fetch_user`
|
|
|
|
* subcommand or as part of tombstone user fetching.)
|
|
|
|
*/
|
2022-02-26 22:09:27 -08:00
|
|
|
func GetUnknownUserWithHandle(handle UserHandle) User {
|
2022-03-13 17:09:43 -07:00
|
|
|
return User{
|
|
|
|
ID: UserID(0), // 2^62 + 1...
|
|
|
|
DisplayName: string(handle),
|
|
|
|
Handle: handle,
|
|
|
|
Bio: "<blank>",
|
|
|
|
FollowersCount: 0,
|
|
|
|
FollowingCount: 0,
|
|
|
|
Location: "<blank>",
|
|
|
|
Website: "<blank>",
|
|
|
|
JoinDate: TimestampFromUnix(0),
|
|
|
|
IsVerified: false,
|
|
|
|
IsPrivate: false,
|
|
|
|
IsNeedingFakeID: true,
|
|
|
|
IsIdFake: true,
|
|
|
|
}
|
2022-02-26 22:09:27 -08:00
|
|
|
}
|
2022-01-06 13:43:22 -05:00
|
|
|
|
2022-01-04 13:15:47 -05:00
|
|
|
/**
|
|
|
|
* Get the URL where we would expect to find a User's tiny profile image
|
|
|
|
*/
|
|
|
|
func (u User) GetTinyProfileImageUrl() string {
|
2022-03-13 17:09:43 -07:00
|
|
|
// If profile image is empty, then just use the default profile image
|
|
|
|
if u.ProfileImageUrl == "" {
|
|
|
|
return DEFAULT_PROFILE_IMAGE_URL
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the format is as expected
|
|
|
|
r := regexp.MustCompile(`(\.\w{2,4})$`)
|
|
|
|
if !r.MatchString(u.ProfileImageUrl) {
|
2022-12-02 20:33:54 -05:00
|
|
|
return u.ProfileImageUrl
|
2022-03-13 17:09:43 -07:00
|
|
|
}
|
2022-12-02 20:33:54 -05:00
|
|
|
|
2022-03-13 17:09:43 -07:00
|
|
|
return r.ReplaceAllString(u.ProfileImageUrl, "_normal$1")
|
2022-01-04 13:15:47 -05:00
|
|
|
}
|
2022-01-05 12:37:11 -05:00
|
|
|
|
2022-01-06 15:21:27 -05:00
|
|
|
/**
|
|
|
|
* If user has a profile image, return the local path for its tiny version.
|
|
|
|
* If user has a blank or default profile image, return a non-personalized default path.
|
|
|
|
*/
|
2022-01-05 12:37:11 -05:00
|
|
|
func (u User) GetTinyProfileImageLocalPath() string {
|
2022-03-13 17:09:43 -07:00
|
|
|
if u.ProfileImageUrl == "" {
|
|
|
|
return path.Base(u.GetTinyProfileImageUrl())
|
|
|
|
}
|
2022-12-02 20:33:54 -05:00
|
|
|
|
|
|
|
r := regexp.MustCompile(`(\.\w{2,4})$`)
|
|
|
|
if !r.MatchString(u.GetTinyProfileImageUrl()) {
|
|
|
|
return string(u.Handle) + "_profile_" + path.Base(u.GetTinyProfileImageUrl()+".jpg")
|
|
|
|
}
|
|
|
|
|
2022-03-13 17:09:43 -07:00
|
|
|
return string(u.Handle) + "_profile_" + path.Base(u.GetTinyProfileImageUrl())
|
2022-01-05 12:37:11 -05:00
|
|
|
}
|
2023-08-13 15:55:04 -03:00
|
|
|
|
|
|
|
// Compute a path that will actually contain an image on disk (relative to the Profile)
|
|
|
|
// TODO: why there are so many functions that appear to do roughly the same thing?
|
|
|
|
func (u User) GetProfileImageLocalPath() string {
|
|
|
|
if u.IsContentDownloaded || u.ProfileImageLocalPath == DEFAULT_PROFILE_IMAGE {
|
|
|
|
return fmt.Sprintf("/profile_images/%s", u.ProfileImageLocalPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
r := regexp.MustCompile(`(\.\w{2,4})$`)
|
|
|
|
return fmt.Sprintf("/profile_images/%s", r.ReplaceAllString(u.ProfileImageLocalPath, "_normal$1"))
|
|
|
|
}
|