Add parsing of Likes feed

This commit is contained in:
Alessio 2023-06-26 12:16:38 -03:00
parent 3d7166c4aa
commit 79a4b87f3a
4 changed files with 70 additions and 0 deletions

View File

@ -506,6 +506,11 @@ type APIV2Response struct {
Data struct {
User struct {
Result struct {
TimelineV2 struct { // "Likes" feed calls this "timeline_v2" for some reason
Timeline struct {
Instructions []APIV2Instruction `json:"instructions"`
} `json:"timeline"`
} `json:"timeline_v2"`
Timeline struct {
Timeline struct {
Instructions []APIV2Instruction `json:"instructions"`
@ -526,6 +531,12 @@ func (api_response APIV2Response) GetMainInstruction() *APIV2Instruction {
return &instructions[i]
}
}
instructions = api_response.Data.User.Result.TimelineV2.Timeline.Instructions
for i := range instructions {
if instructions[i].Type == "TimelineAddEntries" {
return &instructions[i]
}
}
instructions = api_response.Data.ThreadedConversationWithInjectionsV2.Instructions
for i := range instructions {
if instructions[i].Type == "TimelineAddEntries" {
@ -677,6 +688,39 @@ func (api_response APIV2Response) ToTweetTrove() (TweetTrove, error) {
return ret, nil // TODO: This doesn't need to return an error, it's always nil
}
func (r APIV2Response) ToTweetTroveAsLikes() (TweetTrove, error) {
ret, err := r.ToTweetTrove()
if err != nil {
return ret, err
}
// Post-process tweets as Likes
for _, entry := range r.GetMainInstruction().Entries {
// Skip cursors
if entry.Content.EntryType == "TimelineTimelineCursor" {
continue
}
// Assume it's not a TimelineModule or a Tombstone
if entry.Content.EntryType != "TimelineTimelineItem" {
panic(fmt.Sprintf("Unknown Like entry type: %s", entry.Content.EntryType))
}
if entry.Content.ItemContent.ItemType == "TimelineTombstone" {
panic(fmt.Sprintf("Liked tweet is a tombstone: %#v", entry))
}
// Generate a "Like" from the entry
tweet, is_ok := ret.Tweets[TweetID(entry.Content.ItemContent.TweetResults.Result._Result.ID)]
if !is_ok {
panic(entry)
}
ret.Likes[LikeSortID(entry.SortIndex)] = Like{
SortID: LikeSortID(entry.SortIndex),
TweetID: tweet.ID,
}
}
return ret, err
}
// Get a User feed using the new GraphQL twitter api
func (api *API) GetGraphqlFeedFor(user_id UserID, cursor string) (APIV2Response, error) {
url, err := url.Parse(GraphqlURL{

View File

@ -866,3 +866,23 @@ func TestTweetDetailWithUnjoinedNontombstoneTweet(t *testing.T) {
assert.False(t3.IsStub)
assert.Equal(t2.ID, t3.InReplyToID)
}
func TestParseResultAsLikes(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
data, err := os.ReadFile("test_responses/api_v2/likes_feed.json")
require.NoError(err)
var response_result APIV2Response
err = json.Unmarshal(data, &response_result)
require.NoError(err)
trove, err := response_result.ToTweetTroveAsLikes()
require.NoError(err)
assert.Len(trove.Retweets, 0)
assert.True(len(trove.Likes) == 20)
for _, l := range trove.Likes {
_, is_ok := trove.Tweets[l.TweetID]
assert.True(is_ok, "Like (%#v) didn't have its Tweet in the trove", l)
}
}

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,7 @@ type TweetTrove struct {
Users map[UserID]User
Retweets map[TweetID]Retweet
Spaces map[SpaceID]Space
Likes map[LikeSortID]Like
TombstoneUsers []UserHandle
}
@ -22,6 +23,7 @@ func NewTweetTrove() TweetTrove {
ret.Users = make(map[UserID]User)
ret.Retweets = make(map[TweetID]Retweet)
ret.Spaces = make(map[SpaceID]Space)
ret.Likes = make(map[LikeSortID]Like)
ret.TombstoneUsers = []UserHandle{}
return ret
}
@ -54,6 +56,9 @@ func (t1 *TweetTrove) MergeWith(t2 TweetTrove) {
for id, val := range t2.Spaces {
t1.Spaces[id] = val
}
for id, val := range t2.Likes {
t1.Likes[id] = val
}
t1.TombstoneUsers = append(t1.TombstoneUsers, t2.TombstoneUsers...)
}