Skip to content

Commit

Permalink
docs: add twitter token generation script
Browse files Browse the repository at this point in the history
  • Loading branch information
DIYgod committed Dec 1, 2023
1 parent 5fa1cb6 commit 8558382
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ package-lock.json
# pnpm-lock.yaml
yarn.lock
yarn-error.log

scripts/twitter-token/accounts.txt
126 changes: 126 additions & 0 deletions scripts/twitter-token/generate.js
Original file line number Diff line number Diff line change
@@ -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'));
}
})();
17 changes: 17 additions & 0 deletions scripts/twitter-token/generate.sh
Original file line number Diff line number Diff line change
@@ -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'
6 changes: 5 additions & 1 deletion website/docs/install/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `,`
Expand Down
8 changes: 0 additions & 8 deletions website/docs/routes/social-media.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,用英文逗号 `,` 隔开
Expand Down

0 comments on commit 8558382

Please sign in to comment.