mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-10 07:12:51 +08:00
fix(route)(twitter): outdated web API (#9471)
Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
const twitterGot = require('./twitter-got');
|
||||
const config = require('@/config').value;
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L727-L755
|
||||
const _params = {
|
||||
count: 20,
|
||||
include_profile_interstitial_type: 1,
|
||||
@@ -9,6 +12,7 @@ const _params = {
|
||||
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,
|
||||
@@ -20,30 +24,82 @@ const _params = {
|
||||
include_user_entities: true,
|
||||
include_ext_media_color: true,
|
||||
include_ext_media_availability: true,
|
||||
// include_ext_sensitive_media_warning: true, // IDK what it is, maybe disabling it will make NSFW lovers happy?
|
||||
send_error_codes: true,
|
||||
simple_quoted_tweet: true,
|
||||
include_tweet_replies: false,
|
||||
ext: 'mediaStats,highlightedLabel',
|
||||
cursor: undefined,
|
||||
ext: 'mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,superFollowMetadata',
|
||||
};
|
||||
|
||||
const pagination = (endpoint, userId, params) =>
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L756--L770
|
||||
const _variables = {
|
||||
count: 20,
|
||||
includePromotedContent: false,
|
||||
withSuperFollowsUserFields: true,
|
||||
withBirdwatchPivots: false,
|
||||
withDownvotePerspective: false,
|
||||
withReactionsMetadata: false,
|
||||
withReactionsPerspective: false,
|
||||
withSuperFollowsTweetFields: true,
|
||||
withClientEventToken: false,
|
||||
withBirdwatchNotes: false,
|
||||
withVoice: true,
|
||||
withV2Timeline: false,
|
||||
__fs_interactive_text: false,
|
||||
__fs_dont_mention_me_view_api_enabled: false,
|
||||
};
|
||||
|
||||
const paginationLegacy = (endpoint, userId, params) =>
|
||||
twitterGot('https://api.twitter.com' + endpoint, {
|
||||
..._params,
|
||||
...params,
|
||||
userId,
|
||||
});
|
||||
const timelineMedia = (userId, params = {}) => pagination(`/2/timeline/media/${userId}.json`, userId, params);
|
||||
const timelineTweets = (userId, params = {}) => pagination(`/2/timeline/profile/${userId}.json`, userId, params);
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L1075-L1093
|
||||
const paginationTweets = async (endpoint, userId, variables, path) => {
|
||||
const { data } = await twitterGot('https://twitter.com/i/api' + endpoint, {
|
||||
variables: JSON.stringify({
|
||||
..._variables,
|
||||
...variables,
|
||||
userId,
|
||||
}),
|
||||
});
|
||||
|
||||
let instructions;
|
||||
if (!path) {
|
||||
instructions = data.user.result.timeline.timeline.instructions;
|
||||
} else {
|
||||
instructions = data;
|
||||
path.forEach((p) => (instructions = instructions[p]));
|
||||
instructions = instructions.instructions;
|
||||
}
|
||||
|
||||
return instructions.filter((i) => i.type === 'TimelineAddEntries')[0].entries;
|
||||
};
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L807-L814
|
||||
const timelineTweets = (userId, params = {}) =>
|
||||
paginationTweets('/graphql/WZT7sCTrLvSOaWOXLDsWbQ/UserTweets', userId, {
|
||||
...params,
|
||||
withQuickPromoteEligibilityTweetFields: true,
|
||||
});
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L816-L823
|
||||
const timelineTweetsAndReplies = (userId, params = {}) =>
|
||||
pagination(`/2/timeline/profile/${userId}.json`, userId, {
|
||||
paginationTweets('/graphql/t4wEKVulW4Mbv1P0kgxTEw/UserTweetsAndReplies', userId, {
|
||||
...params,
|
||||
include_tweet_replies: true,
|
||||
});
|
||||
const timelineLikes = (userId, params = {}) =>
|
||||
pagination(`/2/timeline/favorites/${userId}.json`, userId, {
|
||||
...params,
|
||||
sorted_by_time: true,
|
||||
withCommunity: true,
|
||||
});
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L825-L831
|
||||
const timelineMedia = (userId, params = {}) => paginationTweets('/graphql/nRybED9kRbN-TOWioHq1ng/UserMedia', userId, params);
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L833-L839
|
||||
const timelineLikes = (userId, params = {}) => paginationLegacy(`/graphql/9MSTt44HoGjVFSg_u3rHDw/Likes`, userId, params);
|
||||
|
||||
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L858-L866
|
||||
const timelineKeywords = (keywords, params = {}) =>
|
||||
twitterGot('https://twitter.com/i/api/2/search/adaptive.json', {
|
||||
..._params,
|
||||
@@ -53,6 +109,26 @@ const timelineKeywords = (keywords, params = {}) =>
|
||||
pc: 1,
|
||||
});
|
||||
|
||||
function gatherLegacyFromData(entries, filter = 'tweet-') {
|
||||
const tweets_dict = {};
|
||||
const tweets = [];
|
||||
entries.forEach((entry) => {
|
||||
tweets_dict[entry.sortIndex] = entry;
|
||||
});
|
||||
entries.forEach((entry) => {
|
||||
if (entry.entryId && entry.entryId.includes(filter)) {
|
||||
const legacy = entry.content.itemContent.tweet_results.result.legacy;
|
||||
legacy.user = entry.content.itemContent.tweet_results.result.core.user_results.result.legacy;
|
||||
if (legacy.retweeted_status_id_str) {
|
||||
legacy.retweeted_status = legacy.retweeted_status_result.result.legacy;
|
||||
legacy.retweeted_status.user = legacy.retweeted_status_result.result.core.user_results.result.legacy;
|
||||
}
|
||||
tweets.push(legacy);
|
||||
}
|
||||
});
|
||||
return tweets;
|
||||
}
|
||||
|
||||
function pickLegacyByID(id, tweets_dict, users_dict) {
|
||||
function pickLegacyFromTweet(tweet) {
|
||||
tweet.user = users_dict[tweet.user_id_str];
|
||||
@@ -67,7 +143,7 @@ function pickLegacyByID(id, tweets_dict, users_dict) {
|
||||
}
|
||||
}
|
||||
|
||||
function gatherLegacyFromData(data, filter = 'tweet-') {
|
||||
function gatherLegacyFromLegacyApiData(data, filter = 'tweet-') {
|
||||
const tweets_dict = data.globalObjects.tweets;
|
||||
const users_dict = data.globalObjects.users;
|
||||
const tweets = [];
|
||||
@@ -106,11 +182,19 @@ const getUserData = (cache, screenName) => cache.tryGet(`twitter-userdata-${scre
|
||||
const getUserID = async (cache, screenName) => (await getUserData(cache, screenName)).data.user.rest_id;
|
||||
const getUser = async (cache, screenName) => (await getUserData(cache, screenName)).data.user.legacy;
|
||||
|
||||
const getUserTweets = async (cache, screenName, params = {}) => getUserTweetsByID(await getUserID(cache, screenName), params);
|
||||
const getUserTweetsAndReplies = async (cache, screenName, params = {}) => getUserTweetsAndRepliesByID(await getUserID(cache, screenName), params);
|
||||
const getUserMedia = async (cache, screenName, params = {}) => getUserMediaByID(await getUserID(cache, screenName), params);
|
||||
const getUserLikes = async (cache, screenName, params = {}) => getUserLikesByID(await getUserID(cache, screenName), params);
|
||||
const getSearch = async (keywords, params = {}) => gatherLegacyFromData(await timelineKeywords(keywords, params), 'sq-I-t-');
|
||||
const cacheTryGet = async (cache, screenName, params, func) => {
|
||||
const id = await getUserID(cache, screenName);
|
||||
const funcName = func.name;
|
||||
const paramsString = JSON.stringify(params);
|
||||
return await cache.tryGet(`twitter:${id}:${funcName}:${paramsString}`, async () => await func(id, params), config.cache.routeExpire, false);
|
||||
};
|
||||
|
||||
const getUserTweets = async (cache, screenName, params = {}) => await cacheTryGet(cache, screenName, params, getUserTweetsByID);
|
||||
const getUserTweetsAndReplies = async (cache, screenName, params = {}) => await cacheTryGet(cache, screenName, params, getUserTweetsAndRepliesByID);
|
||||
const getUserMedia = async (cache, screenName, params = {}) => await cacheTryGet(cache, screenName, params, getUserMediaByID);
|
||||
const getUserLikes = async (cache, screenName, params = {}) => await cacheTryGet(cache, screenName, params, getUserLikesByID);
|
||||
|
||||
const getSearch = async (keywords, params = {}) => gatherLegacyFromLegacyApiData(await timelineKeywords(keywords, params), 'sq-I-t-');
|
||||
|
||||
module.exports = {
|
||||
getUser,
|
||||
|
||||
Reference in New Issue
Block a user