dfa32412 commited on
Commit
31d6236
·
verified ·
1 Parent(s): 29a9a59

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +101 -47
  2. get_xid.py +142 -131
app.py CHANGED
@@ -14,12 +14,15 @@ from flask import Flask, request, Response, jsonify, stream_with_context, render
14
  from curl_cffi import requests as curl_requests
15
  from werkzeug.middleware.proxy_fix import ProxyFix
16
  import get_xid
 
 
17
 
18
  current_dir = os.path.dirname(os.path.abspath(__file__))
19
  env_path = os.path.join(current_dir, '.env')
20
 
21
  load_dotenv(env_path)
22
 
 
23
  class Logger:
24
  def __init__(self, level="INFO", colorize=True, format=None):
25
  logger.remove()
@@ -85,8 +88,8 @@ class Logger:
85
  caller_info = self._get_caller_info()
86
  self.logger.bind(**caller_info).info(f"请求: {request.method} {request.path}", "Request")
87
 
88
- logger = Logger(level="INFO")
89
 
 
90
 
91
  CONFIG = {
92
  "XIDS": None,
@@ -113,11 +116,11 @@ CONFIG = {
113
  },
114
  "ADMIN": {
115
  "MANAGER_SWITCH": os.getenv("MANAGER_SWITCH") or None,
116
- "PASSWORD": os.getenv("ADMINPASSWORD") or None
117
  },
118
  "SERVER": {
119
  "COOKIE": None,
120
- "CF_CLEARANCE":os.getenv("CF_CLEARANCE") or None,
121
  "PORT": int(os.getenv("PORT", 5200))
122
  },
123
  "RETRY": {
@@ -131,7 +134,6 @@ CONFIG = {
131
  "ISSHOW_SEARCH_RESULTS": os.getenv("ISSHOW_SEARCH_RESULTS", "true").lower() == "true"
132
  }
133
 
134
-
135
  DEFAULT_HEADERS = {
136
  'Accept': '*/*',
137
  'Accept-Language': 'zh-CN,zh;q=0.9',
@@ -152,6 +154,7 @@ DEFAULT_HEADERS = {
152
  'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
153
  }
154
 
 
155
  class AuthTokenManager:
156
  def __init__(self):
157
  self.token_model_map = {}
@@ -187,7 +190,8 @@ class AuthTokenManager:
187
  if sso not in self.token_status_map:
188
  self.token_status_map[sso] = {}
189
 
190
- existing_token_entry = next((entry for entry in self.token_model_map[model] if entry["token"] == token), None)
 
191
 
192
  if not existing_token_entry:
193
  self.token_model_map[model].append({
@@ -224,7 +228,8 @@ class AuthTokenManager:
224
  try:
225
  sso = token.split("sso=")[1].split(";")[0]
226
  for model in self.token_model_map:
227
- self.token_model_map[model] = [entry for entry in self.token_model_map[model] if entry["token"] != token]
 
228
 
229
  if sso in self.token_status_map:
230
  del self.token_status_map[sso]
@@ -234,39 +239,41 @@ class AuthTokenManager:
234
  except Exception as error:
235
  logger.error(f"令牌删除失败: {str(error)}")
236
  return False
 
237
  def reduce_token_request_count(self, model_id, count):
238
  try:
239
  normalized_model = self.normalize_model_name(model_id)
240
-
241
  if normalized_model not in self.token_model_map:
242
  logger.error(f"模型 {normalized_model} 不存在", "TokenManager")
243
  return False
244
-
245
  if not self.token_model_map[normalized_model]:
246
  logger.error(f"模型 {normalized_model} 没有可用的token", "TokenManager")
247
  return False
248
-
249
  token_entry = self.token_model_map[normalized_model][0]
250
-
251
  # 确保RequestCount不会小于0
252
  new_count = max(0, token_entry["RequestCount"] - count)
253
  reduction = token_entry["RequestCount"] - new_count
254
-
255
  token_entry["RequestCount"] = new_count
256
-
257
  # 更新token状态
258
  if token_entry["token"]:
259
  sso = token_entry["token"].split("sso=")[1].split(";")[0]
260
  if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:
261
  self.token_status_map[sso][normalized_model]["totalRequestCount"] = max(
262
- 0,
263
  self.token_status_map[sso][normalized_model]["totalRequestCount"] - reduction
264
  )
265
  return True
266
-
267
  except Exception as error:
268
  logger.error(f"重置校对token请求次数时发生错误: {str(error)}", "TokenManager")
269
  return False
 
270
  def get_next_token_for_model(self, model_id, is_return=False):
271
  normalized_model = self.normalize_model_name(model_id)
272
 
@@ -289,7 +296,8 @@ class AuthTokenManager:
289
 
290
  if token_entry["RequestCount"] > self.model_config[normalized_model]["RequestFrequency"]:
291
  self.remove_token_from_model(normalized_model, token_entry["token"])
292
- next_token_entry = self.token_model_map[normalized_model][0] if self.token_model_map[normalized_model] else None
 
293
  return next_token_entry["token"] if next_token_entry else None
294
 
295
  sso = token_entry["token"].split("sso=")[1].split(";")[0]
@@ -428,6 +436,7 @@ class AuthTokenManager:
428
  for entry in model_tokens:
429
  all_tokens.add(entry["token"])
430
  return list(all_tokens)
 
431
  def get_current_token(self, model_id):
432
  normalized_model = self.normalize_model_name(model_id)
433
 
@@ -440,6 +449,7 @@ class AuthTokenManager:
440
  def get_token_status_map(self):
441
  return self.token_status_map
442
 
 
443
  class Utils:
444
  @staticmethod
445
  def organize_search_results(search_results):
@@ -470,19 +480,20 @@ class Utils:
470
 
471
  if proxy:
472
  logger.info(f"使用代理: {proxy}", "Server")
473
-
474
  if proxy.startswith("socks5://"):
475
  proxy_options["proxy"] = proxy
476
-
477
  if '@' in proxy:
478
  auth_part = proxy.split('@')[0].split('://')[1]
479
  if ':' in auth_part:
480
  username, password = auth_part.split(':')
481
  proxy_options["proxy_auth"] = (username, password)
482
  else:
483
- proxy_options["proxies"] = {"https": proxy, "http": proxy}
484
  return proxy_options
485
 
 
486
  class GrokApiClient:
487
  def __init__(self, model_id):
488
  if model_id not in CONFIG["MODELS"]:
@@ -509,6 +520,7 @@ class GrokApiClient:
509
  "mimeType": mime_type,
510
  "fileName": file_name
511
  }
 
512
  def upload_base64_file(self, message, model):
513
  try:
514
  message_base64 = base64.b64encode(message.encode('utf-8')).decode('utf-8')
@@ -519,13 +531,13 @@ class GrokApiClient:
519
  }
520
 
521
  logger.info("发送文字文件请求", "Server")
522
- cookie = f"{Utils.create_auth_headers(model, True)};{CONFIG['SERVER']['CF_CLEARANCE']}"
523
  proxy_options = Utils.get_proxy_options()
524
  response = curl_requests.post(
525
  "https://grok.com/rest/app-chat/upload-file",
526
  headers={
527
  **DEFAULT_HEADERS,
528
- "Cookie":cookie
529
  },
530
  json=upload_data,
531
  impersonate="chrome133a",
@@ -544,6 +556,7 @@ class GrokApiClient:
544
  except Exception as error:
545
  logger.error(str(error), "Server")
546
  raise Exception(f"上传文件失败,状态码:{response.status_code}")
 
547
  def upload_base64_image(self, base64_data, url):
548
  try:
549
  if 'data:image' in base64_data:
@@ -571,7 +584,7 @@ class GrokApiClient:
571
  url,
572
  headers={
573
  **DEFAULT_HEADERS,
574
- "Cookie":CONFIG["SERVER"]['COOKIE']
575
  },
576
  json=upload_data,
577
  impersonate="chrome133a",
@@ -593,8 +606,8 @@ class GrokApiClient:
593
 
594
  def prepare_chat_request(self, request):
595
  if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and
596
- not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and
597
- request.get("stream", False)):
598
  raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")
599
 
600
  todo_messages = request["messages"]
@@ -626,7 +639,9 @@ class GrokApiClient:
626
  if item["type"] == 'image_url':
627
  text_content += ("[图片]" if not text_content else '\n[图片]')
628
  elif item["type"] == 'text':
629
- text_content += (remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(item["text"]))
 
 
630
  return text_content
631
  elif isinstance(content, dict) and content is not None:
632
  if content["type"] == 'image_url':
@@ -657,7 +672,6 @@ class GrokApiClient:
657
  if processed_image:
658
  file_attachments.append(processed_image)
659
 
660
-
661
  text_content = process_content(current.get("content", ""))
662
  if is_last_message and convert_to_file:
663
  last_message_content = f"{role.upper()}: {text_content or '[图片]'}\n"
@@ -673,7 +687,7 @@ class GrokApiClient:
673
  message_length += len(messages)
674
  if message_length >= 40000:
675
  convert_to_file = True
676
-
677
  if convert_to_file:
678
  file_id = self.upload_base64_file(messages, request["model"])
679
  if file_id:
@@ -713,6 +727,7 @@ class GrokApiClient:
713
  "isReasoning": request["model"] == 'grok-3-reasoning'
714
  }
715
 
 
716
  class MessageProcessor:
717
  @staticmethod
718
  def create_chat_response(message, model, is_stream=False):
@@ -748,6 +763,7 @@ class MessageProcessor:
748
  "usage": None
749
  }
750
 
 
751
  def process_model_response(response, model):
752
  result = {"token": None, "imageUrl": None}
753
 
@@ -774,7 +790,8 @@ def process_model_response(response, model):
774
  elif not response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "final":
775
  result["token"] = "</think>" + response.get("token", "")
776
  CONFIG["IS_THINKING"] = False
777
- elif (response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "assistant") or response.get("messageTag") == "final":
 
778
  result["token"] = response.get("token")
779
  elif model == 'grok-3-reasoning':
780
  if response.get("isThinking") and not CONFIG["SHOW_THINKING"]:
@@ -791,6 +808,7 @@ def process_model_response(response, model):
791
 
792
  return result
793
 
 
794
  def handle_image_response(image_url):
795
  max_retries = 2
796
  retry_count = 0
@@ -803,7 +821,7 @@ def handle_image_response(image_url):
803
  f"https://assets.grok.com/{image_url}",
804
  headers={
805
  **DEFAULT_HEADERS,
806
- "Cookie":CONFIG["SERVER"]['COOKIE']
807
  },
808
  impersonate="chrome133a",
809
  **proxy_options
@@ -879,6 +897,7 @@ def handle_image_response(image_url):
879
  logger.error(str(error), "Server")
880
  return "生图失败,请查看TUMY图床密钥是否设置正确"
881
 
 
882
  def handle_non_stream_response(response, model):
883
  try:
884
  logger.info("开始处理非流式响应", "Server")
@@ -925,6 +944,8 @@ def handle_non_stream_response(response, model):
925
  except Exception as error:
926
  logger.error(str(error), "Server")
927
  raise
 
 
928
  def handle_stream_response(response, model):
929
  def generate():
930
  logger.info("开始处理流式响应", "Server")
@@ -968,8 +989,19 @@ def handle_stream_response(response, model):
968
  continue
969
 
970
  yield "data: [DONE]\n\n"
 
971
  return generate()
972
 
 
 
 
 
 
 
 
 
 
 
973
  def initialization():
974
  sso_array = os.getenv("SSO", "").split(',')
975
  logger.info("开始加载令牌", "Server")
@@ -983,6 +1015,8 @@ def initialization():
983
  if CONFIG["API"]["PROXY"]:
984
  logger.info(f"代理已设置: {CONFIG['API']['PROXY']}", "Server")
985
 
 
 
986
 
987
  logger.info("初始化完成", "Server")
988
 
@@ -992,6 +1026,7 @@ app.wsgi_app = ProxyFix(app.wsgi_app)
992
  app.secret_key = os.getenv('FLASK_SECRET_KEY') or secrets.token_hex(16)
993
  app.json.sort_keys = False
994
 
 
995
  @app.route('/manager/login', methods=['GET', 'POST'])
996
  def manager_login():
997
  if CONFIG["ADMIN"]["MANAGER_SWITCH"]:
@@ -1005,21 +1040,25 @@ def manager_login():
1005
  else:
1006
  return redirect('/')
1007
 
 
1008
  def check_auth():
1009
  return session.get('is_logged_in', False)
1010
 
 
1011
  @app.route('/manager')
1012
  def manager():
1013
  if not check_auth():
1014
  return redirect('/manager/login')
1015
  return render_template('manager.html')
1016
 
 
1017
  @app.route('/manager/api/get')
1018
  def get_manager_tokens():
1019
  if not check_auth():
1020
  return jsonify({"error": "Unauthorized"}), 401
1021
  return jsonify(token_manager.get_token_status_map())
1022
 
 
1023
  @app.route('/manager/api/add', methods=['POST'])
1024
  def add_manager_token():
1025
  if not check_auth():
@@ -1033,6 +1072,7 @@ def add_manager_token():
1033
  except Exception as e:
1034
  return jsonify({"error": str(e)}), 500
1035
 
 
1036
  @app.route('/manager/api/delete', methods=['POST'])
1037
  def delete_manager_token():
1038
  if not check_auth():
@@ -1045,8 +1085,9 @@ def delete_manager_token():
1045
  return jsonify({"success": True})
1046
  except Exception as e:
1047
  return jsonify({"error": str(e)}), 500
1048
-
1049
- @app.route('/manager/api/cf_clearance', methods=['POST'])
 
1050
  def setCf_Manager_clearance():
1051
  if not check_auth():
1052
  return jsonify({"error": "Unauthorized"}), 401
@@ -1069,6 +1110,7 @@ def get_tokens():
1069
  return jsonify({"error": 'Unauthorized'}), 401
1070
  return jsonify(token_manager.get_token_status_map())
1071
 
 
1072
  @app.route('/add/token', methods=['POST'])
1073
  def add_token():
1074
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
@@ -1084,7 +1126,8 @@ def add_token():
1084
  except Exception as error:
1085
  logger.error(str(error), "Server")
1086
  return jsonify({"error": '添加sso令牌失败'}), 500
1087
-
 
1088
  @app.route('/set/cf_clearance', methods=['POST'])
1089
  def setCf_clearance():
1090
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
@@ -1097,7 +1140,8 @@ def setCf_clearance():
1097
  except Exception as error:
1098
  logger.error(str(error), "Server")
1099
  return jsonify({"error": '设置cf_clearance失败'}), 500
1100
-
 
1101
  @app.route('/delete/token', methods=['POST'])
1102
  def delete_token():
1103
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
@@ -1114,6 +1158,7 @@ def delete_token():
1114
  logger.error(str(error), "Server")
1115
  return jsonify({"error": '删除sso令牌失败'}), 500
1116
 
 
1117
  @app.route('/v1/models', methods=['GET'])
1118
  def get_models():
1119
  return jsonify({
@@ -1129,6 +1174,7 @@ def get_models():
1129
  ]
1130
  })
