132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
package chatgpt
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
"gpt-plus/pkg/httpclient"
|
|
)
|
|
|
|
const (
|
|
defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
|
|
chatGPTOrigin = "https://chatgpt.com"
|
|
)
|
|
|
|
// defaultLanguage is the package-level language setting used by standalone functions
|
|
// that don't have access to a Session. Set via SetDefaultLanguage().
|
|
var defaultLanguage = "en-US"
|
|
|
|
// StatusFunc is a callback for reporting progress to the user.
|
|
type StatusFunc func(format string, args ...interface{})
|
|
|
|
// GetDefaultUA returns the default User-Agent string.
|
|
func GetDefaultUA() string {
|
|
return defaultUserAgent
|
|
}
|
|
|
|
// SetDefaultLanguage sets the default oai-language for standalone functions (CheckAccount, etc.).
|
|
func SetDefaultLanguage(lang string) {
|
|
if lang != "" {
|
|
defaultLanguage = lang
|
|
}
|
|
}
|
|
|
|
// GetDefaultLanguage returns the current default language setting.
|
|
func GetDefaultLanguage() string {
|
|
return defaultLanguage
|
|
}
|
|
|
|
// Session holds authentication state for a ChatGPT session.
|
|
type Session struct {
|
|
Client *httpclient.Client
|
|
AccessToken string
|
|
RefreshToken string
|
|
IDToken string
|
|
DeviceID string
|
|
AccountID string
|
|
UserID string
|
|
Language string // browser locale e.g. "en-US" — derived from card country
|
|
}
|
|
|
|
// NewSession creates a new Session with all auth details.
|
|
func NewSession(client *httpclient.Client, accessToken, refreshToken, idToken, deviceID, accountID, userID string) *Session {
|
|
return &Session{
|
|
Client: client,
|
|
AccessToken: accessToken,
|
|
RefreshToken: refreshToken,
|
|
IDToken: idToken,
|
|
DeviceID: deviceID,
|
|
AccountID: accountID,
|
|
UserID: userID,
|
|
}
|
|
}
|
|
|
|
// AuthHeaders returns common headers required for ChatGPT API requests.
|
|
func (s *Session) AuthHeaders() map[string]string {
|
|
lang := s.Language
|
|
if lang == "" {
|
|
lang = defaultLanguage
|
|
}
|
|
return map[string]string{
|
|
"Authorization": "Bearer " + s.AccessToken,
|
|
"User-Agent": defaultUserAgent,
|
|
"Accept": "*/*",
|
|
"Content-Type": "application/json",
|
|
"Origin": chatGPTOrigin,
|
|
"Referer": chatGPTOrigin + "/",
|
|
"oai-device-id": s.DeviceID,
|
|
"oai-language": lang,
|
|
}
|
|
}
|
|
|
|
// RefreshSession calls /api/auth/session to get a fresh access token
|
|
// (reflecting current plan status after Plus/Team activation).
|
|
func (s *Session) RefreshSession() error {
|
|
sessionURL := "https://chatgpt.com/api/auth/session"
|
|
|
|
headers := map[string]string{
|
|
"User-Agent": defaultUserAgent,
|
|
"Accept": "application/json",
|
|
"Origin": chatGPTOrigin,
|
|
"Referer": chatGPTOrigin + "/",
|
|
}
|
|
|
|
resp, err := s.Client.Get(sessionURL, headers)
|
|
if err != nil {
|
|
return fmt.Errorf("refresh session request: %w", err)
|
|
}
|
|
|
|
body, err := httpclient.ReadBody(resp)
|
|
if err != nil {
|
|
return fmt.Errorf("read refresh session body: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("refresh session returned %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var result struct {
|
|
AccessToken string `json:"accessToken"`
|
|
RefreshToken string `json:"refreshToken"`
|
|
IDToken string `json:"idToken"`
|
|
}
|
|
if err := json.Unmarshal(body, &result); err != nil {
|
|
return fmt.Errorf("parse refresh session response: %w", err)
|
|
}
|
|
|
|
if result.AccessToken != "" {
|
|
s.AccessToken = result.AccessToken
|
|
}
|
|
if result.RefreshToken != "" {
|
|
s.RefreshToken = result.RefreshToken
|
|
}
|
|
if result.IDToken != "" {
|
|
s.IDToken = result.IDToken
|
|
}
|
|
|
|
log.Printf("[session] tokens refreshed successfully")
|
|
return nil
|
|
}
|