Skip to content

Commit 2135bc2

Browse files
committed
fix: pagination on GetMyActivity
1 parent c428d13 commit 2135bc2

3 files changed

Lines changed: 122 additions & 63 deletions

File tree

bluesky/blueskyapi.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ func SendRequest(token *string, method string, url string, body io.Reader) (*htt
342342
req.Header.Set("Authorization", "Bearer "+*token)
343343
}
344344
req.Header.Set("Content-Type", "application/json") // 99% sure all bluesky requests are json.
345+
req.Header.Set("UserAgent", "ATwitterAPIBridge/1.0")
345346

346347
resp, err := client.Do(req)
347348
if err != nil {

bridge/bridge.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,16 @@ type UserRelationships struct {
262262
}
263263

264264
// Currently known how it forms follows, but we are missing favourites etc
265+
// Some details can be found here:
266+
// https://mgng.mugbum.info/Archive/View/year/2011/month/12
265267
type MyActivity struct {
266268
Action string `json:"action" xml:"action"`
267269
CreatedAt string `json:"created_at" xml:"created_at"`
268-
ID int64 `json:"id" xml:"id"`
270+
MaxPosition int64 `json:"max_position" xml:"max_position"`
271+
MinPosition int64 `json:"min_position" xml:"min_position"`
269272
Sources []TwitterUser `json:"sources"`
270-
Targets []Tweet `json:"targets,omitempty" xml:"targets,omitempty"`
271-
TargetObjects []Tweet `json:"target_objects,omitempty" xml:"target_objects,omitempty"`
273+
Targets []Tweet `json:"targets" xml:"targets"`
274+
TargetObjects []Tweet `json:"target_objects" xml:"target_objects"`
272275
}
273276

274277
// https://web.archive.org/web/20120516154953/https://dev.twitter.com/docs/api/1/get/friendships/show

twitterv1/connect.go

Lines changed: 115 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package twitterv1
22

33
import (
44
"fmt"
5+
"strconv"
6+
"time"
57

68
blueskyapi "github.com/Preloading/MastodonTwitterAPI/bluesky"
79
"github.com/Preloading/MastodonTwitterAPI/bridge"
@@ -46,12 +48,36 @@ func UserSearch(c *fiber.Ctx) error {
4648
func GetMyActivity(c *fiber.Ctx) error {
4749
// Thank you so much @Savefade for what this returns for follows.
4850
// This function could probably optimized to use less GetUsers calls, but whatever.
49-
_, pds, _, oauthToken, err := GetAuthFromReq(c)
51+
my_did, pds, _, oauthToken, err := GetAuthFromReq(c)
5052
if err != nil {
5153
return c.Status(fiber.StatusUnauthorized).SendString("OAuth token not found in Authorization header")
5254
}
5355

54-
bskyNotifcations, err := blueskyapi.GetNotifications(*pds, *oauthToken, 50, "")
56+
// Context is key, or so i've heard.
57+
context := ""
58+
maxID := c.Query("max_id")
59+
if maxID != "" {
60+
maxIDInt, err := strconv.ParseInt(maxID, 10, 64)
61+
if err != nil {
62+
return c.Status(fiber.StatusBadRequest).SendString("Invalid max_id")
63+
}
64+
// I've had some problems with it giving the same result twice, so I'm going to subtract 2ms to the max_id
65+
maxIDInt -= 2
66+
max_time := time.UnixMilli(maxIDInt)
67+
context = max_time.Format(time.RFC3339)
68+
}
69+
70+
// count
71+
countStr := c.Query("count")
72+
count := 50
73+
if countStr != "" {
74+
countInt, err := strconv.Atoi(countStr)
75+
if err == nil {
76+
count = countInt
77+
}
78+
}
79+
80+
bskyNotifcations, err := blueskyapi.GetNotifications(*pds, *oauthToken, count, context)
5581

5682
if err != nil {
5783
fmt.Println("Error:", err)
@@ -75,6 +101,10 @@ func GetMyActivity(c *fiber.Ctx) error {
75101
break
76102
}
77103
}
104+
if position+1 < len(bskyNotifcations.Notifications) {
105+
position--
106+
}
107+
78108
users, err := blueskyapi.GetUsersInfo(*pds, *oauthToken, usersInBlock, false)
79109
if err != nil {
80110
fmt.Println("Error:", err)
@@ -87,23 +117,27 @@ func GetMyActivity(c *fiber.Ctx) error {
87117
}
88118

89119
twitterNotifications = append(twitterNotifications, bridge.MyActivity{
90-
Action: "follow",
91-
CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
92-
ID: *bridge.BlueSkyToTwitterID(notification.URI),
93-
Sources: sources,
120+
Action: "follow",
121+
CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
122+
MinPosition: notification.IndexedAt.UnixMilli(),
123+
MaxPosition: bskyNotifcations.Notifications[position].IndexedAt.UnixMilli(), // I don't believe that these IDs are used for anything besides pagination & positioning
124+
Sources: sources,
94125
})
126+
position++
95127
case "like":
96128
usersInBlock := []string{notification.Author.DID}
97-
notificationId := notification.URI
98129
for position+1 < len(bskyNotifcations.Notifications) {
99130
position++
100131
if bskyNotifcations.Notifications[position].Reason == "like" && bskyNotifcations.Notifications[position].ReasonSubject == notification.ReasonSubject {
101132
usersInBlock = append(usersInBlock, bskyNotifcations.Notifications[position].Author.DID)
102-
notificationId = bskyNotifcations.Notifications[position].URI
103133
} else {
104134
break
105135
}
106136
}
137+
if position+1 < len(bskyNotifcations.Notifications) {
138+
position--
139+
}
140+
107141
// slight optimization in network traffic, saves us from having to call seperately for the poster
108142
_, poster_did, _ := blueskyapi.GetURIComponents(notification.ReasonSubject)
109143
usersInBlock = append(usersInBlock, poster_did)
@@ -112,62 +146,24 @@ func GetMyActivity(c *fiber.Ctx) error {
112146
fmt.Println("Error:", err)
113147
return c.Status(fiber.StatusInternalServerError).SendString("Failed to get user info")
114148
}
115-
users = users[:len(users)-1] // remove the last user, as it's the poster, and we don't need it here
116149

117-
err, likedPost := blueskyapi.GetPost(*pds, *oauthToken, notification.ReasonSubject, 0, 1)
118-
if err != nil {
119-
fmt.Println("Error:", err)
120-
return c.Status(fiber.StatusInternalServerError).SendString("Failed to get post")
121-
}
122-
123-
likedTweet := TranslatePostToTweet(likedPost.Thread.Post, "", "", nil, nil, *oauthToken, *pds)
124-
if likedPost.Thread.Parent != nil {
125-
likedTweet = TranslatePostToTweet(likedPost.Thread.Post, likedPost.Thread.Parent.Post.URI, likedPost.Thread.Parent.Post.Author.DID, &likedPost.Thread.Parent.Post.IndexedAt, nil, *oauthToken, *pds)
126-
}
127-
128-
var sources []bridge.TwitterUser
129-
for _, user := range users {
130-
sources = append(sources, *user) // pain#
131-
}
132-
133-
twitterNotifications = append(twitterNotifications, bridge.MyActivity{
134-
Action: "favorite",
135-
CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
136-
ID: *bridge.BlueSkyToTwitterID(notificationId),
137-
Sources: sources,
138-
Targets: []bridge.Tweet{likedTweet},
139-
})
140-
case "repost":
141-
usersInBlock := []string{notification.Author.DID}
142-
notificationId := notification.URI // We get the last URI so if we get a new repost ontop of this, it adds to to the prev, not a new one
143-
for position+1 < len(bskyNotifcations.Notifications)+1 {
144-
position++
145-
if bskyNotifcations.Notifications[position].Reason == "repost" && bskyNotifcations.Notifications[position].ReasonSubject == notification.ReasonSubject {
146-
usersInBlock = append(usersInBlock, bskyNotifcations.Notifications[position].Author.DID)
147-
notificationId = bskyNotifcations.Notifications[position].URI
148-
} else {
150+
// Remove the current user from the list if present (only once)
151+
for i, user := range users {
152+
if user.ID == *bridge.BlueSkyToTwitterID(*my_did) {
153+
users = append(users[:i], users[i+1:]...)
149154
break
150155
}
151156
}
152-
// slight optimization in network traffic, saves us from having to call seperately for the poster
153-
_, poster_did, _ := blueskyapi.GetURIComponents(notification.ReasonSubject)
154-
usersInBlock = append(usersInBlock, poster_did)
155-
users, err := blueskyapi.GetUsersInfo(*pds, *oauthToken, usersInBlock, false)
156-
if err != nil {
157-
fmt.Println("Error:", err)
158-
return c.Status(fiber.StatusInternalServerError).SendString("Failed to get user info")
159-
}
160-
users = users[:len(users)-1] // remove the last user, as it's the poster, and we don't need it here
161157

162-
err, repostedPost := blueskyapi.GetPost(*pds, *oauthToken, notification.ReasonSubject, 0, 1)
158+
err, likedPost := blueskyapi.GetPost(*pds, *oauthToken, notification.ReasonSubject, 0, 1)
163159
if err != nil {
164160
fmt.Println("Error:", err)
165161
return c.Status(fiber.StatusInternalServerError).SendString("Failed to get post")
166162
}
167163

168-
retweetedTweet := TranslatePostToTweet(repostedPost.Thread.Post, "", "", nil, nil, *oauthToken, *pds)
169-
if repostedPost.Thread.Parent != nil {
170-
retweetedTweet = TranslatePostToTweet(repostedPost.Thread.Post, repostedPost.Thread.Parent.Post.URI, repostedPost.Thread.Parent.Post.Author.DID, &repostedPost.Thread.Parent.Post.IndexedAt, nil, *oauthToken, *pds)
164+
likedTweet := TranslatePostToTweet(likedPost.Thread.Post, "", "", nil, nil, *oauthToken, *pds)
165+
if likedPost.Thread.Parent != nil {
166+
likedTweet = TranslatePostToTweet(likedPost.Thread.Post, likedPost.Thread.Parent.Post.URI, likedPost.Thread.Parent.Post.Author.DID, &likedPost.Thread.Parent.Post.IndexedAt, nil, *oauthToken, *pds)
171167
}
172168

173169
var sources []bridge.TwitterUser
@@ -176,14 +172,73 @@ func GetMyActivity(c *fiber.Ctx) error {
176172
}
177173

178174
twitterNotifications = append(twitterNotifications, bridge.MyActivity{
179-
Action: "retweet",
180-
CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
181-
ID: *bridge.BlueSkyToTwitterID(notificationId),
182-
Sources: sources,
183-
TargetObjects: []bridge.Tweet{retweetedTweet},
175+
Action: "favorite",
176+
CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
177+
MinPosition: notification.IndexedAt.UnixMilli(),
178+
MaxPosition: bskyNotifcations.Notifications[position].IndexedAt.UnixMilli(), // I don't believe that these IDs are used for anything besides pagination & positioning
179+
Sources: sources,
180+
Targets: []bridge.Tweet{likedTweet},
184181
})
182+
position++
183+
// case "repost":
184+
// fmt.Println("Repost")
185+
// usersInBlock := []string{notification.Author.DID}
186+
// for position+1 < len(bskyNotifcations.Notifications)+1 {
187+
// position++
188+
// if bskyNotifcations.Notifications[position].Reason == "repost" && bskyNotifcations.Notifications[position].ReasonSubject == notification.ReasonSubject {
189+
// usersInBlock = append(usersInBlock, bskyNotifcations.Notifications[position].Author.DID)
190+
// } else {
191+
// break
192+
// }
193+
// }
194+
// if position+1 < len(bskyNotifcations.Notifications) {
195+
// position--
196+
// }
197+
198+
// // slight optimization in network traffic, saves us from having to call seperately for the poster
199+
// _, poster_did, _ := blueskyapi.GetURIComponents(notification.ReasonSubject)
200+
// usersInBlock = append(usersInBlock, poster_did)
201+
// users, err := blueskyapi.GetUsersInfo(*pds, *oauthToken, usersInBlock, false)
202+
// if err != nil {
203+
// fmt.Println("Error:", err)
204+
// return c.Status(fiber.StatusInternalServerError).SendString("Failed to get user info")
205+
// }
206+
207+
// // Remove the current user from the list if present (only once)
208+
// for i, user := range users {
209+
// if user.ID == *bridge.BlueSkyToTwitterID(*my_did) {
210+
// users = append(users[:i], users[i+1:]...)
211+
// break
212+
// }
213+
// }
214+
215+
// err, repostedPost := blueskyapi.GetPost(*pds, *oauthToken, notification.ReasonSubject, 0, 1)
216+
// if err != nil {
217+
// fmt.Println("Error:", err)
218+
// return c.Status(fiber.StatusInternalServerError).SendString("Failed to get post")
219+
// }
220+
221+
// retweetedTweet := TranslatePostToTweet(repostedPost.Thread.Post, "", "", nil, nil, *oauthToken, *pds)
222+
// if repostedPost.Thread.Parent != nil {
223+
// retweetedTweet = TranslatePostToTweet(repostedPost.Thread.Post, repostedPost.Thread.Parent.Post.URI, repostedPost.Thread.Parent.Post.Author.DID, &repostedPost.Thread.Parent.Post.IndexedAt, nil, *oauthToken, *pds)
224+
// }
225+
226+
// var sources []bridge.TwitterUser
227+
// for _, user := range users {
228+
// sources = append(sources, *user) // pain#
229+
// }
230+
231+
// twitterNotifications = append(twitterNotifications, bridge.MyActivity{
232+
// Action: "retweet",
233+
// CreatedAt: bridge.TwitterTimeConverter(notification.IndexedAt),
234+
// MinPosition: notification.IndexedAt.UnixMilli(),
235+
// MaxPosition: bskyNotifcations.Notifications[position].IndexedAt.UnixMilli(), // I don't believe that these IDs are used for anything besides pagination & positioning
236+
// Sources: sources,
237+
// TargetObjects: []bridge.Tweet{retweetedTweet},
238+
// })
239+
// position++
185240
default:
186-
//fmt.Println("Unknown notification type:", notification.Reason)
241+
fmt.Println("Unknown notification type:", notification.Reason)
187242
}
188243

189244
position++ // Increment position

0 commit comments

Comments
 (0)