feat(route/twitter): support official api with user credentials (#21139)

* feat(route/twitter): support official api with user credentials (act as a logged user) support

* fix: update namespace docs

* fix(route/twitter): use optional chaining for access tokens
This commit is contained in:
CutCut 2026-02-14 13:32:44 +08:00 committed by GitHub
parent 5310e6a61b
commit 4c4339c625
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 42 additions and 8 deletions

View File

@ -206,6 +206,8 @@ type ConfigEnvKeys =
| 'TUMBLR_REFRESH_TOKEN'
| 'TWITTER_CONSUMER_KEY'
| 'TWITTER_CONSUMER_SECRET'
| 'TWITTER_ACCESS_TOKEN'
| 'TWITTER_ACCESS_SECRET'
// | 'TWITTER_USERNAME'
// | 'TWITTER_PASSWORD'
// | 'TWITTER_AUTHENTICATION_SECRET'
@ -621,6 +623,8 @@ export type Config = {
twitter: {
consumerKey?: string;
consumerSecret?: string;
accessToken?: string;
accessSecret?: string;
// username?: string[];
// password?: string[];
// authenticationSecret?: string[];
@ -1108,6 +1112,8 @@ const calculateValue = () => {
twitter: {
consumerKey: envs.TWITTER_CONSUMER_KEY,
consumerSecret: envs.TWITTER_CONSUMER_SECRET,
accessToken: envs.TWITTER_ACCESS_TOKEN,
accessSecret: envs.TWITTER_ACCESS_SECRET,
// username: envs.TWITTER_USERNAME?.split(','),
// password: envs.TWITTER_PASSWORD?.split(','),
// authenticationSecret: envs.TWITTER_AUTHENTICATION_SECRET?.split(','),

View File

@ -6,7 +6,12 @@ import ConfigNotFoundError from '@/errors/types/config-not-found';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import cache from '@/utils/cache';
const appClients: TwitterApiReadOnly[] = [];
interface ClientWrapper {
client: TwitterApiReadOnly;
isUserAuth: boolean;
}
const appClients: ClientWrapper[] = [];
let index = -1;
const init = () => {
@ -19,18 +24,37 @@ const init = () => {
const consumerKeys = config.twitter.consumerKey.split(',');
const consumerSecrets = config.twitter.consumerSecret.split(',');
const accessTokens = config.twitter.accessToken?.split(',') || [];
const accessSecrets = config.twitter.accessSecret?.split(',') || [];
for (const [index, consumerKey] of consumerKeys.entries()) {
const consumerSecret = consumerSecrets[index];
const accessToken = accessTokens[index];
const accessSecret = accessSecrets[index];
if (!consumerKey || !consumerSecret) {
continue;
}
appClients.push(
new TwitterApi({
appKey: consumerKey,
appSecret: consumerSecret,
}).readOnly
);
if (accessToken && accessSecret) {
appClients.push({
client: new TwitterApi({
appKey: consumerKey,
appSecret: consumerSecret,
accessToken,
accessSecret,
}).readOnly,
isUserAuth: true,
});
} else {
appClients.push({
client: new TwitterApi({
appKey: consumerKey,
appSecret: consumerSecret,
}).readOnly,
isUserAuth: false,
});
}
}
};
@ -40,7 +64,10 @@ export const getAppClient = async () => {
throw new ConfigNotFoundError('Twitter API is not configured');
}
index += 1;
return await appClients[index % appClients.length].appLogin();
const currentWrapper = appClients[index % appClients.length];
return currentWrapper.isUserAuth ? currentWrapper.client : await currentWrapper.client.appLogin();
};
const mapUserToLegacy = (user: Record<string, any>) =>

View File

@ -48,6 +48,7 @@ Currently supports two authentication methods:
~~- Using \`TWITTER_USERNAME\` \`TWITTER_PASSWORD\` and \`TWITTER_AUTHENTICATION_SECRET\`: Configure a comma-separated list of Twitter username and password. RSSHub will use this information to log in to Twitter and obtain data using the mobile API. Please note that if you have not logged in with the current IP address before, it is easy to trigger Twitter's risk control mechanism.~~ This no longer works since mobile client attestation has been implemented in October 2025.
- Using \`TWITTER_CONSUMER_KEY\` and \`TWITTER_CONSUMER_SECRET\`: Configure a comma-separated list of Twitter API keys and secrets. RSSHub will use this information to access Twitter's Pay-Per-Use developer API to obtain data.
- OPTIONAL: Using \`TWITTER_ACCESS_TOKEN\` and \`TWITTER_ACCESS_SECRET\`: Configure a comma-separated list of Twitter API access tokens and secrets. RSSHub will use this information to access Twitter's Pay-Per-Use developer API with user authentication to obtain data. If not provided, RSSHub will only use app authentication, which may only access to public information.
`,
lang: 'en',