feat: 添加代理支持

- 在 .env 中通过 PROXY_URL 配置代理
- 支持 http/https/socks4/socks5 代理
- 所有网络请求(curl_cffi 和 requests)都走代理
- 服务类构造函数新增 proxies 参数传递代理配置
This commit is contained in:
Debug Assistant
2026-02-20 00:05:03 +08:00
parent 91b187903c
commit 6798426c7b
6 changed files with 47 additions and 13 deletions

View File

@@ -5,3 +5,10 @@ FREEMAIL_TOKEN=your-jwt-token
# Turnstile 验证配置 # Turnstile 验证配置
# 如果不填则使用本地 Turnstile Solverhttp://127.0.0.1:5072 # 如果不填则使用本地 Turnstile Solverhttp://127.0.0.1:5072
YESCAPTCHA_KEY= YESCAPTCHA_KEY=
# 代理配置(可选)
# 支持 http、https、socks4、socks5
# 示例:
# PROXY_URL=http://127.0.0.1:7890
# PROXY_URL=socks5://127.0.0.1:10808
PROXY_URL=

View File

@@ -6,7 +6,7 @@ from dotenv import load_dotenv
class EmailService: class EmailService:
def __init__(self): def __init__(self, proxies=None):
load_dotenv() load_dotenv()
self.worker_domain = os.getenv("WORKER_DOMAIN") self.worker_domain = os.getenv("WORKER_DOMAIN")
self.freemail_token = os.getenv("FREEMAIL_TOKEN") self.freemail_token = os.getenv("FREEMAIL_TOKEN")
@@ -14,6 +14,7 @@ class EmailService:
raise ValueError("Missing: WORKER_DOMAIN or FREEMAIL_TOKEN") raise ValueError("Missing: WORKER_DOMAIN or FREEMAIL_TOKEN")
self.base_url = f"https://{self.worker_domain}" self.base_url = f"https://{self.worker_domain}"
self.headers = {"Authorization": f"Bearer {self.freemail_token}"} self.headers = {"Authorization": f"Bearer {self.freemail_token}"}
self.proxies = proxies or {}
def create_email(self): def create_email(self):
"""创建临时邮箱 GET /api/generate""" """创建临时邮箱 GET /api/generate"""
@@ -21,6 +22,7 @@ class EmailService:
res = requests.get( res = requests.get(
f"{self.base_url}/api/generate", f"{self.base_url}/api/generate",
headers=self.headers, headers=self.headers,
proxies=self.proxies,
timeout=10 timeout=10
) )
if res.status_code == 200: if res.status_code == 200:
@@ -44,6 +46,7 @@ class EmailService:
f"{self.base_url}/api/emails", f"{self.base_url}/api/emails",
params={"mailbox": email}, params={"mailbox": email},
headers=self.headers, headers=self.headers,
proxies=self.proxies,
timeout=10 timeout=10
) )
if debug: if debug:
@@ -91,6 +94,7 @@ class EmailService:
f"{self.base_url}/api/mailboxes", f"{self.base_url}/api/mailboxes",
params={"address": address}, params={"address": address},
headers=self.headers, headers=self.headers,
proxies=self.proxies,
timeout=10 timeout=10
) )
return res.status_code == 200 and res.json().get("success") return res.status_code == 200 and res.json().get("success")

View File

