package chatgpt import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" "strings" "go-helper/internal/model" ) // GetUsers fetches the list of members for a team account. func (c *Client) GetUsers(account *model.GptAccount) (int, []model.ChatGPTUser, error) { apiURL := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/users?offset=0&limit=100&query=", account.ChatgptAccountID) req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return 0, nil, err } req.Header = buildHeaders(account.Token, account.ChatgptAccountID) resp, err := c.httpClient.Do(req) if err != nil { return 0, nil, fmt.Errorf("获取成员列表网络错误: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if err := checkAPIError(resp.StatusCode, body, "获取成员"); err != nil { return 0, nil, err } var data struct { Total int `json:"total"` Items []struct { ID string `json:"id"` AccountUserID string `json:"account_user_id"` Email string `json:"email"` Role string `json:"role"` Name string `json:"name"` } `json:"items"` } if err := json.Unmarshal(body, &data); err != nil { return 0, nil, fmt.Errorf("解析成员数据失败: %w", err) } var users []model.ChatGPTUser for _, item := range data.Items { users = append(users, model.ChatGPTUser{ ID: item.ID, AccountUserID: item.AccountUserID, Email: item.Email, Role: item.Role, Name: item.Name, }) } return data.Total, users, nil } // DeleteUser removes a user from the team account. func (c *Client) DeleteUser(account *model.GptAccount, userID string) error { normalizedID := userID if !strings.HasPrefix(normalizedID, "user-") { normalizedID = "user-" + normalizedID } apiURL := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/users/%s", account.ChatgptAccountID, normalizedID) req, err := http.NewRequest("DELETE", apiURL, nil) if err != nil { return err } req.Header = buildHeaders(account.Token, account.ChatgptAccountID) resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("删除用户网络错误: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if err := checkAPIError(resp.StatusCode, body, "删除用户"); err != nil { return err } log.Printf("[Member] 成功删除用户 %s (账号 %s)", normalizedID, account.ChatgptAccountID) return nil } // GetInvites fetches pending invitations for a team account. func (c *Client) GetInvites(account *model.GptAccount) (int, []model.ChatGPTInvite, error) { apiURL := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/invites?offset=0&limit=100&query=", account.ChatgptAccountID) req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return 0, nil, err } req.Header = buildHeaders(account.Token, account.ChatgptAccountID) req.Header.Set("Referer", "https://chatgpt.com/admin/members?tab=invites") resp, err := c.httpClient.Do(req) if err != nil { return 0, nil, fmt.Errorf("获取邀请列表网络错误: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if err := checkAPIError(resp.StatusCode, body, "获取邀请列表"); err != nil { return 0, nil, err } var data struct { Total int `json:"total"` Items []struct { ID string `json:"id"` EmailAddress string `json:"email_address"` Role string `json:"role"` } `json:"items"` } if err := json.Unmarshal(body, &data); err != nil { return 0, nil, fmt.Errorf("解析邀请数据失败: %w", err) } var invites []model.ChatGPTInvite for _, item := range data.Items { invites = append(invites, model.ChatGPTInvite{ ID: item.ID, EmailAddress: item.EmailAddress, Role: item.Role, }) } return data.Total, invites, nil } // DeleteInvite cancels a pending invitation by email on the team account. func (c *Client) DeleteInvite(account *model.GptAccount, email string) error { apiURL := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/invites", account.ChatgptAccountID) payload := map[string]string{"email_address": strings.TrimSpace(strings.ToLower(email))} bodyBytes, _ := json.Marshal(payload) req, err := http.NewRequest("DELETE", apiURL, bytes.NewReader(bodyBytes)) if err != nil { return err } req.Header = buildHeaders(account.Token, account.ChatgptAccountID) req.Header.Set("Content-Type", "application/json") req.Header.Set("Referer", "https://chatgpt.com/admin/members?tab=invites") resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("删除邀请网络错误: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if err := checkAPIError(resp.StatusCode, body, "删除邀请"); err != nil { return err } log.Printf("[Member] 成功删除邀请 %s (账号 %s)", email, account.ChatgptAccountID) return nil } // checkAPIError inspects the HTTP status code and returns a descriptive error. func checkAPIError(statusCode int, body []byte, label string) error { if statusCode >= 200 && statusCode < 300 { return nil } bodyStr := truncate(string(body), 500) // Check for account_deactivated. if strings.Contains(bodyStr, "account_deactivated") { return fmt.Errorf("账号已停用 (account_deactivated)") } switch statusCode { case 401, 402: return fmt.Errorf("Token 已过期或被封禁") case 403: return fmt.Errorf("资源不存在或无权访问") case 429: return fmt.Errorf("API 请求过于频繁,请稍后重试") default: return fmt.Errorf("%s失败 (HTTP %d): %s", label, statusCode, bodyStr) } }