diff --git a/.env.example b/.env.example index 749a0cf..0271525 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ BRANDING_NAME = "pxTwitter" BRANDING_NAME_DISCORD = "pxTwitter // Twittpr - Embed videos, polls & more!" DIRECT_MEDIA_DOMAINS = "d.pxtwitter.com,d.twittpr.com,dl.pxtwitter.com,dl.twittpr.com" +MOSAIC_DOMAIN_LIST = "mosaic1.pxtwitter.com,mosaic2.pxtwitter.com" HOST_URL = "https://pxtwitter.com" REDIRECT_URL = "https://github.com/dangeredwolf/pxTwitter" \ No newline at end of file diff --git a/src/constants.ts b/src/constants.ts index 36c74dd..87c98e6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,6 +4,7 @@ export const Constants = { BRANDING_NAME: BRANDING_NAME, BRANDING_NAME_DISCORD: BRANDING_NAME_DISCORD, DIRECT_MEDIA_DOMAINS: DIRECT_MEDIA_DOMAINS.split(','), + MOSAIC_DOMAIN_LIST: MOSAIC_DOMAIN_LIST.split(','), HOST_URL: HOST_URL, REDIRECT_URL: REDIRECT_URL, TWITTER_ROOT: 'https://twitter.com', diff --git a/src/env.d.ts b/src/env.d.ts index 1445947..f0140ec 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -3,3 +3,4 @@ declare const BRANDING_NAME_DISCORD: string; declare const DIRECT_MEDIA_DOMAINS: string; declare const HOST_URL: string; declare const REDIRECT_URL: string; +declare const MOSAIC_DOMAIN_LIST: string; \ No newline at end of file diff --git a/src/mosaic.ts b/src/mosaic.ts new file mode 100644 index 0000000..d3fc66b --- /dev/null +++ b/src/mosaic.ts @@ -0,0 +1,48 @@ +import { Constants } from "./constants" + +export const handleMosaic = async (mediaList: TweetMedia[]): Promise => { + let mosaicDomains = Constants.MOSAIC_DOMAIN_LIST; + let selectedDomain: string | null = null; + while (selectedDomain === null && mosaicDomains.length > 0) { + // fetch /ping on a random domain + let domain = mosaicDomains[Math.floor(Math.random() * mosaicDomains.length)]; + // let response = await fetch(`https://${domain}/ping`); + // if (response.status === 200) { + selectedDomain = domain; + // } else { + // mosaicDomains = mosaicDomains.filter(d => d !== domain); + // console.log(`${domain} is not available, removing from list`); + // } + } + + // Fallback if all Mosaic servers are down + if (selectedDomain === null) { + return mediaList[0]; + } else { + console.log('mediaList', mediaList); + let mosaicMedia = mediaList.map(media => media.media_url_https?.match(/(?<=\/media\/)[a-zA-Z0-9_\-]+(?=[\.\?])/g)?.[0] || ''); + console.log('mosaicMedia', mosaicMedia); + let constructUrl = `https://${selectedDomain}/1`; + if (mosaicMedia[0]) { + constructUrl += `/${mosaicMedia[0]}`; + } + if (mosaicMedia[1]) { + constructUrl += `/${mosaicMedia[1]}`; + } + if (mosaicMedia[2]) { + constructUrl += `/${mosaicMedia[2]}`; + } + if (mosaicMedia[3]) { + constructUrl += `/${mosaicMedia[3]}`; + } + + return { + media_url_https: constructUrl, + original_info: { + height: mediaList.reduce((acc, media) => acc + media.original_info?.height, 0), + width: mediaList.reduce((acc, media) => acc + media.original_info?.width, 0) + }, + type: 'photo' + } as TweetMedia; + } +} \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 6945411..f38ebd5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -10,7 +10,7 @@ const statusRequest = async (request: Request, event: FetchEvent, flags: InputFl const url = new URL(request.url); const userAgent = request.headers.get('User-Agent') || ''; - let isBotUA = userAgent.match(/bot|facebook/gi) !== null; + let isBotUA = userAgent.match(/bot|facebook|embed|got|Firefox\/92|curl|wget/gi) !== null; if ( url.pathname.match(/\/status(es)?\/\d+\.(mp4|png|jpg)/g) !== null || @@ -28,7 +28,7 @@ const statusRequest = async (request: Request, event: FetchEvent, flags: InputFl let statusResponse = await handleStatus( event, id?.match(/\d{2,20}/)?.[0] || '0', - parseInt(mediaNumber || '1'), + mediaNumber ? parseInt(mediaNumber) : undefined, userAgent, flags ); @@ -56,7 +56,7 @@ const statusRequest = async (request: Request, event: FetchEvent, flags: InputFl return response; } else { - console.log('Matched human UA'); + console.log('Matched human UA', request.headers.get('User-Agent')); return Response.redirect(`${Constants.TWITTER_ROOT}/${handle}/status/${id}`, 302); } }; diff --git a/src/status.ts b/src/status.ts index 25e173e..04b28a1 100644 --- a/src/status.ts +++ b/src/status.ts @@ -6,6 +6,7 @@ import { renderCard } from './card'; import { handleQuote } from './quote'; import { sanitizeText } from './utils'; import { Strings } from './strings'; +import { handleMosaic } from './mosaic'; export const returnError = (error: string): StatusResponse => { return { @@ -258,9 +259,11 @@ export const handleStatus = async ( } }; - let actualMediaNumber = 1; + let actualMediaNumber = 0; + let renderedMosaic = false; console.log('mediaNumber', mediaNumber); + console.log('mediaList length', mediaList.length); /* You can specify a specific photo in the URL and we'll pull the correct one, otherwise it falls back to first */ @@ -271,12 +274,14 @@ export const handleStatus = async ( console.log(`Media ${mediaNumber} found`); actualMediaNumber = mediaNumber - 1; processMedia(mediaList[actualMediaNumber]); - } else { + } else if (mediaList.length === 1 || (userAgent?.indexOf?.('Telegram') || '') > -1) { console.log(`Media ${mediaNumber} not found, ${mediaList.length} total`); - /* I wish Telegram respected multiple photos in a tweet, - and that Discord could do the same for 3rd party providers like us */ - // media.forEach(media => processMedia(media)); processMedia(firstMedia); + // Telegram hates Mosaic media for some reason + } else if (mediaList.length > 1 && userAgent?.indexOf('Telegram') === -1) { + console.log('Handling mosaic'); + processMedia(await handleMosaic(mediaList)); + renderedMosaic = true; } if (flags?.direct && redirectMedia) { @@ -285,7 +290,7 @@ export const handleStatus = async ( return { response: response }; } - if (mediaList.length > 1) { + if (mediaList.length > 1 && !renderedMosaic) { let photoCounter = Strings.PHOTO_COUNT.format({ number: actualMediaNumber + 1, total: mediaList.length @@ -339,4 +344,4 @@ export const handleStatus = async ( headers: headers.join('') }) }; -}; +}; \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 6ba1df3..411f834 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -31,7 +31,10 @@ module.exports = { }), new webpack.DefinePlugin({ REDIRECT_URL: `'${process.env.REDIRECT_URL}'` - }) + }), + new webpack.DefinePlugin({ + MOSAIC_DOMAIN_LIST: `'${process.env.MOSAIC_DOMAIN_LIST}'` + }), ], optimization: { mangleExports: 'size'