1131
 
 
1132
  @app.route('/v1/chat/completions', methods=['POST'])
1133
  def chat_completions():
1134
  response_status_code = 500
@@ -1151,7 +1197,6 @@ def chat_completions():
1151
  retry_count = 0
1152
  grok_client = GrokApiClient(model)
1153
  request_payload = grok_client.prepare_chat_request(data)
1154
-
1155
 
1156
  while retry_count < CONFIG["RETRY"]["MAX_ATTEMPTS"]:
1157
  retry_count += 1
@@ -1161,38 +1206,45 @@ def chat_completions():
1161
  raise ValueError('该模型无可用令牌')
1162
 
1163
  logger.info(
1164
- f"当前令牌: {json.dumps(CONFIG['API']['SIGNATURE_COOKIE'], indent=2)}","Server")
1165
  logger.info(
1166
- f"当前可用模型的全部可用数量: {json.dumps(token_manager.get_remaining_token_request_capacity(), indent=2)}","Server")
1167
-
 
1168
  if CONFIG['SERVER']['CF_CLEARANCE']:
1169
- CONFIG["SERVER"]['COOKIE'] = f"{CONFIG['API']['SIGNATURE_COOKIE']};{CONFIG['SERVER']['CF_CLEARANCE']}"
1170
  else:
