initial commit
This commit is contained in:
1 parent
eeab594b68
commit
e1ed811084
57 files changed
+18704
No files matched your search
@@ -0,0 +1,96 @@
|
||||
import time
|
||||
from DrissionPage import ChromiumPage
|
||||
|
||||
class CloudflareBypasser:
|
||||
def __init__(self, driver: ChromiumPage, max_retries=-1, log=True):
|
||||
self.driver = driver
|
||||
self.max_retries = max_retries
|
||||
self.log = log
|
||||
|
||||
def search_recursively_shadow_root_with_iframe(self,ele):
|
||||
if ele.shadow_root:
|
||||
if ele.shadow_root.child().tag == "iframe":
|
||||
return ele.shadow_root.child()
|
||||
else:
|
||||
for child in ele.children():
|
||||
result = self.search_recursively_shadow_root_with_iframe(child)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
def search_recursively_shadow_root_with_cf_input(self,ele):
|
||||
if ele.shadow_root:
|
||||
if ele.shadow_root.ele("tag:input"):
|
||||
return ele.shadow_root.ele("tag:input")
|
||||
else:
|
||||
for child in ele.children():
|
||||
result = self.search_recursively_shadow_root_with_cf_input(child)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
def locate_cf_button(self):
|
||||
button = None
|
||||
eles = self.driver.eles("tag:input")
|
||||
for ele in eles:
|
||||
if "name" in ele.attrs.keys() and "type" in ele.attrs.keys():
|
||||
if "turnstile" in ele.attrs["name"] and ele.attrs["type"] == "hidden":
|
||||
button = ele.parent().shadow_root.child()("tag:body").shadow_root("tag:input")
|
||||
break
|
||||
|
||||
if button:
|
||||
return button
|
||||
else:
|
||||
# If the button is not found, search it recursively
|
||||
self.log_message("Basic search failed. Searching for button recursively.")
|
||||
ele = self.driver.ele("tag:body")
|
||||
iframe = self.search_recursively_shadow_root_with_iframe(ele)
|
||||
if iframe:
|
||||
button = self.search_recursively_shadow_root_with_cf_input(iframe("tag:body"))
|
||||
else:
|
||||
self.log_message("Iframe not found. Button search failed.")
|
||||
return button
|
||||
|
||||
def log_message(self, message):
|
||||
if self.log:
|
||||
print(message)
|
||||
|
||||
def click_verification_button(self):
|
||||
try:
|
||||
button = self.locate_cf_button()
|
||||
if button:
|
||||
self.log_message("Verification button found. Attempting to click.")
|
||||
button.click()
|
||||
else:
|
||||
self.log_message("Verification button not found.")
|
||||
|
||||
except Exception as e:
|
||||
self.log_message(f"Error clicking verification button: {e}")
|
||||
|
||||
def is_bypassed(self):
|
||||
try:
|
||||
title = self.driver.title.lower()
|
||||
return "just a moment" not in title
|
||||
except Exception as e:
|
||||
self.log_message(f"Error checking page title: {e}")
|
||||
return False
|
||||
|
||||
def bypass(self):
|
||||
|
||||
try_count = 0
|
||||
|
||||
while not self.is_bypassed():
|
||||
if 0 < self.max_retries + 1 <= try_count:
|
||||
self.log_message("Exceeded maximum retries. Bypass failed.")
|
||||
break
|
||||
|
||||
self.log_message(f"Attempt {try_count + 1}: Verification page detected. Trying to bypass...")
|
||||
self.click_verification_button()
|
||||
|
||||
try_count += 1
|
||||
time.sleep(2)
|
||||
|
||||
if self.is_bypassed():
|
||||
self.log_message("Bypass successful.")
|
||||
else:
|
||||
self.log_message("Bypass failed.")
|
||||
@@ -0,0 +1,294 @@
|
||||
from curl_cffi import requests
|
||||
from typing import Optional, Dict, Any, Generator, Literal
|
||||
import json
|
||||
from .pow import DeepSeekPOW
|
||||
import pkg_resources
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
ThinkingMode = Literal['detailed', 'simple', 'disabled']
|
||||
SearchMode = Literal['enabled', 'disabled']
|
||||
|
||||
class DeepSeekError(Exception):
|
||||
"""Base exception for all DeepSeek API errors"""
|
||||
pass
|
||||
|
||||
class AuthenticationError(DeepSeekError):
|
||||
"""Raised when authentication fails"""
|
||||
pass
|
||||
|
||||
class RateLimitError(DeepSeekError):
|
||||
"""Raised when API rate limit is exceeded"""
|
||||
pass
|
||||
|
||||
class NetworkError(DeepSeekError):
|
||||
"""Raised when network communication fails"""
|
||||
pass
|
||||
|
||||
class CloudflareError(DeepSeekError):
|
||||
"""Raised when Cloudflare blocks the request"""
|
||||
pass
|
||||
|
||||
class APIError(DeepSeekError):
|
||||
"""Raised when API returns an error response"""
|
||||
def __init__(self, message: str, status_code: Optional[int] = None):
|
||||
super().__init__(message)
|
||||
self.status_code = status_code
|
||||
|
||||
class DeepSeekAPI:
|
||||
BASE_URL = "https://chat.deepseek.com/api/v0"
|
||||
|
||||
def __init__(self, auth_token: str):
|
||||
if not auth_token or not isinstance(auth_token, str):
|
||||
raise AuthenticationError("Invalid auth token provided")
|
||||
|
||||
try:
|
||||
curl_cffi_version = pkg_resources.get_distribution('curl-cffi').version
|
||||
if curl_cffi_version != '0.8.1b9':
|
||||
print("\033[93mWarning: DeepSeek API requires curl-cffi version 0.8.1b9", file=sys.stderr)
|
||||
print("Please install the correct version using: pip install curl-cffi==0.8.1b9\033[0m", file=sys.stderr)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print("\033[93mWarning: curl-cffi not found. Please install version 0.8.1b9:", file=sys.stderr)
|
||||
print("pip install curl-cffi==0.8.1b9\033[0m", file=sys.stderr)
|
||||
|
||||
self.auth_token = auth_token
|
||||
self.pow_solver = DeepSeekPOW()
|
||||
|
||||
# Load cookies from JSON file
|
||||
cookies_path = Path(__file__).parent / 'cookies.json'
|
||||
try:
|
||||
with open(cookies_path, 'r') as f:
|
||||
cookie_data = json.load(f)
|
||||
self.cookies = cookie_data.get('cookies', {})
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"\033[93mWarning: Could not load cookies from {cookies_path}: {e}\033[0m", file=sys.stderr)
|
||||
self.cookies = {}
|
||||
|
||||
def _get_headers(self, pow_response: Optional[str] = None) -> Dict[str, str]:
|
||||
headers = {
|
||||
'accept': '*/*',
|
||||
'accept-language': 'en,fr-FR;q=0.9,fr;q=0.8,es-ES;q=0.7,es;q=0.6,en-US;q=0.5,am;q=0.4,de;q=0.3',
|
||||
'authorization': f'Bearer {self.auth_token}',
|
||||
'content-type': 'application/json',
|
||||
'origin': 'https://chat.deepseek.com',
|
||||
'referer': 'https://chat.deepseek.com/',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
|
||||
'x-app-version': '20241129.1',
|
||||
'x-client-locale': 'en_US',
|
||||
'x-client-platform': 'web',
|
||||
'x-client-version': '1.0.0-always',
|
||||
}
|
||||
|
||||
if pow_response:
|
||||
headers['x-ds-pow-response'] = pow_response
|
||||
|
||||
return headers
|
||||
|
||||
def _refresh_cookies(self) -> None:
|
||||
"""Run the cookie refresh script and reload cookies"""
|
||||
try:
|
||||
# Get path to bypass.py
|
||||
script_path = Path(__file__).parent / 'bypass.py'
|
||||
|
||||
# Run the script
|
||||
subprocess.run([sys.executable, script_path], check=True)
|
||||
|
||||
# Wait briefly for cookies file to be written
|
||||
time.sleep(2)
|
||||
|
||||
# Reload cookies
|
||||
cookies_path = Path(__file__).parent / 'cookies.json'
|
||||
with open(cookies_path, 'r') as f:
|
||||
cookie_data = json.load(f)
|
||||
self.cookies = cookie_data.get('cookies', {})
|
||||
|
||||
except Exception as e:
|
||||
print(f"\033[93mWarning: Failed to refresh cookies: {e}\033[0m", file=sys.stderr)
|
||||
|
||||
def _make_request(self, method: str, endpoint: str, json_data: Dict[str, Any], pow_required: bool = False) -> Any:
|
||||
url = f"{self.BASE_URL}{endpoint}"
|
||||
|
||||
retry_count = 0
|
||||
max_retries = 2
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
headers = self._get_headers()
|
||||
if pow_required:
|
||||
challenge = self._get_pow_challenge()
|
||||
pow_response = self.pow_solver.solve_challenge(challenge)
|
||||
headers = self._get_headers(pow_response)
|
||||
|
||||
response = requests.request(
|
||||
method=method,
|
||||
url=url,
|
||||
headers=headers,
|
||||
json=json_data,
|
||||
cookies=self.cookies,
|
||||
impersonate='chrome120',
|
||||
timeout=None
|
||||
)
|
||||
|
||||
# Check if we hit Cloudflare protection
|
||||
if "<!DOCTYPE html>" in response.text and "Just a moment" in response.text:
|
||||
print("\033[93mWarning: Cloudflare protection detected. Bypassing...\033[0m", file=sys.stderr)
|
||||
if retry_count < max_retries - 1:
|
||||
self._refresh_cookies() # Refresh cookies
|
||||
retry_count += 1
|
||||
continue
|
||||
|
||||
# Handle other response codes
|
||||
if response.status_code == 401:
|
||||
raise AuthenticationError("Invalid or expired authentication token")
|
||||
elif response.status_code == 429:
|
||||
raise RateLimitError("API rate limit exceeded")
|
||||
elif response.status_code >= 500:
|
||||
raise APIError(f"Server error occurred: {response.text}", response.status_code)
|
||||
elif response.status_code != 200:
|
||||
raise APIError(f"API request failed: {response.text}", response.status_code)
|
||||
|
||||
return response.json()
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise NetworkError(f"Network error occurred: {str(e)}")
|
||||
except json.JSONDecodeError:
|
||||
raise APIError("Invalid JSON response from server")
|
||||
|
||||
raise APIError("Failed to bypass Cloudflare protection after multiple attempts")
|
||||
|
||||
def _get_pow_challenge(self) -> Dict[str, Any]:
|
||||
try:
|
||||
response = self._make_request(
|
||||
'POST',
|
||||
'/chat/create_pow_challenge',
|
||||
{'target_path': '/api/v0/chat/completion'}
|
||||
)
|
||||
return response['data']['biz_data']['challenge']
|
||||
except KeyError:
|
||||
raise APIError("Invalid challenge response format from server")
|
||||
|
||||
def create_chat_session(self) -> str:
|
||||
"""Creates a new chat session and returns the session ID"""
|
||||
try:
|
||||
response = self._make_request(
|
||||
'POST',
|
||||
'/chat_session/create',
|
||||
{'character_id': None}
|
||||
)
|
||||
return response['data']['biz_data']['id']
|
||||
except KeyError:
|
||||
raise APIError("Invalid session creation response format from server")
|
||||
|
||||
def chat_completion(self,
|
||||
chat_session_id: str,
|
||||
prompt: str,
|
||||
parent_message_id: Optional[str] = None,
|
||||
thinking_enabled: bool = True,
|
||||
search_enabled: bool = False) -> Generator[Dict[str, Any], None, None]:
|
||||
"""
|
||||
Send a message and get streaming response
|
||||
|
||||
Args:
|
||||
chat_session_id (str): The ID of the chat session
|
||||
prompt (str): The message to send
|
||||
parent_message_id (Optional[str]): ID of the parent message for threading
|
||||
thinking_enabled (bool): Whether to show the thinking process
|
||||
search_enabled (bool): Whether to enable web search for up-to-date information
|
||||
|
||||
Returns:
|
||||
Generator[Dict[str, Any], None, None]: Yields message chunks with content and type
|
||||
|
||||
Raises:
|
||||
AuthenticationError: If the authentication token is invalid
|
||||
RateLimitError: If the API rate limit is exceeded
|
||||
NetworkError: If a network error occurs
|
||||
APIError: If any other API error occurs
|
||||
"""
|
||||
if not prompt or not isinstance(prompt, str):
|
||||
raise ValueError("Prompt must be a non-empty string")
|
||||
if not chat_session_id or not isinstance(chat_session_id, str):
|
||||
raise ValueError("Chat session ID must be a non-empty string")
|
||||
|
||||
json_data = {
|
||||
'chat_session_id': chat_session_id,
|
||||
'parent_message_id': parent_message_id,
|
||||
'prompt': prompt,
|
||||
'ref_file_ids': [],
|
||||
'thinking_enabled': thinking_enabled,
|
||||
'search_enabled': search_enabled,
|
||||
}
|
||||
|
||||
try:
|
||||
headers = self._get_headers(
|
||||
pow_response=self.pow_solver.solve_challenge(
|
||||
self._get_pow_challenge()
|
||||
)
|
||||
)
|
||||
|
||||
response = requests.post(
|
||||
f"{self.BASE_URL}/chat/completion",
|
||||
headers=headers,
|
||||
json=json_data,
|
||||
cookies=self.cookies, # Add cookies
|
||||
impersonate='chrome120',
|
||||
stream=True,
|
||||
timeout=None
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
error_text = next(response.iter_lines(), b'').decode('utf-8', 'ignore')
|
||||
if response.status_code == 401:
|
||||
raise AuthenticationError("Invalid or expired authentication token")
|
||||
elif response.status_code == 429:
|
||||
raise RateLimitError("API rate limit exceeded")
|
||||
else:
|
||||
raise APIError(f"API request failed: {error_text}", response.status_code)
|
||||
|
||||
for chunk in response.iter_lines():
|
||||
try:
|
||||
parsed = self._parse_chunk(chunk)
|
||||
if parsed:
|
||||
yield parsed
|
||||
# print(parsed)
|
||||
if parsed.get('finish_reason') == 'stop':
|
||||
break
|
||||
except Exception as e:
|
||||
raise APIError(f"Error parsing response chunk: {str(e)}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise NetworkError(f"Network error occurred during streaming: {str(e)}")
|
||||
|
||||
def _parse_chunk(self, chunk: bytes) -> Optional[Dict[str, Any]]:
|
||||
"""Parse a SSE chunk from the API response"""
|
||||
if not chunk:
|
||||
return None
|
||||
|
||||
try:
|
||||
if chunk.startswith(b'data: '):
|
||||
data = json.loads(chunk[6:])
|
||||
|
||||
if 'v' in data and isinstance(data['v'], str):
|
||||
return {
|
||||
'content': data['v'],
|
||||
'type': 'message',
|
||||
'finish_reason': 'finish_reason'
|
||||
}
|
||||
|
||||
if 'choices' in data and data['choices']:
|
||||
choice = data['choices'][0]
|
||||
if 'delta' in choice:
|
||||
delta = choice['delta']
|
||||
|
||||
return {
|
||||
'content': delta.get('content', ''),
|
||||
'type': delta.get('type', ''),
|
||||
'finish_reason': choice.get('finish_reason')
|
||||
}
|
||||
except json.JSONDecodeError:
|
||||
raise APIError("Invalid JSON in response chunk")
|
||||
except Exception as e:
|
||||
raise APIError(f"Error parsing chunk: {str(e)}")
|
||||
|
||||
return None
|
||||
@@ -0,0 +1,87 @@
|
||||
# run_and_get_cookies.py
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
|
||||
def validate_cookies(cookies_data):
|
||||
"""Validate that cf_clearance cookie is present and not empty"""
|
||||
cookies = cookies_data.get('cookies', {})
|
||||
return 'cf_clearance' in cookies and cookies['cf_clearance'].strip() != ''
|
||||
|
||||
def get_and_save_cookies(server_url, cookie_file_path, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = requests.get(server_url)
|
||||
response.raise_for_status()
|
||||
cookies_data = response.json()
|
||||
|
||||
if not validate_cookies(cookies_data):
|
||||
print(f"Attempt {attempt + 1}: cf_clearance cookie not found, retrying...")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
cookies_to_save = {
|
||||
'cookies': cookies_data.get('cookies', {}),
|
||||
'user_agent': cookies_data.get('user_agent', '')
|
||||
}
|
||||
|
||||
os.makedirs(os.path.dirname(cookie_file_path), exist_ok=True)
|
||||
with open(cookie_file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(cookies_to_save, f, indent=4, ensure_ascii=False)
|
||||
print("Successfully obtained and saved cookies with cf_clearance!")
|
||||
return True
|
||||
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"Connection error on attempt {attempt + 1}: {str(e)}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(5)
|
||||
else:
|
||||
print("Max retries reached. Failed to get valid cookies.")
|
||||
return False
|
||||
|
||||
print("Failed to obtain valid cf_clearance cookie after all attempts")
|
||||
return False
|
||||
|
||||
def run_server_background():
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
server_script = os.path.abspath(os.path.join(script_dir, "server.py"))
|
||||
server_dir = os.path.dirname(server_script)
|
||||
|
||||
os.makedirs(server_dir, exist_ok=True)
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, server_script],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
cwd=server_dir,
|
||||
start_new_session=True
|
||||
)
|
||||
return process
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Getting the cookies...")
|
||||
server_process = run_server_background()
|
||||
|
||||
if server_process:
|
||||
# Increase initial wait time to ensure server is fully started
|
||||
time.sleep(10)
|
||||
server_url = "http://localhost:8000/cookies?url=https://chat.deepseek.com"
|
||||
cookie_file = "dsk/cookies.json"
|
||||
|
||||
# Increase max retries for more reliability
|
||||
success = get_and_save_cookies(server_url, cookie_file, max_retries=5)
|
||||
|
||||
if not success:
|
||||
print("Failed to obtain valid cookies.")
|
||||
server_process.terminate()
|
||||
sys.exit(1)
|
||||
server_process.terminate()
|
||||
else:
|
||||
print("Failed to start server.")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
DeepSeek Proof of Work Challenge Implementation
|
||||
Author: @xtekky
|
||||
Date: 2024
|
||||
|
||||
This module implements a proof-of-work challenge solver using WebAssembly (WASM)
|
||||
for Custom sha3 hashing. It provides functionality to solve computational challenges
|
||||
required for authentication or rate limiting purposes.
|
||||
"""
|
||||
|
||||
import json
|
||||
import base64
|
||||
import wasmtime
|
||||
import numpy as np
|
||||
from typing import Dict, Any
|
||||
import os
|
||||
|
||||
WASM_PATH = f'{os.path.dirname(__file__)}/wasm/sha3_wasm_bg.7b9ca65ddd.wasm'
|
||||
|
||||
class DeepSeekHash:
|
||||
def __init__(self):
|
||||
self.instance = None
|
||||
self.memory = None
|
||||
self.store = None
|
||||
|
||||
def init(self, wasm_path: str):
|
||||
engine = wasmtime.Engine()
|
||||
|
||||
with open(wasm_path, 'rb') as f:
|
||||
wasm_bytes = f.read()
|
||||
|
||||
module = wasmtime.Module(engine, wasm_bytes)
|
||||
|
||||
self.store = wasmtime.Store(engine)
|
||||
linker = wasmtime.Linker(engine)
|
||||
linker.define_wasi()
|
||||
|
||||
self.instance = linker.instantiate(self.store, module)
|
||||
self.memory = self.instance.exports(self.store)["memory"]
|
||||
|
||||
return self
|
||||
|
||||
def _write_to_memory(self, text: str) -> tuple[int, int]:
|
||||
encoded = text.encode('utf-8')
|
||||
length = len(encoded)
|
||||
ptr = self.instance.exports(self.store)["__wbindgen_export_0"](self.store, length, 1)
|
||||
|
||||
memory_view = self.memory.data_ptr(self.store)
|
||||
for i, byte in enumerate(encoded):
|
||||
memory_view[ptr + i] = byte
|
||||
|
||||
return ptr, length
|
||||
|
||||
def calculate_hash(self, algorithm: str, challenge: str, salt: str,
|
||||
difficulty: int, expire_at: int) -> float:
|
||||
|
||||
prefix = f"{salt}_{expire_at}_"
|
||||
retptr = self.instance.exports(self.store)["__wbindgen_add_to_stack_pointer"](self.store, -16)
|
||||
|
||||
try:
|
||||
challenge_ptr, challenge_len = self._write_to_memory(challenge)
|
||||
prefix_ptr, prefix_len = self._write_to_memory(prefix)
|
||||
|
||||
self.instance.exports(self.store)["wasm_solve"](
|
||||
self.store,
|
||||
retptr,
|
||||
challenge_ptr,
|
||||
challenge_len,
|
||||
prefix_ptr,
|
||||
prefix_len,
|
||||
float(difficulty)
|
||||
)
|
||||
|
||||
memory_view = self.memory.data_ptr(self.store)
|
||||
status = int.from_bytes(bytes(memory_view[retptr:retptr + 4]), byteorder='little', signed=True)
|
||||
|
||||
if status == 0:
|
||||
return None
|
||||
|
||||
value_bytes = bytes(memory_view[retptr + 8:retptr + 16])
|
||||
value = np.frombuffer(value_bytes, dtype=np.float64)[0]
|
||||
|
||||
return int(value)
|
||||
|
||||
finally:
|
||||
self.instance.exports(self.store)["__wbindgen_add_to_stack_pointer"](self.store, 16)
|
||||
|
||||
class DeepSeekPOW:
|
||||
def __init__(self):
|
||||
self.hasher = DeepSeekHash().init(WASM_PATH)
|
||||
|
||||
def solve_challenge(self, config: Dict[str, Any]) -> str:
|
||||
"""Solves a proof-of-work challenge and returns the encoded response"""
|
||||
answer = self.hasher.calculate_hash(
|
||||
config['algorithm'],
|
||||
config['challenge'],
|
||||
config['salt'],
|
||||
config['difficulty'],
|
||||
config['expire_at']
|
||||
)
|
||||
|
||||
result = {
|
||||
'algorithm': config['algorithm'],
|
||||
'challenge': config['challenge'],
|
||||
'salt': config['salt'],
|
||||
'answer': answer,
|
||||
'signature': config['signature'],
|
||||
'target_path': config['target_path']
|
||||
}
|
||||
|
||||
return base64.b64encode(json.dumps(result).encode()).decode()
|
||||
@@ -0,0 +1,61 @@
|
||||
# run_and_get_cookies.py
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
|
||||
def get_and_save_cookies(server_url, cookie_file_path):
|
||||
for attempt in range(5):
|
||||
try:
|
||||
response = requests.get(server_url)
|
||||
response.raise_for_status()
|
||||
cookies_data = response.json()
|
||||
|
||||
cookies_to_save = {
|
||||
'cookies': cookies_data.get('cookies', {}),
|
||||
'user_agent': cookies_data.get('user_agent', '')
|
||||
}
|
||||
|
||||
os.makedirs(os.path.dirname(cookie_file_path), exist_ok=True)
|
||||
with open(cookie_file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(cookies_to_save, f, indent=4, ensure_ascii=False)
|
||||
return
|
||||
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
if attempt < 4:
|
||||
time.sleep(5)
|
||||
else:
|
||||
raise
|
||||
|
||||
def run_server_background():
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
server_script = os.path.abspath(os.path.join(script_dir, "server.py"))
|
||||
server_dir = os.path.dirname(server_script)
|
||||
|
||||
os.makedirs(server_dir, exist_ok=True)
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, server_script],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
cwd=server_dir,
|
||||
start_new_session=True
|
||||
)
|
||||
return process
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Getting the cookies...")
|
||||
server_process = run_server_background()
|
||||
|
||||
if server_process:
|
||||
time.sleep(5)
|
||||
server_url = "http://localhost:8000/cookies?url=https://chat.deepseek.com"
|
||||
cookie_file = "dsk/cookies.json"
|
||||
get_and_save_cookies(server_url, cookie_file)
|
||||
else:
|
||||
print("Failed to start server.")
|
||||
@@ -0,0 +1,176 @@
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from CloudflareBypasser import CloudflareBypasser
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from fastapi import FastAPI, HTTPException, Response
|
||||
from pydantic import BaseModel
|
||||
from typing import Dict
|
||||
import argparse
|
||||
|
||||
from pyvirtualdisplay import Display
|
||||
import uvicorn
|
||||
import atexit
|
||||
import time
|
||||
|
||||
# Check if running in Docker mode
|
||||
DOCKER_MODE = os.getenv("DOCKERMODE", "false").lower() == "true"
|
||||
|
||||
SERVER_PORT = int(os.getenv("SERVER_PORT", 8000))
|
||||
|
||||
# Chromium options arguments
|
||||
arguments = [
|
||||
# "--remote-debugging-port=9222", # Add this line for remote debugging
|
||||
"-no-first-run",
|
||||
"-force-color-profile=srgb",
|
||||
"-metrics-recording-only",
|
||||
"-password-store=basic",
|
||||
"-use-mock-keychain",
|
||||
"-export-tagged-pdf",
|
||||
"-no-default-browser-check",
|
||||
"-disable-background-mode",
|
||||
"-enable-features=NetworkService,NetworkServiceInProcess,LoadCryptoTokenExtension,PermuteTLSExtensions",
|
||||
"-disable-features=FlashDeprecationWarning,EnablePasswordsAccountStorage",
|
||||
"-deny-permission-prompts",
|
||||
"-disable-gpu",
|
||||
"-accept-lang=en-US",
|
||||
#"-incognito" # You can add this line to open the browser in incognito mode by default
|
||||
]
|
||||
|
||||
browser_path = "/usr/bin/google-chrome"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# Pydantic model for the response
|
||||
class CookieResponse(BaseModel):
|
||||
cookies: Dict[str, str]
|
||||
user_agent: str
|
||||
|
||||
|
||||
# Function to check if the URL is safe
|
||||
def is_safe_url(url: str) -> bool:
|
||||
parsed_url = urlparse(url)
|
||||
ip_pattern = re.compile(
|
||||
r"^(127\.0\.0\.1|localhost|0\.0\.0\.0|::1|10\.\d+\.\d+\.\d+|172\.1[6-9]\.\d+\.\d+|172\.2[0-9]\.\d+\.\d+|172\.3[0-1]\.\d+\.\d+|192\.168\.\d+\.\d+)$"
|
||||
)
|
||||
hostname = parsed_url.hostname
|
||||
if (hostname and ip_pattern.match(hostname)) or parsed_url.scheme == "file":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Function to verify if the page has loaded properly
|
||||
def verify_page_loaded(driver: ChromiumPage) -> bool:
|
||||
"""Verify if the page has loaded properly"""
|
||||
try:
|
||||
# Wait for body element to be present
|
||||
body = driver.ele('tag:body', timeout=10)
|
||||
# Check if page has actual content
|
||||
return len(body.html) > 100
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
# Function to bypass Cloudflare protection
|
||||
def bypass_cloudflare(url: str, retries: int, log: bool, proxy: str = None) -> ChromiumPage:
|
||||
max_load_retries = 3
|
||||
|
||||
for load_attempt in range(max_load_retries):
|
||||
options = ChromiumOptions().auto_port()
|
||||
if DOCKER_MODE:
|
||||
options.set_argument("--auto-open-devtools-for-tabs", "true")
|
||||
options.set_argument("--remote-debugging-port=9222")
|
||||
options.set_argument("--no-sandbox") # Necessary for Docker
|
||||
options.set_argument("--disable-gpu") # Optional, helps in some cases
|
||||
options.set_paths(browser_path=browser_path).headless(False)
|
||||
else:
|
||||
options.set_paths(browser_path=browser_path).headless(False)
|
||||
|
||||
if proxy:
|
||||
options.set_proxy(proxy)
|
||||
|
||||
driver = ChromiumPage(addr_or_opts=options)
|
||||
try:
|
||||
driver.get(url)
|
||||
# Wait for initial page load
|
||||
time.sleep(5)
|
||||
|
||||
if not verify_page_loaded(driver):
|
||||
driver.quit()
|
||||
if load_attempt < max_load_retries - 1:
|
||||
time.sleep(3)
|
||||
continue
|
||||
else:
|
||||
raise Exception("Failed to load page properly after multiple attempts")
|
||||
|
||||
cf_bypasser = CloudflareBypasser(driver, retries, log)
|
||||
cf_bypasser.bypass()
|
||||
return driver
|
||||
except Exception as e:
|
||||
driver.quit()
|
||||
if load_attempt < max_load_retries - 1:
|
||||
time.sleep(3)
|
||||
continue
|
||||
raise e
|
||||
|
||||
|
||||
# Endpoint to get cookies
|
||||
@app.get("/cookies", response_model=CookieResponse)
|
||||
async def get_cookies(url: str, retries: int = 5, proxy: str = None):
|
||||
if not is_safe_url(url):
|
||||
raise HTTPException(status_code=400, detail="Invalid URL")
|
||||
try:
|
||||
driver = bypass_cloudflare(url, retries, log, proxy)
|
||||
cookies = {cookie.get("name", ""): cookie.get("value", " ") for cookie in driver.cookies()}
|
||||
user_agent = driver.user_agent
|
||||
driver.quit()
|
||||
return CookieResponse(cookies=cookies, user_agent=user_agent)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# Endpoint to get HTML content and cookies
|
||||
@app.get("/html")
|
||||
async def get_html(url: str, retries: int = 5, proxy: str = None):
|
||||
if not is_safe_url(url):
|
||||
raise HTTPException(status_code=400, detail="Invalid URL")
|
||||
try:
|
||||
driver = bypass_cloudflare(url, retries, log, proxy)
|
||||
html = driver.html
|
||||
cookies_json = {cookie.get("name", ""): cookie.get("value", " ") for cookie in driver.cookies()}
|
||||
response = Response(content=html, media_type="text/html")
|
||||
response.headers["cookies"] = json.dumps(cookies_json)
|
||||
response.headers["user_agent"] = driver.user_agent
|
||||
driver.quit()
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# Main entry point
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Cloudflare bypass api")
|
||||
|
||||
parser.add_argument("--nolog", action="store_true", help="Disable logging")
|
||||
parser.add_argument("--headless", action="store_true", help="Run in headless mode")
|
||||
|
||||
args = parser.parse_args()
|
||||
display = None
|
||||
|
||||
if args.headless or DOCKER_MODE:
|
||||
display = Display(visible=0, size=(1920, 1080))
|
||||
display.start()
|
||||
|
||||
def cleanup_display():
|
||||
if display:
|
||||
display.stop()
|
||||
atexit.register(cleanup_display)
|
||||
|
||||
if args.nolog:
|
||||
log = False
|
||||
else:
|
||||
log = True
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=SERVER_PORT)
|
||||
Binary file not shown.
Reference in new issue
Block a user