mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
requests, a few x crypto libraries from github
This commit is contained in:
1 parent
c0450f1aaa
commit
df2c2b781f
5 files changed
+754
No files matched your search
+515
@@ -0,0 +1,515 @@
|
||||
#!/usr/bin/env python3
|
||||
# From https://github.com/kbtit25/grok2api/blob/73c917b15438e12699531ccf9411d3a7d1110cbf/xStatsigIDGenerator.py
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
import struct
|
||||
import hashlib
|
||||
import time
|
||||
import secrets
|
||||
import requests
|
||||
import re
|
||||
import json
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
class XStatsigIDGenerator:
|
||||
"""x-statsig-id 生成器"""
|
||||
|
||||
def __init__(self):
|
||||
self.base_timestamp = int(time.time()) # 使用当前系统时间
|
||||
self.grok_url = "https://grok.com"
|
||||
|
||||
def get_grok_meta_content(self) -> bytes:
|
||||
"""
|
||||
从 grok.com 获取 meta 标签中的 grok-site-verification 内容
|
||||
使用多种方法彻底解决403问题
|
||||
|
||||
Returns:
|
||||
48字节的meta内容
|
||||
"""
|
||||
print("🌐 正在请求 grok.com...")
|
||||
|
||||
# 定义多种绕过策略
|
||||
strategies = [
|
||||
self._try_curl_with_proxy,
|
||||
self._try_curl_with_different_ua,
|
||||
self._try_requests_with_session,
|
||||
self._try_curl_cffi_advanced,
|
||||
self._try_alternative_endpoints,
|
||||
self._try_cached_content
|
||||
]
|
||||
|
||||
for i, strategy in enumerate(strategies):
|
||||
try:
|
||||
print(f" 尝试策略 {i+1}: {strategy.__name__}")
|
||||
result = strategy()
|
||||
if result:
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f" 策略 {i+1} 失败: {e}")
|
||||
continue
|
||||
|
||||
# 所有策略都失败,使用备用内容
|
||||
print(" ❌ 所有策略都失败,使用备用meta内容")
|
||||
fallback = b"backup-grok-meta-content-when-request-fails-ok"
|
||||
return fallback + b'\x00' * (48 - len(fallback))
|
||||
|
||||
def _try_curl_with_proxy(self) -> bytes:
|
||||
"""策略1: 使用curl + 代理"""
|
||||
import subprocess
|
||||
|
||||
# 尝试多个公共代理
|
||||
proxies = [
|
||||
None, # 无代理
|
||||
"socks5://127.0.0.1:1080", # 本地代理
|
||||
"http://127.0.0.1:8080", # 本地HTTP代理
|
||||
]
|
||||
|
||||
for proxy in proxies:
|
||||
try:
|
||||
curl_command = [
|
||||
'curl', '-s', '-L', '--max-time', '15',
|
||||
'--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'--header', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'--header', 'Accept-Language: en-US,en;q=0.5',
|
||||
'--header', 'Accept-Encoding: gzip, deflate',
|
||||
'--header', 'Connection: keep-alive',
|
||||
'--header', 'Upgrade-Insecure-Requests: 1',
|
||||
'--compressed'
|
||||
]
|
||||
|
||||
if proxy:
|
||||
curl_command.extend(['--proxy', proxy])
|
||||
|
||||
curl_command.append(self.grok_url)
|
||||
|
||||
result = subprocess.run(curl_command, capture_output=True, text=True, timeout=15)
|
||||
|
||||
if result.returncode == 0 and len(result.stdout) > 1000:
|
||||
print(f" ✅ curl成功 (代理: {proxy or '无'})")
|
||||
return self._extract_meta_from_html(result.stdout)
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _try_curl_with_different_ua(self) -> bytes:
|
||||
"""策略2: 使用不同的User-Agent"""
|
||||
import subprocess
|
||||
|
||||
user_agents = [
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
||||
'Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/109.0 Firefox/119.0',
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15'
|
||||
]
|
||||
|
||||
for ua in user_agents:
|
||||
try:
|
||||
curl_command = [
|
||||
'curl', '-s', '-L', '--max-time', '10',
|
||||
'--user-agent', ua,
|
||||
'--header', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'--header', 'Accept-Language: en-US,en;q=0.9',
|
||||
'--header', 'Cache-Control: no-cache',
|
||||
'--header', 'Pragma: no-cache',
|
||||
'--compressed',
|
||||
self.grok_url
|
||||
]
|
||||
|
||||
result = subprocess.run(curl_command, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0 and len(result.stdout) > 1000:
|
||||
print(f" ✅ 不同UA成功")
|
||||
return self._extract_meta_from_html(result.stdout)
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _try_requests_with_session(self) -> bytes:
|
||||
"""策略3: 使用requests session模拟真实浏览器行为"""
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
|
||||
session = requests.Session()
|
||||
|
||||
# 配置重试策略
|
||||
retry_strategy = Retry(
|
||||
total=3,
|
||||
backoff_factor=1,
|
||||
status_forcelist=[429, 500, 502, 503, 504],
|
||||
)
|
||||
adapter = HTTPAdapter(max_retries=retry_strategy)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
|
||||
# 模拟真实浏览器行为
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'DNT': '1',
|
||||
'Connection': 'keep-alive',
|
||||
'Upgrade-Insecure-Requests': '1',
|
||||
'Sec-Fetch-Dest': 'document',
|
||||
'Sec-Fetch-Mode': 'navigate',
|
||||
'Sec-Fetch-Site': 'none',
|
||||
'Sec-Fetch-User': '?1',
|
||||
'Cache-Control': 'max-age=0'
|
||||
}
|
||||
|
||||
try:
|
||||
# 先访问主页建立session
|
||||
session.get('https://x.com', headers=headers, timeout=5)
|
||||
|
||||
# 再访问grok
|
||||
response = session.get(self.grok_url, headers=headers, timeout=10)
|
||||
|
||||
if response.status_code == 200 and len(response.text) > 1000:
|
||||
print(f" ✅ session请求成功")
|
||||
return self._extract_meta_from_html(response.text)
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def _try_curl_cffi_advanced(self) -> bytes:
|
||||
"""策略4: 使用curl_cffi高级模拟"""
|
||||
try:
|
||||
from curl_cffi import requests as curl_requests
|
||||
|
||||
# 尝试不同的浏览器模拟
|
||||
impersonations = ['chrome120', 'chrome119', 'safari17', 'firefox119']
|
||||
|
||||
for imp in impersonations:
|
||||
try:
|
||||
response = curl_requests.get(
|
||||
self.grok_url,
|
||||
impersonate=imp,
|
||||
timeout=10,
|
||||
headers={
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code == 200 and len(response.text) > 1000:
|
||||
print(f" ✅ curl_cffi成功 ({imp})")
|
||||
return self._extract_meta_from_html(response.text)
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def _try_alternative_endpoints(self) -> bytes:
|
||||
"""策略5: 尝试替代端点"""
|
||||
import subprocess
|
||||
|
||||
# 尝试不同的URL路径
|
||||
alternative_urls = [
|
||||
'https://grok.com/',
|
||||
'https://grok.com/login',
|
||||
'https://grok.com/home',
|
||||
'https://x.com/i/grok'
|
||||
]
|
||||
|
||||
for url in alternative_urls:
|
||||
try:
|
||||
curl_command = [
|
||||
'curl', '-s', '-L', '--max-time', '8',
|
||||
'--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
'--header', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'--compressed',
|
||||
url
|
||||
]
|
||||
|
||||
result = subprocess.run(curl_command, capture_output=True, text=True, timeout=8)
|
||||
|
||||
if result.returncode == 0 and len(result.stdout) > 500:
|
||||
print(f" ✅ 替代端点成功: {url}")
|
||||
meta_result = self._extract_meta_from_html(result.stdout)
|
||||
if meta_result:
|
||||
return meta_result
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _try_cached_content(self) -> bytes:
|
||||
"""策略6: 使用缓存或预设内容"""
|
||||
# 如果有真实的grok meta内容,可以在这里硬编码
|
||||
known_meta_contents = [
|
||||
"grok-site-verification-content-2024-production-v1",
|
||||
"x-grok-verification-meta-content-stable-release",
|
||||
"grok-meta-verification-string-for-api-access"
|
||||
]
|
||||
|
||||
for content in known_meta_contents:
|
||||
try:
|
||||
meta_bytes = content.encode('utf-8')
|
||||
if len(meta_bytes) < 48:
|
||||
meta_bytes = meta_bytes + b'\x00' * (48 - len(meta_bytes))
|
||||
elif len(meta_bytes) > 48:
|
||||
meta_bytes = meta_bytes[:48]
|
||||
|
||||
print(f" ✅ 使用预设内容: {content[:30]}...")
|
||||
return meta_bytes
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _extract_meta_from_html(self, html_content: str) -> bytes:
|
||||
"""从HTML中提取meta内容"""
|
||||
patterns = [
|
||||
r'<meta\s+name=["\']grok-site-verification["\']\s+content=["\']([^"\']+)["\']',
|
||||
r'<meta\s+content=["\']([^"\']+)["\']\s+name=["\']grok-site-verification["\']',
|
||||
r'grok-site-verification["\']?\s*(?:content|value)\s*=\s*["\']([^"\']+)["\']',
|
||||
# 扩展模式,查找其他可能的meta标签
|
||||
r'<meta\s+name=["\']verification["\']\s+content=["\']([^"\']+)["\']',
|
||||
r'<meta\s+name=["\']site-verification["\']\s+content=["\']([^"\']+)["\']'
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, html_content, re.IGNORECASE)
|
||||
if match:
|
||||
verification_content = match.group(1)
|
||||
print(f" ✅ 找到meta内容: {verification_content[:50]}...")
|
||||
|
||||
# 转换为48字节
|
||||
meta_bytes = verification_content.encode('utf-8')
|
||||
if len(meta_bytes) < 48:
|
||||
meta_bytes = meta_bytes + b'\x00' * (48 - len(meta_bytes))
|
||||
elif len(meta_bytes) > 48:
|
||||
meta_bytes = meta_bytes[:48]
|
||||
|
||||
return meta_bytes
|
||||
|
||||
# 如果没找到特定的meta标签,尝试从HTML中提取其他有用信息
|
||||
if 'grok' in html_content.lower() and len(html_content) > 1000:
|
||||
# 使用HTML内容的哈希作为meta内容
|
||||
import hashlib
|
||||
content_hash = hashlib.sha256(html_content.encode()).hexdigest()[:48]
|
||||
meta_bytes = content_hash.encode('utf-8')
|
||||
if len(meta_bytes) < 48:
|
||||
meta_bytes = meta_bytes + b'\x00' * (48 - len(meta_bytes))
|
||||
|
||||
print(f" ✅ 使用内容哈希作为meta: {content_hash[:32]}...")
|
||||
return meta_bytes
|
||||
|
||||
return None
|
||||
|
||||
def generate_browser_fingerprint(self) -> str:
|
||||
"""
|
||||
生成浏览器指纹信息 (结合方法1和方法3)
|
||||
|
||||
Returns:
|
||||
指纹字符串
|
||||
"""
|
||||
print("🔍 生成浏览器指纹...")
|
||||
|
||||
# 模拟浏览器指纹信息
|
||||
fingerprint_data = {
|
||||
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"language": "en",
|
||||
"languages": ["en", "zh", "zh-TW", "zh-CN"],
|
||||
"platform": "MacIntel",
|
||||
"cookieEnabled": True,
|
||||
"doNotTrack": None,
|
||||
"screenWidth": 450,
|
||||
"screenHeight": 654,
|
||||
"screenColorDepth": 24,
|
||||
"screenPixelDepth": 24,
|
||||
"screenAvailWidth": 450,
|
||||
"screenAvailHeight": 654,
|
||||
"innerWidth": 450,
|
||||
"innerHeight": 654,
|
||||
"outerWidth": 1920,
|
||||
"outerHeight": 1055,
|
||||
"timezone": "Asia/Shanghai",
|
||||
"timezoneOffset": -480,
|
||||
"hardwareConcurrency": 14,
|
||||
"deviceMemory": 8,
|
||||
"maxTouchPoints": 0
|
||||
}
|
||||
|
||||
# 方法3: 生成指纹哈希
|
||||
fingerprint_string = json.dumps(fingerprint_data, sort_keys=True, separators=(',', ':'))
|
||||
fingerprint_hash = hashlib.sha256(fingerprint_string.encode('utf-8')).hexdigest()
|
||||
|
||||
print(f" 指纹数据长度: {len(fingerprint_string)} 字符")
|
||||
print(f" 指纹哈希: {fingerprint_hash[:32]}...")
|
||||
|
||||
return fingerprint_hash
|
||||
|
||||
def generate_x_statsig_id(self, method: str = "GET", pathname: str = "/") -> str:
|
||||
"""
|
||||
生成完整的 x-statsig-id
|
||||
|
||||
Args:
|
||||
method: 请求方式 (GET/POST)
|
||||
pathname: 请求路径
|
||||
|
||||
Returns:
|
||||
生成的 x-statsig-id 字符串
|
||||
"""
|
||||
print("=" * 60)
|
||||
print("🚀 开始生成 x-statsig-id")
|
||||
print("=" * 60)
|
||||
|
||||
print(f"📋 生成参数:")
|
||||
print(f" Method: {method}")
|
||||
print(f" Pathname: {pathname}")
|
||||
|
||||
# 1. 获取 grok.com 的 meta content
|
||||
meta_content = self.get_grok_meta_content()
|
||||
|
||||
# 2. 生成浏览器指纹
|
||||
fingerprint = self.generate_browser_fingerprint()
|
||||
|
||||
# 3. 生成当前时间戳
|
||||
current_timestamp = int(time.time())
|
||||
relative_timestamp = current_timestamp - self.base_timestamp
|
||||
|
||||
print(f"⏰ 时间信息:")
|
||||
print(f" 当前时间戳: {current_timestamp}")
|
||||
print(f" 基准时间戳: {self.base_timestamp}")
|
||||
print(f" 相对时间戳: {relative_timestamp}")
|
||||
|
||||
# 4. 生成时间戳字节 (小端序)
|
||||
timestamp_bytes = struct.pack('<I', relative_timestamp)
|
||||
print(f" 时间戳字节: {timestamp_bytes.hex()}")
|
||||
|
||||
# 5. 生成SHA256
|
||||
sha_input = f"{method}!{pathname}!{relative_timestamp}{fingerprint}"
|
||||
sha256_hash = hashlib.sha256(sha_input.encode('utf-8')).digest()
|
||||
sha256_16bytes = sha256_hash[:16]
|
||||
|
||||
print(f"🔐 SHA256信息:")
|
||||
print(f" 输入字符串: {sha_input[:100]}...")
|
||||
print(f" SHA256前16字节: {sha256_16bytes.hex()}")
|
||||
|
||||
# 6. 固定值
|
||||
fixed_byte = b'\x03'
|
||||
|
||||
# 7. 组合payload数据
|
||||
payload_data = meta_content + timestamp_bytes + sha256_16bytes + fixed_byte
|
||||
print(f"📦 Payload长度: {len(payload_data)} 字节")
|
||||
|
||||
# 8. 生成异或key并加密
|
||||
xor_key = secrets.randbits(8)
|
||||
encrypted_payload = bytes([b ^ xor_key for b in payload_data])
|
||||
|
||||
print(f"🔑 异或信息:")
|
||||
print(f" 异或key: 0x{xor_key:02x} ({xor_key})")
|
||||
|
||||
# 9. 组合最终数据
|
||||
final_data = bytes([xor_key]) + encrypted_payload
|
||||
print(f" 最终数据长度: {len(final_data)} 字节")
|
||||
|
||||
# 10. Base64编码
|
||||
result = base64.b64encode(final_data).decode('utf-8')
|
||||
|
||||
print(f"✅ 生成结果:")
|
||||
print(f" x-statsig-id: {result}")
|
||||
print(f" 长度: {len(result)} 字符")
|
||||
|
||||
return result
|
||||
|
||||
def verify_generated_id(self, statsig_id: str) -> bool:
|
||||
"""
|
||||
验证生成的ID结构是否正确
|
||||
|
||||
Args:
|
||||
statsig_id: 要验证的ID
|
||||
|
||||
Returns:
|
||||
验证是否通过
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🔍 验证生成的ID结构")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# Base64解码
|
||||
decoded_bytes = base64.b64decode(statsig_id)
|
||||
print(f"✅ Base64解码成功,长度: {len(decoded_bytes)} 字节")
|
||||
|
||||
# 提取异或key
|
||||
xor_key = decoded_bytes[0]
|
||||
print(f"✅ 异或key: 0x{xor_key:02x} ({xor_key})")
|
||||
|
||||
# 异或解密
|
||||
decrypted = bytearray()
|
||||
for i in range(1, len(decoded_bytes)):
|
||||
decrypted.append(decoded_bytes[i] ^ xor_key)
|
||||
|
||||
print(f"✅ 解密后长度: {len(decrypted)} 字节")
|
||||
|
||||
# 验证数据结构
|
||||
expected_length = 48 + 4 + 16 + 1 # meta + timestamp + sha256 + fixed
|
||||
if len(decrypted) == expected_length:
|
||||
print(f"✅ 数据长度正确: {len(decrypted)}/{expected_length}")
|
||||
else:
|
||||
print(f"❌ 数据长度错误: {len(decrypted)}/{expected_length}")
|
||||
return False
|
||||
|
||||
# 检查固定值
|
||||
fixed_val = decrypted[-1]
|
||||
if fixed_val == 3:
|
||||
print(f"✅ 固定值正确: {fixed_val}")
|
||||
else:
|
||||
print(f"❌ 固定值错误: {fixed_val} (期望: 3)")
|
||||
return False
|
||||
|
||||
# 解析时间戳
|
||||
timestamp_bytes = decrypted[48:52]
|
||||
timestamp = struct.unpack('<I', timestamp_bytes)[0]
|
||||
actual_time = self.base_timestamp + timestamp
|
||||
|
||||
print(f"✅ 时间戳解析:")
|
||||
print(f" 相对时间: {timestamp} 秒")
|
||||
print(f" 绝对时间: {actual_time}")
|
||||
print(f" 时间差: {abs(time.time() - actual_time):.1f} 秒")
|
||||
|
||||
print("🎉 ID结构验证通过!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 验证失败: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""主函数 - 演示完整流程"""
|
||||
generator = XStatsigIDGenerator()
|
||||
|
||||
# 生成 x-statsig-id
|
||||
method = "GET"
|
||||
pathname = "/"
|
||||
|
||||
statsig_id = generator.generate_x_statsig_id(method, pathname)
|
||||
|
||||
# 验证生成的ID
|
||||
generator.verify_generated_id(statsig_id)
|
||||
|
||||
print(f"\n" + "=" * 60)
|
||||
print("📋 使用说明")
|
||||
print("=" * 60)
|
||||
print("在HTTP请求中使用:")
|
||||
print(f"Headers: {{'x-statsig-id': '{statsig_id}'}}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# From https://github.com/realasfngl/Grok-Api/blob/d8f7c6754d95ad3707b214f93930ee454724035d/core/xctid.py
|
||||
|
||||
from math import floor, copysign, pi, cos, sin
|
||||
from base64 import b64decode, b64encode
|
||||
from re import findall, sub
|
||||
from typing import List, Dict
|
||||
from random import random
|
||||
from hashlib import sha256
|
||||
from struct import pack
|
||||
from time import time
|
||||
|
||||
|
||||
class Signature:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _h(x: float, _param: float, c: float, e: bool):
|
||||
f = ((x * (c - _param)) / 255.0) + _param
|
||||
if e:
|
||||
return floor(f)
|
||||
rounded = round(float(f), 2)
|
||||
if rounded == 0.0:
|
||||
return 0.0
|
||||
return rounded
|
||||
|
||||
@staticmethod
|
||||
def cubicBezierEased(t: float, x1: float, y1: float, x2: float, y2: float) -> float:
|
||||
def bezier(u: float):
|
||||
omu = 1.0 - u
|
||||
b1 = 3.0 * omu * omu * u
|
||||
b2 = 3.0 * omu * u * u
|
||||
b3 = u * u * u
|
||||
x = b1 * x1 + b2 * x2 + b3
|
||||
y = b1 * y1 + b2 * y2 + b3
|
||||
return x, y
|
||||
|
||||
lo, hi = 0.0, 1.0
|
||||
for _ in range(80):
|
||||
mid = 0.5 * (lo + hi)
|
||||
if bezier(mid)[0] < t:
|
||||
lo = mid
|
||||
else:
|
||||
hi = mid
|
||||
u = 0.5 * (lo + hi)
|
||||
return bezier(u)[1]
|
||||
|
||||
@staticmethod
|
||||
def xa(svg: str) -> List[List[int]]:
|
||||
s = (svg)
|
||||
substr = s[9:]
|
||||
parts = substr.split("C")
|
||||
out = []
|
||||
for part in parts:
|
||||
cleaned = sub(r"[^\d]+", " ", part).strip()
|
||||
if cleaned == "":
|
||||
nums = [0]
|
||||
else:
|
||||
nums = [int(tok) for tok in cleaned.split() if tok != ""]
|
||||
out.append(nums)
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def tohex(num: float) -> str:
|
||||
rounded = round(float(num), 2)
|
||||
if rounded == 0.0:
|
||||
return "0"
|
||||
sign = "-" if copysign(1.0, rounded) < 0 else ""
|
||||
absval = abs(rounded)
|
||||
intpart = int(floor(absval))
|
||||
frac = absval - intpart
|
||||
if frac == 0.0:
|
||||
return sign + format(intpart, "x")
|
||||
frac_digits = []
|
||||
f = frac
|
||||
for _ in range(20):
|
||||
f *= 16
|
||||
digit = int(floor(f + 1e-12))
|
||||
frac_digits.append(format(digit, "x"))
|
||||
f -= digit
|
||||
if abs(f) < 1e-12:
|
||||
break
|
||||
frac_str = "".join(frac_digits).rstrip("0")
|
||||
if frac_str == "":
|
||||
return sign + format(intpart, "x")
|
||||
return sign + format(intpart, "x") + "." + frac_str
|
||||
|
||||
@staticmethod
|
||||
def simulateStyle(values: List[int], c: int) -> Dict[str,str]:
|
||||
duration = 4096
|
||||
currentTime = round(c / 10.0) * 10
|
||||
t = currentTime / duration
|
||||
|
||||
cp = [Signature._h(v, -1 if (i % 2) else 0, 1, False) for i, v in enumerate(values[7:])]
|
||||
|
||||
easedY = Signature.cubicBezierEased(t, cp[0], cp[1], cp[2], cp[3])
|
||||
|
||||
start = [float(x) for x in values[0:3]]
|
||||
end = [float(x) for x in values[3:6]]
|
||||
r = round(start[0] + (end[0] - start[0]) * easedY)
|
||||
g = round(start[1] + (end[1] - start[1]) * easedY)
|
||||
b = round(start[2] + (end[2] - start[2]) * easedY)
|
||||
color = f"rgb({r}, {g}, {b})"
|
||||
|
||||
endAngle = Signature._h(values[6], 60, 360, True)
|
||||
angle = endAngle * easedY
|
||||
rad = angle * pi / 180.0
|
||||
|
||||
def is_effectively_zero(val: float) -> bool:
|
||||
return abs(val) < 1e-7
|
||||
|
||||
def is_effectively_integer(val: float) -> bool:
|
||||
return abs(val - round(val)) < 1e-7
|
||||
|
||||
cosv = cos(rad)
|
||||
sinv = sin(rad)
|
||||
|
||||
if is_effectively_zero(cosv):
|
||||
a = 0
|
||||
d = 0
|
||||
else:
|
||||
if is_effectively_integer(cosv):
|
||||
a = int(round(cosv))
|
||||
d = int(round(cosv))
|
||||
else:
|
||||
a = f"{cosv:.6f}"
|
||||
d = f"{cosv:.6f}"
|
||||
|
||||
if is_effectively_zero(sinv):
|
||||
bval = 0
|
||||
cval = 0
|
||||
else:
|
||||
if is_effectively_integer(sinv):
|
||||
bval = int(round(sinv))
|
||||
cval = int(round(-sinv))
|
||||
else:
|
||||
bval = f"{sinv:.7f}"
|
||||
cval = f"{(-sinv):.7f}"
|
||||
|
||||
transform = f"matrix({a}, {bval}, {cval}, {d}, 0, 0)"
|
||||
return {"color": color, "transform": transform}
|
||||
|
||||
@staticmethod
|
||||
def xs(x_bytes: bytes, svg: str, x_values: list) -> str:
|
||||
arr = list(x_bytes)
|
||||
idx = arr[x_values[0]] % 16
|
||||
c = ((arr[x_values[1]] % 16) * (arr[x_values[2]] % 16)) * (arr[x_values[3]] % 16)
|
||||
o = Signature.xa(svg)
|
||||
vals = o[idx]
|
||||
k = Signature.simulateStyle(vals, c)
|
||||
|
||||
concat = str(k["color"]) + str(k["transform"])
|
||||
matches = findall(r"[\d\.\-]+", concat)
|
||||
converted = []
|
||||
for m in matches:
|
||||
num = float(m)
|
||||
hexstr = Signature.tohex(num)
|
||||
converted.append(hexstr)
|
||||
joined = "".join(converted)
|
||||
cleaned = joined.replace(".", "").replace("-", "")
|
||||
return cleaned
|
||||
|
||||
@staticmethod
|
||||
def generate_sign(path: str, method: str, verification: str, svg: str, x_values: list, time_n: int = None, random_float: float = None) -> str:
|
||||
|
||||
n = int(time() - 1682924400) if not time_n else time_n
|
||||
t = pack('<I', n)
|
||||
r = b64decode(verification)
|
||||
o = Signature.xs(r, svg, x_values)
|
||||
|
||||
msg = "!".join([method, path, str(n)]) + "obfiowerehiring" + o
|
||||
digest = sha256(msg.encode('utf-8')).digest()[:16]
|
||||
|
||||
prefix_byte = int(floor(random() if not random_float else random_float * 256))
|
||||
assembled = bytes([prefix_byte]) + r + t + digest + bytes([3])
|
||||
|
||||
arr = bytearray(assembled)
|
||||
if len(arr) > 0:
|
||||
first = arr[0]
|
||||
for i in range(1, len(arr)):
|
||||
arr[i] = arr[i] ^ first
|
||||
|
||||
return b64encode(bytes(arr)).decode('ascii').replace('=', '')
|
||||
|
||||
|
||||
def main():
|
||||
sig = Signature.generate_sign('/rest/app-chat/conversations/new', 'POST', self.verification_token, self.svg_data, self.numbers)
|
||||
print('Signature', sig)
|
||||
pass
|
||||
|
||||
if __name__ == 'main':
|
||||
main()
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
curl 'https://grok.com/rest/app-chat/conversations/new' \
|
||||
-H 'accept: */*' \
|
||||
-H 'accept-language: en-US,en;q=0.9' \
|
||||
-H 'baggage: sentry-environment=production,sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c,sentry-trace_id=a70178dd605614c0d7d802211bb98d58,sentry-org_id=4508179396558848,sentry-sampled=false,sentry-sample_rand=0.6923031198588183,sentry-sample_rate=0' \
|
||||
-H 'cache-control: no-cache' \
|
||||
-H 'content-type: application/json' \
|
||||
-b '_ga=GA1.1.1232202746.1760710013; i18nextLng=en; sso=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzZXNzaW9uX2lkIjoiOGU3MDFiNzctOTdkNC00ZjM0LWExOTctOWFmMDU1MzY3NDAwIn0.-a6x0InxbGzfTVfUlrdzxskxCnvMDI8lC90z4wHeGIk; sso-rw=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzZXNzaW9uX2lkIjoiOGU3MDFiNzctOTdkNC00ZjM0LWExOTctOWFmMDU1MzY3NDAwIn0.-a6x0InxbGzfTVfUlrdzxskxCnvMDI8lC90z4wHeGIk; stblid=b3331fc1-45d7-466b-83df-67427c0b2367; cf_clearance=r8Af0A6SdygBrS_n_CoVmSdnYlDuZYCbuQ7ctUUnzH0-1761153368-1.2.1.1-0OFIiAijcxNPep6Hsj0zGk3OElsfpd66VUxZ0_3kGokIi0VB6QRnwagSG1BVJwlguWf7ij19dieSYMgMML_SwgijuN0GYN3PPGHR0HBzOwBmHhMPs1Z05GS621E9Q3hKkiM7BdeRzFs2ZpTnRVJUEV0OfuNeOvsH0K_GAxQ05LBz4EuQL7_EujHDFNB_KHpC.hg7Z2mMqRKN2JThEv8KmrjIWq9MiJ7uJq2EkkzTdu0; mp_ea93da913ddb66b6372b89d97b1029ac_mixpanel=%7B%22distinct_id%22%3A%2285980643-ffab-4984-a3de-59a608c47d7f%22%2C%22%24device_id%22%3A%2279ce237a-a0f3-4913-bf4b-519ac8a98263%22%2C%22%24initial_referrer%22%3A%22%24direct%22%2C%22%24initial_referring_domain%22%3A%22%24direct%22%2C%22__mps%22%3A%7B%7D%2C%22__mpso%22%3A%7B%7D%2C%22__mpus%22%3A%7B%7D%2C%22__mpa%22%3A%7B%7D%2C%22__mpu%22%3A%7B%7D%2C%22__mpr%22%3A%5B%5D%2C%22__mpap%22%3A%5B%5D%2C%22%24user_id%22%3A%2285980643-ffab-4984-a3de-59a608c47d7f%22%7D; _ga_8FEWB057YH=GS2.1.s1761152442$o14$g1$t1761154051$j49$l0$h0' \
|
||||
-H 'origin: https://grok.com' \
|
||||
-H 'pragma: no-cache' \
|
||||
-H 'priority: u=1, i' \
|
||||
-H 'referer: https://grok.com/imagine/post/5088adf9-85fd-4f51-a514-005aae2e1027' \
|
||||
-H 'sec-ch-ua: "Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"' \
|
||||
-H 'sec-ch-ua-mobile: ?0' \
|
||||
-H 'sec-ch-ua-platform: "macOS"' \
|
||||
-H 'sec-fetch-dest: empty' \
|
||||
-H 'sec-fetch-mode: cors' \
|
||||
-H 'sec-fetch-site: same-origin' \
|
||||
-H 'sentry-trace: a70178dd605614c0d7d802211bb98d58-9b67b382ef8b611d-0' \
|
||||
-H 'traceparent: 00-1e9cf7a2c56a1f16783b613191e7a1a9-e2c806c705dd0856-00' \
|
||||
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' \
|
||||
-H 'x-statsig-id: JWnSxWdSIehXqgWnPtVBmedI/AeX2F7u0mrBWtlqQYItKm6hn0T2jo3+FgysEfOmUrCVjCHzUY/R6BeVN8JyIRKv6Zm8Jg' \
|
||||
-H 'x-xai-request-id: d22009f5-bf35-41b4-834f-123aed8c935e' \
|
||||
--data-raw '{"temporary":true,"modelName":"grok-3","message":"https://assets.grok.com/users/85980643-ffab-4984-a3de-59a608c47d7f/5088adf9-85fd-4f51-a514-005aae2e1027/content --mode=normal","fileAttachments":["5088adf9-85fd-4f51-a514-005aae2e1027"],"toolOverrides":{"videoGen":true}}'
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
curl 'https://grok.com/rest/app-chat/conversations/new' \
|
||||
-H 'accept: */*' \
|
||||
-H 'accept-language: en-US,en;q=0.9' \
|
||||
-H 'baggage: sentry-environment=production,sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c,sentry-trace_id=a70178dd605614c0d7d802211bb98d58,sentry-org_id=4508179396558848,sentry-sampled=false,sentry-sample_rand=0.6923031198588183,sentry-sample_rate=0' \
|
||||
-H 'cache-control: no-cache' \
|
||||
-H 'content-type: application/json' \
|
||||
-b '_ga=GA1.1.1232202746.1760710013; i18nextLng=en; sso=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzZXNzaW9uX2lkIjoiOGU3MDFiNzctOTdkNC00ZjM0LWExOTctOWFmMDU1MzY3NDAwIn0.-a6x0InxbGzfTVfUlrdzxskxCnvMDI8lC90z4wHeGIk; sso-rw=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzZXNzaW9uX2lkIjoiOGU3MDFiNzctOTdkNC00ZjM0LWExOTctOWFmMDU1MzY3NDAwIn0.-a6x0InxbGzfTVfUlrdzxskxCnvMDI8lC90z4wHeGIk; stblid=b3331fc1-45d7-466b-83df-67427c0b2367; cf_clearance=r8Af0A6SdygBrS_n_CoVmSdnYlDuZYCbuQ7ctUUnzH0-1761153368-1.2.1.1-0OFIiAijcxNPep6Hsj0zGk3OElsfpd66VUxZ0_3kGokIi0VB6QRnwagSG1BVJwlguWf7ij19dieSYMgMML_SwgijuN0GYN3PPGHR0HBzOwBmHhMPs1Z05GS621E9Q3hKkiM7BdeRzFs2ZpTnRVJUEV0OfuNeOvsH0K_GAxQ05LBz4EuQL7_EujHDFNB_KHpC.hg7Z2mMqRKN2JThEv8KmrjIWq9MiJ7uJq2EkkzTdu0; mp_ea93da913ddb66b6372b89d97b1029ac_mixpanel=%7B%22distinct_id%22%3A%2285980643-ffab-4984-a3de-59a608c47d7f%22%2C%22%24device_id%22%3A%2279ce237a-a0f3-4913-bf4b-519ac8a98263%22%2C%22%24initial_referrer%22%3A%22%24direct%22%2C%22%24initial_referring_domain%22%3A%22%24direct%22%2C%22__mps%22%3A%7B%7D%2C%22__mpso%22%3A%7B%7D%2C%22__mpus%22%3A%7B%7D%2C%22__mpa%22%3A%7B%7D%2C%22__mpu%22%3A%7B%7D%2C%22__mpr%22%3A%5B%5D%2C%22__mpap%22%3A%5B%5D%2C%22%24user_id%22%3A%2285980643-ffab-4984-a3de-59a608c47d7f%22%7D; _ga_8FEWB057YH=GS2.1.s1761152442$o14$g1$t1761154054$j46$l0$h0' \
|
||||
-H 'origin: https://grok.com' \
|
||||
-H 'pragma: no-cache' \
|
||||
-H 'priority: u=1, i' \
|
||||
-H 'referer: https://grok.com/imagine/post/5088adf9-85fd-4f51-a514-005aae2e1027' \
|
||||
-H 'sec-ch-ua: "Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"' \
|
||||
-H 'sec-ch-ua-mobile: ?0' \
|
||||
-H 'sec-ch-ua-platform: "macOS"' \
|
||||
-H 'sec-fetch-dest: empty' \
|
||||
-H 'sec-fetch-mode: cors' \
|
||||
-H 'sec-fetch-site: same-origin' \
|
||||
-H 'sentry-trace: a70178dd605614c0d7d802211bb98d58-b7262a86b9e17c69-0' \
|
||||
-H 'traceparent: 00-500b380fc103cc5ed0d67c08afcb760f-3468484e94e4b4d3-00' \
|
||||
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' \
|
||||
-H 'x-statsig-id: OXXO2XtOPfRLthm7IsldhftU4BuLxELyznbdRsV2XZ4xNnK9g1jqkpHiChCwDe+6TtmJkD3ZVxtt5raP7qpwux2GVfTCOg' \
|
||||
-H 'x-xai-request-id: 7cb93340-5fb6-4fb3-b88d-ff9f5d99d456' \
|
||||
--data-raw '{"temporary":true,"modelName":"grok-3","message":"https://assets.grok.com/users/85980643-ffab-4984-a3de-59a608c47d7f/5088adf9-85fd-4f51-a514-005aae2e1027/content --mode=normal","fileAttachments":["5088adf9-85fd-4f51-a514-005aae2e1027"],"toolOverrides":{"videoGen":true}}'
|
||||
Reference in new issue
Block a user