Files
gpt-plus-gpt/pkg/chatgpt/session.go
2026-03-15 20:48:19 +08:00

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
}