1171
  CONFIG["SERVER"]['COOKIE'] = CONFIG['API']['SIGNATURE_COOKIE']
1172
- logger.info("有请求到来","Server")
 
 
 
 
 
1173
  try:
1174
  proxy_options = Utils.get_proxy_options()
1175
  response = curl_requests.post(
1176
  f"{CONFIG['API']['BASE_URL']}/rest/app-chat/conversations/new",
1177
  headers={
1178
- **DEFAULT_HEADERS,
1179
- "Cookie":CONFIG["SERVER"]['COOKIE']
1180
  },
1181
  json=request_payload,
1182
  impersonate="chrome133a",
1183
  verify=False,
1184
  stream=True,
1185
  **proxy_options)
1186
- logger.info(CONFIG["SERVER"]['COOKIE'],"Server")
1187
  if response.status_code == 200:
1188
  response_status_code = 200
1189
  logger.info("请求成功", "Server")
1190
- logger.info(f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}","Server")
 
1191
 
1192
  try:
1193
  if stream:
1194
  return Response(stream_with_context(
1195
- handle_stream_response(response, model)),content_type='text/event-stream')
1196
  else:
1197
  content = handle_non_stream_response(response, model)
1198
  return jsonify(
@@ -1207,13 +1259,13 @@ def chat_completions():
1207
  raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1208
  elif response.status_code == 403:
1209
  response_status_code = 403
1210
- token_manager.reduce_token_request_count(model,1)#重置去除当前因为错误未成功请��的次数,确保不会因为错误未成功请求的次数导致次数上限
1211
  if token_manager.get_token_count_for_model(model) == 0:
1212
  raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1213
  raise ValueError(f"IP暂时被封无法破盾,请稍后重试或者更换ip")
1214
  elif response.status_code == 429:
1215
  response_status_code = 429
1216
- token_manager.reduce_token_request_count(model,1)
1217
  if CONFIG["API"]["IS_CUSTOM_SSO"]:
1218
  raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1219
 
@@ -1226,7 +1278,7 @@ def chat_completions():
1226
  if CONFIG["API"]["IS_CUSTOM_SSO"]:
1227
  raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1228
 
1229
- logger.error(f"令牌异常错误状态!status: {response.status_code}","Server")
1230
  token_manager.remove_token_from_model(model, CONFIG["API"]["SIGNATURE_COOKIE"])
1231
  logger.info(
1232
  f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}",
@@ -1240,7 +1292,7 @@ def chat_completions():
1240
  if response_status_code == 403:
1241
  raise ValueError('IP暂时被封无法破盾,请稍后重试或者更换ip')
1242
  elif response_status_code == 500:
1243
- raise ValueError('当前模型所有令牌暂无可用,请稍后重试')
1244
 
1245
  except Exception as error:
1246
  logger.error(str(error), "ChatAPI")
@@ -1250,11 +1302,13 @@ def chat_completions():
1250
  "type": "server_error"
1251
  }}), response_status_code