@@ -25,6 +25,7 @@ class NsfwSettingsService:
user_agent: Optional[str] = None, user_agent: Optional[str] = None,
cf_clearance: Optional[str] = None, cf_clearance: Optional[str] = None,
timeout: int = 15, timeout: int = 15,
proxies: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
启用 always_show_nsfw_content。 启用 always_show_nsfw_content。
@@ -88,6 +89,7 @@ class NsfwSettingsService:
data=data, data=data,
impersonate=impersonate or "chrome120", impersonate=impersonate or "chrome120",
timeout=timeout, timeout=timeout,
proxies=proxies or {},
) )
hex_reply = response.content.hex() hex_reply = response.content.hex()
grpc_status = response.headers.get("grpc-status") grpc_status = response.headers.get("grpc-status")
@@ -124,6 +126,7 @@ class NsfwSettingsService:
impersonate: str = "chrome120", impersonate: str = "chrome120",
user_agent: Optional[str] = None, user_agent: Optional[str] = None,
timeout: int = 30, timeout: int = 30,
proxies: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
使用帖子方法开启 Unhinged 模式(二次验证)。 使用帖子方法开启 Unhinged 模式(二次验证)。
@@ -153,6 +156,7 @@ class NsfwSettingsService:
data=data, data=data,
impersonate=impersonate, impersonate=impersonate,
timeout=timeout, timeout=timeout,
proxies=proxies or {},
) )
return { return {
"ok": response.status_code == 200, "ok": response.status_code == 200,

View File

@@ -12,13 +12,14 @@ load_dotenv()
class TurnstileService: class TurnstileService:
"""Turnstile验证服务类""" """Turnstile验证服务类"""
def __init__(self, solver_url="http://127.0.0.1:5072"): def __init__(self, solver_url="http://127.0.0.1:5072", proxies=None):
""" """
初始化Turnstile服务 初始化Turnstile服务
""" """
self.yescaptcha_key = os.getenv('YESCAPTCHA_KEY', '').strip() self.yescaptcha_key = os.getenv('YESCAPTCHA_KEY', '').strip()
self.solver_url = solver_url self.solver_url = solver_url
self.yescaptcha_api = "https://api.yescaptcha.com" self.yescaptcha_api = "https://api.yescaptcha.com"
self.proxies = proxies or {}
def create_task(self, siteurl, sitekey): def create_task(self, siteurl, sitekey):
""" """
@@ -35,7 +36,7 @@ class TurnstileService:
"websiteKey": sitekey "websiteKey": sitekey
} }
} }
response = requests.post(url, json=payload) response = requests.post(url, json=payload, proxies=self.proxies)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
if data.get('errorId') != 0: if data.get('errorId') != 0:
@@ -44,7 +45,7 @@ class TurnstileService:
else: else:
# 使用本地 Turnstile Solver # 使用本地 Turnstile Solver
url = f"{self.solver_url}/turnstile?url={siteurl}&sitekey={sitekey}" url = f"{self.solver_url}/turnstile?url={siteurl}&sitekey={sitekey}"
response = requests.get(url) response = requests.get(url, proxies=self.proxies)
response.raise_for_status() response.raise_for_status()
return response.json()['taskId'] return response.json()['taskId']
@@ -63,7 +64,7 @@ class TurnstileService:
"clientKey": self.yescaptcha_key, "clientKey": self.yescaptcha_key,
"taskId": task_id "taskId": task_id
} }
response = requests.post(url, json=payload) response = requests.post(url, json=payload, proxies=self.proxies)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
@@ -86,7 +87,7 @@ class TurnstileService:
else: else:
# 使用本地 Turnstile Solver # 使用本地 Turnstile Solver
url = f"{self.solver_url}/result?id={task_id}" url = f"{self.solver_url}/result?id={task_id}"
response = requests.get(url) response = requests.get(url, proxies=self.proxies)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
captcha = data.get('solution', {}).get('token', None) captcha = data.get('solution', {}).get('token', None)

View File

@@ -25,6 +25,7 @@ class UserAgreementService:
user_agent: Optional[str] = None, user_agent: Optional[str] = None,
cf_clearance: Optional[str] = None, cf_clearance: Optional[str] = None,
timeout: int = 15, timeout: int = 15,
proxies: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
同意 TOS 版本。 同意 TOS 版本。
@@ -85,6 +86,7 @@ class UserAgreementService:
data=data, data=data,
impersonate=impersonate or "chrome120", impersonate=impersonate or "chrome120",
timeout=timeout, timeout=timeout,
proxies=proxies or {},
) )
hex_reply = response.content.hex() hex_reply = response.content.hex()
grpc_status = response.headers.get("grpc-status") grpc_status = response.headers.get("grpc-status")

30
grok.py
View File

