=> {
+ const apiStatus = {} as APITwitterStatus;
- /* Sometimes, Twitter returns a different kind of Tweet type called 'TweetWithVisibilityResults'.
+ /* Sometimes, Twitter returns a different kind of type called 'TweetWithVisibilityResults'.
It has slightly different attributes from the regular 'Tweet' type. We fix that up here. */
- if (typeof tweet.core === 'undefined' && typeof tweet.result !== 'undefined') {
- tweet = tweet.result;
+ if (typeof status.core === 'undefined' && typeof status.result !== 'undefined') {
+ status = status.result;
}
- if (typeof tweet.core === 'undefined' && typeof tweet.tweet?.core !== 'undefined') {
- tweet.core = tweet.tweet.core;
+ if (typeof status.core === 'undefined' && typeof status.tweet?.core !== 'undefined') {
+ status.core = status.tweet.core;
}
- if (typeof tweet.legacy === 'undefined' && typeof tweet.tweet?.legacy !== 'undefined') {
- tweet.legacy = tweet.tweet?.legacy;
+ if (typeof status.legacy === 'undefined' && typeof status.tweet?.legacy !== 'undefined') {
+ status.legacy = status.tweet?.legacy;
}
- if (typeof tweet.views === 'undefined' && typeof tweet?.tweet?.views !== 'undefined') {
- tweet.views = tweet?.tweet?.views;
+ if (typeof status.views === 'undefined' && typeof status?.tweet?.views !== 'undefined') {
+ status.views = status?.tweet?.views;
}
- if (typeof tweet.core === 'undefined') {
- console.log('Tweet still not valid', tweet);
- if (tweet.__typename === 'TweetUnavailable' && tweet.reason === 'Protected') {
+ if (typeof status.core === 'undefined') {
+ console.log('Status still not valid', status);
+ if (status.__typename === 'TweetUnavailable' && status.reason === 'Protected') {
return { status: 401 };
} else {
return { status: 404 };
}
}
- const graphQLUser = tweet.core.user_results.result;
+ const graphQLUser = status.core.user_results.result;
const apiUser = convertToApiUser(graphQLUser);
/* Sometimes, `rest_id` is undefined for some reason. Inconsistent behavior. See: https://github.com/FixTweet/FixTweet/issues/416 */
- const id = tweet.rest_id ?? tweet.legacy.id_str;
+ const id = status.rest_id ?? status.legacy.id_str;
/* Populating a lot of the basics */
- apiTweet.url = `${Constants.TWITTER_ROOT}/${apiUser.screen_name}/status/${id}`;
- apiTweet.id = id;
- apiTweet.text = unescapeText(
- linkFixer(tweet.legacy.entities?.urls, tweet.legacy.full_text || '')
+ apiStatus.url = `${Constants.TWITTER_ROOT}/${apiUser.screen_name}/status/${id}`;
+ apiStatus.id = id;
+ apiStatus.text = unescapeText(
+ linkFixer(status.legacy.entities?.urls, status.legacy.full_text || '')
);
if (!threadPiece) {
- apiTweet.author = {
+ apiStatus.author = {
id: apiUser.id,
name: apiUser.name,
screen_name: apiUser.screen_name,
@@ -79,111 +78,111 @@ export const buildAPITweet = async (
website: apiUser.website
};
}
- apiTweet.replies = tweet.legacy.reply_count;
+ apiStatus.replies = status.legacy.reply_count;
if (legacyAPI) {
// @ts-expect-error Use retweets for legacy API
- apiTweet.retweets = tweet.legacy.retweet_count;
+ apiStatus.retweets = status.legacy.retweet_count;
// @ts-expect-error `tweets` is only part of legacy API
- apiTweet.author.tweets = apiTweet.author.statuses;
+ apiStatus.author.tweets = apiStatus.author.statuses;
// @ts-expect-error Part of legacy API that we no longer are able to track
- apiTweet.author.avatar_color = null;
+ apiStatus.author.avatar_color = null;
// @ts-expect-error Use retweets for legacy API
- delete apiTweet.reposts;
+ delete apiStatus.reposts;
// @ts-expect-error Use tweets and not posts for legacy API
- delete apiTweet.author.statuses;
- delete apiTweet.author.global_screen_name;
+ delete apiStatus.author.statuses;
+ delete apiStatus.author.global_screen_name;
} else {
- apiTweet.reposts = tweet.legacy.retweet_count;
- apiTweet.author.global_screen_name = apiUser.global_screen_name;
+ apiStatus.reposts = status.legacy.retweet_count;
+ apiStatus.author.global_screen_name = apiUser.global_screen_name;
}
- apiTweet.likes = tweet.legacy.favorite_count;
- apiTweet.embed_card = 'tweet';
- apiTweet.created_at = tweet.legacy.created_at;
- apiTweet.created_timestamp = new Date(tweet.legacy.created_at).getTime() / 1000;
+ apiStatus.likes = status.legacy.favorite_count;
+ apiStatus.embed_card = 'tweet';
+ apiStatus.created_at = status.legacy.created_at;
+ apiStatus.created_timestamp = new Date(status.legacy.created_at).getTime() / 1000;
- apiTweet.possibly_sensitive = tweet.legacy.possibly_sensitive;
+ apiStatus.possibly_sensitive = status.legacy.possibly_sensitive;
- if (tweet.views.state === 'EnabledWithCount') {
- apiTweet.views = parseInt(tweet.views.count || '0') ?? null;
+ if (status.views.state === 'EnabledWithCount') {
+ apiStatus.views = parseInt(status.views.count || '0') ?? null;
} else {
- apiTweet.views = null;
+ apiStatus.views = null;
}
- console.log('note_tweet', JSON.stringify(tweet.note_tweet));
- const noteTweetText = tweet.note_tweet?.note_tweet_results?.result?.text;
+ console.log('note_tweet', JSON.stringify(status.note_tweet));
+ const noteTweetText = status.note_tweet?.note_tweet_results?.result?.text;
if (noteTweetText) {
- tweet.legacy.entities.urls = tweet.note_tweet?.note_tweet_results?.result?.entity_set.urls;
- tweet.legacy.entities.hashtags =
- tweet.note_tweet?.note_tweet_results?.result?.entity_set.hashtags;
- tweet.legacy.entities.symbols =
- tweet.note_tweet?.note_tweet_results?.result?.entity_set.symbols;
+ status.legacy.entities.urls = status.note_tweet?.note_tweet_results?.result?.entity_set.urls;
+ status.legacy.entities.hashtags =
+ status.note_tweet?.note_tweet_results?.result?.entity_set.hashtags;
+ status.legacy.entities.symbols =
+ status.note_tweet?.note_tweet_results?.result?.entity_set.symbols;
console.log('We meet the conditions to use new note tweets');
- apiTweet.text = unescapeText(linkFixer(tweet.legacy.entities.urls, noteTweetText));
- apiTweet.is_note_tweet = true;
+ apiStatus.text = unescapeText(linkFixer(status.legacy.entities.urls, noteTweetText));
+ apiStatus.is_note_tweet = true;
} else {
- apiTweet.is_note_tweet = false;
+ apiStatus.is_note_tweet = false;
}
- if (tweet.legacy.lang !== 'unk') {
- apiTweet.lang = tweet.legacy.lang;
+ if (status.legacy.lang !== 'unk') {
+ apiStatus.lang = status.legacy.lang;
} else {
- apiTweet.lang = null;
+ apiStatus.lang = null;
}
if (legacyAPI) {
// @ts-expect-error Use replying_to string for legacy API
- apiTweet.replying_to = tweet.legacy?.in_reply_to_screen_name || null;
+ apiStatus.replying_to = status.legacy?.in_reply_to_screen_name || null;
// @ts-expect-error Use replying_to_status string for legacy API
- apiTweet.replying_to_status = tweet.legacy?.in_reply_to_status_id_str || null;
- } else if (tweet.legacy.in_reply_to_screen_name) {
- apiTweet.replying_to = {
- screen_name: tweet.legacy.in_reply_to_screen_name || null,
- post: tweet.legacy.in_reply_to_status_id_str || null
+ apiStatus.replying_to_status = status.legacy?.in_reply_to_status_id_str || null;
+ } else if (status.legacy.in_reply_to_screen_name) {
+ apiStatus.replying_to = {
+ screen_name: status.legacy.in_reply_to_screen_name || null,
+ post: status.legacy.in_reply_to_status_id_str || null
};
} else {
- apiTweet.replying_to = null;
+ apiStatus.replying_to = null;
}
- apiTweet.media = {};
+ apiStatus.media = {};
- /* We found a quote tweet, let's process that too */
- const quoteTweet = tweet.quoted_status_result;
- if (quoteTweet) {
- const buildQuote = await buildAPITweet(c, quoteTweet, language, threadPiece, legacyAPI);
+ /* We found a quote, let's process that too */
+ const quote = status.quoted_status_result;
+ if (quote) {
+ const buildQuote = await buildAPITwitterStatus(c, quote, language, threadPiece, legacyAPI);
if ((buildQuote as FetchResults).status) {
- apiTweet.quote = undefined;
+ apiStatus.quote = undefined;
} else {
- apiTweet.quote = buildQuote as APITweet;
+ apiStatus.quote = buildQuote as APITwitterStatus;
}
- /* Only override the embed_card if it's a basic tweet, since media always takes precedence */
- if (apiTweet.embed_card === 'tweet' && typeof apiTweet.quote !== 'undefined') {
- apiTweet.embed_card = apiTweet.quote.embed_card;
+ /* Only override the embed_card if it's a basic status, since media always takes precedence */
+ if (apiStatus.embed_card === 'tweet' && typeof apiStatus.quote !== 'undefined') {
+ apiStatus.embed_card = apiStatus.quote.embed_card;
}
}
const mediaList = Array.from(
- tweet.legacy.extended_entities?.media || tweet.legacy.entities?.media || []
+ status.legacy.extended_entities?.media || status.legacy.entities?.media || []
);
- // console.log('tweet', JSON.stringify(tweet));
+ // console.log('status', JSON.stringify(status));
- /* Populate this Tweet's media */
+ /* Populate status media */
mediaList.forEach(media => {
const mediaObject = processMedia(media);
if (mediaObject) {
- apiTweet.media.all = apiTweet.media?.all ?? [];
- apiTweet.media?.all?.push(mediaObject);
+ apiStatus.media.all = apiStatus.media?.all ?? [];
+ apiStatus.media?.all?.push(mediaObject);
if (mediaObject.type === 'photo') {
- apiTweet.embed_card = 'summary_large_image';
- apiTweet.media.photos = apiTweet.media?.photos ?? [];
- apiTweet.media.photos?.push(mediaObject);
+ apiStatus.embed_card = 'summary_large_image';
+ apiStatus.media.photos = apiStatus.media?.photos ?? [];
+ apiStatus.media.photos?.push(mediaObject);
} else if (mediaObject.type === 'video' || mediaObject.type === 'gif') {
- apiTweet.embed_card = 'player';
- apiTweet.media.videos = apiTweet.media?.videos ?? [];
- apiTweet.media.videos?.push(mediaObject);
+ apiStatus.embed_card = 'player';
+ apiStatus.media.videos = apiStatus.media?.videos ?? [];
+ apiStatus.media.videos?.push(mediaObject);
} else {
console.log('Unknown media type', mediaObject.type);
}
@@ -193,21 +192,21 @@ export const buildAPITweet = async (
/* Grab color palette data */
/*
if (mediaList[0]?.ext_media_color?.palette) {
- apiTweet.color = colorFromPalette(mediaList[0].ext_media_color.palette);
+ apiStatus.color = colorFromPalette(mediaList[0].ext_media_color.palette);
}
*/
/* Handle photos and mosaic if available */
- if ((apiTweet?.media.photos?.length || 0) > 1 && !threadPiece) {
- const mosaic = await handleMosaic(apiTweet.media?.photos || [], id);
- if (typeof apiTweet.media !== 'undefined' && mosaic !== null) {
- apiTweet.media.mosaic = mosaic;
+ if ((apiStatus?.media.photos?.length || 0) > 1 && !threadPiece) {
+ const mosaic = await handleMosaic(apiStatus.media?.photos || [], id);
+ if (typeof apiStatus.media !== 'undefined' && mosaic !== null) {
+ apiStatus.media.mosaic = mosaic;
}
}
- // Add Tweet source but remove the link HTML tag
- if (tweet.source) {
- apiTweet.source = (tweet.source || '').replace(
+ // Add source but remove the link HTML tag
+ if (status.source) {
+ apiStatus.source = (status.source || '').replace(
/(.+?)<\/a>/,
'$2'
);
@@ -215,34 +214,34 @@ export const buildAPITweet = async (
/* Populate a Twitter card */
- if (tweet.card) {
- const card = renderCard(tweet.card);
+ if (status.card) {
+ const card = renderCard(status.card);
if (card.external_media) {
- apiTweet.media.external = card.external_media;
+ apiStatus.media.external = card.external_media;
}
if (card.poll) {
- apiTweet.poll = card.poll;
+ apiStatus.poll = card.poll;
}
}
if (
- apiTweet.media?.videos &&
- apiTweet.media?.videos.length > 0 &&
- apiTweet.embed_card !== 'player'
+ apiStatus.media?.videos &&
+ apiStatus.media?.videos.length > 0 &&
+ apiStatus.embed_card !== 'player'
) {
- apiTweet.embed_card = 'player';
+ apiStatus.embed_card = 'player';
}
console.log('language?', language);
/* If a language is specified in API or by user, let's try translating it! */
- if (typeof language === 'string' && language.length === 2 && language !== tweet.legacy.lang) {
- console.log(`Attempting to translate Tweet to ${language}...`);
- const translateAPI = await translateTweet(tweet, '', language, c);
+ if (typeof language === 'string' && language.length === 2 && language !== status.legacy.lang) {
+ console.log(`Attempting to translate status to ${language}...`);
+ const translateAPI = await translateStatus(status, '', language, c);
if (translateAPI !== null && translateAPI?.translation) {
- apiTweet.translation = {
+ apiStatus.translation = {
text: unescapeText(
- linkFixer(tweet.legacy?.entities?.urls, translateAPI?.translation || '')
+ linkFixer(status.legacy?.entities?.urls, translateAPI?.translation || '')
),
source_lang: translateAPI?.sourceLanguage || '',
target_lang: translateAPI?.destinationLanguage || '',
@@ -253,16 +252,16 @@ export const buildAPITweet = async (
if (legacyAPI) {
// @ts-expect-error Use twitter_card for legacy API
- apiTweet.twitter_card = apiTweet.embed_card;
+ apiStatus.twitter_card = apiStatus.embed_card;
// @ts-expect-error Part of legacy API that we no longer are able to track
- apiTweet.color = null;
+ apiStatus.color = null;
// @ts-expect-error Use twitter_card for legacy API
- delete apiTweet.embed_card;
- if ((apiTweet.media.all?.length ?? 0) < 1 && !apiTweet.media.external) {
+ delete apiStatus.embed_card;
+ if ((apiStatus.media.all?.length ?? 0) < 1 && !apiStatus.media.external) {
// @ts-expect-error media is not required in legacy API if empty
- delete apiTweet.media;
+ delete apiStatus.media;
}
}
- return apiTweet;
+ return apiStatus;
};
diff --git a/src/realms/twitter/router.ts b/src/realms/twitter/router.ts
index a4014aa..19512c7 100644
--- a/src/realms/twitter/router.ts
+++ b/src/realms/twitter/router.ts
@@ -27,72 +27,72 @@ export const getBaseRedirectUrl = (c: Context) => {
};
/* Workaround for some dumb maybe-build time issue where statusRequest isn't ready or something because none of these trigger*/
-const tweetRequest = async (c: Context) => await statusRequest(c);
+const twitterStatusRequest = async (c: Context) => await statusRequest(c);
const _profileRequest = async (c: Context) => await profileRequest(c);
/* How can hono not handle trailing slashes? This is so stupid,
serious TODO: Figure out how to make this not stupid. */
-twitter.get('/:endpoint{status(es)?}/:id', tweetRequest);
-twitter.get('/:endpoint{status(es)?}/:id/', tweetRequest);
-twitter.get('/:endpoint{status(es)?}/:id/:language/', tweetRequest);
-twitter.get('/:endpoint{status(es)?}/:id/:language', tweetRequest);
+twitter.get('/:endpoint{status(es)?}/:id', twitterStatusRequest);
+twitter.get('/:endpoint{status(es)?}/:id/', twitterStatusRequest);
+twitter.get('/:endpoint{status(es)?}/:id/:language/', twitterStatusRequest);
+twitter.get('/:endpoint{status(es)?}/:id/:language', twitterStatusRequest);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:language',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:language/',
- tweetRequest
+ twitterStatusRequest
);
-twitter.get('/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id', tweetRequest);
-twitter.get('/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/', tweetRequest);
+twitter.get('/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id', twitterStatusRequest);
+twitter.get('/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/', twitterStatusRequest);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:language',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:language/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/:language',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/:language/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/:language',
- tweetRequest
+ twitterStatusRequest
);
twitter.get(
'/:prefix{(dir|dl)}/:handle{[0-9a-zA-Z_]+}/:endpoint{status(es)?}/:id/:mediaType{(photos?|videos?)}/:mediaNumber{[1-4]}/:language/',
- tweetRequest
+ twitterStatusRequest
);
twitter.get('/version/', versionRoute);
diff --git a/src/realms/twitter/routes/oembed.ts b/src/realms/twitter/routes/oembed.ts
index fe3aa9b..de80759 100644
--- a/src/realms/twitter/routes/oembed.ts
+++ b/src/realms/twitter/routes/oembed.ts
@@ -18,7 +18,7 @@ export const oembed = async (c: Context) => {
const data: OEmbed = {
author_name: text,
author_url: `${Constants.TWITTER_ROOT}/${encodeURIComponent(author)}/status/${status}`,
- /* Change provider name if tweet is on deprecated domain. */
+ /* Change provider name if status is on deprecated domain. */
provider_name:
searchParams.get('deprecated') === 'true' ? Strings.DEPRECATED_DOMAIN_NOTICE_DISCORD : name,
provider_url: url,
diff --git a/src/realms/twitter/routes/status.ts b/src/realms/twitter/routes/status.ts
index d3560a5..8b5d4c8 100644
--- a/src/realms/twitter/routes/status.ts
+++ b/src/realms/twitter/routes/status.ts
@@ -4,7 +4,7 @@ import { getBaseRedirectUrl } from '../router';
import { handleStatus } from '../../../embed/status';
import { Strings } from '../../../strings';
-/* Handler for status (Tweet) request */
+/* Handler for status request */
export const statusRequest = async (c: Context) => {
const { prefix, handle, id, mediaNumber, language } = c.req.param();
const url = new URL(c.req.url);
@@ -35,8 +35,8 @@ export const statusRequest = async (c: Context) => {
const isBotUA = userAgent.match(Constants.BOT_UA_REGEX) !== null || flags?.archive;
/* Check if domain is a direct media domain (i.e. d.fxtwitter.com),
- the tweet is prefixed with /dl/ or /dir/ (for TwitFix interop), or the
- tweet ends in .mp4, .jpg, .jpeg, or .png
+ the status is prefixed with /dl/ or /dir/ (for TwitFix interop), or the
+ status ends in .mp4, .jpg, .jpeg, or .png
Note that .png is not documented because images always redirect to a jpg,
but it will help someone who does it mistakenly on something like Discord
@@ -63,7 +63,7 @@ export const statusRequest = async (c: Context) => {
flags.direct = true;
}
- /* The pxtwitter.com domain is deprecated and Tweets posted after deprecation
+ /* The pxtwitter.com domain is deprecated and statuses posted after deprecation
date will have a notice saying we've moved to fxtwitter.com! */
if (
Constants.DEPRECATED_DOMAIN_LIST.includes(url.hostname) &&
@@ -115,9 +115,9 @@ export const statusRequest = async (c: Context) => {
if (statusResponse) {
/* We're checking if the User Agent is a bot again specifically in case they requested
- direct media (d.fxtwitter.com, .mp4/.jpg, etc) but the Tweet contains no media.
+ direct media (d.fxtwitter.com, .mp4/.jpg, etc) but the status contains no media.
- Since we obviously have no media to give the user, we'll just redirect to the Tweet.
+ Since we obviously have no media to give the user, we'll just redirect to the status.
Embeds will return as usual to bots as if direct media was never specified. */
if (!isBotUA && !flags.api && !flags.direct) {
const baseUrl = getBaseRedirectUrl(c);
@@ -135,7 +135,7 @@ export const statusRequest = async (c: Context) => {
}
} else {
/* A human has clicked a fxtwitter.com/:screen_name/status/:id link!
- Obviously we just need to redirect to the Tweet directly.*/
+ Obviously we just need to redirect to the status directly.*/
console.log('Matched human UA', userAgent);
return c.redirect(`${baseUrl}/${handle || 'i'}/status/${id?.match(/\d{2,20}/)?.[0]}`, 302);
diff --git a/src/render/instantview.ts b/src/render/instantview.ts
index 40a5fa9..3dddc85 100644
--- a/src/render/instantview.ts
+++ b/src/render/instantview.ts
@@ -4,7 +4,7 @@ import { getSocialTextIV } from '../helpers/author';
import { sanitizeText } from '../helpers/utils';
import { Strings } from '../strings';
-const populateUserLinks = (tweet: APIStatus, text: string): string => {
+const populateUserLinks = (status: APIStatus, text: string): string => {
/* TODO: Maybe we can add username splices to our API so only genuinely valid users are linked? */
text.match(/@(\w{1,15})/g)?.forEach(match => {
const username = match.replace('@', '');
@@ -16,10 +16,10 @@ const populateUserLinks = (tweet: APIStatus, text: string): string => {
return text;
};
-const generateTweetMedia = (tweet: APIStatus): string => {
+const generateStatusMedia = (status: APIStatus): string => {
let media = '';
- if (tweet.media?.all?.length) {
- tweet.media.all.forEach(mediaItem => {
+ if (status.media?.all?.length) {
+ status.media.all.forEach(mediaItem => {
switch (mediaItem.type) {
case 'photo':
// eslint-disable-next-line no-case-declarations
@@ -30,10 +30,10 @@ const generateTweetMedia = (tweet: APIStatus): string => {
});
break;
case 'video':
- media += ``;
+ media += ``;
break;
case 'gif':
- media += ``;
+ media += ``;
break;
}
});
@@ -82,23 +82,23 @@ function paragraphify(text: string, isQuote = false): string {
.join('\n');
}
-function getTranslatedText(tweet: APITweet, isQuote = false): string | null {
- if (!tweet.translation) {
+function getTranslatedText(status: APITwitterStatus, isQuote = false): string | null {
+ if (!status.translation) {
return null;
}
- let text = paragraphify(sanitizeText(tweet.translation?.text), isQuote);
+ let text = paragraphify(sanitizeText(status.translation?.text), isQuote);
text = htmlifyLinks(text);
text = htmlifyHashtags(text);
- text = populateUserLinks(tweet, text);
+ text = populateUserLinks(status, text);
const formatText =
- tweet.translation.target_lang === 'en'
+ status.translation.target_lang === 'en'
? Strings.TRANSLATE_TEXT.format({
- language: tweet.translation.source_lang_en
+ language: status.translation.source_lang_en
})
: Strings.TRANSLATE_TEXT_INTL.format({
- source: tweet.translation.source_lang.toUpperCase(),
- destination: tweet.translation.target_lang.toUpperCase()
+ source: status.translation.source_lang.toUpperCase(),
+ destination: status.translation.target_lang.toUpperCase()
});
return `${formatText}
${text}Original
`;
@@ -117,13 +117,13 @@ const truncateSocialCount = (count: number): string => {
}
};
-const generateTweetFooter = (tweet: APIStatus, isQuote = false): string => {
- const { author } = tweet;
+const generateStatusFooter = (status: APIStatus, isQuote = false): string => {
+ const { author } = status;
let description = author.description;
description = htmlifyLinks(description);
description = htmlifyHashtags(description);
- description = populateUserLinks(tweet, description);
+ description = populateUserLinks(status, description);
return `
{socialText}
@@ -131,8 +131,8 @@ const generateTweetFooter = (tweet: APIStatus, isQuote = false): string => {
{aboutSection}
`.format({
- socialText: getSocialTextIV(tweet as APITweet) || '',
- viewOriginal: !isQuote ? `View original post` : notApplicableComment,
+ socialText: getSocialTextIV(status as APITwitterStatus) || '',
+ viewOriginal: !isQuote ? `View original post` : notApplicableComment,
aboutSection: isQuote
? ''
: `About author
@@ -144,7 +144,7 @@ const generateTweetFooter = (tweet: APIStatus, isQuote = false): string => {
{following} Following
{followers} Followers
- {tweets} Posts
+ {statuses} Posts
`.format({
pfp: `
View original post` : notApplicableComment}
+
+ ${!isQuote && status.quote ? generateStatus(status.quote, true) : notApplicableComment}
+ ${!isQuote ? generateStatusFooter(status) : ''}
+
${!isQuote ? `View original post` : notApplicableComment}
`.format({
quoteHeader: isQuote
- ? ``
+ ? ``
: ''
});
};
export const renderInstantView = (properties: RenderProperties): ResponseInstructions => {
console.log('Generating Instant View...');
- const { tweet, flags } = properties;
+ const { status, flags } = properties;
const instructions: ResponseInstructions = { addHeaders: [] };
/* Use ISO date for Medium template */
- const postDate = new Date(tweet.created_at).toISOString();
+ const statusDate = new Date(status.created_at).toISOString();
/* Pretend to be Medium to allow Instant View to work.
Thanks to https://nikstar.me/post/instant-view/ for the help!
@@ -202,7 +202,7 @@ export const renderInstantView = (properties: RenderProperties): ResponseInstruc
contact me https://t.me/dangeredwolf */
instructions.addHeaders = [
``,
- ``,
+ ``,
flags?.archive
? ``
: ``
@@ -216,13 +216,13 @@ export const renderInstantView = (properties: RenderProperties): ResponseInstruc
flags?.archive
? `${Constants.BRANDING_NAME} archive`
: 'If you can see this, your browser is doing something weird with your user agent.'
- } View original post
+ } View original post
- View original
- ${tweet.author.name} (@${tweet.author.screen_name})
+ View original
+ ${status.author.name} (@${status.author.screen_name})
- ${generateTweet(tweet)}
+ ${generateStatus(status)}
`;
return instructions;
diff --git a/src/render/photo.ts b/src/render/photo.ts
index bba2871..62852a2 100644
--- a/src/render/photo.ts
+++ b/src/render/photo.ts
@@ -5,15 +5,15 @@ export const renderPhoto = (
properties: RenderProperties,
photo: APIPhoto | APIMosaicPhoto
): ResponseInstructions => {
- const { tweet, engagementText, authorText, isOverrideMedia, userAgent } = properties;
+ const { status, engagementText, authorText, isOverrideMedia, userAgent } = properties;
const instructions: ResponseInstructions = { addHeaders: [] };
- if ((tweet.media?.photos?.length || 0) > 1 && (!tweet.media?.mosaic || isOverrideMedia)) {
+ if ((status.media?.photos?.length || 0) > 1 && (!status.media?.mosaic || isOverrideMedia)) {
photo = photo as APIPhoto;
- const all = tweet.media?.all as APIMedia[];
+ const all = status.media?.all as APIMedia[];
const baseString =
- all.length === tweet.media?.photos?.length ? Strings.PHOTO_COUNT : Strings.MEDIA_COUNT;
+ all.length === status.media?.photos?.length ? Strings.PHOTO_COUNT : Strings.MEDIA_COUNT;
const photoCounter = baseString.format({
number: String(all.indexOf(photo) + 1),
diff --git a/src/render/video.ts b/src/render/video.ts
index 994a8ec..88bfaec 100644
--- a/src/render/video.ts
+++ b/src/render/video.ts
@@ -6,10 +6,10 @@ export const renderVideo = (
properties: RenderProperties,
video: APIVideo
): ResponseInstructions => {
- const { tweet, userAgent, text } = properties;
+ const { status, userAgent, text } = properties;
const instructions: ResponseInstructions = { addHeaders: [] };
- const all = tweet.media?.all as APIMedia[];
+ const all = status.media?.all as APIMedia[];
/* This fix is specific to Discord not wanting to render videos that are too large,
or rendering low quality videos too small.
@@ -32,7 +32,7 @@ export const renderVideo = (
we'll put an indicator if there are more than one video */
if (all && all.length > 1 && (userAgent?.indexOf('Telegram') ?? 0) > -1) {
const baseString =
- all.length === tweet.media?.videos?.length ? Strings.VIDEO_COUNT : Strings.MEDIA_COUNT;
+ all.length === status.media?.videos?.length ? Strings.VIDEO_COUNT : Strings.MEDIA_COUNT;
const videoCounter = baseString.format({
number: String(all.indexOf(video) + 1),
total: String(all.length)
@@ -41,10 +41,10 @@ export const renderVideo = (
instructions.siteName = `${Constants.BRANDING_NAME} - ${videoCounter}`;
}
- instructions.authorText = tweet.translation?.text || text || '';
+ instructions.authorText = status.translation?.text || text || '';
- if (instructions.authorText.length < 40 && tweet.quote) {
- instructions.authorText += `\n${handleQuote(tweet.quote)}`;
+ if (instructions.authorText.length < 40 && status.quote) {
+ instructions.authorText += `\n${handleQuote(status.quote)}`;
}
/* Push the raw video-related headers */
diff --git a/src/types/types.d.ts b/src/types/types.d.ts
index 48d360e..bfd048a 100644
--- a/src/types/types.d.ts
+++ b/src/types/types.d.ts
@@ -28,7 +28,7 @@ interface ResponseInstructions {
}
interface RenderProperties {
- tweet: APITweet;
+ status: APITwitterStatus;
siteText?: string;
authorText?: string;
engagementText?: string;
@@ -41,13 +41,13 @@ interface RenderProperties {
interface TweetAPIResponse {
code: number;
message: string;
- tweet?: APITweet;
+ tweet?: APITwitterStatus;
}
-interface SocialPostAPIResponse {
+interface StatusAPIResponse {
code: number;
message: string;
- post?: APITweet;
+ status?: APITwitterStatus;
}
interface UserAPIResponse {
@@ -146,7 +146,7 @@ interface APIStatus {
embed_card: 'tweet' | 'summary' | 'summary_large_image' | 'player';
}
-interface APITweet extends APIStatus {
+interface APITwitterStatus extends APIStatus {
views?: number | null;
translation?: APITranslate;
@@ -183,13 +183,13 @@ interface APIUser {
}
interface SocialPost {
- status: APIStatus | APITweet | null;
+ status: APIStatus | APITwitterStatus | null;
author: APIUser | null;
}
interface SocialThread {
- status: APIStatus | APITweet | null;
- thread: (APIStatus | APITweet)[] | null;
+ status: APIStatus | APITwitterStatus | null;
+ thread: (APIStatus | APITwitterStatus)[] | null;
author: APIUser | null;
code: number;
}
diff --git a/src/types/vendor/twitter.d.ts b/src/types/vendor/twitter.d.ts
index e8e693d..b2bcd97 100644
--- a/src/types/vendor/twitter.d.ts
+++ b/src/types/vendor/twitter.d.ts
@@ -308,7 +308,7 @@ type GraphQLUser = {
};
};
-type GraphQLTweetLegacy = {
+type GraphQLTwitterStatusLegacy = {
id_str: string; // "1674824189176590336"
created_at: string; // "Tue Sep 14 20:00:00 +0000 2021"
conversation_id_str: string; // "1674824189176590336"
@@ -351,9 +351,9 @@ type GraphQLTweetLegacy = {
};
};
-type GraphQLTweet = {
+type GraphQLTwitterStatus = {
// Workaround
- result: GraphQLTweet;
+ result: GraphQLTwitterStatus;
__typename: 'Tweet' | 'TweetWithVisibilityResults' | 'TweetUnavailable';
reason: string; // used for errors
rest_id: string; // "1674824189176590336",
@@ -364,7 +364,7 @@ type GraphQLTweet = {
};
};
tweet?: {
- legacy: GraphQLTweetLegacy;
+ legacy: GraphQLTwitterStatusLegacy;
views: {
count: string; // "562"
state: string; // "EnabledWithCount"
@@ -383,8 +383,8 @@ type GraphQLTweet = {
state: string; // "EnabledWithCount"
};
source: string; // "Twitter Web App"
- quoted_status_result?: GraphQLTweet;
- legacy: GraphQLTweetLegacy;
+ quoted_status_result?: GraphQLTwitterStatus;
+ legacy: GraphQLTwitterStatusLegacy;
note_tweet: {
is_expandable: boolean;
note_tweet_results: {
@@ -450,7 +450,7 @@ type GraphQLTimelineTweet = {
item: 'TimelineTweet';
__typename: 'TimelineTweet';
tweet_results: {
- result: GraphQLTweet | TweetTombstone;
+ result: GraphQLTwitterStatus | TweetTombstone;
};
};
@@ -521,7 +521,7 @@ type TimelineTerminateTimelineInstruction = {
type: 'TimelineTerminateTimeline';
direction: 'Top';
};
-type GraphQLTweetNotFoundResponse = {
+type GraphQLTwitterStatusNotFoundResponse = {
errors: [
{
message: string; // "_Missing: No status found with that ID"
@@ -560,7 +560,7 @@ type TweetResultsByRestIdResult = {
errors?: unknown[];
data?: {
tweetResult?: {
- result?: TweetStub | GraphQLTweet;
+ result?: TweetStub | GraphQLTwitterStatus;
};
};
};
@@ -571,6 +571,6 @@ type TweetStub = {
};
interface GraphQLProcessBucket {
- tweets: GraphQLTweet[];
+ statuses: GraphQLTwitterStatus[];
cursors: GraphQLTimelineCursor[];
}
diff --git a/test/worker.test.ts b/test/worker.test.ts
index a726c7b..df527c2 100644
--- a/test/worker.test.ts
+++ b/test/worker.test.ts
@@ -47,7 +47,7 @@ test('Home page redirect', async () => {
expect(resultHuman.headers.get('location')).toEqual(githubUrl);
});
-test('Tweet redirect human', async () => {
+test('Status redirect human', async () => {
const result = await app.request(
new Request('https://fxtwitter.com/jack/status/20', {
method: 'GET',
@@ -58,7 +58,7 @@ test('Tweet redirect human', async () => {
expect(result.headers.get('location')).toEqual('https://twitter.com/jack/status/20');
});
-test('Tweet redirect human trailing slash', async () => {
+test('Status redirect human trailing slash', async () => {
const result = await app.request(
new Request('https://fxtwitter.com/jack/status/20/', {
method: 'GET',
@@ -69,7 +69,7 @@ test('Tweet redirect human trailing slash', async () => {
expect(result.headers.get('location')).toEqual('https://twitter.com/jack/status/20');
});
-test('Tweet redirect human custom base redirect', async () => {
+test('Status redirect human custom base redirect', async () => {
const result = await app.request(
new Request('https://fxtwitter.com/jack/status/20', {
method: 'GET',
@@ -97,7 +97,7 @@ test('Twitter moment redirect', async () => {
expect(result.headers.get('location')).toEqual(`${twitterBaseUrl}/i/events/1572638642127966214`);
});
-test('Tweet response robot', async () => {
+test('Status response robot', async () => {
const result = await app.request(
new Request('https://fxtwitter.com/jack/status/20', {
method: 'GET',
@@ -107,7 +107,7 @@ test('Tweet response robot', async () => {
expect(result.status).toEqual(200);
});
-test('Tweet response robot (trailing slash/query string and extra characters)', async () => {
+test('Status response robot (trailing slash/query string and extra characters)', async () => {
const result = await app.request(
new Request('https://fxtwitter.com/jack/status/20||/?asdf=ghjk&klop;', {
method: 'GET',
@@ -117,7 +117,7 @@ test('Tweet response robot (trailing slash/query string and extra characters)',
expect(result.status).toEqual(200);
});
-test('API fetch basic Tweet', async () => {
+test('API fetch basic Status', async () => {
const result = await app.request(
new Request('https://api.fxtwitter.com/status/20', {
method: 'GET',
@@ -130,29 +130,29 @@ test('API fetch basic Tweet', async () => {
expect(response.code).toEqual(200);
expect(response.message).toEqual('OK');
- const tweet = response.tweet as APITweet;
- expect(tweet).toBeTruthy();
- 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');
- expect(tweet.author.id).toEqual('12');
- expect(tweet.author.name).toBeTruthy();
- expect(tweet.author.avatar_url).toBeTruthy();
- expect(tweet.author.banner_url).toBeTruthy();
- expect(tweet.replies).toBeGreaterThan(0);
+ const status = response.tweet as APITwitterStatus;
+ expect(status).toBeTruthy();
+ expect(status.url).toEqual(`${twitterBaseUrl}/jack/status/20`);
+ expect(status.id).toEqual('20');
+ expect(status.text).toEqual('just setting up my twttr');
+ expect(status.author.screen_name?.toLowerCase()).toEqual('jack');
+ expect(status.author.id).toEqual('12');
+ expect(status.author.name).toBeTruthy();
+ expect(status.author.avatar_url).toBeTruthy();
+ expect(status.author.banner_url).toBeTruthy();
+ expect(status.replies).toBeGreaterThan(0);
// @ts-expect-error retweets only in legacy API
- expect(tweet.retweets).toBeGreaterThan(0);
- expect(tweet.likes).toBeGreaterThan(0);
+ expect(status.retweets).toBeGreaterThan(0);
+ expect(status.likes).toBeGreaterThan(0);
// @ts-expect-error twitter_card only in legacy API
- expect(tweet.twitter_card).toEqual('tweet');
- expect(tweet.created_at).toEqual('Tue Mar 21 20:50:14 +0000 2006');
- expect(tweet.created_timestamp).toEqual(1142974214);
- expect(tweet.lang).toEqual('en');
- expect(tweet.replying_to).toBeNull();
+ expect(status.twitter_card).toEqual('tweet');
+ expect(status.created_at).toEqual('Tue Mar 21 20:50:14 +0000 2006');
+ expect(status.created_timestamp).toEqual(1142974214);
+ expect(status.lang).toEqual('en');
+ expect(status.replying_to).toBeNull();
});
-// test('API fetch video Tweet', async () => {
+// test('API fetch video Status', async () => {
// const result = await app.request(
// new Request('https://api.fxtwitter.com/X/status/854416760933556224', {
// method: 'GET',
@@ -165,27 +165,27 @@ test('API fetch basic Tweet', async () => {
// expect(response.code).toEqual(200);
// expect(response.message).toEqual('OK');
-// const tweet = response.tweet as APITweet;
-// expect(tweet).toBeTruthy();
-// expect(tweet.url).toEqual(`${twitterBaseUrl}/X/status/854416760933556224`);
-// expect(tweet.id).toEqual('854416760933556224');
-// expect(tweet.text).toEqual(
+// const status = response.tweet as APITwitterStatus;
+// expect(status).toBeTruthy();
+// expect(status.url).toEqual(`${twitterBaseUrl}/X/status/854416760933556224`);
+// expect(status.id).toEqual('854416760933556224');
+// expect(status.text).toEqual(
// 'Get the sauces ready, #NuggsForCarter has 3 million+ Retweets.'
// );
-// expect(tweet.author.screen_name?.toLowerCase()).toEqual('x');
-// expect(tweet.author.id).toEqual('783214');
-// expect(tweet.author.name).toBeTruthy();
-// expect(tweet.author.avatar_url).toBeTruthy();
-// expect(tweet.author.banner_url).toBeTruthy();
-// expect(tweet.replies).toBeGreaterThan(0);
-// expect(tweet.retweets).toBeGreaterThan(0);
-// expect(tweet.likes).toBeGreaterThan(0);
-// expect(tweet.twitter_card).toEqual('player');
-// expect(tweet.created_at).toEqual('Tue Apr 18 19:30:04 +0000 2017');
-// expect(tweet.created_timestamp).toEqual(1492543804);
-// expect(tweet.lang).toEqual('en');
-// expect(tweet.replying_to).toBeNull();
-// const video = tweet.media?.videos?.[0] as APIVideo;
+// expect(status.author.screen_name?.toLowerCase()).toEqual('x');
+// expect(status.author.id).toEqual('783214');
+// expect(status.author.name).toBeTruthy();
+// expect(status.author.avatar_url).toBeTruthy();
+// expect(status.author.banner_url).toBeTruthy();
+// expect(status.replies).toBeGreaterThan(0);
+// expect(status.retweets).toBeGreaterThan(0);
+// expect(status.likes).toBeGreaterThan(0);
+// expect(status.twitter_card).toEqual('player');
+// expect(status.created_at).toEqual('Tue Apr 18 19:30:04 +0000 2017');
+// expect(status.created_timestamp).toEqual(1492543804);
+// expect(status.lang).toEqual('en');
+// expect(status.replying_to).toBeNull();
+// const video = status.media?.videos?.[0] as APIVideo;
// expect(video.url).toEqual(
// 'https://video.twimg.com/amplify_video/854415175776059393/vid/720x720/dNEi0crU-jA4mTtr.mp4'
// );
@@ -197,7 +197,7 @@ test('API fetch basic Tweet', async () => {
// expect(video.type).toEqual('video');
// });
-// test('API fetch multi-photo Tweet', async () => {
+// test('API fetch multi-photo status', async () => {
// const result = await app.request(
// new Request('https://api.fxtwitter.com/Twitter/status/1445094085593866246', {
// method: 'GET',
@@ -210,22 +210,22 @@ test('API fetch basic Tweet', async () => {
// expect(response.code).toEqual(200);
// expect(response.message).toEqual('OK');
-// const tweet = response.tweet as APITweet;
-// expect(tweet).toBeTruthy();
-// 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');
-// expect(tweet.author.id).toEqual('783214');
-// expect(tweet.author.name).toBeTruthy();
-// expect(tweet.author.avatar_url).toBeTruthy();
-// expect(tweet.author.banner_url).toBeTruthy();
-// expect(tweet.twitter_card).toEqual('summary_large_image');
-// expect(tweet.created_at).toEqual('Mon Oct 04 18:30:53 +0000 2021');
-// expect(tweet.created_timestamp).toEqual(1633372253);
-// expect(tweet.replying_to?.toLowerCase()).toEqual('netflix');
-// expect(tweet.media?.photos).toBeTruthy();
-// const photos = tweet.media?.photos as APIPhoto[];
+// const status = response.tweet as APITwitterStatus;
+// expect(status).toBeTruthy();
+// expect(status.url).toEqual(`${twitterBaseUrl}/X/status/1445094085593866246`);
+// expect(status.id).toEqual('1445094085593866246');
+// expect(status.text).toEqual('@netflix');
+// expect(status.author.screen_name?.toLowerCase()).toEqual('x');
+// expect(status.author.id).toEqual('783214');
+// expect(status.author.name).toBeTruthy();
+// expect(status.author.avatar_url).toBeTruthy();
+// expect(status.author.banner_url).toBeTruthy();
+// expect(status.twitter_card).toEqual('summary_large_image');
+// expect(status.created_at).toEqual('Mon Oct 04 18:30:53 +0000 2021');
+// expect(status.created_timestamp).toEqual(1633372253);
+// expect(status.replying_to?.toLowerCase()).toEqual('netflix');
+// expect(status.media?.photos).toBeTruthy();
+// const photos = status.media?.photos as APIPhoto[];
// expect(photos[0].url).toEqual('https://pbs.twimg.com/media/FA4BaFaXoBUV3di.jpg');
// expect(photos[0].width).toEqual(950);
// expect(photos[0].height).toEqual(620);
@@ -234,8 +234,8 @@ test('API fetch basic Tweet', async () => {
// expect(photos[1].width).toEqual(1386);
// expect(photos[1].height).toEqual(706);
// expect(photos[1].altText).toBeTruthy();
-// expect(tweet.media?.mosaic).toBeTruthy();
-// const mosaic = tweet.media?.mosaic as APIMosaicPhoto;
+// expect(status.media?.mosaic).toBeTruthy();
+// const mosaic = status.media?.mosaic as APIMosaicPhoto;
// expect(mosaic.formats?.jpeg).toEqual(
// 'https://mosaic.fxtwitter.com/jpeg/1445094085593866246/FA4BaFaXoBUV3di/FA4BaUyXEAcAHvK'
// );
@@ -244,7 +244,7 @@ test('API fetch basic Tweet', async () => {
// );
// });
-// test('API fetch poll Tweet', async () => {
+// test('API fetch poll status', async () => {
// const result = await app.request(
// new Request('https://api.fxtwitter.com/status/1055475950543167488', {
// method: 'GET',
@@ -257,23 +257,23 @@ test('API fetch basic Tweet', async () => {
// expect(response.code).toEqual(200);
// expect(response.message).toEqual('OK');
-// const tweet = response.tweet as APITweet;
-// expect(tweet).toBeTruthy();
-// 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');
-// expect(tweet.author.id).toEqual('783214');
-// expect(tweet.author.name).toBeTruthy();
-// expect(tweet.author.avatar_url).toBeTruthy();
-// expect(tweet.author.banner_url).toBeTruthy();
-// expect(tweet.twitter_card).toEqual('tweet');
-// expect(tweet.created_at).toEqual('Thu Oct 25 15:07:31 +0000 2018');
-// expect(tweet.created_timestamp).toEqual(1540480051);
-// expect(tweet.lang).toEqual('en');
-// expect(tweet.replying_to).toBeNull();
-// expect(tweet.poll).toBeTruthy();
-// const poll = tweet.poll as APIPoll;
+// const status = response.tweet as APITwitterStatus;
+// expect(status).toBeTruthy();
+// expect(status.url).toEqual(`${twitterBaseUrl}/X/status/1055475950543167488`);
+// expect(status.id).toEqual('1055475950543167488');
+// expect(status.text).toEqual('A poll:');
+// expect(status.author.screen_name?.toLowerCase()).toEqual('x');
+// expect(status.author.id).toEqual('783214');
+// expect(status.author.name).toBeTruthy();
+// expect(status.author.avatar_url).toBeTruthy();
+// expect(status.author.banner_url).toBeTruthy();
+// expect(status.twitter_card).toEqual('tweet');
+// expect(status.created_at).toEqual('Thu Oct 25 15:07:31 +0000 2018');
+// expect(status.created_timestamp).toEqual(1540480051);
+// expect(status.lang).toEqual('en');
+// expect(status.replying_to).toBeNull();
+// expect(status.poll).toBeTruthy();
+// const poll = status.poll as APIPoll;
// expect(poll.ends_at).toEqual('2018-10-26T03:07:30Z');
// expect(poll.time_left_en).toEqual('Final results');
// expect(poll.total_votes).toEqual(54703);