Skip to content

Commit

Permalink
Support no auth conversation
Browse files Browse the repository at this point in the history
1. Update OpenAIAuth and funcaptcha library
2. `POST /chatgpt/conversation`, `POST /chatgpt/backend-api/conversation` and `/api/conversation` support no auth conversation
  • Loading branch information
leokwsw committed Apr 5, 2024
1 parent 989d36d commit 58989d7
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 45 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## 此為個人維護版,與原版相比,有以下改動及根據OpenAI的更改而去改變

### 2024-04-06

- Update OpenAIAuth and funcaptcha library
- `POST /chatgpt/conversation`, `POST /chatgpt/backend-api/conversation` and `/api/conversation` support no auth conversation
- Sample Request [freeChatgpt.http](example%2FfreeChatgpt.http)

### 2024-03-23

- Fix 403 Error
Expand Down
102 changes: 75 additions & 27 deletions api/chatgpt/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/linweiyuan/go-logger/logger"
"io"
Expand Down Expand Up @@ -34,6 +35,7 @@ func GetConversations(c *gin.Context) {
func CreateConversation(c *gin.Context) {
var request CreateConversationRequest
var apiVersion int
uid := uuid.NewString()

if err := c.BindJSON(&request); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, api.ReturnMessage(parseJsonErrorMessage))
Expand Down Expand Up @@ -63,15 +65,15 @@ func CreateConversation(c *gin.Context) {
apiVersion = 3
}

chatRequirements, err := GetChatRequirementsByGin(c)
chatRequirements, err := GetChatRequirementsByGin(c, uid)

if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, api.ReturnMessage(err.Error()))
return
}

if chatRequirements.Arkose.Required == true && request.ArkoseToken == "" {
arkoseToken, err := api.GetArkoseToken(apiVersion)
arkoseToken, err := api.GetArkoseToken(apiVersion, chatRequirements.Arkose.Dx)
if err != nil || arkoseToken == "" {
c.AbortWithStatusJSON(http.StatusForbidden, api.ReturnMessage(err.Error()))
return
Expand All @@ -80,19 +82,34 @@ func CreateConversation(c *gin.Context) {
request.ArkoseToken = arkoseToken
}

resp, done := sendConversationRequest(c, request, chatRequirements.Token)
if c.GetHeader(api.AuthorizationHeader) == "" {
request.Model = gpt3dot5Model
}

resp, done := sendConversationRequest(c, request, chatRequirements.Token, uid)
if done {
return
}
handleConversationResponse(c, resp, request, chatRequirements.Token)
handleConversationResponse(c, resp, request, chatRequirements.Token, chatRequirements.Arkose.Dx, uid)
}

func sendConversationRequest(c *gin.Context, request CreateConversationRequest, chatRequirementsToken string) (*http.Response, bool) {
func sendConversationRequest(c *gin.Context, request CreateConversationRequest, chatRequirementsToken string, uid string) (*http.Response, bool) {
jsonBytes, _ := json.Marshal(request)
req, _ := http.NewRequest(http.MethodPost, ApiPrefix+"/conversation", bytes.NewBuffer(jsonBytes))

urlPrefix := ""

if c.GetHeader(api.AuthorizationHeader) == "" {
urlPrefix = AnonPrefix
} else {
urlPrefix = ApiPrefix
}

req, _ := http.NewRequest(http.MethodPost, urlPrefix+"/conversation", bytes.NewBuffer(jsonBytes))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", api.UserAgent)
req.Header.Set(api.AuthorizationHeader, api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)))
if urlPrefix == ApiPrefix {
req.Header.Set(api.AuthorizationHeader, api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)))
}
req.Header.Set("Accept", "text/event-stream")
if request.ArkoseToken != "" {
req.Header.Set("Openai-Sentinel-Arkose-Token", request.ArkoseToken)
Expand All @@ -104,9 +121,14 @@ func sendConversationRequest(c *gin.Context, request CreateConversationRequest,
req.Header.Set("Cookie", "_puid="+api.PUID+";")
}
req.Header.Set("Oai-Language", api.Language)
if api.OAIDID != "" {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+api.OAIDID)
req.Header.Set("Oai-Device-Id", api.OAIDID)
if urlPrefix == ApiPrefix {
if api.OAIDID != "" {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+api.OAIDID)
req.Header.Set("Oai-Device-Id", api.OAIDID)
}
} else {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+uid)
req.Header.Set("Oai-Device-Id", uid)
}
resp, err := api.Client.Do(req)
if err != nil {
Expand All @@ -125,9 +147,18 @@ func sendConversationRequest(c *gin.Context, request CreateConversationRequest,
return nil, true
}

req, _ := http.NewRequest(http.MethodGet, ApiPrefix+"/models", nil)
req, _ := http.NewRequest(http.MethodGet, urlPrefix+"/models", nil)
req.Header.Set("User-Agent", api.UserAgent)
req.Header.Set(api.AuthorizationHeader, api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)))
if urlPrefix == ApiPrefix {
req.Header.Set(api.AuthorizationHeader, api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)))
if api.OAIDID != "" {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+api.OAIDID)
req.Header.Set("Oai-Device-Id", api.OAIDID)
}
} else {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+uid)
req.Header.Set("Oai-Device-Id", uid)
}
response, err := api.Client.Do(req)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, api.ReturnMessage(err.Error()))
Expand All @@ -149,7 +180,7 @@ func sendConversationRequest(c *gin.Context, request CreateConversationRequest,
return nil, true
}

