@@ -7,8 +7,16 @@ const BEARER_TOKEN =
77const HOME_TIMELINE_QUERY_ID = 'c-CzHF1LboFilMpsx4ZCrQ' ;
88const HOME_LATEST_TIMELINE_QUERY_ID = 'BKB7oi212Fi7kQtCBGE4zA' ;
99
10+ type TimelineType = 'for-you' | 'following' ;
11+
12+ interface TimelineEndpointConfig {
13+ endpoint : string ;
14+ method : 'GET' | 'POST' ;
15+ fallbackQueryId : string ;
16+ }
17+
1018// Endpoint config: for-you uses GET HomeTimeline, following uses POST HomeLatestTimeline
11- const TIMELINE_ENDPOINTS : Record < string , { endpoint : string ; method : string ; fallbackQueryId : string } > = {
19+ const TIMELINE_ENDPOINTS : Record < TimelineType , TimelineEndpointConfig > = {
1220 'for-you' : { endpoint : 'HomeTimeline' , method : 'GET' , fallbackQueryId : HOME_TIMELINE_QUERY_ID } ,
1321 following : { endpoint : 'HomeLatestTimeline' , method : 'POST' , fallbackQueryId : HOME_LATEST_TIMELINE_QUERY_ID } ,
1422} ;
@@ -61,15 +69,20 @@ interface TimelineTweet {
6169 url : string ;
6270}
6371
64- function buildHomeTimelineUrl ( queryId : string , endpoint : string , count : number , cursor ?: string | null ) : string {
65- const vars : Record < string , any > = {
72+ function buildTimelineVariables ( type : TimelineType , count : number , cursor ?: string | null ) : Record < string , unknown > {
73+ const vars : Record < string , unknown > = {
6674 count,
6775 includePromotedContent : false ,
6876 latestControlAvailable : true ,
6977 requestContext : 'launch' ,
70- withCommunity : true ,
7178 } ;
79+ if ( type === 'for-you' ) vars . withCommunity = true ;
80+ if ( type === 'following' ) vars . seenTweetIds = [ ] ;
7281 if ( cursor ) vars . cursor = cursor ;
82+ return vars ;
83+ }
84+
85+ function buildHomeTimelineUrl ( queryId : string , endpoint : string , vars : Record < string , unknown > ) : string {
7386
7487 return (
7588 `/i/api/graphql/${ queryId } /${ endpoint } ` +
@@ -170,7 +183,8 @@ cli({
170183 columns : [ 'id' , 'author' , 'text' , 'likes' , 'retweets' , 'replies' , 'views' , 'created_at' , 'url' ] ,
171184 func : async ( page , kwargs ) => {
172185 const limit = kwargs . limit || 20 ;
173- const { endpoint, method, fallbackQueryId } = TIMELINE_ENDPOINTS [ kwargs . type || 'for-you' ] ;
186+ const timelineType : TimelineType = kwargs . type === 'following' ? 'following' : 'for-you' ;
187+ const { endpoint, method, fallbackQueryId } = TIMELINE_ENDPOINTS [ timelineType ] ;
174188
175189 // Navigate to x.com for cookie context
176190 await page . goto ( 'https://x.com' ) ;
@@ -212,7 +226,8 @@ cli({
212226
213227 for ( let i = 0 ; i < 5 && allTweets . length < limit ; i ++ ) {
214228 const fetchCount = Math . min ( 40 , limit - allTweets . length + 5 ) ; // over-fetch slightly for promoted filtering
215- const apiUrl = buildHomeTimelineUrl ( queryId , endpoint , fetchCount , cursor ) ;
229+ const variables = buildTimelineVariables ( timelineType , fetchCount , cursor ) ;
230+ const apiUrl = buildHomeTimelineUrl ( queryId , endpoint , variables ) ;
216231
217232 const data = await page . evaluate ( `async () => {
218233 const r = await fetch("${ apiUrl } ", { method: "${ method } ", headers: ${ headers } , credentials: 'include' });
@@ -235,3 +250,9 @@ cli({
235250 return allTweets . slice ( 0 , limit ) ;
236251 } ,
237252} ) ;
253+
254+ export const __test__ = {
255+ buildTimelineVariables,
256+ buildHomeTimelineUrl,
257+ parseHomeTimeline,
258+ } ;
0 commit comments