Initial sanitized code sync
This commit is contained in:
119
pkg/chatgpt/invite.go
Normal file
119
pkg/chatgpt/invite.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package chatgpt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"gpt-plus/pkg/httpclient"
|
||||
)
|
||||
|
||||
// InviteResult holds the result of a team member invitation.
|
||||
type InviteResult struct {
|
||||
Email string
|
||||
Success bool
|
||||
Error string
|
||||
}
|
||||
|
||||
// GetWorkspaceAccessToken gets a workspace-scoped access token by calling
|
||||
// GET /api/auth/session with chatgpt-account-id header.
|
||||
// This is simpler than the exchange_workspace_token approach and matches browser behavior.
|
||||
func GetWorkspaceAccessToken(client *httpclient.Client, teamAccountID string) (string, error) {
|
||||
sessionURL := "https://chatgpt.com/api/auth/session"
|
||||
|
||||
headers := map[string]string{
|
||||
"User-Agent": defaultUserAgent,
|
||||
"Accept": "application/json",
|
||||
"Origin": chatGPTOrigin,
|
||||
"Referer": chatGPTOrigin + "/",
|
||||
"chatgpt-account-id": teamAccountID,
|
||||
}
|
||||
|
||||
resp, err := client.Get(sessionURL, headers)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("workspace session request: %w", err)
|
||||
}
|
||||
|
||||
body, err := httpclient.ReadBody(resp)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read workspace session body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("workspace session returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return "", fmt.Errorf("parse workspace session response: %w", err)
|
||||
}
|
||||
|
||||
if result.AccessToken == "" {
|
||||
return "", fmt.Errorf("empty accessToken in workspace session response")
|
||||
}
|
||||
|
||||
log.Printf("[invite] workspace access token obtained for %s, len=%d", teamAccountID, len(result.AccessToken))
|
||||
return result.AccessToken, nil
|
||||
}
|
||||
|
||||
// InviteToTeam sends a team invitation to the specified email address.
|
||||
// POST /backend-api/accounts/{account_id}/invites
|
||||
func InviteToTeam(client *httpclient.Client, workspaceToken, teamAccountID, deviceID, email string) error {
|
||||
inviteURL := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/invites", teamAccountID)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"email_addresses": []string{email},
|
||||
"role": "standard-user",
|
||||
"resend_emails": true,
|
||||
}
|
||||
jsonBody, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal invite body: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", inviteURL, strings.NewReader(string(jsonBody)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create invite request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+workspaceToken)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", defaultUserAgent)
|
||||
req.Header.Set("Origin", chatGPTOrigin)
|
||||
req.Header.Set("Referer", chatGPTOrigin+"/")
|
||||
req.Header.Set("oai-device-id", deviceID)
|
||||
req.Header.Set("oai-language", defaultLanguage)
|
||||
req.Header.Set("chatgpt-account-id", teamAccountID)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("send invite request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := httpclient.ReadBody(resp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read invite response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("invite returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
// Verify response contains account_invites
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return fmt.Errorf("parse invite response: %w", err)
|
||||
}
|
||||
|
||||
if _, ok := result["account_invites"]; !ok {
|
||||
return fmt.Errorf("no account_invites in response: %s", string(body))
|
||||
}
|
||||
|
||||
log.Printf("[invite] successfully invited %s to team %s", email, teamAccountID)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user