diff --git a/src/constants.ts b/src/constants.ts index c71a40e..6a369ff 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,3 @@ -/* We keep this value up-to-date for making our requests to Twitter as - indistinguishable from normal user traffic as possible. */ -const fakeChromeVersion = '105'; - export const Constants = { /* These constants are populated by variables in .env, then set by Webpack */ BRANDING_NAME: BRANDING_NAME, @@ -38,12 +34,10 @@ export const Constants = { 'simple_quoted_tweet=true' ].join('&'), BASE_HEADERS: { - 'sec-ch-ua': `".Not/A)Brand";v="99", "Google Chrome";v="${fakeChromeVersion}", "Chromium";v="${fakeChromeVersion}"`, 'DNT': `1`, 'x-twitter-client-language': `en`, 'sec-ch-ua-mobile': `?0`, 'content-type': `application/x-www-form-urlencoded`, - 'User-Agent': `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${fakeChromeVersion}.0.0.0 Safari/537.36`, 'x-twitter-active-user': `yes`, 'sec-ch-ua-platform': `"Windows"`, 'Accept': `*/*`, diff --git a/src/fetch.ts b/src/fetch.ts index 7ec50ce..b7057d6 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,4 +1,5 @@ import { Constants } from './constants'; +import { generateUserAgent } from './helpers/useragent'; const API_ATTEMPTS = 16; @@ -10,8 +11,13 @@ export const twitterFetch = async ( let apiAttempts = 0; let newTokenGenerated = false; + const [userAgent, secChUa] = generateUserAgent(); + console.log('Hello, I am', userAgent); + const tokenHeaders: { [header: string]: string } = { - Authorization: Constants.GUEST_BEARER_TOKEN, + 'Authorization': Constants.GUEST_BEARER_TOKEN, + 'User-Agent': userAgent, + 'sec-ch-ua': secChUa, ...Constants.BASE_HEADERS }; diff --git a/src/helpers/author.ts b/src/helpers/author.ts index 453326f..18164e0 100644 --- a/src/helpers/author.ts +++ b/src/helpers/author.ts @@ -1,4 +1,4 @@ -import { formatNumber } from "./utils"; +import { formatNumber } from './utils'; /* The embed "author" text we populate with replies, retweets, and likes unless it's a video */ export const getAuthorText = (tweet: APITweet): string | null => { diff --git a/src/helpers/useragent.ts b/src/helpers/useragent.ts new file mode 100644 index 0000000..b7aa56d --- /dev/null +++ b/src/helpers/useragent.ts @@ -0,0 +1,50 @@ +/* We keep this value up-to-date for making our requests to Twitter as + indistinguishable from normal user traffic as possible. */ +const fakeChromeVersion = 109; +const platformWindows = 'Windows NT 10.0; Win64; x64'; +const platformMac = 'Macintosh; Intel Mac OS X 10_15_7'; +const platformLinux = 'X11; Linux x86_64'; +const platformAndroid = 'Linux; Android 13; Pixel 7'; +const chromeUA = `Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version}.0.0.0 Safari/537.36`; +const edgeUA = `Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version}.0.0.0 Safari/537.36 Edg/{version}.0.0.0`; +const chromeMobileUA = `Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version}.0.0.0 Mobile Safari/537.36`; +const edgeMobileUA = `Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version}.0.0.0 Mobile Safari/537.36 Edg/{version}.0.0.0`; + +enum Platforms { + Windows, + Mac, + Linux, + Android +} + +/* Return a random version of Chrome between current and 2 previous versions (i.e. For 109, also return 108 or 107) */ +const getRandomVersion = (): number => fakeChromeVersion - Math.floor(Math.random() * 3); + +export const generateUserAgent = (): [string, string] => { + const platform = Math.floor(Math.random() * 4); + const isEdge = Math.random() > 0.5; + const version = getRandomVersion(); + + let userAgent = isEdge ? edgeUA : chromeUA; + userAgent = userAgent.format({ version: String(version) }); + const secChUaChrome = `".Not/A)Brand";v="99", "Google Chrome";v="{version}", "Chromium";v="{version}"`; + const secChUaEdge = `".Not/A)Brand";v="99", "Microsoft Edge";v="{version}", "Chromium";v="{version}"`; + const secChUa = (isEdge ? secChUaEdge : secChUaChrome).format({ + version: String(version) + }); + + switch (platform) { + case Platforms.Mac: + return [userAgent.format({ platform: platformMac }), secChUa]; + case Platforms.Linux: + return [userAgent.format({ platform: platformLinux }), secChUa]; + case Platforms.Android: + userAgent = isEdge ? edgeMobileUA : chromeMobileUA; + return [ + userAgent.format({ platform: platformAndroid, version: String(version) }), + secChUa + ]; + default: + return [userAgent.format({ platform: platformWindows }), secChUa]; + } +}; diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 2829722..4568b61 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -17,4 +17,4 @@ export const unescapeText = (text: string) => { const numberFormat = new Intl.NumberFormat('en-US'); -export const formatNumber = (num: number) => numberFormat.format(num) \ No newline at end of file +export const formatNumber = (num: number) => numberFormat.format(num);