From fa1f181a8ae94a54bdb4ce5529381aeb70454422 Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:27:35 -0700 Subject: [PATCH 1/6] Send hashed IDs to client, and store actual ID in DB --- bluesky/blueskyapi.go | 9 +- bridge/bridge.go | 263 +++++++++++------------------- {bridge => cryption}/authcrypt.go | 2 +- db_controller/db_controller.go | 52 +++++- twitterv1/auth.go | 19 ++- twitterv1/discover.go | 16 +- twitterv1/interaction.go | 27 ++- twitterv1/post_viewing.go | 93 ++++++----- twitterv1/user.go | 169 ++++++++++++++----- 9 files changed, 355 insertions(+), 295 deletions(-) rename {bridge => cryption}/authcrypt.go (99%) diff --git a/bluesky/blueskyapi.go b/bluesky/blueskyapi.go index 68e8920..cc82899 100644 --- a/bluesky/blueskyapi.go +++ b/bluesky/blueskyapi.go @@ -600,7 +600,7 @@ func AuthorTTB(author User) *bridge.TwitterUser { URL: "", UtcOffset: nil, ID: bridge.BlueSkyToTwitterID(author.DID), - IDStr: bridge.BlueSkyToTwitterID(author.DID).String(), + IDStr: fmt.Sprintf("%d", bridge.BlueSkyToTwitterID(author.DID)), ProfileUseBackgroundImage: false, ListedCount: 0, ProfileTextColor: "000000", @@ -860,7 +860,12 @@ func UpdateStatus(pds string, token string, my_did string, status string, in_rep var mentionDID string for _, user := range mentionedUsers { if user.ScreenName == mention.Item { - mentionDID = bridge.TwitterIDToBlueSky(*user.ID) // efficency is poor + mentionDIDPtr, err := bridge.TwitterIDToBlueSky(user.ID) // efficency is poor + if err != nil { + mentionDID = "" + continue + } + mentionDID = *mentionDIDPtr break } } diff --git a/bridge/bridge.go b/bridge/bridge.go index 2dfd359..677e715 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -2,13 +2,12 @@ package bridge import ( "bytes" - "encoding/base32" "encoding/xml" - "errors" "fmt" - "math/big" - "strings" + "hash/fnv" "time" + + "github.com/Preloading/MastodonTwitterAPI/db_controller" ) type RelatedResultsQuery struct { @@ -46,7 +45,7 @@ type Tweet struct { Text string `json:"text"` Annotations interface{} `json:"annotations"` Contributors interface{} `json:"contributors"` - ID *big.Int `json:"id"` + ID uint64 `json:"id"` IDStr string `json:"id_str"` Geo interface{} `json:"geo"` Place interface{} `json:"place"` @@ -54,11 +53,11 @@ type Tweet struct { Source string `json:"source"` // Reply stuff - InReplyToUserID *big.Int `json:"in_reply_to_user_id"` - InReplyToUserIDStr *string `json:"in_reply_to_user_id_str"` - InReplyToStatusID *big.Int `json:"in_reply_to_status_id"` - InReplyToStatusIDStr *string `json:"in_reply_to_status_id_str"` - InReplyToScreenName *string `json:"in_reply_to_screen_name"` + InReplyToUserID *uint64 `json:"in_reply_to_user_id"` + InReplyToUserIDStr *string `json:"in_reply_to_user_id_str"` + InReplyToStatusID *uint64 `json:"in_reply_to_status_id"` + InReplyToStatusIDStr *string `json:"in_reply_to_status_id_str"` + InReplyToScreenName *string `json:"in_reply_to_screen_name"` // The following aren't found in home_timeline, but can be found when directly fetching a tweet. @@ -73,8 +72,8 @@ type Tweet struct { CurrentUserRetweet *CurrentUserRetweet `json:"current_user_retweet,omitempty"` } type CurrentUserRetweet struct { - ID *big.Int `json:"id"` - IDStr string `json:"id_str"` + ID uint64 `json:"id"` + IDStr string `json:"id_str"` } type TwitterUser struct { @@ -85,29 +84,29 @@ type TwitterUser struct { CreatedAt string `json:"created_at" xml:"created_at"` ProfileImageURL string `json:"profile_image_url" xml:"profile_image_url"` // ProfileImageURLHttps string `json:"profile_image_url_https" xml:"profile_image_url_https"` - Location string `json:"location" xml:"location"` - ProfileLinkColor string `json:"profile_link_color" xml:"profile_link_color"` - FollowRequestSent bool `json:"follow_request_sent" xml:"follow_request_sent"` - URL string `json:"url" xml:"url"` - FavouritesCount int `json:"favourites_count" xml:"favourites_count"` - ContributorsEnabled bool `json:"contributors_enabled" xml:"contributors_enabled"` - UtcOffset *int `json:"utc_offset" xml:"utc_offset"` - ID *big.Int `json:"id" xml:"id"` - IDStr string `json:"id_str" xml:"id_str"` - ProfileUseBackgroundImage bool `json:"profile_use_background_image" xml:"profile_use_background_image"` - ProfileTextColor string `json:"profile_text_color" xml:"profile_text_color"` - Protected bool `json:"protected" xml:"protected"` - FollowersCount int `json:"followers_count" xml:"followers_count"` - Lang string `json:"lang" xml:"lang"` - Notifications *bool `json:"notifications" xml:"notifications"` - TimeZone *string `json:"time_zone" xml:"time_zone"` - Verified bool `json:"verified" xml:"verified"` - ProfileBackgroundColor string `json:"profile_background_color" xml:"profile_background_color"` - GeoEnabled bool `json:"geo_enabled" xml:"geo_enabled"` - Description string `json:"description" xml:"description"` - FriendsCount int `json:"friends_count" xml:"friends_count"` - StatusesCount int `json:"statuses_count" xml:"statuses_count"` - ProfileBackgroundImageURL string `json:"profile_background_image_url" xml:"profile_background_image_url"` + Location string `json:"location" xml:"location"` + ProfileLinkColor string `json:"profile_link_color" xml:"profile_link_color"` + FollowRequestSent bool `json:"follow_request_sent" xml:"follow_request_sent"` + URL string `json:"url" xml:"url"` + FavouritesCount int `json:"favourites_count" xml:"favourites_count"` + ContributorsEnabled bool `json:"contributors_enabled" xml:"contributors_enabled"` + UtcOffset *int `json:"utc_offset" xml:"utc_offset"` + ID uint64 `json:"id" xml:"id"` + IDStr string `json:"id_str" xml:"id_str"` + ProfileUseBackgroundImage bool `json:"profile_use_background_image" xml:"profile_use_background_image"` + ProfileTextColor string `json:"profile_text_color" xml:"profile_text_color"` + Protected bool `json:"protected" xml:"protected"` + FollowersCount int `json:"followers_count" xml:"followers_count"` + Lang string `json:"lang" xml:"lang"` + Notifications *bool `json:"notifications" xml:"notifications"` + TimeZone *string `json:"time_zone" xml:"time_zone"` + Verified bool `json:"verified" xml:"verified"` + ProfileBackgroundColor string `json:"profile_background_color" xml:"profile_background_color"` + GeoEnabled bool `json:"geo_enabled" xml:"geo_enabled"` + Description string `json:"description" xml:"description"` + FriendsCount int `json:"friends_count" xml:"friends_count"` + StatusesCount int `json:"statuses_count" xml:"statuses_count"` + ProfileBackgroundImageURL string `json:"profile_background_image_url" xml:"profile_background_image_url"` // ProfileBackgroundImageURLHttps string `json:"profile_background_image_url_https" xml:"profile_background_image_url_https"` Following *bool `json:"following" xml:"following"` ScreenName string `json:"screen_name" xml:"screen_name"` @@ -120,12 +119,12 @@ type TwitterUser struct { } type TwitterActivitiySummary struct { - Favourites []big.Int `json:"favoriters"` // Pretty sure this is the User ID of the favouriters - FavouritesCount int `json:"favoriters_count"` - Repliers []big.Int `json:"repliers"` - RepliersCount int `json:"repliers_count"` - Retweets []big.Int `json:"retweeters"` - RetweetsCount int `json:"retweeters_count"` + Favourites []uint64 `json:"favoriters"` // Pretty sure this is the User ID of the favouriters + FavouritesCount int `json:"favoriters_count"` + Repliers []uint64 `json:"repliers"` + RepliersCount int `json:"repliers_count"` + Retweets []uint64 `json:"retweeters"` + RetweetsCount int `json:"retweeters_count"` } type MediaSize struct { @@ -135,7 +134,7 @@ type MediaSize struct { } type Media struct { - ID *big.Int `json:"id"` + ID uint64 `json:"id"` IDStr string `json:"id_str"` MediaURL string `json:"media_url"` MediaURLHttps string `json:"media_url_https"` @@ -167,11 +166,11 @@ type Hashtag struct { } type UserMention struct { - Name string `json:"name"` - ID *big.Int `json:"id"` - IDStr string `json:"id_str"` - Indices []int `json:"indices"` - ScreenName string `json:"screen_name"` + Name string `json:"name"` + ID uint64 `json:"id"` + IDStr string `json:"id_str"` + Indices []int `json:"indices"` + ScreenName string `json:"screen_name"` } type SleepTime struct { @@ -214,7 +213,7 @@ type Config struct { type UsersRelationship struct { Name string `json:"name" xml:"name"` IDStr string `json:"id_str" xml:"id_str"` - ID *big.Int `json:"id" xml:"id"` + ID uint64 `json:"id" xml:"id"` Connections Connections `json:"connections" xml:"connections"` ScreenName string `json:"screen_name" xml:"screen_name"` } @@ -237,7 +236,7 @@ type UserRelationships struct { type MyActivity struct { Action string `json:"action" xml:"action"` CreatedAt string `json:"created_at" xml:"created_at"` - ID *big.Int `json:"id" xml:"id"` + ID uint64 `json:"id" xml:"id"` Sources []TwitterUser `json:"sources" xml:"sources"` Targets []Tweet `json:"targets,omitempty" xml:"targets,omitempty"` TargetObjects []Tweet `json:"target_objects,omitempty" xml:"target_objects,omitempty"` @@ -246,17 +245,17 @@ type MyActivity struct { // https://web.archive.org/web/20120516154953/https://dev.twitter.com/docs/api/1/get/friendships/show // used in the /friendships/show endpoint type UserFriendship struct { - ID *big.Int `json:"id" xml:"id"` - IDStr string `json:"id_str" xml:"id_str"` - ScreenName string `json:"screen_name" xml:"screen_name"` - Following bool `json:"following" xml:"following"` - FollowedBy bool `json:"followed_by" xml:"followed_by"` - NotificationsEnabled *bool `json:"notifications_enabled" xml:"notifications_enabled"` // unknown - CanDM *bool `json:"can_dm,omitempty" xml:"can_dm,omitempty"` - Blocking *bool `json:"blocking" xml:"blocking"` // unknown - WantRetweets *bool `json:"want_retweets" xml:"want_retweets"` // unknown - MarkedSpam *bool `json:"marked_spam" xml:"marked_spam"` // unknown - AllReplies *bool `json:"all_replies" xml:"all_replies"` // unknown + ID uint64 `json:"id" xml:"id"` + IDStr string `json:"id_str" xml:"id_str"` + ScreenName string `json:"screen_name" xml:"screen_name"` + Following bool `json:"following" xml:"following"` + FollowedBy bool `json:"followed_by" xml:"followed_by"` + NotificationsEnabled *bool `json:"notifications_enabled" xml:"notifications_enabled"` // unknown + CanDM *bool `json:"can_dm,omitempty" xml:"can_dm,omitempty"` + Blocking *bool `json:"blocking" xml:"blocking"` // unknown + WantRetweets *bool `json:"want_retweets" xml:"want_retweets"` // unknown + MarkedSpam *bool `json:"marked_spam" xml:"marked_spam"` // unknown + AllReplies *bool `json:"all_replies" xml:"all_replies"` // unknown } type SourceTargetFriendship struct { @@ -285,7 +284,7 @@ type TwitterUsers struct { } type TwitterRecommendation struct { - UserID *big.Int `json:"user_id"` + UserID uint64 `json:"user_id"` User TwitterUser `json:"user"` Token string `json:"token"` } @@ -300,6 +299,12 @@ type FacetParsing struct { Item string } +func encodeToUint64(input string) uint64 { + hasher := fnv.New64a() // Create a new FNV-1a 64-bit hash + hasher.Write([]byte(input)) // Write the input string as bytes + return hasher.Sum64() // Return the 64-bit hash +} + // Bluesky's API returns a letter ID for each user, // While twitter uses a numeric ID, meaning we // need to convert between the two @@ -308,129 +313,53 @@ type FacetParsing struct { const base38Chars = "0123456789abcdefghijklmnopqrstuvwxyz:/." // BlueSkyToTwitterID converts a letter ID to a compact numeric representation using Base37 -func BlueSkyToTwitterID(letterID string) *big.Int { - letterID = strings.ToLower(letterID) - numericID := big.NewInt(0) - base := big.NewInt(39) - - for _, char := range letterID { - base37Value := strings.IndexRune(base38Chars, char) - if base37Value == -1 { - // Handle invalid characters - continue - } - numericID.Mul(numericID, base) - numericID.Add(numericID, big.NewInt(int64(base37Value))) +func BlueSkyToTwitterID(letterID string) uint64 { + twitterId := encodeToUint64(letterID) + if err := (db_controller.StoreTwitterIdInDatabase(twitterId, letterID, nil, nil)); err != nil { + fmt.Println("Error storing Twitter ID in database:", err) + panic(err) } - - return numericID + return twitterId } // TwitterIDToBlueSky converts a numeric ID to a letter ID representation using Base37 -func TwitterIDToBlueSky(numericID big.Int) string { - if numericID.Cmp(big.NewInt(0)) == 0 { - return string(base38Chars[0]) - } - - base := big.NewInt(39) - letterID := "" - tempID := new(big.Int).Set(&numericID) // Create a copy of numericID - - for tempID.Cmp(big.NewInt(0)) > 0 { - remainder := new(big.Int) - tempID.DivMod(tempID, base, remainder) - letterID = string(base38Chars[remainder.Int64()]) + letterID +func TwitterIDToBlueSky(numericID uint64) (*string, error) { + // Get the letter ID from the database + letterID, _, _, err := db_controller.GetTwitterIDFromDatabase(numericID) + if err != nil { + return nil, err } - return letterID + return letterID, nil } -func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *string) big.Int { - var encodedId *big.Int +func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *string) uint64 { + var encodedId uint64 if retweetUserId != nil { - encodedId = BlueSkyToTwitterID(fmt.Sprintf("%s:/:%s:/:%s", uri, creationTime.Format("20060102T15:04:05Z"), *retweetUserId)) - } else { - encodedId = BlueSkyToTwitterID(fmt.Sprintf("%s:/:%s", uri, creationTime.Format("20060102T15:04:05Z"))) - } - return *encodedId -} - -// This is here soley because we have to use psudo ids for retweets. -func TwitterMsgIdToBluesky(id *big.Int) (*string, *time.Time, *string, error) { - // If the tweet is not a retweet, we can use the timestamp inside of the bluesky ID - // Yup! I was also suprised that the timestamp is in the ID - // See https://atproto.com/specs/tid - - // Although if it is a retweet, we can't rely on the timestamp in the ID, since we don't have the proper ID, only the reposter & the original tweet - // Theoretically we could hack it together since we know the time of the retweet (and the thing we are missing is the encoded timestamp), but that - // seems really finky - encodedId := TwitterIDToBlueSky(*id) - uri := "" - retweetUserId := "" - timestamp := time.Time{} - err := error(nil) - - parts := strings.Split(encodedId, ":/:") - if len(parts) == 3 { - // Retweet - uri = parts[0] - retweetUserId = parts[2] - timestamp, err = time.Parse("20060102T15:04:05Z", strings.ToUpper(parts[1])) - if err != nil { - return nil, nil, nil, err + encodedId = encodeToUint64(uri + *retweetUserId + creationTime.Format("20060102150405")) // We add the date to avoid having the same ID for reposts + if err := (db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil)); err != nil { + fmt.Println("Error storing Twitter ID in database:", err) + panic(err) // TODO: handle this gracefully? } - } else if len(parts) == 2 { - // Any other type of tweet - uri = parts[0] - timestamp, err = time.Parse("20060102T15:04:05Z", strings.ToUpper(parts[1])) - if err != nil { - return nil, nil, nil, err - } - // Example URI: at://did:plc:sykr3znzovcjo7kkvt4z5ywh/app.bsky.feed.post/3ldlvtjyjwc22 - // uriparts := strings.Split(encodedId, "/") - // if len(uriparts) != 5 { - // return nil, nil, nil, errors.New("invalid URI format") - // } - // timestamp, err = DecodeTID(uriparts[4]) } else { - return nil, nil, nil, errors.New("invalid ID format") + encodedId = encodeToUint64(uri) + if err := (db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil)); err != nil { + fmt.Println("Error storing Twitter ID in database:", err) + panic(err) + } } - return &uri, ×tamp, &retweetUserId, nil + return encodedId } -// DecodeTID decodes a base32-sortable encoded TID and extracts the timestamp. -// Bluesky Specific -// Doesn't work, i can't figure out why -func DecodeTID(tid string) (time.Time, error) { - fmt.Println("TID: " + tid) - if len(tid) != 13 { - return time.Time{}, errors.New("invalid TID length") - } - - // Base32-sortable character set - base32Chars := "234567abcdefghijklmnopqrstuvwxyz" - base32Encoding := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding) - - // Decode the TID - decoded, err := base32Encoding.DecodeString(tid) +// This is here soley because we have to use psudo ids for retweets. +func TwitterMsgIdToBluesky(id uint64) (*string, *time.Time, *string, error) { + // Get the letter ID from the database + uri, createdAt, retweetUserId, err := db_controller.GetTwitterIDFromDatabase(id) if err != nil { - return time.Time{}, err + return nil, nil, nil, err } - // Convert the decoded bytes to a 64-bit integer - var id uint64 - for _, b := range decoded { - id = (id << 8) | uint64(b) - } - fmt.Println(id) - // Extract the timestamp (53 bits) and convert to microseconds - timestampMicroseconds := id >> 10 - - // Convert microseconds to time.Time - timestamp := time.Unix(0, int64(timestampMicroseconds)*1000) - fmt.Println("Decoded: " + timestamp.String()) - - return timestamp, nil + return uri, createdAt, retweetUserId, nil } // FormatTime converts Go's time.Time into the format "Wed Sep 01 00:00:00 +0000 2021" diff --git a/bridge/authcrypt.go b/cryption/authcrypt.go similarity index 99% rename from bridge/authcrypt.go rename to cryption/authcrypt.go index 7917a30..e1d5791 100644 --- a/bridge/authcrypt.go +++ b/cryption/authcrypt.go @@ -1,4 +1,4 @@ -package bridge +package cryption import ( "crypto/aes" diff --git a/db_controller/db_controller.go b/db_controller/db_controller.go index 1849f21..f89497e 100644 --- a/db_controller/db_controller.go +++ b/db_controller/db_controller.go @@ -4,9 +4,10 @@ import ( "fmt" "os" "path/filepath" + "time" - "github.com/Preloading/MastodonTwitterAPI/bridge" "github.com/Preloading/MastodonTwitterAPI/config" + authcrypt "github.com/Preloading/MastodonTwitterAPI/cryption" "github.com/google/uuid" "gorm.io/driver/mysql" "gorm.io/driver/postgres" @@ -54,6 +55,13 @@ type Token struct { RefreshExpiry float64 `gorm:"type:float;not null"` } +type TwitterIDs struct { + BlueskyID string `gorm:"type:string;primaryKey;not null"` + TwitterID uint64 `gorm:"type:string;not null"` + ReposterDid *string `gorm:"type:string"` + DateCreated *time.Time `gorm:"type:timestamp"` +} + type MessageContext struct { UserDid string `gorm:"type:string;primaryKey;not null"` TokenUUID string `gorm:"type:string;primaryKey;not null"` @@ -90,6 +98,7 @@ func InitDB(cfg config.Config) { // Auto-migrate the schema db.AutoMigrate(&Token{}) db.AutoMigrate(&MessageContext{}) + db.AutoMigrate(&TwitterIDs{}) } // StoreToken stores an encrypted access token and refresh token in the database. @@ -124,12 +133,12 @@ func StoreToken(did string, pds string, accessToken string, refreshToken string, } func UpdateToken(uuid string, did string, pds string, accessToken string, refreshToken string, encryptionKey string, accessExpiry float64, refreshExpiry float64) (*string, error) { - encryptedAccess, err := bridge.Encrypt(accessToken, encryptionKey) + encryptedAccess, err := authcrypt.Encrypt(accessToken, encryptionKey) if err != nil { return nil, fmt.Errorf("failed to encrypt access token: %v", err) } - encryptedRefresh, err := bridge.Encrypt(refreshToken, encryptionKey) + encryptedRefresh, err := authcrypt.Encrypt(refreshToken, encryptionKey) if err != nil { return nil, fmt.Errorf("failed to encrypt refresh token: %v", err) } @@ -168,15 +177,48 @@ func GetToken(did string, tokenUUID string, encryptionKey string) (*string, *str return nil, nil, nil, nil, nil, err } - accessToken, err := bridge.Decrypt(token.EncryptedAccessToken, encryptionKey) + accessToken, err := authcrypt.Decrypt(token.EncryptedAccessToken, encryptionKey) if err != nil { return nil, nil, nil, nil, nil, err } - refreshToken, err := bridge.Decrypt(token.EncryptedRefreshToken, encryptionKey) + refreshToken, err := authcrypt.Decrypt(token.EncryptedRefreshToken, encryptionKey) if err != nil { return nil, nil, nil, nil, nil, err } return &accessToken, &refreshToken, &token.AccessExpiry, &token.RefreshExpiry, &token.UserPDS, nil } + +// Stores ID data in the database. +// @params: twitterID, blueskyID, dateCreated, reposterDid +// @results: error +func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *time.Time, reposterDid *string) error { + storedData := TwitterIDs{ + TwitterID: twitterID, + BlueskyID: blueskyId, + DateCreated: dateCreated, + ReposterDid: reposterDid, + } + + result := db.Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "twitter_id"}, + }, + UpdateAll: true, + }).Create(&storedData) + + return result.Error +} + +// Gets a twitter id from the database +// @params: twitterID +// @results: blueskyID, dateCreated, reposterDid, error +func GetTwitterIDFromDatabase(twitterID uint64) (*string, *time.Time, *string, error) { + var blueskyID TwitterIDs + if err := db.Where("twitter_id = ?", twitterID).First(&blueskyID).Error; err != nil { + return nil, nil, nil, err + } + + return &blueskyID.BlueskyID, blueskyID.DateCreated, blueskyID.ReposterDid, nil +} diff --git a/twitterv1/auth.go b/twitterv1/auth.go index b9d52d5..dd40d4b 100644 --- a/twitterv1/auth.go +++ b/twitterv1/auth.go @@ -9,6 +9,7 @@ import ( blueskyapi "github.com/Preloading/MastodonTwitterAPI/bluesky" "github.com/Preloading/MastodonTwitterAPI/bridge" + "github.com/Preloading/MastodonTwitterAPI/cryption" "github.com/Preloading/MastodonTwitterAPI/db_controller" "github.com/gofiber/fiber/v2" ) @@ -31,18 +32,18 @@ func access_token(c *fiber.Ctx) error { } // Our bluesky authentication was sucessful! Now we should store the auth info, encryted, in the DB - encryptionkey, err := bridge.GenerateKey() + encryptionkey, err := cryption.GenerateKey() if err != nil { fmt.Println("Error:", err) return c.SendStatus(500) } - access_token_expiry, err := bridge.GetJWTTokenExpirationUnix(res.AccessJwt) + access_token_expiry, err := cryption.GetJWTTokenExpirationUnix(res.AccessJwt) if err != nil { fmt.Println("Error:", err) return c.SendStatus(500) } - refresh_token_expiry, err := bridge.GetJWTTokenExpirationUnix(res.RefreshJwt) + refresh_token_expiry, err := cryption.GetJWTTokenExpirationUnix(res.RefreshJwt) if err != nil { fmt.Println("Error:", err) return c.SendStatus(500) @@ -58,9 +59,9 @@ func access_token(c *fiber.Ctx) error { encryptionkey = strings.ReplaceAll(encryptionkey, "/", "_") encryptionkey = strings.ReplaceAll(encryptionkey, "=", "") // remove padding - oauth_token := fmt.Sprintf("%s.%s.%s", bridge.Base64URLEncode(res.DID), bridge.Base64URLEncode(*uuid), encryptionkey) + oauth_token := fmt.Sprintf("%s.%s.%s", cryption.Base64URLEncode(res.DID), cryption.Base64URLEncode(*uuid), encryptionkey) - return c.SendString(fmt.Sprintf("oauth_token=%s&oauth_token_secret=%s&user_id=%s&screen_name=twitterapi&x_auth_expires=%f", oauth_token, oauth_token, bridge.BlueSkyToTwitterID(res.DID).String(), *access_token_expiry)) + return c.SendString(fmt.Sprintf("oauth_token=%s&oauth_token_secret=%s&user_id=%s&screen_name=twitterapi&x_auth_expires=%f", oauth_token, oauth_token, fmt.Sprintf("%d", bridge.BlueSkyToTwitterID(res.DID)), *access_token_expiry)) } // We have an unknown request. huh. Probably registration, i'll find a way to send an error msg for that later, as registration is out of scope. return c.SendStatus(501) @@ -109,14 +110,14 @@ func GetAuthFromReq(c *fiber.Ctx) (*string, *string, *string, *string, error) { // Replace URL-friendly characters with original base64 characters // Get user DID - userDID, err := bridge.Base64URLDecode(oauthTokenSegments[0]) + userDID, err := cryption.Base64URLDecode(oauthTokenSegments[0]) if err != nil { return nil, &fallbackRoute, nil, nil, err } // Get our token UUID. This is used to look up the token in the database. - tokenUUID, err := bridge.Base64URLDecode(oauthTokenSegments[1]) + tokenUUID, err := cryption.Base64URLDecode(oauthTokenSegments[1]) if err != nil { return nil, &fallbackRoute, nil, nil, err @@ -157,11 +158,11 @@ func GetAuthFromReq(c *fiber.Ctx) (*string, *string, *string, *string, error) { accessJwt = &new_auth.AccessJwt - access_token_expiry, err := bridge.GetJWTTokenExpirationUnix(new_auth.AccessJwt) + access_token_expiry, err := cryption.GetJWTTokenExpirationUnix(new_auth.AccessJwt) if err != nil { return nil, &fallbackRoute, nil, nil, errors.New("failed to get access token expiry") } - refresh_token_expiry, err := bridge.GetJWTTokenExpirationUnix(new_auth.RefreshJwt) + refresh_token_expiry, err := cryption.GetJWTTokenExpirationUnix(new_auth.RefreshJwt) if err != nil { return nil, &fallbackRoute, nil, nil, errors.New("failed to get refresh token expiry") } diff --git a/twitterv1/discover.go b/twitterv1/discover.go index 3926488..584bfd6 100644 --- a/twitterv1/discover.go +++ b/twitterv1/discover.go @@ -2,8 +2,8 @@ package twitterv1 import ( "fmt" - "math/big" "net/url" + "strconv" "strings" "time" @@ -32,12 +32,11 @@ func InternalSearch(c *fiber.Ctx) error { max_id := c.Query("max_id") var until *time.Time if max_id != "" { - maxIDBigInt := new(big.Int) - maxIDBigInt, ok := maxIDBigInt.SetString(max_id, 10) - if !ok { + maxIDInt, err := strconv.ParseUint(max_id, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id") } - _, until, _, err = bridge.TwitterMsgIdToBluesky(maxIDBigInt) + _, until, _, err = bridge.TwitterMsgIdToBluesky(maxIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id") } @@ -46,12 +45,11 @@ func InternalSearch(c *fiber.Ctx) error { var since *time.Time since_id := c.Query("since_id") if since_id != "" { - sinceIDBigInt := new(big.Int) - sinceIDBigInt, ok := sinceIDBigInt.SetString(since_id, 10) - if !ok { + sinceIDInt, err := strconv.ParseUint(since_id, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid since_id") } - _, until, _, err = bridge.TwitterMsgIdToBluesky(sinceIDBigInt) + _, until, _, err = bridge.TwitterMsgIdToBluesky(sinceIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid since_id") } diff --git a/twitterv1/interaction.go b/twitterv1/interaction.go index 6f56ac6..c459707 100644 --- a/twitterv1/interaction.go +++ b/twitterv1/interaction.go @@ -2,8 +2,8 @@ package twitterv1 import ( "fmt" - "math/big" "regexp" + "strconv" "time" blueskyapi "github.com/Preloading/MastodonTwitterAPI/bluesky" @@ -28,10 +28,9 @@ func status_update(c *fiber.Ctx) error { // trim_user := c.FormValue("trim_user") // Unused encoded_in_reply_to_status_id_str := c.FormValue("in_reply_to_status_id") - encoded_in_reply_to_status_id_int := new(big.Int) - encoded_in_reply_to_status_id_int, ok := encoded_in_reply_to_status_id_int.SetString(encoded_in_reply_to_status_id_str, 10) + encoded_in_reply_to_status_id_int, err := strconv.ParseUint(encoded_in_reply_to_status_id_str, 10, 64) var in_reply_to_status_id *string - if ok { + if err == nil { in_reply_to_status_id, _, _, err = bridge.TwitterMsgIdToBluesky(encoded_in_reply_to_status_id_int) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid in_reply_to_status_id format") @@ -62,8 +61,8 @@ func retweet(c *fiber.Ctx) error { } // Get our IDs - idBigInt, ok := new(big.Int).SetString(postId, 10) - if !ok { + idBigInt, err := strconv.ParseUint(postId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) @@ -88,8 +87,8 @@ func retweet(c *fiber.Ctx) error { retweet.Retweeted = true now := time.Now() // pain, also fix this to use the proper timestamp according to the server. retweetId := bridge.BskyMsgToTwitterID(originalPost.Thread.Post.URI, &now, user_did) - retweet.ID = &retweetId - retweet.IDStr = retweet.ID.String() + retweet.ID = retweetId + retweet.IDStr = strconv.FormatUint(retweet.ID, 10) originalPost.Thread.Post.Viewer.Repost = blueskyRepostURI return c.JSON(bridge.Retweet{ @@ -114,8 +113,8 @@ func favourite(c *fiber.Ctx) error { } // Fetch ID - idBigInt, ok := new(big.Int).SetString(postId, 10) - if !ok { + idBigInt, err := strconv.ParseUint(postId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) @@ -153,8 +152,8 @@ func Unfavourite(c *fiber.Ctx) error { // yes i am canadian } // Fetch ID - idBigInt, ok := new(big.Int).SetString(postId, 10) - if !ok { + idBigInt, err := strconv.ParseUint(postId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) @@ -190,8 +189,8 @@ func DeleteTweet(c *fiber.Ctx) error { } // Fetch ID - idBigInt, ok := new(big.Int).SetString(postId, 10) - if !ok { + idBigInt, err := strconv.ParseUint(postId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } postIdPtr, _, repostUser, err := bridge.TwitterMsgIdToBluesky(idBigInt) diff --git a/twitterv1/post_viewing.go b/twitterv1/post_viewing.go index db55033..ae4f068 100644 --- a/twitterv1/post_viewing.go +++ b/twitterv1/post_viewing.go @@ -2,7 +2,6 @@ package twitterv1 import ( "fmt" - "math/big" "net/url" "slices" "strconv" @@ -24,11 +23,15 @@ func user_timeline(c *fiber.Ctx) error { if actor == "" { return c.Status(fiber.StatusBadRequest).SendString("No user provided") } - actorBigInt, ok := new(big.Int).SetString(actor, 10) - if !ok { + actorInt, err := strconv.ParseUint(actor, 10, 64) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") + } + actorPtr, err := bridge.TwitterIDToBlueSky(actorInt) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*actorBigInt) + actor = *actorPtr } return convert_timeline(c, actor, blueskyapi.GetUserTimeline) } @@ -36,11 +39,15 @@ func user_timeline(c *fiber.Ctx) error { func likes_timeline(c *fiber.Ctx) error { // We shall pretend that the only thing it can be is a user id. TODO: maybe rectify this later actor := c.Params("id") - actorBigInt, ok := new(big.Int).SetString(actor, 10) - if !ok { + actorInt, err := strconv.ParseUint(actor, 10, 64) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") + } + actorPtr, err := bridge.TwitterIDToBlueSky(actorInt) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*actorBigInt) + actor = *actorPtr return convert_timeline(c, actor, blueskyapi.GetActorLikes) } @@ -74,11 +81,11 @@ func convert_timeline(c *fiber.Ctx, param string, fetcher func(string, string, s // Handle getting things in the past if max_id != "" { // Get the timeline context from the DB - maxIDBigInt, ok := new(big.Int).SetString(max_id, 10) - if !ok { + maxIDInt, err := strconv.ParseUint(max_id, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") } - _, date, _, err := bridge.TwitterMsgIdToBluesky(maxIDBigInt) + _, date, _, err := bridge.TwitterMsgIdToBluesky(maxIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") } @@ -117,12 +124,12 @@ func convert_timeline(c *fiber.Ctx, param string, fetcher func(string, string, s // This is going to be painful to implement with lack of any docs func RelatedResults(c *fiber.Ctx) error { encodedId := c.Params("id") - idBigInt, ok := new(big.Int).SetString(encodedId, 10) - if !ok { + idInt, err := strconv.ParseUint(encodedId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID - uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -164,7 +171,7 @@ func RelatedResults(c *fiber.Ctx) error { twitterReplies.Results = append(twitterReplies.Results, bridge.Results{ Kind: "Tweet", Score: 1.0, - Value: TranslatePostToTweet(reply.Post, uri, postAuthor.String(), &thread.Thread.Post.Record.CreatedAt, nil, *oauthToken, *pds), + Value: TranslatePostToTweet(reply.Post, uri, strconv.FormatUint(postAuthor, 10), &thread.Thread.Post.Record.CreatedAt, nil, *oauthToken, *pds), Annotations: []bridge.Annotations{ { ConversationRole: "Descendant", @@ -179,12 +186,12 @@ func RelatedResults(c *fiber.Ctx) error { // https://web.archive.org/web/20120708204036/https://dev.twitter.com/docs/api/1/get/statuses/show/%3Aid func GetStatusFromId(c *fiber.Ctx) error { encodedId := c.Params("id") - idBigInt, ok := new(big.Int).SetString(encodedId, 10) - if !ok { + idInt, err := strconv.ParseUint(encodedId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID - uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idInt) if err != nil { fmt.Println("Error:", err) return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") @@ -226,7 +233,7 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs for _, image := range tweet.Record.Embed.Images { // Process each image tweetEntities.Media = append(tweetEntities.Media, bridge.Media{ - ID: big.NewInt(int64(id)), + ID: uint64(id), IDStr: strconv.Itoa(id), MediaURL: configData.CdnURL + "/cdn/img/?url=" + url.QueryEscape("https://cdn.bsky.app/img/feed_thumbnail/plain/"+tweet.Author.DID+"/"+image.Image.Ref.Link+"/@jpeg"), }) @@ -251,7 +258,7 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs //ScreenName: "test", ScreenName: tweet.Record.Text[faucet.Index.ByteStart+1 : faucet.Index.ByteEnd], ID: bridge.BlueSkyToTwitterID(faucet.Features[0].Did), - IDStr: bridge.BlueSkyToTwitterID(faucet.Features[0].Did).String(), + IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(faucet.Features[0].Did), 10), Indices: []int{ faucet.Index.ByteStart, faucet.Index.ByteEnd, @@ -316,45 +323,43 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs Entities: tweetEntities, Annotations: nil, // I am curious what annotations are Contributors: nil, - ID: func() *big.Int { + ID: func() uint64 { // we have to use psudo ids because of https://github.com/bluesky-social/atproto/issues/1811 if isRetweet { - id := bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) - return &id + return bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) } - id := bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) - return &id + return bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) }(), IDStr: func() string { if isRetweet { id := bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) - return id.String() + return strconv.FormatUint(id, 10) } id := bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) - return id.String() + return strconv.FormatUint(id, 10) }(), Geo: nil, Place: nil, PossiblySensitive: false, - InReplyToUserID: func() *big.Int { + InReplyToUserID: func() *uint64 { id := bridge.BlueSkyToTwitterID(replyUserBskyId) - if id.Cmp(big.NewInt(0)) == 0 { + if id == 0 { return nil } - return id + return &id }(), InReplyToUserIDStr: func() *string { id := bridge.BlueSkyToTwitterID(replyUserBskyId) - if id.Cmp(big.NewInt(0)) == 0 { + if id == 0 { return nil } - idStr := id.String() + idStr := strconv.FormatUint(id, 10) return &idStr }(), InReplyToScreenName: &tweet.Author.DisplayName, User: *author, Source: "Bluesky", - InReplyToStatusID: func() *big.Int { + InReplyToStatusID: func() *uint64 { if replyMsgBskyURI == "" { return nil } @@ -366,7 +371,7 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs return nil } id := bridge.BskyMsgToTwitterID(replyMsgBskyURI, replyTimeStamp, nil) - idStr := id.String() + idStr := strconv.FormatUint(id, 10) return &idStr }(), Retweeted: tweet.Viewer.Repost != nil, @@ -391,8 +396,8 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs _, my_did, _ := blueskyapi.GetURIComponents(*tweet.Viewer.Repost) retweetId := bridge.BskyMsgToTwitterID(tweet.URI, &RepostRecord.Value.CreatedAt, &my_did) return &bridge.CurrentUserRetweet{ - ID: &retweetId, - IDStr: retweetId.String(), + ID: retweetId, + IDStr: strconv.FormatUint(retweetId, 10), } } return nil @@ -425,7 +430,7 @@ func GetUserInfoFromTweetData(tweet blueskyapi.Post) bridge.TwitterUser { UtcOffset: nil, IsTranslator: false, ID: bridge.BlueSkyToTwitterID(tweet.URI), - IDStr: bridge.BlueSkyToTwitterID(tweet.URI).String(), + IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(tweet.URI), 10), ProfileUseBackgroundImage: false, ProfileTextColor: "333333", Protected: false, @@ -463,8 +468,8 @@ func TweetInfo(c *fiber.Ctx) error { } encodedId := c.Params("id") - idBigInt, ok := new(big.Int).SetString(encodedId, 10) - if !ok { + idBigInt, err := strconv.ParseUint(encodedId, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID @@ -492,18 +497,18 @@ func TweetInfo(c *fiber.Ctx) error { return err } - repliers := []big.Int{} - favourites := []big.Int{} - retweeters := []big.Int{} + repliers := []uint64{} + favourites := []uint64{} + retweeters := []uint64{} for _, reply := range *thread.Thread.Replies { - repliers = append(repliers, *bridge.BlueSkyToTwitterID(reply.Post.Author.DID)) + repliers = append(repliers, bridge.BlueSkyToTwitterID(reply.Post.Author.DID)) } for _, like := range likes.Likes { - favourites = append(favourites, *bridge.BlueSkyToTwitterID(like.Actor.DID)) + favourites = append(favourites, bridge.BlueSkyToTwitterID(like.Actor.DID)) } for _, reposter := range reposters.RepostedBy { - retweeters = append(retweeters, *bridge.BlueSkyToTwitterID(reposter.DID)) + retweeters = append(retweeters, bridge.BlueSkyToTwitterID(reposter.DID)) } return c.JSON(bridge.TwitterActivitiySummary{ diff --git a/twitterv1/user.go b/twitterv1/user.go index 5b7fa1b..9d5b4ad 100644 --- a/twitterv1/user.go +++ b/twitterv1/user.go @@ -2,7 +2,6 @@ package twitterv1 import ( "fmt" - "math/big" "strconv" "strings" @@ -17,11 +16,18 @@ func user_info(c *fiber.Ctx) error { if screen_name == "" { userIDStr := c.Query("user_id") - userID, ok := new(big.Int).SetString(userIDStr, 10) - if !ok { + userID, err := strconv.ParseUint(userIDStr, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - screen_name = bridge.TwitterIDToBlueSky(*userID) // yup + screen_namePtr, err := bridge.TwitterIDToBlueSky(userID) // yup + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if screen_namePtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + screen_name = *screen_namePtr if screen_name == "" { return c.Status(fiber.StatusBadRequest).SendString("No screen_name or user_id provided") @@ -63,13 +69,16 @@ func UsersLookup(c *fiber.Ctx) error { } else if user_id != "" { userIDs := strings.Split(user_id, ",") for _, idStr := range userIDs { - userID, ok := new(big.Int).SetString(idStr, 10) - if !ok { + userID, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - handle := bridge.TwitterIDToBlueSky(*userID) - if handle != "" { - usersToLookUp = append(usersToLookUp, handle) + handle, err := bridge.TwitterIDToBlueSky(userID) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if *handle != "" { + usersToLookUp = append(usersToLookUp, *handle) } } } else { @@ -126,18 +135,24 @@ func UserRelationships(c *fiber.Ctx) error { } if isID { for i, actor := range actorsArray { - actorID, ok := new(big.Int).SetString(actor, 10) - if !ok { + actorID, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorsArray[i] = bridge.TwitterIDToBlueSky(*actorID) + handlePtr, err := bridge.TwitterIDToBlueSky(actorID) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if handlePtr != nil { + actorsArray[i] = *handlePtr + } } } relationships := []bridge.UsersRelationship{} users, err := blueskyapi.GetUsersInfoRaw(*pds, *oauthToken, actorsArray, false) for _, user := range users { - encodedUserId := *bridge.BlueSkyToTwitterID(user.DID) + encodedUserId := bridge.BlueSkyToTwitterID(user.DID) if err != nil { fmt.Println("Error:", err) return c.Status(fiber.StatusInternalServerError).SendString("Failed to encode user id") @@ -166,8 +181,8 @@ func UserRelationships(c *fiber.Ctx) error { return user.DisplayName }(), ScreenName: user.Handle, - ID: &encodedUserId, - IDStr: encodedUserId.String(), + ID: encodedUserId, + IDStr: strconv.FormatUint(encodedUserId, 10), Connections: connections, }) } @@ -201,11 +216,18 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("No source_id provided") } } else { - sourceIDInt, ok := new(big.Int).SetString(sourceActor, 10) - if !ok { + sourceIDInt, err := strconv.ParseUint(sourceActor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid source_id provided") } - sourceActor = bridge.TwitterIDToBlueSky(*sourceIDInt) + sourceActorPtr, err := bridge.TwitterIDToBlueSky(sourceIDInt) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") + } + if sourceActorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") + } + sourceActor = *sourceActorPtr } targetActor := c.Query("target_id") @@ -215,11 +237,18 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("No source_id provided") } } else { - targetIDInt, ok := new(big.Int).SetString(targetActor, 10) - if !ok { + targetIDInt, err := strconv.ParseUint(targetActor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid source_id provided") } - targetActor = bridge.TwitterIDToBlueSky(*targetIDInt) + targetActorPtr, err := bridge.TwitterIDToBlueSky(targetIDInt) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") + } + if targetActorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") + } + targetActor = *targetActorPtr } // auth @@ -241,10 +270,16 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("Failed to fetch source user") } - targetDID := bridge.TwitterIDToBlueSky(*targetUser.ID) // not the most efficient way to do this, but it works - sourceDID := bridge.TwitterIDToBlueSky(*sourceUser.ID) + targetDID, err := bridge.TwitterIDToBlueSky(targetUser.ID) // not the most efficient way to do this, but it works + if err != nil { + return c.Status(fiber.StatusInternalServerError).SendString("Failed to convert target user ID to BlueSky ID") + } + sourceDID, err := bridge.TwitterIDToBlueSky(sourceUser.ID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).SendString("Failed to convert source user ID to BlueSky ID") + } - relationship, err := blueskyapi.GetRelationships(*pds, *oauthToken, sourceDID, []string{targetDID}) + relationship, err := blueskyapi.GetRelationships(*pds, *oauthToken, *sourceDID, []string{*targetDID}) if err != nil { fmt.Println("Error:", err) return c.Status(fiber.StatusInternalServerError).SendString("Failed to fetch relationship") @@ -254,14 +289,14 @@ func GetUsersRelationship(c *fiber.Ctx) error { friendship := bridge.SourceTargetFriendship{ Target: bridge.UserFriendship{ ID: targetUser.ID, - IDStr: targetUser.ID.String(), + IDStr: strconv.FormatUint(targetUser.ID, 10), ScreenName: targetUser.ScreenName, Following: relationship.Relationships[0].FollowedBy != "", FollowedBy: relationship.Relationships[0].Following != "", }, Source: bridge.UserFriendship{ ID: sourceUser.ID, - IDStr: sourceUser.ID.String(), + IDStr: strconv.FormatUint(sourceUser.ID, 10), ScreenName: sourceUser.ScreenName, Following: relationship.Relationships[0].Following != "", FollowedBy: relationship.Relationships[0].FollowedBy != "", @@ -293,11 +328,18 @@ func FollowUser(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, ok := new(big.Int).SetString(actor, 10) - if !ok { + id, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*id) + actorPtr, err := bridge.TwitterIDToBlueSky(id) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if actorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + actor = *actorPtr } // follow @@ -365,11 +407,18 @@ func UnfollowUserForm(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, ok := new(big.Int).SetString(actor, 10) - if !ok { + id, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*id) + actorPtr, err := bridge.TwitterIDToBlueSky(id) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if actorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + actor = *actorPtr } return UnfollowUser(c, actor) } @@ -377,11 +426,18 @@ func UnfollowUserForm(c *fiber.Ctx) error { func UnfollowUserParams(c *fiber.Ctx) error { // This should allow lookup with a handle, but tbh, i'm too lazy to implement that right now as i do not see it being used. actor := c.Params("id") - actorID, ok := new(big.Int).SetString(actor, 10) - if !ok { + actorID, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*actorID) + actorPtr, err := bridge.TwitterIDToBlueSky(actorID) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if actorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + actor = *actorPtr return UnfollowUser(c, actor) } @@ -404,11 +460,18 @@ func GetFollowers(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, ok := new(big.Int).SetString(actor, 10) - if !ok { + id, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*id) + actorPtr, err := bridge.TwitterIDToBlueSky(id) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if actorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + actor = *actorPtr } // fetch followers @@ -467,11 +530,18 @@ func GetFollows(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, ok := new(big.Int).SetString(actor, 10) - if !ok { + id, err := strconv.ParseUint(actor, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actor = bridge.TwitterIDToBlueSky(*id) + actorPtr, err := bridge.TwitterIDToBlueSky(id) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if actorPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + actor = *actorPtr } // fetch followers @@ -535,12 +605,23 @@ func GetSuggestedUsers(c *fiber.Ctx) error { // see if they provided a user id userID := c.Query("user_id") if userID != "" { - userIDInt, ok := new(big.Int).SetString(userID, 10) - if !ok { + userIDInt, err := strconv.ParseUint(userID, 10, 64) + if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - userID = bridge.TwitterIDToBlueSky(*userIDInt) + userIDPtr, err := bridge.TwitterIDToBlueSky(userIDInt) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + if userIDPtr == nil { + return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") + } + userID = *userIDPtr + recommendedUsers, err = blueskyapi.GetOthersSuggestedUsers(*pds, *oauthToken, limit, userID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).SendString("Failed to fetch suggested users") + } } else { recommendedUsers, err = blueskyapi.GetMySuggestedUsers(*pds, *oauthToken, limit) } From 2df3c64869639ed0eb153cfd26c2be4db5e8efa6 Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:40:07 -0700 Subject: [PATCH 2/6] fix the db stuff --- db_controller/db_controller.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/db_controller/db_controller.go b/db_controller/db_controller.go index f89497e..564ba83 100644 --- a/db_controller/db_controller.go +++ b/db_controller/db_controller.go @@ -6,6 +6,8 @@ import ( "path/filepath" "time" + "strconv" + "github.com/Preloading/MastodonTwitterAPI/config" authcrypt "github.com/Preloading/MastodonTwitterAPI/cryption" "github.com/google/uuid" @@ -56,8 +58,8 @@ type Token struct { } type TwitterIDs struct { - BlueskyID string `gorm:"type:string;primaryKey;not null"` - TwitterID uint64 `gorm:"type:string;not null"` + BlueskyID string `gorm:"type:string;not null"` + TwitterID string `gorm:"type:string;primaryKey;not null"` // Ensure this has a unique constraint ReposterDid *string `gorm:"type:string"` DateCreated *time.Time `gorm:"type:timestamp"` } @@ -195,7 +197,7 @@ func GetToken(did string, tokenUUID string, encryptionKey string) (*string, *str // @results: error func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *time.Time, reposterDid *string) error { storedData := TwitterIDs{ - TwitterID: twitterID, + TwitterID: strconv.FormatUint(twitterID, 10), // Convert uint64 to string BlueskyID: blueskyId, DateCreated: dateCreated, ReposterDid: reposterDid, @@ -216,7 +218,7 @@ func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *t // @results: blueskyID, dateCreated, reposterDid, error func GetTwitterIDFromDatabase(twitterID uint64) (*string, *time.Time, *string, error) { var blueskyID TwitterIDs - if err := db.Where("twitter_id = ?", twitterID).First(&blueskyID).Error; err != nil { + if err := db.Where("twitter_id = ?", strconv.FormatUint(twitterID, 10)).First(&blueskyID).Error; err != nil { return nil, nil, nil, err } From 6cdefab4389baa776396216b410f82f5bb9de2ca Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:30:17 -0700 Subject: [PATCH 3/6] suffer with me on ids --- bluesky/blueskyapi.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bluesky/blueskyapi.go b/bluesky/blueskyapi.go index cc82899..cd248b8 100644 --- a/bluesky/blueskyapi.go +++ b/bluesky/blueskyapi.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/url" + "strconv" "strings" "sync" "time" @@ -600,7 +601,7 @@ func AuthorTTB(author User) *bridge.TwitterUser { URL: "", UtcOffset: nil, ID: bridge.BlueSkyToTwitterID(author.DID), - IDStr: fmt.Sprintf("%d", bridge.BlueSkyToTwitterID(author.DID)), + IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(author.DID), 10), ProfileUseBackgroundImage: false, ListedCount: 0, ProfileTextColor: "000000", From 57d51784640bc9d56dc5e3dea36d9ae4a85ef03a Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:44:47 -0700 Subject: [PATCH 4/6] quick author id optimization --- bluesky/blueskyapi.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bluesky/blueskyapi.go b/bluesky/blueskyapi.go index cd248b8..0b5e0b8 100644 --- a/bluesky/blueskyapi.go +++ b/bluesky/blueskyapi.go @@ -582,6 +582,7 @@ func GetRelationships(pds string, token string, source string, others []string) } func AuthorTTB(author User) *bridge.TwitterUser { + id := bridge.BlueSkyToTwitterID(author.DID) return &bridge.TwitterUser{ ProfileSidebarFillColor: "e0ff92", Name: func() string { @@ -600,8 +601,8 @@ func AuthorTTB(author User) *bridge.TwitterUser { ContributorsEnabled: false, URL: "", UtcOffset: nil, - ID: bridge.BlueSkyToTwitterID(author.DID), - IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(author.DID), 10), + ID: id, + IDStr: strconv.FormatUint(id, 10), ProfileUseBackgroundImage: false, ListedCount: 0, ProfileTextColor: "000000", From 158f444c936f961da0ac67806377d03ecdf51fb0 Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:03:34 -0700 Subject: [PATCH 5/6] changes i forgor about --- bridge/bridge.go | 6 +++--- db_controller/db_controller.go | 9 ++++++++- twitterv1/post_viewing.go | 17 ++++++++++++++--- twitterv1/twitterv1.go | 4 +++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 677e715..a3a4329 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -315,7 +315,7 @@ const base38Chars = "0123456789abcdefghijklmnopqrstuvwxyz:/." // BlueSkyToTwitterID converts a letter ID to a compact numeric representation using Base37 func BlueSkyToTwitterID(letterID string) uint64 { twitterId := encodeToUint64(letterID) - if err := (db_controller.StoreTwitterIdInDatabase(twitterId, letterID, nil, nil)); err != nil { + if err := db_controller.StoreTwitterIdInDatabase(twitterId, letterID, nil, nil); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) } @@ -337,13 +337,13 @@ func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *stri var encodedId uint64 if retweetUserId != nil { encodedId = encodeToUint64(uri + *retweetUserId + creationTime.Format("20060102150405")) // We add the date to avoid having the same ID for reposts - if err := (db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil)); err != nil { + if err := db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, retweetUserId); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) // TODO: handle this gracefully? } } else { encodedId = encodeToUint64(uri) - if err := (db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil)); err != nil { + if err := db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) } diff --git a/db_controller/db_controller.go b/db_controller/db_controller.go index 564ba83..733c23c 100644 --- a/db_controller/db_controller.go +++ b/db_controller/db_controller.go @@ -210,7 +210,14 @@ func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *t UpdateAll: true, }).Create(&storedData) - return result.Error + if result.Error != nil { + // If there's an error, try updating the existing record + fmt.Println("Error:", result.Error) + panic(result.Error) + //return db.Model(&TwitterIDs{}).Where("twitter_id = ?", strconv.FormatUint(twitterID, 10)).Updates(storedData).Error + } + + return nil } // Gets a twitter id from the database diff --git a/twitterv1/post_viewing.go b/twitterv1/post_viewing.go index ae4f068..2ae63b9 100644 --- a/twitterv1/post_viewing.go +++ b/twitterv1/post_viewing.go @@ -82,16 +82,19 @@ func convert_timeline(c *fiber.Ctx, param string, fetcher func(string, string, s if max_id != "" { // Get the timeline context from the DB maxIDInt, err := strconv.ParseUint(max_id, 10, 64) + fmt.Println("Max ID:", maxIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") } - _, date, _, err := bridge.TwitterMsgIdToBluesky(maxIDInt) + uri, date, _, err := bridge.TwitterMsgIdToBluesky(maxIDInt) + fmt.Println("Max ID:", uri) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") } context = date.Format(time.RFC3339) } + fmt.Println("Context:", context) err, res := fetcher(*pds, *oauthToken, context, param, limit) if err != nil { fmt.Println("Error:", err) @@ -342,6 +345,10 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs Place: nil, PossiblySensitive: false, InReplyToUserID: func() *uint64 { + if replyMsgBskyURI == "" { + return nil + } + id := bridge.BlueSkyToTwitterID(replyUserBskyId) if id == 0 { return nil @@ -349,6 +356,9 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs return &id }(), InReplyToUserIDStr: func() *string { + if replyMsgBskyURI == "" { + return nil + } id := bridge.BlueSkyToTwitterID(replyUserBskyId) if id == 0 { return nil @@ -374,19 +384,20 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs idStr := strconv.FormatUint(id, 10) return &idStr }(), - Retweeted: tweet.Viewer.Repost != nil, + Retweeted: tweet.Viewer.Repost != nil && !isRetweet, RetweetCount: tweet.RepostCount, RetweetedStatus: func() *bridge.Tweet { if isRetweet { retweet_bsky := tweet retweet_bsky.Author = bsky_retweet_og_author + //retweet_bsky.Viewer.Repost = nil translatedTweet := TranslatePostToTweet(retweet_bsky, replyMsgBskyURI, replyUserBskyId, replyTimeStamp, nil, token, pds) return &translatedTweet } return nil }(), CurrentUserRetweet: func() *bridge.CurrentUserRetweet { - if tweet.Viewer.Repost != nil { + if tweet.Viewer.Repost != nil && !isRetweet { RepostRecord, err := blueskyapi.GetRecordWithUri(pds, *tweet.Viewer.Repost) if err != nil { fmt.Println("Error:", err) diff --git a/twitterv1/twitterv1.go b/twitterv1/twitterv1.go index 9fafd85..9f96f1f 100644 --- a/twitterv1/twitterv1.go +++ b/twitterv1/twitterv1.go @@ -1,6 +1,8 @@ package twitterv1 import ( + "fmt" + blueskyapi "github.com/Preloading/MastodonTwitterAPI/bluesky" "github.com/Preloading/MastodonTwitterAPI/config" "github.com/gofiber/fiber/v2" @@ -23,7 +25,7 @@ func InitServer(config *config.Config) { if config.DeveloperMode { app.Use(func(c *fiber.Ctx) error { // fmt.Println("Request Method:", c.Method()) - // fmt.Println("Request URL:", c.OriginalURL()) + fmt.Println("Request URL:", c.OriginalURL()) // fmt.Println("Post Body:", string(c.Body())) // fmt.Println("Headers:", string(c.Request().Header.Header())) // fmt.Println() From de70756eb8a69943a1acfe5ad1edb657bff2b9c7 Mon Sep 17 00:00:00 2001 From: Logan <60761520+Preloading@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:44:24 -0700 Subject: [PATCH 6/6] fix: ids are now 63 bits and less buggy --- bluesky/blueskyapi.go | 17 +++----- bridge/bridge.go | 71 +++++++++++++++++-------------- db_controller/db_controller.go | 16 +++++-- twitterv1/connect.go | 6 +-- twitterv1/discover.go | 8 ++-- twitterv1/interaction.go | 24 +++++------ twitterv1/post_viewing.go | 78 ++++++++++++++++------------------ twitterv1/user.go | 56 ++++++++++++------------ 8 files changed, 139 insertions(+), 137 deletions(-) diff --git a/bluesky/blueskyapi.go b/bluesky/blueskyapi.go index 0b5e0b8..72b8eaf 100644 --- a/bluesky/blueskyapi.go +++ b/bluesky/blueskyapi.go @@ -601,8 +601,8 @@ func AuthorTTB(author User) *bridge.TwitterUser { ContributorsEnabled: false, URL: "", UtcOffset: nil, - ID: id, - IDStr: strconv.FormatUint(id, 10), + ID: *id, + IDStr: strconv.FormatInt(*id, 10), ProfileUseBackgroundImage: false, ListedCount: 0, ProfileTextColor: "000000", @@ -859,20 +859,15 @@ func UpdateStatus(pds string, token string, my_did string, status string, in_rep // add mentions to the facets if err == nil { for _, mention := range mentions { - var mentionDID string + var mentionDID *string for _, user := range mentionedUsers { if user.ScreenName == mention.Item { - mentionDIDPtr, err := bridge.TwitterIDToBlueSky(user.ID) // efficency is poor - if err != nil { - mentionDID = "" - continue - } - mentionDID = *mentionDIDPtr + mentionDID, _ = bridge.TwitterIDToBlueSky(&user.ID) // efficency is poor break } } - if mentionDID == "" { + if mentionDID == nil { continue } @@ -884,7 +879,7 @@ func UpdateStatus(pds string, token string, my_did string, status string, in_rep Features: []Feature{ { Type: "app.bsky.richtext.facet#mention", - Did: mentionDID, + Did: *mentionDID, }, }, }) diff --git a/bridge/bridge.go b/bridge/bridge.go index a3a4329..383de74 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -45,7 +45,7 @@ type Tweet struct { Text string `json:"text"` Annotations interface{} `json:"annotations"` Contributors interface{} `json:"contributors"` - ID uint64 `json:"id"` + ID int64 `json:"id"` IDStr string `json:"id_str"` Geo interface{} `json:"geo"` Place interface{} `json:"place"` @@ -53,9 +53,9 @@ type Tweet struct { Source string `json:"source"` // Reply stuff - InReplyToUserID *uint64 `json:"in_reply_to_user_id"` + InReplyToUserID *int64 `json:"in_reply_to_user_id"` InReplyToUserIDStr *string `json:"in_reply_to_user_id_str"` - InReplyToStatusID *uint64 `json:"in_reply_to_status_id"` + InReplyToStatusID *int64 `json:"in_reply_to_status_id"` InReplyToStatusIDStr *string `json:"in_reply_to_status_id_str"` InReplyToScreenName *string `json:"in_reply_to_screen_name"` @@ -72,7 +72,7 @@ type Tweet struct { CurrentUserRetweet *CurrentUserRetweet `json:"current_user_retweet,omitempty"` } type CurrentUserRetweet struct { - ID uint64 `json:"id"` + ID int64 `json:"id"` IDStr string `json:"id_str"` } @@ -91,7 +91,7 @@ type TwitterUser struct { FavouritesCount int `json:"favourites_count" xml:"favourites_count"` ContributorsEnabled bool `json:"contributors_enabled" xml:"contributors_enabled"` UtcOffset *int `json:"utc_offset" xml:"utc_offset"` - ID uint64 `json:"id" xml:"id"` + ID int64 `json:"id" xml:"id"` IDStr string `json:"id_str" xml:"id_str"` ProfileUseBackgroundImage bool `json:"profile_use_background_image" xml:"profile_use_background_image"` ProfileTextColor string `json:"profile_text_color" xml:"profile_text_color"` @@ -119,12 +119,12 @@ type TwitterUser struct { } type TwitterActivitiySummary struct { - Favourites []uint64 `json:"favoriters"` // Pretty sure this is the User ID of the favouriters - FavouritesCount int `json:"favoriters_count"` - Repliers []uint64 `json:"repliers"` - RepliersCount int `json:"repliers_count"` - Retweets []uint64 `json:"retweeters"` - RetweetsCount int `json:"retweeters_count"` + Favourites []int64 `json:"favoriters"` // Pretty sure this is the User ID of the favouriters + FavouritesCount int `json:"favoriters_count"` + Repliers []int64 `json:"repliers"` + RepliersCount int `json:"repliers_count"` + Retweets []int64 `json:"retweeters"` + RetweetsCount int `json:"retweeters_count"` } type MediaSize struct { @@ -134,7 +134,7 @@ type MediaSize struct { } type Media struct { - ID uint64 `json:"id"` + ID int64 `json:"id"` IDStr string `json:"id_str"` MediaURL string `json:"media_url"` MediaURLHttps string `json:"media_url_https"` @@ -167,7 +167,7 @@ type Hashtag struct { type UserMention struct { Name string `json:"name"` - ID uint64 `json:"id"` + ID *int64 `json:"id"` IDStr string `json:"id_str"` Indices []int `json:"indices"` ScreenName string `json:"screen_name"` @@ -213,7 +213,7 @@ type Config struct { type UsersRelationship struct { Name string `json:"name" xml:"name"` IDStr string `json:"id_str" xml:"id_str"` - ID uint64 `json:"id" xml:"id"` + ID int64 `json:"id" xml:"id"` Connections Connections `json:"connections" xml:"connections"` ScreenName string `json:"screen_name" xml:"screen_name"` } @@ -236,7 +236,7 @@ type UserRelationships struct { type MyActivity struct { Action string `json:"action" xml:"action"` CreatedAt string `json:"created_at" xml:"created_at"` - ID uint64 `json:"id" xml:"id"` + ID int64 `json:"id" xml:"id"` Sources []TwitterUser `json:"sources" xml:"sources"` Targets []Tweet `json:"targets,omitempty" xml:"targets,omitempty"` TargetObjects []Tweet `json:"target_objects,omitempty" xml:"target_objects,omitempty"` @@ -245,7 +245,7 @@ type MyActivity struct { // https://web.archive.org/web/20120516154953/https://dev.twitter.com/docs/api/1/get/friendships/show // used in the /friendships/show endpoint type UserFriendship struct { - ID uint64 `json:"id" xml:"id"` + ID int64 `json:"id" xml:"id"` IDStr string `json:"id_str" xml:"id_str"` ScreenName string `json:"screen_name" xml:"screen_name"` Following bool `json:"following" xml:"following"` @@ -284,7 +284,7 @@ type TwitterUsers struct { } type TwitterRecommendation struct { - UserID uint64 `json:"user_id"` + UserID int64 `json:"user_id"` User TwitterUser `json:"user"` Token string `json:"token"` } @@ -299,22 +299,23 @@ type FacetParsing struct { Item string } -func encodeToUint64(input string) uint64 { - hasher := fnv.New64a() // Create a new FNV-1a 64-bit hash - hasher.Write([]byte(input)) // Write the input string as bytes - return hasher.Sum64() // Return the 64-bit hash +func encodeToUint63(input string) *int64 { + hasher := fnv.New64a() // Create a new FNV-1a 64-bit hash + hasher.Write([]byte(input)) // Write the input string as bytes + hash := hasher.Sum64() // Get the 64-bit hash + result := int64(hash & ((1 << 63) - 1)) // Mask the MSB to ensure 63 bits and convert to int64 + return &result } // Bluesky's API returns a letter ID for each user, // While twitter uses a numeric ID, meaning we // need to convert between the two -// Base36 characters (digits and lowercase letters) -const base38Chars = "0123456789abcdefghijklmnopqrstuvwxyz:/." - -// BlueSkyToTwitterID converts a letter ID to a compact numeric representation using Base37 -func BlueSkyToTwitterID(letterID string) uint64 { - twitterId := encodeToUint64(letterID) +func BlueSkyToTwitterID(letterID string) *int64 { + if letterID == "" { + return nil + } + twitterId := encodeToUint63(letterID) if err := db_controller.StoreTwitterIdInDatabase(twitterId, letterID, nil, nil); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) @@ -323,7 +324,7 @@ func BlueSkyToTwitterID(letterID string) uint64 { } // TwitterIDToBlueSky converts a numeric ID to a letter ID representation using Base37 -func TwitterIDToBlueSky(numericID uint64) (*string, error) { +func TwitterIDToBlueSky(numericID *int64) (*string, error) { // Get the letter ID from the database letterID, _, _, err := db_controller.GetTwitterIDFromDatabase(numericID) if err != nil { @@ -333,16 +334,20 @@ func TwitterIDToBlueSky(numericID uint64) (*string, error) { return letterID, nil } -func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *string) uint64 { - var encodedId uint64 +func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *string) *int64 { + if uri == "" { + return nil + } + + var encodedId *int64 if retweetUserId != nil { - encodedId = encodeToUint64(uri + *retweetUserId + creationTime.Format("20060102150405")) // We add the date to avoid having the same ID for reposts + encodedId = encodeToUint63(uri + *retweetUserId + creationTime.Format("20060102150405")) // We add the date to avoid having the same ID for reposts if err := db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, retweetUserId); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) // TODO: handle this gracefully? } } else { - encodedId = encodeToUint64(uri) + encodedId = encodeToUint63(uri) if err := db_controller.StoreTwitterIdInDatabase(encodedId, uri, creationTime, nil); err != nil { fmt.Println("Error storing Twitter ID in database:", err) panic(err) @@ -352,7 +357,7 @@ func BskyMsgToTwitterID(uri string, creationTime *time.Time, retweetUserId *stri } // This is here soley because we have to use psudo ids for retweets. -func TwitterMsgIdToBluesky(id uint64) (*string, *time.Time, *string, error) { +func TwitterMsgIdToBluesky(id *int64) (*string, *time.Time, *string, error) { // Get the letter ID from the database uri, createdAt, retweetUserId, err := db_controller.GetTwitterIDFromDatabase(id) if err != nil { diff --git a/db_controller/db_controller.go b/db_controller/db_controller.go index 733c23c..6123207 100644 --- a/db_controller/db_controller.go +++ b/db_controller/db_controller.go @@ -195,9 +195,13 @@ func GetToken(did string, tokenUUID string, encryptionKey string) (*string, *str // Stores ID data in the database. // @params: twitterID, blueskyID, dateCreated, reposterDid // @results: error -func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *time.Time, reposterDid *string) error { +func StoreTwitterIdInDatabase(twitterID *int64, blueskyId string, dateCreated *time.Time, reposterDid *string) error { + if twitterID == nil { + return fmt.Errorf("twitterID is nil") + } + storedData := TwitterIDs{ - TwitterID: strconv.FormatUint(twitterID, 10), // Convert uint64 to string + TwitterID: strconv.FormatInt(*twitterID, 10), // Convert *int64 to string BlueskyID: blueskyId, DateCreated: dateCreated, ReposterDid: reposterDid, @@ -223,9 +227,13 @@ func StoreTwitterIdInDatabase(twitterID uint64, blueskyId string, dateCreated *t // Gets a twitter id from the database // @params: twitterID // @results: blueskyID, dateCreated, reposterDid, error -func GetTwitterIDFromDatabase(twitterID uint64) (*string, *time.Time, *string, error) { +func GetTwitterIDFromDatabase(twitterID *int64) (*string, *time.Time, *string, error) { + if twitterID == nil { + return nil, nil, nil, fmt.Errorf("twitterID is nil") + } + var blueskyID TwitterIDs - if err := db.Where("twitter_id = ?", strconv.FormatUint(twitterID, 10)).First(&blueskyID).Error; err != nil { + if err := db.Where("twitter_id = ?", strconv.FormatInt(*twitterID, 10)).First(&blueskyID).Error; err != nil { return nil, nil, nil, err } diff --git a/twitterv1/connect.go b/twitterv1/connect.go index 61f3985..92f9df5 100644 --- a/twitterv1/connect.go +++ b/twitterv1/connect.go @@ -89,7 +89,7 @@ func GetMyActivity(c *fiber.Ctx) error { twitterNotifications = append(twitterNotifications, bridge.MyActivity{ Action: "follow", CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt), - ID: bridge.BlueSkyToTwitterID(notification.URI), + ID: *bridge.BlueSkyToTwitterID(notification.URI), Sources: sources, }) case "like": @@ -133,7 +133,7 @@ func GetMyActivity(c *fiber.Ctx) error { twitterNotifications = append(twitterNotifications, bridge.MyActivity{ Action: "favorite", CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt), - ID: bridge.BlueSkyToTwitterID(notificationId), + ID: *bridge.BlueSkyToTwitterID(notificationId), Sources: sources, Targets: []bridge.Tweet{likedTweet}, }) @@ -178,7 +178,7 @@ func GetMyActivity(c *fiber.Ctx) error { twitterNotifications = append(twitterNotifications, bridge.MyActivity{ Action: "retweet", CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt), - ID: bridge.BlueSkyToTwitterID(notificationId), + ID: *bridge.BlueSkyToTwitterID(notificationId), Sources: sources, TargetObjects: []bridge.Tweet{retweetedTweet}, }) diff --git a/twitterv1/discover.go b/twitterv1/discover.go index 584bfd6..114e02e 100644 --- a/twitterv1/discover.go +++ b/twitterv1/discover.go @@ -32,11 +32,11 @@ func InternalSearch(c *fiber.Ctx) error { max_id := c.Query("max_id") var until *time.Time if max_id != "" { - maxIDInt, err := strconv.ParseUint(max_id, 10, 64) + maxIDInt, err := strconv.ParseInt(max_id, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id") } - _, until, _, err = bridge.TwitterMsgIdToBluesky(maxIDInt) + _, until, _, err = bridge.TwitterMsgIdToBluesky(&maxIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id") } @@ -45,11 +45,11 @@ func InternalSearch(c *fiber.Ctx) error { var since *time.Time since_id := c.Query("since_id") if since_id != "" { - sinceIDInt, err := strconv.ParseUint(since_id, 10, 64) + sinceIDInt, err := strconv.ParseInt(since_id, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid since_id") } - _, until, _, err = bridge.TwitterMsgIdToBluesky(sinceIDInt) + _, until, _, err = bridge.TwitterMsgIdToBluesky(&sinceIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid since_id") } diff --git a/twitterv1/interaction.go b/twitterv1/interaction.go index c459707..e8f4d3b 100644 --- a/twitterv1/interaction.go +++ b/twitterv1/interaction.go @@ -28,10 +28,10 @@ func status_update(c *fiber.Ctx) error { // trim_user := c.FormValue("trim_user") // Unused encoded_in_reply_to_status_id_str := c.FormValue("in_reply_to_status_id") - encoded_in_reply_to_status_id_int, err := strconv.ParseUint(encoded_in_reply_to_status_id_str, 10, 64) + encoded_in_reply_to_status_id_int, err := strconv.ParseInt(encoded_in_reply_to_status_id_str, 10, 64) var in_reply_to_status_id *string if err == nil { - in_reply_to_status_id, _, _, err = bridge.TwitterMsgIdToBluesky(encoded_in_reply_to_status_id_int) + in_reply_to_status_id, _, _, err = bridge.TwitterMsgIdToBluesky(&encoded_in_reply_to_status_id_int) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid in_reply_to_status_id format") } @@ -61,11 +61,11 @@ func retweet(c *fiber.Ctx) error { } // Get our IDs - idBigInt, err := strconv.ParseUint(postId, 10, 64) + idBigInt, err := strconv.ParseInt(postId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } - postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idBigInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -87,8 +87,8 @@ func retweet(c *fiber.Ctx) error { retweet.Retweeted = true now := time.Now() // pain, also fix this to use the proper timestamp according to the server. retweetId := bridge.BskyMsgToTwitterID(originalPost.Thread.Post.URI, &now, user_did) - retweet.ID = retweetId - retweet.IDStr = strconv.FormatUint(retweet.ID, 10) + retweet.ID = *retweetId + retweet.IDStr = strconv.FormatInt(retweet.ID, 10) originalPost.Thread.Post.Viewer.Repost = blueskyRepostURI return c.JSON(bridge.Retweet{ @@ -113,11 +113,11 @@ func favourite(c *fiber.Ctx) error { } // Fetch ID - idBigInt, err := strconv.ParseUint(postId, 10, 64) + idBigInt, err := strconv.ParseInt(postId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } - postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idBigInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -152,11 +152,11 @@ func Unfavourite(c *fiber.Ctx) error { // yes i am canadian } // Fetch ID - idBigInt, err := strconv.ParseUint(postId, 10, 64) + idBigInt, err := strconv.ParseInt(postId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } - postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + postIdPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idBigInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -189,11 +189,11 @@ func DeleteTweet(c *fiber.Ctx) error { } // Fetch ID - idBigInt, err := strconv.ParseUint(postId, 10, 64) + idBigInt, err := strconv.ParseInt(postId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } - postIdPtr, _, repostUser, err := bridge.TwitterMsgIdToBluesky(idBigInt) + postIdPtr, _, repostUser, err := bridge.TwitterMsgIdToBluesky(&idBigInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } diff --git a/twitterv1/post_viewing.go b/twitterv1/post_viewing.go index 2ae63b9..d51abba 100644 --- a/twitterv1/post_viewing.go +++ b/twitterv1/post_viewing.go @@ -23,11 +23,11 @@ func user_timeline(c *fiber.Ctx) error { if actor == "" { return c.Status(fiber.StatusBadRequest).SendString("No user provided") } - actorInt, err := strconv.ParseUint(actor, 10, 64) + actorInt, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(actorInt) + actorPtr, err := bridge.TwitterIDToBlueSky(&actorInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } @@ -39,11 +39,11 @@ func user_timeline(c *fiber.Ctx) error { func likes_timeline(c *fiber.Ctx) error { // We shall pretend that the only thing it can be is a user id. TODO: maybe rectify this later actor := c.Params("id") - actorInt, err := strconv.ParseUint(actor, 10, 64) + actorInt, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(actorInt) + actorPtr, err := bridge.TwitterIDToBlueSky(&actorInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } @@ -81,12 +81,12 @@ func convert_timeline(c *fiber.Ctx, param string, fetcher func(string, string, s // Handle getting things in the past if max_id != "" { // Get the timeline context from the DB - maxIDInt, err := strconv.ParseUint(max_id, 10, 64) + maxIDInt, err := strconv.ParseInt(max_id, 10, 64) fmt.Println("Max ID:", maxIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") } - uri, date, _, err := bridge.TwitterMsgIdToBluesky(maxIDInt) + uri, date, _, err := bridge.TwitterMsgIdToBluesky(&maxIDInt) fmt.Println("Max ID:", uri) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id format") @@ -127,12 +127,12 @@ func convert_timeline(c *fiber.Ctx, param string, fetcher func(string, string, s // This is going to be painful to implement with lack of any docs func RelatedResults(c *fiber.Ctx) error { encodedId := c.Params("id") - idInt, err := strconv.ParseUint(encodedId, 10, 64) + idInt, err := strconv.ParseInt(encodedId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID - uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idInt) + uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -174,7 +174,7 @@ func RelatedResults(c *fiber.Ctx) error { twitterReplies.Results = append(twitterReplies.Results, bridge.Results{ Kind: "Tweet", Score: 1.0, - Value: TranslatePostToTweet(reply.Post, uri, strconv.FormatUint(postAuthor, 10), &thread.Thread.Post.Record.CreatedAt, nil, *oauthToken, *pds), + Value: TranslatePostToTweet(reply.Post, uri, strconv.FormatInt(*postAuthor, 10), &thread.Thread.Post.Record.CreatedAt, nil, *oauthToken, *pds), Annotations: []bridge.Annotations{ { ConversationRole: "Descendant", @@ -189,12 +189,12 @@ func RelatedResults(c *fiber.Ctx) error { // https://web.archive.org/web/20120708204036/https://dev.twitter.com/docs/api/1/get/statuses/show/%3Aid func GetStatusFromId(c *fiber.Ctx) error { encodedId := c.Params("id") - idInt, err := strconv.ParseUint(encodedId, 10, 64) + idInt, err := strconv.ParseInt(encodedId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID - uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idInt) + uriPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idInt) if err != nil { fmt.Println("Error:", err) return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") @@ -236,7 +236,7 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs for _, image := range tweet.Record.Embed.Images { // Process each image tweetEntities.Media = append(tweetEntities.Media, bridge.Media{ - ID: uint64(id), + ID: int64(id), IDStr: strconv.Itoa(id), MediaURL: configData.CdnURL + "/cdn/img/?url=" + url.QueryEscape("https://cdn.bsky.app/img/feed_thumbnail/plain/"+tweet.Author.DID+"/"+image.Image.Ref.Link+"/@jpeg"), }) @@ -261,7 +261,7 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs //ScreenName: "test", ScreenName: tweet.Record.Text[faucet.Index.ByteStart+1 : faucet.Index.ByteEnd], ID: bridge.BlueSkyToTwitterID(faucet.Features[0].Did), - IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(faucet.Features[0].Did), 10), + IDStr: strconv.FormatInt(*bridge.BlueSkyToTwitterID(faucet.Features[0].Did), 10), Indices: []int{ faucet.Index.ByteStart, faucet.Index.ByteEnd, @@ -326,62 +326,56 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs Entities: tweetEntities, Annotations: nil, // I am curious what annotations are Contributors: nil, - ID: func() uint64 { + ID: func() int64 { // we have to use psudo ids because of https://github.com/bluesky-social/atproto/issues/1811 if isRetweet { - return bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) + return *bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) } - return bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) + return *bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) }(), IDStr: func() string { if isRetweet { id := bridge.BskyMsgToTwitterID(tweet.URI, &postReason.IndexedAt, &postReason.By.DID) - return strconv.FormatUint(id, 10) + return strconv.FormatInt(*id, 10) } id := bridge.BskyMsgToTwitterID(tweet.URI, &tweet.Record.CreatedAt, nil) - return strconv.FormatUint(id, 10) + return strconv.FormatInt(*id, 10) }(), Geo: nil, Place: nil, PossiblySensitive: false, - InReplyToUserID: func() *uint64 { + InReplyToUserID: func() *int64 { if replyMsgBskyURI == "" { return nil } id := bridge.BlueSkyToTwitterID(replyUserBskyId) - if id == 0 { - return nil - } - return &id + return id }(), InReplyToUserIDStr: func() *string { if replyMsgBskyURI == "" { return nil } id := bridge.BlueSkyToTwitterID(replyUserBskyId) - if id == 0 { - return nil - } - idStr := strconv.FormatUint(id, 10) + idStr := strconv.FormatInt(*id, 10) return &idStr }(), InReplyToScreenName: &tweet.Author.DisplayName, User: *author, Source: "Bluesky", - InReplyToStatusID: func() *uint64 { + InReplyToStatusID: func() *int64 { if replyMsgBskyURI == "" { return nil } id := bridge.BskyMsgToTwitterID(replyMsgBskyURI, replyTimeStamp, nil) - return &id + return id }(), InReplyToStatusIDStr: func() *string { if replyMsgBskyURI == "" { return nil } id := bridge.BskyMsgToTwitterID(replyMsgBskyURI, replyTimeStamp, nil) - idStr := strconv.FormatUint(id, 10) + idStr := strconv.FormatInt(*id, 10) return &idStr }(), Retweeted: tweet.Viewer.Repost != nil && !isRetweet, @@ -407,8 +401,8 @@ func TranslatePostToTweet(tweet blueskyapi.Post, replyMsgBskyURI string, replyUs _, my_did, _ := blueskyapi.GetURIComponents(*tweet.Viewer.Repost) retweetId := bridge.BskyMsgToTwitterID(tweet.URI, &RepostRecord.Value.CreatedAt, &my_did) return &bridge.CurrentUserRetweet{ - ID: retweetId, - IDStr: strconv.FormatUint(retweetId, 10), + ID: *retweetId, + IDStr: strconv.FormatInt(*retweetId, 10), } } return nil @@ -440,8 +434,8 @@ func GetUserInfoFromTweetData(tweet blueskyapi.Post) bridge.TwitterUser { ContributorsEnabled: false, UtcOffset: nil, IsTranslator: false, - ID: bridge.BlueSkyToTwitterID(tweet.URI), - IDStr: strconv.FormatUint(bridge.BlueSkyToTwitterID(tweet.URI), 10), + ID: *bridge.BlueSkyToTwitterID(tweet.URI), + IDStr: strconv.FormatInt(*bridge.BlueSkyToTwitterID(tweet.URI), 10), ProfileUseBackgroundImage: false, ProfileTextColor: "333333", Protected: false, @@ -479,12 +473,12 @@ func TweetInfo(c *fiber.Ctx) error { } encodedId := c.Params("id") - idBigInt, err := strconv.ParseUint(encodedId, 10, 64) + idBigInt, err := strconv.ParseInt(encodedId, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } // Fetch ID - idPtr, _, _, err := bridge.TwitterMsgIdToBluesky(idBigInt) + idPtr, _, _, err := bridge.TwitterMsgIdToBluesky(&idBigInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid ID format") } @@ -508,18 +502,18 @@ func TweetInfo(c *fiber.Ctx) error { return err } - repliers := []uint64{} - favourites := []uint64{} - retweeters := []uint64{} + repliers := []int64{} + favourites := []int64{} + retweeters := []int64{} for _, reply := range *thread.Thread.Replies { - repliers = append(repliers, bridge.BlueSkyToTwitterID(reply.Post.Author.DID)) + repliers = append(repliers, *bridge.BlueSkyToTwitterID(reply.Post.Author.DID)) } for _, like := range likes.Likes { - favourites = append(favourites, bridge.BlueSkyToTwitterID(like.Actor.DID)) + favourites = append(favourites, *bridge.BlueSkyToTwitterID(like.Actor.DID)) } for _, reposter := range reposters.RepostedBy { - retweeters = append(retweeters, bridge.BlueSkyToTwitterID(reposter.DID)) + retweeters = append(retweeters, *bridge.BlueSkyToTwitterID(reposter.DID)) } return c.JSON(bridge.TwitterActivitiySummary{ diff --git a/twitterv1/user.go b/twitterv1/user.go index 9d5b4ad..c7866aa 100644 --- a/twitterv1/user.go +++ b/twitterv1/user.go @@ -16,11 +16,11 @@ func user_info(c *fiber.Ctx) error { if screen_name == "" { userIDStr := c.Query("user_id") - userID, err := strconv.ParseUint(userIDStr, 10, 64) + userID, err := strconv.ParseInt(userIDStr, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - screen_namePtr, err := bridge.TwitterIDToBlueSky(userID) // yup + screen_namePtr, err := bridge.TwitterIDToBlueSky(&userID) // yup if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -69,11 +69,11 @@ func UsersLookup(c *fiber.Ctx) error { } else if user_id != "" { userIDs := strings.Split(user_id, ",") for _, idStr := range userIDs { - userID, err := strconv.ParseUint(idStr, 10, 64) + userID, err := strconv.ParseInt(idStr, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - handle, err := bridge.TwitterIDToBlueSky(userID) + handle, err := bridge.TwitterIDToBlueSky(&userID) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -135,11 +135,11 @@ func UserRelationships(c *fiber.Ctx) error { } if isID { for i, actor := range actorsArray { - actorID, err := strconv.ParseUint(actor, 10, 64) + actorID, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - handlePtr, err := bridge.TwitterIDToBlueSky(actorID) + handlePtr, err := bridge.TwitterIDToBlueSky(&actorID) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -181,8 +181,8 @@ func UserRelationships(c *fiber.Ctx) error { return user.DisplayName }(), ScreenName: user.Handle, - ID: encodedUserId, - IDStr: strconv.FormatUint(encodedUserId, 10), + ID: *encodedUserId, + IDStr: strconv.FormatInt(*encodedUserId, 10), Connections: connections, }) } @@ -216,11 +216,11 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("No source_id provided") } } else { - sourceIDInt, err := strconv.ParseUint(sourceActor, 10, 64) + sourceIDInt, err := strconv.ParseInt(sourceActor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid source_id provided") } - sourceActorPtr, err := bridge.TwitterIDToBlueSky(sourceIDInt) + sourceActorPtr, err := bridge.TwitterIDToBlueSky(&sourceIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") } @@ -237,11 +237,11 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("No source_id provided") } } else { - targetIDInt, err := strconv.ParseUint(targetActor, 10, 64) + targetIDInt, err := strconv.ParseInt(targetActor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid source_id provided") } - targetActorPtr, err := bridge.TwitterIDToBlueSky(targetIDInt) + targetActorPtr, err := bridge.TwitterIDToBlueSky(&targetIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert source_id to screen_name") } @@ -270,11 +270,11 @@ func GetUsersRelationship(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("Failed to fetch source user") } - targetDID, err := bridge.TwitterIDToBlueSky(targetUser.ID) // not the most efficient way to do this, but it works + targetDID, err := bridge.TwitterIDToBlueSky(&targetUser.ID) // not the most efficient way to do this, but it works if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("Failed to convert target user ID to BlueSky ID") } - sourceDID, err := bridge.TwitterIDToBlueSky(sourceUser.ID) + sourceDID, err := bridge.TwitterIDToBlueSky(&sourceUser.ID) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("Failed to convert source user ID to BlueSky ID") } @@ -289,14 +289,14 @@ func GetUsersRelationship(c *fiber.Ctx) error { friendship := bridge.SourceTargetFriendship{ Target: bridge.UserFriendship{ ID: targetUser.ID, - IDStr: strconv.FormatUint(targetUser.ID, 10), + IDStr: strconv.FormatInt(targetUser.ID, 10), ScreenName: targetUser.ScreenName, Following: relationship.Relationships[0].FollowedBy != "", FollowedBy: relationship.Relationships[0].Following != "", }, Source: bridge.UserFriendship{ ID: sourceUser.ID, - IDStr: strconv.FormatUint(sourceUser.ID, 10), + IDStr: strconv.FormatInt(sourceUser.ID, 10), ScreenName: sourceUser.ScreenName, Following: relationship.Relationships[0].Following != "", FollowedBy: relationship.Relationships[0].FollowedBy != "", @@ -328,11 +328,11 @@ func FollowUser(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, err := strconv.ParseUint(actor, 10, 64) + id, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(id) + actorPtr, err := bridge.TwitterIDToBlueSky(&id) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -407,11 +407,11 @@ func UnfollowUserForm(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, err := strconv.ParseUint(actor, 10, 64) + id, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(id) + actorPtr, err := bridge.TwitterIDToBlueSky(&id) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -426,11 +426,11 @@ func UnfollowUserForm(c *fiber.Ctx) error { func UnfollowUserParams(c *fiber.Ctx) error { // This should allow lookup with a handle, but tbh, i'm too lazy to implement that right now as i do not see it being used. actor := c.Params("id") - actorID, err := strconv.ParseUint(actor, 10, 64) + actorID, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(actorID) + actorPtr, err := bridge.TwitterIDToBlueSky(&actorID) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -460,11 +460,11 @@ func GetFollowers(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, err := strconv.ParseUint(actor, 10, 64) + id, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(id) + actorPtr, err := bridge.TwitterIDToBlueSky(&id) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -530,11 +530,11 @@ func GetFollows(c *fiber.Ctx) error { c.Status(fiber.StatusBadRequest).SendString("No user provided") } } else { - id, err := strconv.ParseUint(actor, 10, 64) + id, err := strconv.ParseInt(actor, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - actorPtr, err := bridge.TwitterIDToBlueSky(id) + actorPtr, err := bridge.TwitterIDToBlueSky(&id) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") } @@ -605,11 +605,11 @@ func GetSuggestedUsers(c *fiber.Ctx) error { // see if they provided a user id userID := c.Query("user_id") if userID != "" { - userIDInt, err := strconv.ParseUint(userID, 10, 64) + userIDInt, err := strconv.ParseInt(userID, 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Invalid user_id provided") } - userIDPtr, err := bridge.TwitterIDToBlueSky(userIDInt) + userIDPtr, err := bridge.TwitterIDToBlueSky(&userIDInt) if err != nil { return c.Status(fiber.StatusBadRequest).SendString("Failed to convert user_id to screen_name") }