mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-06 13:08:14 +08:00
110 lines
3.7 KiB
JavaScript
110 lines
3.7 KiB
JavaScript
const { CookieJar, Cookie } = require('tough-cookie');
|
|
const { promisify } = require('util');
|
|
const queryString = require('query-string');
|
|
const got = require('@/utils/got');
|
|
const config = require('@/config').value;
|
|
const constants = require('./constants');
|
|
|
|
const tokens = config.twitter.authorization && config.twitter.authorization.length ? config.twitter.authorization : constants.tokens;
|
|
|
|
// https://github.com/mikf/gallery-dl/blob/a53cfc845e12d9e98fefd07e43ebffaec488c18f/gallery_dl/extractor/twitter.py#L716-L726
|
|
const headers = {
|
|
authorization: undefined,
|
|
'x-guest-token': undefined,
|
|
'x-twitter-auth-type': undefined,
|
|
'x-twitter-client-language': 'en',
|
|
'x-twitter-active-user': 'yes',
|
|
'x-csrf-token': undefined,
|
|
Referer: 'https://twitter.com/',
|
|
};
|
|
|
|
let cookieJar,
|
|
setCookie,
|
|
getCookies,
|
|
tries = 0;
|
|
|
|
const cookiedomain = 'twitter.com';
|
|
const cookieurl = 'https://twitter.com';
|
|
|
|
async function twitterGot(options) {
|
|
const response = await got({
|
|
...options,
|
|
headers: { ...headers, ...(options.headers || {}) },
|
|
cookieJar,
|
|
});
|
|
// 更新csrfToken
|
|
for (const c of await getCookies(cookieurl)) {
|
|
if (c.key === 'ct0') {
|
|
headers['x-csrf-token'] = c.value;
|
|
}
|
|
}
|
|
return response;
|
|
}
|
|
|
|
async function resetSession() {
|
|
cookieJar = new CookieJar();
|
|
getCookies = promisify(cookieJar.getCookies.bind(cookieJar));
|
|
setCookie = promisify(cookieJar.setCookie.bind(cookieJar));
|
|
let response;
|
|
// auth
|
|
headers.authorization = `Basic ${Buffer.from(tokens[tries++ % tokens.length]).toString('base64')}`;
|
|
response = await twitterGot({
|
|
url: 'https://api.twitter.com/oauth2/token',
|
|
method: 'POST',
|
|
searchParams: queryString.stringify({ grant_type: 'client_credentials' }),
|
|
});
|
|
headers.authorization = `Bearer ${response.data.access_token}`;
|
|
// 生成csrf-token
|
|
const csrfToken = [...Array(16 * 2)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
await setCookie(new Cookie({ key: 'ct0', value: csrfToken, domain: cookiedomain, secure: false }), cookieurl);
|
|
headers['x-csrf-token'] = csrfToken;
|
|
headers['x-guest-token'] = undefined;
|
|
// 发起初始化请求
|
|
response = await twitterGot({
|
|
url: 'https://api.twitter.com/1.1/guest/activate.json',
|
|
method: 'POST',
|
|
});
|
|
// 获取guest-token
|
|
// TODO: OAuth2Session, 参见 https://github.com/DIYgod/RSSHub/pull/7739#discussionR655932602
|
|
const guestToken = response.data.guest_token;
|
|
headers['x-guest-token'] = guestToken;
|
|
await setCookie(new Cookie({ key: 'gt', value: guestToken, domain: cookiedomain, secure: false }), cookieurl);
|
|
// 发起第二个初始化请求, 获取_twitter_sess
|
|
await twitterGot({
|
|
url: 'https://twitter.com/i/js_inst',
|
|
method: 'GET',
|
|
searchParams: queryString.stringify({ c_name: 'ui_metrics' }),
|
|
});
|
|
return cookieJar;
|
|
}
|
|
|
|
const initSession = () => cookieJar || resetSession();
|
|
|
|
async function twitterRequest(url, params, method) {
|
|
await initSession();
|
|
// 发起请求
|
|
const request = () =>
|
|
twitterGot({
|
|
url,
|
|
method,
|
|
searchParams: queryString.stringify(params),
|
|
});
|
|
let response;
|
|
try {
|
|
response = await request();
|
|
} catch (e) {
|
|
if (e.response.status === 403 || (e.response.status === 404 && !e.response.data)) {
|
|
await resetSession();
|
|
response = await request();
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
if (response.data.errors) {
|
|
throw Error('API reports error:\n' + response.data.errors.map((e) => `${e.code}: ${e.message}`).join('\n'));
|
|
}
|
|
return response.data;
|
|
}
|
|
|
|
module.exports = twitterRequest;
|