1252
 
 
1253
  @app.route('/', defaults={'path': ''})
1254
  @app.route('/<path:path>')
1255
  def catch_all(path):
1256
  return 'api运行正常', 200
1257
 
 
1258
  if __name__ == '__main__':
1259
  token_manager = AuthTokenManager()
1260
  initialization()
 
14
  from curl_cffi import requests as curl_requests
15
  from werkzeug.middleware.proxy_fix import ProxyFix
16
  import get_xid
17
+ import threading
18
+
19
 
20
  current_dir = os.path.dirname(os.path.abspath(__file__))
21
  env_path = os.path.join(current_dir, '.env')
22
 
23
  load_dotenv(env_path)
24
 
25
+
26
  class Logger:
27
  def __init__(self, level="INFO", colorize=True, format=None):
28
  logger.remove()
 
88
  caller_info = self._get_caller_info()
89
  self.logger.bind(**caller_info).info(f"请求: {request.method} {request.path}", "Request")
90
 
 
91
 
92
+ logger = Logger(level="INFO")
93
 
94
  CONFIG = {
95
  "XIDS": None,
 
116
  },
117
  "ADMIN": {
118
  "MANAGER_SWITCH": os.getenv("MANAGER_SWITCH") or None,
119
+ "PASSWORD": os.getenv("ADMINPASSWORD") or None
120
  },
121
  "SERVER": {
122
  "COOKIE": None,
123
+ "CF_CLEARANCE": os.getenv("CF_CLEARANCE") or None,
124
  "PORT": int(os.getenv("PORT", 5200))
125
  },
126
  "RETRY": {
 
134
  "ISSHOW_SEARCH_RESULTS": os.getenv("ISSHOW_SEARCH_RESULTS", "true").lower() == "true"
135
  }
136
 
 
137
  DEFAULT_HEADERS = {
138
  'Accept': '*/*',
139
  'Accept-Language': 'zh-CN,zh;q=0.9',
 
154
  'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
155
  }
156
 
157
+
158
  class AuthTokenManager:
159
  def __init__(self):
160
  self.token_model_map = {}
 
190
  if sso not in self.token_status_map:
191
  self.token_status_map[sso] = {}
192
 
193
+ existing_token_entry = next((entry for entry in self.token_model_map[model] if entry["token"] == token),
194
+ None)
195
 
196
  if not existing_token_entry:
197
  self.token_model_map[model].append({
 
228
  try:
229
  sso = token.split("sso=")[1].split(";")[0]
230
  for model in self.token_model_map:
231
+ self.token_model_map[model] = [entry for entry in self.token_model_map[model] if
232
+ entry["token"] != token]
233
 
234
  if sso in self.token_status_map:
235
  del self.token_status_map[sso]
 
239
  except Exception as error:
240
  logger.error(f"令牌删除失败: {str(error)}")
241
  return False
242
+
243
  def reduce_token_request_count(self, model_id, count):
244
  try:
245
  normalized_model = self.normalize_model_name(model_id)
246
+
247
  if normalized_model not in self.token_model_map:
248
  logger.error(f"模型 {normalized_model} 不存在", "TokenManager")
249
  return False
250
+
251
  if not self.token_model_map[normalized_model]:
252
  logger.error(f"模型 {normalized_model} 没有可用的token", "TokenManager")
253
  return False
254
+
255
  token_entry = self.token_model_map[normalized_model][0]
256
+
257
  # 确保RequestCount不会小于0
258
  new_count = max(0, token_entry["RequestCount"] - count)
259
  reduction = token_entry["RequestCount"] - new_count
260
+
261
  token_entry["RequestCount"] = new_count
262
+
263
  # 更新token状态
264
  if token_entry["token"]:
265
  sso = token_entry["token"].split("sso=")[1].split(";")[0]
266
  if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:
267
  self.token_status_map[sso][normalized_model]["totalRequestCount"] = max(
268
+ 0,
269
  self.token_status_map[sso][normalized_model]["totalRequestCount"] - reduction
270
  )
271
  return True
272
+
273
  except Exception as error:
274
  logger.error(f"重置校对token请求次数时发生错误: {str(error)}", "TokenManager")
275
  return False
276
+
277
  def get_next_token_for_model(self, model_id, is_return=False):
278
  normalized_model = self.normalize_model_name(model_id)
279
 
 
296
 
297
  if token_entry["RequestCount"] > self.model_config[normalized_model]["RequestFrequency"]:
298
  self.remove_token_from_model(normalized_model, token_entry["token"])
299
+ next_token_entry = self.token_model_map[normalized_model][0] if self.token_model_map[
300
+ normalized_model] else None
301
  return next_token_entry["token"] if next_token_entry else None
302
 
303
  sso = token_entry["token"].split("sso=")[1].split(";")[0]
 
436
  for entry in model_tokens:
437
  all_tokens.add(entry["token"])
438
  return list(all_tokens)
439
+
440
  def get_current_token(self, model_id):
441
  normalized_model = self.normalize_model_name(model_id)
442
 
 
449
  def get_token_status_map(self):
450
  return self.token_status_map
451
 
452
+
453
  class Utils:
454
  @staticmethod
455
  def organize_search_results(search_results):
 
480
 
481
  if proxy:
482
  logger.info(f"使用代理: {proxy}", "Server")
483
+
484
  if proxy.startswith("socks5://"):
485
  proxy_options["proxy"] = proxy
486
+
487
  if '@' in proxy:
488
  auth_part = proxy.split('@')[0].split('://')[1]
489
  if ':' in auth_part:
490
  username, password = auth_part.split(':')
491
  proxy_options["proxy_auth"] = (username, password)
492
  else:
493
+ proxy_options["proxies"] = {"https": proxy, "http": proxy}
494
  return proxy_options
495
 
496
+
497
  class GrokApiClient:
498
  def __init__(self, model_id):
499
  if model_id not in CONFIG["MODELS"]:
 
520
  "mimeType": mime_type,
521
  "fileName": file_name
522
  }
523
+
524
  def upload_base64_file(self, message, model):
525
  try:
526
  message_base64 = base64.b64encode(message.encode('utf-8')).decode('utf-8')
 
531
  }
