feat(route/twitter): use unique ID in some routes (#12759)
Signed-off-by: Rongrong <i@rong.moe>
This commit is contained in:
parent
ef497881a7
commit
c771fbd3cd
|
|
@ -494,15 +494,15 @@ generates
|
|||
|
||||
### User timeline
|
||||
|
||||
<RouteEn author="DIYgod yindaheng98 Rongronggg9" path="/twitter/user/:id/:routeParams?" example="/twitter/user/DIYgod" :paramsDesc="['user id', 'extra parameters, see the table above; particularly when `routeParams=exclude_replies`, replies are excluded; `routeParams=exclude_rts` excludes retweets,`routeParams=exclude_rts_replies` exclude replies and retweets; for default include all.']" radar="1" rssbud="1"/>
|
||||
<RouteEn author="DIYgod yindaheng98 Rongronggg9" path="/twitter/user/:id/:routeParams?" example="/twitter/user/DIYgod" :paramsDesc="['username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`', 'extra parameters, see the table above; particularly when `routeParams=exclude_replies`, replies are excluded; `routeParams=exclude_rts` excludes retweets,`routeParams=exclude_rts_replies` exclude replies and retweets; for default include all.']" radar="1" rssbud="1"/>
|
||||
|
||||
### User media
|
||||
|
||||
<RouteEn author="yindaheng98 Rongronggg9" path="/twitter/media/:id/:routeParams?" example="/twitter/media/DIYgod" :paramsDesc="['user id', 'extra parameters, see the table above.']" radar="1" rssbud="1"/>
|
||||
<RouteEn author="yindaheng98 Rongronggg9" path="/twitter/media/:id/:routeParams?" example="/twitter/media/DIYgod" :paramsDesc="['username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`', 'extra parameters, see the table above.']" radar="1" rssbud="1"/>
|
||||
|
||||
### User following timeline
|
||||
|
||||
<RouteEn author="DIYgod" example="/twitter/followings/DIYgod" path="/twitter/followings/:id/:routeParams?" :paramsDesc="['user id', 'extra parameters, see the table above']" radar="1" rssbud="1" selfhost="1">
|
||||
<RouteEn author="DIYgod" example="/twitter/followings/DIYgod" path="/twitter/followings/:id/:routeParams?" :paramsDesc="['username', 'extra parameters, see the table above']" radar="1" rssbud="1" selfhost="1">
|
||||
|
||||
::: warning
|
||||
|
||||
|
|
@ -514,11 +514,11 @@ This route requires Twitter token's corresponding id, therefore it's only availa
|
|||
|
||||
### List timeline
|
||||
|
||||
<RouteEn author="xyqfer" example="/twitter/list/ladyleet/javascript" path="/twitter/list/:id/:name/:routeParams?" :paramsDesc="['user name', 'list name', 'extra parameters, see the table above']" radar="1" rssbud="1"/>
|
||||
<RouteEn author="xyqfer" example="/twitter/list/ladyleet/javascript" path="/twitter/list/:id/:name/:routeParams?" :paramsDesc="['username', 'list name', 'extra parameters, see the table above']" radar="1" rssbud="1"/>
|
||||
|
||||
### User likes
|
||||
|
||||
<RouteEn author="xyqfer" example="/twitter/likes/DIYgod" path="/twitter/likes/:id/:routeParams?" :paramsDesc="['user name', 'extra parameters, see the table above']" radar="1" rssbud="1"/>
|
||||
<RouteEn author="xyqfer" example="/twitter/likes/DIYgod" path="/twitter/likes/:id/:routeParams?" :paramsDesc="['username', 'extra parameters, see the table above']" radar="1" rssbud="1"/>
|
||||
|
||||
### Keyword
|
||||
|
||||
|
|
@ -530,7 +530,7 @@ This route requires Twitter token's corresponding id, therefore it's only availa
|
|||
|
||||
### Collection
|
||||
|
||||
<RouteEn author="TonyRL" example="/twitter/collection/DIYgod/1527857429467172864" path="/twitter/collection/:uid/:collectionId/:routeParams?" :paramsDesc="['User name, should match the generated token', 'Collection ID, can be found in URL', 'extra parameters, see the table above']" radar="1" rssbud="1" selfhost="1">
|
||||
<RouteEn author="TonyRL" example="/twitter/collection/DIYgod/1527857429467172864" path="/twitter/collection/:uid/:collectionId/:routeParams?" :paramsDesc="['username, should match the generated token', 'collection ID, can be found in URL', 'extra parameters, see the table above']" radar="1" rssbud="1" selfhost="1">
|
||||
|
||||
::: warning
|
||||
|
||||
|
|
@ -542,7 +542,7 @@ This route requires Twitter token's corresponding id, therefore it's only availa
|
|||
|
||||
### Tweet Details
|
||||
|
||||
<Route author="LarchLiu Rongronggg9" example="/twitter/tweet/DIYgod/status/1650844643997646852" path="/twitter/tweet/:id/status/:status/:original?" :paramsDesc="['User name', 'Tweet ID', 'extra parameters, data type of return, if the value is not `0`/`false` and `config.isPackage` is `true`, return the original data of twitter']" radar="1" rssbud="1"/>
|
||||
<Route author="LarchLiu Rongronggg9" example="/twitter/tweet/DIYgod/status/1650844643997646852" path="/twitter/tweet/:id/status/:status/:original?" :paramsDesc="['username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`', 'tweet ID', 'extra parameters, data type of return, if the value is not `0`/`false` and `config.isPackage` is `true`, return the original data of twitter']" radar="1" rssbud="1"/>
|
||||
|
||||
## Vimeo
|
||||
|
||||
|
|
|
|||
|
|
@ -832,11 +832,11 @@ Instagram Stories 没有可靠的 guid,你的 RSS 阅读器可能将同一条
|
|||
|
||||
### 用户时间线
|
||||
|
||||
<Route author="DIYgod yindaheng98 Rongronggg9" example="/twitter/user/DIYgod" path="/twitter/user/:id/:routeParams?" :paramsDesc="['用户名', '额外参数;请参阅上面的说明和表格;特别地,当 `routeParams=exclude_replies`时去除回复,`routeParams=exclude_rts`去除转推,`routeParams=exclude_rts_replies`去除回复和转推,默认包含全部回复和转推。']" radar="1" rssbud="1"/>
|
||||
<Route author="DIYgod yindaheng98 Rongronggg9" example="/twitter/user/DIYgod" path="/twitter/user/:id/:routeParams?" :paramsDesc="['用户名;特别地,以 `+` 开头则代表[唯一 ID](https://github.com/DIYgod/RSSHub/issues/12221),如 `+44196397`', '额外参数;请参阅上面的说明和表格;特别地,当 `routeParams=exclude_replies`时去除回复,`routeParams=exclude_rts`去除转推,`routeParams=exclude_rts_replies`去除回复和转推,默认包含全部回复和转推。']" radar="1" rssbud="1"/>
|
||||
|
||||
### 用户媒体时间线
|
||||
|
||||
<Route author="yindaheng98 Rongronggg9" example="/twitter/media/DIYgod" path="/twitter/media/:id/:routeParams?" :paramsDesc="['用户名', '额外参数;请参阅上面的说明和表格。']" radar="1" rssbud="1"/>
|
||||
<Route author="yindaheng98 Rongronggg9" example="/twitter/media/DIYgod" path="/twitter/media/:id/:routeParams?" :paramsDesc="['用户名;特别地,以 `+` 开头则代表[唯一 ID](https://github.com/DIYgod/RSSHub/issues/12221),如 `+44196397`', '额外参数;请参阅上面的说明和表格。']" radar="1" rssbud="1"/>
|
||||
|
||||
### 用户关注时间线
|
||||
|
||||
|
|
@ -880,7 +880,7 @@ Instagram Stories 没有可靠的 guid,你的 RSS 阅读器可能将同一条
|
|||
|
||||
### 推文详情
|
||||
|
||||
<Route author="LarchLiu Rongronggg9" example="/twitter/tweet/DIYgod/status/1650844643997646852" path="/twitter/tweet/:id/status/:status/:original?" :paramsDesc="['用户名', '推文 ID', '额外参数;返回数据类型,当非 `0`/`false` 且 `config.isPackage` 为 `true`时,返回 twitter 原始数据']" radar="1" rssbud="1"/>
|
||||
<Route author="LarchLiu Rongronggg9" example="/twitter/tweet/DIYgod/status/1650844643997646852" path="/twitter/tweet/:id/status/:status/:original?" :paramsDesc="['用户名;特别地,以 `+` 开头则代表[唯一 ID](https://github.com/DIYgod/RSSHub/issues/12221),如 `+44196397`', '推文 ID', '额外参数;返回数据类型,当非 `0`/`false` 且 `config.isPackage` 为 `true`时,返回 twitter 原始数据']" radar="1" rssbud="1"/>
|
||||
|
||||
## Vimeo
|
||||
|
||||
|
|
|
|||
|
|
@ -2,24 +2,32 @@ const utils = require('../utils');
|
|||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
|
||||
// For compatibility
|
||||
const { exclude_replies, include_rts, count } = utils.parseRouteParams(ctx.params.routeParams);
|
||||
const client = await utils.getAppClient();
|
||||
const data = await client.v1.get('statuses/user_timeline.json', {
|
||||
screen_name: id,
|
||||
const user_timeline_query = {
|
||||
tweet_mode: 'extended',
|
||||
exclude_replies,
|
||||
include_rts,
|
||||
count,
|
||||
});
|
||||
};
|
||||
let screen_name;
|
||||
if (id.startsWith('+')) {
|
||||
user_timeline_query.user_id = +id.slice(1);
|
||||
} else {
|
||||
user_timeline_query.screen_name = id;
|
||||
screen_name = id;
|
||||
}
|
||||
const data = await client.v1.get('statuses/user_timeline.json', user_timeline_query);
|
||||
const userInfo = data[0].user;
|
||||
|
||||
if (!screen_name) {
|
||||
screen_name = userInfo.screen_name;
|
||||
}
|
||||
const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https;
|
||||
|
||||
ctx.state.data = {
|
||||
title: `Twitter @${userInfo.name}`,
|
||||
link: `https://twitter.com/${id}`,
|
||||
link: `https://twitter.com/${screen_name}`,
|
||||
image: profileImageUrl,
|
||||
description: userInfo.description,
|
||||
item: utils.ProcessFeed(ctx, {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const tokens = [
|
|||
|
||||
const graphQLEndpointsPlain = [
|
||||
'/graphql/oUZZZ8Oddwxs8Cd3iW3UEA/UserByScreenName',
|
||||
'/graphql/Lxg1V9AiIzzXEiP2c8dRnw/UserByRestId',
|
||||
'/graphql/3XDB26fBve-MmjHaWTUZxA/TweetDetail',
|
||||
'/graphql/QqZBEqganhHwmU9QscmIug/UserTweets',
|
||||
'/graphql/wxoVeDnl0mP7VLhe6mTOdg/UserTweetsAndReplies',
|
||||
|
|
@ -21,6 +22,7 @@ const graphQLEndpointsPlain = [
|
|||
|
||||
const graphQLMap = Object.fromEntries(graphQLEndpointsPlain.map((endpoint) => [endpoint.split('/')[3], endpoint]));
|
||||
|
||||
// captured from Twitter web
|
||||
const featuresMap = {
|
||||
UserByScreenName: JSON.stringify({
|
||||
hidden_profile_likes_enabled: false,
|
||||
|
|
@ -32,6 +34,15 @@ const featuresMap = {
|
|||
responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
|
||||
responsive_web_graphql_timeline_navigation_enabled: true,
|
||||
}),
|
||||
UserByRestId: JSON.stringify({
|
||||
hidden_profile_likes_enabled: false,
|
||||
responsive_web_graphql_exclude_directive_enabled: true,
|
||||
verified_phone_label_enabled: false,
|
||||
highlights_tweets_tab_ui_enabled: true,
|
||||
creator_subscriptions_tweet_preview_api_enabled: true,
|
||||
responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
|
||||
responsive_web_graphql_timeline_navigation_enabled: true,
|
||||
}),
|
||||
UserTweets: JSON.stringify({
|
||||
rweb_lists_timeline_redesign_enabled: true,
|
||||
responsive_web_graphql_exclude_directive_enabled: true,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module.exports = async (ctx) => {
|
|||
|
||||
ctx.state.data = {
|
||||
title: `Twitter @${userInfo.name}`,
|
||||
link: `https://twitter.com/${id}/media`,
|
||||
link: `https://twitter.com/${userInfo.screen_name}/media`,
|
||||
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
|
||||
description: userInfo.description,
|
||||
item: utils.ProcessFeed(ctx, {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module.exports = async (ctx) => {
|
|||
|
||||
ctx.state.data = {
|
||||
title: `Twitter @${userInfo.name}`,
|
||||
link: `https://twitter.com/${id}/status/${status}`,
|
||||
link: `https://twitter.com/${userInfo.screen_name}/status/${status}`,
|
||||
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
|
||||
description: userInfo.description,
|
||||
item,
|
||||
|
|
|
|||
|
|
@ -231,22 +231,36 @@ const userByScreenName = (screenName) =>
|
|||
variables: `{"screen_name":"${screenName}","withHighlightedLabel":true}`,
|
||||
features: featuresMap.UserByScreenName,
|
||||
});
|
||||
const getUserData = (cache, screenName) => cache.tryGet(`twitter-userdata-${screenName}`, () => userByScreenName(screenName));
|
||||
const getUserID = async (cache, screenName) => (await getUserData(cache, screenName)).data.user.result.rest_id;
|
||||
const getUser = async (cache, screenName) => (await getUserData(cache, screenName)).data.user.result.legacy;
|
||||
const userByRestId = (restId) =>
|
||||
twitterGot(`https://twitter.com/i/api${graphQLMap.UserByRestId}`, {
|
||||
variables: `{"userId":"${restId}","withHighlightedLabel":true}`,
|
||||
features: featuresMap.UserByRestId,
|
||||
});
|
||||
const userByAuto = (id) => {
|
||||
if (id.startsWith('+')) {
|
||||
return userByRestId(id.slice(1));
|
||||
}
|
||||
return userByScreenName(id);
|
||||
};
|
||||
const getUserData = (cache, id) => cache.tryGet(`twitter-userdata-${id}`, () => userByAuto(id));
|
||||
const getUserID = async (cache, id) => (await getUserData(cache, id)).data.user.result.rest_id;
|
||||
const getUser = async (cache, id) => (await getUserData(cache, id)).data.user.result.legacy;
|
||||
|
||||
const cacheTryGet = async (cache, screenName, params, func) => {
|
||||
const id = await getUserID(cache, screenName);
|
||||
const cacheTryGet = async (cache, _id, params, func) => {
|
||||
const id = await getUserID(cache, _id);
|
||||
if (id === undefined) {
|
||||
throw Error('User not found');
|
||||
}
|
||||
const funcName = func.name;
|
||||
const paramsString = JSON.stringify(params);
|
||||
return cache.tryGet(`twitter:${id}:${funcName}:${paramsString}`, () => func(id, params), config.cache.routeExpire, false);
|
||||
};
|
||||
|
||||
const getUserTweets = (cache, screenName, params = {}) => cacheTryGet(cache, screenName, params, getUserTweetsByID);
|
||||
const getUserTweetsAndReplies = (cache, screenName, params = {}) => cacheTryGet(cache, screenName, params, getUserTweetsAndRepliesByID);
|
||||
const getUserMedia = (cache, screenName, params = {}) => cacheTryGet(cache, screenName, params, getUserMediaByID);
|
||||
const getUserLikes = (cache, screenName, params = {}) => cacheTryGet(cache, screenName, params, getUserLikesByID);
|
||||
const getUserTweet = (cache, screenName, params) => cacheTryGet(cache, screenName, params, getUserTweetByStatus);
|
||||
const getUserTweets = (cache, id, params = {}) => cacheTryGet(cache, id, params, getUserTweetsByID);
|
||||
const getUserTweetsAndReplies = (cache, id, params = {}) => cacheTryGet(cache, id, params, getUserTweetsAndRepliesByID);
|
||||
const getUserMedia = (cache, id, params = {}) => cacheTryGet(cache, id, params, getUserMediaByID);
|
||||
const getUserLikes = (cache, id, params = {}) => cacheTryGet(cache, id, params, getUserLikesByID);
|
||||
const getUserTweet = (cache, id, params) => cacheTryGet(cache, id, params, getUserTweetByStatus);
|
||||
|
||||
const getSearch = async (keywords, params = {}) => gatherLegacyFromLegacyApiData(await timelineKeywords(keywords, params), 'sq-I-t-');
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module.exports = async (ctx) => {
|
|||
|
||||
ctx.state.data = {
|
||||
title: `Twitter @${userInfo.name}`,
|
||||
link: `https://twitter.com/${id}`,
|
||||
link: `https://twitter.com/${userInfo.screen_name}`,
|
||||
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
|
||||
description: userInfo.description,
|
||||
item: utils.ProcessFeed(ctx, {
|
||||
|
|
|
|||
Loading…
Reference in New Issue