diff --git a/app/services/grok/services/assets.py b/app/services/grok/services/assets.py
index 59f041b..15791f4 100644
--- a/app/services/grok/services/assets.py
+++ b/app/services/grok/services/assets.py
@@ -37,7 +37,8 @@ DOWNLOAD_API = "https://assets.grok.com"
LOCK_DIR = Path(__file__).parent.parent.parent.parent.parent / "data" / ".locks"
TIMEOUT = 120
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
+DEFAULT_USER_AGENT = "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"
DEFAULT_MIME = "application/octet-stream"
# 并发控制
@@ -179,6 +180,7 @@ class BaseService:
def _headers(self, token: str, referer: str = "https://grok.com/") -> dict:
"""构建请求头"""
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
@@ -199,7 +201,7 @@ class BaseService:
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
- "User-Agent": "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",
+ "User-Agent": user_agent,
}
apply_statsig(headers)
@@ -354,7 +356,7 @@ class UploadService(BaseService):
UPLOAD_API,
headers=headers,
json=payload,
- impersonate=BROWSER,
+ impersonate=get_config("grok.browser", DEFAULT_BROWSER),
timeout=self.timeout,
proxies=self._proxies(),
)
@@ -448,7 +450,7 @@ class ListService(BaseService):
LIST_API,
headers=headers,
params=params,
- impersonate=BROWSER,
+ impersonate=get_config("grok.browser", DEFAULT_BROWSER),
timeout=self.timeout,
proxies=self._proxies(),
)
@@ -527,7 +529,7 @@ class DeleteService(BaseService):
response = await session.delete(
url,
headers=headers,
- impersonate=BROWSER,
+ impersonate=get_config("grok.browser", DEFAULT_BROWSER),
timeout=self.timeout,
proxies=self._proxies(),
)
@@ -663,7 +665,7 @@ class DownloadService(BaseService):
proxies=self._proxies(),
timeout=self.timeout,
allow_redirects=True,
- impersonate=BROWSER,
+ impersonate=get_config("grok.browser", DEFAULT_BROWSER),
stream=True,
)
diff --git a/app/services/grok/services/chat.py b/app/services/grok/services/chat.py
index 1609f89..237cd38 100644
--- a/app/services/grok/services/chat.py
+++ b/app/services/grok/services/chat.py
@@ -26,7 +26,8 @@ from app.services.token import get_token_manager, EffortType
CHAT_API = "https://grok.com/rest/app-chat/conversations/new"
TIMEOUT = 120
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
+DEFAULT_USER_AGENT = "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"
@dataclass
@@ -162,6 +163,7 @@ class ChatRequestBuilder:
@staticmethod
def build_headers(token: str) -> Dict[str, str]:
"""构造请求头"""
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
@@ -182,7 +184,7 @@ class ChatRequestBuilder:
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
- "User-Agent": "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",
+ "User-Agent": user_agent,
}
apply_statsig(headers)
@@ -321,7 +323,8 @@ class GrokChatService:
# 建立连接函数
async def establish_connection():
"""建立连接并返回 response 对象"""
- session = AsyncSession(impersonate=BROWSER)
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
+ session = AsyncSession(impersonate=browser)
try:
response = await session.post(
CHAT_API,
diff --git a/app/services/grok/services/media.py b/app/services/grok/services/media.py
index da4f1af..7b0eab7 100644
--- a/app/services/grok/services/media.py
+++ b/app/services/grok/services/media.py
@@ -26,7 +26,8 @@ CREATE_POST_API = "https://grok.com/rest/media/post/create"
CHAT_API = "https://grok.com/rest/app-chat/conversations/new"
# 常量
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
+DEFAULT_USER_AGENT = "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"
TIMEOUT = 300
DEFAULT_MAX_CONCURRENT = 50
_MEDIA_SEMAPHORE = asyncio.Semaphore(DEFAULT_MAX_CONCURRENT)
@@ -58,6 +59,7 @@ class VideoService:
self, token: str, referer: str = "https://grok.com/imagine"
) -> dict:
"""构建请求头"""
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
@@ -78,7 +80,7 @@ class VideoService:
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
- "User-Agent": "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",
+ "User-Agent": user_agent,
}
apply_statsig(headers)
@@ -119,11 +121,12 @@ class VideoService:
payload = {"mediaType": media_type, "prompt": prompt}
async with AsyncSession() as session:
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
response = await session.post(
CREATE_POST_API,
headers=headers,
json=payload,
- impersonate=BROWSER,
+ impersonate=browser,
timeout=30,
proxies=self._build_proxies(),
)
@@ -243,7 +246,8 @@ class VideoService:
prompt, post_id, aspect_ratio, video_length, resolution_name, preset
)
- session = AsyncSession(impersonate=BROWSER)
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
+ session = AsyncSession(impersonate=browser)
response = await session.post(
CHAT_API,
headers=headers,
@@ -323,7 +327,8 @@ class VideoService:
prompt, post_id, aspect_ratio, video_length, resolution, preset
)
- session = AsyncSession(impersonate=BROWSER)
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
+ session = AsyncSession(impersonate=browser)
response = await session.post(
CHAT_API,
headers=headers,
diff --git a/app/services/grok/services/nsfw.py b/app/services/grok/services/nsfw.py
index 2041ec1..be797a4 100644
--- a/app/services/grok/services/nsfw.py
+++ b/app/services/grok/services/nsfw.py
@@ -23,7 +23,8 @@ from app.services.grok.utils.headers import build_sso_cookie
NSFW_API = "https://grok.com/auth_mgmt.AuthManagement/UpdateUserFeatureControls"
BIRTH_DATE_API = "https://grok.com/rest/auth/set-birth-date"
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
+DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
TIMEOUT = 30
@@ -61,12 +62,13 @@ class NSFWService:
def _build_headers(self, token: str) -> dict:
"""构造 gRPC-Web 请求头"""
cookie = build_sso_cookie(token, include_rw=True)
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
return {
"accept": "*/*",
"content-type": "application/grpc-web+proto",
"origin": "https://grok.com",
"referer": "https://grok.com/",
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
+ "user-agent": user_agent,
"x-grpc-web": "1",
"x-user-agent": "connect-es/2.1.1",
"cookie": cookie,
@@ -75,12 +77,13 @@ class NSFWService:
def _build_birth_headers(self, token: str) -> dict:
"""构造设置出生日期请求头"""
cookie = build_sso_cookie(token, include_rw=True)
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
return {
"accept": "*/*",
"content-type": "application/json",
"origin": "https://grok.com",
"referer": "https://grok.com/?_s=account",
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
+ "user-agent": user_agent,
"cookie": cookie,
}
@@ -127,7 +130,8 @@ class NSFWService:
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
- async with AsyncSession(impersonate=BROWSER) as session:
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
+ async with AsyncSession(impersonate=browser) as session:
ok, birth_status, birth_err = await self._set_birth_date(
session, token
)
diff --git a/app/services/grok/services/usage.py b/app/services/grok/services/usage.py
index e6b7c57..a339962 100644
--- a/app/services/grok/services/usage.py
+++ b/app/services/grok/services/usage.py
@@ -15,7 +15,8 @@ from app.services.grok.utils.retry import retry_on_status
LIMITS_API = "https://grok.com/rest/rate-limits"
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
+DEFAULT_USER_AGENT = "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"
TIMEOUT = 10
DEFAULT_MAX_CONCURRENT = 25
_USAGE_SEMAPHORE = asyncio.Semaphore(DEFAULT_MAX_CONCURRENT)
@@ -45,6 +46,7 @@ class UsageService:
def _build_headers(self, token: str) -> dict:
"""构建请求头"""
+ user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
@@ -65,7 +67,7 @@ class UsageService:
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
- "User-Agent": "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",
+ "User-Agent": user_agent,
}
apply_statsig(headers)
@@ -103,13 +105,14 @@ class UsageService:
try:
headers = self._build_headers(token)
payload = {"requestKind": "DEFAULT", "modelName": model_name}
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
async with AsyncSession() as session:
response = await session.post(
LIMITS_API,
headers=headers,
json=payload,
- impersonate=BROWSER,
+ impersonate=browser,
timeout=self.timeout,
proxies=self._build_proxies(),
)
diff --git a/app/services/grok/services/voice.py b/app/services/grok/services/voice.py
index 9e03880..8b87991 100644
--- a/app/services/grok/services/voice.py
+++ b/app/services/grok/services/voice.py
@@ -15,7 +15,7 @@ from app.services.grok.utils.headers import apply_statsig, build_sso_cookie
LIVEKIT_TOKEN_API = "https://grok.com/rest/livekit/tokens"
TIMEOUT = 30
-BROWSER = "chrome136"
+DEFAULT_BROWSER = "chrome136"
class VoiceService:
@@ -45,7 +45,8 @@ class VoiceService:
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
try:
- async with AsyncSession(impersonate=BROWSER) as session:
+ browser = get_config("grok.browser", DEFAULT_BROWSER)
+ async with AsyncSession(impersonate=browser) as session:
response = await session.post(
LIVEKIT_TOKEN_API,
headers=headers,
diff --git a/app/static/config/config.html b/app/static/config/config.html
index 6d70398..7fb207f 100644
--- a/app/static/config/config.html
+++ b/app/static/config/config.html
@@ -50,7 +50,7 @@
-
+