Skip to content
This repository has been archived by the owner on Mar 20, 2024. It is now read-only.

Commit

Permalink
from goland to vscode
Browse files Browse the repository at this point in the history
  • Loading branch information
linweiyuan committed Sep 25, 2023
1 parent a9ad29c commit 1d8c5db
Show file tree
Hide file tree
Showing 30 changed files with 139 additions and 323 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ PORT=8080
# Network proxy server address
PROXY=socks5://ip:port
# Imitate access_token
IMITATE_ACCESS_TOKEN=
IMITATE_ACCESS_TOKEN=
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.idea
.env
go-chatgpt-api
undetected_chromedriver
example/idea/http-client.private.env.json
__debug*
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ COPY --from=builder /app/go-chatgpt-api .
RUN apk add --no-cache tzdata
ENV TZ=Asia/Shanghai
EXPOSE 8080
CMD ["/app/go-chatgpt-api"]
CMD ["/app/go-chatgpt-api"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
- https://platform.openai.com/playground 登录返回 `apiKey`
- `apiKey` 余额查询
- 等等 ...
- 支持 `ChatGPT``API`,接口 `/imitate/v1/chat/completions`,利用 `accessToken` 模拟 `apiKey`,实现伪免费使用 `API`
,从而支持集成仅支持 `apiKey` 调用的第三方客户端项目,分享一个好用的脚本测试 `web-to-api` (https://github.com/linweiyuan/go-chatgpt-api/issues/251)
- 支持 `ChatGPT``API`,接口 `/imitate/v1/chat/completions`,利用 `accessToken` 模拟 `apiKey`,实现伪免费使用 `API`,从而支持集成仅支持 `apiKey` 调用的第三方客户端项目,分享一个好用的脚本测试 `web-to-api` (https://github.com/linweiyuan/go-chatgpt-api/issues/251)

```python
import openai
Expand Down Expand Up @@ -48,8 +47,7 @@ https://github.com/linweiyuan/go-chatgpt-api/tree/main/example

汇总贴:https://github.com/linweiyuan/go-chatgpt-api/issues/74

如果有疑问而不是什么程序出错其实可以在 [Discussions](https://github.com/linweiyuan/go-chatgpt-api/discussions) 里发而不是新增
Issue
如果有疑问而不是什么程序出错其实可以在 [Discussions](https://github.com/linweiyuan/go-chatgpt-api/discussions) 里发而不是新增 Issue

群聊:https://github.com/linweiyuan/go-chatgpt-api/discussions/197

Expand All @@ -59,18 +57,15 @@ Issue

### 配置

如需设置代理,可以设置环境变量 `PROXY`,比如 `PROXY=http://127.0.0.1:20171`
或者 `PROXY=socks5://127.0.0.1:20170`,注释掉或者留空则不启用
如需设置代理,可以设置环境变量 `PROXY`,比如 `PROXY=http://127.0.0.1:20171` 或者 `PROXY=socks5://127.0.0.1:20170`,注释掉或者留空则不启用

如果代理需账号密码验证,则 `http://username:password@ip:port` 或者 `socks5://username:password@ip:port`

如需配合 `warp` 使用:`PROXY=socks5://chatgpt-proxy-server-warp:65535`,因为需要设置 `warp`
的场景已经默认可以直接访问 `ChatGPT` 官网,因此共用一个变量不冲突(国内 `VPS` 不在讨论范围内,请自行配置网络环境,`warp`
服务在魔法环境下才能正常工作)
如需配合 `warp` 使用:`PROXY=socks5://chatgpt-proxy-server-warp:65535`,因为需要设置 `warp` 的场景已经默认可以直接访问 `ChatGPT` 官网,因此共用一个变量不冲突(国内 `VPS` 不在讨论范围内,请自行配置网络环境,`warp` 服务在魔法环境下才能正常工作)

家庭网络无需跑 `warp` 服务,跑了也没用,会报错,仅在服务器需要

`CONTINUE_SIGNAL=1`,开启/imitate接口自动继续会话功能,留空关闭,默认关闭
`CONTINUE_SIGNAL=1`,开启 `/imitate` 接口自动继续会话功能,留空关闭,默认关闭

---

Expand All @@ -89,7 +84,6 @@ Issue
服务器不定时维护,不保证高可用,利用这些服务导致的账号安全问题,与本项目无关

- https://go-chatgpt-api.linweiyuan.com
- https://api.tms.im

</details>

Expand All @@ -98,6 +92,7 @@ Issue
<summary>网络在直连或者通过代理的情况下可以正常访问 ChatGPT</summary>

```yaml
services:
go-chatgpt-api:
container_name: go-chatgpt-api
image: linweiyuan/go-chatgpt-api
Expand All @@ -115,6 +110,7 @@ Issue
<summary>服务器无法正常访问 ChatGPT</summary>
```yaml
services:
go-chatgpt-api:
container_name: go-chatgpt-api
image: linweiyuan/go-chatgpt-api
Expand All @@ -137,9 +133,7 @@ Issue
---
目前 `warp` 容器检测到流量超过 1G 会自动重启,如果你知道什么是 `teams-enroll-token`
(不知道就跳过),可以通过环境变量 `TEAMS_ENROLL_TOKEN`
设置它的值,然后利用这条命令来检查是否生效
目前 `warp` 容器检测到流量超过 1G 会自动重启,如果你知道什么是 `teams-enroll-token` (不知道就跳过),可以通过环境变量 `TEAMS_ENROLL_TOKEN` 设置它的值,然后利用这条命令来检查是否生效

`docker-compose exec chatgpt-proxy-server-warp warp-cli --accept-tos account | awk 'NR==1'`

Expand Down Expand Up @@ -229,13 +223,10 @@ BASE_URL=http://go-chatgpt-api:8080/imitate

### 最后感谢各位同学

<!--suppress HtmlRequiredAltAttribute -->
<a href="https://github.com/linweiyuan/go-chatgpt-api/graphs/contributors">
<img src="https://contrib.rocks/image?repo=linweiyuan/go-chatgpt-api" alt=""/>
</a>

Made with [contrib.rocks](https://contrib.rocks).

---

![](https://linweiyuan.github.io/about/mm_reward_qrcode.png)
![](https://linweiyuan.github.io/about/mm_reward_qrcode.png)
19 changes: 7 additions & 12 deletions api/chatgpt/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ import (
"strings"

"github.com/PuerkitoBio/goquery"
"github.com/linweiyuan/go-chatgpt-api/api"

http "github.com/bogdanfinn/fhttp"

"github.com/linweiyuan/go-chatgpt-api/api"
)

//goland:noinspection GoUnhandledErrorResult,GoErrorStringFormat
func (userLogin *UserLogin) GetAuthorizedUrl(csrfToken string) (string, int, error) {
form := url.Values{
"callbackUrl": {"/"},
"csrfToken": {csrfToken},
"json": {"true"},
}
req, err := http.NewRequest(http.MethodPost, promptLoginUrl, strings.NewReader(form.Encode()))
req, _ := http.NewRequest(http.MethodPost, promptLoginUrl, strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", api.ContentType)
req.Header.Set("User-Agent", api.UserAgent)
resp, err := userLogin.client.Do(req)
Expand All @@ -38,9 +37,8 @@ func (userLogin *UserLogin) GetAuthorizedUrl(csrfToken string) (string, int, err
return responseMap["url"], http.StatusOK, nil
}

//goland:noinspection GoUnhandledErrorResult,GoErrorStringFormat
func (userLogin *UserLogin) GetState(authorizedUrl string) (string, int, error) {
req, err := http.NewRequest(http.MethodGet, authorizedUrl, nil)
req, _ := http.NewRequest(http.MethodGet, authorizedUrl, nil)
req.Header.Set("Content-Type", api.ContentType)
req.Header.Set("User-Agent", api.UserAgent)
resp, err := userLogin.client.Do(req)
Expand All @@ -58,7 +56,6 @@ func (userLogin *UserLogin) GetState(authorizedUrl string) (string, int, error)
return state, http.StatusOK, nil
}

//goland:noinspection GoUnhandledErrorResult,GoErrorStringFormat
func (userLogin *UserLogin) CheckUsername(state string, username string) (int, error) {
formParams := url.Values{
"state": {state},
Expand All @@ -85,15 +82,14 @@ func (userLogin *UserLogin) CheckUsername(state string, username string) (int, e
return http.StatusOK, nil
}

//goland:noinspection GoUnhandledErrorResult,GoErrorStringFormat
func (userLogin *UserLogin) CheckPassword(state string, username string, password string) (string, int, error) {
formParams := url.Values{
"state": {state},
"username": {username},
"password": {password},
"action": {"default"},
}
req, err := http.NewRequest(http.MethodPost, api.LoginPasswordUrl+state, strings.NewReader(formParams.Encode()))
req, _ := http.NewRequest(http.MethodPost, api.LoginPasswordUrl+state, strings.NewReader(formParams.Encode()))
req.Header.Set("Content-Type", api.ContentType)
req.Header.Set("User-Agent", api.UserAgent)
userLogin.client.SetFollowRedirect(false)
Expand Down Expand Up @@ -125,7 +121,7 @@ func (userLogin *UserLogin) CheckPassword(state string, username string, passwor
if resp.StatusCode == http.StatusFound {
location := resp.Header.Get("Location")
if strings.HasPrefix(location, "/u/mfa-otp-challenge") {
return "", http.StatusBadRequest, errors.New("Login with two-factor authentication enabled is not supported currently.")
return "", http.StatusBadRequest, errors.New("login with two-factor authentication enabled is not supported currently")
}

req, _ := http.NewRequest(http.MethodGet, location, nil)
Expand Down Expand Up @@ -156,9 +152,8 @@ func (userLogin *UserLogin) CheckPassword(state string, username string, passwor
return "", resp.StatusCode, nil
}

//goland:noinspection GoUnhandledErrorResult,GoErrorStringFormat,GoUnusedParameter
func (userLogin *UserLogin) GetAccessToken(code string) (string, int, error) {
req, err := http.NewRequest(http.MethodGet, authSessionUrl, nil)
req, _ := http.NewRequest(http.MethodGet, authSessionUrl, nil)
req.Header.Set("User-Agent", api.UserAgent)
resp, err := userLogin.client.Do(req)
if err != nil {
Expand Down
91 changes: 6 additions & 85 deletions api/chatgpt/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,21 @@ import (
"strings"
"time"

http "github.com/bogdanfinn/fhttp"
"github.com/gin-gonic/gin"

"github.com/linweiyuan/go-chatgpt-api/api"
"github.com/linweiyuan/go-logger/logger"
"github.com/xqdoo00o/funcaptcha"

http "github.com/bogdanfinn/fhttp"
)

//goland:noinspection SpellCheckingInspection
var (
arkoseTokenUrl string
PUID string
bx string
PUID string
)

//goland:noinspection SpellCheckingInspection,GoUnhandledErrorResult
func init() {
arkoseTokenUrl = os.Getenv("ARKOSE_TOKEN_URL")

bx = os.Getenv("BX")
if bx != "" {
// to fix first time 403
funcaptcha.GetOpenAITokenWithBx(bx, "", "")
} else {
bxUrl := os.Getenv("BX_URL")
if bxUrl != "" {
go getBX(bxUrl)
} else {
// to fix first time 403
funcaptcha.GetOpenAIToken("", "")
}
}

setupPUID()
}

//goland:noinspection GoUnhandledErrorResult
func CreateConversation(c *gin.Context) {
var request CreateConversationRequest
if err := c.BindJSON(&request); err != nil {
Expand All @@ -64,12 +42,10 @@ func CreateConversation(c *gin.Context) {
}
}

logger.Info(request.Model)

if strings.HasPrefix(request.Model, gpt4Model) && request.ArkoseToken == "" {
arkoseToken, err := GetArkoseToken()
arkoseToken, err := api.GetArkoseToken()
if err != nil || arkoseToken == "" {
c.AbortWithStatusJSON(http.StatusForbidden, api.ReturnMessage(api.GetArkoseTokenErrorMessage))
c.AbortWithStatusJSON(http.StatusForbidden, api.ReturnMessage(err.Error()))
return
}

Expand All @@ -84,15 +60,13 @@ func CreateConversation(c *gin.Context) {
handleConversationResponse(c, resp, request)
}

//goland:noinspection GoUnhandledErrorResult
func sendConversationRequest(c *gin.Context, request CreateConversationRequest) (*http.Response, bool) {
jsonBytes, _ := json.Marshal(request)
req, _ := http.NewRequest(http.MethodPost, api.ChatGPTApiUrlPrefix+"/backend-api/conversation", bytes.NewBuffer(jsonBytes))
req.Header.Set("User-Agent", api.UserAgent)
req.Header.Set(api.AuthorizationHeader, api.GetAccessToken(c))
req.Header.Set("Accept", "text/event-stream")
if PUID != "" {
//goland:noinspection SpellCheckingInspection
req.Header.Set("Cookie", "_puid="+PUID)
}
resp, err := api.Client.Do(req)
Expand Down Expand Up @@ -148,7 +122,6 @@ func sendConversationRequest(c *gin.Context, request CreateConversationRequest)
return resp, false
}

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

Expand Down Expand Up @@ -217,7 +190,6 @@ func handleConversationResponse(c *gin.Context, resp *http.Response, request Cre
}
}

//goland:noinspection SpellCheckingInspection,GoUnhandledErrorResult
func setupPUID() {
username := os.Getenv("OPENAI_EMAIL")
password := os.Getenv("OPENAI_PASSWORD")
Expand Down Expand Up @@ -245,7 +217,7 @@ func setupPUID() {
req, _ := http.NewRequest(http.MethodGet, api.ChatGPTApiUrlPrefix+"/backend-api/models?history_and_training_disabled=false", nil)
req.Header.Set("User-Agent", api.UserAgent)
req.Header.Set(api.AuthorizationHeader, accessToken)
resp, err := api.Client.Do(req)
resp, err := api.NewHttpClient().Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
logger.Error(refreshPuidErrorMessage)
return
Expand All @@ -265,54 +237,3 @@ func setupPUID() {
}()
}
}

//goland:noinspection GoUnhandledErrorResult
func GetArkoseToken() (string, error) {
if arkoseTokenUrl == "" {
return funcaptcha.GetOpenAIToken("", "")
}

req, _ := http.NewRequest(http.MethodGet, arkoseTokenUrl, nil)
resp, err := api.ArkoseClient.Do(req)
if err != nil {
return "", err
}

if resp.StatusCode != http.StatusOK {
return "", err
}

defer resp.Body.Close()
responseMap := make(map[string]interface{})
json.NewDecoder(resp.Body).Decode(&responseMap)
arkoseToken, ok := responseMap["token"]
if !ok || arkoseToken == "" {
return "", nil
}

return arkoseToken.(string), nil
}

//goland:noinspection GoUnhandledErrorResult
func getBX(url string) {
// refresh bx hourly
for {
req, _ := http.NewRequest(http.MethodGet, url, nil)
resp, err := api.ArkoseClient.Do(req)
if err != nil {
return
}

responseMap := make(map[string]interface{})
json.NewDecoder(resp.Body).Decode(&responseMap)
resp.Body.Close()

data, _ := json.Marshal(responseMap["bx"])
bx = string(data)

// fix 403
funcaptcha.GetOpenAITokenWithBx(bx, "", "")

time.Sleep(time.Hour)
}
}
9 changes: 4 additions & 5 deletions api/chatgpt/constant.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package chatgpt

//goland:noinspection SpellCheckingInspection
const (
defaultRole = "user"
parseJsonErrorMessage = "Failed to parse json request body."
parseJsonErrorMessage = "failed to parse json request body"

csrfUrl = "https://chat.openai.com/api/auth/csrf"
promptLoginUrl = "https://chat.openai.com/api/auth/signin/auth0?prompt=login"
getCsrfTokenErrorMessage = "Failed to get CSRF token."
getCsrfTokenErrorMessage = "failed to get CSRF token"
authSessionUrl = "https://chat.openai.com/api/auth/session"

gpt4Model = "gpt-4"
actionContinue = "continue"
responseTypeMaxTokens = "max_tokens"
responseStatusFinishedSuccessfully = "finished_successfully"
noModelPermissionErrorMessage = "You have no permission to use this model, maybe you Plus has expired, or this model is temporary disabled, or the arkoseToken is invalid."
noModelPermissionErrorMessage = "you have no permission to use this model"

refreshPuidErrorMessage = "Failed to refresh PUID."
refreshPuidErrorMessage = "failed to refresh PUID"
)
Loading

0 comments on commit 1d8c5db

Please sign in to comment.