import { Constants } from './constants'; import { handleQuote } from './quote'; import { sanitizeText } from './utils'; import { Strings } from './strings'; import { getAuthorText } from './author'; import { statusAPI } from './api'; export const returnError = (error: string): StatusResponse => { return { text: Strings.BASE_HTML.format({ lang: '', headers: [ ``, `` ].join('') }) }; }; export const handleStatus = async ( status: string, mediaNumber?: number, userAgent?: string, flags?: InputFlags, language?: string ): Promise => { console.log('Direct?', flags?.direct); const api = await statusAPI(status, language); const tweet = api?.tweet as APITweet; if (flags?.api) { return { response: new Response(JSON.stringify(api), { headers: { ...Constants.RESPONSE_HEADERS, 'content-type': 'application/json' }, status: api.code }) }; } switch (api.code) { case 401: return returnError(Strings.ERROR_PRIVATE); case 404: return returnError(Strings.ERROR_TWEET_NOT_FOUND); case 500: return returnError(Strings.ERROR_API_FAIL); } if (flags?.direct && tweet.media) { let redirectUrl: string | null = null; if (tweet.media.video) { redirectUrl = tweet.media.video.url; } else if (tweet.media.photos) { const photos = tweet.media.photos; redirectUrl = (photos[(mediaNumber || 1) - 1] || photos[0]).url; } if (redirectUrl) { return { response: Response.redirect(redirectUrl, 302) }; } } /* Use quote media if there is no media */ if (!tweet.media && tweet.quote?.media) { tweet.media = tweet.quote.media; tweet.twitter_card = tweet.quote.twitter_card; } let authorText = getAuthorText(tweet) || Strings.DEFAULT_AUTHOR_TEXT; const engagementText = authorText.replace(/ {4}/g, ' '); const siteName = Constants.BRANDING_NAME; let newText = tweet.text; const headers = [ ``, ``, ``, ``, `` ]; if (userAgent?.indexOf('Telegram') === -1) { headers.push( `` ); } if (tweet.translation) { const { translation } = tweet; const formatText = language === 'en' ? Strings.TRANSLATE_TEXT.format({ language: translation.source_lang_en }) : Strings.TRANSLATE_TEXT_INTL.format({ source: translation.source_lang.toUpperCase(), destination: translation.target_lang.toUpperCase() }); newText = `${translation.text}\n\n` + `${formatText}\n\n` + `${newText}`; } /* Video renderer */ if (tweet.media?.video) { authorText = newText || ''; if (tweet?.translation) { authorText = tweet.translation?.text || ''; } const { video } = tweet.media; /* Multiplying by 0.5 is an ugly hack to fix Discord disliking videos that are too large lol */ let sizeMultiplier = 1; if (video.width > 1920 || video.height > 1920) { sizeMultiplier = 0.5; } if (video.width < 400 && video.height < 400) { sizeMultiplier = 2; } headers.push( ``, ``, ``, ``, ``, ``, ``, ``, `` ); } /* Photo renderer */ if (tweet.media?.photos) { const { photos } = tweet.media; let photo = photos[(mediaNumber || 1) - 1]; if (typeof mediaNumber !== 'number' && tweet.media.mosaic) { photo = { url: userAgent?.indexOf('Telegram') === -1 ? tweet.media.mosaic.formats.webp : tweet.media.mosaic.formats.jpeg, width: tweet.media.mosaic.width, height: tweet.media.mosaic.height, type: 'photo' }; } else if (photos.length > 1) { const photoCounter = Strings.PHOTO_COUNT.format({ number: String(photos.indexOf(photo) + 1), total: String(photos.length) }); authorText = authorText === Strings.DEFAULT_AUTHOR_TEXT ? photoCounter : `${authorText} ― ${photoCounter}`; let siteName = `${Constants.BRANDING_NAME} - ${photoCounter}`; if (engagementText) { siteName = `${Constants.BRANDING_NAME} - ${engagementText} - ${photoCounter}`; } headers.push(``); } headers.push( ``, ``, ``, ``, ``, `` ); } /* External media renderer (i.e. YouTube) */ if (tweet.media?.external) { const { external } = tweet.media; authorText = newText || ''; headers.push( ``, ``, ``, ``, ``, ``, ``, `` ); } /* Poll renderer */ if (tweet.poll) { const { poll } = tweet; let barLength = 36; let str = ''; if (userAgent?.indexOf('Telegram') !== -1) { barLength = 24; } tweet.poll.choices.forEach(choice => { // render bar const bar = '█'.repeat((choice.percentage / 100) * barLength); // eslint-disable-next-line no-irregular-whitespace str += `${bar}\n${choice.label}  (${choice.percentage}%) `; }); str += `\n${poll.total_votes} votes · ${poll.time_left_en}`; newText += `\n\n${str}`; } if (!tweet.media?.video && !tweet.media?.photos) { headers.push( // Use a slightly higher resolution image for profile pics ``, `` ); } if (api.tweet?.quote) { const quoteText = handleQuote(api.tweet.quote); newText += `\n${quoteText}`; } headers.push( ``, ``, `` ); /* Special reply handling if authorText is not overriden */ if (tweet.replying_to && authorText === Strings.DEFAULT_AUTHOR_TEXT) { authorText = `↪ Replying to @${tweet.replying_to}`; } /* The additional oembed is pulled by Discord to enable improved embeds. Telegram does not use this. */ headers.push( `` ); /* When dealing with a Tweet of unknown lang, fall back to en */ const lang = tweet.lang === 'unk' ? 'en' : tweet.lang || 'en'; return { text: Strings.BASE_HTML.format({ lang: `lang="${lang}"`, headers: headers.join('') }) }; };