Fix NSFW, added i.fxtwitter.com

This commit is contained in:
dangered wolf 2023-08-20 21:22:36 -04:00
parent d4a6582bd6
commit 775d244c7b
No known key found for this signature in database
GPG key ID: 41E4D37680ED8B58
12 changed files with 50 additions and 24 deletions

View file

@ -1,6 +1,7 @@
BRANDING_NAME = "FixTweet"
DIRECT_MEDIA_DOMAINS = "d.fxtwitter.com,dl.fxtwitter.com,d.pxtwitter.com,d.twittpr.com,dl.pxtwitter.com,dl.twittpr.com"
TEXT_ONLY_DOMAINS = "t.fxtwitter.com,t.twittpr.com"
DIRECT_MEDIA_DOMAINS = "d.fxtwitter.com,dl.fxtwitter.com,d.pxtwitter.com,d.twittpr.com,dl.pxtwitter.com,dl.twittpr.com,d.fixupx.com,d.xfixup.com,dl.fixupx.com,dl.xfixup.com"
TEXT_ONLY_DOMAINS = "t.fxtwitter.com,t.twittpr.com,t.fixupx.com,t.xfixup.com"
INSTANT_VIEW_DOMAINS = "i.fxtwitter.com,i.twittpr.com,i.fixupx.com,i.xfixup.com"
DEPRECATED_DOMAIN_LIST = "pxtwitter.com,www.pxtwitter.com"
DEPRECATED_DOMAIN_EPOCH = "1559320000000000000"
MOSAIC_DOMAIN_LIST = "mosaic.fxtwitter.com"

View file

