diff --git a/.env.example b/.env.example index dc7ee0c..98172a4 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ API_PREFIX=your_prefix -AUTHORIZATION=your_first_key, your_second_key -CHATGPT_BASE_URL=https://chat.openai.com +CHATGPT_BASE_URL=https://chatgpt.com HISTORY_DISABLED=true PROXY_URL=your_first_proxy, your_second_proxy ARKOSE_TOKEN_URL=https://arkose.example.com/token +POW_DIFFICULTY=4 RETRY_TIMES=3 diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index 39af2bd..767b3e2 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -35,7 +35,7 @@ jobs: images: lanqian528/chat2api tags: | type=raw,value=latest,enable={{is_default_branch}} - type=raw,value=v1.0.0 + type=raw,value=v1.0.1 - name: Build and push uses: docker/build-push-action@v5 diff --git a/README.md b/README.md index c784862..ab3d321 100644 --- a/README.md +++ b/README.md @@ -4,36 +4,26 @@ 🌟 无需账号即可使用免费、无限的 `GPT-3.5` -💥 支持AccessToken使用账号,支持 `GPT-4`、`GPT-4o` +💥 支持AccessToken使用账号,支持 `GPT-4`、`GPT-4o`、 `GPTs` 🔍 回复格式与真实api完全一致,适配几乎所有客户端 ## 交流群 -https://t.me/chat2api +https://t.me/chat2api 要提问请先阅读完仓库介绍 ## 功能 > 已完成 -> - [x] 免登录 GPT3.5 -> - [x] 使用 AccessToken -> - [x] GPT3.5 对话 (传入模型名不包含gpt-4,则默认使用gpt-3.5,也就是text-davinci-002-render-sha) -> - [x] GPT4.0 对话 (传入模型名包含: gpt-4,gpt-4o,gpt-4-moblie 即可使用对应模型) -> - [x] Tokens 计算 -> - [x] Stream 流式传输 -> - [x] 配置 PROXY 代理 -> - [x] 配置 BASE_URL -> - [x] 外置 ArkoseToken -> - [x] 使用 RefreshToken 代替 AccessToken -> - [x] 反向代理 UI (http://127.0.0.1:5005, 不支持登录使用) -> - [x] GPT4.0 画图、工具 (beta) -> - [x] 支持 WSS (beta) -> - [x] 返回 conversation_id (beta) +> - [x] 免登录 GPT3.5 对话 +> - [x] GPT-3.5 对话 (传入模型名不包含gpt-4,则默认使用gpt-3.5,也就是text-davinci-002-render-sha) +> - [x] GPT-4 对话 (传入模型名包含: gpt-4,gpt-4o,gpt-4-moblie 即可使用对应模型, 需传入AccessToken) +> - [x] GPT-4 画图、代码、联网 > - [x] 支持GPTs (传入模型名:gpt-4-gizmo-g-*) > - [x] 上传图片、文件 (格式为API对应格式,支持url和base64) +> - [x] 反向代理 UI (http://127.0.0.1:5005, 不支持登录使用) > TODO -> - [ ] claude2api > - [ ] 暂无,欢迎提 issue ## 部署 @@ -91,28 +81,24 @@ docker-compose up -d http://127.0.0.1:5005 ``` -- 使用 API , 支持传入 AccessToken 或 RefreshToken, 可用 GPT4.0: +- 使用 API , 支持传入 AccessToken 或 RefreshToken, 可用 GPT-4, GPT-4o, GPTs: ```bash curl --location 'http://127.0.0.1:5005/v1/chat/completions' \ --header 'Content-Type: application/json' \ +--header 'Authorization: Bearer {{Token}}' \ --data '{ "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Say this is a test!"}], "stream": true }' ``` +> `Token` 处填写你账号的 `AccessToken` 或 `RefreshToken` +> - `AccessToken` 获取: chatgpt官网登录后,再打开 https://chatgpt.com/api/auth/session 获取 `accessToken` 这个值 +> - 免登录 gpt3.5 无需传入 Token -## 常见问题 -> - 错误代码: -> - `401`:当前IP不支持免登录,请尝试更换IP地址,或者在环境变量 `PROXY_URL` 中设置代理。 -> - `403`:当前IP地址被 CF 盾拦截,请尝试更换IP地址,或者在环境变量 `PROXY_URL` 中设置代理。 -> - `429`:当前IP请求1小时内请求超过限制,请稍后再试,或更换ip。 -> - `500`:服务器内部错误,请求失败。 -> - `502`:服务器网关错误,或网络不可用,请尝试更换网络环境。 - -## 使用GPT4 +## ArkoseToken > #### 目前支持外部服务提供 ArkoseToken > @@ -127,34 +113,50 @@ curl --location 'http://127.0.0.1:5005/v1/chat/completions' \ - 请求体: ```request body -{ - "blob": "rFYaxQNEApDlx/Db.KyrE79pAAFBs70CYtbM4pMNUsc7jIkLGdiDs7vziHRGe78bqWXDo0AYyq2A10qIlcTt89lBYXJqCbONC/nD8C199pEZ/c9ocVKKtM27jZQ7fyOpWd9p5qjKeXT4xEGBFpoE3Re1DwdQeijYp7VMJQyw7RYN+IDB1QEx3aKSO6aTI+ivnhw9ztfn/p1SkvAyyOhur/ArF08WQ+rXQpxpttaSQlzMsIwlYbuUUuYE2f9JrQaYG7qip1DKvju111P6wTNy4QVlMXG32VrzaOWh4nmQ0lOcZ1DmN6u2aeJZotffHV2zOOQAqqnParidTbN+qFre2t77ZwBuGKGqLyT8LeOp02GdFwcyw0kkeX+L7vwYAzBpjA5ky0r0X+i8HpzWt8QCyWzEW9kHn9LLCTwg2MOumzjb66Ad4WDe+C1bAcOKuEyXiYh+a1cWZAOdzEuxEg90yCfI7DZR94BsoDR85gEC/Og88i098u5HV7hZZEOQ6J8fmi68FSyPkN7oLCmBsZCMAZqzapNP/MkeIMExrdw7Jf/PtMrZN4bwM56mWfyIJf5h/zXu8PUajVwE9Pj/M5VtB0spZg49JNeHExosVCAB0C0JW+T8vEIwoqiY4pRQ0lbMHTQZFpU2xURTgcgh+m6g1SEYR1FY3de1XnzfiTQq1RTNJPydj5xpt6r6okr8yIJdRhmVXlQI+pS7vi3+Lls2hnpr7L+l1mcUIMPZNBCs3AUFJNpp6SwQjZkPvKggg1p+uS6PdvKRizM9O9+FKc103AhuSia8KTrvU8tWhBhCzIHCD4LNfnkjuBWSdbDttva4AEXUoPuKkQCWaBzq4lQPUIHFOM9HmNe738vVkNdAuOYffxDNegcpIxLVgZGfbgLQ=" -} +{"blob": "rFYaxQNEApDlx/Db.KyrE79pAAFBs70CYtbM4pMNUsc7jIkLGdiDs7vziHRGe78bqWXDo0AYyq2A10qIlcTt89lBYXJqCbONC/nD8C199pEZ/c9ocVKKtM27jZQ7fyOpWd9p5qjKeXT4xEGBFpoE3Re1DwdQeijYp7VMJQyw7RYN+IDB1QEx3aKSO6aTI+ivnhw9ztfn/p1SkvAyyOhur/ArF08WQ+rXQpxpttaSQlzMsIwlYbuUUuYE2f9JrQaYG7qip1DKvju111P6wTNy4QVlMXG32VrzaOWh4nmQ0lOcZ1DmN6u2aeJZotffHV2zOOQAqqnParidTbN+qFre2t77ZwBuGKGqLyT8LeOp02GdFwcyw0kkeX+L7vwYAzBpjA5ky0r0X+i8HpzWt8QCyWzEW9kHn9LLCTwg2MOumzjb66Ad4WDe+C1bAcOKuEyXiYh+a1cWZAOdzEuxEg90yCfI7DZR94BsoDR85gEC/Og88i098u5HV7hZZEOQ6J8fmi68FSyPkN7oLCmBsZCMAZqzapNP/MkeIMExrdw7Jf/PtMrZN4bwM56mWfyIJf5h/zXu8PUajVwE9Pj/M5VtB0spZg49JNeHExosVCAB0C0JW+T8vEIwoqiY4pRQ0lbMHTQZFpU2xURTgcgh+m6g1SEYR1FY3de1XnzfiTQq1RTNJPydj5xpt6r6okr8yIJdRhmVXlQI+pS7vi3+Lls2hnpr7L+l1mcUIMPZNBCs3AUFJNpp6SwQjZkPvKggg1p+uS6PdvKRizM9O9+FKc103AhuSia8KTrvU8tWhBhCzIHCD4LNfnkjuBWSdbDttva4AEXUoPuKkQCWaBzq4lQPUIHFOM9HmNe738vVkNdAuOYffxDNegcpIxLVgZGfbgLQ="} ``` - 响应体: ```response body -{ - "token": "45017c7bb17115f36.7290869304|r=ap-southeast-1|meta=3|metabgclr=transparent|metaiconclr=%23757575|guitextcolor=%23000000|pk=0A1D34FC-659D-4E23-B17B-694DCFCF6A6C|at=40|sup=1|rid=3|ag=101|cdn_url=https%3A%2F%2Ftcr9i.openai.com%2Fcdn%2Ffc|lurl=https%3A%2F%2Faudio-ap-southeast-1.arkoselabs.com|surl=https%3A%2F%2Ftcr9i.openai.com|smurl=https%3A%2F%2Ftcr9i.openai.com%2Fcdn%2Ffc%2Fassets%2Fstyle-manager" -} +{"token": "45017c7bb17115f36.7290869304|r=ap-southeast-1|meta=3|metabgclr=transparent|metaiconclr=%23757575|guitextcolor=%23000000|pk=0A1D34FC-659D-4E23-B17B-694DCFCF6A6C|at=40|sup=1|rid=3|ag=101|cdn_url=https%3A%2F%2Ftcr9i.openai.com%2Fcdn%2Ffc|lurl=https%3A%2F%2Faudio-ap-southeast-1.arkoselabs.com|surl=https%3A%2F%2Ftcr9i.openai.com|smurl=https%3A%2F%2Ftcr9i.openai.com%2Fcdn%2Ffc%2Fassets%2Fstyle-manager"} ``` -## 高级设置 -默认情况都不需要设置,除非你有需求 -### 环境变量 +## 常见问题 -每个环境变量都有默认值,如果不懂环境变量的含义,请不要设置 +> - 错误代码: +> - `401`:当前IP不支持免登录,请尝试更换IP地址,或者在环境变量 `PROXY_URL` 中设置代理。 +> - `403`:请在日志中查看具体报错信息 +> - `429`:当前IP请求1小时内请求超过限制,请稍后再试,或更换ip。 +> - `500`:服务器内部错误,请求失败。 +> - `502`:服务器网关错误,或网络不可用,请尝试更换网络环境。 + +> - 已知情况: +> - 日本IP很多不支持免登,免登3.5建议使用美国IP +> - 99%的账号都支持免费 `GPT-4o` ,但根据IP地区开启,目前日本和新加坡IP已知开启概率较大 + +> - AccessToken 如何获取? +> - chatgpt官网登录后,再打开 https://chatgpt.com/api/auth/session 获取 `accessToken` 这个值 +> - PLUS账号报错`403`? +> - PLUS账号需要配置 `ArkoseToken`,请根据上文进行配置 +> - ArkoseToken 是什么,怎么获取? +> - 请参考上文的说明,更多请参考 https://www.arkoselabs.com/ + + + +## 环境变量 + +每个环境变量都有默认值,如果不懂环境变量的含义,请不要设置,更不要传空值 ``` -API_PREFIX=your_prefix // API前缀,设置后需请求 http://127.0.0.1:5005/your_prefix/v1/chat/completions -AUTHORIZATION=your_first_key, your_second_key // 使用免登3.5的Bearer token,不设置则无需Bearer token (不是 AccessToken) -CHATGPT_BASE_URL=https://chat.openai.com // ChatGPT网关地址,设置后会改变请求的网站,多个网关用逗号分隔 +API_PREFIX=your_prefix // API前缀,设置后需请求 /your_prefix/v1/chat/completions +CHATGPT_BASE_URL=https://chatgpt.com // ChatGPT网关地址,设置后会改变请求的网站,多个网关用逗号分隔 HISTORY_DISABLED=true // 是否不保存聊天记录并返回 conversation_id,true为不保存且不返回 PROXY_URL=your_first_proxy, your_second_proxy // 代理url,多个代理用逗号分隔 ARKOSE_TOKEN_URL=https://arkose.example.com/token // 获取Arkose token的地址,上文有提供说明 +POW_DIFFICULTY=4 // 要解决的工作量证明难度,数值越大,计算时间越长,建议3或者4 RETRY_TIMES=3 // 出错重试次数 ``` diff --git a/app.py b/app.py index a053901..1af80b1 100644 --- a/app.py +++ b/app.py @@ -36,15 +36,12 @@ async def to_send_conversation(request_data, access_token): @app.post(f"/{api_prefix}/v1/chat/completions" if api_prefix else "/v1/chat/completions") async def send_conversation(request: Request, token=Depends(verify_token)): - access_token = None - if token and (token.startswith("eyJhbGciOi") or token.startswith("fk-")): - access_token = token try: request_data = await request.json() except Exception: raise HTTPException(status_code=400, detail={"error": "Invalid JSON body"}) - chat_service = await async_retry(to_send_conversation, request_data, access_token) + chat_service = await async_retry(to_send_conversation, request_data, token) try: await chat_service.prepare_send_conversation() res = await chat_service.send_conversation() diff --git a/chatgpt/ChatService.py b/chatgpt/ChatService.py index 780bf84..082b59a 100644 --- a/chatgpt/ChatService.py +++ b/chatgpt/ChatService.py @@ -51,7 +51,6 @@ class ChatService: self.chat_headers = None self.chat_request = None - self.s.session.cookies.set("__Secure-next-auth.callback-url", "https%3A%2F%2Fchatgpt.com;", secure=True) self.base_headers = { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br, zstd', @@ -76,6 +75,9 @@ class ChatService: else: self.base_url = self.host_url + "/backend-anon" + await get_dpl(self) + self.s.session.cookies.set("__Secure-next-auth.callback-url", "https%3A%2F%2Fchatgpt.com;", domain=self.host_url.split("://")[1], secure=True) + async def get_wss_url(self): url = f'{self.base_url}/register-websocket' headers = self.base_headers.copy() @@ -95,7 +97,6 @@ class ChatService: url = f'{self.base_url}/sentinel/chat-requirements' headers = self.base_headers.copy() try: - await get_dpl(self) config = get_config(self.user_agent) data = {'p': get_requirements_token(config)} r = await self.s.post(url, headers=headers, json=data, timeout=5) diff --git a/chatgpt/proofofWork.py b/chatgpt/proofofWork.py index c466615..0c6d94c 100644 --- a/chatgpt/proofofWork.py +++ b/chatgpt/proofofWork.py @@ -294,7 +294,7 @@ async def get_dpl(service): return True headers = service.base_headers.copy() try: - r = await service.s.get(f"{service.host_url}/?oai-dm=1", headers=headers) + r = await service.s.get(f"{service.host_url}/?oai-dm=1", headers=headers, timeout=5) r.raise_for_status() parser = ScriptSrcParser() parser.feed(r.text) diff --git a/utils/authorization.py b/utils/authorization.py index 7d70769..ccd240d 100644 --- a/utils/authorization.py +++ b/utils/authorization.py @@ -20,6 +20,6 @@ async def verify_token(token: str = Depends(oauth2_scheme)): elif not authorization_list: return token elif token and token in authorization_list: - return token + return None else: raise HTTPException(status_code=401) diff --git a/utils/config.py b/utils/config.py index 91abe6e..8921ddd 100644 --- a/utils/config.py +++ b/utils/config.py @@ -23,7 +23,7 @@ arkose_token_url = os.getenv('ARKOSE_TOKEN_URL', '').replace(' ', '') proxy_url = os.getenv('PROXY_URL', '').replace(' ', '') history_disabled_str = os.getenv('HISTORY_DISABLED', 'true').replace(' ', '') history_disabled = is_true(history_disabled_str) -pow_difficulty = int(os.getenv('POW_DIFFICULTY', 3)) +pow_difficulty = int(os.getenv('POW_DIFFICULTY', 4)) retry_times = int(os.getenv('RETRY_TIMES', 3)) authorization_list = authorization.split(',') if authorization else [] @@ -32,7 +32,7 @@ arkose_token_url_list = arkose_token_url.split(',') if arkose_token_url else [] proxy_url_list = proxy_url.split(',') if proxy_url else [] logger.info("-" * 60) -logger.info("Chat2Api v1.0.0 | https://github.com/lanqian528/chat2api") +logger.info("Chat2Api v1.0.1 | https://github.com/lanqian528/chat2api") logger.info("-" * 60) logger.info("Environment variables:") logger.info("API_PREFIX: " + str(api_prefix))