532
 
533
  logger.info("发送文字文件请求", "Server")
534
+ cookie = f"{Utils.create_auth_headers(model, True)};{CONFIG['SERVER']['CF_CLEARANCE']}"
535
  proxy_options = Utils.get_proxy_options()
536
  response = curl_requests.post(
537
  "https://grok.com/rest/app-chat/upload-file",
538
  headers={
539
  **DEFAULT_HEADERS,
540
+ "Cookie": cookie
541
  },
542
  json=upload_data,
543
  impersonate="chrome133a",
 
556
  except Exception as error:
557
  logger.error(str(error), "Server")
558
  raise Exception(f"上传文件失败,状态码:{response.status_code}")
559
+
560
  def upload_base64_image(self, base64_data, url):
561
  try:
562
  if 'data:image' in base64_data:
 
584
  url,
585
  headers={
586
  **DEFAULT_HEADERS,
587
+ "Cookie": CONFIG["SERVER"]['COOKIE']
588
  },
589
  json=upload_data,
590
  impersonate="chrome133a",
 
606
 
607
  def prepare_chat_request(self, request):
608
  if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and
609
+ not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and
610
+ request.get("stream", False)):
611
  raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")
612
 
613
  todo_messages = request["messages"]
 
639
  if item["type"] == 'image_url':
640
  text_content += ("[图片]" if not text_content else '\n[图片]')
641
  elif item["type"] == 'text':
642
+ text_content += (
643
+ remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(
644
+ item["text"]))
645
  return text_content
646
  elif isinstance(content, dict) and content is not None:
647
  if content["type"] == 'image_url':
 
672
  if processed_image:
673
  file_attachments.append(processed_image)
674
 
 
675
  text_content = process_content(current.get("content", ""))
676
  if is_last_message and convert_to_file:
677
  last_message_content = f"{role.upper()}: {text_content or '[图片]'}\n"
 
687
  message_length += len(messages)
688
  if message_length >= 40000:
689
  convert_to_file = True
690
+
691
  if convert_to_file:
692
  file_id = self.upload_base64_file(messages, request["model"])
693
  if file_id:
 
727
  "isReasoning": request["model"] == 'grok-3-reasoning'
728
  }
729
 
730
+
731
  class MessageProcessor:
732
  @staticmethod
733
  def create_chat_response(message, model, is_stream=False):
 
763
  "usage": None
764
  }
765
 
766
+
767
  def process_model_response(response, model):
768
  result = {"token": None, "imageUrl": None}
769
 
 
790
  elif not response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "final":
791
  result["token"] = "</think>" + response.get("token", "")
792
  CONFIG["IS_THINKING"] = False
793
+ elif (response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get(
794
+ "messageTag") == "assistant") or response.get("messageTag") == "final":
795
  result["token"] = response.get("token")
796
  elif model == 'grok-3-reasoning':
797
  if response.get("isThinking") and not CONFIG["SHOW_THINKING"]:
 
808
 
809
  return result
810
 
811
+
812
  def handle_image_response(image_url):
813
  max_retries = 2
814
  retry_count = 0
 
821
  f"https://assets.grok.com/{image_url}",
822
  headers={
823
  **DEFAULT_HEADERS,
824
+ "Cookie": CONFIG["SERVER"]['COOKIE']
825
  },
826
  impersonate="chrome133a",
827
  **proxy_options
 
897
  logger.error(str(error), "Server")
898
  return "生图失败,请查看TUMY图床密钥是否设置正确"
899
 
900
+
901
  def handle_non_stream_response(response, model):
902
  try:
903
  logger.info("开始处理非流式响应", "Server")
 
944
  except Exception as error:
945
  logger.error(str(error), "Server")
946
  raise
947
+
948
+
949
  def handle_stream_response(response, model):
950
  def generate():
951
  logger.info("开始处理流式响应", "Server")
 
989
  continue
990
 
991
  yield "data: [DONE]\n\n"
992
+
993
  return generate()
994
 
995
+ def schedule_task():
996
+ while True:
997
+ # 创建并启动一个新线程来执行任务
998
+ xid = get_xid.main()
999
+ if xid is not None:
1000
+ logger.info(f"成功获取XID: {xid}", "Server")
1001
+ CONFIG["XIDS"] = xid
1002
+
1003
+ time.sleep(3 * 60 * 60)
1004
+
1005
  def initialization():
1006
  sso_array = os.getenv("SSO", "").split(',')
1007
  logger.info("开始加载令牌", "Server")
 
1015
  if CONFIG["API"]["PROXY"]:
1016
  logger.info(f"代理已设置: {CONFIG['API']['PROXY']}", "Server")
1017
 
1018
+ thread = threading.Thread(target=schedule_task)
1019
+ thread.start()
1020
 
1021
  logger.info("初始化完成", "Server")
1022
 
 
1026
  app.secret_key = os.getenv('FLASK_SECRET_KEY') or secrets.token_hex(16)
1027
  app.json.sort_keys = False
1028
 
1029
+
1030
  @app.route('/manager/login', methods=['GET', 'POST'])
1031
  def manager_login():
1032
  if CONFIG["ADMIN"]["MANAGER_SWITCH"]:
 
1040
  else:
1041
  return redirect('/')
1042
 
1043
+
1044
  def check_auth():
1045
  return session.get('is_logged_in', False)
1046
 