@ -6,6 +6,7 @@
"globals": {
"BRANDING_NAME": "FixTweet",
"TEXT_ONLY_DOMAINS": "t.fxtwitter.com,t.twittpr.com",
"INSTANT_VIEW_DOMAINS": "i.fxtwitter.com,i.twittpr.com",
"DIRECT_MEDIA_DOMAINS": "d.fxtwitter.com,dl.fxtwitter.com",
"MOSAIC_DOMAIN_LIST": "mosaic.fxtwitter.com",
"DEPRECATED_DOMAIN_LIST": "pxtwitter.com,www.pxtwitter.com",

View file

@ -210,22 +210,23 @@ export const statusAPI = async (
event: FetchEvent,
flags?: InputFlags
): Promise<TweetAPIResponse> => {
let wasMediaBlockedNSFW = false;
let res = await fetchConversation(status, event);
const res = await fetchConversation(status, event);
const tweet = res.data?.tweetResult?.result;
if (!tweet) {
return { code: 404, message: 'NOT_FOUND' };
}
if (tweet.__typename === 'TweetUnavailable' && tweet.reason === 'NsfwLoggedOut') {
wasMediaBlockedNSFW = true;
res = await fetchConversation(status, event, true);
}
/* We're handling this in the actual fetch code now */
// if (tweet.__typename === 'TweetUnavailable' && tweet.reason === 'NsfwLoggedOut') {
// wasMediaBlockedNSFW = true;
// res = await fetchConversation(status, event, true);
// }
// console.log(JSON.stringify(tweet))
if (tweet.__typename === 'TweetUnavailable') {
if (tweet.reason === 'Protected') {
writeDataPoint(event, language, wasMediaBlockedNSFW, 'PRIVATE_TWEET', flags);
if ((tweet as {reason: string})?.reason === 'Protected') {
writeDataPoint(event, language, false, 'PRIVATE_TWEET', flags);
return { code: 401, message: 'PRIVATE_TWEET' };
// } else if (tweet.reason === 'NsfwLoggedOut') {
// // API failure as elongator should have handled this
@ -233,14 +234,14 @@ export const statusAPI = async (
// return { code: 500, message: 'API_FAIL' };
} else {
// Api failure at parsing status
writeDataPoint(event, language, wasMediaBlockedNSFW, 'API_FAIL', flags);
writeDataPoint(event, language, false, 'API_FAIL', flags);
return { code: 500, message: 'API_FAIL' };
}
}
// If the tweet is not a graphQL tweet something went wrong
if (!isGraphQLTweet(tweet)) {
console.log('Tweet was not a valid tweet', tweet);
writeDataPoint(event, language, wasMediaBlockedNSFW, 'API_FAIL', flags);
writeDataPoint(event, language, false, 'API_FAIL', flags);
return { code: 500, message: 'API_FAIL' };
}
@ -274,7 +275,7 @@ export const statusAPI = async (
/* Finally, staple the Tweet to the response and return it */
response.tweet = apiTweet;
writeDataPoint(event, language, wasMediaBlockedNSFW, 'OK', flags);
writeDataPoint(event, language, false, 'OK', flags);
return response;
};

View file

@ -3,6 +3,7 @@ export const Constants = {
BRANDING_NAME: BRANDING_NAME,
DIRECT_MEDIA_DOMAINS: DIRECT_MEDIA_DOMAINS.split(','),
TEXT_ONLY_DOMAINS: TEXT_ONLY_DOMAINS.split(','),
INSTANT_VIEW_DOMAINS: INSTANT_VIEW_DOMAINS.split(','),
DEPRECATED_DOMAIN_LIST: DEPRECATED_DOMAIN_LIST.split(','),
DEPRECATED_DOMAIN_EPOCH: BigInt(DEPRECATED_DOMAIN_EPOCH),
MOSAIC_DOMAIN_LIST: MOSAIC_DOMAIN_LIST.split(','),

View file

@ -42,7 +42,7 @@ export const handleStatus = async (
isTelegram /*&& !tweet.possibly_sensitive*/ &&
!flags?.direct &&
!flags?.api &&
(tweet.media?.mosaic || tweet.is_note_tweet || tweet.quote);
(tweet.media?.mosaic || tweet.is_note_tweet || tweet.quote || flags?.forceInstantView);
let ivbody = '';

View file

@ -128,11 +128,13 @@ export const twitterFetch = async (
method: 'GET',
headers: headers
});
console.log('Elongator request successful');
} else {
apiRequest = await fetch(url, {
method: 'GET',
headers: headers
});
console.log('Guest API request successful');
}
response = await apiRequest?.json();
@ -148,12 +150,19 @@ export const twitterFetch = async (
continue;
}
// @ts-expect-error This is safe due to optional chaining
if ((response as TweetResultsByRestIdResult)?.data?.tweetResult?.result?.reason === 'NsfwLoggedOut') {
console.log(`nsfw tweet detected, it's elongator time`);
useElongator = true;
continue;
}
const remainingRateLimit = parseInt(
apiRequest.headers.get('x-rate-limit-remaining') || '0'
);
console.log(`Remaining rate limit: ${remainingRateLimit} requests`);
/* Running out of requests within our rate limit, let's purge the cache */
if (remainingRateLimit < 10) {
if (remainingRateLimit < 10 && !useElongator) {
console.log(`Purging token on this edge due to low rate limit remaining`);
event &&
event.waitUntil(
@ -180,6 +189,7 @@ export const twitterFetch = async (
// @ts-expect-error - We'll pin the guest token to whatever response we have
response.guestToken = guestToken;
console.log('twitterFetch is all done here, see you soon!');
return response;
}
@ -240,13 +250,17 @@ export const fetchConversation = async (
if (isGraphQLTweet(tweet)) {
return true;
}
if (tweet?.__typename === 'TweetUnavailable' && tweet.reason === 'Protected') {
console.log('invalid graphql tweet');
if (tweet?.__typename === 'TweetUnavailable' && tweet.reason === 'NsfwLoggedOut') {
console.log('tweet is nsfw');
return true;
}
if (tweet?.__typename === 'TweetUnavailable' && tweet.reason === 'NsfwLoggedOut') {
if (tweet?.__typename === 'TweetUnavailable' && tweet.reason === 'Protected') {
console.log('tweet is protected');
return true;
}
if (tweet?.__typename === 'TweetUnavailable') {
console.log('generic tweet unavailable error')
return true;
}
// Final clause for checking if it's valid is if there's errors
@ -284,6 +298,7 @@ export const fetchUser = async (
const response = _res as GraphQLUserResponse;
// If _res.data is an empty object, we have no user
if (!Object.keys(response?.data).length) {
console.log(`response.data is empty, can't continue`);
return false;
}
return !(

View file

@ -53,6 +53,9 @@ const statusRequest = async (
} else if (Constants.TEXT_ONLY_DOMAINS.includes(url.hostname)) {
console.log('Text-only embed request');
flags.textOnly = true;
} else if (Constants.INSTANT_VIEW_DOMAINS.includes(url.hostname)) {
console.log('Forced instant view request');
flags.forceInstantView = true;
} else if (prefix === 'dl' || prefix === 'dir') {
console.log('Direct media request by path prefix');
flags.direct = true;

1
src/types/env.d.ts vendored
View file

@ -1,6 +1,7 @@
declare const BRANDING_NAME: string;
declare const DIRECT_MEDIA_DOMAINS: string;
declare const TEXT_ONLY_DOMAINS: string;
declare const INSTANT_VIEW_DOMAINS: string;
declare const DEPRECATED_DOMAIN_LIST: string;
declare const DEPRECATED_DOMAIN_EPOCH: string;
declare const HOST_URL: string;

View file

@ -308,7 +308,7 @@ type GraphQLUser = {
type GraphQLTweet = {
// Workaround
result: GraphQLTweet;
__typename: 'Tweet';
__typename: 'Tweet' | 'TweetUnavailable';
rest_id: string; // "1674824189176590336",
has_birdwatch_notes: false;
core: {

View file

@ -8,6 +8,7 @@ type InputFlags = {
deprecated?: boolean;
textOnly?: boolean;
isXDomain?: boolean;
forceInstantView?: boolean;
};
interface StatusResponse {

View file

@ -6,6 +6,7 @@ const humanHeaders = {
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
};
const githubUrl = 'https://github.com/FixTweet/FixTweet';
const twitterBaseUrl = 'https://twitter.com';
test('Home page redirect', async () => {
const result = await cacheWrapper(
@ -49,7 +50,7 @@ test('Twitter moment redirect', async () => {
);
expect(result.status).toEqual(302);
expect(result.headers.get('location')).toEqual(
'https://twitter.com/i/events/1572638642127966214'
`${twitterBaseUrl}/i/events/1572638642127966214`
);
});
@ -78,7 +79,7 @@ test('API fetch basic Tweet', async () => {
const tweet = response.tweet as APITweet;
expect(tweet).toBeTruthy();
expect(tweet.url).toEqual('https://twitter.com/jack/status/20');
expect(tweet.url).toEqual(`${twitterBaseUrl}/jack/status/20`);
expect(tweet.id).toEqual('20');
expect(tweet.text).toEqual('just setting up my twttr');
expect(tweet.author.screen_name?.toLowerCase()).toEqual('jack');
@ -112,7 +113,7 @@ test('API fetch video Tweet', async () => {
const tweet = response.tweet as APITweet;
expect(tweet).toBeTruthy();
expect(tweet.url).toEqual('https://twitter.com/X/status/854416760933556224');
expect(tweet.url).toEqual(`${twitterBaseUrl}/X/status/854416760933556224`);
expect(tweet.id).toEqual('854416760933556224');
expect(tweet.text).toEqual(
'Get the sauces ready, #NuggsForCarter has 3 million+ Retweets.'
@ -158,7 +159,7 @@ test('API fetch multi-photo Tweet', async () => {
const tweet = response.tweet as APITweet;
expect(tweet).toBeTruthy();
expect(tweet.url).toEqual('https://twitter.com/X/status/1445094085593866246');
expect(tweet.url).toEqual(`${twitterBaseUrl}/X/status/1445094085593866246`);
expect(tweet.id).toEqual('1445094085593866246');
expect(tweet.text).toEqual('@netflix');
expect(tweet.author.screen_name?.toLowerCase()).toEqual('x');
@ -206,7 +207,7 @@ test('API fetch poll Tweet', async () => {
const tweet = response.tweet as APITweet;
expect(tweet).toBeTruthy();
expect(tweet.url).toEqual('https://twitter.com/X/status/1055475950543167488');
expect(tweet.url).toEqual(`${twitterBaseUrl}/X/status/1055475950543167488`);
expect(tweet.id).toEqual('1055475950543167488');
expect(tweet.text).toEqual('A poll:');
expect(tweet.author.screen_name?.toLowerCase()).toEqual('x');
@ -256,7 +257,7 @@ test('API fetch user', async () => {
const user = response.user as APIUser;
expect(user).toBeTruthy();
expect(user.url).toEqual('https://twitter.com/X');
expect(user.url).toEqual(`${twitterBaseUrl}/X`);
expect(user.id).toEqual('783214');
expect(user.screen_name).toEqual('X');
expect(user.followers).toEqual(expect.any(Number));

View file

@ -21,6 +21,7 @@ let envVariables = [
'BRANDING_NAME',
'DIRECT_MEDIA_DOMAINS',
'TEXT_ONLY_DOMAINS',
'INSTANT_VIEW_DOMAINS',
'HOST_URL',
'REDIRECT_URL',
'EMBED_URL',