diff --git a/.env.example b/.env.example index fb2d5f8..e0f9351 100644 --- a/.env.example +++ b/.env.example @@ -12,4 +12,5 @@ EMBED_URL = "https://github.com/FixTweet/FixTweet" SENTRY_DSN = "" SENTRY_AUTH_TOKEN = "" SENTRY_ORG = "" -SENTRY_PROJECT = "" \ No newline at end of file +SENTRY_PROJECT = "" +API_FALLBACK_DOMAIN = "" \ No newline at end of file diff --git a/src/api/status.ts b/src/api/status.ts index 71a3f93..32a56fb 100644 --- a/src/api/status.ts +++ b/src/api/status.ts @@ -154,21 +154,35 @@ export const statusAPI = async ( /* We've got timeline instructions, so the Tweet is probably private */ if (conversation.timeline?.instructions?.length > 0) { - return { code: 401, message: 'PRIVATE_TWEET' }; - } + // Try request again with fallback this time + console.log('No Tweet was found, trying fallback in case of geo-restriction'); + const conversationFallback = await fetchConversation(status, event, true); + tweet = conversationFallback?.globalObjects?.tweets?.[status] || {}; - /* {"errors":[{"code":34,"message":"Sorry, that page does not exist."}]} */ - if (conversation.errors?.[0]?.code === 34) { - return { code: 404, message: 'NOT_FOUND' }; - } + if (typeof tweet.full_text !== 'undefined') { + console.log('Successfully loaded Tweet, requiring fallback'); + } - /* Tweets object is completely missing, smells like API failure */ - if (typeof conversation?.globalObjects?.tweets === 'undefined') { + if ( + typeof tweet.full_text === 'undefined' && + conversation.timeline?.instructions?.length > 0 + ) { + return { code: 401, message: 'PRIVATE_TWEET' }; + } + } else { + /* {"errors":[{"code":34,"message":"Sorry, that page does not exist."}]} */ + if (conversation.errors?.[0]?.code === 34) { + return { code: 404, message: 'NOT_FOUND' }; + } + + /* Tweets object is completely missing, smells like API failure */ + if (typeof conversation?.globalObjects?.tweets === 'undefined') { + return { code: 500, message: 'API_FAIL' }; + } + + /* If we have no idea what happened then just return API error */ return { code: 500, message: 'API_FAIL' }; } - - /* If we have no idea what happened then just return API error */ - return { code: 500, message: 'API_FAIL' }; } /* Creating the response objects */ diff --git a/src/constants.ts b/src/constants.ts index 306be9d..5e89eb3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,6 +15,7 @@ export const Constants = { API_DOCS_URL: `https://github.com/dangeredwolf/FixTweet/wiki/API-Home`, TWITTER_ROOT: 'https://twitter.com', TWITTER_API_ROOT: 'https://api.twitter.com', + API_FALLBACK_DOMAIN: API_FALLBACK_DOMAIN, BOT_UA_REGEX: /bot|facebook|embed|got|firefox\/92|firefox\/38|curl|wget|go-http|yahoo|generator|whatsapp|preview|link|proxy|vkshare|images|analyzer|index|crawl|spider|python|cfnetwork|node/gi, /* 3 hours */ diff --git a/src/fetch.ts b/src/fetch.ts index 65b20a9..e5eda06 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -113,9 +113,6 @@ export const twitterFetch = async ( 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. - We probably don't have to do this at all. But hey, better to be consistent with Twitter Web App. */ let response: unknown; let apiRequest; @@ -179,10 +176,13 @@ export const twitterFetch = async ( export const fetchConversation = async ( status: string, - event: FetchEvent + event: FetchEvent, + fallback = false ): Promise => { return (await twitterFetch( - `${Constants.TWITTER_API_ROOT}/2/timeline/conversation/${status}.json?${Constants.GUEST_FETCH_PARAMETERS}`, + `${ + fallback ? Constants.API_FALLBACK_DOMAIN : Constants.TWITTER_API_ROOT + }/2/timeline/conversation/${status}.json?${Constants.GUEST_FETCH_PARAMETERS}`, event, (_conversation: unknown) => { const conversation = _conversation as TimelineBlobPartial; diff --git a/src/types/env.d.ts b/src/types/env.d.ts index 3f6de37..0d008bf 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -9,6 +9,7 @@ declare const EMBED_URL: string; declare const REDIRECT_URL: string; declare const MOSAIC_DOMAIN_LIST: string; declare const API_HOST_LIST: string; +declare const API_FALLBACK_DOMAIN: string; declare const SENTRY_DSN: string; declare const RELEASE_NAME: string; diff --git a/webpack.config.js b/webpack.config.js index b3a9c1c..40b7321 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,7 +29,8 @@ let envVariables = [ 'API_HOST_LIST', 'SENTRY_DSN', 'DEPRECATED_DOMAIN_LIST', - 'DEPRECATED_DOMAIN_EPOCH' + 'DEPRECATED_DOMAIN_EPOCH', + 'API_FALLBACK_DOMAIN' ]; let plugins = [