diff --git a/lib/config.ts b/lib/config.ts index fda6dd036..67ea9b99a 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -268,7 +268,6 @@ export type Config = { username?: string[]; password?: string[]; authenticationSecret?: string[]; - cookie?: string; authToken?: string[]; }; weibo: { @@ -639,7 +638,6 @@ const calculateValue = () => { username: envs.TWITTER_USERNAME?.split(','), password: envs.TWITTER_PASSWORD?.split(','), authenticationSecret: envs.TWITTER_AUTHENTICATION_SECRET?.split(','), - cookie: envs.TWITTER_COOKIE, authToken: envs.TWITTER_AUTH_TOKEN?.split(','), }, weibo: { diff --git a/lib/routes/twitter/api/index.ts b/lib/routes/twitter/api/index.ts index 0f769c021..ed1acd069 100644 --- a/lib/routes/twitter/api/index.ts +++ b/lib/routes/twitter/api/index.ts @@ -4,7 +4,7 @@ import webApi from './web-api/api'; import { config } from '@/config'; const enableMobileApi = config.twitter.username && config.twitter.password; -const enableWebApi = config.twitter.cookie || config.twitter.authToken; +const enableWebApi = config.twitter.authToken; type ApiItem = (id: string, params?: Record) => Promise> | Record | null; let api: { diff --git a/lib/routes/twitter/api/web-api/utils.ts b/lib/routes/twitter/api/web-api/utils.ts index 6db758ee8..ea8630637 100644 --- a/lib/routes/twitter/api/web-api/utils.ts +++ b/lib/routes/twitter/api/web-api/utils.ts @@ -5,63 +5,53 @@ import got from '@/utils/got'; import queryString from 'query-string'; import { Cookie, CookieJar } from 'tough-cookie'; import { CookieAgent } from 'http-cookie-agent/undici'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; const dispatchers = {}; let authTokenIndex = 0; +const token2Cookie = (token) => + cache.tryGet(`twitter:cookie:${token}`, async () => { + const jar = new CookieJar(); + jar.setCookieSync(`auth_token=${token}`, 'https://x.com'); + try { + await got('https://x.com', { + dispatcher: new CookieAgent({ cookies: { jar } }), + }); + return JSON.stringify(jar.serializeSync()); + } catch { + // ignore + return ''; + } + }); + export const twitterGot = async (url, params) => { - if (!config.twitter.cookie && !config.twitter.authToken) { + if (!config.twitter.authToken) { throw new ConfigNotFoundError('Twitter cookie is not configured'); } - let requestData; - if (config.twitter.cookie) { - const jsonCookie = Object.fromEntries( - config.twitter.cookie - .split(';') - .map((c) => Cookie.parse(c)?.toJSON()) - .map((c) => [c?.key, c?.value]) - ); - if (!jsonCookie || !jsonCookie.auth_token || !jsonCookie.ct0) { - throw new ConfigNotFoundError('Twitter cookie is not valid'); + const token = config.twitter.authToken[authTokenIndex++ % config.twitter.authToken.length]; + let cookie = await token2Cookie(token); + if (cookie) { + logger.debug(`Got twitter cookie for token ${token}`); + if (typeof cookie === 'string') { + cookie = JSON.parse(cookie); } - - requestData = { - headers: { - cookie: config.twitter.cookie, - 'x-csrf-token': jsonCookie.ct0, - }, - }; - } else if (config.twitter.authToken) { - const token = config.twitter.authToken[authTokenIndex++ % config.twitter.authToken.length]; - if (!dispatchers[token]) { - const jar = new CookieJar(); - jar.setCookieSync(`auth_token=${token}`, 'https://x.com'); - dispatchers[token] = { - jar, - agent: new CookieAgent({ cookies: { jar } }), - }; - try { - await got('https://x.com', { - dispatcher: dispatchers[token].agent, - }); - } catch { - // ignore - } - } - const jsonCookie = Object.fromEntries( - dispatchers[token].jar - .getCookieStringSync(url) - .split(';') - .map((c) => Cookie.parse(c)?.toJSON()) - .map((c) => [c?.key, c?.value]) - ); - requestData = { - headers: { - 'x-csrf-token': jsonCookie.ct0, - }, - dispatcher: dispatchers[token].agent, + const jar = CookieJar.deserializeSync(cookie as any); + dispatchers[token] = { + jar, + agent: new CookieAgent({ cookies: { jar } }), }; + } else { + throw new ConfigNotFoundError(`Twitter cookie for token ${token} is not valid`); } + const jsonCookie = Object.fromEntries( + dispatchers[token].jar + .getCookieStringSync(url) + .split(';') + .map((c) => Cookie.parse(c)?.toJSON()) + .map((c) => [c?.key, c?.value]) + ); const response = await got(`${url}?${queryString.stringify(params)}`, { headers: { @@ -77,11 +67,16 @@ export const twitterGot = async (url, params) => { 'x-twitter-active-user': 'yes', 'x-twitter-auth-type': 'OAuth2Session', 'x-twitter-client-language': 'en', - ...requestData.headers, + 'x-csrf-token': jsonCookie.ct0, }, - dispatcher: requestData.dispatcher, + dispatcher: dispatchers[token].agent, }); + if (token) { + logger.debug(`Reset twitter cookie for token ${token}`); + await cache.set(`twitter:cookie:${token}`, JSON.stringify(dispatchers[token].jar.serializeSync()), config.cache.contentExpire); + } + return response.data; };