Upload 19 files
Browse files- .gitignore +2 -0
- Dockerfile +21 -0
- chat/api.go +285 -0
- chat/proxy.go +61 -0
- config/config.go +36 -0
- go.mod +56 -0
- go.sum +149 -0
- httpclient/client.go +8 -0
- httpclient/reqclient.go +42 -0
- logger/logger.go +22 -0
- logger/logrus.go +41 -0
- main.go +19 -0
- model/model.go +121 -0
- model/request/request.go +12 -0
- model/response/response.go +27 -0
- model/response/stream.go +19 -0
- proofofwork/proofofwork.go +84 -0
- router/router.go +54 -0
- utils/util.go +42 -0
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.idea/
|
| 2 |
+
.env
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM golang:1.21 AS builder
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY go.mod go.sum ./
|
| 6 |
+
|
| 7 |
+
RUN go mod download
|
| 8 |
+
|
| 9 |
+
COPY . .
|
| 10 |
+
|
| 11 |
+
RUN CGO_ENABLED=0 go build -o /app/free-chat-to-api .
|
| 12 |
+
|
| 13 |
+
FROM alpine:latest
|
| 14 |
+
|
| 15 |
+
WORKDIR /app
|
| 16 |
+
|
| 17 |
+
COPY --from=builder /app/free-chat-to-api /app/free-chat-to-api
|
| 18 |
+
|
| 19 |
+
EXPOSE 8484
|
| 20 |
+
|
| 21 |
+
CMD [ "/app/free-chat-to-api" ]
|
chat/api.go
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package chat
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"encoding/json"
|
| 5 |
+
"errors"
|
| 6 |
+
"fmt"
|
| 7 |
+
"github.com/gin-gonic/gin"
|
| 8 |
+
"github.com/google/uuid"
|
| 9 |
+
"github.com/launchdarkly/eventsource"
|
| 10 |
+
"github.com/zbronya/free-chat-to-api/config"
|
| 11 |
+
"github.com/zbronya/free-chat-to-api/httpclient"
|
| 12 |
+
"github.com/zbronya/free-chat-to-api/logger"
|
| 13 |
+
"github.com/zbronya/free-chat-to-api/model"
|
| 14 |
+
"github.com/zbronya/free-chat-to-api/model/request"
|
| 15 |
+
"github.com/zbronya/free-chat-to-api/model/response"
|
| 16 |
+
"github.com/zbronya/free-chat-to-api/proofofwork"
|
| 17 |
+
"github.com/zbronya/free-chat-to-api/utils"
|
| 18 |
+
"io"
|
| 19 |
+
"net/http"
|
| 20 |
+
"strings"
|
| 21 |
+
"time"
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
func Completions(c *gin.Context) {
|
| 25 |
+
req := &request.ChatRequest{}
|
| 26 |
+
err := c.BindJSON(req)
|
| 27 |
+
if err != nil {
|
| 28 |
+
utils.ErrorResp(c, http.StatusBadRequest, "Invalid parameter", nil)
|
| 29 |
+
return
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
ua := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
| 33 |
+
client := httpclient.NewReqClient()
|
| 34 |
+
pConfig := proofofwork.GetConfig(ua)
|
| 35 |
+
chatRequirementReq := proofofwork.GetChatRequirementReq(pConfig)
|
| 36 |
+
deviceId := uuid.NewString()
|
| 37 |
+
|
| 38 |
+
requirement, err := getChatRequirement(c, client, chatRequirementReq, ua, deviceId)
|
| 39 |
+
if err != nil {
|
| 40 |
+
return
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
if requirement.ForceLogin {
|
| 44 |
+
utils.ErrorResp(c, http.StatusForbidden, "force login", nil)
|
| 45 |
+
return
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
token := "gAAAAAB" + proofofwork.CalcProofToken(pConfig, requirement.Proof.Seed, requirement.Proof.Difficulty)
|
| 49 |
+
|
| 50 |
+
doConversation(c, client, req, requirement, ua, deviceId, token)
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
func doConversation(c *gin.Context, client *httpclient.ReqClient, req *request.ChatRequest, requirement *model.ChatRequirementRes, ua string, deviceId string, token string) {
|
| 54 |
+
completionReq := model.ApiReqToChatReq(req)
|
| 55 |
+
url := config.GatewayUrl + "/backend-anon/conversation"
|
| 56 |
+
|
| 57 |
+
header := map[string]string{
|
| 58 |
+
"Accept": "text/event-stream",
|
| 59 |
+
"Accept-Encoding": "gzip, deflate, br",
|
| 60 |
+
"Accept-Language": "en-US,en;q=0.9",
|
| 61 |
+
"Content-Type": "application/json",
|
| 62 |
+
"Oai-Device-Id": deviceId,
|
| 63 |
+
"Oai-Language": "en-US",
|
| 64 |
+
"Openai-Sentinel-Chat-Requirements-Token": requirement.Token,
|
| 65 |
+
"Openai-Sentinel-Proof-Token": token,
|
| 66 |
+
"Origin": "https://chatgpt.com",
|
| 67 |
+
"Priority": "u=1, i",
|
| 68 |
+
"Referer": "https://chatgpt.com/?oai-dm=1",
|
| 69 |
+
"User-Agent": ua,
|
| 70 |
+
"Cookie": "__Secure-next-auth.callback-url=https%3A%2F%2Fchatgpt.com;",
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
j, _ := json.Marshal(completionReq)
|
| 74 |
+
|
| 75 |
+
resp, err := client.Post(url, header, j)
|
| 76 |
+
|
| 77 |
+
if err != nil {
|
| 78 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "fail to completions", nil)
|
| 79 |
+
return
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
if resp.StatusCode != 200 {
|
| 83 |
+
body, _ := io.ReadAll(resp.Body)
|
| 84 |
+
utils.ErrorResp(c, resp.StatusCode, string(body), nil)
|
| 85 |
+
return
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
if req.Stream {
|
| 89 |
+
conversationStream(c, req, resp)
|
| 90 |
+
} else {
|
| 91 |
+
conversation(c, req, resp)
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
func getChatRequirement(c *gin.Context, client httpclient.HttpClient, req model.ChatRequirementReq, ua string, deviceId string) (*model.ChatRequirementRes, error) {
|
| 97 |
+
url := config.GatewayUrl + "/backend-anon/sentinel/chat-requirements"
|
| 98 |
+
header := map[string]string{
|
| 99 |
+
"Accept": "*/*",
|
| 100 |
+
"Accept-Language": "en-US,en;q=0.9",
|
| 101 |
+
"Content-Type": "application/json",
|
| 102 |
+
"Oai-Device-Id": deviceId,
|
| 103 |
+
"Oai-Language": "en-US",
|
| 104 |
+
"Origin": "https://chatgpt.com",
|
| 105 |
+
"Referer": "https://chatgpt.com/?oai-dm=1",
|
| 106 |
+
"User-Agent": ua,
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
j, _ := json.Marshal(req)
|
| 110 |
+
|
| 111 |
+
resp, err := client.Post(url, header, j)
|
| 112 |
+
if err != nil {
|
| 113 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "fail to get chat requirements", nil)
|
| 114 |
+
return nil, err
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
if resp.StatusCode != 200 {
|
| 118 |
+
body, _ := io.ReadAll(resp.Body)
|
| 119 |
+
utils.ErrorResp(c, resp.StatusCode, string(body), nil)
|
| 120 |
+
return nil, errors.New("fail to get chat requirements")
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
defer resp.Body.Close()
|
| 124 |
+
|
| 125 |
+
var chatRequirementRes model.ChatRequirementRes
|
| 126 |
+
|
| 127 |
+
err = json.NewDecoder(resp.Body).Decode(&chatRequirementRes)
|
| 128 |
+
return &chatRequirementRes, err
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
func conversationStream(c *gin.Context, req *request.ChatRequest, resp *http.Response) {
|
| 132 |
+
messageTemp := ""
|
| 133 |
+
decoder := eventsource.NewDecoder(resp.Body)
|
| 134 |
+
defer func(decoder *eventsource.Decoder) {
|
| 135 |
+
_, _ = decoder.Decode()
|
| 136 |
+
}(decoder)
|
| 137 |
+
id := utils.GenerateID(29)
|
| 138 |
+
handlingSigns := false
|
| 139 |
+
for {
|
| 140 |
+
event, err := decoder.Decode()
|
| 141 |
+
if err != nil {
|
| 142 |
+
logger.GetLogger().Error(err.Error())
|
| 143 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "", nil)
|
| 144 |
+
break
|
| 145 |
+
}
|
| 146 |
+
name := event.Event()
|
| 147 |
+
data := event.Data()
|
| 148 |
+
if data == "" {
|
| 149 |
+
continue
|
| 150 |
+
}
|
| 151 |
+
if data == "[DONE]" {
|
| 152 |
+
result := &response.Stream{}
|
| 153 |
+
result.ID = id
|
| 154 |
+
result.Created = time.Now().Unix()
|
| 155 |
+
result.Object = "chat.completion.chunk"
|
| 156 |
+
delta := response.StreamDelta{
|
| 157 |
+
Content: "",
|
| 158 |
+
}
|
| 159 |
+
choices := response.StreamChoice{
|
| 160 |
+
Delta: delta,
|
| 161 |
+
FinishReason: "stop",
|
| 162 |
+
}
|
| 163 |
+
result.Choices = append(result.Choices, choices)
|
| 164 |
+
result.Model = req.Model
|
| 165 |
+
bytes, err := json.Marshal(result)
|
| 166 |
+
if err != nil {
|
| 167 |
+
logger.GetLogger().Error(err.Error())
|
| 168 |
+
continue
|
| 169 |
+
}
|
| 170 |
+
c.SSEvent(name, fmt.Sprint(" ", string(bytes)))
|
| 171 |
+
c.SSEvent(name, " [DONE]")
|
| 172 |
+
break
|
| 173 |
+
}
|
| 174 |
+
chatResp := &model.ChatCompletionResp{}
|
| 175 |
+
err = json.Unmarshal([]byte(data), chatResp)
|
| 176 |
+
if chatResp.Error != nil && !handlingSigns {
|
| 177 |
+
logger.GetLogger().Error(fmt.Sprint(chatResp.Error))
|
| 178 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "", nil)
|
| 179 |
+
return
|
| 180 |
+
}
|
| 181 |
+
if err != nil {
|
| 182 |
+
continue
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
if chatResp.Message.Author.Role == "assistant" && (chatResp.Message.Status == "in_progress" || handlingSigns) {
|
| 186 |
+
handlingSigns = true
|
| 187 |
+
parts := chatResp.Message.Content.Parts[0]
|
| 188 |
+
content := strings.Replace(parts, messageTemp, "", 1)
|
| 189 |
+
messageTemp = parts
|
| 190 |
+
if content == "" {
|
| 191 |
+
continue
|
| 192 |
+
}
|
| 193 |
+
apiResp := &response.Stream{}
|
| 194 |
+
apiResp.ID = id
|
| 195 |
+
apiResp.Created = time.Now().Unix()
|
| 196 |
+
apiResp.Object = "chat.completion.chunk"
|
| 197 |
+
delta := response.StreamDelta{
|
| 198 |
+
Content: content,
|
| 199 |
+
}
|
| 200 |
+
choices := response.StreamChoice{
|
| 201 |
+
Delta: delta,
|
| 202 |
+
}
|
| 203 |
+
apiResp.Choices = append(apiResp.Choices, choices)
|
| 204 |
+
apiResp.Model = req.Model
|
| 205 |
+
|
| 206 |
+
bytes, err := json.Marshal(apiResp)
|
| 207 |
+
if err != nil {
|
| 208 |
+
logger.GetLogger().Error(err.Error())
|
| 209 |
+
continue
|
| 210 |
+
}
|
| 211 |
+
c.SSEvent(name, fmt.Sprint(" ", string(bytes)))
|
| 212 |
+
continue
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
func conversation(c *gin.Context, req *request.ChatRequest, resp *http.Response) {
|
| 218 |
+
content := ""
|
| 219 |
+
decoder := eventsource.NewDecoder(resp.Body)
|
| 220 |
+
defer func(decoder *eventsource.Decoder) {
|
| 221 |
+
_, _ = decoder.Decode()
|
| 222 |
+
}(decoder)
|
| 223 |
+
|
| 224 |
+
handlingSigns := false
|
| 225 |
+
for {
|
| 226 |
+
event, err := decoder.Decode()
|
| 227 |
+
if err != nil {
|
| 228 |
+
logger.GetLogger().Error(err.Error())
|
| 229 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "", nil)
|
| 230 |
+
break
|
| 231 |
+
}
|
| 232 |
+
data := event.Data()
|
| 233 |
+
if data == "" {
|
| 234 |
+
continue
|
| 235 |
+
}
|
| 236 |
+
if data == "[DONE]" {
|
| 237 |
+
result := &response.ChatResponse{}
|
| 238 |
+
result.ID = utils.GenerateID(29)
|
| 239 |
+
result.Created = time.Now().Unix()
|
| 240 |
+
result.Object = "chat.completion"
|
| 241 |
+
result.Model = req.Model
|
| 242 |
+
usage := response.Usage{
|
| 243 |
+
PromptTokens: 0,
|
| 244 |
+
CompletionTokens: 0,
|
| 245 |
+
TotalTokens: 0,
|
| 246 |
+
}
|
| 247 |
+
result.Usage = usage
|
| 248 |
+
message := response.Message{
|
| 249 |
+
Role: "assistant",
|
| 250 |
+
Content: content,
|
| 251 |
+
}
|
| 252 |
+
choice := response.Choice{
|
| 253 |
+
Message: message,
|
| 254 |
+
FinishReason: "stop",
|
| 255 |
+
Index: 0,
|
| 256 |
+
}
|
| 257 |
+
result.Choices = append(result.Choices, choice)
|
| 258 |
+
c.JSON(http.StatusOK, result)
|
| 259 |
+
break
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
chatResp := &model.ChatCompletionResp{}
|
| 263 |
+
err = json.Unmarshal([]byte(data), chatResp)
|
| 264 |
+
if chatResp.Error != nil && !handlingSigns {
|
| 265 |
+
logger.GetLogger().Error(fmt.Sprint(chatResp.Error))
|
| 266 |
+
utils.ErrorResp(c, http.StatusInternalServerError, "", nil)
|
| 267 |
+
return
|
| 268 |
+
}
|
| 269 |
+
if err != nil {
|
| 270 |
+
continue
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
if chatResp.Message.Author.Role == "assistant" && (chatResp.Message.Status == "in_progress" || handlingSigns) {
|
| 274 |
+
handlingSigns = true
|
| 275 |
+
if !strings.Contains(chatResp.Message.Content.Parts[0], content) {
|
| 276 |
+
continue
|
| 277 |
+
}
|
| 278 |
+
content = chatResp.Message.Content.Parts[0]
|
| 279 |
+
if content == "" {
|
| 280 |
+
continue
|
| 281 |
+
}
|
| 282 |
+
continue
|
| 283 |
+
}
|
| 284 |
+
}
|
| 285 |
+
}
|
chat/proxy.go
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package chat
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/gin-gonic/gin"
|
| 5 |
+
"github.com/zbronya/free-chat-to-api/httpclient"
|
| 6 |
+
"github.com/zbronya/free-chat-to-api/utils"
|
| 7 |
+
"io"
|
| 8 |
+
"net/url"
|
| 9 |
+
"strings"
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
var client = httpclient.NewReqClient()
|
| 13 |
+
|
| 14 |
+
var headersFilterList = map[string]bool{
|
| 15 |
+
"x-real-ip": true, "x-forwarded-for": true, "x-forwarded-proto": true,
|
| 16 |
+
"x-forwarded-port": true, "x-forwarded-host": true, "x-forwarded-server": true,
|
| 17 |
+
"cf-warp-tag-id": true, "cf-visitor": true, "cf-ray": true,
|
| 18 |
+
"cf-connecting-ip": true, "cf-ipcountry": true, "cdn-loop": true,
|
| 19 |
+
"remote-host": true, "x-frame-options": true, "x-xss-protection": true,
|
| 20 |
+
"x-content-type-options": true, "content-security-policy": true,
|
| 21 |
+
"host": true, "cookie": true, "connection": true,
|
| 22 |
+
"content-length": true, "content-encoding": true,
|
| 23 |
+
"x-middleware-prefetch": true, "x-nextjs-data": true, "purpose": true,
|
| 24 |
+
"x-forwarded-uri": true, "x-forwarded-path": true,
|
| 25 |
+
"x-forwarded-method": true, "x-forwarded-protocol": true,
|
| 26 |
+
"x-forwarded-scheme": true, "cf-request-id": true,
|
| 27 |
+
"cf-worker": true, "cf-access-client-id": true,
|
| 28 |
+
"cf-access-client-device-type": true, "cf-access-client-device-model": true,
|
| 29 |
+
"cf-access-client-device-name": true, "cf-access-client-device-brand": true,
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
func ReverseProxy(c *gin.Context) {
|
| 33 |
+
var targetURL, _ = url.Parse("https://chatgpt.com")
|
| 34 |
+
targetURL.Path = c.Request.URL.Path
|
| 35 |
+
targetURL.RawQuery = c.Request.URL.RawQuery
|
| 36 |
+
|
| 37 |
+
headers := make(map[string]string)
|
| 38 |
+
for k, v := range c.Request.Header {
|
| 39 |
+
if _, found := headersFilterList[strings.ToLower(k)]; !found {
|
| 40 |
+
headers[k] = v[0]
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
headers["Host"] = targetURL.Host
|
| 45 |
+
|
| 46 |
+
body, _ := io.ReadAll(c.Request.Body)
|
| 47 |
+
resp, err := client.Post(targetURL.String(), headers, body)
|
| 48 |
+
|
| 49 |
+
if err != nil {
|
| 50 |
+
utils.ErrorResp(c, 500, "fail to proxy", err)
|
| 51 |
+
return
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
for k, v := range resp.Header {
|
| 55 |
+
c.Writer.Header()[k] = v
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
c.Writer.WriteHeader(resp.StatusCode)
|
| 59 |
+
io.Copy(c.Writer, resp.Body)
|
| 60 |
+
|
| 61 |
+
}
|
config/config.go
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package config
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/joho/godotenv"
|
| 5 |
+
"os"
|
| 6 |
+
"strings"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
var (
|
| 10 |
+
Bind string
|
| 11 |
+
Port string
|
| 12 |
+
ProxyUrl string
|
| 13 |
+
GatewayUrl string
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
func init() {
|
| 17 |
+
_ = godotenv.Load()
|
| 18 |
+
Bind = os.Getenv("BIND")
|
| 19 |
+
if Bind == "" {
|
| 20 |
+
Bind = "0.0.0.0"
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
Port = os.Getenv("PORT")
|
| 24 |
+
if Port == "" {
|
| 25 |
+
Port = "8484"
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
ProxyUrl = os.Getenv("PROXY_URL")
|
| 29 |
+
|
| 30 |
+
GatewayUrl = os.Getenv("GATEWAY_URL")
|
| 31 |
+
if GatewayUrl == "" {
|
| 32 |
+
GatewayUrl = "https://chatgpt.com"
|
| 33 |
+
} else {
|
| 34 |
+
GatewayUrl = strings.TrimRight(GatewayUrl, "/")
|
| 35 |
+
}
|
| 36 |
+
}
|
go.mod
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module github.com/zbronya/free-chat-to-api
|
| 2 |
+
|
| 3 |
+
go 1.21
|
| 4 |
+
|
| 5 |
+
require (
|
| 6 |
+
github.com/gin-gonic/gin v1.9.1
|
| 7 |
+
github.com/google/uuid v1.6.0
|
| 8 |
+
github.com/imroc/req/v3 v3.43.3
|
| 9 |
+
github.com/joho/godotenv v1.5.1
|
| 10 |
+
github.com/launchdarkly/eventsource v1.7.1
|
| 11 |
+
github.com/sirupsen/logrus v1.9.3
|
| 12 |
+
golang.org/x/crypto v0.22.0
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
require (
|
| 16 |
+
github.com/andybalholm/brotli v1.1.0 // indirect
|
| 17 |
+
github.com/bytedance/sonic v1.9.1 // indirect
|
| 18 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
| 19 |
+
github.com/cloudflare/circl v1.3.8 // indirect
|
| 20 |
+
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
| 21 |
+
github.com/gin-contrib/sse v0.1.0 // indirect
|
| 22 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
| 23 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
| 24 |
+
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
| 25 |
+
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
| 26 |
+
github.com/goccy/go-json v0.10.2 // indirect
|
| 27 |
+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
| 28 |
+
github.com/hashicorp/errwrap v1.1.0 // indirect
|
| 29 |
+
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
| 30 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
| 31 |
+
github.com/klauspost/compress v1.17.8 // indirect
|
| 32 |
+
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
| 33 |
+
github.com/kr/pretty v0.3.1 // indirect
|
| 34 |
+
github.com/leodido/go-urn v1.2.4 // indirect
|
| 35 |
+
github.com/mattn/go-isatty v0.0.19 // indirect
|
| 36 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
| 37 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
| 38 |
+
github.com/onsi/ginkgo/v2 v2.17.2 // indirect
|
| 39 |
+
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
| 40 |
+
github.com/quic-go/qpack v0.4.0 // indirect
|
| 41 |
+
github.com/quic-go/quic-go v0.43.0 // indirect
|
| 42 |
+
github.com/refraction-networking/utls v1.6.3 // indirect
|
| 43 |
+
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
| 44 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
| 45 |
+
github.com/ugorji/go/codec v1.2.11 // indirect
|
| 46 |
+
go.uber.org/mock v0.4.0 // indirect
|
| 47 |
+
golang.org/x/arch v0.3.0 // indirect
|
| 48 |
+
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
| 49 |
+
golang.org/x/mod v0.17.0 // indirect
|
| 50 |
+
golang.org/x/net v0.24.0 // indirect
|
| 51 |
+
golang.org/x/sys v0.19.0 // indirect
|
| 52 |
+
golang.org/x/text v0.14.0 // indirect
|
| 53 |
+
golang.org/x/tools v0.20.0 // indirect
|
| 54 |
+
google.golang.org/protobuf v1.33.0 // indirect
|
| 55 |
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
| 56 |
+
)
|
go.sum
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
| 2 |
+
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
| 3 |
+
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
| 4 |
+
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
| 5 |
+
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
| 6 |
+
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
| 7 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
| 8 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
| 9 |
+
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
| 10 |
+
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
| 11 |
+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
| 12 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 13 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 14 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 15 |
+
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
| 16 |
+
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
| 17 |
+
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
| 18 |
+
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
| 19 |
+
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
| 20 |
+
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
| 21 |
+
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
| 22 |
+
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
| 23 |
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
| 24 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
| 25 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
| 26 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
| 27 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
| 28 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
| 29 |
+
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
| 30 |
+
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
| 31 |
+
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
| 32 |
+
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
| 33 |
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
| 34 |
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
| 35 |
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
| 36 |
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
| 37 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 38 |
+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
| 39 |
+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
| 40 |
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
| 41 |
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
| 42 |
+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
| 43 |
+
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
| 44 |
+
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
| 45 |
+
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
| 46 |
+
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
| 47 |
+
github.com/imroc/req/v3 v3.43.3 h1:WdZhpUev9THtuwEZsW2LOYacl12fm7IkB7OgACv40+k=
|
| 48 |
+
github.com/imroc/req/v3 v3.43.3/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA=
|
| 49 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
| 50 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
| 51 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
| 52 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
| 53 |
+
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
| 54 |
+
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
| 55 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
| 56 |
+
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
| 57 |
+
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
| 58 |
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
| 59 |
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
| 60 |
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
| 61 |
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
| 62 |
+
github.com/launchdarkly/eventsource v1.7.1 h1:StoRQeiPyrcQIXjlQ7b5jWMzHW4p+GGczN2r2oBhujg=
|
| 63 |
+
github.com/launchdarkly/eventsource v1.7.1/go.mod h1:LHxSeb4OnqznNZxCSXbFghxS/CjIQfzHovNoAqbO/Wk=
|
| 64 |
+
github.com/launchdarkly/go-test-helpers/v2 v2.2.0 h1:L3kGILP/6ewikhzhdNkHy1b5y4zs50LueWenVF0sBbs=
|
| 65 |
+
github.com/launchdarkly/go-test-helpers/v2 v2.2.0/go.mod h1:L7+th5govYp5oKU9iN7To5PgznBuIjBPn+ejqKR0avw=
|
| 66 |
+
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
| 67 |
+
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
| 68 |
+
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
| 69 |
+
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
| 70 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 71 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
| 72 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 73 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
| 74 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
| 75 |
+
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
|
| 76 |
+
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
| 77 |
+
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
| 78 |
+
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
| 79 |
+
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
| 80 |
+
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
| 81 |
+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
| 82 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 83 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
| 84 |
+
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
| 85 |
+
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
| 86 |
+
github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g=
|
| 87 |
+
github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
| 88 |
+
github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc=
|
| 89 |
+
github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
|
| 90 |
+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
| 91 |
+
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
| 92 |
+
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
| 93 |
+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
| 94 |
+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
| 95 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 96 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 97 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 98 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
| 99 |
+
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
| 100 |
+
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 101 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 102 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 103 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 104 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
| 105 |
+
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
| 106 |
+
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 107 |
+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
| 108 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 109 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
| 110 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
| 111 |
+
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
| 112 |
+
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
| 113 |
+
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
| 114 |
+
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
| 115 |
+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
| 116 |
+
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
| 117 |
+
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
| 118 |
+
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
| 119 |
+
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
| 120 |
+
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
| 121 |
+
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
| 122 |
+
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
| 123 |
+
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
| 124 |
+
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
| 125 |
+
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
| 126 |
+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
| 127 |
+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
| 128 |
+
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 129 |
+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 130 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 131 |
+
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
| 132 |
+
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
| 133 |
+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
| 134 |
+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
| 135 |
+
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
| 136 |
+
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
| 137 |
+
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
| 138 |
+
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
| 139 |
+
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
| 140 |
+
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
| 141 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 142 |
+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
| 143 |
+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 144 |
+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 145 |
+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 146 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 147 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 148 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 149 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
httpclient/client.go
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package httpclient
|
| 2 |
+
|
| 3 |
+
import "net/http"
|
| 4 |
+
|
| 5 |
+
type HttpClient interface {
|
| 6 |
+
Get(url string, headers map[string]string) (*http.Response, error)
|
| 7 |
+
Post(url string, headers map[string]string, body []byte) (*http.Response, error)
|
| 8 |
+
}
|
httpclient/reqclient.go
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package httpclient
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/imroc/req/v3"
|
| 5 |
+
"github.com/zbronya/free-chat-to-api/config"
|
| 6 |
+
"net/http"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
type ReqClient struct {
|
| 10 |
+
client *req.Client
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
func NewReqClient() *ReqClient {
|
| 14 |
+
return &ReqClient{
|
| 15 |
+
client: req.C().
|
| 16 |
+
ImpersonateChrome().SetProxyURL(config.ProxyUrl),
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
func (r *ReqClient) Get(url string, headers map[string]string) (*http.Response, error) {
|
| 21 |
+
resp, err := r.client.R().
|
| 22 |
+
SetHeaders(headers).
|
| 23 |
+
Get(url)
|
| 24 |
+
|
| 25 |
+
if err != nil {
|
| 26 |
+
return nil, err
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
return resp.Response, nil
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
func (r *ReqClient) Post(url string, headers map[string]string, body []byte) (*http.Response, error) {
|
| 33 |
+
resp, err := r.client.R().
|
| 34 |
+
SetHeaders(headers).
|
| 35 |
+
SetBody(body).
|
| 36 |
+
Post(url)
|
| 37 |
+
|
| 38 |
+
if err != nil {
|
| 39 |
+
return nil, err
|
| 40 |
+
}
|
| 41 |
+
return resp.Response, nil
|
| 42 |
+
}
|
logger/logger.go
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package logger
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/sirupsen/logrus"
|
| 5 |
+
"sync"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
var (
|
| 9 |
+
once sync.Once
|
| 10 |
+
instance *LogrusLogger
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
type Logger interface {
|
| 14 |
+
Debug(args ...interface{})
|
| 15 |
+
Info(args ...interface{})
|
| 16 |
+
Warn(args ...interface{})
|
| 17 |
+
Error(args ...interface{})
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
type LogrusLogger struct {
|
| 21 |
+
*logrus.Logger
|
| 22 |
+
}
|
logger/logrus.go
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package logger
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/sirupsen/logrus"
|
| 5 |
+
"os"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
func init() {
|
| 9 |
+
once.Do(func() {
|
| 10 |
+
l := logrus.New()
|
| 11 |
+
l.SetOutput(os.Stdout)
|
| 12 |
+
l.SetLevel(logrus.DebugLevel)
|
| 13 |
+
l.SetFormatter(&logrus.TextFormatter{
|
| 14 |
+
FullTimestamp: true,
|
| 15 |
+
})
|
| 16 |
+
|
| 17 |
+
instance = &LogrusLogger{
|
| 18 |
+
Logger: l,
|
| 19 |
+
}
|
| 20 |
+
})
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
func GetLogger() Logger {
|
| 24 |
+
return instance
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
func (l *LogrusLogger) Debug(args ...interface{}) {
|
| 28 |
+
l.Logger.Debug(args...)
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
func (l *LogrusLogger) Info(args ...interface{}) {
|
| 32 |
+
l.Logger.Info(args...)
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
func (l *LogrusLogger) Warn(args ...interface{}) {
|
| 36 |
+
l.Logger.Warn(args...)
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
func (l *LogrusLogger) Error(args ...interface{}) {
|
| 40 |
+
l.Logger.Error(args...)
|
| 41 |
+
}
|
main.go
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package main
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"github.com/gin-gonic/gin"
|
| 6 |
+
"github.com/zbronya/free-chat-to-api/config"
|
| 7 |
+
"github.com/zbronya/free-chat-to-api/logger"
|
| 8 |
+
"github.com/zbronya/free-chat-to-api/router"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
func main() {
|
| 12 |
+
gin.SetMode(gin.ReleaseMode)
|
| 13 |
+
g := gin.New()
|
| 14 |
+
g.Use(gin.Recovery())
|
| 15 |
+
router.InitRouter(g)
|
| 16 |
+
host := config.Bind
|
| 17 |
+
logger.GetLogger().Info(fmt.Sprint("Server started on http://", host, ":", config.Port))
|
| 18 |
+
_ = g.Run(fmt.Sprint(config.Bind, ":", config.Port))
|
| 19 |
+
}
|
model/model.go
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package model
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/google/uuid"
|
| 5 |
+
"github.com/zbronya/free-chat-to-api/model/request"
|
| 6 |
+
"github.com/zbronya/free-chat-to-api/utils"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
type ChatRequirementReq struct {
|
| 10 |
+
P string `json:"p"`
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
type ProofWork struct {
|
| 14 |
+
Difficulty string `json:"difficulty"`
|
| 15 |
+
Required bool `json:"required"`
|
| 16 |
+
Seed string `json:"seed"`
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
type ChatRequirementRes struct {
|
| 20 |
+
Token string `json:"token"`
|
| 21 |
+
Proof ProofWork `json:"proofofwork"`
|
| 22 |
+
Arkose struct {
|
| 23 |
+
Required bool `json:"required"`
|
| 24 |
+
DX string `json:"dx"`
|
| 25 |
+
} `json:"arkose"`
|
| 26 |
+
ForceLogin bool `json:"force_login"`
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
type ChatAuthor struct {
|
| 30 |
+
Role string `json:"role"`
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
type ChatContent struct {
|
| 34 |
+
ContentType string `json:"content_type"`
|
| 35 |
+
Parts []string `json:"parts"`
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
type ChatMessages struct {
|
| 39 |
+
Author ChatAuthor `json:"author"`
|
| 40 |
+
Content ChatContent `json:"content"`
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
type ChatConversationMode struct {
|
| 44 |
+
Kind string `json:"kind"`
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
type ChatCompletionRequest struct {
|
| 48 |
+
Action string `json:"action"`
|
| 49 |
+
Messages []ChatMessages `json:"messages"`
|
| 50 |
+
ParentMessageId string `json:"parent_message_id"`
|
| 51 |
+
Model string `json:"model"`
|
| 52 |
+
TimeZoneOffsetMin int `json:"timezone_offset_min"`
|
| 53 |
+
Suggestions []string `json:"suggestions"`
|
| 54 |
+
HistoryAndTrainingDisabled bool `json:"history_and_training_disabled"`
|
| 55 |
+
ConversationMode ChatConversationMode `json:"conversation_mode"`
|
| 56 |
+
WebsocketRequestId string `json:"websocket_request_id"`
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
func ApiReqToChatReq(req *request.ChatRequest) (chatReq *ChatCompletionRequest) {
|
| 60 |
+
messages := make([]ChatMessages, 0)
|
| 61 |
+
for _, apiMessage := range req.Messages {
|
| 62 |
+
chatMessage := ChatMessages{
|
| 63 |
+
Author: ChatAuthor{
|
| 64 |
+
Role: apiMessage.Role,
|
| 65 |
+
},
|
| 66 |
+
Content: ChatContent{
|
| 67 |
+
ContentType: "text",
|
| 68 |
+
Parts: []string{apiMessage.Content},
|
| 69 |
+
},
|
| 70 |
+
}
|
| 71 |
+
messages = append(messages, chatMessage)
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
chatReq = &ChatCompletionRequest{
|
| 75 |
+
Action: "next",
|
| 76 |
+
Messages: messages,
|
| 77 |
+
ParentMessageId: uuid.New().String(),
|
| 78 |
+
Model: utils.MappingModel(req.Model),
|
| 79 |
+
TimeZoneOffsetMin: -180,
|
| 80 |
+
Suggestions: make([]string, 0),
|
| 81 |
+
HistoryAndTrainingDisabled: true,
|
| 82 |
+
ConversationMode: ChatConversationMode{
|
| 83 |
+
Kind: "primary_assistant",
|
| 84 |
+
},
|
| 85 |
+
WebsocketRequestId: uuid.New().String(),
|
| 86 |
+
}
|
| 87 |
+
return chatReq
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
type ChatCompletionResp struct {
|
| 91 |
+
Message struct {
|
| 92 |
+
Id string `json:"id"`
|
| 93 |
+
Author struct {
|
| 94 |
+
Role string `json:"role"`
|
| 95 |
+
Name interface{} `json:"name"`
|
| 96 |
+
Metadata struct {
|
| 97 |
+
} `json:"metadata"`
|
| 98 |
+
} `json:"author"`
|
| 99 |
+
CreateTime float64 `json:"create_time"`
|
| 100 |
+
UpdateTime interface{} `json:"update_time"`
|
| 101 |
+
Content struct {
|
| 102 |
+
ContentType string `json:"content_type"`
|
| 103 |
+
Parts []string `json:"parts"`
|
| 104 |
+
} `json:"content"`
|
| 105 |
+
Status string `json:"status"`
|
| 106 |
+
EndTurn interface{} `json:"end_turn"`
|
| 107 |
+
Weight float64 `json:"weight"`
|
| 108 |
+
Metadata struct {
|
| 109 |
+
Citations []interface{} `json:"citations"`
|
| 110 |
+
GizmoId interface{} `json:"gizmo_id"`
|
| 111 |
+
MessageType string `json:"message_type"`
|
| 112 |
+
ModelSlug string `json:"model_slug"`
|
| 113 |
+
DefaultModelSlug string `json:"default_model_slug"`
|
| 114 |
+
Pad string `json:"pad"`
|
| 115 |
+
ParentId string `json:"parent_id"`
|
| 116 |
+
} `json:"metadata"`
|
| 117 |
+
Recipient string `json:"recipient"`
|
| 118 |
+
} `json:"message"`
|
| 119 |
+
ConversationId string `json:"conversation_id"`
|
| 120 |
+
Error interface{} `json:"error"`
|
| 121 |
+
}
|
model/request/request.go
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package request
|
| 2 |
+
|
| 3 |
+
type ChatMessage struct {
|
| 4 |
+
Role string `json:"role"`
|
| 5 |
+
Content string `json:"content"`
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
type ChatRequest struct {
|
| 9 |
+
Messages []ChatMessage `json:"messages"`
|
| 10 |
+
Model string `json:"model"`
|
| 11 |
+
Stream bool `json:"stream"`
|
| 12 |
+
}
|
model/response/response.go
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package response
|
| 2 |
+
|
| 3 |
+
type ChatResponse struct {
|
| 4 |
+
ID string `json:"id"`
|
| 5 |
+
Object string `json:"object"`
|
| 6 |
+
Created int64 `json:"created"`
|
| 7 |
+
Model string `json:"model"`
|
| 8 |
+
Usage Usage `json:"usage"`
|
| 9 |
+
Choices []Choice `json:"choices"`
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
type Message struct {
|
| 13 |
+
Role string `json:"role"`
|
| 14 |
+
Content string `json:"content"`
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type Choice struct {
|
| 18 |
+
Message Message `json:"message"`
|
| 19 |
+
FinishReason string `json:"finish_reason"`
|
| 20 |
+
Index int `json:"index"`
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
type Usage struct {
|
| 24 |
+
PromptTokens int `json:"prompt_tokens"`
|
| 25 |
+
CompletionTokens int `json:"completion_tokens"`
|
| 26 |
+
TotalTokens int `json:"total_tokens"`
|
| 27 |
+
}
|
model/response/stream.go
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package response
|
| 2 |
+
|
| 3 |
+
type Stream struct {
|
| 4 |
+
ID string `json:"id"`
|
| 5 |
+
Object string `json:"object"`
|
| 6 |
+
Created int64 `json:"created"`
|
| 7 |
+
Model string `json:"model"`
|
| 8 |
+
Choices []StreamChoice `json:"choices"`
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
type StreamChoice struct {
|
| 12 |
+
Delta StreamDelta `json:"delta"`
|
| 13 |
+
Index int `json:"index"`
|
| 14 |
+
FinishReason string `json:"finish_reason"`
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
type StreamDelta struct {
|
| 18 |
+
Content string `json:"content"`
|
| 19 |
+
}
|
proofofwork/proofofwork.go
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package proofofwork
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"encoding/base64"
|
| 5 |
+
"encoding/hex"
|
| 6 |
+
"encoding/json"
|
| 7 |
+
"github.com/google/uuid"
|
| 8 |
+
"github.com/zbronya/free-chat-to-api/model"
|
| 9 |
+
"golang.org/x/crypto/sha3"
|
| 10 |
+
"math/rand"
|
| 11 |
+
"time"
|
| 12 |
+
_ "time/tzdata"
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
var (
|
| 16 |
+
cores = []int{1, 2, 4, 8}
|
| 17 |
+
screens = []int{3000, 4000, 6000}
|
| 18 |
+
script = "https://cdn.oaistatic.com/_next/static/chunks/2565-9cf19ba0b7d24a5d.js?dpl=4811fd1c94b550c8f03fcc863ee6c1a99940efc5"
|
| 19 |
+
|
| 20 |
+
dpl = "4811fd1c94b550c8f03fcc863ee6c1a99940efc5"
|
| 21 |
+
|
| 22 |
+
errorPrefix = "gAAAAABwQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D"
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
func getParseTime() string {
|
| 26 |
+
loc, _ := time.LoadLocation("America/Los_Angeles")
|
| 27 |
+
now := time.Now().In(loc)
|
| 28 |
+
return now.Format("Mon Jan 02 2006 15:04:05") + " GMT-0800 (Pacific Time)"
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
func GetConfig(ua string) []interface{} {
|
| 32 |
+
rand.New(rand.NewSource(time.Now().UnixNano()))
|
| 33 |
+
core := cores[rand.Intn(4)]
|
| 34 |
+
rand.New(rand.NewSource(time.Now().UnixNano()))
|
| 35 |
+
screen := screens[rand.Intn(3)]
|
| 36 |
+
rand.New(rand.NewSource(time.Now().UnixNano()))
|
| 37 |
+
return []interface{}{core + screen,
|
| 38 |
+
getParseTime(),
|
| 39 |
+
int64(4294705152),
|
| 40 |
+
0,
|
| 41 |
+
ua,
|
| 42 |
+
script,
|
| 43 |
+
dpl,
|
| 44 |
+
"en-US",
|
| 45 |
+
"en-US,en",
|
| 46 |
+
0,
|
| 47 |
+
"updateAdInterestGroups−function updateAdInterestGroups() { [native code] }",
|
| 48 |
+
"location",
|
| 49 |
+
"__NEXT_PRELOADREADY",
|
| 50 |
+
885.6999999880791,
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
func GetChatRequirementReq(config []interface{}) model.ChatRequirementReq {
|
| 55 |
+
//randomFloat := rand.Float64()
|
| 56 |
+
//seed := fmt.Sprintf("%.6f", randomFloat)
|
| 57 |
+
//token := CalcProofToken(config, seed, "0")
|
| 58 |
+
return model.ChatRequirementReq{
|
| 59 |
+
P: "hello openai" + uuid.NewString(),
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
func CalcProofToken(config []interface{}, seed string, diff string) string {
|
| 65 |
+
|
| 66 |
+
diffLen := len(diff) / 2
|
| 67 |
+
hasher := sha3.New512()
|
| 68 |
+
startTime := time.Now()
|
| 69 |
+
for i := 0; i < 1000000; i++ {
|
| 70 |
+
config[3] = i
|
| 71 |
+
endTime := time.Now()
|
| 72 |
+
elapsed := endTime.Sub(startTime)
|
| 73 |
+
config[9] = elapsed.Milliseconds()
|
| 74 |
+
j, _ := json.Marshal(config)
|
| 75 |
+
base := base64.StdEncoding.EncodeToString(j)
|
| 76 |
+
hasher.Write([]byte(seed + base))
|
| 77 |
+
hash := hasher.Sum(nil)
|
| 78 |
+
hasher.Reset()
|
| 79 |
+
if hex.EncodeToString(hash[:diffLen]) <= diff {
|
| 80 |
+
return base
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
return errorPrefix + base64.StdEncoding.EncodeToString([]byte(`"`+seed+`"`))
|
| 84 |
+
}
|
router/router.go
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package router
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"github.com/gin-gonic/gin"
|
| 6 |
+
"github.com/zbronya/free-chat-to-api/chat"
|
| 7 |
+
"github.com/zbronya/free-chat-to-api/logger"
|
| 8 |
+
"net/http"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
func Cors(c *gin.Context) {
|
| 12 |
+
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
| 13 |
+
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
| 14 |
+
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
| 15 |
+
c.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept")
|
| 16 |
+
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
| 17 |
+
if c.Request.Method == "OPTIONS" {
|
| 18 |
+
c.AbortWithStatus(204)
|
| 19 |
+
return
|
| 20 |
+
}
|
| 21 |
+
c.Next()
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
func RequestLogger(c *gin.Context) {
|
| 25 |
+
c.Next()
|
| 26 |
+
|
| 27 |
+
logger.GetLogger().Info(fmt.Sprintf("IP: %s, Method: %s, UserAgent: %s, URL: %s, Status: %d",
|
| 28 |
+
c.Request.RemoteAddr,
|
| 29 |
+
c.Request.Method,
|
| 30 |
+
c.Request.UserAgent(),
|
| 31 |
+
c.Request.URL.Path,
|
| 32 |
+
c.Writer.Status()))
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
func InitRouter(g *gin.Engine) {
|
| 36 |
+
g.GET("/", func(c *gin.Context) {
|
| 37 |
+
c.String(http.StatusOK,
|
| 38 |
+
"This is Free Chat To API Service \n"+
|
| 39 |
+
"https://github.com/zbronya/free-chat-to-api")
|
| 40 |
+
})
|
| 41 |
+
|
| 42 |
+
g.GET("/ping", func(c *gin.Context) {
|
| 43 |
+
c.JSON(200, gin.H{
|
| 44 |
+
"message": "pong",
|
| 45 |
+
})
|
| 46 |
+
})
|
| 47 |
+
|
| 48 |
+
g.Use(Cors)
|
| 49 |
+
g.Use(RequestLogger)
|
| 50 |
+
g.OPTIONS("/v1/chat/completions")
|
| 51 |
+
g.POST("/v1/chat/completions", chat.Completions)
|
| 52 |
+
g.POST("/backend-anon/sentinel/chat-requirements", chat.ReverseProxy)
|
| 53 |
+
g.POST("/backend-anon/conversation", chat.ReverseProxy)
|
| 54 |
+
}
|
utils/util.go
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package utils
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"github.com/gin-gonic/gin"
|
| 5 |
+
"math/rand"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
func MappingModel(model string) string {
|
| 9 |
+
var modelMapping = map[string]string{
|
| 10 |
+
"gpt-3.5-turbo": "text-davinci-002-render-sha",
|
| 11 |
+
"gpt-3.5-turbo-16k": "text-davinci-002-render-sha",
|
| 12 |
+
"gpt-3.5-turbo-16k-0613": "text-davinci-002-render-sha",
|
| 13 |
+
"gpt-3.5-turbo-0301": "text-davinci-002-render-sha",
|
| 14 |
+
"gpt-3.5-turbo-0613": "text-davinci-002-render-sha",
|
| 15 |
+
"gpt-3.5-turbo-1106": "text-davinci-002-render-sha",
|
| 16 |
+
}
|
| 17 |
+
if model == "" {
|
| 18 |
+
return "text-davinci-002-render-sha"
|
| 19 |
+
}
|
| 20 |
+
if v, ok := modelMapping[model]; ok {
|
| 21 |
+
return v
|
| 22 |
+
}
|
| 23 |
+
return "text-davinci-002-render-sha"
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
func GenerateID(length int) string {
|
| 27 |
+
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
| 28 |
+
id := "chatcmpl-"
|
| 29 |
+
for i := 0; i < length; i++ {
|
| 30 |
+
id += string(charset[rand.Intn(len(charset))])
|
| 31 |
+
}
|
| 32 |
+
return id
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
func ErrorResp(c *gin.Context, code int, msg interface{}, err interface{}) {
|
| 36 |
+
c.AbortWithStatusJSON(code, gin.H{
|
| 37 |
+
"code": code,
|
| 38 |
+
"msg": msg,
|
| 39 |
+
"error": err,
|
| 40 |
+
})
|
| 41 |
+
return
|
| 42 |
+
}
|