diff --git a/app/services/grok/statsig.py b/app/services/grok/statsig.py index abeb212..5e6e240 100644 --- a/app/services/grok/statsig.py +++ b/app/services/grok/statsig.py @@ -1,11 +1,62 @@ """Grok 请求头管理模块""" +import base64 +import random +import string import uuid from typing import Dict +from app.core.logger import logger from app.core.config import setting +def _generate_random_string(length: int, use_letters: bool = True) -> str: + """生成随机字符串 + + Args: + length: 字符串长度 + use_letters: 是否使用字母(True)或数字+字母(False) + + Returns: + 随机字符串 + """ + if use_letters: + # 生成随机字母(小写) + return ''.join(random.choices(string.ascii_lowercase, k=length)) + else: + # 生成随机数字和字母组合(小写) + return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) + + +def _generate_statsig_id() -> str: + """动态生成 x-statsig-id + + 随机选择两种格式之一: + 1. e:TypeError: Cannot read properties of null (reading 'children['xxxxx']') + 其中 xxxxx 是5位随机字符串 + 2. e:TypeError: Cannot read properties of undefined (reading 'xxxxxxxxxx') + 其中 xxxxxxxxxx 是10位随机字母 + + Returns: + base64 编码后的字符串 + """ + # 随机选择一种格式 + format_type = random.choice([1, 2]) + + if format_type == 1: + # 格式1: children['xxxxx'] + random_str = _generate_random_string(5, use_letters=False) + error_msg = f"e:TypeError: Cannot read properties of null (reading 'children['{random_str}']')" + else: + # 格式2: 'xxxxxxxxxx' + random_str = _generate_random_string(10, use_letters=True) + error_msg = f"e:TypeError: Cannot read properties of undefined (reading '{random_str}')" + + # base64 编码 + encoded = base64.b64encode(error_msg.encode('utf-8')).decode('utf-8') + return encoded + + def get_dynamic_headers(pathname: str = "/rest/app-chat/conversations/new") -> Dict[str, str]: """获取请求头 @@ -15,10 +66,19 @@ def get_dynamic_headers(pathname: str = "/rest/app-chat/conversations/new") -> D Returns: 请求头字典 """ - # 获取配置的 x-statsig-id - statsig_id = setting.grok_config.get("x_statsig_id") - if not statsig_id: - raise ValueError("配置文件中未设置 x_statsig_id") + # 检查是否启用动态生成 + dynamic_statsig = setting.grok_config.get("dynamic_statsig", False) + + if dynamic_statsig: + # 动态生成 x-statsig-id + statsig_id = _generate_statsig_id() + logger.debug(f"[Statsig] 动态生成值 {statsig_id}") + else: + # 使用配置文件中的固定值 + statsig_id = setting.grok_config.get("x_statsig_id") + logger.debug(f"[Statsig] 使用固定值 {statsig_id}") + if not statsig_id: + raise ValueError("配置文件中未设置 x_statsig_id") # 构建基础请求头 headers = { diff --git a/app/template/admin.html b/app/template/admin.html index c6dd38c..34e4380 100644 --- a/app/template/admin.html +++ b/app/template/admin.html @@ -20,54 +20,17 @@ .hover-card-content.top::after{top:100%;border-top-color:hsl(0 0% 3.9%)} .hover-card-content.bottom::after{bottom:100%;border-bottom-color:hsl(0 0% 3.9%)} .hover-card-trigger:hover+.hover-card-content{opacity:1;visibility:visible} + .btn-icon{display:inline-flex;align-items:center;justify-content:center;border-radius:0.375rem;transition:color .2s,background-color .2s;height:2rem;width:2rem} + .btn-icon:focus-visible{outline:2px solid transparent;outline-offset:2px;box-shadow:0 0 0 1px hsl(0 0% 3.9%)} + .btn-icon:hover{background-color:hsl(0 0% 96.1%);color:hsl(0 0% 9%)} + .cfg-label{font-size:0.875rem;font-weight:500;color:hsl(0 0% 45.1%);margin-bottom:0.5rem;display:flex;align-items:center;gap:0.25rem} + .cfg-input{display:flex;height:2.25rem;width:100%;border-radius:0.375rem;border:1px solid hsl(0 0% 89%);background-color:hsl(0 0% 100%);padding:0.5rem 0.75rem;font-size:0.875rem} + .help-icon{display:inline-flex;align-items:center;justify-content:center;width:0.875rem;height:0.875rem;border-radius:9999px;border:1px solid hsl(0 0% 45.1%);color:hsl(0 0% 45.1%);cursor:help;font-size:10px;line-height:1} [title]{position:relative} - [title]:hover::after{ - content:attr(title); - position:absolute; - bottom:100%; - left:50%; - transform:translateX(-50%) translateY(-8px); - background:hsl(0 0% 11%); - color:hsl(0 0% 98%); - padding:8px 12px; - border-radius:6px; - font-size:12px; - font-weight:500; - line-height:1.4; - white-space:pre-line; - max-width:350px; - width:max-content; - word-wrap:break-word; - z-index:1000; - pointer-events:none; - box-shadow:0 8px 20px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.1); - opacity:0; - visibility:hidden; - transition:all 0.2s ease; - animation:tooltipFadeIn 0.2s ease forwards; - } - [title]:hover::before{ - content:''; - position:absolute; - bottom:100%; - left:50%; - transform:translateX(-50%) translateY(-4px); - border:6px solid transparent; - border-top-color:hsl(0 0% 11%); - z-index:1000; - opacity:0; - visibility:hidden; - transition:opacity 0.2s ease, visibility 0.2s ease; - } - [title]:hover::after, - [title]:hover::before{ - opacity:1; - visibility:visible; - } - @keyframes tooltipFadeIn{ - from{opacity:0;transform:translateX(-50%) translateY(-4px)} - to{opacity:1;transform:translateX(-50%) translateY(-8px)} - } + [title]:hover::after{content:attr(title);position:absolute;bottom:100%;left:50%;transform:translateX(-50%) translateY(-8px);background:hsl(0 0% 11%);color:hsl(0 0% 98%);padding:8px 12px;border-radius:6px;font-size:12px;font-weight:500;line-height:1.4;white-space:pre-line;max-width:350px;width:max-content;word-wrap:break-word;z-index:1000;pointer-events:none;box-shadow:0 8px 20px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.1);opacity:0;visibility:hidden;transition:all 0.2s ease;animation:tooltipFadeIn 0.2s ease forwards} + [title]:hover::before{content:'';position:absolute;bottom:100%;left:50%;transform:translateX(-50%) translateY(-4px);border:6px solid transparent;border-top-color:hsl(0 0% 11%);z-index:1000;opacity:0;visibility:hidden;transition:opacity 0.2s ease, visibility 0.2s ease} + [title]:hover::after,[title]:hover::before{opacity:1;visibility:visible} + @keyframes tooltipFadeIn{from{opacity:0;transform:translateX(-50%) translateY(-4px)}to{opacity:1;transform:translateX(-50%) translateY(-8px)}} \ No newline at end of file