1047
+
1048
  @app.route('/manager')
1049
  def manager():
1050
  if not check_auth():
1051
  return redirect('/manager/login')
1052
  return render_template('manager.html')
1053
 
1054
+
1055
  @app.route('/manager/api/get')
1056
  def get_manager_tokens():
1057
  if not check_auth():
1058
  return jsonify({"error": "Unauthorized"}), 401
1059
  return jsonify(token_manager.get_token_status_map())
1060
 
1061
+
1062
  @app.route('/manager/api/add', methods=['POST'])
1063
  def add_manager_token():
1064
  if not check_auth():
 
1072
  except Exception as e:
1073
  return jsonify({"error": str(e)}), 500
1074
 
1075
+
1076
  @app.route('/manager/api/delete', methods=['POST'])
1077
  def delete_manager_token():
1078
  if not check_auth():
 
1085
  return jsonify({"success": True})
1086
  except Exception as e:
1087
  return jsonify({"error": str(e)}), 500
1088
+
1089
+
1090
+ @app.route('/manager/api/cf_clearance', methods=['POST'])
1091
  def setCf_Manager_clearance():
1092
  if not check_auth():
1093
  return jsonify({"error": "Unauthorized"}), 401
 
1110
  return jsonify({"error": 'Unauthorized'}), 401
1111
  return jsonify(token_manager.get_token_status_map())
1112
 
1113
+
1114
  @app.route('/add/token', methods=['POST'])
1115
  def add_token():
1116
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
 
1126
  except Exception as error:
1127
  logger.error(str(error), "Server")
1128
  return jsonify({"error": '添加sso令牌失败'}), 500
1129
+
1130
+
1131
  @app.route('/set/cf_clearance', methods=['POST'])
1132
  def setCf_clearance():
1133
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
 
1140
  except Exception as error:
1141
  logger.error(str(error), "Server")
1142
  return jsonify({"error": '设置cf_clearance失败'}), 500
1143
+
1144
+
1145
  @app.route('/delete/token', methods=['POST'])
1146
  def delete_token():
1147
  auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
 
1158
  logger.error(str(error), "Server")
1159
  return jsonify({"error": '删除sso令牌失败'}), 500
1160
 
1161
+
1162
  @app.route('/v1/models', methods=['GET'])
1163
  def get_models():
1164
  return jsonify({
 
1174
  ]
1175
  })
1176
 
1177
+
1178
  @app.route('/v1/chat/completions', methods=['POST'])
1179
  def chat_completions():
1180
  response_status_code = 500
 
1197
  retry_count = 0
1198
  grok_client = GrokApiClient(model)
1199
  request_payload = grok_client.prepare_chat_request(data)
 
1200
 
1201
  while retry_count < CONFIG["RETRY"]["MAX_ATTEMPTS"]:
1202
  retry_count += 1
 
1206
  raise ValueError('该模型无可用令牌')
1207
 
1208
  logger.info(
1209
+ f"当前令牌: {json.dumps(CONFIG['API']['SIGNATURE_COOKIE'], indent=2)}", "Server")
1210
  logger.info(
1211
+ f"当前可用模型的全部可用数量: {json.dumps(token_manager.get_remaining_token_request_capacity(), indent=2)}",
1212
+ "Server")
1213
+
1214
  if CONFIG['SERVER']['CF_CLEARANCE']:
1215
+ CONFIG["SERVER"]['COOKIE'] = f"{CONFIG['API']['SIGNATURE_COOKIE']};{CONFIG['SERVER']['CF_CLEARANCE']}"
1216
  else:
1217
  CONFIG["SERVER"]['COOKIE'] = CONFIG['API']['SIGNATURE_COOKIE']
1218
+ logger.info("有请求到来", "Server")
1219
+ if CONFIG['XIDS'] is not None:
1220
+ DEFAULT_HEADERS['x-xai-request-id'] = CONFIG['XIDS']['x-xai-request-id']
1221
+ DEFAULT_HEADERS['x-statsig-id'] = CONFIG['XIDS']['x-statsig-id']
1222
+ DEFAULT_HEADERS['User-Agent'] = CONFIG['XIDS']['user-agent']
1223
+
1224
  try:
1225
  proxy_options = Utils.get_proxy_options()
1226
  response = curl_requests.post(
1227
  f"{CONFIG['API']['BASE_URL']}/rest/app-chat/conversations/new",
1228
  headers={
1229
+ **DEFAULT_HEADERS,
1230
+ "Cookie": CONFIG["SERVER"]['COOKIE']
1231
  },
1232
  json=request_payload,
1233
  impersonate="chrome133a",
1234
  verify=False,
1235
  stream=True,
1236
  **proxy_options)
1237
+ logger.info(CONFIG["SERVER"]['COOKIE'], "Server")
1238
  if response.status_code == 200:
1239
  response_status_code = 200
1240
  logger.info("请求成功", "Server")
1241
+ logger.info(f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}",
1242
+ "Server")
1243
 
1244
  try:
1245
  if stream:
1246
  return Response(stream_with_context(
1247
+ handle_stream_response(response, model)), content_type='text/event-stream')
1248
  else:
1249
  content = handle_non_stream_response(response, model)
1250
  return jsonify(
 
1259
  raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1260
  elif response.status_code == 403:
1261
  response_status_code = 403
1262
+ token_manager.reduce_token_request_count(model, 1) # 重置去除当前因为错误未成功请求的次数,确保不会因为错误未成功请求的次数导致次数上限
1263
  if token_manager.get_token_count_for_model(model) == 0:
1264
  raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1265
  raise ValueError(f"IP暂时被封无法破盾,请稍后重试或者更换ip")
1266
  elif response.status_code == 429:
1267
  response_status_code = 429
1268
+ token_manager.reduce_token_request_count(model, 1)
1269
  if CONFIG["API"]["IS_CUSTOM_SSO"]:
1270
  raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1271
 
 
1278
  if CONFIG["API"]["IS_CUSTOM_SSO"]:
1279
  raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1280
 
1281
+ logger.error(f"令牌异常错误状态!status: {response.status_code}", "Server")
1282
  token_manager.remove_token_from_model(model, CONFIG["API"]["SIGNATURE_COOKIE"])
1283
  logger.info(
1284
  f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}",
 
1292
  if response_status_code == 403:
1293
  raise ValueError('IP暂时被封无法破盾,请稍后重试或者更换ip')
1294
  elif response_status_code == 500:
1295
+ raise ValueError('当前模型所有令牌暂无可用,请稍后重试')
1296
 
1297
  except Exception as error:
1298
  logger.error(str(error), "ChatAPI")
 
1302
  "type": "server_error"
1303
  }}), response_status_code