@@ -6,9 +6,22 @@ import traceback
from urllib.parse import urljoin, urlparse from urllib.parse import urljoin, urlparse
from curl_cffi import requests from curl_cffi import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from dotenv import load_dotenv
from g import EmailService, TurnstileService, UserAgreementService, NsfwSettingsService from g import EmailService, TurnstileService, UserAgreementService, NsfwSettingsService
# 加载环境变量
load_dotenv()
# 获取代理配置
PROXY_URL = os.getenv("PROXY_URL", "").strip()
if PROXY_URL:
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}
print(f"[*] 使用代理: {PROXY_URL}")
else:
PROXIES = {}
print("[*] 未配置代理,使用直连")
# 基础配置 # 基础配置
# 基础 URL用于 API 请求和 Solver # 基础 URL用于 API 请求和 Solver
base_url = "https://accounts.x.ai" base_url = "https://accounts.x.ai"
@@ -39,10 +52,7 @@ def get_random_chrome_profile():
f"Chrome/{profile['version']} Safari/537.36" f"Chrome/{profile['version']} Safari/537.36"
) )
return profile["impersonate"], ua return profile["impersonate"], ua
PROXIES = {
# "http": "http://127.0.0.1:10808",
# "https": "http://127.0.0.1:10808"
}
# 动态获取的全局变量 # 动态获取的全局变量
config = { config = {
@@ -283,6 +293,7 @@ def register_single_thread(debug_mode=False, single_run=False):
sso_rw=sso_rw or "", sso_rw=sso_rw or "",
impersonate=impersonate_fingerprint, impersonate=impersonate_fingerprint,
user_agent=account_user_agent, user_agent=account_user_agent,
proxies=PROXIES,
) )
tos_hex = tos_result.get("hex_reply") or "" tos_hex = tos_result.get("hex_reply") or ""
if debug_mode: if debug_mode:
@@ -300,6 +311,7 @@ def register_single_thread(debug_mode=False, single_run=False):
sso_rw=sso_rw or "", sso_rw=sso_rw or "",
impersonate=impersonate_fingerprint, impersonate=impersonate_fingerprint,
user_agent=account_user_agent, user_agent=account_user_agent,
proxies=PROXIES,
) )
nsfw_hex = nsfw_result.get("hex_reply") or "" nsfw_hex = nsfw_result.get("hex_reply") or ""
nsfw_ok = nsfw_result.get("ok", False) nsfw_ok = nsfw_result.get("ok", False)
@@ -311,7 +323,11 @@ def register_single_thread(debug_mode=False, single_run=False):
# 立即进行二次验证 (enable_unhinged) # 立即进行二次验证 (enable_unhinged)
if debug_mode: if debug_mode:
print(f"[DEBUG] [{thread_id}] 启用 Unhinged...") print(f"[DEBUG] [{thread_id}] 启用 Unhinged...")
unhinged_result = nsfw_service.enable_unhinged(sso, sso_rw or "") unhinged_result = nsfw_service.enable_unhinged(
sso=sso,
sso_rw=sso_rw or "",
proxies=PROXIES,
)
unhinged_ok = unhinged_result.get("ok", False) unhinged_ok = unhinged_result.get("ok", False)
if debug_mode: if debug_mode:
print(f"[DEBUG] [{thread_id}] Unhinged 结果: ok={unhinged_ok}") print(f"[DEBUG] [{thread_id}] Unhinged 结果: ok={unhinged_ok}")
@@ -385,7 +401,7 @@ def main():
print("[*] 正在初始化...") print("[*] 正在初始化...")
start_url = site_url start_url = site_url
print(f"[DEBUG] 请求 URL: {start_url}") print(f"[DEBUG] 请求 URL: {start_url}")
with requests.Session(impersonate=DEFAULT_IMPERSONATE) as s: with requests.Session(impersonate=DEFAULT_IMPERSONATE, proxies=PROXIES) as s:
try: try:
print("[DEBUG] 正在获取页面...") print("[DEBUG] 正在获取页面...")
html = s.get(start_url, timeout=30).text html = s.get(start_url, timeout=30).text
@@ -407,7 +423,7 @@ def main():
print(f"[DEBUG] 找到 {len(js_urls)} 个 JS 文件") print(f"[DEBUG] 找到 {len(js_urls)} 个 JS 文件")
for js_url in js_urls: for js_url in js_urls:
print(f"[DEBUG] 正在请求 JS: {js_url}") print(f"[DEBUG] 正在请求 JS: {js_url}")
js_content = s.get(js_url, timeout=30).text js_content = s.get(js_url, timeout=30, proxies=PROXIES).text
match = re.search(r'7f[a-fA-F0-9]{40}', js_content) match = re.search(r'7f[a-fA-F0-9]{40}', js_content)
if match: if match:
config["action_id"] = match.group(0) config["action_id"] = match.group(0)