sanbo commited on
Commit
d1d4670
·
1 Parent(s): eb6a925

update sth at 2025-10-10 00:55:40

Browse files
Files changed (1) hide show
  1. main.go +145 -73
main.go CHANGED
@@ -3,10 +3,13 @@ package main
3
  import (
4
  "bufio"
5
  "bytes"
 
6
  "crypto/sha256"
 
7
  "encoding/json"
8
  "fmt"
9
  "io"
 
10
  "log"
11
  "net/http"
12
  "net/url"
@@ -15,6 +18,8 @@ import (
15
  "strings"
16
  "sync"
17
  "time"
 
 
18
  )
19
 
20
  // 配置变量(从环境变量读取)
@@ -60,14 +65,14 @@ var (
60
 
61
  // 思考内容处理策略
62
  const (
63
- THINK_TAGS_MODE = "think" // strip: 去除<details>标签;think: 转为<think>标签;raw: 保留原样
64
  )
65
 
66
  // 系统配置常量
67
  const (
68
- MAX_LIVE_REQUESTS = 400 // 最多保留的实时请求记录数
69
- AUTH_TOKEN_TIMEOUT = 30 // 获取匿名token的超时时间(秒)
70
- UPSTREAM_TIMEOUT = 100 // 上游API调用超时时间(秒)
71
  TOKEN_DISPLAY_LENGTH = 10 // token显示时的截取长度
72
  NANOSECONDS_TO_SECONDS = 1000000000 // 纳秒转秒的倍数
73
  )
@@ -106,7 +111,7 @@ func initConfig() {
106
  DEBUG_MODE = getEnv("DEBUG_MODE", "true") == "true"
107
  DEFAULT_STREAM = getEnv("DEFAULT_STREAM", "true") == "true"
108
  DASHBOARD_ENABLED = getEnv("DASHBOARD_ENABLED", "true") == "true"
109
- ENABLE_THINKING = getEnv("ENABLE_THINKING", "false") == "true"
110
  }
111
 
112
  // 记录请求统计信息
@@ -381,6 +386,14 @@ func getUpstreamModelID(modelName string) string {
381
  switch modelName {
382
  case "GLM-4.6":
383
  return "GLM-4-6-API-V1" // 使用官方API的真实模型名称
 
 
 
 
 
 
 
 
384
  default:
385
  debugLog("未知模型名称: %s,使用GLM-4.6作为默认", modelName)
386
  return "GLM-4-6-API-V1" // 默认使用GLM-4.6
@@ -389,39 +402,50 @@ func getUpstreamModelID(modelName string) string {
389
 
390
  // 获取匿名token(每次对话使用不同token,避免共享记忆)
391
  func getAnonymousToken() (string, error) {
 
 
 
392
  client := &http.Client{Timeout: AUTH_TOKEN_TIMEOUT * time.Second}
393
- req, err := http.NewRequest("GET", ORIGIN_BASE+"/api/v1/auths/", nil)
394
  if err != nil {
 
395
  return "", err
396
  }
397
- // 伪装浏览器头
398
- req.Header.Set("User-Agent", BROWSER_UA)
399
  req.Header.Set("Accept", "*/*")
400
  req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
401
- req.Header.Set("X-FE-Version", X_FE_VERSION)
402
- req.Header.Set("sec-ch-ua", SEC_CH_UA)
403
- req.Header.Set("sec-ch-ua-mobile", SEC_CH_UA_MOB)
404
- req.Header.Set("sec-ch-ua-platform", SEC_CH_UA_PLAT)
405
- req.Header.Set("Origin", ORIGIN_BASE)
406
  req.Header.Set("Referer", ORIGIN_BASE+"/")
407
 
408
  resp, err := client.Do(req)
409
  if err != nil {
 
410
  return "", err
411
  }
412
  defer resp.Body.Close()
 
 
 
413
  if resp.StatusCode != http.StatusOK {
414
- return "", fmt.Errorf("anon token status=%d", resp.StatusCode)
 
415
  }
 
416
  var body struct {
417
  Token string `json:"token"`
418
  }
419
  if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
 
 
420
  return "", err
421
  }
 
422
  if body.Token == "" {
 
423
  return "", fmt.Errorf("anon token empty")
424
  }
 
 
425
  return body.Token, nil
426
  }
427
 
@@ -1484,21 +1508,16 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
1484
  chatID := fmt.Sprintf("%d-%d", time.Now().UnixNano(), time.Now().Unix())
1485
  msgID := fmt.Sprintf("%d", time.Now().UnixNano())
1486
 
1487
- // 决定是否启用思考功能:优先使用请求参数,其次使用环境变量
1488
- enableThinking := ENABLE_THINKING // 默认使用环境变量值
1489
- if req.EnableThinking != nil {
1490
- enableThinking = *req.EnableThinking
1491
- debugLog("使用请求参数中的思考功能设置: %v", enableThinking)
1492
- } else {
1493
- debugLog("使用环境变量中的思考功能设置: %v", enableThinking)
1494
- }
1495
 
1496
  // 构造上游请求
1497
  upstreamReq := UpstreamRequest{
1498
  Stream: true, // 总是使用流式从上游获取
1499
  ChatID: chatID,
1500
  ID: msgID,
1501
- Model: getUpstreamModelID(MODEL_NAME), // 根据模型名称获取上游实际模型ID
1502
  Messages: req.Messages,
1503
  Params: map[string]interface{}{},
1504
  Features: map[string]interface{}{
@@ -1513,7 +1532,7 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
1513
  ID string `json:"id"`
1514
  Name string `json:"name"`
1515
  OwnedBy string `json:"owned_by"`
1516
- }{ID: getUpstreamModelID(MODEL_NAME), Name: MODEL_NAME, OwnedBy: "openai"},
1517
  ToolServers: []string{},
1518
  Variables: map[string]string{
1519
  "{{USER_NAME}}": "User",
@@ -1553,6 +1572,73 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
1553
  }
1554
  }
1555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1556
  func callUpstreamWithHeaders(upstreamReq UpstreamRequest, refererChatID string, authToken string) (*http.Response, error) {
1557
  reqBody, err := json.Marshal(upstreamReq)
1558
  if err != nil {
@@ -1562,36 +1648,37 @@ func callUpstreamWithHeaders(upstreamReq UpstreamRequest, refererChatID string,
1562
 
1563
  // 构建带URL参数的完整URL
1564
  baseURL := UPSTREAM_URL
1565
- timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
1566
-
1567
- // 生成UUID (简化版,使用crypto/rand会更好)
1568
- requestID := fmt.Sprintf("%x-%x-%x-%x-%x",
1569
- time.Now().UnixNano(), time.Now().Unix(),
1570
- time.Now().Nanosecond(), time.Now().Second(), time.Now().Minute())
1571
- userID := fmt.Sprintf("%x-%x-%x-%x-%x",
1572
- time.Now().Unix(), time.Now().Nanosecond(),
1573
- time.Now().Second(), time.Now().Minute(), time.Now().Hour())
1574
-
1575
- // 构建URL参数 - 添加所有必要的指纹参数
1576
- fullURL := fmt.Sprintf("%s?timestamp=%s&requestId=%s&user_id=%s&version=0.0.1&platform=web&token=%s"+
1577
- "&user_agent=%s&language=zh-CN&languages=zh-CN,zh&timezone=Asia/Shanghai"+
1578
- "&cookie_enabled=true&screen_width=1680&screen_height=1050&screen_resolution=1680x1050"+
1579
- "&viewport_height=812&viewport_width=1087&viewport_size=1087x812"+
1580
- "&color_depth=30&pixel_ratio=2"+
1581
- "&current_url=%s&pathname=/c/%s&search=&hash="+
1582
- "&host=chat.z.ai&hostname=chat.z.ai&protocol=https:&referrer="+
1583
- "&title=%s"+
1584
- "&timezone_offset=-480&local_time=%s&utc_time=%s"+
1585
- "&is_mobile=false&is_touch=false&max_touch_points=0"+
1586
- "&browser_name=Chrome&os_name=Mac+OS&signature_timestamp=%s",
1587
- baseURL, timestamp, requestID, userID, authToken,
1588
- url.QueryEscape(BROWSER_UA),
1589
- url.QueryEscape(ORIGIN_BASE+"/c/"+refererChatID), refererChatID,
1590
- url.QueryEscape("Z.ai Chat - Free AI powered by GLM-4.6"),
1591
- url.QueryEscape(time.Now().Format("2006-01-02T15:04:05.000Z")),
1592
- url.QueryEscape(time.Now().UTC().Format(time.RFC1123)),
1593
- timestamp,
1594
- )
 
1595
 
1596
  debugLog("调用上游API: %s", fullURL)
1597
  debugLog("上游请求体: %s", string(reqBody))
@@ -1602,31 +1689,16 @@ func callUpstreamWithHeaders(upstreamReq UpstreamRequest, refererChatID string,
1602
  return nil, err
1603
  }
1604
 
1605
- // 生成 X-Signature - 基于请求体的 SHA-256 哈希(426错误修复)
1606
- hash := sha256.Sum256(reqBody)
1607
- signature := fmt.Sprintf("%x", hash)
1608
-
1609
- debugLog("生成签名: %s (基于请求体SHA256)", signature)
1610
-
1611
  req.Header.Set("Content-Type", "application/json")
1612
- req.Header.Set("Accept", "*/*")
1613
- req.Header.Set("Accept-Language", "zh-CN")
1614
  req.Header.Set("User-Agent", BROWSER_UA)
 
1615
  req.Header.Set("Authorization", "Bearer "+authToken)
1616
- req.Header.Set("sec-ch-ua", SEC_CH_UA)
1617
- req.Header.Set("sec-ch-ua-mobile", SEC_CH_UA_MOB)
1618
- req.Header.Set("sec-ch-ua-platform", SEC_CH_UA_PLAT)
1619
- req.Header.Set("X-FE-Version", X_FE_VERSION)
1620
  req.Header.Set("X-Signature", signature)
 
1621
  req.Header.Set("Origin", ORIGIN_BASE)
1622
  req.Header.Set("Referer", ORIGIN_BASE+"/c/"+refererChatID)
1623
- req.Header.Set("Connection", "keep-alive")
1624
- req.Header.Set("Sec-Fetch-Dest", "empty")
1625
- req.Header.Set("Sec-Fetch-Mode", "cors")
1626
- req.Header.Set("Sec-Fetch-Site", "same-origin")
1627
-
1628
- // 添加Cookie
1629
- req.Header.Set("Cookie", fmt.Sprintf("token=%s", authToken))
1630
 
1631
  client := &http.Client{Timeout: UPSTREAM_TIMEOUT * time.Second}
1632
  resp, err := client.Do(req)
 
3
  import (
4
  "bufio"
5
  "bytes"
6
+ "crypto/hmac"
7
  "crypto/sha256"
8
+ "encoding/base64"
9
  "encoding/json"
10
  "fmt"
11
  "io"
12
+ "io/ioutil"
13
  "log"
14
  "net/http"
15
  "net/url"
 
18
  "strings"
19
  "sync"
20
  "time"
21
+
22
+ "github.com/google/uuid"
23
  )
24
 
25
  // 配置变量(从环境变量读取)
 
65
 
66
  // 思考内容处理策略
67
  const (
68
+ THINK_TAGS_MODE = "strip" // strip: 去除<details>标签;think: 转为<think>标签;raw: 保留原样
69
  )
70
 
71
  // 系统配置常量
72
  const (
73
+ MAX_LIVE_REQUESTS = 100 // 最多保留的实时请求记录数
74
+ AUTH_TOKEN_TIMEOUT = 10 // 获取匿名token的超时时间(秒)
75
+ UPSTREAM_TIMEOUT = 60 // 上游API调用超时时间(秒)
76
  TOKEN_DISPLAY_LENGTH = 10 // token显示时的截取长度
77
  NANOSECONDS_TO_SECONDS = 1000000000 // 纳秒转秒的倍数
78
  )
 
111
  DEBUG_MODE = getEnv("DEBUG_MODE", "true") == "true"
112
  DEFAULT_STREAM = getEnv("DEFAULT_STREAM", "true") == "true"
113
  DASHBOARD_ENABLED = getEnv("DASHBOARD_ENABLED", "true") == "true"
114
+ ENABLE_THINKING = getEnv("ENABLE_THINKING", "true") == "true"
115
  }
116
 
117
  // 记录请求统计信息
 
386
  switch modelName {
387
  case "GLM-4.6":
388
  return "GLM-4-6-API-V1" // 使用官方API的真实模型名称
389
+ case "GLM-4.5":
390
+ return "0727-360B-API"
391
+ case "GLM-4.5-Thinking":
392
+ return "0727-360B-API"
393
+ case "GLM-4.5-Search":
394
+ return "0727-360B-API"
395
+ case "GLM-4.6-Thinking":
396
+ return "GLM-4-6-API-V1"
397
  default:
398
  debugLog("未知模型名称: %s,使用GLM-4.6作为默认", modelName)
399
  return "GLM-4-6-API-V1" // 默认使用GLM-4.6
 
402
 
403
  // 获取匿名token(每次对话使用不同token,避免共享记忆)
404
  func getAnonymousToken() (string, error) {
405
+ tokenURL := ORIGIN_BASE + "/api/v1/auths/"
406
+ debugLog("获取匿名token: %s", tokenURL)
407
+
408
  client := &http.Client{Timeout: AUTH_TOKEN_TIMEOUT * time.Second}
409
+ req, err := http.NewRequest("GET", tokenURL, nil)
410
  if err != nil {
411
+ debugLog("创建获取匿名token请求失败: %v", err)
412
  return "", err
413
  }
414
+
 
415
  req.Header.Set("Accept", "*/*")
416
  req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
417
+ req.Header.Set("User-Agent", BROWSER_UA)
 
 
 
 
418
  req.Header.Set("Referer", ORIGIN_BASE+"/")
419
 
420
  resp, err := client.Do(req)
421
  if err != nil {
422
+ debugLog("获取匿名token请求失败: %v", err)
423
  return "", err
424
  }
425
  defer resp.Body.Close()
426
+
427
+ debugLog("获取匿名token响应状态: %d", resp.StatusCode)
428
+
429
  if resp.StatusCode != http.StatusOK {
430
+ debugLog("获取匿名token失败: 状态码 %d", resp.StatusCode)
431
+ return "", fmt.Errorf("获取匿名token失败: 状态码 %d", resp.StatusCode)
432
  }
433
+
434
  var body struct {
435
  Token string `json:"token"`
436
  }
437
  if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
438
+ bodyContent, _ := ioutil.ReadAll(resp.Body)
439
+ debugLog("解析匿名token响应失败: %v, 响应内容: %s", err, string(bodyContent))
440
  return "", err
441
  }
442
+
443
  if body.Token == "" {
444
+ debugLog("匿名token为空")
445
  return "", fmt.Errorf("anon token empty")
446
  }
447
+
448
+ debugLog("成功获取匿名token")
449
  return body.Token, nil
450
  }
451
 
 
1508
  chatID := fmt.Sprintf("%d-%d", time.Now().UnixNano(), time.Now().Unix())
1509
  msgID := fmt.Sprintf("%d", time.Now().UnixNano())
1510
 
1511
+ // 决定是否启用思考功能:根据模型名称判断
1512
+ enableThinking := strings.Contains(strings.ToLower(req.Model), "thinking")
1513
+ debugLog("根据模型名称启用思考功能: %v (模型: %s)", enableThinking, req.Model)
 
 
 
 
 
1514
 
1515
  // 构造上游请求
1516
  upstreamReq := UpstreamRequest{
1517
  Stream: true, // 总是使用流式从上游获取
1518
  ChatID: chatID,
1519
  ID: msgID,
1520
+ Model: getUpstreamModelID(req.Model), // 使用用户请求中的模型名称
1521
  Messages: req.Messages,
1522
  Params: map[string]interface{}{},
1523
  Features: map[string]interface{}{
 
1532
  ID string `json:"id"`
1533
  Name string `json:"name"`
1534
  OwnedBy string `json:"owned_by"`
1535
+ }{ID: getUpstreamModelID(req.Model), Name: req.Model, OwnedBy: "openai"},
1536
  ToolServers: []string{},
1537
  Variables: map[string]string{
1538
  "{{USER_NAME}}": "User",
 
1572
  }
1573
  }
1574
 
1575
+ // 从JWT token中提取user_id
1576
+ func extractUserIDFromToken(token string) string {
1577
+ parts := strings.Split(token, ".")
1578
+ if len(parts) < 2 {
1579
+ return "guest"
1580
+ }
1581
+
1582
+ // Base64解码payload部分
1583
+ payloadRaw := parts[1]
1584
+ // 添加缺失的padding
1585
+ padding := "="
1586
+ numPadding := (-len(payloadRaw)) % 4
1587
+ if numPadding > 0 {
1588
+ padding = strings.Repeat("=", numPadding)
1589
+ }
1590
+
1591
+ payloadBytes, err := base64.RawURLEncoding.DecodeString(payloadRaw + padding)
1592
+ if err != nil {
1593
+ debugLog("JWT payload解码失败: %v", err)
1594
+ return "guest"
1595
+ }
1596
+
1597
+ // 解析JSON payload
1598
+ var payload map[string]interface{}
1599
+ err = json.Unmarshal(payloadBytes, &payload)
1600
+ if err != nil {
1601
+ debugLog("JWT payload解析失败: %v", err)
1602
+ return "guest"
1603
+ }
1604
+
1605
+ // 尝试多个可能的user_id字段
1606
+ userIDKeys := []string{"id", "user_id", "uid", "sub"}
1607
+ for _, key := range userIDKeys {
1608
+ if val, exists := payload[key]; exists && val != nil {
1609
+ // 将值转换为字符串
1610
+ return fmt.Sprintf("%v", val)
1611
+ }
1612
+ }
1613
+
1614
+ return "guest"
1615
+ }
1616
+
1617
+ // 生成双层HMAC-SHA256签名
1618
+ func generateSignature(messageText string, requestID string, timestampMs int64, userID string) string {
1619
+ signingSecret := "junjie" // Z.AI的默认签名密钥
1620
+
1621
+ // 计算时间窗口索引(5分钟窗口)
1622
+ windowIndex := timestampMs / (5 * 60 * 1000)
1623
+
1624
+ // Layer1: 派生密钥
1625
+ rootKey := []byte(signingSecret)
1626
+ h := hmac.New(sha256.New, rootKey)
1627
+ h.Write([]byte(fmt.Sprintf("%d", windowIndex)))
1628
+ derivedHex := fmt.Sprintf("%x", h.Sum(nil))
1629
+
1630
+ // Layer2: 生成签名
1631
+ canonicalString := fmt.Sprintf(
1632
+ "requestId,%s,timestamp,%d,user_id,%s|%s|%d",
1633
+ requestID, timestampMs, userID, messageText, timestampMs,
1634
+ )
1635
+ h2 := hmac.New(sha256.New, []byte(derivedHex))
1636
+ h2.Write([]byte(canonicalString))
1637
+ signature := fmt.Sprintf("%x", h2.Sum(nil))
1638
+
1639
+ return signature
1640
+ }
1641
+
1642
  func callUpstreamWithHeaders(upstreamReq UpstreamRequest, refererChatID string, authToken string) (*http.Response, error) {
1643
  reqBody, err := json.Marshal(upstreamReq)
1644
  if err != nil {
 
1648
 
1649
  // 构建带URL参数的完整URL
1650
  baseURL := UPSTREAM_URL
1651
+ timestampMs := time.Now().UnixMilli()
1652
+
1653
+ // 生成UUID
1654
+ requestID := uuid.New().String()
1655
+ userID := extractUserIDFromToken(authToken)
1656
+
1657
+ // 提取最后一条用户消息用于签名
1658
+ lastUserMessage := ""
1659
+ if len(upstreamReq.Messages) > 0 {
1660
+ for i := len(upstreamReq.Messages) - 1; i >= 0; i-- {
1661
+ if upstreamReq.Messages[i].Role == "user" {
1662
+ lastUserMessage = upstreamReq.Messages[i].Content
1663
+ break
1664
+ }
1665
+ }
1666
+ }
1667
+
1668
+ // 生成签名
1669
+ signature := generateSignature(lastUserMessage, requestID, timestampMs, userID)
1670
+
1671
+ // 构建URL参数
1672
+ queryParams := url.Values{}
1673
+ queryParams.Set("timestamp", fmt.Sprintf("%d", timestampMs))
1674
+ queryParams.Set("requestId", requestID)
1675
+ queryParams.Set("user_id", userID)
1676
+ queryParams.Set("token", authToken)
1677
+ queryParams.Set("current_url", ORIGIN_BASE+"/c/"+refererChatID)
1678
+ queryParams.Set("pathname", fmt.Sprintf("/c/%s", refererChatID))
1679
+ queryParams.Set("signature_timestamp", fmt.Sprintf("%d", timestampMs))
1680
+
1681
+ fullURL := fmt.Sprintf("%s?%s", baseURL, queryParams.Encode())
1682
 
1683
  debugLog("调用上游API: %s", fullURL)
1684
  debugLog("上游请求体: %s", string(reqBody))
 
1689
  return nil, err
1690
  }
1691
 
1692
+ // 设置请求头
 
 
 
 
 
1693
  req.Header.Set("Content-Type", "application/json")
1694
+ req.Header.Set("Accept", "application/json, text/event-stream")
 
1695
  req.Header.Set("User-Agent", BROWSER_UA)
1696
+ req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
1697
  req.Header.Set("Authorization", "Bearer "+authToken)
 
 
 
 
1698
  req.Header.Set("X-Signature", signature)
1699
+ req.Header.Set("X-FE-Version", "prod-fe-1.0.69")
1700
  req.Header.Set("Origin", ORIGIN_BASE)
1701
  req.Header.Set("Referer", ORIGIN_BASE+"/c/"+refererChatID)
 
 
 
 
 
 
 
1702
 
1703
  client := &http.Client{Timeout: UPSTREAM_TIMEOUT * time.Second}
1704
  resp, err := client.Do(req)