diff --git a/.gitignore b/.gitignore index 0f1bcc4871414c..efd824937cfc20 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ package-lock.json # pnpm-lock.yaml yarn.lock yarn-error.log + +scripts/twitter-token/accounts.txt diff --git a/scripts/twitter-token/generate.js b/scripts/twitter-token/generate.js new file mode 100644 index 00000000000000..bc16f66e84f172 --- /dev/null +++ b/scripts/twitter-token/generate.js @@ -0,0 +1,126 @@ +const got = require('got'); +const { HttpsProxyAgent } = require('https-proxy-agent'); +const fs = require('fs'); +const path = require('path'); + +const concurrency = 5; // Please do not set it too large to avoid Twitter discovering our little secret +const proxyUrl = ''; // Add your proxy here + +const baseURL = 'https://api.twitter.com/1.1/'; +const headers = { + Authorization: 'Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F', + 'User-Agent': 'TwitterAndroid/10.10.0', +}; + +const accounts = []; + +function generateOne() { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + const timeout = setTimeout(() => { + // eslint-disable-next-line no-console + console.log(`Failed to generate account, continue... timeout`); + resolve(); + }, 30000); + + const agent = { + https: proxyUrl && new HttpsProxyAgent(proxyUrl), + }; + + try { + const response = await got.post(`${baseURL}guest/activate.json`, { + headers: { + Authorization: headers.Authorization, + }, + agent, + timeout: { + request: 20000, + }, + }); + const guestToken = JSON.parse(response.body).guest_token; + + const flowResponse = await got.post(`${baseURL}onboarding/task.json?flow_name=welcome`, { + json: { + flow_token: null, + input_flow_data: { + flow_context: { + start_location: { + location: 'splash_screen', + }, + }, + }, + }, + headers: { + ...headers, + 'X-Guest-Token': guestToken, + }, + agent, + timeout: { + request: 20000, + }, + }); + const flowToken = JSON.parse(flowResponse.body).flow_token; + + const finalResponse = await got.post(`${baseURL}onboarding/task.json`, { + json: { + flow_token: flowToken, + subtask_inputs: [ + { + open_link: { + link: 'next_link', + }, + subtask_id: 'NextTaskOpenLink', + }, + ], + }, + headers: { + ...headers, + 'X-Guest-Token': guestToken, + }, + agent, + timeout: { + request: 20000, + }, + }); + + const account = JSON.parse(finalResponse.body).subtasks[0].open_account; + + if (account) { + accounts.push({ + t: account.oauth_token, + s: account.oauth_token_secret, + }); + } else { + // eslint-disable-next-line no-console + console.log(`Failed to generate account, continue... no account`); + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(`Failed to generate account, continue... ${error}`); + } + + clearTimeout(timeout); + resolve(); + }); +} + +(async () => { + const oldAccounts = fs.readFileSync(path.join(__dirname, 'accounts.txt')); + const tokens = oldAccounts.toString().split('\n')[0].split('=')[1].split(','); + const secrets = oldAccounts.toString().split('\n')[1].split('=')[1].split(','); + for (let i = 0; i < tokens.length; i++) { + accounts.push({ + t: tokens[i], + s: secrets[i], + }); + } + + for (let i = 0; i < 1000; i++) { + // eslint-disable-next-line no-console + console.log(`Generating accounts ${i * concurrency}-${(i + 1) * concurrency - 1}, total ${accounts.length}`); + + // eslint-disable-next-line no-await-in-loop + await Promise.all(Array.from({ length: concurrency }, () => generateOne())); + fs.writeFileSync(path.join(__dirname, 'accounts.txt'), [`TWITTER_OAUTH_TOKEN=${accounts.map((account) => account.t).join(',')}`, `TWITTER_OAUTH_TOKEN_SECRET=${accounts.map((account) => account.s).join(',')}`].join('\n')); + } +})(); diff --git a/scripts/twitter-token/generate.sh b/scripts/twitter-token/generate.sh new file mode 100644 index 00000000000000..8f26691f16452c --- /dev/null +++ b/scripts/twitter-token/generate.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +guest_token=$(curl -s -XPOST https://api.twitter.com/1.1/guest/activate.json -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' | jq -r '.guest_token') + +flow_token=$(curl -s -XPOST 'https://api.twitter.com/1.1/onboarding/task.json?flow_name=welcome' \ + -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' \ + -H 'Content-Type: application/json' \ + -H "User-Agent: TwitterAndroid/10.10.0" \ + -H "X-Guest-Token: ${guest_token}" \ + -d '{"flow_token":null,"input_flow_data":{"flow_context":{"start_location":{"location":"splash_screen"}}}}' | jq -r .flow_token) + +curl -s -XPOST 'https://api.twitter.com/1.1/onboarding/task.json' \ + -H 'Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F' \ + -H 'Content-Type: application/json' \ + -H "User-Agent: TwitterAndroid/10.10.0" \ + -H "X-Guest-Token: ${guest_token}" \ + -d "{\"flow_token\":\"${flow_token}\",\"subtask_inputs\":[{\"open_link\":{\"link\":\"next_link\"},\"subtask_id\":\"NextTaskOpenLink\"}]}" | jq -c -r '.subtasks[0]|if(.open_account) then {oauth_token: .open_account.oauth_token, oauth_token_secret: .open_account.oauth_token_secret} else empty end' diff --git a/website/docs/install/config.md b/website/docs/install/config.md index 7d5642cdb00a30..bc6531cb4e66e1 100644 --- a/website/docs/install/config.md +++ b/website/docs/install/config.md @@ -464,7 +464,11 @@ For user data related routes ### Twitter -[Token generation](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) +Given the recent changes in Twitter and its API access a new method for accessing Twitter anonymously was devised. This method involves using temporary guest accounts created when going through the onboarding process with the Android app. + +Please see the details in [Nitter - Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) and [zedeus/nitter#983](https://github.com/zedeus/nitter/issues/983). + +In addition, we have prepared a Node.js script to help you use proxies to create these tokens in batches. [Please click here](https://github.com/DIYgod/RSSHub/tree/master/scripts/twitter-token/generate.js). - `TWITTER_OAUTH_TOKEN`: Support multiple keys, split them with `,` - `TWITTER_OAUTH_TOKEN_SECRET`: Support multiple keys, split them with `,` diff --git a/website/docs/routes/social-media.mdx b/website/docs/routes/social-media.mdx index cd245957f0680b..631e681a07088b 100644 --- a/website/docs/routes/social-media.mdx +++ b/website/docs/routes/social-media.mdx @@ -931,14 +931,6 @@ https://rsshub.app/threads/zuck/showAuthorInTitle=1&showAuthorInDesc=1&showQuote ## Twitter {#twitter} -:::warning - -Given the recent changes in Twitter and its API access a new method for accessing Twitter anonymously was devised. This method involves using temporary guest accounts created when going through the onboarding process with the Android app. - -Please see the details in [Nitter - Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) - -::: - Specify options (in the format of query string) in parameter `routeParams` to control some extra features for Tweets | Key | Description | Accepts | Defaults to | diff --git a/website/i18n/zh/docusaurus-plugin-content-docs/current/install/config.md b/website/i18n/zh/docusaurus-plugin-content-docs/current/install/config.md index 0c0aa11e2d8c84..a7e19f01640084 100644 --- a/website/i18n/zh/docusaurus-plugin-content-docs/current/install/config.md +++ b/website/i18n/zh/docusaurus-plugin-content-docs/current/install/config.md @@ -449,7 +449,11 @@ RSSHub 支持使用访问密钥 / 码,允许清单和拒绝清单三种方式 ### Twitter -[Token 生成](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) +鉴于 Twitter 和其 API 访问的最新变化,已经设计了一种新的匿名访问 Twitter 的方法。该方法涉及使用在通过 Android 应用程序进行注册流程时创建的临时访客账户。 + +详细信息请参阅 [Nitter - Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) 和 [zedeus/nitter#983](https://github.com/zedeus/nitter/issues/983)。 + +另外我们也准备了一个 Node.js 脚本来帮助你使用代理批量创建这些 token,[请点击这里](https://github.com/DIYgod/RSSHub/tree/master/scripts/twitter-token/generate.js)。 - `TWITTER_OAUTH_TOKEN`: 支持多个 key,用英文逗号 `,` 隔开 - `TWITTER_OAUTH_TOKEN_SECRET`: 支持多个 key,用英文逗号 `,` 隔开