diff --git a/src/fetch.ts b/src/fetch.ts index a36313a..f73dfa8 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,4 +1,4 @@ -import { Constants } from '../constants'; +import { Constants } from './constants'; export const fetchUsingGuest = async (status: string): Promise => { const csrfToken = crypto.randomUUID().replace(/-/g, ''); // Generate a random CSRF token, this doesn't matter, Twitter just cares that header and cookie match @@ -8,20 +8,35 @@ export const fetchUsingGuest = async (status: string): Promise => ...Constants.BASE_HEADERS, }; + /* If all goes according to plan, we have a guest token we can use to call API + AFAIK there is no limit to how many guest tokens you can request. + + This can effectively mean virtually unlimited (read) access to Twitter' API, + which is very funny. */ const activate = await fetch(`${Constants.TWITTER_API_ROOT}/1.1/guest/activate.json`, { method: 'POST', headers: headers, body: '', }); + /* Let's grab that guest_token so we can use it */ const activateJson = (await activate.json()) as { guest_token: string }; const guestToken = activateJson.guest_token; - headers['Cookie'] = `guest_id=v1%3A${guestToken}; ct0=${csrfToken};`; + /* Just some cookies to mimick what the Twitter Web App would send */ + headers['Cookie'] = [ + `guest_id_ads=v1%3A${guestToken}`, + `guest_id_marketing=v1%3A${guestToken}`, + `guest_id=v1%3A${guestToken}`, + `ct0=${csrfToken};` + ].join('; '); + headers['x-csrf-token'] = csrfToken; headers['x-twitter-active-user'] = 'yes'; headers['x-guest-token'] = guestToken; + /* We pretend to be the Twitter Web App as closely as possible, + so we use twitter.com/i/api/2 instead of api.twitter.com/2 */ const conversation = (await ( await fetch( `${Constants.TWITTER_ROOT}/i/api/2/timeline/conversation/${status}.json?${Constants.GUEST_FETCH_PARAMETERS}`, @@ -33,10 +48,9 @@ export const fetchUsingGuest = async (status: string): Promise => ).json()) as TimelineBlobPartial; const tweet = conversation?.globalObjects?.tweets?.[status] || {}; - /* - With v2 conversation API we re-add the user object ot the tweet because - Twitter stores it separately in the conversation API. - */ + /* With v2 conversation API we re-add the user object ot the tweet because + Twitter stores it separately in the conversation API. This is to consolidate + it in case a user appears multiple times in a thread. */ tweet.user = conversation?.globalObjects?.users?.[tweet.user_id_str] || {}; return tweet;