1304
 
1305
+
1306
  @app.route('/', defaults={'path': ''})
1307
  @app.route('/<path:path>')
1308
  def catch_all(path):
1309
  return 'api运行正常', 200
1310
 
1311
+
1312
  if __name__ == '__main__':
1313
  token_manager = AuthTokenManager()
1314
  initialization()
get_xid.py CHANGED
@@ -1,132 +1,143 @@
1
- from playwright.sync_api import sync_playwright
2
-
3
- # 用于存储捕获到的请求头信息
4
- all_request_headers_info = []
5
-
6
-
7
- def handle_request(request):
8
- """
9
- 捕获每个请求的URL, 方法和头部信息
10
- """
11
- # print(f"Intercepted request to: {request.url}") # 调试时可以取消注释
12
- all_request_headers_info.append({
13
- "url": request.url,
14
- "method": request.method,
15
- "headers": request.headers # request.headers 是一个字典
16
- })
17
-
18
-
19
- def main():
20
- with sync_playwright() as p:
21
- # 启动浏览器,可以是 chromium, firefox, or webkit
22
- # headless=False 可以看到浏览器操作,True则为无头模式
23
- browser = p.chromium.launch(headless=True,
24
- args=[
25
- '--no-sandbox',
26
- '--disable-setuid-sandbox',
27
- '--disable-dev-shm-usage' # 有时也需要这个,但 --shm-size 更好
28
- ])
29
-
30
- # 创建一个新的浏览器上下文
31
- # 可以在这里设置 user_agent, viewport, etc.
32
- context = browser.new_context(
33
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0",
34
- )
35
-
36
- # 在上下文中创建一个新页面
37
- page = context.new_page()
38
-
39
- # 注册请求拦截器,这必须在导航之前完成
40
- # 'request' 事件会在每个HTTP请求发起时触发
41
- page.on("request", handle_request)
42
-
43
- print(f"Navigating to https://grok.com/ ...")
44
- try:
45
- # 访问目标网站,设置一个合理的超时时间(例如60秒)
46
- page.goto("https://grok.com/", timeout=60000)
47
- print("Page loaded. Waiting for 10 seconds for dynamic content or further requests...")
48
-
49
- # 检查是否仍然被 Cloudflare 阻止 (例如,查找特定的标题或元素)
50
- title = page.title()
51
- print(f"Page title: {title}")
52
-
53
- if "请稍候…" in page.content() or "Just a moment..." in page.content() or "Cloudflare" in title or "Checking your browser" in title:
54
- print("Still on a Cloudflare challenge page. Waiting longer or trying interaction...")
55
- # 你可能需要在这里添加更长的等待或模拟用户交互
56
- # 例如,等待特定的元素出现,表明挑战已通过
57
- try:
58
- page.wait_for_selector("body:not(:has-text('请稍候…'))", timeout=60000)
59
- print("Cloudflare challenge likely passed.")
60
- title = page.title()
61
- print(f"New page title: {title}")
62
- except Exception as e:
63
- print(f"Failed to pass Cloudflare challenge after extended wait: {e}")
64
- else:
65
- print("Successfully navigated to the page.")
66
-
67
-
68
- page.wait_for_timeout(10000)
69
-
70
-
71
- try:
72
- textarea_locator = page.get_by_label("向Grok提任何问题")
73
- textarea_locator.fill("你好")
74
- print("Successfully entered '你好' into the textarea.")
75
- except Exception as e:
76
- print(f"Could not find or fill the textarea with aria-label '向Grok提任何问题'. Error: {e}")
77
- browser.close()
78
- return
79
-
80
- # 2. 查找 aria-label 为“提交”的 button 并点击
81
- # 使用 get_by_role('button', name='...') 是 Playwright 推荐的方式来查找具有特定可访问名称的按钮
82
- try:
83
- submit_button_locator = page.get_by_role("button", name="提交")
84
- submit_button_locator.click()
85
- print("Successfully clicked the '提交' button.")
86
- except Exception as e:
87
- print(f"Could not find or click the button with aria-label '提交'. Error: {e}")
88
- browser.close()
89
- return
90
-
91
- # 等待10秒
92
- # Playwright 的 page.wait_for_timeout() 是首选,因为它与Playwright的事件循环集成
93
- # page.wait_for_timeout(10000)
94
- # 或者使用 time.sleep(10) 也可以,但在Playwright脚本中前者更佳
95
-
96
- print("\n--- Cookies ---")
97
- # 获取当前上下文中的所有cookies
98
- cookies = context.cookies()
99
- if cookies:
100
- for cookie in cookies:
101
- print(
102
- f"Name: {cookie['name']}, Value: {cookie['value']}, Domain: {cookie['domain']}, Path: {cookie['path']}")
103
- else:
104
- print("No cookies found.")
105
-
106
- print("\n--- Request Headers (collected during the session) ---")
107
- if all_request_headers_info:
108
- # 打印捕获到的每个请求的头部信息
109
- # 注意:这里会包含所有资源的请求(HTML, CSS, JS, XHR, 图片等)
110
- for i, req_info in enumerate(all_request_headers_info):
111
- if req_info['url'] == 'https://grok.com/rest/app-chat/conversations/new':
112
- datas = {
113
- 'x-xai-request-id': req_info['headers']['x-xai-request-id'],
114
- 'x-statsig-id':req_info['headers']['x-statsig-id'],
115
- 'user-agent': req_info['headers']['user-agent'],
116
- }
117
- print(datas)
118
- return datas
119
- else:
120
- print("No requests were intercepted (this is unlikely if the page loaded).")
121
-
122
- except Exception as e:
123
- print(f"An error occurred: {e}")
124
- finally:
125
- # 确保浏览器关闭
126
- print("\nClosing browser...")
127
- browser.close()
128
- return None
129
-
130
-
131
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
132
  main()
 
