let loadingDetails = {};
let loadingReplies = {};
let loadingLikers = {};
let tweetStorage = {};
let hashflagStorage = {};
setInterval(() => {
// clearing cache
chrome.storage.local.set({userUpdates: {}}, () => {});
chrome.storage.local.set({peopleRecommendations: {}}, () => {});
chrome.storage.local.set({tweetReplies: {}}, () => {});
chrome.storage.local.set({tweetDetails: {}}, () => {});
chrome.storage.local.set({tweetLikers: {}}, () => {});
chrome.storage.local.set({listData: {}}, () => {});
chrome.storage.local.set({trends: {}}, () => {});
chrome.storage.local.set({trendsv2: {}}, () => {});
}, 60000*10);
setInterval(() => {
// on first minute of hour
if(new Date().getMinutes() !== 0) return;
chrome.storage.local.set({translations: {}}, () => {});
chrome.storage.local.set({hashflags: {}}, () => {});
hashflagStorage = {};
}, 60000);
function debugLog(...args) {
if(typeof vars === "object" && vars.developerMode) {
if(vars.extensiveLogging) {
console.trace(...args);
} else {
console.log(...args, new Error().stack.split("\n")[2].trim()); // genius
}
}
}
// extract full text and url entities from "note_tweet"
function parseNoteTweet(result) {
let text, entities;
if(result.note_tweet.note_tweet_results.entity_set) {
entities = result.note_tweet.note_tweet_results.entity_set;
}
text = result.note_tweet.note_tweet_results.text;
if(typeof text !== "string") {
text = result.note_tweet.note_tweet_results.result.text;
entities = result.note_tweet.note_tweet_results.result.entity_set;
}
return {text, entities};
}
// transform ugly useless twitter api reply to usable legacy tweet
function parseTweet(res) {
if(typeof res !== "object") return;
if(res.limitedActionResults) {
let limitation = res.limitedActionResults.limited_actions.find(l => l.action === "Reply");
if(limitation) {
res.tweet.legacy.limited_actions_text = limitation.prompt ? limitation.prompt.subtext.text : LOC.limited_tweet.message;
}
res = res.tweet;
}
if(!res.legacy && res.tweet) res = res.tweet;
let tweet = res.legacy;
if(!res.core) return;
tweet.user = res.core.user_results.result.legacy;
tweet.user.id_str = tweet.user_id_str;
if(tweet.retweeted_status_result) {
let result = tweet.retweeted_status_result.result;
if(result.limitedActionResults) {
let limitation = result.limitedActionResults.limited_actions.find(l => l.action === "Reply");
if(limitation) {
result.tweet.legacy.limited_actions_text = limitation.prompt ? limitation.prompt.subtext.text : LOC.limited_tweet.message;
}
result = result.tweet;
}
if(
result.quoted_status_result &&
result.quoted_status_result.result.legacy &&
result.quoted_status_result.result.core &&
result.quoted_status_result.result.core.user_results.result.legacy
) {
result.legacy.quoted_status = result.quoted_status_result.result.legacy;
if(result.legacy.quoted_status) {
result.legacy.quoted_status.user = result.quoted_status_result.result.core.user_results.result.legacy;
result.legacy.quoted_status.user.id_str = result.legacy.quoted_status.user_id_str;
} else {
console.warn("No retweeted quoted status", result);
}
}
tweet.retweeted_status = result.legacy;
if(tweet.retweeted_status && result.core.user_results.result.legacy) {
tweet.retweeted_status.user = result.core.user_results.result.legacy;
tweet.retweeted_status.user.id_str = tweet.retweeted_status.user_id_str;
tweet.retweeted_status.ext = {};
if(result.views) {
tweet.retweeted_status.ext.views = {r: {ok: {count: +result.views.count}}};
}
} else {
console.warn("No retweeted status", result);
}
if(result.note_tweet && result.note_tweet.note_tweet_results) {
let note = parseNoteTweet(result);
tweet.retweeted_status.full_text = note.text;
tweet.retweeted_status.entities = note.entities;
tweet.retweeted_status.display_text_range = undefined; // no text range for long tweets
}
}
if(res.quoted_status_result) {
tweet.quoted_status_result = res.quoted_status_result;
}
if(res.note_tweet && res.note_tweet.note_tweet_results) {
let note = parseNoteTweet(res);
tweet.full_text = note.text;
tweet.entities = note.entities;
tweet.display_text_range = undefined; // no text range for long tweets
}
if(tweet.quoted_status_result) {
let result = tweet.quoted_status_result.result;
if(!result.core && result.tweet) result = result.tweet;
if(result.limitedActionResults) {
let limitation = result.limitedActionResults.limited_actions.find(l => l.action === "Reply");
if(limitation) {
result.tweet.legacy.limited_actions_text = limitation.prompt ? limitation.prompt.subtext.text : LOC.limited_tweet.message;
}
result = result.tweet;
}
tweet.quoted_status = result.legacy;
if(tweet.quoted_status) {
tweet.quoted_status.user = result.core.user_results.result.legacy;
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.ext = {};
if(result.views) {
tweet.quoted_status.ext.views = {r: {ok: {count: +result.views.count}}};
}
} else {
console.warn("No quoted status", result);
}
}
if(res.card && res.card.legacy) {
tweet.card = res.card.legacy;
let bvo = {};
for(let i = 0; i < tweet.card.binding_values.length; i++) {
let bv = tweet.card.binding_values[i];
bvo[bv.key] = bv.value;
}
tweet.card.binding_values = bvo;
}
if(res.views) {
if(!tweet.ext) tweet.ext = {};
tweet.ext.views = {r: {ok: {count: +res.views.count}}};
}
if(res.source) {
tweet.source = res.source;
}
if(res.birdwatch_pivot) { // community notes
tweet.birdwatch = res.birdwatch_pivot;
}
tweetStorage[tweet.id_str] = tweet;
return tweet;
}
function parseHomeTimeline(entries, data) {
let tweets = [];
for(let e of entries) {
// thats a lot of trash https://lune.dimden.dev/0bf524e52eb.png
if(e.entryId.startsWith('tweet-')) {
let res = e.content.itemContent.tweet_results.result;
let tweet = parseTweet(res);
if(!tweet) continue;
if(
tweet.source &&
(tweet.source.includes('Twitter for Advertisers') || tweet.source.includes('advertiser-interface'))
) continue;
if(tweet.user.blocking || tweet.user.muting) continue;
if(e.content.feedbackInfo) {
tweet.feedback = e.content.feedbackInfo.feedbackKeys.map(f => data.data.home.home_timeline_urt.responseObjects.feedbackActions.find(a => a.key === f).value).filter(f => f);
if(tweet.feedback) {
tweet.feedbackMetadata = e.content.feedbackInfo.feedbackMetadata;
}
}
if(e.content.itemContent.socialContext) {
if(e.content.itemContent.socialContext.topic) {
tweet.socialContext = e.content.itemContent.socialContext.topic;
} else {
tweet.socialContext = e.content.itemContent.socialContext;
}
}
tweet.hasModeratedReplies = e.content.itemContent.hasModeratedReplies;
tweets.push(tweet);
} else if(e.entryId.startsWith('home-conversation-')) {
let items = e.content.items;
for(let i = 0; i < items.length; i++) {
let item = items[i];
if(item.entryId.includes('-tweet-') && !item.entryId.includes('promoted')) {
let res = item.item.itemContent.tweet_results.result;
let tweet = parseTweet(res);
if(!tweet) continue;
if(
tweet.source &&
(tweet.source.includes('Twitter for Advertisers') || tweet.source.includes('advertiser-interface'))
) continue;
if(tweet.user.blocking || tweet.user.muting) break;
if(item.item.feedbackInfo) {
tweet.feedback = item.item.feedbackInfo.feedbackKeys.map(f => data.data.home.home_timeline_urt.responseObjects.feedbackActions.find(a => a.key === f).value).filter(f => f);
if(tweet.feedback) {
tweet.feedbackMetadata = item.item.feedbackInfo.feedbackMetadata;
}
}
if(item.item.itemContent.socialContext) {
if(item.item.itemContent.socialContext.topic) {
tweet.socialContext = item.item.itemContent.socialContext.topic;
} else {
tweet.socialContext = item.item.itemContent.socialContext;
}
}
if(i !== items.length - 1) tweet.threadContinuation = true;
if(i !== 0) tweet.noTop = true;
tweet.hasModeratedReplies = item.item.itemContent.hasModeratedReplies;
tweets.push(tweet);
}
}
}
}
return tweets;
}
const API = {
account: {
verifyCredentials: () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['credentials'], d => {
if(d.credentials && Date.now() - d.credentials.date < 15000) {
return resolve(d.credentials.data);
}
fetch(`https://api.twitter.com/1.1/account/verify_credentials.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(response => response.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
chrome.storage.local.remove(["lastUserId", "credentials", "inboxData", "tweetDetails", "savedSearches", "discoverData", "userUpdates", "peopleRecommendations", "tweetReplies", "tweetLikers", "listData", "twitterSettings", "algoTimeline"], () => {});
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({credentials: {
date: Date.now(),
data
}}, () => {});
chrome.storage.local.get(['lastUserId'], d => {
if(typeof d.lastUserId === 'string') {
if(d.lastUserId !== data.id_str) {
chrome.storage.local.remove(["credentials", "inboxData", "tweetDetails", "savedSearches", "discoverData", "userUpdates", "peopleRecommendations", "tweetReplies", "tweetLikers", "listData", "twitterSettings", "algoTimeline"], () => {
chrome.storage.local.set({lastUserId: data.id_str}, () => {
location.reload();
});
});
}
} else {
chrome.storage.local.set({lastUserId: data.id_str}, () => {});
}
});
}).catch(e => {
reject(e);
});
});
})
},
logout: () => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/account/logout.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include",
method: 'post',
body: 'redirectAfterLogout=https%3A%2F%2Ftwitter.com%2Faccount%2Fswitch'
}).then(i => i.json()).then(data => {
chrome.storage.local.remove(["credentials", "inboxData", "tweetDetails", "savedSearches", "discoverData", "userUpdates", "peopleRecommendations", "tweetReplies", "tweetLikers", "listData", "twitterSettings", "algoTimeline"], () => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
});
}).catch(e => {
reject(e);
});
});
},
getAccounts: (cache = true) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['accountsList'], d => {
if(cache && d.accountsList && Date.now() - d.accountsList.date < 60000*5) {
return resolve(d.accountsList.data);
}
fetch(`https://twitter.com/i/api/1.1/account/multi/list.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/",
"x-twitter-active-user": "yes",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({accountsList: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
switch: id => {
return new Promise((resolve, reject) => {
let status;
fetch(`https://twitter.com/i/api/1.1/account/multi/switch.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-active-user": "yes",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${id}`
}).then(i => {
status = i.status;
return i.text();
}).then(data => {
chrome.storage.local.remove(["credentials", "inboxData", "tweetDetails", "savedSearches", "discoverData", "userUpdates", "peopleRecommendations", "tweetReplies", "tweetLikers", "listData", "twitterSettings", "algoTimeline"], () => {
chrome.storage.local.set({lastUserId: id}, () => {
if(String(status).startsWith("2")) {
resolve(data);
} else {
reject(data);
}
});
});
}).catch(e => {
reject(e);
});
});
},
updateProfile: (data) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/account/update_profile.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include",
method: "post",
body: new URLSearchParams(data).toString()
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getSettings: () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['twitterSettings'], d => {
if(d.twitterSettings && Date.now() - d.twitterSettings.date < 60000*10) {
return resolve(d.twitterSettings.data);
}
fetch(`https://api.twitter.com/1.1/account/settings.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
setTimeout(() => {
location.href = "https://twitter.com/i/flow/login?newtwitter=true";
}, 1000);
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({twitterSettings: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
}
},
timeline: {
getChronological: (max_id) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/statuses/home_timeline.json?count=40&include_my_retweet=1&cards_platform=Web-12&include_cards=1&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified&include_reply_count=true${max_id ? `&max_id=${max_id}` : ''}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en",
"x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220811153004 web/"
},
credentials: "include"
}).then(response => response.json()).then(data => {
debugLog('timeline.getChronological', {max_id, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getChronologicalV2: (cursor, count = 40) => {
return new Promise((resolve, reject) => {
let variables = {
includePromotedContent: false,
latestControlAvailable: true,
count
};
if(cursor) {
variables.cursor = cursor;
}
fetch(`https://twitter.com/i/api/graphql/iMKdg5Vq-ldwmiqCbvX1QA/HomeLatestTimeline?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include"
}).then(response => response.json()).then(data => {
debugLog('timeline.getChronologicalV2', 'start', {cursor, count, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let instructions = data.data.home.home_timeline_urt.instructions;
let entries = instructions.find(i => i.type === 'TimelineAddEntries');
if(!entries) {
debugLog('timeline.getChronologicalV2', 'end', {list: [], cursor: undefined});
return resolve({
list: [],
cursor: undefined
});
}
entries = entries.entries;
let out = {
list: parseHomeTimeline(entries, data),
cursor: entries.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
}
debugLog('timeline.getChronologicalV2', 'end', {cursor, count, out});
return resolve(out);
}).catch(e => {
reject(e);
});
});
},
getAlgorithmical: (cursor, count = 40) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/timeline/home.json?${cursor ? `cursor=${cursor.replace(/\+/g, '%2B')}&` : ''}include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&earned=1&count=${count}&lca=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified&browserNotificationPermission=default`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(response => response.json()).then(data => {
debugLog('timeline.getAlgorithmical', 'start', {cursor, count, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let tweets = data.globalObjects.tweets;
let users = data.globalObjects.users;
let entries = data.timeline.instructions.find(i => i.addEntries);
if(!entries) {
return reject({
list: [],
cursor: undefined
});
}
entries = entries.addEntries.entries;
let list = [];
for (let i = 0; i < entries.length; i++) {
let e = entries[i].content.item;
if(!e || !e.content || !e.content.tweet) continue;
if(e.content.tweet.promotedMetadata && !vars.enableAd) continue;
let tweet = tweets[e.content.tweet.id];
if(!tweet) continue;
let user = users[tweet.user_id_str];
if(user.blocking || user.muting) continue;
tweet.user = user;
tweet.id_str = e.content.tweet.id;
if(
tweet.source &&
(tweet.source.includes('Twitter for Advertisers') || tweet.source.includes('advertiser-interface')) &&
!vars.enableAd
) continue;
if(e.feedbackInfo) {
tweet.feedback = e.feedbackInfo.feedbackKeys.map(f => data.timeline.responseObjects.feedbackActions[f]);
if(tweet.feedback) tweet.feedbackMetadata = e.feedbackInfo.feedbackMetadata;
}
if(tweet.retweeted_status_id_str) {
tweet.retweeted_status = tweets[tweet.retweeted_status_id_str];
if(tweet.retweeted_status) {
tweet.retweeted_status.user = users[tweet.retweeted_status.user_id_str];
tweet.retweeted_status.user.id_str = tweet.retweeted_status.user_id_str;
tweet.retweeted_status.id_str = tweet.retweeted_status_id_str;
}
}
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweets[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = users[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.id_str = tweet.quoted_status_id_str;
}
}
if(e.content.tweet.socialContext) {
if(e.content.tweet.socialContext.topicContext) {
tweet.socialContext = data.globalObjects.topics[e.content.tweet.socialContext.topicContext.topicId];
} else {
tweet.socialContext = e.content.tweet.socialContext.generalContext;
}
}
list.push(tweet);
}
let out = {
list,
cursor: entries.find(e => e.entryId.startsWith('cursor-bottom-')).content.operation.cursor.value
}
debugLog('timeline.getAlgorithmical', 'end', {cursor, count, out});
return resolve(out)
}).catch(e => {
reject(e);
});
});
},
getAlgorithmicalV2: (cursor, count = 40) => {
return new Promise((resolve, reject) => {
let variables = {
includePromotedContent: false,
latestControlAvailable: true,
count
};
if(cursor) {
variables.cursor = cursor;
}
fetch(`https://twitter.com/i/api/graphql/W4Tpu1uueTGK53paUgxF0Q/HomeTimeline?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include"
}).then(response => response.json()).then(data => {
debugLog('timeline.getAlgorithmicalV2', 'start', {cursor, count, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let instructions = data.data.home.home_timeline_urt.instructions;
let entries = instructions.find(i => i.type === 'TimelineAddEntries');
if(!entries) {
debugLog('timeline.getAlgorithmicalV2', 'end', {list: [], cursor: undefined});
return resolve({
list: [],
cursor: undefined
});
}
entries = entries.entries;
let out = {
list: parseHomeTimeline(entries, data),
cursor: entries.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
}
debugLog('timeline.getAlgorithmicalV2', 'end', {cursor, count, out});
return resolve(out);
}).catch(e => {
reject(e);
});
});
},
getAlgorithmicalV2WithCache: () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['algoTimeline'], d => {
if(d.algoTimeline && Date.now() - d.algoTimeline.date < 60000*3) {
debugLog('timeline.getAlgorithmicalV2WithCache', 'cache', d.algoTimeline.data);
return resolve(d.algoTimeline.data);
}
API.timeline.getAlgorithmicalV2().then(data => {
chrome.storage.local.set({
algoTimeline: {
date: Date.now(),
data
}
});
resolve(data);
}).catch(e => {
reject(e);
});
});
});
},
getMixed: async () => {
let [chrono, algo] = await Promise.allSettled([API.timeline.getChronologicalV2(), API.timeline.getAlgorithmicalV2WithCache()]);
debugLog('timeline.getMixed', 'start', {chrono, algo});
if(chrono.reason) {
throw chrono.reason;
}
chrono = chrono.value;
if(algo.reason) {
algo = [];
} else {
algo = algo.value.list;
}
let social = algo.filter(t => t.socialContext && (t.socialContext.contextType === 'Like' || t.socialContext.contextType === 'Follow'));
for(let i = chrono.list.length-1; i >= 0; i--) {
if(social.length === 0) break;
if(i % 7 === 0) {
if(chrono.list.map(t => t.id_str).includes(social[social.length-1].id_str)) {
social.pop();
} else {
chrono.list.splice(chrono.list.length-i, 0, social.pop());
}
}
}
debugLog('timeline.getMixed', 'end', chrono);
return chrono;
}
},
discover: {
getPeople: (cache = true) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['discoverData'], d => {
if(cache && d.discoverData && Date.now() - d.discoverData.date < 60000*10) {
debugLog('discover.getPeople', 'cache', d.discoverData.data)
return resolve(d.discoverData.data);
}
fetch(`https://twitter.com/i/api/2/people_discovery/modules_urt.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&count=20&display_location=connect&client_type=rweb&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerifiedhighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/",
"x-twitter-active-user": "yes",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('discover.getPeople', {cache, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({discoverData: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
getSimilarPeople: (id, cache = true, by_screen_name = false) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get([`peopleRecommendations`], d => {
if(!d.peopleRecommendations) d.peopleRecommendations = {};
if(cache && d.peopleRecommendations[`${id}${by_screen_name}`] && Date.now() - d.peopleRecommendations[`${id}${by_screen_name}`].date < 60000*7) {
debugLog('discover.getSimilarPeople', 'cache', d.peopleRecommendations[`${id}${by_screen_name}`].data);
return resolve(d.peopleRecommendations[`${id}${by_screen_name}`].data);
}
fetch(`https://twitter.com/i/api/1.1/users/recommendations.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&&pc=true&display_location=profile_accounts_sidebar&limit=4&${by_screen_name ? 'screen_name' : 'user_id'}=${id}&ext=mediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/",
"x-twitter-active-user": "yes",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('discover.getSimilarPeople', {id, cache, by_screen_name, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
d.peopleRecommendations[`${id}${by_screen_name}`] = {
date: Date.now(),
data
};
chrome.storage.local.set({peopleRecommendations: d.peopleRecommendations}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
getTrends: () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['trends'], d => {
if(d.trends && Date.now() - d.trends.date < 60000*5) {
debugLog('discover.getTrends', 'cache', d.trends.data);
return resolve(d.trends.data);
}
fetch(`https://api.twitter.com/1.1/trends/plus.json?max_trends=8`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
}).then(i => i.json()).then(data => {
debugLog('discover.getTrends', data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({trends: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
getTrendsV2: (cache) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['trendsv2'], d => {
if(d.trendsv2 && Date.now() - d.trendsv2.date < 60000*5 && cache) {
debugLog('discover.getTrendsV2', 'cache', d.trendsv2.data);
return resolve(d.trendsv2.data);
}
fetch(`
https://twitter.com/i/api/2/guide.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&include_ext_is_blue_verified=1&include_ext_verified_type=1&include_ext_profile_image_shape=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_ext_limited_action_results=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_views=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&count=20&requestContext=launch&candidate_source=trends&include_page_configuration=false&entity_tokens=false&ext=mediaStats%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2CbirdwatchPivot%2CsuperFollowMetadata%2CunmentionInfo%2CeditControl`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en",
"X-Twitter-Utcoffset": getTimeZone().replace(":", ""),
},
credentials: "include",
}).then(i => i.json()).then(d => {
debugLog('discover.getTrendsV2', 'start', {cache, data: d});
if (d.errors && d.errors[0]) {
return reject(d.errors[0].message);
}
let data = [];
let instructions = d.timeline.instructions;
let ae = instructions.find(i => i.addEntries);
if(!ae) return resolve([]);
let entries = ae.addEntries.entries;
let trends = entries.find(i => i.entryId === 'trends');
if(!trends) return resolve([]);
trends = trends.content.timelineModule.items;
trends.forEach(trend => {
if(!trend.item || !trend.item.content || !trend.item.content.trend) return;
let desc = trend.item.content.trend.trendMetadata.domainContext;
if(String(desc).includes("undefined")) {//maybe promotioned trends?
desc = ``;
if(trend.item.content.trend.trendMetadata.metaDescription) {
desc += trend.item.content.trend.trendMetadata.metaDescription;
}
}
else {
if(trend.item.content.trend.trendMetadata.metaDescription) {
desc += ` ยท ${trend.item.content.trend.trendMetadata.metaDescription}`;
}
}
data.push({trend:{
name: trend.item.content.trend.name,
meta_description: desc,
}})
});
debugLog('discover.getTrendsV2', 'end', {cache, data: {modules: data}});
resolve({modules: data});
chrome.storage.local.set({trendsv2: {
date: Date.now(),
data: {modules: data}
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
getHashflags: () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['hashflags'], d => {
if(d.hashflags && Date.now() - d.hashflags.date < 60000*60*4) {
return resolve(d.hashflags.data);
}
fetch(`https://twitter.com/i/api/1.1/hashflags.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({hashflags: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
getHashflagsV2: () => { // uses memory-caching for better performance
return new Promise((resolve, reject) => {
// check in memory first
if(hashflagStorage && Date.now() - hashflagStorage.date < 60000*60*4) {
return resolve(hashflagStorage.data);
}
// then in local storage
chrome.storage.local.get(['hashflags'], d => {
if(d.hashflags && Date.now() - d.hashflags.date < 60000*60*4) {
// copy to memory
hashflagStorage = d.hashflags;
return resolve(d.hashflags.data);
}
fetch(`https://twitter.com/i/api/1.1/hashflags.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
// save to both local storage and memory
chrome.storage.local.set({hashflags: {
date: Date.now(),
data
}}, () => {});
hashflagStorage = {
date: Date.now(),
data
};
}).catch(e => {
reject(e);
});
});
});
},
},
notifications: {
getUnreadCount: (cache = true) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['unreadCount'], d => {
if(cache && d.unreadCount && Date.now() - d.unreadCount.date < 18000) {
return resolve(d.unreadCount.data);
}
fetch(`https://twitter.com/i/api/2/badge_count/badge_count.json?supports_ntab_urt=1`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({unreadCount: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
get: (cursor, onlyMentions = false) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/notifications/${onlyMentions ? 'mentions' : 'all'}.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&count=50&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control${cursor ? `&cursor=${cursor}` : ''}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
}).then(i => i.json()).then(data => {
debugLog('notifications.get', {cursor, onlyMentions, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
markAsRead: cursor => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/notifications/all/last_seen_cursor.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded",
},
credentials: "include",
method: 'post',
body: `cursor=${cursor}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getDeviceFollowTweets: (cursor) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/notifications/device_follow.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_ext_limited_action_results=false&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&count=20&ext=mediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2CeditControl%2Ccollab_control%2Cvibe${cursor ? `&cursor=${cursor}` : ''}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
}).then(i => i.json()).then(data => {
debugLog('notifications.getDeviceFollowTweets', 'start', {cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let entries = data.timeline.instructions.find(i => i.addEntries).addEntries.entries;
let tweets = entries.filter(i => i.entryId.startsWith('tweet-')).map(i => data.globalObjects.tweets[i.content.item.content.tweet.id]);
for(let i in tweets) {
tweets[i].user = data.globalObjects.users[tweets[i].user_id_str];
if(tweets[i].quoted_status_id_str) {
tweets[i].quoted_status = data.globalObjects.tweets[tweets[i].quoted_status_id_str];
if(tweets[i].quoted_status) tweets[i].quoted_status.user = data.globalObjects.users[tweets[i].quoted_status.user_id_str];
}
if(tweets[i].retweeted_status_id_str) {
tweets[i].retweeted_status = data.globalObjects.tweets[tweets[i].retweeted_status_id_str];
if(tweets[i].retweeted_status) tweets[i].retweeted_status.user = data.globalObjects.users[tweets[i].retweeted_status.user_id_str];
}
}
let newCursor = entries.find(e => e.entryId.startsWith('cursor-bottom-'));
if(newCursor) {
newCursor = newCursor.content.operation.cursor.value;
}
let out = {
list: tweets,
cursor: newCursor
};
debugLog('notifications.getDeviceFollowTweets', 'end', out);
resolve(out)
}).catch(e => {
reject(e);
});
});
},
view: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/notifications/view/${id}.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_ext_limited_action_results=false&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&count=20&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2CeditControl%2Ccollab_control%2Cvibe`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
}).then(i => i.json()).then(data => {
debugLog('notifications.view', 'start', {id, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let entries = data.timeline.instructions.find(i => i.addEntries).addEntries.entries;
let tl = [];
for(let i in entries) {
let e = entries[i];
if(e.entryId.startsWith('tweet-')) {
let tweet = data.globalObjects.tweets[e.content.item.content.tweet.id];
tweet.user = data.globalObjects.users[tweet.user_id_str];
if(tweet.quoted_status_id_str) {
tweet.quoted_status = data.globalObjects.tweets[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = data.globalObjects.users[tweet.quoted_status.user_id_str];
}
}
if(tweet.retweeted_status_id_str) {
tweet.retweeted_status = data.globalObjects.tweets[tweet.retweeted_status_id_str];
if(tweet.retweeted_status) {
tweet.retweeted_status.user = data.globalObjects.users[tweet.retweeted_status.user_id_str];
}
}
tl.push({data: tweet, type: 'tweet'});
} else if(e.entryId.startsWith('main-tweet-')) {
let id = e.content.timelineModule.items[0].item.content.tweet.id;
let tweet = data.globalObjects.tweets[id];
tweet.user = data.globalObjects.users[tweet.user_id_str];
if(tweet.quoted_status_id_str) {
tweet.quoted_status = data.globalObjects.tweets[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = data.globalObjects.users[tweet.quoted_status.user_id_str];
}
}
if(tweet.retweeted_status_id_str) {
tweet.retweeted_status = data.globalObjects.tweets[tweet.retweeted_status_id_str];
if(tweet.retweeted_status) {
tweet.retweeted_status.user = data.globalObjects.users[tweet.retweeted_status.user_id_str];
}
}
tl.push({data: tweet, type: 'tweet'});
} else if(e.entryId.startsWith('user-')) {
let id = e.content.item.content.user.id;
let user = data.globalObjects.users[id];
tl.push({data: user, type: 'user'});
} else if(e.entryId.startsWith('main-user-')) {
let id = e.content.timelineModule.items[0].item.content.user.id;
let user = data.globalObjects.users[id];
tl.push({data: user, type: 'user'});
}
}
debugLog('notifications.view', 'end', tl);
resolve(tl);
}).catch(e => {
reject(e);
});
});
}
},
user: {
get: (val, byId = true) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/users/show.json?${byId ? `user_id=${val}` : `screen_name=${val}`}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": window.LANGUAGE ? window.LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => {
if(i.status === 401) {
setTimeout(() => {
location.href = `https://twitter.com/i/flow/login?newtwitter=true`;
}, 50);
}
return i.json();
}).then(data => {
debugLog('user.get', {val, byId, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getV2: name => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/sLVLhk0bGj3MVFEKTdax1w/UserByScreenName?variables=%7B%22screen_name%22%3A%22${name}%22%2C%22withSafetyModeUserFields%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%7D&features=${encodeURIComponent(JSON.stringify({"blue_business_profile_image_shape_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getV2', 'start', {name, data});
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let result = data.data.user.result;
result.legacy.id_str = result.rest_id;
if(result.legacy_extended_profile.birthdate) {
result.legacy.birthdate = result.legacy_extended_profile.birthdate;
}
if(result.professional) {
result.legacy.professional = result.professional;
}
if(result.affiliates_highlighted_label && result.affiliates_highlighted_label.label) {
result.legacy.affiliates_highlighted_label = result.affiliates_highlighted_label.label;
}
if(result.is_blue_verified && !result.legacy.verified_type) {
result.legacy.verified_type = "Blue";
}
debugLog('user.getV2', 'end', result.legacy);
resolve(result.legacy);
}).catch(e => {
reject(e);
});
});
},
follow: screen_name => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/friendships/create.json`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
body: `screen_name=${screen_name}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unfollow: screen_name => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/friendships/destroy.json`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
body: `screen_name=${screen_name}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
cancelFollowRequest: screen_name => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/cancel.json`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
body: `screen_name=${screen_name}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getTweets: (id, max_id, replies = false) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/statuses/user_timeline.json?count=100&exclude_replies=${!replies}&include_my_retweet=1&include_rts=1&user_id=${id}${max_id ? `&max_id=${max_id}` : ''}&cards_platform=Web-12&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getTweetsV2: (id, cursor, replies = false) => {
return new Promise((resolve, reject) => {
let variables = {"userId":id,"count":100,"includePromotedContent":false,"withQuickPromoteEligibilityTweetFields":false,"withVoice":true,"withV2Timeline":true};
let features = {"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false};
let fieldToggles = {"withArticleRichContentState":false};
if(cursor) {
variables.cursor = cursor;
}
let api = "QqZBEqganhHwmU9QscmIug/UserTweets";
if(replies) {
api = "wxoVeDnl0mP7VLhe6mTOdg/UserTweetsAndReplies";
}
fetch(`https://twitter.com/i/api/graphql/${api}?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify(features))}&fieldToggles=${encodeURIComponent(JSON.stringify(fieldToggles))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(r => {
r.text().then(data => {
try {
data = JSON.parse(data);
} catch(e) {
console.error(e, data);
if(String(e).includes("SyntaxError")) {
if(String(e).includes('Rate limit exceeded')) {
let rateLimitReset = r.headers.get('x-rate-limit-reset');
if(rateLimitReset) {
let date = new Date(+rateLimitReset * 1000);
let minutesLeft = Math.floor((date - Date.now()) / 1000 / 60);
reject(`Rate limit exceeded, try again in ${minutesLeft} minutes.`);
}
}
return reject(data);
} else {
return reject(e);
}
}
debugLog('user.getTweetsV2', 'start', data);
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let instructions = data.data.user.result.timeline_v2.timeline.instructions;
let entries = instructions.find(e => e.type === "TimelineAddEntries");
if(!entries) {
return reject("Nothing here");
}
entries = entries.entries;
let tweets = [];
for(let entry of entries) {
if(entry.entryId.startsWith("tweet-")) {
let result = entry.content.itemContent.tweet_results.result;
let tweet = parseTweet(result);
if(tweet) {
tweet.hasModeratedReplies = entry.content.itemContent.hasModeratedReplies;
if(replies) {
tweet.nonReply = true;
}
tweets.push(tweet);
}
} else if(entry.entryId.startsWith("profile-conversation-")) {
let items = entry.content.items;
for(let i = 0; i < items.length; i++) {
let item = items[i];
let result = item.item.itemContent.tweet_results.result;
if(item.entryId.includes("-tweet-")) {
let tweet = parseTweet(result);
if(!tweet) continue;
if(i !== items.length - 1) tweet.threadContinuation = true;
if(i !== 0) tweet.noTop = true;
tweet.hasModeratedReplies = item.item.itemContent.hasModeratedReplies;
tweets.push(tweet);
}
}
}
}
let pinEntry = instructions.find(e => e.type === "TimelinePinEntry");
let pinnedTweet;
if(pinEntry) {
let result = pinEntry.entry.content.itemContent.tweet_results.result;
pinnedTweet = parseTweet(result);
if(pinnedTweet) {
pinnedTweet.hasModeratedReplies = pinEntry.entry.content.itemContent.hasModeratedReplies;
}
}
const out = {
tweets,
pinnedTweet,
cursor: entries.find(e => e.entryId.startsWith("sq-cursor-bottom-") || e.entryId.startsWith("cursor-bottom-")).content.value
};
debugLog('user.getTweetsV2', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
});
},
getMediaTweets: (id, cursor) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/qJPOeW9Q8icdlpfnPhsqJQ/UserMedia?variables=${encodeURIComponent(JSON.stringify({"userId":id,"count":20,"cursor":cursor,"includePromotedContent":false,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withClientEventToken":false,"withBirdwatchNotes":false,"withVoice":true,"withV2Timeline":true}))}&features=${encodeURIComponent(JSON.stringify({"blue_business_profile_image_shape_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"longform_notetweets_richtext_consumption_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getMediaTweets', 'start', {id, cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let entries = data.data.user.result.timeline_v2.timeline.instructions.find(i => i.type === 'TimelineAddEntries').entries;
let tweets = entries.filter(i => i.entryId.startsWith('tweet-')).map(t => {
let tweet = parseTweet(t.content.itemContent.tweet_results.result);
if(tweet) {
tweet.hasModeratedReplies = t.content.itemContent.hasModeratedReplies;
}
return tweet;
}).filter(i => i);
let newCursor = entries.find(e => e.entryId.startsWith('cursor-bottom-'));
if(newCursor) {
newCursor = newCursor.content.value;
}
let out = {
tweets,
cursor: newCursor
};
debugLog('user.getMediaTweets', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
friendsFollowing: (val, by_id = true) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friends/following/list.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cursor=-1&${by_id ? `user_id=${val}` : `screen_name=${val}`}&count=10&with_total_count=true`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getRelationship: id => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/friendships/show.json?source_id=${id}&target_screen_name=JinjersTemple&cards_platform=Web-13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
receiveNotifications: (id, receive = false) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/update.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cursor=-1&id=${id}&device=${receive}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
block: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/blocks/create.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unblock: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/blocks/destroy.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
mute: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/mutes/users/create.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unmute: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/mutes/users/destroy.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
removeFollower: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/QpNfg0kpPRfjROQ_9eOLXA/RemoveFollower`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include",
method: 'post',
body: JSON.stringify({"variables":{"target_user_id":id},"queryId":"QpNfg0kpPRfjROQ_9eOLXA"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getFavorites: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {
"userId": id,
"count": 50,
"includePromotedContent": false,
"withSuperFollowsUserFields": true,
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false,
"withSuperFollowsTweetFields": true,
"withClientEventToken": false,
"withBirdwatchNotes": false,
"withVoice": true,
"withV2Timeline": true
};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/vni8vUvtZvJoIsl49VPudg/Likes?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({
"dont_mention_me_view_api_enabled": true,
"interactive_text_enabled": true,
"responsive_web_uc_gql_enabled": false,
"vibe_tweet_context_enabled": false,
"responsive_web_edit_tweet_api_enabled": false,
"standardized_nudges_misinfo": false,
"responsive_web_enhance_cards_enabled": false
}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getFavorites', 'start', {id, cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
if(!data.data.user.result.timeline_v2.timeline.instructions[0]) {
return resolve({
tl: [],
cursor: null
})
}
let out = {
tl: data.data.user.result.timeline_v2.timeline.instructions[0].entries
.filter(e => e.entryId.startsWith('tweet-') && e.content.itemContent.tweet_results.result)
.map(e => parseTweet(e.content.itemContent.tweet_results.result))
.filter(e => e),
cursor: data.data.user.result.timeline_v2.timeline.instructions[0].entries.find(e => e.entryId.startsWith('cursor-bottom')).content.value
};
debugLog('user.getFavorites', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getFollowing: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {
"userId": id,
"count": 100,
"includePromotedContent": false
};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/t-BPOrMIduGUJWO_LxcvNQ/Following?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getFollowing', 'start', {id, cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.user.result.timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries').entries;
const out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(e => {
let user = e.content.itemContent.user_results.result;
user.legacy.id_str = user.rest_id;
if(user.is_blue_verified && !user.legacy.verified_type) {
user.legacy.verified = true;
user.legacy.verified_type = "Blue";
}
return user.legacy;
}),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
}
debugLog('user.getFollowing', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getFollowers: (id, cursor, count = 100) => {
return new Promise((resolve, reject) => {
let obj = {
"userId": id,
"count": count,
"includePromotedContent": false,
"withSuperFollowsUserFields": true,
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false,
"withSuperFollowsTweetFields": true,
"withClientEventToken": false,
"withBirdwatchNotes": false,
"withVoice": true,
"withV2Timeline": true
};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/fJSopkDA3UP9priyce4RgQ/Followers?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({
"dont_mention_me_view_api_enabled": true,
"interactive_text_enabled": true,
"responsive_web_uc_gql_enabled": false,
"vibe_tweet_context_enabled": false,
"responsive_web_edit_tweet_api_enabled": false,
"standardized_nudges_misinfo": false,
"responsive_web_enhance_cards_enabled": false
}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getFollowers', 'start', {id, cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.user.result.timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries').entries;
const out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(e => {
let user = e.content.itemContent.user_results.result;
user.legacy.id_str = user.rest_id;
if(user.is_blue_verified && !user.legacy.verified_type) {
user.legacy.verified = true;
user.legacy.verified_type = "Blue";
}
return user.legacy;
}),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('user.getFollowers', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getFollowingIds: (cursor = -1, count = 5000) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/friends/ids.json?cursor=${cursor}&stringify_ids=true&count=${count}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getFollowersIds: (cursor = -1, count = 5000) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/followers/ids.json?cursor=${cursor}&stringify_ids=true&count=${count}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
lookup: ids => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/users/lookup.json?user_id=${ids.join(",")}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getFollowersYouFollow: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {
"userId": id,
"count": 50,
"includePromotedContent": false
};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/m8AXvuS9H0aAI09J3ISOrw/FollowersYouKnow?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('user.getFollowersYouFollow', 'start', {id, cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.user.result.timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries').entries;
const out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(e => {
let user = e.content.itemContent.user_results.result;
user.legacy.id_str = user.rest_id;
if(user.is_blue_verified && !user.legacy.verified_type) {
user.legacy.verified = true;
user.legacy.verified_type = "Blue";
}
return user.legacy;
}),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('user.getFollowersYouFollow', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
switchRetweetsVisibility: (user_id, see) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/update.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `id=${user_id}&retweets=${see}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getFollowRequests: (cursor = -1) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/incoming.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cursor=${cursor}&stringify_ids=true&count=100`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
acceptFollowRequest: user_id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/accept.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${user_id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
declineFollowRequest: user_id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/friendships/deny.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `user_id=${user_id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
translateBio: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/strato/column/None/profileUserId=${id},destinationLanguage=None,translationSource=Some(Google)/translation/service/translateProfile`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.profileTranslation);
}).catch(e => {
reject(e);
});
});
},
getLists: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/mLKOzzVOWUycBiExBT1gjg/CombinedLists?variables=${encodeURIComponent(JSON.stringify({"userId":id,"count":100,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true}))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":false,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(
data.data.user.result.timeline.timeline.instructions.find(i => i.entries).entries.filter(e => e.entryId.startsWith('list-')).map(e => e.content.itemContent.list)
);
}).catch(e => {
reject(e);
});
});
}
},
tweet: {
post: data => { // deprecated
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/statuses/update.json`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: new URLSearchParams(data).toString(),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
/*
text | tweet_text | status - tweet text
media | media_ids - media ids
card_uri - card uri
sensitive - sensitive media
in_reply_to_status_id | in_reply_to_tweet_id - reply to tweet id
exclude_reply_user_ids - exclude mentions
attachment_url - quote tweet url
circle - circle id
conversation_control - conversation control (follows | mentions)
*/
postV2: tweet => {
return new Promise((resolve, reject) => {
let text;
if(tweet.text) {
text = tweet.text;
} else if(tweet.tweet_text) {
text = tweet.tweet_text;
} else if(tweet.status) {
text = tweet.status;
} else {
text = "";
}
let variables = {
"tweet_text": text,
"media": {
"media_entities": [],
"possibly_sensitive": false
},
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false,
"withSuperFollowsTweetFields": true,
"withSuperFollowsUserFields": true,
"semantic_annotation_ids": [],
"dark_request": false
};
if(tweet.card_uri) {
variables.card_uri = tweet.card_uri;
}
if(tweet.media_ids) {
if(typeof tweet.media_ids === "string") {
tweet.media = tweet.media_ids.split(",");
} else {
tweet.media = tweet.media_ids;
}
}
if(tweet.media) {
variables.media.media_entities = tweet.media.map(i => ({media_id: i, tagged_users: []}));
if(tweet.sensitive) {
variables.media.possibly_sensitive = true;
}
}
if(tweet.conversation_control === 'follows') {
variables.conversation_control = { mode: 'Community' };
} else if(tweet.conversation_control === 'mentions') {
variables.conversation_control = { mode: 'ByInvitation' };
}
if(tweet.circle) {
variables.trusted_friends_control_options = { "trusted_friends_list_id": tweet.circle };
}
if(tweet.in_reply_to_status_id) {
tweet.in_reply_to_tweet_id = tweet.in_reply_to_status_id;
delete tweet.in_reply_to_status_id;
}
if(tweet.in_reply_to_tweet_id) {
variables.reply = {
in_reply_to_tweet_id: tweet.in_reply_to_tweet_id,
exclude_reply_user_ids: []
}
if(tweet.exclude_reply_user_ids) {
if(typeof tweet.exclude_reply_user_ids === "string") {
tweet.exclude_reply_user_ids = tweet.exclude_reply_user_ids.split(",");
}
variables.reply.exclude_reply_user_ids = tweet.exclude_reply_user_ids;
}
}
if(tweet.attachment_url) {
variables.attachment_url = tweet.attachment_url;
}
debugLog('tweet.postV2', 'init', {tweet, variables});
fetch(`https://twitter.com/i/api/graphql/tTsjMKyhajZvK4q76mpIBg/CreateTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({
variables,
"features": {"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_enhance_cards_enabled":false},
"fieldToggles":{"withArticleRichContentState":false,"withAuxiliaryUserLabels":false},
"queryId": "tTsjMKyhajZvK4q76mpIBg"
})
}).then(i => i.json()).then(data => {
debugLog('tweet.postV2', 'start', data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let result = data.data.create_tweet.tweet_results.result;
let tweet = result.legacy;
tweet.id_str = result.rest_id;
tweet.user = result.core.user_results.result.legacy;
tweet.user.id_str = result.core.user_results.result.rest_id;
if(result.card) {
tweet.card = result.card.legacy;
tweet.card.id_str = result.card.rest_id;
tweet.card.id = result.card.rest_id;
let binding_values = {};
for(let i in tweet.card.binding_values) {
let bv = tweet.card.binding_values[i];
binding_values[bv.key] = bv.value;
}
tweet.card.binding_values = binding_values;
}
debugLog('tweet.postV2', 'end', tweet);
resolve(tweet);
}).catch(e => {
reject(e);
});
});
},
postScheduled: data => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/LCVzRQGxOaGnOnYH01NQXg/CreateScheduledTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify(data)
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
favorite: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/lI07N6Otwv1PhnEgXILM7A/FavoriteTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweet_id":id},"queryId":"lI07N6Otwv1PhnEgXILM7A"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unfavorite: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/ZYKSe-w7KEslx3JhSIk5LA/UnfavoriteTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweet_id":id},"queryId":"ZYKSe-w7KEslx3JhSIk5LA"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
retweet: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/ojPdsZsimiJrUGLR1sjUtA/CreateRetweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweet_id":id,"dark_request":false},"queryId":"ojPdsZsimiJrUGLR1sjUtA"})
}).then(i => i.json()).then(data => {
debugLog('tweet.retweet', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unretweet: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/iQtK4dl5hBmXewYZuEOKVw/DeleteRetweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"source_tweet_id":id,"dark_request":false},"queryId":"iQtK4dl5hBmXewYZuEOKVw"})
}).then(i => i.json()).then(data => {
debugLog('tweet.unretweet', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
delete: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/VaenaVgh5q5ih7kvyVjgtg/DeleteTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweet_id":id,"dark_request":false},"queryId":"VaenaVgh5q5ih7kvyVjgtg"})
}).then(i => i.json()).then(data => {
debugLog('tweet.delete', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
get: id => { // deprecated
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/statuses/show.json?id=${id}&include_my_retweet=1&cards_platform=Web13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getV2: id => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['tweetDetails'], d => {
if(!d.tweetDetails) d.tweetDetails = {};
if(d.tweetDetails[id] && Date.now() - d.tweetDetails[id].date < 60000*3) {
debugLog('tweet.getV2', 'cache', id, d.tweetDetails[id].data);
return resolve(d.tweetDetails[id].data);
}
if(loadingDetails[id]) {
return loadingDetails[id].listeners.push([resolve, reject]);
} else {
loadingDetails[id] = {
listeners: []
};
}
fetch(`https://twitter.com/i/api/graphql/KwGBbJZc6DBx8EKmyQSP7g/TweetDetail?variables=${encodeURIComponent(JSON.stringify({
"focalTweetId":id,
"with_rux_injections":false,
"includePromotedContent":false,
"withCommunity":true,
"withQuickPromoteEligibilityTweetFields":true,
"withBirdwatchNotes":true,
"withVoice":true,
"withV2Timeline":true
}))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"blue_business_profile_image_shape_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('tweet.getV2', 'start', id, data);
if (data.errors && data.errors[0]) {
if(loadingDetails[id]) loadingDetails[id].listeners.forEach(l => l[1](data.errors[0].message));
delete loadingDetails[id];
return reject(data.errors[0].message);
}
let ic = data.data.threaded_conversation_with_injections_v2.instructions.find(i => i.type === "TimelineAddEntries").entries.find(e => e.entryId === `tweet-${id}`).content.itemContent;
let res = ic.tweet_results.result;
let tweet = parseTweet(res);
if(tweet) {
tweet.hasModeratedReplies = ic.hasModeratedReplies;
}
debugLog('tweet.getV2', 'end', id, tweet);
resolve(tweet);
if(loadingDetails[id]) loadingDetails[id].listeners.forEach(l => l[0](tweet));
delete loadingDetails[id];
chrome.storage.local.get(['tweetDetails'], d => {
if(!d.tweetDetails) d.tweetDetails = {};
d.tweetDetails[id] = {
date: Date.now(),
data: tweet
};
chrome.storage.local.set({tweetDetails: d.tweetDetails}, () => {});
});
}).catch(e => {
reject(e);
});
});
});
},
vote: (api, tweet_id, card_uri, card_name, selected_choice) => {
return new Promise((resolve, reject) => {
fetch(`https://caps.twitter.com/v2/capi/${api.split('//')[1]}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `twitter%3Astring%3Acard_uri=${encodeURIComponent(card_uri)}&twitter%3Along%3Aoriginal_tweet_id=${tweet_id}&twitter%3Astring%3Aresponse_card_name=${card_name}&twitter%3Astring%3Acards_platform=Web-12&twitter%3Astring%3Aselected_choice=${selected_choice}`
}).then(response => response.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
})
},
createCard: card_data => {
return new Promise((resolve, reject) => {
fetch(`https://caps.twitter.com/v2/cards/create.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `card_data=${encodeURIComponent(JSON.stringify(card_data))}`
}).then(response => response.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
})
},
getReplies: (id, cursor) => { // deprecated
return new Promise((resolve, reject) => {
if(cursor) {
cursor = cursor.replace(/\+/g, '%2B');
}
chrome.storage.local.get(['tweetReplies'], async d => {
if(!d.tweetReplies) d.tweetReplies = {};
if(!cursor) {
if(d.tweetReplies[id] && Date.now() - d.tweetReplies[id].date < 60000) {
return resolve(d.tweetReplies[id].data);
}
if(loadingReplies[id]) {
return loadingReplies[id].listeners.push([resolve, reject]);
} else {
loadingReplies[id] = {
listeners: []
};
}
}
fetch(`https://api.twitter.com/2/timeline/conversation/${id}.json?${cursor ? `cursor=${cursor}`: ''}&count=30&include_reply_count=true&cards_platform=Web-13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified%2CnoteTweet`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.text()).then(data => {
try {
data = JSON.parse(data);
} catch(e) {
if(String(e).includes("SyntaxError")) {
return reject(data);
} else {
return reject(e);
}
}
if (data.errors && data.errors[0].code === 32) {
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[1]('Not logged in'));
delete loadingReplies[id];
}
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[1](data.errors[0].message));
delete loadingReplies[id];
}
return reject(data.errors[0].message);
}
let tweetData = data.globalObjects.tweets;
let userData = data.globalObjects.users;
let ae = data.timeline.instructions.find(i => i.addEntries);
if(!ae) {
let out = {
list: [],
cursor: null,
users: userData
};
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[0](out));
delete loadingReplies[id];
}
return resolve(out);
}
let entries = ae.addEntries.entries;
let list = [];
for (let i = 0; i < entries.length; i++) {
let e = entries[i];
if (e.entryId.startsWith('tweet-')) {
let tweet = tweetData[e.content.item.content.tweet.id];
if(!tweet) continue;
let user = userData[tweet.user_id_str];
tweet.id_str = e.content.item.content.tweet.id;
tweet.user = user;
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweetData[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = userData[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.id_str = tweet.quoted_status_id_str;
}
}
list.push({
type: tweet.id_str === id ? 'mainTweet' : 'tweet',
data: tweet
});
} else if (e.entryId.startsWith('tombstone-')) {
if(e.content.item.content.tombstone.tweet) {
let tweet = tweetData[e.content.item.content.tombstone.tweet.id];
let user = userData[tweet.user_id_str];
tweet.id_str = e.content.item.content.tombstone.tweet.id;
tweet.user = user;
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweetData[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = userData[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.id_str = tweet.quoted_status_id_str;
}
}
tweet.tombstone = e.content.item.content.tombstone.tombstoneInfo.text;
list.push({
type: tweet.id_str === id ? 'mainTweet' : 'tweet',
data: tweet
});
} else {
list.push({
type: 'tombstone',
data: e.content.item.content.tombstone.tombstoneInfo.text
});
}
} else if(e.entryId.startsWith('conversationThread-')) {
let thread = e.content.item.content.conversationThread.conversationComponents.filter(c => c.conversationTweetComponent);
let threadList = [];
for (let j = 0; j < thread.length; j++) {
let t = thread[j];
let tweet = tweetData[t.conversationTweetComponent.tweet.id];
if(!tweet) continue;
let user = userData[tweet.user_id_str];
tweet.id_str = t.conversationTweetComponent.tweet.id;
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweetData[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = userData[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.id_str = tweet.quoted_status_id_str;
}
}
tweet.user = user;
threadList.push(tweet);
}
if(threadList.length === 1) {
list.push({
type: threadList[0].id_str === id ? 'mainTweet' : 'tweet',
data: threadList[0]
});
} else {
list.push({
type: 'conversation',
data: threadList
});
}
} else if(e.entryId.startsWith('cursor-showmorethreadsprompt')) {
list.push({
type: 'showMore',
data: {
cursor: e.content.itemContent.value,
labelText: e.content.itemContent.displayTreatment.labelText,
actionText: e.content.itemContent.displayTreatment.actionText
}
});
}
}
let newCursor;
try {
newCursor = entries.find(e => e.entryId.startsWith('cursor-bottom-')).content.operation.cursor.value;
} catch(e) {}
resolve({
list,
cursor: newCursor,
users: userData
});
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[0]({
list,
cursor: newCursor,
users: userData
}));
delete loadingReplies[id];
chrome.storage.local.get(['tweetReplies'], d => {
if(!d.tweetReplies) d.tweetReplies = {};
d.tweetReplies[id] = {
date: Date.now(),
data: {
list,
cursor: newCursor,
users: userData
}
};
chrome.storage.local.set({tweetReplies: d.tweetReplies}, () => {});
});
}
}).catch(e => {
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[1](e));
delete loadingReplies[id];
}
reject(e);
});
});
});
},
getRepliesV2: (id, cursor) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['tweetReplies'], d => {
if(!d.tweetReplies) d.tweetReplies = {};
if(!cursor) {
if(d.tweetReplies[id] && Date.now() - d.tweetReplies[id].date < 60000) {
debugLog('tweet.getRepliesV2', 'cache', d.tweetReplies[id].data);
return resolve(d.tweetReplies[id].data);
}
if(loadingReplies[id]) {
return loadingReplies[id].listeners.push([resolve, reject]);
} else {
loadingReplies[id] = {
listeners: []
};
}
}
fetch(`https://twitter.com/i/api/graphql/KwGBbJZc6DBx8EKmyQSP7g/TweetDetail?variables=${encodeURIComponent(JSON.stringify({
"focalTweetId":id,
"with_rux_injections":false,
"includePromotedContent":false,
"withCommunity":true,
"withQuickPromoteEligibilityTweetFields":true,
"withBirdwatchNotes":true,
"withVoice":true,
"withV2Timeline":true,
"cursor":cursor
}))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"blue_business_profile_image_shape_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('tweet.getRepliesV2', 'start', data);
if (data.errors && data.errors[0]) {
if(loadingReplies[id]) loadingReplies[id].listeners.forEach(l => l[1](data.errors[0].message));
delete loadingReplies[id];
return reject(data.errors[0].message);
}
let ae = data.data.threaded_conversation_with_injections_v2.instructions.find(i => i.entries);
if(!ae) {
let out = {
list: [],
cursor: null,
users: {}
};
if(!cursor) {
if(loadingReplies[id]) loadingReplies[id].listeners.forEach(l => l[0](out));
delete loadingReplies[id];
}
return resolve(out);
}
let entries = ae.entries;
let list = [];
let users = {};
for (let i = 0; i < entries.length; i++) {
let e = entries[i];
if (e.entryId.startsWith('tweet-')) {
if(e.content && e.content.itemContent && e.content.itemContent.promotedMetadata) continue;
let tweetData = e.content.itemContent.tweet_results.result;
if(!tweetData) continue;
if(tweetData.tombstone) {
let text = tweetData.tombstone.text.text;
if(tweetData.tombstone.text.entities && tweetData.tombstone.text.entities.length > 0) {
let en = tweetData.tombstone.text.entities[0];
text = text.slice(0, en.fromIndex) + `` + text.slice(en.fromIndex, en.toIndex) + "" + text.slice(en.toIndex);
}
let tombstoneTweetId = e.entryId.slice(6);
let replyTweet = entries.find(i =>
i && i.content && i.content.itemContent &&
i.content.itemContent.tweet_results &&
i.content.itemContent.tweet_results.result &&
i.content.itemContent.tweet_results.result.legacy &&
i.content.itemContent.tweet_results.result.legacy.in_reply_to_status_id_str == tombstoneTweetId
);
list.push({
type: 'tombstone',
data: text,
replyTweet
});
continue;
}
let tweet = parseTweet(tweetData);
if(tweet) {
tweet.hasModeratedReplies = e.content.itemContent.hasModeratedReplies;
list.push({
type: tweet.id_str === id ? 'mainTweet' : 'tweet',
data: tweet
});
}
} else if (e.entryId.startsWith('tombstone-')) {
if(e.content.item.content.tombstone.tweet) {
let tweet = tweetData[e.content.item.content.tombstone.tweet.id];
let user = userData[tweet.user_id_str];
tweet.id_str = e.content.item.content.tombstone.tweet.id;
tweet.user = user;
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweetData[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = userData[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.quoted_status.id_str = tweet.quoted_status_id_str;
}
}
tweet.tombstone = e.content.item.content.tombstone.tombstoneInfo.text;
list.push({
type: tweet.id_str === id ? 'mainTweet' : 'tweet',
data: tweet
});
} else {
list.push({
type: 'tombstone',
data: e.content.item.content.tombstone.tombstoneInfo.text
});
}
} else if(e.entryId.startsWith('conversationthread-')) {
let thread = e.content.items;
let threadList = [];
for (let j = 0; j < thread.length; j++) {
if(thread[j].entryId.includes("-tweetcomposer-")) {
continue;
}
if(thread[j].entryId.includes("cursor-showmore")) {
list.push({
type: 'showMoreMiddle',
data: {
cursor: thread[j].item.itemContent.value,
labelText: thread[j].item.itemContent.displayTreatment.labelText,
actionText: thread[j].item.itemContent.displayTreatment.actionText
}
});
continue;
}
let ic = thread[j].item.itemContent;
if(ic.promotedMetadata) continue;
if(ic.tombstoneInfo) {
let richText = ic.tombstoneInfo.richText;
let text = richText.text;
if(richText.entities && richText.entities.length > 0) {
let en = richText.entities[0];
text = text.slice(0, en.fromIndex) + `` + text.slice(en.fromIndex, en.toIndex) + "" + text.slice(en.toIndex);
}
list.push({
type: 'tombstone',
data: text
});
continue;
}
let tweetData = ic.tweet_results.result;
if(!tweetData) continue;
if(tweetData.tombstone) {
let text = tweetData.tombstone.text.text;
if(tweetData.tombstone.text.entities && tweetData.tombstone.text.entities.length > 0) {
let en = tweetData.tombstone.text.entities[0];
text = text.slice(0, en.fromIndex) + `` + text.slice(en.fromIndex, en.toIndex) + "" + text.slice(en.toIndex);
}
list.push({
type: 'tombstone',
data: text
});
continue;
}
let tweet = parseTweet(tweetData);
if(tweet) {
tweet.hasModeratedReplies = ic.hasModeratedReplies;
threadList.push(tweet);
}
}
if(threadList.length === 1) {
list.push({
type: threadList[0].id_str === id ? 'mainTweet' : 'tweet',
data: threadList[0]
});
} else {
list.push({
type: 'conversation',
data: threadList
});
}
} else if(e.entryId.startsWith('cursor-showmorethreadsprompt') || e.entryId.startsWith('cursor-showmorethreads-')) {
list.push({
type: 'showMore',
data: {
cursor: e.content.itemContent.value,
labelText: e.content.itemContent.displayTreatment.labelText,
actionText: e.content.itemContent.displayTreatment.actionText
}
});
}
}
let newCursor;
try {
newCursor = entries.find(e => e.entryId.startsWith('cursor-bottom-')).content.itemContent.value;
} catch(e) {};
const out = {
list,
cursor: newCursor,
users
};
debugLog('tweet.getRepliesV2', 'end', out);
resolve(out);
if(!cursor) {
loadingReplies[id].listeners.forEach(l => l[0]({
list,
cursor: newCursor,
users
}));
delete loadingReplies[id];
chrome.storage.local.get(['tweetReplies'], d => {
if(!d.tweetReplies) d.tweetReplies = {};
d.tweetReplies[id] = {
date: Date.now(),
data: {
list,
cursor: newCursor,
users
}
};
chrome.storage.local.set({tweetReplies: d.tweetReplies}, () => {});
});
}
}).catch(e => {
reject(e);
});
});
});
},
getLikers: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {
"tweetId": id,
"count": 10,
"includePromotedContent": false,
"withSuperFollowsUserFields": true,
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false,
"withSuperFollowsTweetFields": true,
"withClientEventToken": false,
"withBirdwatchNotes": false,
"withVoice": true,
"withV2Timeline": true
};
if(cursor) obj.cursor = cursor;
chrome.storage.local.get(['tweetLikers'], d => {
if(!cursor) cursor = '';
if(!d.tweetLikers) d.tweetLikers = {};
if(!cursor) {
if(d.tweetLikers[id] && Date.now() - d.tweetLikers[id].date < 60000) {
debugLog('tweet.getLikers', 'cache', d.tweetLikers[id].data);
return resolve(d.tweetLikers[id].data);
}
if(loadingLikers[id]) {
return loadingLikers[id].listeners.push([resolve, reject]);
} else {
loadingLikers[id] = {
listeners: []
};
}
}
fetch(`https://twitter.com/i/api/graphql/RMoTahkos95Jcdw-UWlZog/Favoriters?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({
"dont_mention_me_view_api_enabled": true,
"interactive_text_enabled": true,
"responsive_web_uc_gql_enabled": false,
"vibe_tweet_context_enabled": false,
"responsive_web_edit_tweet_api_enabled": false,
"standardized_nudges_misinfo": false,
"responsive_web_enhance_cards_enabled": false
}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('tweet.getLikers', 'start', { id, cursor, data });
if (data.errors && data.errors[0].code === 32) {
if(!cursor) {
loadingLikers[id].listeners.forEach(l => l[1]('Not logged in'));
delete loadingLikers[id];
}
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
if(!cursor) {
loadingLikers[id].listeners.forEach(l => l[1](data.errors[0].message));
delete loadingLikers[id];
}
return reject(data.errors[0].message);
}
let list = data.data.favoriters_timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) {
if(!cursor) {
loadingLikers[id].listeners.forEach(l => l[0]({ list: [], cursor: undefined }));
delete loadingLikers[id];
}
return resolve({ list: [], cursor: undefined });
}
list = list.entries;
let rdata = {
list: list.filter(e => e.entryId.startsWith('user-')).map(e => {
if(e.content.itemContent.user_results.result.__typename === "UserUnavailable") return;
let user = e.content.itemContent.user_results.result;
user.legacy.id_str = user.rest_id;
return user.legacy;
}).filter(u => u),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('tweet.getLikers', 'end', id, rdata);
resolve(rdata);
if(!cursor) {
loadingLikers[id].listeners.forEach(l => l[0](rdata));
delete loadingLikers[id];
d.tweetLikers[id] = {
date: Date.now(),
data: rdata
};
chrome.storage.local.set({tweetLikers: d.tweetLikers}, () => {});
}
}).catch(e => {
if(!cursor) {
loadingLikers[id].listeners.forEach(l => l[1](e));
delete loadingLikers[id];
}
reject(e);
});
});
});
},
getRetweeters: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {
"tweetId": id,
"count": 50,
"includePromotedContent": false,
"withSuperFollowsUserFields": true,
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false,
"withSuperFollowsTweetFields": true,
"withClientEventToken": false,
"withBirdwatchNotes": false,
"withVoice": true,
"withV2Timeline": true
};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/qVWT1Tn1FiklyVDqYiOhLg/Retweeters?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({
"dont_mention_me_view_api_enabled": true,
"interactive_text_enabled": true,
"responsive_web_uc_gql_enabled": false,
"vibe_tweet_context_enabled": false,
"responsive_web_edit_tweet_api_enabled": false,
"standardized_nudges_misinfo": false,
"responsive_web_enhance_cards_enabled": false
}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('tweet.getRetweeters', 'start', { id, cursor, data });
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.retweeters_timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) return resolve({ list: [], cursor: undefined });
list = list.entries;
let out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(e => {
if(e.content.itemContent.user_results.result.__typename === "UserUnavailable") return;
let user = e.content.itemContent.user_results.result;
user.legacy.id_str = user.rest_id;
return user.legacy;
}).filter(u => u),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('tweet.getRetweeters', 'end', id, out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getQuotes: (id, cursor) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/search/adaptive.json?${cursor ? `cursor=${cursor}&` : ''}include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&q=quoted_tweet_id%3A${id}&vertical=tweet_detail_quote&count=40&pc=1&spelling_corrections=1&include_ext_edit_control=false&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
}).then(i => i.json()).then(data => {
debugLog('tweet.getQuotes', 'start', { id, cursor, data });
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let tweets = data.globalObjects.tweets;
let users = data.globalObjects.users;
let entries = data.timeline.instructions.find(i => i.addEntries);
if(!entries) return resolve({
list: [],
cursor: undefined
});
entries = entries.addEntries.entries;
let list = entries.filter(e => e.entryId.startsWith('sq-I-t-') || e.entryId.startsWith('tweet-'));
let newCursor = entries.find(e => e.entryId.startsWith('sq-cursor-bottom') || e.entryId.startsWith('cursor-bottom'));
if(!newCursor) {
let entries = data.timeline.instructions.find(i => i.replaceEntry && (i.replaceEntry.entryIdToReplace.includes('sq-cursor-bottom') || i.replaceEntry.entryIdToReplace.includes('cursor-bottom')));
if(entries) {
newCursor = entries.replaceEntry.entry.content.operation.cursor.value;
}
} else {
newCursor = newCursor.content.operation.cursor.value;
}
let out = {
list: list.map(e => {
let tweet = tweets[e.content.item.content.tweet.id];
let user = users[tweet.user_id_str];
user.id_str = tweet.user_id_str;
tweet.quoted_status = tweets[tweet.quoted_status_id_str];
tweet.quoted_status.user = users[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
tweet.user = user;
return tweet;
}),
cursor: newCursor
};
debugLog('tweet.getQuotes', 'end', id, out);
return resolve(out);
}).catch(e => {
reject(e);
});
});
},
mute: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/mutes/conversations/create.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `tweet_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unmute: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/mutes/conversations/destroy.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: 'post',
body: `tweet_id=${id}`
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
lookup: ids => { // deprecated
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/statuses/lookup.json?id=${ids.join(',')}&include_entities=true&include_ext_alt_text=true&include_card_uri=true&tweet_mode=extended&include_reply_count=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
translate: id => {
return new Promise((resolve, reject) => {
chrome.storage.local.get([`translations`], d => {
if(!d.translations) d.translations = {};
if(d.translations[id] && Date.now() - d.translations[id].date < 60000*60*4) {
return resolve(d.translations[id].data);
}
fetch(`https://twitter.com/i/api/1.1/strato/column/None/tweetId=${id},destinationLanguage=None,translationSource=Some(Google),feature=None,timeout=None,onlyCached=None/translation/service/translateTweet`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve({
translated_lang: data.localizedSourceLanguage,
text: data.translation,
entities: data.entities
});
d.translations[id] = {
date: Date.now(),
data: {
translated_lang: data.localizedSourceLanguage,
text: data.translation,
entities: data.entities
}
};
chrome.storage.local.set({translations: d.translations}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
pin: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/account/pin_tweet.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
body: `id=${id}`
}).then(i => i.text()).then(data => {
resolve(true);
}).catch(e => {
reject(e);
});
});
},
unpin: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/account/unpin_tweet.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
body: `id=${id}`
}).then(i => i.text()).then(data => {
resolve(true);
}).catch(e => {
reject(e);
});
});
},
moderate: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/pjFnHGVqCjTcZol0xcBJjw/ModerateTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweetId":id},"queryId":"pjFnHGVqCjTcZol0xcBJjw"})
}).then(i => i.json()).then(data => {
debugLog('tweet.moderate', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
unmoderate: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/pVSyu6PA57TLvIE4nN2tsA/UnmoderateTweet`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json; charset=utf-8"
},
credentials: "include",
body: JSON.stringify({"variables":{"tweetId":"1683331680751308802"},"queryId":"pVSyu6PA57TLvIE4nN2tsA"})
}).then(i => i.json()).then(data => {
debugLog('tweet.unmoderate', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getModeratedReplies: (id, cursor) => {
return new Promise((resolve, reject) => {
let variables = {"rootTweetId":id,"count":20,"includePromotedContent":false};
if(cursor) variables.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/SiKS1_3937rb72ytFnDHmA/ModeratedTimeline?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
method: 'POST',
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('tweet.getModeratedReplies', 'start', id, data);
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let entries = data.data.tweet.result.timeline_response.timeline.instructions.find(i => i.entries);
if(!entries) return resolve({
list: [],
cursor: undefined
});
entries = entries.entries;
let list = entries.filter(e => e.entryId.startsWith('tweet-'));
let cursor = entries.find(e => e.entryId.startsWith('cursor-bottom'));
if(!cursor) {
let entries = data.data.tweet.result.timeline_response.timeline.instructions.find(i => i.replaceEntry && i.replaceEntry.entryIdToReplace.includes('cursor-bottom'));
if(entries) {
cursor = entries.replaceEntry.entry.content.operation.cursor.value;
}
} else {
cursor = cursor.content.operation.cursor.value;
}
let out = {
list: list.map(e => {
let tweet = parseTweet(e.content.itemContent.tweet_results.result);
if(!tweet) return;
tweet.moderated = true;
return tweet;
}).filter(e => e),
cursor
};
debugLog('tweet.getModeratedReplies', 'end', id, out);
resolve(data);
}).catch(e => {
reject(e);
});
});
}
},
search: {
typeahead: query => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/search/typeahead.json?q=${encodeURIComponent(query)}&include_can_dm=1&count=5&prefetch=false&cards_platform=Web-13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
adaptive: (obj, cursor) => { // deprecated
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/2/search/adaptive.json?${cursor ? `cursor=${cursor}&` : ''}${obj.tweet_search_mode ? `tweet_search_mode=${obj.tweet_search_mode}&` : ''}include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&include_ext_trusted_friends_metadata=true&send_error_codes=true&simple_quoted_tweet=true&q=${obj.q}${obj.social_filter ? `&social_filter=${obj.social_filter}`:''}${obj.result_filter ? `&result_filter=${obj.result_filter}`:''}&count=50&query_source=typed_query&pc=1&spelling_corrections=1&include_ext_edit_control=false&ext=views%2CmediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let tweets = data.globalObjects.tweets;
let users = data.globalObjects.users;
let entries = data.timeline.instructions.find(i => i.addEntries);
if(!entries) return resolve({
list: [],
cursor: undefined
});
entries = entries.addEntries.entries;
let list = entries.filter(e => e.entryId.startsWith('sq-I-t-') || e.entryId.startsWith('user-') || e.entryId.startsWith('tweet-'));
let cursor = entries.find(e => e.entryId.startsWith('sq-cursor-bottom') || e.entryId.startsWith('cursor-bottom'));
if(!cursor) {
let entries = data.timeline.instructions.find(i => i.replaceEntry && (i.replaceEntry.entryIdToReplace.includes('sq-cursor-bottom') || i.replaceEntry.entryIdToReplace.includes('cursor-bottom')));
if(entries) {
cursor = entries.replaceEntry.entry.content.operation.cursor.value;
}
} else {
cursor = cursor.content.operation.cursor.value;
}
return resolve({
list: list.map(e => {
if(e.entryId.startsWith('sq-I-t-') || e.entryId.startsWith('tweet-')) {
let tweet = tweets[e.content.item.content.tweet.id];
let user = users[tweet.user_id_str];
user.id_str = tweet.user_id_str;
if(
tweet && tweet.source &&
(tweet.source.includes('Twitter for Advertisers') || tweet.source.includes('advertiser-interface'))
) return;
if(tweet.quoted_status_id_str) {
tweet.quoted_status = tweets[tweet.quoted_status_id_str];
if(tweet.quoted_status) {
tweet.quoted_status.user = users[tweet.quoted_status.user_id_str];
tweet.quoted_status.user.id_str = tweet.quoted_status.user_id_str;
}
}
if(tweet.retweeted_status_id_str) {
tweet.retweeted_status = tweets[tweet.retweeted_status_id_str];
tweet.retweeted_status.user = users[tweet.retweeted_status.user_id_str];
tweet.retweeted_status.user.id_str = tweet.retweeted_status.user_id_str;
tweet.retweeted_status.id_str = tweet.retweeted_status_id_str;
}
tweet.user = user;
tweet.type = 'tweet';
return tweet;
} else if(e.entryId.startsWith('user-')) {
let user = users[e.content.item.content.user.id];
user.id_str = e.content.item.content.user.id;
user.type = 'user';
return user;
} else {
return e;
}
}).filter(e => e),
cursor
});
}).catch(e => {
reject(e);
});
});
},
adaptiveV2: (obj, cursor) => {
return new Promise((resolve, reject) => {
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/nK1dw4oV3k4w5TdtcAdSww/SearchTimeline?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let instructions = data.data.search_by_raw_query.search_timeline.timeline.instructions;
let entries = instructions.find(i => i.entries);
if(!entries) {
return resolve([]);
}
entries = entries.entries;
let res = [];
for(let entry of entries) {
if(entry.entryId.startsWith('sq-I-t-') || entry.entryId.startsWith('tweet-')) {
let result = entry.content.itemContent.tweet_results.result;
if(entry.content.itemContent.promotedMetadata) {
continue;
}
let tweet = parseTweet(result);
if(!tweet) {
continue;
}
tweet.type = 'tweet';
res.push(tweet);
} else if(entry.entryId.startsWith('sq-I-u-') || entry.entryId.startsWith("user-")) {
let result = entry.content.itemContent.user_results.result;
if(!result || !result.legacy) {
console.log("Bug: no user", entry);
continue;
}
let user = result.legacy;
user.id_str = result.rest_id;
user.type = 'user';
user.socialContext = entry.content.itemContent.socialContext;
res.push(user);
}
}
let cursor = entries.find(e => e.entryId.startsWith('sq-cursor-bottom-') || e.entryId.startsWith('cursor-bottom-'));
if(cursor) {
cursor = cursor.content.value;
} else {
cursor = instructions.find(e => e.entry_id_to_replace && (e.entry_id_to_replace.startsWith('sq-cursor-bottom-') || e.entry_id_to_replace.startsWith('cursor-bottom-')));
if(cursor) {
cursor = cursor.entry.content.value;
} else {
cursor = null;
}
}
resolve({list: res, cursor});
});
});
},
getSaved: (cache = true) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['savedSearches'], d => {
if(cache && d.savedSearches && Date.now() - d.savedSearches.date < 60000) {
return resolve(d.savedSearches.data);
}
fetch(`https://twitter.com/i/api/1.1/saved_searches/list.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
chrome.storage.local.set({savedSearches: {
date: Date.now(),
data
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
deleteSaved: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/saved_searches/destroy/${id}.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: "post",
body: "id=" + id
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
save: q => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/saved_searches/create.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded"
},
credentials: "include",
method: "post",
body: "q=" + encodeURIComponent(q)
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
trustedFriendsTypeahead: (circle_id, query) => {
return new Promise((resolve, reject) => {
let variables = {"trustedFriendsId": circle_id, "prefix": query};
fetch(`https://twitter.com/i/api/graphql/4lk-D0Y8kfimSyPJjEocsA/TrustedFriendsTypeahead?variables=${encodeURIComponent(JSON.stringify(variables))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.data.trusted_friends_list_by_rest_id.recommended_members_typeahead_results.map(i => i.result));
}).catch(e => {
reject(e);
});
});
}
},
inbox: {
get: max_id => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['inboxData'], d => {
if(!max_id && d.inboxData && Date.now() - d.inboxData.date < 18000) {
return resolve(d.inboxData.data);
}
fetch(`https://api.twitter.com/1.1/dm/user_inbox.json?max_conv_count=20&include_groups=true${max_id ? `&max_id=${max_id}` : ''}&cards_platform=Web-13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.user_inbox);
if(!max_id) chrome.storage.local.set({inboxData: {
date: Date.now(),
data: data.user_inbox
}}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
markRead: eventId => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/dm/conversation/mark_read.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
body: `last_read_event_id=${eventId}`
}).then(i => i.text()).then(data => {
resolve(1);
}).catch(e => {
reject(e);
});
});
},
getConversation: (id, max_id) => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/dm/conversation/${id}.json?ext=altText${max_id ? `&max_id=${max_id}` : ''}&count=100&cards_platform=Web-13&include_entities=1&include_user_entities=1&include_cards=1&send_error_codes=1&tweet_mode=extended&include_ext_alt_text=true&include_reply_count=true`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.conversation_timeline);
}).catch(e => {
reject(e);
});
});
},
send: obj => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/dm/new.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
body: new URLSearchParams(obj).toString()
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
}).catch(e => {
reject(e);
});
});
},
getUpdates: cursor => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['userUpdates'], d => {
if(!cursor) cursor = '';
if(!d.userUpdates) d.userUpdates = {};
if(d.userUpdates[cursor] && Date.now() - d.userUpdates[cursor].date < 4000) {
return resolve(d.userUpdates[cursor].data);
}
fetch(`https://twitter.com/i/api/1.1/dm/user_updates.json?${cursor ? `cursor=${cursor}&` : ''}cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_ext_collab_control=true&dm_users=false&include_groups=true&include_inbox_timelines=true&include_ext_media_color=true&supports_reactions=true&nsfw_filtering_enabled=false&cursor=GRwmiICwidfJnf8qFozAuPGoksj_KiUkAAA&filter_low_quality=false&include_quality=all&include_ext_edit_control=false&ext=mediaColor%2CaltText%2CmediaStats%2CverifiedType%2CisBlueVerified%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2Cenrichments%2CsuperFollowMetadata%2CunmentionInfo%2Ccollab_control`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220811153004 web/"
},
credentials: "include",
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data);
d.userUpdates[cursor] = {
date: Date.now(),
data: data
};
chrome.storage.local.set({userUpdates: d.userUpdates}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
deleteMessage: id => {
return new Promise((resolve, reject) => {
fetch(`https://api.twitter.com/1.1/dm/destroy.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.oauth_key,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
body: `dm_id=${id}`
}).then(i => i.text()).then(data => {
resolve(true);
}).catch(e => {
reject(e);
});
});
},
deleteConversation: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/1.1/dm/conversation/${id}/delete.json`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
credentials: "include",
method: 'post',
}).then(i => i.text()).then(data => {
resolve(true);
}).catch(e => {
reject(e);
});
});
}
},
bookmarks: {
get: (cursor) => {
return new Promise((resolve, reject) => {
let obj = {"count":50,"includePromotedContent":false};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/3OjEFzT2VjX-X7w4KYBJRg/Bookmarks?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"graphql_timeline_v2_bookmark_timeline":true,"blue_business_profile_image_shape_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"longform_notetweets_rich_text_read_enabled":true,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('bookmarks.get', 'start', {cursor, data});
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.bookmark_timeline_v2.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) return resolve({ list: [], cursor: undefined });
list = list.entries;
let out = {
list: list.filter(e => e.entryId.startsWith('tweet-')).map(e => {
let res = e.content.itemContent.tweet_results.result;
return parseTweet(res);
}).filter(i => !!i),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('bookmarks.get', 'end', out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
deleteAll: () => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/skiACZKC1GDYli-M8RzEPQ/BookmarksAllDelete`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include",
method: 'post',
body: `{"variables":{},"queryId":"skiACZKC1GDYli-M8RzEPQ"}`
}).then(i => i.text()).then(() => {
resolve(true);
}).catch(e => {
reject(e);
});
});
},
create: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/aoDbu3RHznuiSkQ9aNM67Q/CreateBookmark`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include",
method: 'post',
body: JSON.stringify({"variables":{"tweet_id":id},"queryId":"aoDbu3RHznuiSkQ9aNM67Q"})
}).then(i => i.text()).then(() => {
resolve(true);
}).catch(e => {
reject(e);
});
});
},
delete: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/Wlmlj2-xzyS1GN3a6cj-mQ/DeleteBookmark`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include",
method: 'post',
body: JSON.stringify({"variables":{"tweet_id":id},"queryId":"Wlmlj2-xzyS1GN3a6cj-mQ"})
}).then(i => i.text()).then(() => {
resolve(true);
}).catch(e => {
reject(e);
});
});
}
},
list: {
getTweets: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {"listId":id,"count":40};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/2Vjeyo_L0nizAUhHe3fKyA/ListLatestTweetsTimeline?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"rweb_lists_timeline_redesign_enabled":false,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('list.getTweets', 'start', id, cursor, data);
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.list.tweets_timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) return resolve({ list: [], cursor: undefined });
list = list.entries;
let tweets = [];
for(let e of list) {
if(e.entryId.startsWith('tweet-')) {
let res = e.content.itemContent.tweet_results.result;
let tweet = parseTweet(res);
if(tweet) {
tweet.hasModeratedReplies = e.content.itemContent.hasModeratedReplies;
tweets.push(tweet);
}
} else if(e.entryId.startsWith('list-conversation-')) {
let lt = e.content.items;
for(let i = 0; i < lt.length; i++) {
let t = lt[i];
if(t.entryId.includes('-tweet-')) {
let res = t.item.itemContent.tweet_results.result;
let tweet = parseTweet(res);
if(!tweet) continue;
if(i !== lt.length - 1) {
tweet.threadContinuation = true;
}
if(i !== 0) {
tweet.noTop = true;
}
tweet.hasModeratedReplies = t.item.itemContent.hasModeratedReplies;
tweets.push(tweet);
}
}
}
}
let out = {
list: tweets,
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('list.getTweets', 'end', id, cursor, out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getMembers: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {"listId":id,"count":20,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true,"withSafetyModeUserFields":true};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/sXFXEmtFr3nLyG1dmS81jw/ListMembers?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":false,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('list.getMembers', 'start', {id, cursor, data})
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.list.members_timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) return resolve({ list: [], cursor: undefined });
list = list.entries;
let out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(u => {
let res = u.content.itemContent.user_results.result;
res.legacy.id_str = res.rest_id;
return res.legacy;
}),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('list.getMembers', 'end', id, out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
getFollowers: (id, cursor) => {
return new Promise((resolve, reject) => {
let obj = {"listId":id,"count":20,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true,"withSafetyModeUserFields":true};
if(cursor) obj.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/LxXoouvfd5E8PXsdrQ0iMg/ListSubscribers?variables=${encodeURIComponent(JSON.stringify(obj))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":false,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('list.getFollowers', 'start', {id, cursor, data})
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
let list = data.data.list.subscribers_timeline.timeline.instructions.find(i => i.type === 'TimelineAddEntries');
if(!list) return resolve({ list: [], cursor: undefined });
list = list.entries;
let out = {
list: list.filter(e => e.entryId.startsWith('user-')).map(u => {
let res = u.content.itemContent.user_results.result;
res.legacy.id_str = res.rest_id;
return res.legacy;
}),
cursor: list.find(e => e.entryId.startsWith('cursor-bottom-')).content.value
};
debugLog('list.getFollowers', 'end', id, out);
resolve(out);
}).catch(e => {
reject(e);
});
});
},
get: id => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['listData'], d => {
if(!d.listData) d.listData = {};
if(d.listData[id] && Date.now() - d.listData[id].date < 60000) {
debugLog('list.get', 'cache', id, d.listData[id].data);
return resolve(d.listData[id].data);
}
fetch(`https://twitter.com/i/api/graphql/vxx-Y8zadpAP64HHiw4hMQ/ListByRestId?variables=${encodeURIComponent(JSON.stringify({"listId":id,"withSuperFollowsUserFields":true}))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
debugLog('list.get', id, data);
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.data.list);
d.listData[id] = {
date: Date.now(),
data: data.data.list
};
chrome.storage.local.set({listData: d.listData}, () => {});
}).catch(e => {
reject(e);
});
});
});
},
subscribe: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/nymTz5ek0FQPC3kh63Tp1w/ListSubscribe`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"listId":id,"withSuperFollowsUserFields":true},"features":{"responsive_web_graphql_timeline_navigation_enabled":false},"queryId":"nymTz5ek0FQPC3kh63Tp1w"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
unsubscribe: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/Wi5-aG4bvTmdjyRyRGkyhA/ListUnsubscribe`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"listId":id,"withSuperFollowsUserFields":true},"features":{"responsive_web_graphql_timeline_navigation_enabled":false},"queryId":"Wi5-aG4bvTmdjyRyRGkyhA"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
update: (id, name, description, isPrivate) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/P9YDuvCt6ogRf-kyr5E5xw/UpdateList`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({
"variables": {
"listId": id,
"isPrivate": isPrivate,
"description": description,
"name": name,
"withSuperFollowsUserFields": true
},
"features": {
"responsive_web_graphql_timeline_navigation_enabled": false
},
"queryId": "P9YDuvCt6ogRf-kyr5E5xw"
}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
delete: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/UnN9Th1BDbeLjpgjGSpL3Q/DeleteList`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"listId":id},"queryId":"UnN9Th1BDbeLjpgjGSpL3Q"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
addMember: (listId, userId) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/RKtQuzpcy2gym71UorWg6g/ListAddMember`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"listId":listId,"userId":userId,"withSuperFollowsUserFields":true},"features":{"responsive_web_graphql_timeline_navigation_enabled":false},"queryId":"RKtQuzpcy2gym71UorWg6g"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
removeMember: (listId, userId) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/mDlp1UvnnALC_EzybKAMtA/ListRemoveMember`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"listId":listId,"userId":userId,"withSuperFollowsUserFields":true},"features":{"responsive_web_graphql_timeline_navigation_enabled":false},"queryId":"mDlp1UvnnALC_EzybKAMtA"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(true);
}).catch(e => {
reject(e);
});
});
},
getMyLists: () => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/cl2dF-zeGiLvZDsMGZhL4g/ListsManagementPageTimeline?variables=${encodeURIComponent(JSON.stringify({"count":100,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true}))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":false,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(
data.data.viewer.list_management_timeline
.timeline.instructions.find(i => i.entries)
.entries.find(i => i.entryId.startsWith('owned-subscribed-list-module'))
.content.items.map(i => i.item.itemContent.list)
);
}).catch(e => {
reject(e);
});
});
},
create: (name, description, isPrivate) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/x5aSMDodNU02VT1VRyW48A/CreateList`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
method: "POST",
body: JSON.stringify({"variables":{"isPrivate":isPrivate,"name":name,"description":description,"withSuperFollowsUserFields":true},"features":{"responsive_web_graphql_timeline_navigation_enabled":false},"queryId":"x5aSMDodNU02VT1VRyW48A"}),
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(data.data.list);
}).catch(e => {
reject(e);
});
});
},
getOwnerships: (myId, userId) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/6E69fsenLDPDcprqtogzdw/ListOwnerships?variables=${encodeURIComponent(JSON.stringify({"userId":myId,"isListMemberTargetUserId":userId,"count":100,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true}))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":false,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
chrome.storage.local.set({listData: {}}, () => {});
resolve(
data.data.user.result.timeline.timeline.instructions.find(i => i.entries).entries.filter(e => e.entryId.startsWith('list-')).map(e => e.content.itemContent.list)
);
}).catch(e => {
reject(e);
});
});
}
},
circle: {
getCircles: () => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/QjN8ZdavFDqxUjNn3r9cig/AuthenticatedUserTFLists?variables=%7B%7D`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.data.authenticated_user_trusted_friends_lists);
}).catch(e => {
reject(e);
});
});
},
getMembers: (id, cursor = null) => {
return new Promise((resolve, reject) => {
let variables = {"trustedFriendsId":id,"cursor":cursor, count: 150};
let features = {"responsive_web_graphql_timeline_navigation_enabled":false};
fetch(`https://twitter.com/i/api/graphql/i3_opgZeSaeWbfyFQjZ5Sw/TrustedFriendsMembersQuery?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify(features))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.data.trusted_friends_list_by_rest_id.members_slice.items_results.filter(u => u.result && u.result.is_trusted_friends_list_member).map(u => u.result));
}).catch(e => {
reject(e);
});
});
},
removeUser: (circle_id, circle_rest_id, item_id, user_id) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/fl9NbcQB1UE5uiYvEHfHGA/TrustedFriendsAddRemoveButtonRemoveMutation`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({
variables: {
trustedFriendsId: circle_rest_id,
userId: user_id,
slices: [
`client:${circle_id}=:__TrustedFriendsMembers_slice_result_slice`
],
itemID: item_id
},
features: {"responsive_web_graphql_timeline_navigation_enabled":false},
queryId: "fl9NbcQB1UE5uiYvEHfHGA"
})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
},
addUser: (circle_id, circle_rest_id, user_id) => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/QFcDZhljP_e9bzeT8saZ3A/TrustedFriendsAddRemoveButtonAddMutation`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({
variables: {
trustedFriendsId: circle_rest_id,
userId: user_id,
slices: [
`client:${circle_id}=:__TrustedFriendsMembers_slice_result_slice`
],
},
features: {"responsive_web_graphql_timeline_navigation_enabled":false},
queryId: "QFcDZhljP_e9bzeT8saZ3A"
})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
}
},
topic: {
landingPage: (id, cursor) => {
return new Promise((resolve, reject) => {
let variables = {"rest_id": id,"context":"{}","withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true};
if(cursor) variables.cursor = cursor;
fetch(`https://twitter.com/i/api/graphql/4exqISyA1-LejxLHY4RqJA/TopicLandingPage?variables=${encodeURIComponent(JSON.stringify(variables))}&features=${encodeURIComponent(JSON.stringify({"responsive_web_graphql_timeline_navigation_enabled":false,"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled":true,"dont_mention_me_view_api_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}))}`, {
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include"
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(data.data.topic_by_rest_id.topic_page);
}).catch(e => {
reject(e);
});
});
},
notInterested: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/cPCFdDAaqRjlMRYInZzoDA/TopicNotInterested`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({"variables":{"topicId":id},"queryId":"cPCFdDAaqRjlMRYInZzoDA"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
},
undoNotInterested: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/4tVnt6FoSxaX8L-mDDJo4Q/TopicUndoNotInterested`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({"variables":{"topicId":id},"queryId":"4tVnt6FoSxaX8L-mDDJo4Q"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
},
follow: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/ElqSLWFmsPL4NlZI5e1Grg/TopicFollow`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({"variables":{"topicId":id},"queryId":"ElqSLWFmsPL4NlZI5e1Grg"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
},
unfollow: id => {
return new Promise((resolve, reject) => {
fetch(`https://twitter.com/i/api/graphql/srwjU6JM_ZKTj_QMfUGNcw/TopicUnfollow`, {
method: "POST",
headers: {
"authorization": OLDTWITTER_CONFIG.public_token,
"x-csrf-token": OLDTWITTER_CONFIG.csrf,
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
"x-twitter-client-language": LANGUAGE ? LANGUAGE : navigator.language ? navigator.language : "en"
},
credentials: "include",
body: JSON.stringify({"variables":{"topicId":id},"queryId":"srwjU6JM_ZKTj_QMfUGNcw"})
}).then(i => i.json()).then(data => {
if (data.errors && data.errors[0].code === 32) {
return reject("Not logged in");
}
if (data.errors && data.errors[0]) {
return reject(data.errors[0].message);
}
resolve(true);
}).catch(e => {
reject(e);
});
});
}
},
/*
media_type: "video/mp4",
media_category: "tweet_video" | "tweet_image" | "tweet_gif",
media: ArrayBuffer,
loadCallback: function,
alt: "alt text"
*/
uploadMedia: (data) => {
return new Promise(async (resolve, reject) => {
let obj = {
command: "INIT",
total_bytes: data.media.byteLength,
media_type: data.media_type
};
if(data.media_category) obj.media_category = data.media_category;
let initUpload = await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
credentials: "include",
method: "post",
body: new URLSearchParams(obj).toString()
}).then(i => i.json());
if (initUpload.errors && initUpload.errors[0]) {
return reject(initUpload.errors[0].message);
}
let mediaId = initUpload.media_id_string;
let segments = [];
let segmentSize = 1084576;
let segmentCount = Math.ceil(data.media.byteLength / segmentSize);
for (let i = 0; i < segmentCount; i++) {
let segmentData = data.media.slice(i * segmentSize, (i + 1) * segmentSize);
segments.push(segmentData);
}
for(let i in segments) {
let segment = segments[i];
try {
await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
credentials: "include",
method: "post",
body: new URLSearchParams({
command: "APPEND",
media_id: mediaId,
media_data: arrayBufferToBase64(segment),
segment_index: +i
}).toString()
}).then(i => i.text());
} catch (e) {
await new Promise((resolve, reject) => {
console.error(e);
setTimeout(async () => {
await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
credentials: "include",
method: "post",
body: new URLSearchParams({
command: "APPEND",
media_id: mediaId,
media_data: arrayBufferToBase64(segment),
segment_index: +i
}).toString()
}).then(i => i.text()).catch(reject);
if(data.loadCallback) {
data.loadCallback({
text: `Uploading`,
progress: Math.round(((+i + 1) / segments.length)*100)
});
}
resolve(true);
}, 1000);
});
}
if(data.loadCallback) {
data.loadCallback({
text: `Uploading`,
progress: Math.round(((+i + 1) / segments.length)*100)
});
}
}
let finalData = await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
credentials: "include",
method: "post",
body: new URLSearchParams({
command: "FINALIZE",
media_id: mediaId
}).toString()
}).then(i => i.json());
if (finalData.errors && finalData.errors[0]) {
return reject(finalData.errors[0].message);
}
if((typeof data.alt === 'string' && data.alt.length > 0) || data.cw.length > 0) {
try {
let obj = {
media_id: mediaId
};
if(data.alt) {
obj.alt_text = {
text: data.alt
};
}
if(data.cw.length > 0) {
obj.sensitive_media_warning = data.cw;
}
await fetch(`https://upload.twitter.com/1.1/media/metadata/create.json`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/" },
credentials: "include",
method: "post",
body: JSON.stringify(obj)
}).then(i => i.json());
} catch(e) {
console.warn(e);
}
}
if(!finalData.processing_info) {
return resolve(mediaId);
}
let statusTries = 0;
async function checkStatus() {
if(statusTries++ > 60*20) return clearInterval(statusInterval);
await fetch(`https://upload.twitter.com/1.1/media/upload.json?${new URLSearchParams({ command: "STATUS", media_id: mediaId }).toString()}`, {
headers: { "authorization": OLDTWITTER_CONFIG.oauth_key, "x-csrf-token": OLDTWITTER_CONFIG.csrf, "x-twitter-auth-type": "OAuth2Session", "x-twitter-client-version": "Twitter-TweetDeck-blackbird-chrome/4.0.220630115210 web/", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
credentials: "include",
}).then(i => i.json()).then(i => {
if (i.processing_info.state === "succeeded") {
resolve(i.media_id_string);
}
if (i.processing_info.state === "failed") {
reject(i.processing_info.error.message);
}
if(i.processing_info.state === "in_progress") {
setTimeout(checkStatus, i.processing_info.check_after_secs*1000);
if(data.loadCallback) {
data.loadCallback({
text: `Processing`,
progress: i.processing_info.progress_percent
});
}
}
});
};
setTimeout(checkStatus, 500);
});
}
};