fmt.Printf("OpenAI Request Method : %s ; url : %s ; Status : %d\n\n", http.MethodPost, ApiPrefix+"/conversation", resp.StatusCode)
fmt.Printf("OpenAI Request Method : %s ; url : %s ; Status : %d\n\n", http.MethodPost, urlPrefix+"/conversation", resp.StatusCode)
responseMap := make(map[string]interface{})
json.NewDecoder(resp.Body).Decode(&responseMap)

Expand All @@ -161,7 +192,7 @@ func sendConversationRequest(c *gin.Context, request CreateConversationRequest,
return resp, false
}

func handleConversationResponse(c *gin.Context, resp *http.Response, request CreateConversationRequest, chatRequirementsToken string) {
func handleConversationResponse(c *gin.Context, resp *http.Response, request CreateConversationRequest, chatRequirementsToken string, chatRequirementsArkoseDx string, uid string) {
c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")

isMaxTokens := false
Expand Down Expand Up @@ -293,7 +324,7 @@ func handleConversationResponse(c *gin.Context, resp *http.Response, request Cre

if isMaxTokens {
var continueConversationRequest = CreateConversationRequest{
ArkoseToken: request.ArkoseToken,
//ArkoseToken: request.ArkoseToken,
HistoryAndTrainingDisabled: request.HistoryAndTrainingDisabled,
Model: request.Model,
TimezoneOffsetMin: request.TimezoneOffsetMin,
Expand All @@ -302,12 +333,13 @@ func handleConversationResponse(c *gin.Context, resp *http.Response, request Cre
ParentMessageID: continueParentMessageID,
ConversationID: &continueConversationID,
}
resp, done := sendConversationRequest(c, continueConversationRequest, chatRequirementsToken)
RenewTokenForRequest(&request, chatRequirementsArkoseDx)
resp, done := sendConversationRequest(c, continueConversationRequest, chatRequirementsToken, uid)
if done {
return
}

handleConversationResponse(c, resp, continueConversationRequest, chatRequirementsToken)
handleConversationResponse(c, resp, continueConversationRequest, chatRequirementsToken, chatRequirementsArkoseDx, uid)
}
}

Expand Down Expand Up @@ -466,9 +498,9 @@ func handlePostOrPatch(c *gin.Context, req *http.Request, errorMessage string) {
io.Copy(c.Writer, resp.Body)
}

func GetChatRequirementsByGin(c *gin.Context) (*ChatRequirements, error) {
func GetChatRequirementsByGin(c *gin.Context, uid string) (*ChatRequirements, error) {

chatRequirements, err := GetChatRequirementsByAccessToken(api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)))
chatRequirements, err := GetChatRequirementsByAccessToken(api.GetAccessToken(c.GetHeader(api.AuthorizationHeader)), uid)

if err != nil {
return nil, err
Expand All @@ -477,20 +509,36 @@ func GetChatRequirementsByGin(c *gin.Context) (*ChatRequirements, error) {
return chatRequirements, nil
}

func GetChatRequirementsByAccessToken(accessToken string) (*ChatRequirements, error) {
req, _ := http.NewRequest(http.MethodPost, ApiPrefix+"/sentinel/chat-requirements", bytes.NewBuffer([]byte(`{"conversation_mode_kind":"primary_assistant"}`)))
func GetChatRequirementsByAccessToken(accessToken string, uid string) (*ChatRequirements, error) {

urlPrefix := ""

if accessToken == "Bearer " {
urlPrefix = AnonPrefix
} else {
urlPrefix = ApiPrefix
}

req, _ := http.NewRequest(http.MethodPost, urlPrefix+"/sentinel/chat-requirements", bytes.NewBuffer([]byte(`{"conversation_mode_kind":"primary_assistant"}`)))

if api.PUID != "" {
req.Header.Set("Cookie", "_puid="+api.PUID+";")
}
req.Header.Set("Oai-Language", api.Language)
if api.OAIDID != "" {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+api.OAIDID)
req.Header.Set("Oai-Device-Id", api.OAIDID)
if urlPrefix == ApiPrefix {
if api.OAIDID != "" {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+api.OAIDID)
req.Header.Set("Oai-Device-Id", api.OAIDID)
}
} else {
req.Header.Set("Cookie", req.Header.Get("Cookie")+"oai-did="+uid)
req.Header.Set("Oai-Device-Id", uid)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", api.UserAgent)
req.Header.Set(api.AuthorizationHeader, accessToken)
if urlPrefix == ApiPrefix {
req.Header.Set(api.AuthorizationHeader, accessToken)
}

res, err := api.Client.Do(req)

Expand All @@ -507,14 +555,14 @@ func GetChatRequirementsByAccessToken(accessToken string) (*ChatRequirements, er
return &require, nil
}

func RenewTokenForRequest(request *CreateConversationRequest) {
func RenewTokenForRequest(request *CreateConversationRequest, chatRequirementsArkoseDx string) {
var apiVersion int
if strings.HasPrefix(request.Model, "gpt-4") {
apiVersion = 4
} else {
apiVersion = 3
}
token, err := api.GetArkoseToken(apiVersion)
token, err := api.GetArkoseToken(apiVersion, chatRequirementsArkoseDx)
if err == nil {
request.ArkoseToken = token
} else {
Expand Down
4 changes: 3 additions & 1 deletion api/chatgpt/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
PublicApiPrefix = "https://chat.openai.com/public-api"
conversationLimit = PublicApiPrefix + "/conversation_limit"
ApiPrefix = "https://chat.openai.com/backend-api"
AnonPrefix = "https://chat.openai.com/backend-anon"
getConversationsErrorMessage = "Failed to get conversations."
generateTitleErrorMessage = "Failed to generate title."
getContentErrorMessage = "Failed to get content."
Expand All @@ -18,7 +19,8 @@ const (
getAccountCheckErrorMessage = "Check failed." // Placeholder. Never encountered.
parseJsonErrorMessage = "Failed to parse json request body."

gpt4Model = "gpt-4"
gpt4Model = "gpt-4"
gpt3dot5Model = "text-davinci-002-render-sha"

actionContinue = "continue"
responseTypeMaxTokens = "max_tokens"
Expand Down
2 changes: 1 addition & 1 deletion api/chatgpt/typings.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type CreateConversationRequest struct {
ConversationMode ConversationMode `json:"conversation_mode"`
ForceParagen bool `json:"force_paragen"`
ForceParagenModelSlug string `json:"force_paragen_model_slug"`
ForceNulligen string `json:"force_nulligen"`
ForceNulligen bool `json:"force_nulligen"`
ForceRateLimit bool `json:"force_rate_limit"`
Suggestions []string `json:"suggestions"`
WebSocketRequestId string `json:"websocket_request_id"`
Expand Down
4 changes: 2 additions & 2 deletions api/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ func GetAccessToken(accessToken string) string {
return accessToken
}

func GetArkoseToken(api_version int) (string, error) {
return funcaptcha.GetOpenAIToken(api_version, PUID, ProxyUrl)
func GetArkoseToken(apiVersion int, dx string) (string, error) {
return funcaptcha.GetOpenAIToken(apiVersion, PUID, dx, ProxyUrl)
}

func setupID() {
Expand Down
10 changes: 5 additions & 5 deletions api/imitate/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func CreateChatCompletions(c *gin.Context) {
}()
go func() {
defer waitGroup.Done()
chatRequirements, err = chatgpt.GetChatRequirementsByAccessToken(token)
chatRequirements, err = chatgpt.GetChatRequirementsByAccessToken(token, uid)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, api.ReturnMessage(err.Error()))
return
Expand All @@ -98,7 +98,7 @@ func CreateChatCompletions(c *gin.Context) {
}

// 将聊天请求转换为ChatGPT请求。
translatedRequest := convertAPIRequest(originalRequest, chatRequirements.Arkose.Required)
translatedRequest := convertAPIRequest(originalRequest, chatRequirements.Arkose.Required, chatRequirements.Arkose.Dx)

response, done := sendConversationRequest(c, translatedRequest, token, chatRequirements.Token)
if done {
Expand Down Expand Up @@ -135,7 +135,7 @@ func CreateChatCompletions(c *gin.Context) {
translatedRequest.ConversationID = &continueInfo.ConversationID
translatedRequest.ParentMessageID = continueInfo.ParentID
if chatRequirements.Arkose.Required {
chatgpt.RenewTokenForRequest(&translatedRequest)
chatgpt.RenewTokenForRequest(&translatedRequest, chatRequirements.Arkose.Dx)
}
response, done = sendConversationRequest(c, translatedRequest, token, chatRequirements.Token)

Expand Down Expand Up @@ -183,7 +183,7 @@ func generateId() string {
return "chatcmpl-" + id
}

func convertAPIRequest(apiRequest APIRequest, chatRequirementsArkoseRequired bool) chatgpt.CreateConversationRequest {
func convertAPIRequest(apiRequest APIRequest, chatRequirementsArkoseRequired bool, chatRequirementsArkoseDx string) chatgpt.CreateConversationRequest {
chatgptRequest := NewChatGPTRequest()

var apiVersion int
Expand All @@ -200,7 +200,7 @@ func convertAPIRequest(apiRequest APIRequest, chatRequirementsArkoseRequired boo
}

if chatRequirementsArkoseRequired {
token, err := api.GetArkoseToken(apiVersion)
token, err := api.GetArkoseToken(apiVersion, chatRequirementsArkoseDx)
if err == nil {
chatgptRequest.ArkoseToken = token
} else {
Expand Down
30 changes: 30 additions & 0 deletions example/freeChatgpt.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
### create conversation
#POST {{baseUrl}}/chatgpt/backend-api/conversation
#POST {{baseUrl}}/chatgpt/conversation
POST {{baseUrl}}/api/conversation
Content-Type: application/json
Accept: text/event-stream

{
"action": "next",
"conversation_mode": {
"kind": "primary_assistant"
},
"variant_purpose": "none",
"messages": [
{
"id": "{{$random.uuid}}",
"author": {
"role": "user"
},
"content": {
"content_type": "text",
"parts": [
"Hello"
]
}
}
],
"model": "text-davinci-002-render-sha",
"timezone_offset_min": -480
}
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ require (
github.com/gorilla/websocket v1.5.1
github.com/joho/godotenv v1.5.1
github.com/linweiyuan/go-logger v0.0.0-20230709142852-da1f090a7d4c
github.com/xqdoo00o/OpenAIAuth v0.0.0-20240320160511-93065690aa0e
github.com/xqdoo00o/funcaptcha v0.0.0-20240313153914-4ab805804232
github.com/xqdoo00o/OpenAIAuth v0.0.0-20240403091529-7ef147706fc4
github.com/xqdoo00o/funcaptcha v0.0.0-20240403090732-1b604d808f6c
)

require (
Expand Down Expand Up @@ -59,13 +59,13 @@ require (
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/tools v0.20.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
Loading

0 comments on commit 58989d7

Please sign in to comment.