зеркало из
				https://github.com/viginum-datalab/twscrape.git
				synced 2025-10-31 05:56:24 +02:00 
			
		
		
		
	add basic login flow
Этот коммит содержится в:
		
							родитель
							
								
									25c7ceb2f8
								
							
						
					
					
						Коммит
						60e08624c5
					
				
							
								
								
									
										188
									
								
								twapi/client.py
									
									
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										188
									
								
								twapi/client.py
									
									
									
									
									
										Обычный файл
									
								
							| @ -0,0 +1,188 @@ | |||||||
|  | from fake_useragent import UserAgent | ||||||
|  | from httpx import Client, HTTPStatusError, Response | ||||||
|  | 
 | ||||||
|  | TOKEN = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" | ||||||
|  | TASK_URL = "https://api.twitter.com/1.1/onboarding/task.json" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def raise_for_status(rep: Response, label: str): | ||||||
|  |     try: | ||||||
|  |         rep.raise_for_status() | ||||||
|  |     except HTTPStatusError: | ||||||
|  |         raise Exception(f"{label} - {rep.status_code} - {rep.text}") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_get_guest_token(client: Client) -> str: | ||||||
|  |     rep = client.post("https://api.twitter.com/1.1/guest/activate.json") | ||||||
|  |     raise_for_status(rep, "guest_token") | ||||||
|  |     return rep.json()["guest_token"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_initiate(client: Client) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "input_flow_data": { | ||||||
|  |             "flow_context": {"debug_overrides": {}, "start_location": {"location": "unknown"}} | ||||||
|  |         }, | ||||||
|  |         "subtask_versions": {}, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, params={"flow_name": "login"}, json=payload) | ||||||
|  |     raise_for_status(rep, "login_initiate") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_instrumentation(client, flow_token: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [ | ||||||
|  |             { | ||||||
|  |                 "subtask_id": "LoginJsInstrumentationSubtask", | ||||||
|  |                 "js_instrumentation": {"response": "{}", "link": "next_link"}, | ||||||
|  |             } | ||||||
|  |         ], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_instrumentation") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_enter_username(client: Client, flow_token: str, username: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [ | ||||||
|  |             { | ||||||
|  |                 "subtask_id": "LoginEnterUserIdentifierSSO", | ||||||
|  |                 "settings_list": { | ||||||
|  |                     "setting_responses": [ | ||||||
|  |                         { | ||||||
|  |                             "key": "user_identifier", | ||||||
|  |                             "response_data": {"text_data": {"result": username}}, | ||||||
|  |                         } | ||||||
|  |                     ], | ||||||
|  |                     "link": "next_link", | ||||||
|  |                 }, | ||||||
|  |             } | ||||||
|  |         ], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_username") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_enter_password(client: Client, flow_token: str, password: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [ | ||||||
|  |             { | ||||||
|  |                 "subtask_id": "LoginEnterPassword", | ||||||
|  |                 "enter_password": {"password": password, "link": "next_link"}, | ||||||
|  |             } | ||||||
|  |         ], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_password") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_duplication_check(client: Client, flow_token: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [ | ||||||
|  |             { | ||||||
|  |                 "subtask_id": "AccountDuplicationCheck", | ||||||
|  |                 "check_logged_in_account": {"link": "AccountDuplicationCheck_false"}, | ||||||
|  |             } | ||||||
|  |         ], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_duplication_check") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_confirm_email(client: Client, flow_token: str, email: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [ | ||||||
|  |             { | ||||||
|  |                 "subtask_id": "LoginAcid", | ||||||
|  |                 "enter_text": {"text": email, "link": "next_link"}, | ||||||
|  |             } | ||||||
|  |         ], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_confirm_email") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def login_success(client: Client, flow_token: str) -> Response: | ||||||
|  |     payload = { | ||||||
|  |         "flow_token": flow_token, | ||||||
|  |         "subtask_inputs": [], | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rep = client.post(TASK_URL, json=payload) | ||||||
|  |     raise_for_status(rep, "login_success") | ||||||
|  |     return rep | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class UserClient: | ||||||
|  |     def __init__(self, username: str, password: str, email: str): | ||||||
|  |         self.username = username | ||||||
|  |         self.password = password | ||||||
|  |         self.email = email | ||||||
|  |         self.client = Client() | ||||||
|  | 
 | ||||||
|  |     def _next_login_task(self, rep: Response): | ||||||
|  |         client = self.client | ||||||
|  |         data = rep.json() | ||||||
|  | 
 | ||||||
|  |         # print("-" * 20) | ||||||
|  |         # print([x["subtask_id"] for x in data["subtasks"]]) | ||||||
|  |         # print(rep.text) | ||||||
|  | 
 | ||||||
|  |         flow_token = data["flow_token"] | ||||||
|  |         for x in data["subtasks"]: | ||||||
|  |             task_id = x["subtask_id"] | ||||||
|  | 
 | ||||||
|  |             if task_id == "LoginSuccessSubtask": | ||||||
|  |                 return login_success(client, flow_token) | ||||||
|  |             if task_id == "LoginAcid": | ||||||
|  |                 return login_confirm_email(client, flow_token, self.email) | ||||||
|  |             if task_id == "AccountDuplicationCheck": | ||||||
|  |                 return login_duplication_check(client, flow_token) | ||||||
|  |             if task_id == "LoginEnterPassword": | ||||||
|  |                 return login_enter_password(client, flow_token, self.password) | ||||||
|  |             if task_id == "LoginEnterUserIdentifierSSO": | ||||||
|  |                 return login_enter_username(client, flow_token, self.username) | ||||||
|  |             if task_id == "LoginJsInstrumentationSubtask": | ||||||
|  |                 return login_instrumentation(client, flow_token) | ||||||
|  | 
 | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     def login(self): | ||||||
|  |         guest_token = login_get_guest_token(self.client) | ||||||
|  |         headers = { | ||||||
|  |             "authorization": TOKEN, | ||||||
|  |             "user-agent": UserAgent().safari, | ||||||
|  |             "x-twitter-active-user": "yes", | ||||||
|  |             "x-twitter-client-language": "en", | ||||||
|  |             "x-guest-token": guest_token, | ||||||
|  |             "content-type": "application/json", | ||||||
|  |         } | ||||||
|  |         self.client.headers.update(headers) | ||||||
|  | 
 | ||||||
|  |         rep = login_initiate(self.client) | ||||||
|  |         while True: | ||||||
|  |             rep = self._next_login_task(rep) | ||||||
|  |             if rep is None: | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |         self.client.headers["x-csrf-token"] = self.client.cookies["ct0"] | ||||||
|  |         self.client.headers["x-twitter-auth-type"] = "OAuth2Session" | ||||||
|  | 
 | ||||||
|  |         print(f"login success for {self.username}") | ||||||
		Загрузка…
	
	
			
			x
			
			
		
	
		Ссылка в новой задаче
	
	Block a user
	 Vlad Pronsky
						Vlad Pronsky