1
+ from playwright.sync_api import sync_playwright
2
+
3
+ # 用于存储捕获到的请求头信息
4
+ all_request_headers_info = []
5
+
6
+
7
+ def handle_request(request):
8
+ """
9
+ 捕获每个请求的URL, 方法和头部信息
10
+ """
11
+ # print(f"Intercepted request to: {request.url}") # 调试时可以取消注释
12
+ all_request_headers_info.append({
13
+ "url": request.url,
14
+ "method": request.method,
15
+ "headers": request.headers # request.headers 是一个字典
16
+ })
17
+
18
+
19
+ def main():
20
+ with sync_playwright() as p:
21
+ # 启动浏览器,可以是 chromium, firefox, or webkit
22
+ # headless=False 可以看到浏览器操作,True则为无头模式
23
+ browser = p.chromium.launch(headless=True,
24
+ args=[
25
+ '--no-sandbox',
26
+ '--disable-setuid-sandbox',
27
+ '--disable-dev-shm-usage' # 有时也需要这个,但 --shm-size 更好
28
+ ])
29
+
30
+ # 创建一个新的浏览器上下文
31
+ # 可以在这里设置 user_agent, viewport, etc.
32
+ context = browser.new_context(
33
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0",
34
+ )
35
+
36
+ # 在上下文中创建一个新页面
37
+ page = context.new_page()
38
+
39
+ # 注册请求拦截器,这必须在导航之前完成
40
+ # 'request' 事件会在每个HTTP请求发起时触发
41
+ page.on("request", handle_request)
42
+
43
+ print(f"Navigating to https://grok.com/ ...")
44
+ try:
45
+ # 访问目标网站,设置一个合理的超时时间(例如60秒)
46
+ page.goto("https://grok.com/", timeout=60000)
47
+ page.wait_for_timeout(5000)
48
+ print("Page loaded. Waiting for 10 seconds for dynamic content or further requests...")
49
+
50
+ # 检查是否仍然被 Cloudflare 阻止 (例如,查找特定的标题或元素)
51
+ title = page.title()
52
+ print(f"Page title: {title}")
53
+
54
+ if "请稍候…" in page.content() or "Just a moment..." in page.content() or "Cloudflare" in title or "Checking your browser" in title:
55
+ print("Still on a Cloudflare challenge page. Waiting longer or trying interaction...")
56
+ # 你可能需要在这里添加更长的等待或模拟用户交互
57
+ # 例如,等待特定的元素出现,表明挑战已通过
58
+ try:
59
+ page.wait_for_selector("body:not(:has-text('请稍候…'))", timeout=60000)
60
+ print("Cloudflare challenge likely passed.")
61
+ title = page.title()
62
+ print(f"New page title: {title}")
63
+ page.screenshot(path="cf_passed.png")
64
+ except Exception as e:
65
+ print(f"Failed to pass Cloudflare challenge after extended wait: {e}")
66
+ page.screenshot(path="cf_failed.png")
67
+ else:
68
+ print("Successfully navigated to the page.")
69
+ page.screenshot(path="cf_success.png")
70
+
71
+ page.evaluate("""
72
+ function(){
73
+ const element = document.getElementById('turnstile-widget');
74
+ if (element) {
75
+ element.style.display = 'none';
76
+ }
77
+ }
78
+ """)
79
+ page.wait_for_timeout(10000)
80
+
81
+ try:
82
+ textarea_locator = page.get_by_label("Ask Grok anything")
83
+ textarea_locator.fill("你好")
84
+ print("Successfully entered '你好' into the textarea.")
85
+ except Exception as e:
86
+ print(f"Could not find or fill the textarea with aria-label 'Ask Grok anything'. Error: {e}")
87
+ browser.close()
88
+ return
89
+
90
+ # 2. 查找 aria-label 为“提交”的 button 并点击
91
+ # 使用 get_by_role('button', name='...') 是 Playwright 推荐的方式��查找具有特定可访问名称的按钮
92
+ try:
93
+ submit_button_locator = page.get_by_role("button", name="Submit")
94
+ submit_button_locator.click()
95
+ print("Successfully clicked the 'Submit' button.")
96
+ except Exception as e:
97
+ print(f"Could not find or click the button with aria-label 'Submit'. Error: {e}")
98
+ browser.close()
99
+ return
100
+
101
+ # 等待10秒
102
+ # Playwright page.wait_for_timeout() 是首选,因为它与Playwright的事件循环集成
103
+ # page.wait_for_timeout(10000)
104
+ # 或者使用 time.sleep(10) 也可以,但在Playwright脚本中前者更佳
105
+
106
+ print("\n--- Cookies ---")
107
+ # 获取当前上下文中的所有cookies
108
+ cookies = context.cookies()
109
+ if cookies:
110
+ for cookie in cookies:
111
+ print(
112
+ f"Name: {cookie['name']}, Value: {cookie['value']}, Domain: {cookie['domain']}, Path: {cookie['path']}")
113
+ else:
114
+ print("No cookies found.")
115
+
116
+ print("\n--- Request Headers (collected during the session) ---")
117
+ if all_request_headers_info:
118
+ # 打印捕获到的每个请求的头部信息
119
+ # 注意:这里会包含所有资源的请求(HTML, CSS, JS, XHR, 图片等)
120
+ for i, req_info in enumerate(all_request_headers_info):
121
+ if req_info['url'] == 'https://grok.com/rest/app-chat/conversations/new':
122
+ datas = {
123
+ 'x-xai-request-id': req_info['headers']['x-xai-request-id'],
124
+ 'x-statsig-id': req_info['headers']['x-statsig-id'],
125
+ 'user-agent': req_info['headers']['user-agent'],
126
+ }
127
+ print(datas)
128
+ return datas
129
+ else:
130
+ print("No requests were intercepted (this is unlikely if the page loaded).")
131
+
132
+ except Exception as e:
133
+ print(f"An error occurred: {e}")
134
+ finally:
135
+ # 确保浏览器关闭
136
+ print("\nClosing browser...")
137
+ page.close()
138
+ browser.close()
139
+ return None
140
+
141
+
142
+ if __name__ == "__main__":
143
  main()