Add parsing of HomeTimeline (not For You)
This commit is contained in:
parent
72110e6558
commit
4c91977e38
@ -428,10 +428,12 @@ func (e APIV2Entry) ToTweetTrove() TweetTrove {
|
|||||||
ret := NewTweetTrove()
|
ret := NewTweetTrove()
|
||||||
|
|
||||||
parts := strings.Split(e.EntryID, "-")
|
parts := strings.Split(e.EntryID, "-")
|
||||||
if parts[0] == "homeConversation" || parts[0] == "conversationthread" || strings.Join(parts[0:2], "-") == "profile-conversation" {
|
if parts[0] == "homeConversation" || parts[0] == "conversationthread" ||
|
||||||
|
strings.Join(parts[0:2], "-") == "profile-conversation" || strings.Join(parts[0:2], "-") == "home-conversation" {
|
||||||
// Process it.
|
// Process it.
|
||||||
// - "profile-conversation": conversation thread on a user feed
|
// - "profile-conversation": conversation thread on a user feed
|
||||||
// - "homeConversation": This looks like it got changed to "profile-conversation"
|
// - "homeConversation": This looks like it got changed to "profile-conversation"
|
||||||
|
// - "home-conversation": probably same as above lol-- someone did some refactoring
|
||||||
// - "conversationthread": conversation thread in the replies under a TweetDetail view
|
// - "conversationthread": conversation thread in the replies under a TweetDetail view
|
||||||
for _, item := range e.Content.Items {
|
for _, item := range e.Content.Items {
|
||||||
if item.Item.ItemContent.ItemType == "TimelineTimelineCursor" {
|
if item.Item.ItemContent.ItemType == "TimelineTimelineCursor" {
|
||||||
@ -504,6 +506,11 @@ type APIV2Instruction struct {
|
|||||||
|
|
||||||
type APIV2Response struct {
|
type APIV2Response struct {
|
||||||
Data struct {
|
Data struct {
|
||||||
|
Home struct {
|
||||||
|
HomeTimelineUrt struct {
|
||||||
|
Instructions []APIV2Instruction `json:"instructions"`
|
||||||
|
} `json:"home_timeline_urt"`
|
||||||
|
} `json:"home"`
|
||||||
User struct {
|
User struct {
|
||||||
Result struct {
|
Result struct {
|
||||||
TimelineV2 struct { // "Likes" feed calls this "timeline_v2" for some reason
|
TimelineV2 struct { // "Likes" feed calls this "timeline_v2" for some reason
|
||||||
@ -543,6 +550,12 @@ func (api_response APIV2Response) GetMainInstruction() *APIV2Instruction {
|
|||||||
return &instructions[i]
|
return &instructions[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
instructions = api_response.Data.Home.HomeTimelineUrt.Instructions
|
||||||
|
for i := range instructions {
|
||||||
|
if instructions[i].Type == "TimelineAddEntries" {
|
||||||
|
return &instructions[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
panic("No 'TimelineAddEntries' found")
|
panic("No 'TimelineAddEntries' found")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,3 +937,60 @@ func (api API) GetUserLikes(user_id UserID, cursor string) (TweetTrove, error) {
|
|||||||
func GetUserLikes(user_id UserID, cursor string) (TweetTrove, error) {
|
func GetUserLikes(user_id UserID, cursor string) (TweetTrove, error) {
|
||||||
return the_api.GetUserLikes(user_id, cursor)
|
return the_api.GetUserLikes(user_id, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api API) GetHomeTimeline(cursor string) (TweetTrove, error) {
|
||||||
|
url := "https://twitter.com/i/api/graphql/W4Tpu1uueTGK53paUgxF0Q/HomeTimeline"
|
||||||
|
body_struct := struct {
|
||||||
|
Variables GraphqlVariables `json:"variables"`
|
||||||
|
Features GraphqlFeatures `json:"features"`
|
||||||
|
QueryID string `json:"queryId"`
|
||||||
|
}{
|
||||||
|
Variables: GraphqlVariables{
|
||||||
|
Count: 40,
|
||||||
|
Cursor: cursor,
|
||||||
|
IncludePromotedContent: false,
|
||||||
|
// LatestControlAvailable: true, // TODO: new field?
|
||||||
|
WithCommunity: true,
|
||||||
|
// SeenTweetIDs: []string{"...some TweetIDs"}? // TODO: new field?
|
||||||
|
},
|
||||||
|
Features: GraphqlFeatures{
|
||||||
|
RWebListsTimelineRedesignEnabled: true,
|
||||||
|
ResponsiveWebGraphqlExcludeDirectiveEnabled: true,
|
||||||
|
VerifiedPhoneLabelEnabled: false,
|
||||||
|
CreatorSubscriptionsTweetPreviewApiEnabled: true,
|
||||||
|
ResponsiveWebGraphqlTimelineNavigationEnabled: true,
|
||||||
|
ResponsiveWebGraphqlSkipUserProfileImageExtensionsEnabled: false,
|
||||||
|
TweetypieUnmentionOptimizationEnabled: true,
|
||||||
|
ResponsiveWebEditTweetApiEnabled: true,
|
||||||
|
GraphqlIsTranslatableRWebTweetIsTranslatableEnabled: true,
|
||||||
|
ViewCountsEverywhereApiEnabled: true,
|
||||||
|
LongformNotetweetsConsumptionEnabled: true,
|
||||||
|
TweetAwardsWebTippingEnabled: false,
|
||||||
|
FreedomOfSpeechNotReachFetchEnabled: false,
|
||||||
|
StandardizedNudgesMisinfo: true,
|
||||||
|
TweetWithVisibilityResultsPreferGqlLimitedActionsPolicyEnabled: true,
|
||||||
|
LongformNotetweetsRichTextReadEnabled: true,
|
||||||
|
LongformNotetweetsInlineMediaEnabled: true,
|
||||||
|
ResponsiveWebEnhanceCardsEnabled: false,
|
||||||
|
},
|
||||||
|
QueryID: "W4Tpu1uueTGK53paUgxF0Q",
|
||||||
|
}
|
||||||
|
var response APIV2Response
|
||||||
|
body_bytes, err := json.Marshal(body_struct)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = api.do_http_POST(url, string(body_bytes), &response)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
trove, err := response.ToTweetTrove()
|
||||||
|
if err != nil {
|
||||||
|
return TweetTrove{}, err
|
||||||
|
}
|
||||||
|
return trove, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHomeTimeline(cursor string) (TweetTrove, error) {
|
||||||
|
return the_api.GetHomeTimeline(cursor)
|
||||||
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package scraper_test
|
package scraper_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -867,6 +868,21 @@ func TestTweetDetailWithUnjoinedNontombstoneTweet(t *testing.T) {
|
|||||||
assert.Equal(t2.ID, t3.InReplyToID)
|
assert.Equal(t2.ID, t3.InReplyToID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseHomeTimeline(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
data, err := os.ReadFile("test_responses/api_v2/home_timeline.json")
|
||||||
|
require.NoError(err)
|
||||||
|
var response_result APIV2Response
|
||||||
|
err = json.Unmarshal(data, &response_result)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
trove, err := response_result.ToTweetTrove()
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
require.Len(trove.Tweets, 13)
|
||||||
|
require.Len(trove.Users, 11)
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseResultAsLikes(t *testing.T) {
|
func TestParseResultAsLikes(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
1
pkg/scraper/test_responses/api_v2/home_timeline.json
Normal file
1
pkg/scraper/test_responses/api_v2/home_timeline.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user