зеркало из
				https://github.com/viginum-datalab/twscrape.git
				synced 2025-10-30 21:46:13 +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