···
1
+
# nitter-guest-account.py
2
+
# cross-platform port of https://github.com/zedeus/nitter/issues/983#issuecomment-1681199357
6
+
from base64 import b64encode
7
+
from argparse import ArgumentParser
12
+
print("\x1b[31m[!] Could not import `requests`.")
13
+
print("\x1b[31m[!] This script requires the requests module to be installed.")
14
+
print("\x1b[31m[!] We apologize but using plain http.client is way too painful."
15
+
" Please reach out with a PR if you would like to change that!")
19
+
noprettyprint = False
22
+
CONSUMER_KEY = "3nVuSoBZnx6U4vzUxf5w"
23
+
CONSUMER_SECRET = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys"
24
+
EXPECTED_BEARER_TOKEN = "Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F"
25
+
BASE_REQUEST_HEADERS = {
26
+
'Content-Type': 'application/json',
27
+
'User-Agent': 'TwitterAndroid/9.95.0-release.0 (29950000-r-0) ONEPLUS+A3010/9 (OnePlus;ONEPLUS+A3010;OnePlus;OnePlus3;0;;1;2016)',
28
+
'X-Twitter-API-Version': '5',
29
+
'X-Twitter-Client': 'TwitterAndroid',
30
+
'X-Twitter-Client-Version': '9.95.0-release.0',
32
+
'System-User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3010 Build/PKQ1.181203.001)',
33
+
'X-Twitter-Active-User': 'yes',
36
+
BASE_URL = "https://api.twitter.com"
37
+
BEARER_TOKEN_ENDPOINT = "/oauth2/token"
38
+
GUEST_TOKEN_ENDPOINT = "/1.1/guest/activate.json"
39
+
FLOW_TOKEN_ENDPOINT = "/1.1/onboarding/task.json?flow_name=welcome&api_version=1&known_device_token=&sim_country_code=us"
40
+
TASKS_ENDPOINT = "/1.1/onboarding/task.json"
42
+
def send_req(method, endpoint, **kwargs) -> requests.Response:
43
+
debug(f"attempting `{endpoint}`")
45
+
res = requests.request(method, BASE_URL + endpoint, **kwargs)
46
+
res.raise_for_status()
47
+
except requests.HTTPError:
48
+
error("HTTP request failed (non 2xx), unable to proceed.")
49
+
error('Please try again in a bit')
50
+
debug(f"request headers => {res.request.headers}")
51
+
debug(f"response headers => {res.headers}")
52
+
debug(f'response body => {res.content}')
55
+
error(f"an unhandled error while sending a request to {endpoint} occurred")
58
+
debug(f'got response body {res.content}')
61
+
B_SUBTASK_VERSIONS = {
64
+
"open_home_timeline": 1,
65
+
"app_locale_update": 1,
67
+
"email_verification": 3,
68
+
"enter_password": 5,
72
+
"single_sign_on": 1,
73
+
"fetch_persisted_data": 1,
74
+
"enter_username": 3,
76
+
"fetch_temporary_password": 1,
78
+
"sign_up_review": 5,
79
+
"interest_picker": 4,
80
+
"user_recommendations_urt": 3,
81
+
"in_app_notification": 1,
83
+
"typeahead_search": 1,
84
+
"user_recommendations_list": 4,
86
+
"contacts_live_sync_permission_prompt": 3,
87
+
"choice_selection": 5,
88
+
"js_instrumentation": 1,
89
+
"alert_dialog_suppress_client_events": 1,
90
+
"privacy_options": 1,
91
+
"topics_selector": 1,
93
+
"tweet_selection_urt": 1,
96
+
"open_external_link": 1,
97
+
"phone_verification": 5,
109
+
"check_logged_in_account": 1,
111
+
"select_avatar": 4,
112
+
"location_permission_prompt": 2,
113
+
"notifications_permission_prompt": 4
116
+
def get_flow_token_body():
118
+
"flow_token": None,
119
+
"input_flow_data": {
120
+
"country_code": None,
122
+
"start_location": {
123
+
"location": "splash_screen"
126
+
"requested_variant": None,
127
+
"target_user_id": 0
129
+
"subtask_versions": B_SUBTASK_VERSIONS
132
+
def get_tasks_body(flow_token: str) -> dict:
134
+
"flow_token": flow_token,
135
+
"subtask_inputs": [{
137
+
"link": "next_link"
139
+
"subtask_id": "NextTaskOpenLink"
141
+
"subtask_versions": B_SUBTASK_VERSIONS
145
+
def format_json(msg, object) -> str:
146
+
return json.dumps(object, indent=None if noprettyprint else 4)
148
+
def debug(msg, *arg, **kwarg) -> None:
151
+
print("\x1b[37m[*]", msg, *arg, "\x1b[0m", file=sys.stderr, **kwarg)
153
+
def info(msg, *arg, **kwarg) -> None:
154
+
print("\x1b[34m[i]", msg, *arg, "\x1b[0m", file=sys.stderr, **kwarg)
157
+
def success(msg, *arg, **kwarg) -> None:
158
+
print("\x1b[32m[i]", msg, *arg, "\x1b[0m", file=sys.stderr, **kwarg)
161
+
def warn(msg, *arg, **kwarg) -> None:
162
+
print("\x1b[33m[!]", msg, *arg, "\x1b[0m", file=sys.stderr, **kwarg)
165
+
def error(msg, *arg, **kwarg) -> None:
166
+
print("\x1b[31m[x]", msg, *arg, "\x1b[0m", file=sys.stderr, **kwarg)
169
+
parser = ArgumentParser()
170
+
parser.add_argument('-v', '--verbose', action='store_true', help="be more noisy")
171
+
parser.add_argument('-P', '--no-pretty', action='store_true', help="disable pretty-printing of json data")
172
+
parser.add_argument(
176
+
help="the json output file to put/append received account data to."
182
+
args = parser.parse_args()
183
+
verbose = args.verbose
185
+
info("nitter-guest-account.py (2023-08-25)")
186
+
info("This is free software: you are free to change and redistribute it, under the terms of the Apache-2.0 license")
187
+
info("There is NO WARRANTY, to the extent permitted by law.")
189
+
info("Fetching bearer token...")
190
+
bt_raw = send_req('post', BEARER_TOKEN_ENDPOINT,
191
+
auth=requests.auth.HTTPBasicAuth(CONSUMER_KEY, CONSUMER_SECRET),
192
+
data={'grant_type': "client_credentials"}
194
+
bearer_token = ' '.join(bt_raw.values())
195
+
if bearer_token.lower() != EXPECTED_BEARER_TOKEN.lower():
196
+
warn('Received bearer token does not match expected value. Continuing anyways, but beware of errors.')
197
+
info(f'bearer token => {bearer_token}')
199
+
success('Received bearer token matches expected value.')
201
+
info("Fetching guest token...")
202
+
guest_token = send_req('post', GUEST_TOKEN_ENDPOINT, headers={'Authorization': bearer_token}).json()['guest_token']
203
+
success(f'guest token => {guest_token}')
205
+
debug('updating header with acquried credentials')
206
+
request_headers = BASE_REQUEST_HEADERS.copy()
207
+
request_headers.update({
208
+
"authorization": bearer_token,
209
+
"X-Guest-Token": guest_token
212
+
info('Fetching flow token...')
213
+
flow_token = send_req('post', FLOW_TOKEN_ENDPOINT, headers=request_headers, json=get_flow_token_body()).json()['flow_token']
214
+
success(f'flow token => {flow_token}')
216
+
info('Fetching final account object...')
217
+
tasks = send_req('post', TASKS_ENDPOINT, headers=request_headers, json=get_tasks_body(flow_token))
223
+
if __name__ == "__main__":