1
0

fix conflict

This commit is contained in:
root 2023-06-28 12:31:56 +08:00
commit 931d5a245c
13 changed files with 172 additions and 52 deletions

43
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,43 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: Release Workflow
on:
release:
types:
- created
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build
run: go build -o bin/ .
- name: recursively list files
run: ls -R
- name: Get existing release body
id: get_release_body
run: |
echo "::set-output name=body::$(curl -s -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ github.ref_path }} | jq -r '.body')"
- name: Upload release artifact
uses: svenstaro/upload-release-action@v2
with:
file: bin/*
file_glob: true
tag: ${{ github.ref }}
body: |
${{ steps.get_release_body.outputs.body }}
repo_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -49,6 +49,7 @@ go build
- `PUID` - Plus账户可在`chat.openai.com`的cookies里找到用于绕过cf的频率限制
- `SERVER_HOST` - 默认127.0.0.1
- `SERVER_PORT` - 默认8080
- `ENABLE_HISTORY` - 默认true允许网页端历史记录
### 可选文件配置
- `proxies.txt` - 存放代理地址的文件

View File

@ -1,20 +1,35 @@
package chatgpt
import (
"fmt"
chatgpt_types "freechatgpt/typings/chatgpt"
official_types "freechatgpt/typings/official"
"strings"
arkose "github.com/acheong08/funcaptcha"
)
func ConvertAPIRequest(api_request official_types.APIRequest) chatgpt_types.ChatGPTRequest {
chatgpt_request := chatgpt_types.NewChatGPTRequest()
if strings.HasPrefix(api_request.Model, "gpt-3.5") {
chatgpt_request.Model = "text-davinci-002-render-sha"
}
if strings.HasPrefix(api_request.Model, "gpt-4") {
token, err := arkose.GetOpenAIToken()
if err == nil {
chatgpt_request.ArkoseToken = token
} else {
fmt.Println("Error getting Arkose token: ", err)
}
chatgpt_request.Model = "gpt-4"
if api_request.Model == "gpt-4-browsing" || api_request.Model == "gpt-4-plugins" || api_request.Model == "gpt-4-mobile" || api_request.Model == "gpt-4-code-interpreter" {
if api_request.Model == "gpt-4-browsing" || api_request.Model == "gpt-4-mobile" || api_request.Model == "gpt-4-code-interpreter" {
chatgpt_request.Model = api_request.Model
}
}
if api_request.PluginIDs != nil {
chatgpt_request.PluginIDs = api_request.PluginIDs
chatgpt_request.Model = "gpt-4-plugins"
}
for _, api_message := range api_request.Messages {
if api_message.Role == "system" {
api_message.Role = "critic"

View File

@ -7,9 +7,11 @@ import (
"strings"
)
func ConvertToString(chatgpt_response *chatgpt_types.ChatGPTResponse, previous_text *typings.StringStruct) string {
func ConvertToString(chatgpt_response *chatgpt_types.ChatGPTResponse, previous_text *typings.StringStruct, role bool) string {
translated_response := official_types.NewChatCompletionChunk(strings.ReplaceAll(chatgpt_response.Message.Content.Parts[0], *&previous_text.Text, ""))
if role {
translated_response.Choices[0].Delta.Role = chatgpt_response.Message.Author.Role
}
previous_text.Text = chatgpt_response.Message.Content.Parts[0]
return "data: " + translated_response.String() + "\n\n"

3
go.mod
View File

@ -4,7 +4,8 @@ go 1.20
require (
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057
github.com/acheong08/endless v0.0.0-20230529075213-74050cf641c8
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd
github.com/acheong08/funcaptcha v0.2.1-0.20230626152808-543148a3c981
github.com/bogdanfinn/fhttp v0.5.23
github.com/bogdanfinn/tls-client v1.4.0
github.com/gin-gonic/gin v1.9.1

8
go.sum
View File

@ -1,7 +1,11 @@
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057 h1:AmqKpClFTUHREYekSfSy3Yn7MR/oc6WTfKtEyzB/J7o=
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057/go.mod h1:ES3Dh9hnbR2mDPlNTagj5e3b4nXECd4tbAjVgxggXEE=
github.com/acheong08/endless v0.0.0-20230529075213-74050cf641c8 h1:mHtMoGlGNUfMRjsWcb5Kvd1mJfJG8Gr1TtIghE8iiN8=
github.com/acheong08/endless v0.0.0-20230529075213-74050cf641c8/go.mod h1:0yO7neMeJLvKk/B/fq5votDY8rByrOPDubpvU+6saKo=
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd h1:oIpfrRhD7Jus41dotbK+SQjWSFRnf1cLZUYCZpF/o/4=
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd/go.mod h1:0yO7neMeJLvKk/B/fq5votDY8rByrOPDubpvU+6saKo=
github.com/acheong08/funcaptcha v0.2.1-0.20230626140502-adeab195a0b3 h1:3SiHkE69H/JWT19fpIvjRScPDx5DnWnzzZpR5V4vHkQ=
github.com/acheong08/funcaptcha v0.2.1-0.20230626140502-adeab195a0b3/go.mod h1:fKxNB5i7g9h6QDTIY1YZamwFmMpAJK++wMYij5NuMm4=
github.com/acheong08/funcaptcha v0.2.1-0.20230626152808-543148a3c981 h1:ibvQvXMdniYRTYJFDaUJvG+25BF/bQxzE3AfDtv+0Ag=
github.com/acheong08/funcaptcha v0.2.1-0.20230626152808-543148a3c981/go.mod h1:VupbjtVAODvgyAB3Zo86fOA53G+UAmaV/Rk9jUCGuTU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bogdanfinn/fhttp v0.5.23 h1:4Xb5OjYArB8GpnUw4A4r5jmt8UW0/Cvey3R9nS2dC9U=

View File

@ -12,12 +12,26 @@ import (
)
func openaiHandler(c *gin.Context) {
var authorizations struct {
OpenAI_Email string `json:"openai_email"`
OpenAI_Password string `json:"openai_password"`
Official_API_Key string `json:"official_api_key"`
}
err := c.BindJSON(&authorizations)
if err != nil {
c.JSON(400, gin.H{"error": "JSON invalid"})
}
if authorizations.OpenAI_Email != "" && authorizations.OpenAI_Password != "" {
os.Setenv("OPENAI_EMAIL", authorizations.OpenAI_Email)
os.Setenv("OPENAI_PASSWORD", authorizations.OpenAI_Password)
}
if authorizations.Official_API_Key != "" {
os.Setenv("OFFICIAL_API_KEY", authorizations.Official_API_Key)
}
if authorizations.OpenAI_Email == "" && authorizations.OpenAI_Password == "" && authorizations.Official_API_Key == "" {
c.JSON(400, gin.H{"error": "JSON invalid"})
return
}
c.String(200, "OpenAI credentials updated")
}
@ -84,6 +98,7 @@ func nightmare(c *gin.Context) {
"param": nil,
"code": err.Error(),
}})
return
}
authHeader := c.GetHeader("Authorization")
@ -91,7 +106,7 @@ func nightmare(c *gin.Context) {
if authHeader != "" {
customAccessToken := strings.Replace(authHeader, "Bearer ", "", 1)
// Check if customAccessToken starts with sk-
if strings.HasPrefix(customAccessToken, "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UaEVOVUpHTkVNMVFURTRNMEZCTWpkQ05UZzVNRFUxUlRVd1FVSkRNRU13UmtGRVFrRXpSZyJ9") {
if strings.HasPrefix(customAccessToken, "eyJhbGciOiJSUzI1NiI") {
token = customAccessToken
}
}
@ -107,7 +122,7 @@ func nightmare(c *gin.Context) {
// Convert the chat request to a ChatGPT request
translated_request := chatgpt_request_converter.ConvertAPIRequest(original_request)
response, err := chatgpt.Send_request(translated_request, token, proxy_url)
response, err := chatgpt.POSTconversation(translated_request, token, proxy_url)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
@ -119,7 +134,7 @@ func nightmare(c *gin.Context) {
return
}
var full_response string
for i := 2; i > 0; i-- {
for i := 3; i > 0; i-- {
var continue_info *chatgpt.ContinueInfo
var response_part string
response_part, continue_info = chatgpt.Handler(c, response, token, translated_request, original_request.Stream)
@ -132,7 +147,7 @@ func nightmare(c *gin.Context) {
translated_request.Action = "continue"
translated_request.ConversationID = continue_info.ConversationID
translated_request.ParentMessageID = continue_info.ParentID
response, err = chatgpt.Send_request(translated_request, token, proxy_url)
response, err = chatgpt.POSTconversation(translated_request, token, proxy_url)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
@ -151,3 +166,14 @@ func nightmare(c *gin.Context) {
}
}
func engines_handler(c *gin.Context) {
resp, status, err := chatgpt.GETengines()
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
c.JSON(status, resp)
}

View File

@ -25,7 +25,7 @@ var (
jar = tls_client.NewCookieJar()
options = []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(360),
tls_client.WithClientProfile(tls_client.Safari_IOS_15_5),
tls_client.WithClientProfile(tls_client.Safari_Ipad_15_6),
tls_client.WithNotFollowRedirects(),
tls_client.WithCookieJar(jar), // create cookieJar instance and pass it as argument
// Disable SSL verification
@ -35,7 +35,7 @@ var (
API_REVERSE_PROXY = os.Getenv("API_REVERSE_PROXY")
)
func Send_request(message chatgpt_types.ChatGPTRequest, access_token string, proxy string) (*http.Response, error) {
func POSTconversation(message chatgpt_types.ChatGPTRequest, access_token string, proxy string) (*http.Response, error) {
if proxy != "" {
client.SetProxy(proxy)
}
@ -123,6 +123,7 @@ func Handler(c *gin.Context, response *http.Response, token string, translated_r
var finish_reason string
var previous_text typings.StringStruct
var original_response chatgpt_types.ChatGPTResponse
var isRole = true
for {
line, err := reader.ReadString('\n')
if err != nil {
@ -142,8 +143,6 @@ func Handler(c *gin.Context, response *http.Response, token string, translated_r
err = json.Unmarshal([]byte(line), &original_response)
if err != nil {
println("Failed to parse JSON")
println(line)
continue
}
if original_response.Error != nil {
@ -153,10 +152,11 @@ func Handler(c *gin.Context, response *http.Response, token string, translated_r
if original_response.Message.Author.Role != "assistant" || original_response.Message.Content.Parts == nil {
continue
}
if original_response.Message.Metadata.MessageType != "next" {
if original_response.Message.Metadata.MessageType != "next" && original_response.Message.Metadata.MessageType != "continue" || original_response.Message.EndTurn != nil {
continue
}
response_string := chatgpt_response_converter.ConvertToString(&original_response, &previous_text)
response_string := chatgpt_response_converter.ConvertToString(&original_response, &previous_text, isRole)
isRole = false
if stream {
_, err = c.Writer.WriteString(response_string)
if err != nil {
@ -188,3 +188,17 @@ func Handler(c *gin.Context, response *http.Response, token string, translated_r
ParentID: original_response.Message.ID,
}
}
func GETengines() (interface{}, int, error) {
url := "https://api.openai.com/v1/models"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer "+os.Getenv("OFFICIAL_API_KEY"))
resp, err := client.Do(req)
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()
var result interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result, resp.StatusCode, nil
}

18
main.go
View File

@ -39,18 +39,14 @@ func checkProxy() {
}
}
var authorizations struct {
OpenAI_Email string `json:"openai_email"`
OpenAI_Password string `json:"openai_password"`
}
func init() {
authorizations.OpenAI_Email = os.Getenv("OPENAI_EMAIL")
authorizations.OpenAI_Password = os.Getenv("OPENAI_PASSWORD")
if authorizations.OpenAI_Email != "" && authorizations.OpenAI_Password != "" {
go func() {
for {
authenticator := auth.NewAuthenticator(authorizations.OpenAI_Email, authorizations.OpenAI_Password, os.Getenv("http_proxy"))
if os.Getenv("OPENAI_EMAIL") == "" || os.Getenv("OPENAI_PASSWORD") == "" {
time.Sleep(24 * time.Hour * 7)
continue
}
authenticator := auth.NewAuthenticator(os.Getenv("OPENAI_EMAIL"), os.Getenv("OPENAI_PASSWORD"), os.Getenv("http_proxy"))
err := authenticator.Begin()
if err != nil {
log.Println(err)
@ -65,7 +61,7 @@ func init() {
time.Sleep(24 * time.Hour * 7)
}
}()
}
HOST = os.Getenv("SERVER_HOST")
PORT = os.Getenv("SERVER_PORT")
if HOST == "" {
@ -100,5 +96,7 @@ func main() {
/// Public routes
router.OPTIONS("/v1/chat/completions", optionsHandler)
router.POST("/v1/chat/completions", Authorization, nightmare)
router.GET("/v1/engines", Authorization, engines_handler)
router.GET("/v1/models", Authorization, engines_handler)
endless.ListenAndServe(HOST+":"+PORT, router)
}

View File

@ -3,6 +3,7 @@ package main
import (
"bufio"
"os"
"strings"
gin "github.com/gin-gonic/gin"
)
@ -50,7 +51,15 @@ func Authorization(c *gin.Context) {
}
}
if len(API_KEYS) != 0 && !API_KEYS[c.Request.Header.Get("Authorization")] {
c.String(401, "Unauthorized")
if c.Request.Header.Get("Authorization") == "" {
c.JSON(401, gin.H{"error": "No API key provided. Get one at https://discord.gg/9K2BvbXEHT"})
} else if strings.HasPrefix(c.Request.Header.Get("Authorization"), "Bearer sk-") {
c.JSON(401, gin.H{"error": "You tried to use the official API key which is not supported."})
} else if strings.HasPrefix(c.Request.Header.Get("Authorization"), "Bearer eyJhbGciOiJSUzI1NiI") {
return
} else {
c.JSON(401, gin.H{"error": "Invalid API key."})
}
c.Abort()
return
}

View File

@ -1,6 +1,10 @@
package chatgpt
import "github.com/google/uuid"
import (
"os"
"github.com/google/uuid"
)
type chatgpt_message struct {
ID uuid.UUID `json:"id"`
@ -24,14 +28,17 @@ type ChatGPTRequest struct {
ConversationID string `json:"conversation_id,omitempty"`
Model string `json:"model"`
HistoryAndTrainingDisabled bool `json:"history_and_training_disabled"`
ArkoseToken string `json:"arkose_token,omitempty"`
PluginIDs []string `json:"plugin_ids,omitempty"`
}
func NewChatGPTRequest() ChatGPTRequest {
enable_history := os.Getenv("ENABLE_HISTORY") == ""
return ChatGPTRequest{
Action: "next",
ParentMessageID: uuid.NewString(),
Model: "text-davinci-002-render-sha",
HistoryAndTrainingDisabled: true,
HistoryAndTrainingDisabled: !enable_history,
}
}

View File

@ -4,6 +4,7 @@ type APIRequest struct {
Messages []api_message `json:"messages"`
Stream bool `json:"stream"`
Model string `json:"model"`
PluginIDs []string `json:"plugin_ids"`
}
type api_message struct {

View File

@ -22,8 +22,8 @@ type Choices struct {
}
type Delta struct {
Content string `json:"content"`
Role string `json:"role"`
Content string `json:"content,omitempty"`
Role string `json:"role,omitempty"`
}
func NewChatCompletionChunk(text string) ChatCompletionChunk {
@ -37,7 +37,6 @@ func NewChatCompletionChunk(text string) ChatCompletionChunk {
Index: 0,
Delta: Delta{
Content: text,
Role: "assistant",
},
FinishReason: nil,
},