letterm commited on
Commit
1c670a9
·
verified ·
1 Parent(s): 40d1fa2

Delete login_client.py

Browse files
Files changed (1) hide show
  1. login_client.py +0 -375
login_client.py DELETED
@@ -1,375 +0,0 @@
1
- """
2
- 登录客户端模块
3
- 处理Warp登录相关功能,包括获取refresh token
4
- """
5
- import re
6
- import requests
7
- import urllib.parse
8
- from typing import Optional, Dict, List, Tuple
9
- from concurrent.futures import ThreadPoolExecutor
10
- from loguru import logger
11
-
12
- from config import Config
13
- from protobuf_manager import ProtobufManager
14
-
15
-
16
- class LoginClient:
17
- """Warp登录客户端"""
18
-
19
- def __init__(self):
20
- self.session = requests.Session()
21
-
22
- def create_protobuf_data(self, login_url: str) -> Optional[bytes]:
23
- """创建登录请求的protobuf数据"""
24
- return ProtobufManager.create_login_request(login_url)
25
-
26
- def get_oob_code(self, login_url: str) -> Optional[str]:
27
- """获取oob_code"""
28
- logger.debug(f"🔄 开始获取oob_code,URL: {login_url[:50]}...")
29
-
30
- protobuf_data = self.create_protobuf_data(login_url)
31
- if not protobuf_data:
32
- return None
33
-
34
- headers = {
35
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
36
- "Connection": "keep-alive",
37
- "Accept": "*/*",
38
- "Accept-Encoding": "gzip, deflate, br, zstd",
39
- "Content-Type": "application/octet-stream",
40
- "x-goog-api-key": "AIzaSyA2KlwBX3mkFo30om9LUFYQhpqLoa_BNhE",
41
- "content-type": "application/x-protobuf",
42
- "x-client-data": "CK+1yQEIjLbJAQimtskBCKmdygEI74zLAQiVocsBCJKjywEIhaDNAQjY7c4BCOLwzgEIpfLOAQi88s4BCJP2zgEYnPPOAQ==",
43
- "sec-fetch-site": "none",
44
- "sec-fetch-mode": "no-cors",
45
- "sec-fetch-dest": "empty",
46
- "accept-language": "zh-CN,zh;q=0.9",
47
- "priority": "u=4, i"
48
- }
49
-
50
- try:
51
- response = self.session.post(
52
- Config.OPTIMIZATION_GUIDE_URL,
53
- headers=headers,
54
- data=protobuf_data,
55
- timeout=60
56
- )
57
-
58
- if response.status_code == 200:
59
- logger.info(f"🔄 响应内容: {response.text}")
60
-
61
- # 使用ProtobufManager解析响应
62
- decoded_url = ProtobufManager.parse_login_response(response.content)
63
- logger.info(f"🔄 解码后的URL: {decoded_url}")
64
- if decoded_url == "":
65
- logger.error("❌ 解码后的URL为空")
66
- decoded_url = response.text
67
-
68
- # 提取oob_code
69
- pattern = r'oobCode=([^&]+)'
70
- match = re.search(pattern, decoded_url)
71
- if match:
72
- oob_code = match.group(1)
73
- logger.success(f"✅ 获取到oob_code: {oob_code[:20]}...")
74
- return oob_code
75
- else:
76
- logger.error("❌ 未能从响应中提取oob_code")
77
- return None
78
- else:
79
- logger.error(f"❌ 请求失败: {response.status_code}")
80
- return None
81
-
82
- except Exception as e:
83
- logger.error(f"❌ 获取oob_code时出错: {e}")
84
- return None
85
-
86
- def verify_id_token(self, id_token: str) -> bool:
87
- """验证idToken并使refresh_token生效"""
88
- logger.debug(f"🔍 开始验证idToken: {id_token[:20]}...")
89
-
90
- headers = {
91
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
92
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
93
- 'Content-Type': 'application/json',
94
- 'sec-ch-ua-platform': '"Windows"',
95
- 'x-client-version': 'Chrome/JsCore/11.3.1/FirebaseCore-web',
96
- 'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
97
- 'sec-ch-ua-mobile': '?0',
98
- 'x-firebase-gmpid': '1:13153726198:web:1cc16bca7287752f8e06d7',
99
- 'origin': 'https://app.warp.dev',
100
- 'x-browser-channel': 'stable',
101
- 'x-browser-year': '2025',
102
- 'x-browser-validation': '6h3XF8YcD8syi2FF2BbuE2KllQo=',
103
- 'x-browser-copyright': 'Copyright 2025 Google LLC. All rights reserved.',
104
- 'x-client-data': 'CK+1yQEIjLbJAQimtskBCKmdygEI74zLAQiWocsBCJKjywEIhaDNAQji8M4BCJP2zgE=',
105
- 'sec-fetch-site': 'cross-site',
106
- 'sec-fetch-mode': 'cors',
107
- 'sec-fetch-dest': 'empty',
108
- 'accept-language': 'zh-CN,zh;q=0.9',
109
- 'priority': 'u=1, i'
110
- }
111
-
112
- payload = {
113
- 'idToken': id_token
114
- }
115
-
116
- try:
117
- response = self.session.post(
118
- 'https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=AIzaSyBdy3O3S9hrdayLJxJ7mriBR4qgUaUygAs',
119
- headers=headers,
120
- json=payload,
121
- timeout=30
122
- )
123
-
124
- if response.status_code == 200:
125
- logger.success("✅ idToken验证成功,refresh_token已生效")
126
- return True
127
- else:
128
- logger.error(f"❌ idToken验证失败,状态码: {response.status_code}")
129
- logger.debug(f"❌ 响应内容: {response.text}")
130
- return False
131
-
132
- except Exception as e:
133
- logger.error(f"❌ 验证idToken时出错: {e}")
134
- return False
135
-
136
- def create_user_with_access_token(self, access_token: str) -> bool:
137
- """使用access_token创建用户"""
138
- logger.info(f"👤 开始创建用户,access_token: {access_token[:20]}...")
139
-
140
- headers = {
141
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
142
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
143
- 'Content-Type': 'application/json',
144
- 'sec-ch-ua-platform': '"Windows"',
145
- 'authorization': f'Bearer {access_token}',
146
- 'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
147
- 'sec-ch-ua-mobile': '?0',
148
- 'origin': 'https://app.warp.dev',
149
- 'sec-fetch-site': 'same-origin',
150
- 'sec-fetch-mode': 'cors',
151
- 'sec-fetch-dest': 'empty',
152
- 'accept-language': 'zh-CN,zh;q=0.9',
153
- 'priority': 'u=1, i',
154
- 'Cookie': 'rl_anonymous_id=8b178568-76c6-4e9f-8c72-461bd1909c1c'
155
- }
156
-
157
- payload = {
158
- "operationName": "GetOrCreateUser",
159
- "variables": {
160
- "input": {},
161
- "requestContext": {
162
- "osContext": {},
163
- "clientContext": {}
164
- }
165
- },
166
- "query": "mutation GetOrCreateUser($input: GetOrCreateUserInput!, $requestContext: RequestContext!) {\n getOrCreateUser(requestContext: $requestContext, input: $input) {\n ... on GetOrCreateUserOutput {\n uid\n isOnboarded\n anonymousUserInfo {\n anonymousUserType\n linkedAt\n __typename\n }\n workspaces {\n joinableTeams {\n teamUid\n numMembers\n name\n teamAcceptingInvites\n __typename\n }\n __typename\n }\n deletedAnonymousUser\n __typename\n }\n ... on UserFacingError {\n error {\n message\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"
167
- }
168
-
169
- try:
170
- response = self.session.post(
171
- 'https://app.warp.dev/graphql/v2?op=GetOrCreateUser',
172
- headers=headers,
173
- json=payload,
174
- timeout=30
175
- )
176
-
177
- if response.status_code == 200:
178
- response_data = response.json()
179
- # 检查是否有错误
180
- if 'errors' in response_data:
181
- logger.error(f"❌ 创建用户失败,GraphQL错误: {response_data['errors']}")
182
- return False
183
-
184
- # 检查响应数据
185
- data = response_data.get('data', {})
186
- get_or_create_user = data.get('getOrCreateUser', {})
187
-
188
- if 'uid' in get_or_create_user:
189
- logger.success(f"✅ 用户创建成功,UID: {get_or_create_user['uid']}")
190
- return True
191
- elif 'error' in get_or_create_user:
192
- logger.error(f"❌ 创建用户失败: {get_or_create_user['error']}")
193
- return False
194
- else:
195
- logger.success("✅ 用户创建请求已发送")
196
- return True
197
-
198
- else:
199
- logger.error(f"❌ 创建用户失败,状态码: {response.status_code}")
200
- logger.debug(f"❌ 响应内容: {response.text}")
201
- return False
202
-
203
- except Exception as e:
204
- logger.error(f"❌ 创建用户时出错: {e}")
205
- return False
206
-
207
- def get_refresh_token(self, email: str, oob_code: str) -> Optional[str]:
208
- """使用email和oob_code获取refresh token,并验证idToken"""
209
- logger.info(f"🔄 开始获取refresh token for {email}")
210
-
211
- headers = {
212
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
213
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
214
- 'Content-Type': 'application/json',
215
- 'sec-ch-ua-platform': 'Windows',
216
- 'x-client-version': 'Chrome/JsCore/11.3.1/FirebaseCore-web',
217
- 'sec-ch-ua': '"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
218
- 'sec-ch-ua-mobile': '?0',
219
- 'x-firebase-gmpid': '1:13153726198:web:1cc16bca7287752f8e06d7',
220
- 'origin': 'https://app.warp.dev',
221
- 'x-client-data': 'CK+1yQEIjLbJAQimtskBCKmdygEI74zLAQiVocsBCJKjywEIhaDNAQji8M0BCKXyzgEIk/bOAQ==',
222
- 'sec-fetch-site': 'cross-site',
223
- 'sec-fetch-mode': 'cors',
224
- 'sec-fetch-dest': 'empty',
225
- 'accept-language': 'zh-CN,zh;q=0.9',
226
- 'priority': 'u=1, i'
227
- }
228
-
229
- payload = {
230
- 'email': email,
231
- 'oobCode': oob_code
232
- }
233
-
234
- try:
235
- response = self.session.post(Config.FIREBASE_LOGIN_URL, headers=headers, json=payload, timeout=30)
236
-
237
- if response.status_code == 200:
238
- response_data = response.json()
239
- refresh_token = response_data.get("refreshToken")
240
- id_token = response_data.get("idToken")
241
-
242
- if not refresh_token:
243
- logger.error(f"❌ 响应中未找到refreshToken字段 for {email}")
244
- return None
245
-
246
- if not id_token:
247
- logger.error(f"❌ 响应中未找到idToken字段 for {email}")
248
- return None
249
-
250
- logger.success(f"✅ 获取到refresh token和idToken for {email}")
251
-
252
- # 验证idToken使refresh_token生效
253
- if self.verify_id_token(id_token):
254
- logger.success(f"🎉 refresh token验证成功并已生效 for {email}")
255
- return refresh_token
256
- else:
257
- logger.error(f"❌ refresh token验证失败,设置为无效 for {email}")
258
- return None
259
-
260
- else:
261
- logger.error(f"❌ 获取refresh token失败 for {email}: {response.status_code}")
262
- logger.debug(f"❌ 响应内容: {response.text}")
263
- return None
264
-
265
- except Exception as e:
266
- logger.error(f"❌ 获取refresh token时出错 for {email}: {e}")
267
- return None
268
-
269
- def process_single_email_with_user_creation(self, email: str, login_url: str, token_manager=None) -> Tuple[str, Optional[str], str]:
270
- """处理单个email,返回(email, refresh_token, status),并在获取refresh_token后创建用户"""
271
- logger.info(f"🚀 开始处理邮箱: {email}")
272
-
273
- try:
274
- # 获取oob_code
275
- oob_code = self.get_oob_code(login_url)
276
- if not oob_code:
277
- return email, None, "获取oob_code失败"
278
-
279
- # 获取refresh token并验证
280
- refresh_token = self.get_refresh_token(email, oob_code)
281
- if refresh_token:
282
- logger.success(f"✅ 邮箱 {email} refresh_token获取成功")
283
-
284
- # 如果有token_manager,立即获取access_token并创建用户
285
- if token_manager:
286
- logger.info(f"🔄 正在为 {email} 获取access_token并创建用户...")
287
- access_token = token_manager.get_access_token(refresh_token)
288
- if access_token:
289
- logger.info(f"✅ 为 {email} 获取到access_token")
290
- if self.create_user_with_access_token(access_token):
291
- logger.success(f"🎉 为 {email} 创建用户成功")
292
- return email, refresh_token, "成功并已创建用户"
293
- else:
294
- logger.warning(f"⚠️ 为 {email} 创建用户失败,但refresh_token有效")
295
- return email, refresh_token, "成功,但创建用户失败"
296
- else:
297
- logger.warning(f"⚠️ 为 {email} 获取access_token失败")
298
- return email, refresh_token, "成功,但获取access_token失败"
299
- else:
300
- return email, refresh_token, "成功"
301
- else:
302
- logger.error(f"❌ 邮箱 {email} 处理失败")
303
- return email, None, "获取refresh_token失败或验证失败"
304
-
305
- except Exception as e:
306
- logger.error(f"❌ 处理邮箱 {email} 时异常: {e}")
307
- return email, None, f"处理异常: {str(e)}"
308
-
309
- def process_single_email(self, email: str, login_url: str) -> Tuple[str, Optional[str], str]:
310
- """处理单个email,返回(email, refresh_token, status)"""
311
- logger.info(f"🚀 开始处理邮箱: {email}")
312
-
313
- try:
314
- # 获取oob_code
315
- oob_code = self.get_oob_code(login_url)
316
- if not oob_code:
317
- return email, None, "获取oob_code失败"
318
-
319
- # 获取refresh token并验证
320
- refresh_token = self.get_refresh_token(email, oob_code)
321
- if refresh_token:
322
- logger.success(f"✅ 邮箱 {email} 处理成功")
323
- return email, refresh_token, "成功"
324
- else:
325
- logger.error(f"❌ 邮箱 {email} 处理失败")
326
- return email, None, "获取refresh_token失败或验证失败"
327
-
328
- except Exception as e:
329
- logger.error(f"❌ 处理邮箱 {email} 时异常: {e}")
330
- return email, None, f"处理异常: {str(e)}"
331
-
332
- def batch_process_emails(self, email_url_dict: Dict[str, str], max_workers: int = 5, token_manager=None) -> Dict[str, Dict]:
333
- """批量处理emails,返回结果字典"""
334
- logger.info(f"🚀 开始批量处理 {len(email_url_dict)} 个邮箱,并发数: {max_workers}")
335
- results = {}
336
-
337
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
338
- # 提交所有任务
339
- if token_manager:
340
- future_to_email = {
341
- executor.submit(self.process_single_email_with_user_creation, email, url, token_manager): email
342
- for email, url in email_url_dict.items()
343
- }
344
- else:
345
- future_to_email = {
346
- executor.submit(self.process_single_email, email, url): email
347
- for email, url in email_url_dict.items()
348
- }
349
-
350
- # 收集结果
351
- for future in future_to_email:
352
- email = future_to_email[future]
353
- try:
354
- email, refresh_token, status = future.result()
355
- results[email] = {
356
- 'refresh_token': refresh_token,
357
- 'status': status
358
- }
359
-
360
- if refresh_token:
361
- logger.success(f"✅ {email}: {status}")
362
- else:
363
- logger.error(f"❌ {email}: {status}")
364
-
365
- except Exception as e:
366
- logger.error(f"❌ {email}: 处理异常: {str(e)}")
367
- results[email] = {
368
- 'refresh_token': None,
369
- 'status': f"处理异常: {str(e)}"
370
- }
371
-
372
- success_count = sum(1 for result in results.values() if result['refresh_token'])
373
- logger.info(f"📊 批量处理完成: {success_count}/{len(email_url_dict)} 成功")
374
-
375
- return results