mirror of
https://github.com/lanqian528/chat2api.git
synced 2026-06-13 21:02:46 +08:00
commit
dcec8be538
12
.env.example
12
.env.example
@ -1,14 +1,4 @@
|
||||
API_PREFIX=your_prefix
|
||||
CHATGPT_BASE_URL=https://chatgpt.com
|
||||
HISTORY_DISABLED=true
|
||||
PROXY_URL=your_first_proxy, your_second_proxy
|
||||
EXPORT_PROXY_URL=your_export_proxy
|
||||
ARK0SE_TOKEN_URL=https://ark0se.example.com/token
|
||||
POW_DIFFICULTY=000032
|
||||
RETRY_TIMES=3
|
||||
ENABLE_GATEWAY=true
|
||||
CONVERSATION_ONLY=false
|
||||
ENABLE_LIMIT=true
|
||||
UPLOAD_BY_URL=false
|
||||
SCHEDULED_REFRESH=false
|
||||
USER_AGENTS=["ua1", "ua2"]
|
||||
SCHEDULED_REFRESH=false
|
||||
@ -140,6 +140,7 @@ curl --location 'http://127.0.0.1:5005/v1/chat/completions' \
|
||||
| | SCHEDULED_REFRESH | `false` | `false` | 是否定时刷新 `AccessToken` ,开启后每次启动程序将会全部非强制刷新一次,每4天晚上3点全部强制刷新一次。 |
|
||||
| | RANDOM_TOKEN | `true` | `true` | 是否随机选取后台 `Token` ,开启后随机后台账号,关闭后为顺序轮询 |
|
||||
| 网关功能 | ENABLE_GATEWAY | `false` | `false` | 是否启用网关模式,开启后可以使用镜像站,但也将会不设防 |
|
||||
| | AUTO_SEED | `false` | `true` | 是否启用随机账号模式,默认启用,输入`seed`后随机匹配后台`Token`。关闭之后需要手动对接接口,来进行`Token`管控。 |
|
||||
|
||||
## 部署
|
||||
|
||||
|
||||
122
api/chat2api.py
Normal file
122
api/chat2api.py
Normal file
@ -0,0 +1,122 @@
|
||||
import asyncio
|
||||
import types
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from fastapi import Request, Depends, HTTPException, Form
|
||||
from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
import utils.globals as globals
|
||||
from app import templates, oauth2_scheme, app
|
||||
from chatgpt.ChatService import ChatService
|
||||
from chatgpt.authorization import refresh_all_tokens
|
||||
from utils.Logger import logger
|
||||
from utils.configs import api_prefix, scheduled_refresh
|
||||
from utils.retry import async_retry
|
||||
|
||||
scheduler = AsyncIOScheduler()
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def app_start():
|
||||
if scheduled_refresh:
|
||||
scheduler.add_job(id='refresh', func=refresh_all_tokens, trigger='cron', hour=3, minute=0, day='*/2',
|
||||
kwargs={'force_refresh': True})
|
||||
scheduler.start()
|
||||
asyncio.get_event_loop().call_later(0, lambda: asyncio.create_task(refresh_all_tokens(force_refresh=False)))
|
||||
|
||||
|
||||
async def to_send_conversation(request_data, req_token):
|
||||
chat_service = ChatService(req_token)
|
||||
try:
|
||||
await chat_service.set_dynamic_data(request_data)
|
||||
await chat_service.get_chat_requirements()
|
||||
return chat_service
|
||||
except HTTPException as e:
|
||||
await chat_service.close_client()
|
||||
raise HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
await chat_service.close_client()
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
|
||||
|
||||
async def process(request_data, req_token):
|
||||
chat_service = await to_send_conversation(request_data, req_token)
|
||||
await chat_service.prepare_send_conversation()
|
||||
res = await chat_service.send_conversation()
|
||||
return chat_service, res
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/v1/chat/completions" if api_prefix else "/v1/chat/completions")
|
||||
async def send_conversation(request: Request, req_token: str = Depends(oauth2_scheme)):
|
||||
try:
|
||||
request_data = await request.json()
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail={"error": "Invalid JSON body"})
|
||||
chat_service, res = await async_retry(process, request_data, req_token)
|
||||
try:
|
||||
if isinstance(res, types.AsyncGeneratorType):
|
||||
background = BackgroundTask(chat_service.close_client)
|
||||
return StreamingResponse(res, media_type="text/event-stream", background=background)
|
||||
else:
|
||||
background = BackgroundTask(chat_service.close_client)
|
||||
return JSONResponse(res, media_type="application/json", background=background)
|
||||
except HTTPException as e:
|
||||
await chat_service.close_client()
|
||||
if e.status_code == 500:
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
raise HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
await chat_service.close_client()
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
|
||||
|
||||
@app.get(f"/{api_prefix}/tokens" if api_prefix else "/tokens", response_class=HTMLResponse)
|
||||
async def upload_html(request: Request):
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return templates.TemplateResponse("tokens.html",
|
||||
{"request": request, "api_prefix": api_prefix, "tokens_count": tokens_count})
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/upload" if api_prefix else "/tokens/upload")
|
||||
async def upload_post(text: str = Form(...)):
|
||||
lines = text.split("\n")
|
||||
for line in lines:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
globals.token_list.append(line.strip())
|
||||
with open("../data/token.txt", "a", encoding="utf-8") as f:
|
||||
f.write(line.strip() + "\n")
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/clear" if api_prefix else "/tokens/clear")
|
||||
async def upload_post():
|
||||
globals.token_list.clear()
|
||||
globals.error_token_list.clear()
|
||||
with open("../data/token.txt", "w", encoding="utf-8") as f:
|
||||
pass
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/error" if api_prefix else "/tokens/error")
|
||||
async def error_tokens():
|
||||
error_tokens_list = list(set(globals.error_token_list))
|
||||
return {"status": "success", "error_tokens": error_tokens_list}
|
||||
|
||||
|
||||
@app.get(f"/{api_prefix}/tokens/add/{{token}}" if api_prefix else "/tokens/add/{token}")
|
||||
async def add_token(token: str):
|
||||
if token.strip() and not token.startswith("#"):
|
||||
globals.token_list.append(token.strip())
|
||||
with open("../data/token.txt", "a", encoding="utf-8") as f:
|
||||
f.write(token.strip() + "\n")
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
@ -4,7 +4,7 @@ import pybase64
|
||||
from PIL import Image
|
||||
|
||||
from utils.Client import Client
|
||||
from utils.config import export_proxy_url, cf_file_url
|
||||
from utils.configs import export_proxy_url, cf_file_url
|
||||
|
||||
|
||||
async def get_file_content(url):
|
||||
|
||||
31
app.py
31
app.py
@ -1,4 +1,13 @@
|
||||
import warnings
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
log_config = uvicorn.config.LOGGING_CONFIG
|
||||
default_format = "%(asctime)s | %(levelname)s | %(message)s"
|
||||
@ -6,5 +15,23 @@ access_format = r'%(asctime)s | %(levelname)s | %(client_addr)s: %(request_line)
|
||||
log_config["formatters"]["default"]["fmt"] = default_format
|
||||
log_config["formatters"]["access"]["fmt"] = access_format
|
||||
|
||||
uvicorn.run("chat2api:app", host="0.0.0.0", port=5005)
|
||||
# uvicorn.run("chat2api:app", host="0.0.0.0", port=5005, ssl_keyfile="key.pem", ssl_certfile="cert.pem")
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)
|
||||
|
||||
import gateway.backend
|
||||
import gateway.share
|
||||
import api.chat2api
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("app:app", host="0.0.0.0", port=5005)
|
||||
# uvicorn.run("app:app", host="0.0.0.0", port=5005, ssl_keyfile="key.pem", ssl_certfile="cert.pem")
|
||||
|
||||
254
chat2api.py
254
chat2api.py
@ -1,254 +0,0 @@
|
||||
import asyncio
|
||||
import time
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from fastapi import FastAPI, Request, Depends, HTTPException, Form
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from starlette.background import BackgroundTask
|
||||
from starlette.responses import RedirectResponse, Response
|
||||
|
||||
from chatgpt.ChatService import ChatService
|
||||
from chatgpt.authorization import refresh_all_tokens, verify_token, get_req_token
|
||||
import chatgpt.globals as globals
|
||||
from chatgpt.reverseProxy import chatgpt_reverse_proxy
|
||||
from utils.Logger import logger
|
||||
from utils.config import api_prefix, scheduled_refresh, enable_gateway
|
||||
from utils.retry import async_retry
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
app = FastAPI()
|
||||
scheduler = AsyncIOScheduler()
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def app_start():
|
||||
if scheduled_refresh:
|
||||
scheduler.add_job(id='refresh', func=refresh_all_tokens, trigger='cron', hour=3, minute=0, day='*/4',
|
||||
kwargs={'force_refresh': True})
|
||||
scheduler.start()
|
||||
asyncio.get_event_loop().call_later(0, lambda: asyncio.create_task(refresh_all_tokens(force_refresh=False)))
|
||||
|
||||
|
||||
async def to_send_conversation(request_data, req_token):
|
||||
chat_service = ChatService(req_token)
|
||||
try:
|
||||
await chat_service.set_dynamic_data(request_data)
|
||||
await chat_service.get_chat_requirements()
|
||||
return chat_service
|
||||
except HTTPException as e:
|
||||
await chat_service.close_client()
|
||||
raise HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
await chat_service.close_client()
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
|
||||
|
||||
async def process(request_data, req_token):
|
||||
chat_service = await to_send_conversation(request_data, req_token)
|
||||
await chat_service.prepare_send_conversation()
|
||||
res = await chat_service.send_conversation()
|
||||
return chat_service, res
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/v1/chat/completions" if api_prefix else "/v1/chat/completions")
|
||||
async def send_conversation(request: Request, req_token: str = Depends(oauth2_scheme)):
|
||||
try:
|
||||
request_data = await request.json()
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail={"error": "Invalid JSON body"})
|
||||
chat_service, res = await async_retry(process, request_data, req_token)
|
||||
try:
|
||||
if isinstance(res, types.AsyncGeneratorType):
|
||||
background = BackgroundTask(chat_service.close_client)
|
||||
return StreamingResponse(res, media_type="text/event-stream", background=background)
|
||||
else:
|
||||
background = BackgroundTask(chat_service.close_client)
|
||||
return JSONResponse(res, media_type="application/json", background=background)
|
||||
except HTTPException as e:
|
||||
await chat_service.close_client()
|
||||
if e.status_code == 500:
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
raise HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
await chat_service.close_client()
|
||||
logger.error(f"Server error, {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Server error")
|
||||
|
||||
|
||||
@app.get(f"/{api_prefix}/tokens" if api_prefix else "/tokens", response_class=HTMLResponse)
|
||||
async def upload_html(request: Request):
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return templates.TemplateResponse("tokens.html",
|
||||
{"request": request, "api_prefix": api_prefix, "tokens_count": tokens_count})
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/upload" if api_prefix else "/tokens/upload")
|
||||
async def upload_post(text: str = Form(...)):
|
||||
lines = text.split("\n")
|
||||
for line in lines:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
globals.token_list.append(line.strip())
|
||||
with open("data/token.txt", "a", encoding="utf-8") as f:
|
||||
f.write(line.strip() + "\n")
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/clear" if api_prefix else "/tokens/clear")
|
||||
async def upload_post():
|
||||
globals.token_list.clear()
|
||||
globals.error_token_list.clear()
|
||||
with open("data/token.txt", "w", encoding="utf-8") as f:
|
||||
pass
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
|
||||
|
||||
@app.post(f"/{api_prefix}/tokens/error" if api_prefix else "/tokens/error")
|
||||
async def error_tokens():
|
||||
error_tokens_list = list(set(globals.error_token_list))
|
||||
return {"status": "success", "error_tokens": error_tokens_list}
|
||||
|
||||
|
||||
@app.get(f"/{api_prefix}/tokens/add/{{token}}" if api_prefix else "/tokens/add/{token}")
|
||||
async def add_token(token: str):
|
||||
if token.strip() and not token.startswith("#"):
|
||||
globals.token_list.append(token.strip())
|
||||
with open("data/token.txt", "a", encoding="utf-8") as f:
|
||||
f.write(token.strip() + "\n")
|
||||
logger.info(f"Token count: {len(globals.token_list)}, Error token count: {len(globals.error_token_list)}")
|
||||
tokens_count = len(set(globals.token_list) - set(globals.error_token_list))
|
||||
return {"status": "success", "tokens_count": tokens_count}
|
||||
|
||||
|
||||
if enable_gateway:
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def chatgpt_html(request: Request):
|
||||
token = request.query_params.get("token")
|
||||
if not token:
|
||||
token = request.cookies.get("token")
|
||||
if not token:
|
||||
return await login_html(request)
|
||||
|
||||
response = templates.TemplateResponse("chatgpt.html", {"request": request, "token": token})
|
||||
response.set_cookie("token", value=token)
|
||||
return response
|
||||
|
||||
@app.get("/login", response_class=HTMLResponse)
|
||||
async def login_html(request: Request):
|
||||
response = templates.TemplateResponse("login.html", {"request": request})
|
||||
return response
|
||||
|
||||
|
||||
@app.get("/backend-api/gizmos/bootstrap")
|
||||
async def get_gizmos_bootstrap():
|
||||
return {"gizmos": []}
|
||||
|
||||
|
||||
# @app.get("/backend-api/conversations")
|
||||
# async def get_conversations():
|
||||
# return {"items": [], "total": 0, "limit": 28, "offset": 0, "has_missing_conversations": False}
|
||||
|
||||
# @app.patch("/backend-api/conversations")
|
||||
# async def get_conversations():
|
||||
# return {"success": True, "message": None}
|
||||
|
||||
|
||||
@app.get("/backend-api/me")
|
||||
async def get_me(request: Request):
|
||||
me = {
|
||||
"object": "user",
|
||||
"id": "org-chatgpt",
|
||||
"email": "chatgpt@openai.com",
|
||||
"name": "ChatGPT",
|
||||
"picture": "https://cdn.auth0.com/avatars/ai.png",
|
||||
"created": int(time.time()),
|
||||
"phone_number": None,
|
||||
"mfa_flag_enabled": False,
|
||||
"amr": [],
|
||||
"groups": [],
|
||||
"orgs": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "organization",
|
||||
"id": "org-chatgpt",
|
||||
"created": 1715641300,
|
||||
"title": "Personal",
|
||||
"name": "user-chatgpt",
|
||||
"description": "Personal org for chatgpt@openai.com",
|
||||
"personal": True,
|
||||
"settings": {},
|
||||
"parent_org_id": None,
|
||||
"is_default": False,
|
||||
"role": "owner",
|
||||
"is_scale_tier_authorized_purchaser": None,
|
||||
"is_scim_managed": False,
|
||||
"projects": {
|
||||
"object": "list",
|
||||
"data": []
|
||||
},
|
||||
"groups": [],
|
||||
"geography": None
|
||||
}
|
||||
]
|
||||
},
|
||||
"has_payg_project_spend_limit": None
|
||||
}
|
||||
return me
|
||||
|
||||
|
||||
banned_paths = [
|
||||
"backend-api/accounts/logout_all",
|
||||
"backend-api/accounts/deactivate",
|
||||
"backend-api/user_system_messages",
|
||||
"backend-api/memories",
|
||||
"backend-api/settings/clear_account_user_memory"
|
||||
]
|
||||
redirect_paths = ["auth/logout"]
|
||||
chatgpt_paths = ["c/"]
|
||||
|
||||
|
||||
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"])
|
||||
async def reverse_proxy(request: Request, path: str):
|
||||
for chatgpt_path in chatgpt_paths:
|
||||
if chatgpt_path in path:
|
||||
return await chatgpt_html(request)
|
||||
|
||||
for banned_path in banned_paths:
|
||||
if banned_path in path:
|
||||
return Response(status_code=404)
|
||||
|
||||
for redirect_path in redirect_paths:
|
||||
if redirect_path in path:
|
||||
redirect_url = str(request.base_url)
|
||||
response = RedirectResponse(url=f"{redirect_url}", status_code=302)
|
||||
response.delete_cookie("token")
|
||||
return response
|
||||
|
||||
return await chatgpt_reverse_proxy(request, path)
|
||||
else:
|
||||
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"])
|
||||
async def reverse_proxy():
|
||||
raise HTTPException(status_code=404, detail="Gateway is disabled")
|
||||
@ -8,14 +8,14 @@ from starlette.concurrency import run_in_threadpool
|
||||
|
||||
from api.files import get_image_size, get_file_extension, determine_file_use_case
|
||||
from api.models import model_proxy
|
||||
from chatgpt.authorization import get_req_token, verify_token, get_ua
|
||||
from chatgpt.authorization import get_req_token, verify_token, get_fp
|
||||
from chatgpt.chatFormat import api_messages_to_chat, stream_response, format_not_stream_response, head_process_response
|
||||
from chatgpt.chatLimit import check_is_limit, handle_request_limit
|
||||
from chatgpt.proofofWork import get_config, get_dpl, get_answer_token, get_requirements_token
|
||||
|
||||
from utils.Client import Client
|
||||
from utils.Logger import logger
|
||||
from utils.config import (
|
||||
from utils.configs import (
|
||||
proxy_url_list,
|
||||
chatgpt_base_url_list,
|
||||
ark0se_token_url_list,
|
||||
@ -26,7 +26,6 @@ from utils.config import (
|
||||
upload_by_url,
|
||||
check_model,
|
||||
auth_key,
|
||||
user_agents_list,
|
||||
turnstile_solver_url,
|
||||
)
|
||||
|
||||
@ -35,20 +34,12 @@ class ChatService:
|
||||
def __init__(self, origin_token=None):
|
||||
# self.user_agent = random.choice(user_agents_list) if user_agents_list else "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
|
||||
self.req_token = get_req_token(origin_token)
|
||||
self.ua = get_ua(self.req_token)
|
||||
self.user_agent = self.ua.get(
|
||||
"user-agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
|
||||
)
|
||||
self.chat_token = "gAAAAAB"
|
||||
self.s = None
|
||||
self.ws = None
|
||||
|
||||
async def set_dynamic_data(self, data):
|
||||
if self.req_token:
|
||||
logger.info(f"Request impersonate: {self.ua.get('impersonate')}")
|
||||
logger.info(f"Request ua:{self.user_agent}")
|
||||
logger.info(f"Request token: {self.req_token}")
|
||||
req_len = len(self.req_token.split(","))
|
||||
if req_len == 1:
|
||||
self.access_token = await verify_token(self.req_token)
|
||||
@ -61,6 +52,15 @@ class ChatService:
|
||||
self.access_token = None
|
||||
self.account_id = None
|
||||
|
||||
self.fp = get_fp(self.req_token)
|
||||
self.proxy_url = self.fp.get("proxy_url")
|
||||
self.user_agent = self.fp.get("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")
|
||||
self.impersonate = self.fp.get("impersonate", "safari15_3")
|
||||
logger.info(f"Request token: {self.req_token}")
|
||||
logger.info(f"Request proxy: {self.proxy_url}")
|
||||
logger.info(f"Request UA: {self.user_agent}")
|
||||
logger.info(f"Request impersonate: {self.impersonate}")
|
||||
|
||||
self.data = data
|
||||
await self.set_model()
|
||||
if enable_limit and self.req_token:
|
||||
@ -79,11 +79,12 @@ class ChatService:
|
||||
if not isinstance(self.max_tokens, int):
|
||||
self.max_tokens = 2147483647
|
||||
|
||||
self.proxy_url = random.choice(proxy_url_list) if proxy_url_list else None
|
||||
# self.proxy_url = random.choice(proxy_url_list) if proxy_url_list else None
|
||||
|
||||
self.host_url = random.choice(chatgpt_base_url_list) if chatgpt_base_url_list else "https://chatgpt.com"
|
||||
self.ark0se_token_url = random.choice(ark0se_token_url_list) if ark0se_token_url_list else None
|
||||
|
||||
self.s = Client(proxy=self.proxy_url, impersonate=self.ua.get("impersonate", "safari15_3"))
|
||||
self.s = Client(proxy=self.proxy_url, impersonate=self.impersonate)
|
||||
|
||||
self.oai_device_id = str(uuid.uuid4())
|
||||
self.persona = None
|
||||
@ -104,9 +105,9 @@ class ChatService:
|
||||
'origin': self.host_url,
|
||||
'priority': 'u=1, i',
|
||||
'referer': f'{self.host_url}/',
|
||||
'sec-ch-ua': self.ua.get("sec-ch-ua", '"Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"'),
|
||||
'sec-ch-ua-mobile': self.ua.get("sec-ch-ua-mobile", "?0"),
|
||||
'sec-ch-ua-platform': self.ua.get("sec-ch-ua-platform", '"Windows"'),
|
||||
'sec-ch-ua': self.fp.get("sec-ch-ua", '"Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"'),
|
||||
'sec-ch-ua-mobile': self.fp.get("sec-ch-ua-mobile", "?0"),
|
||||
'sec-ch-ua-platform': self.fp.get("sec-ch-ua-platform", '"Windows"'),
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
@ -114,9 +115,9 @@ class ChatService:
|
||||
}
|
||||
if self.access_token:
|
||||
self.base_url = self.host_url + "/backend-api"
|
||||
self.base_headers['Authorization'] = f'Bearer {self.access_token}'
|
||||
self.base_headers['authorization'] = f'Bearer {self.access_token}'
|
||||
if self.account_id:
|
||||
self.base_headers['Chatgpt-Account-Id'] = self.account_id
|
||||
self.base_headers['chatgpt-account-id'] = self.account_id
|
||||
else:
|
||||
self.base_url = self.host_url + "/backend-anon"
|
||||
|
||||
@ -224,7 +225,7 @@ class ChatService:
|
||||
if not self.ark0se_token_url:
|
||||
raise HTTPException(status_code=403, detail="Ark0se service required")
|
||||
ark0se_dx = ark0se.get("dx")
|
||||
ark0se_client = Client(impersonate=self.ua.get("impersonate", "safari15_3"))
|
||||
ark0se_client = Client(impersonate=self.fp.get("impersonate", "safari15_3"))
|
||||
try:
|
||||
r2 = await ark0se_client.post(
|
||||
url=self.ark0se_token_url, json={"blob": ark0se_dx, "method": ark0se_method}, timeout=15
|
||||
@ -382,7 +383,7 @@ class ChatService:
|
||||
url = f"{self.base_url}/files/{file_id}/download"
|
||||
headers = self.base_headers.copy()
|
||||
try:
|
||||
r = await self.s.get(url, headers=headers, timeout=5)
|
||||
r = await self.s.get(url, headers=headers, timeout=10)
|
||||
if r.status_code == 200:
|
||||
download_url = r.json().get('download_url')
|
||||
return download_url
|
||||
@ -396,7 +397,7 @@ class ChatService:
|
||||
url = f"{self.base_url}/files/{file_id}/uploaded"
|
||||
headers = self.base_headers.copy()
|
||||
try:
|
||||
r = await self.s.post(url, headers=headers, json={}, timeout=30)
|
||||
r = await self.s.post(url, headers=headers, json={}, timeout=10)
|
||||
if r.status_code == 200:
|
||||
download_url = r.json().get('download_url')
|
||||
return download_url
|
||||
@ -413,7 +414,7 @@ class ChatService:
|
||||
r = await self.s.post(
|
||||
url,
|
||||
headers=headers,
|
||||
json={"file_name": file_name, "file_size": file_size, "timezone_offset_min": -480, "use_case": use_case},
|
||||
json={"file_name": file_name, "file_size": file_size, "reset_rate_limits": False, "timezone_offset_min": -480, "use_case": use_case},
|
||||
timeout=5,
|
||||
)
|
||||
if r.status_code == 200:
|
||||
@ -438,7 +439,9 @@ class ChatService:
|
||||
'x-ms-version': '2020-04-08',
|
||||
}
|
||||
)
|
||||
headers.pop('Authorization', None)
|
||||
headers.pop('authorization', None)
|
||||
headers.pop('oai-device-id', None)
|
||||
headers.pop('oai-language', None)
|
||||
try:
|
||||
r = await self.s.put(upload_url, headers=headers, data=file_content, timeout=60)
|
||||
if r.status_code == 201:
|
||||
|
||||
@ -1,77 +1,97 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
import ua_generator
|
||||
from ua_generator.options import Options
|
||||
from ua_generator.data.version import VersionRange
|
||||
from fastapi import HTTPException
|
||||
|
||||
import chatgpt.globals as globals
|
||||
import utils.configs as configs
|
||||
import utils.globals as globals
|
||||
from chatgpt.refreshToken import rt2ac
|
||||
from utils.Logger import logger
|
||||
from utils.config import authorization_list, random_token
|
||||
|
||||
os.environ['PYTHONHASHSEED'] = '0'
|
||||
random.seed(0)
|
||||
|
||||
|
||||
def get_req_token(req_token, seed=None):
|
||||
available_token_list = list(set(globals.token_list) - set(globals.error_token_list))
|
||||
length = len(available_token_list)
|
||||
if seed and length > 0:
|
||||
req_token = globals.token_list[hash(seed) % length]
|
||||
while req_token in globals.error_token_list:
|
||||
req_token = random.choice(globals.token_list)
|
||||
return req_token
|
||||
|
||||
if req_token in authorization_list:
|
||||
if len(available_token_list) > 0:
|
||||
if random_token:
|
||||
req_token = random.choice(available_token_list)
|
||||
return req_token
|
||||
if configs.auto_seed or not seed:
|
||||
available_token_list = list(set(globals.token_list) - set(globals.error_token_list))
|
||||
length = len(available_token_list)
|
||||
if seed and length > 0:
|
||||
if seed not in globals.seed_map.keys():
|
||||
globals.seed_map[seed] = {"token": random.choice(available_token_list), "conversations": []}
|
||||
with open(globals.SEED_MAP_FILE, "w") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
else:
|
||||
globals.count += 1
|
||||
globals.count %= length
|
||||
return available_token_list[globals.count]
|
||||
req_token = globals.seed_map[seed]["token"]
|
||||
return req_token
|
||||
|
||||
if req_token in configs.authorization_list:
|
||||
if len(available_token_list) > 0:
|
||||
if configs.random_token:
|
||||
req_token = random.choice(available_token_list)
|
||||
return req_token
|
||||
else:
|
||||
globals.count += 1
|
||||
globals.count %= length
|
||||
return available_token_list[globals.count]
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
return req_token
|
||||
else:
|
||||
return req_token
|
||||
if seed not in globals.seed_map.keys():
|
||||
raise HTTPException(status_code=401, detail={"error": "Invalid Seed"})
|
||||
return globals.seed_map[seed]["token"]
|
||||
|
||||
|
||||
def get_ua(req_token):
|
||||
user_agent = globals.user_agent_map.get(req_token, {})
|
||||
user_agent = {k.lower(): v for k, v in user_agent.items()}
|
||||
if not user_agent:
|
||||
if not req_token:
|
||||
ua = ua_generator.generate(device='desktop', browser=('chrome', 'edge'), platform=('windows', 'macos'))
|
||||
return {
|
||||
"user-agent": ua.text,
|
||||
"sec-ch-ua-platform": ua.platform,
|
||||
"sec-ch-ua": ua.ch.brands,
|
||||
"sec-ch-ua-mobile": ua.ch.mobile,
|
||||
"impersonate": random.choice(globals.impersonate_list),
|
||||
}
|
||||
else:
|
||||
ua = ua_generator.generate(device='desktop', browser=('chrome', 'edge'), platform=('windows', 'macos'))
|
||||
user_agent = {
|
||||
"user-agent": ua.text,
|
||||
"sec-ch-ua-platform": ua.platform,
|
||||
"sec-ch-ua": ua.ch.brands,
|
||||
"sec-ch-ua-mobile": ua.ch.mobile,
|
||||
"impersonate": random.choice(globals.impersonate_list),
|
||||
}
|
||||
globals.user_agent_map[req_token] = user_agent
|
||||
with open(globals.USER_AGENTS_FILE, "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(globals.user_agent_map, indent=4))
|
||||
return user_agent
|
||||
else:
|
||||
def get_fp(req_token):
|
||||
fp = globals.user_agent_map.get(req_token, {})
|
||||
if fp and fp.get("user-agent") and fp.get("impersonate"):
|
||||
if "proxy_url" in fp.keys() and fp["proxy_url"] and fp["proxy_url"] not in configs.proxy_url_list:
|
||||
fp["proxy_url"] = random.choice(configs.proxy_url_list) if configs.proxy_url_list else None
|
||||
globals.user_agent_map[req_token] = fp
|
||||
with open(globals.FP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.user_agent_map, f, indent=4)
|
||||
if globals.impersonate_list and "impersonate" in fp.keys() and fp["impersonate"] not in globals.impersonate_list:
|
||||
fp["impersonate"] = random.choice(globals.impersonate_list)
|
||||
globals.user_agent_map[req_token] = fp
|
||||
with open(globals.FP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.user_agent_map, f, indent=4)
|
||||
if configs.user_agents_list and "user-agent" in fp.keys() and fp["user-agent"] not in configs.user_agents_list:
|
||||
fp["user-agent"] = random.choice(configs.user_agents_list)
|
||||
globals.user_agent_map[req_token] = fp
|
||||
with open(globals.FP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.user_agent_map, f, indent=4)
|
||||
user_agent = {k.lower(): v for k, v in fp.items()}
|
||||
return user_agent
|
||||
else:
|
||||
options = Options(version_ranges={
|
||||
'chrome': VersionRange(min_version=124),
|
||||
'edge': VersionRange(min_version=124),
|
||||
})
|
||||
ua = ua_generator.generate(device='desktop', browser=('chrome', 'edge', 'firefox', 'safari'),
|
||||
platform=('windows', 'macos'), options=options)
|
||||
fp = {
|
||||
"user-agent": ua.text if not configs.user_agents_list else random.choice(configs.user_agents_list),
|
||||
"sec-ch-ua-platform": ua.ch.platform,
|
||||
"sec-ch-ua": ua.ch.brands,
|
||||
"sec-ch-ua-mobile": ua.ch.mobile,
|
||||
"impersonate": random.choice(globals.impersonate_list),
|
||||
"proxy_url": random.choice(configs.proxy_url_list) if configs.proxy_url_list else None,
|
||||
}
|
||||
if not req_token:
|
||||
return fp
|
||||
else:
|
||||
globals.user_agent_map[req_token] = fp
|
||||
with open(globals.FP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.user_agent_map, f, indent=4)
|
||||
return fp
|
||||
|
||||
|
||||
async def verify_token(req_token):
|
||||
if not req_token:
|
||||
if authorization_list:
|
||||
if configs.authorization_list:
|
||||
logger.error("Unauthorized with empty token.")
|
||||
raise HTTPException(status_code=401)
|
||||
else:
|
||||
@ -94,7 +114,7 @@ async def refresh_all_tokens(force_refresh=False):
|
||||
for token in list(set(globals.token_list) - set(globals.error_token_list)):
|
||||
if len(token) == 45:
|
||||
try:
|
||||
await asyncio.sleep(2)
|
||||
await asyncio.sleep(0.5)
|
||||
await rt2ac(token, force_refresh=force_refresh)
|
||||
except HTTPException:
|
||||
pass
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import ua_generator
|
||||
import random
|
||||
|
||||
from utils.Logger import logger
|
||||
|
||||
DATA_FOLDER = "data"
|
||||
TOKENS_FILE = os.path.join(DATA_FOLDER, "token.txt")
|
||||
REFRESH_MAP_FILE = os.path.join(DATA_FOLDER, "refresh_map.json")
|
||||
ERROR_TOKENS_FILE = os.path.join(DATA_FOLDER, "error_token.txt")
|
||||
WSS_MAP_FILE = os.path.join(DATA_FOLDER, "wss_map.json")
|
||||
USER_AGENTS_FILE = os.path.join(DATA_FOLDER, "user_agents.json")
|
||||
|
||||
count = 0
|
||||
token_list = []
|
||||
error_token_list = []
|
||||
refresh_map = {}
|
||||
wss_map = {}
|
||||
user_agent_map = {}
|
||||
impersonate_list = [
|
||||
"chrome99",
|
||||
"chrome100",
|
||||
"chrome101",
|
||||
"chrome104",
|
||||
"chrome107",
|
||||
"chrome110",
|
||||
"chrome116",
|
||||
"chrome119",
|
||||
"chrome120",
|
||||
"chrome123",
|
||||
"edge99",
|
||||
"edge101",
|
||||
]
|
||||
|
||||
if not os.path.exists(DATA_FOLDER):
|
||||
os.makedirs(DATA_FOLDER)
|
||||
|
||||
if os.path.exists(REFRESH_MAP_FILE):
|
||||
with open(REFRESH_MAP_FILE, "r") as file:
|
||||
refresh_map = json.load(file)
|
||||
else:
|
||||
refresh_map = {}
|
||||
|
||||
if os.path.exists(WSS_MAP_FILE):
|
||||
with open(WSS_MAP_FILE, "r") as file:
|
||||
wss_map = json.load(file)
|
||||
else:
|
||||
wss_map = {}
|
||||
|
||||
|
||||
if os.path.exists(TOKENS_FILE):
|
||||
with open(TOKENS_FILE, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
token_list.append(line.strip())
|
||||
else:
|
||||
with open(TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
pass
|
||||
|
||||
|
||||
if os.path.exists(ERROR_TOKENS_FILE):
|
||||
with open(ERROR_TOKENS_FILE, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
error_token_list.append(line.strip())
|
||||
else:
|
||||
with open(ERROR_TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
pass
|
||||
|
||||
if os.path.exists(USER_AGENTS_FILE):
|
||||
with open(USER_AGENTS_FILE, "r", encoding="utf-8") as f:
|
||||
try:
|
||||
user_agent_map = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
user_agent_map = {}
|
||||
# token数量变化时,更新ua
|
||||
if len(user_agent_map.keys()) != len(token_list):
|
||||
new_tokens = list(set(token_list) - user_agent_map.keys())
|
||||
for token in new_tokens:
|
||||
ua = ua_generator.generate(device='desktop', browser=('chrome', 'edge'), platform=('windows', 'macos'))
|
||||
ua_dict = {
|
||||
"user-agent": ua.text,
|
||||
"sec-ch-ua-platform": ua.platform,
|
||||
"sec-ch-ua": ua.ch.brands,
|
||||
"sec-ch-ua-mobile": ua.ch.mobile,
|
||||
"impersonate": random.choice(impersonate_list),
|
||||
}
|
||||
user_agent_map[token] = ua_dict
|
||||
with open(USER_AGENTS_FILE, "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(user_agent_map, indent=4))
|
||||
else:
|
||||
for token in token_list:
|
||||
ua = ua_generator.generate(device='desktop', browser=('chrome', 'edge'), platform=('windows', 'macos'))
|
||||
ua_dict = {
|
||||
"user-agent": ua.text,
|
||||
"sec-ch-ua-platform": ua.platform,
|
||||
"sec-ch-ua": ua.ch.brands,
|
||||
"sec-ch-ua-mobile": ua.ch.mobile,
|
||||
"impersonate": random.choice(impersonate_list),
|
||||
}
|
||||
user_agent_map[token] = ua_dict
|
||||
with open(USER_AGENTS_FILE, "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(user_agent_map, indent=4))
|
||||
|
||||
if token_list:
|
||||
logger.info(f"Token list count: {len(token_list)}, Error token list count: {len(error_token_list)}")
|
||||
@ -10,7 +10,7 @@ from html.parser import HTMLParser
|
||||
import pybase64
|
||||
|
||||
from utils.Logger import logger
|
||||
from utils.config import conversation_only
|
||||
from utils.configs import conversation_only
|
||||
|
||||
cores = [16, 24, 32]
|
||||
screens = [3000, 4000, 6000]
|
||||
|
||||
@ -6,25 +6,21 @@ from fastapi import HTTPException
|
||||
|
||||
from utils.Client import Client
|
||||
from utils.Logger import logger
|
||||
from utils.config import proxy_url_list
|
||||
import chatgpt.globals as globals
|
||||
|
||||
|
||||
def save_refresh_map(refresh_map):
|
||||
with open(globals.REFRESH_MAP_FILE, "w") as file:
|
||||
json.dump(refresh_map, file)
|
||||
from utils.configs import proxy_url_list
|
||||
import utils.globals as globals
|
||||
|
||||
|
||||
async def rt2ac(refresh_token, force_refresh=False):
|
||||
if not force_refresh and (refresh_token in globals.refresh_map and int(time.time()) - globals.refresh_map.get(refresh_token, {}).get("timestamp", 0) < 5 * 24 * 60 * 60):
|
||||
access_token = globals.refresh_map[refresh_token]["token"]
|
||||
logger.info(f"refresh_token -> access_token from cache")
|
||||
# logger.info(f"refresh_token -> access_token from cache")
|
||||
return access_token
|
||||
else:
|
||||
try:
|
||||
access_token = await chat_refresh(refresh_token)
|
||||
globals.refresh_map[refresh_token] = {"token": access_token, "timestamp": int(time.time())}
|
||||
save_refresh_map(globals.refresh_map)
|
||||
with open(globals.REFRESH_MAP_FILE, "w") as f:
|
||||
json.dump(globals.refresh_map, f, indent=4)
|
||||
logger.info(f"refresh_token -> access_token with openai: {access_token}")
|
||||
return access_token
|
||||
except HTTPException as e:
|
||||
|
||||
@ -2,12 +2,12 @@ import json
|
||||
import time
|
||||
|
||||
from utils.Logger import logger
|
||||
import chatgpt.globals as globals
|
||||
import utils.globals as globals
|
||||
|
||||
|
||||
def save_wss_map(wss_map):
|
||||
with open(globals.WSS_MAP_FILE, "w") as file:
|
||||
json.dump(wss_map, file)
|
||||
with open(globals.WSS_MAP_FILE, "w") as f:
|
||||
json.dump(wss_map, f, indent=4)
|
||||
|
||||
|
||||
async def token2wss(token):
|
||||
|
||||
332
gateway/backend.py
Normal file
332
gateway/backend.py
Normal file
@ -0,0 +1,332 @@
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse, Response
|
||||
|
||||
import utils.globals as globals
|
||||
from app import app, templates
|
||||
from gateway.reverseProxy import chatgpt_reverse_proxy
|
||||
from utils.configs import authorization_list
|
||||
from utils.configs import enable_gateway
|
||||
|
||||
with open("templates/remix_context.json", "r", encoding="utf-8") as f:
|
||||
remix_context = json.load(f)
|
||||
|
||||
|
||||
def set_value_for_key(data, target_key, new_value):
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
if key == target_key:
|
||||
data[key] = new_value
|
||||
else:
|
||||
set_value_for_key(value, target_key, new_value)
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
set_value_for_key(item, target_key, new_value)
|
||||
|
||||
|
||||
if enable_gateway:
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def chatgpt_html(request: Request):
|
||||
token = request.query_params.get("token")
|
||||
if not token:
|
||||
token = request.cookies.get("token")
|
||||
if not token:
|
||||
return await login_html(request)
|
||||
|
||||
user_remix_context = remix_context.copy()
|
||||
set_value_for_key(user_remix_context, "user", {"id": "user-chatgpt"})
|
||||
set_value_for_key(user_remix_context, "accessToken", token)
|
||||
|
||||
response = templates.TemplateResponse("chatgpt.html", {"request": request, "remix_context": user_remix_context})
|
||||
response.set_cookie("token", value=token, expires="Thu, 01 Jan 2099 00:00:00 GMT")
|
||||
return response
|
||||
|
||||
# @app.get("/backend-api/accounts/check/v4-2023-04-27")
|
||||
# async def check_account(request: Request):
|
||||
# token = request.headers.get("Authorization").replace("Bearer ", "")
|
||||
# check_account_response = await chatgpt_reverse_proxy(request, "backend-api/accounts/check/v4-2023-04-27")
|
||||
# if len(token) == 45 or token.startswith("eyJhbGciOi"):
|
||||
# return check_account_response
|
||||
# else:
|
||||
# check_account_str = check_account_response.body.decode('utf-8')
|
||||
# check_account_info = json.loads(check_account_str)
|
||||
# for key in check_account_info.get("accounts", {}).keys():
|
||||
# account_id = check_account_info["accounts"][key]["account"]["account_id"]
|
||||
# globals.seed_map[token]["user_id"] = check_account_info["accounts"][key]["account"]["account_user_id"].split("__")[0]
|
||||
# check_account_info["accounts"][key]["account"]["account_user_id"] = f"user-chatgpt__{account_id}"
|
||||
# with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
# json.dump(globals.seed_map, f, indent=4)
|
||||
# return check_account_info
|
||||
|
||||
|
||||
def verify_authorization(request: Request):
|
||||
auth_header = request.headers.get("Authorization").replace("Bearer ", "")
|
||||
|
||||
if not auth_header:
|
||||
raise HTTPException(status_code=401, detail="Authorization header is missing")
|
||||
if auth_header not in authorization_list:
|
||||
raise HTTPException(status_code=401, detail="Invalid authorization")
|
||||
|
||||
|
||||
@app.get("/seedtoken")
|
||||
async def get_seedtoken(request: Request):
|
||||
verify_authorization(request)
|
||||
try:
|
||||
params = request.query_params
|
||||
seed = params.get("seed")
|
||||
|
||||
if seed:
|
||||
if seed not in globals.seed_map:
|
||||
raise HTTPException(status_code=404, detail=f"Seed '{seed}' not found")
|
||||
return {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"seed": seed,
|
||||
"token": globals.seed_map[seed]["token"]
|
||||
}
|
||||
}
|
||||
|
||||
token_map = {
|
||||
seed: data["token"]
|
||||
for seed, data in globals.seed_map.items()
|
||||
}
|
||||
return {"status": "success", "data": token_map}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
|
||||
|
||||
|
||||
@app.post("/seedtoken")
|
||||
async def set_seedtoken(request: Request):
|
||||
verify_authorization(request)
|
||||
data = await request.json()
|
||||
|
||||
seed = data.get("seed")
|
||||
token = data.get("token")
|
||||
|
||||
if seed not in globals.seed_map:
|
||||
globals.seed_map[seed] = {
|
||||
"token": token,
|
||||
"conversations": []
|
||||
}
|
||||
else:
|
||||
globals.seed_map[seed]["token"] = token
|
||||
|
||||
with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
|
||||
return {"status": "success", "message": "Token updated successfully"}
|
||||
|
||||
|
||||
@app.delete("/seedtoken")
|
||||
async def delete_seedtoken(request: Request):
|
||||
verify_authorization(request)
|
||||
|
||||
try:
|
||||
data = await request.json()
|
||||
seed = data.get("seed")
|
||||
|
||||
if seed == "clear":
|
||||
globals.seed_map.clear()
|
||||
with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
return {"status": "success", "message": "All seeds deleted successfully"}
|
||||
|
||||
if not seed:
|
||||
raise HTTPException(status_code=400, detail="Missing required field: seed")
|
||||
|
||||
if seed not in globals.seed_map:
|
||||
raise HTTPException(status_code=404, detail=f"Seed '{seed}' not found")
|
||||
del globals.seed_map[seed]
|
||||
|
||||
with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Seed '{seed}' deleted successfully"
|
||||
}
|
||||
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(status_code=400, detail="Invalid JSON data")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
|
||||
|
||||
|
||||
@app.get("/login", response_class=HTMLResponse)
|
||||
async def login_html(request: Request):
|
||||
response = templates.TemplateResponse("login.html", {"request": request})
|
||||
return response
|
||||
|
||||
|
||||
@app.get("/gpts")
|
||||
async def get_gpts():
|
||||
return {"kind": "store"}
|
||||
|
||||
|
||||
@app.get("/backend-api/gizmos/bootstrap")
|
||||
async def get_gizmos_bootstrap():
|
||||
return {"gizmos": []}
|
||||
|
||||
|
||||
@app.get("/backend-api/conversations")
|
||||
async def get_conversations(request: Request):
|
||||
limit = int(request.query_params.get("limit", 28))
|
||||
offset = int(request.query_params.get("offset", 0))
|
||||
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||
if len(token) == 45 or token.startswith("eyJhbGciOi"):
|
||||
return await chatgpt_reverse_proxy(request, "backend-api/conversations")
|
||||
else:
|
||||
items = []
|
||||
for conversation_id in globals.seed_map.get(token, {}).get("conversations", []):
|
||||
conversation = globals.conversation_map.get(conversation_id, None)
|
||||
if conversation:
|
||||
items.append(conversation)
|
||||
items = items[int(offset):int(offset) + int(limit)]
|
||||
conversations = {
|
||||
"items": items,
|
||||
"total": len(items),
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
"has_missing_conversations": False
|
||||
}
|
||||
return conversations
|
||||
|
||||
|
||||
@app.get("/backend-api/conversation/{conversation_id}")
|
||||
async def update_conversation(request: Request, conversation_id: str):
|
||||
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||
conversation_details_response = await chatgpt_reverse_proxy(request,
|
||||
f"backend-api/conversation/{conversation_id}")
|
||||
if len(token) == 45 or token.startswith("eyJhbGciOi"):
|
||||
return conversation_details_response
|
||||
else:
|
||||
conversation_details_str = conversation_details_response.body.decode('utf-8')
|
||||
conversation_details = json.loads(conversation_details_str)
|
||||
if conversation_id in globals.seed_map[token][
|
||||
"conversations"] and conversation_id in globals.conversation_map:
|
||||
globals.conversation_map[conversation_id]["title"] = conversation_details.get("title", None)
|
||||
globals.conversation_map[conversation_id]["is_archived"] = conversation_details.get("is_archived",
|
||||
False)
|
||||
with open(globals.CONVERSATION_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.conversation_map, f, indent=4)
|
||||
return conversation_details_response
|
||||
|
||||
|
||||
@app.patch("/backend-api/conversation/{conversation_id}")
|
||||
async def patch_conversation(request: Request, conversation_id: str):
|
||||
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||
patch_response = (await chatgpt_reverse_proxy(request, f"backend-api/conversation/{conversation_id}"))
|
||||
if len(token) == 45 or token.startswith("eyJhbGciOi"):
|
||||
return patch_response
|
||||
else:
|
||||
data = await request.json()
|
||||
if conversation_id in globals.seed_map[token][
|
||||
"conversations"] and conversation_id in globals.conversation_map:
|
||||
if not data.get("is_visible", True):
|
||||
globals.conversation_map.pop(conversation_id)
|
||||
globals.seed_map[token]["conversations"].remove(conversation_id)
|
||||
with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
else:
|
||||
globals.conversation_map[conversation_id].update(data)
|
||||
with open(globals.CONVERSATION_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.conversation_map, f, indent=4)
|
||||
return patch_response
|
||||
|
||||
@app.get("/backend-api/me")
|
||||
async def get_me(request: Request):
|
||||
me = {
|
||||
"object": "user",
|
||||
"id": "org-chatgpt",
|
||||
"email": "chatgpt@openai.com",
|
||||
"name": "ChatGPT",
|
||||
"picture": "https://cdn.auth0.com/avatars/ai.png",
|
||||
"created": int(time.time()),
|
||||
"phone_number": None,
|
||||
"mfa_flag_enabled": False,
|
||||
"amr": [],
|
||||
"groups": [],
|
||||
"orgs": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "organization",
|
||||
"id": "org-chatgpt",
|
||||
"created": 1715641300,
|
||||
"title": "Personal",
|
||||
"name": "user-chatgpt",
|
||||
"description": "Personal org for chatgpt@openai.com",
|
||||
"personal": True,
|
||||
"settings": {
|
||||
"threads_ui_visibility": "NONE",
|
||||
"usage_dashboard_visibility": "ANY_ROLE",
|
||||
"disable_user_api_keys": False
|
||||
},
|
||||
"parent_org_id": None,
|
||||
"is_default": True,
|
||||
"role": "owner",
|
||||
"is_scale_tier_authorized_purchaser": None,
|
||||
"is_scim_managed": False,
|
||||
"projects": {
|
||||
"object": "list",
|
||||
"data": []
|
||||
},
|
||||
"groups": [],
|
||||
"geography": None
|
||||
}
|
||||
]
|
||||
},
|
||||
"has_payg_project_spend_limit": True
|
||||
}
|
||||
return me
|
||||
|
||||
|
||||
banned_paths = [
|
||||
"backend-api/accounts/logout_all",
|
||||
"backend-api/accounts/deactivate",
|
||||
"backend-api/user_system_messages",
|
||||
"backend-api/memories",
|
||||
"backend-api/settings/clear_account_user_memory",
|
||||
"backend-api/conversations/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
||||
"backend-api/accounts/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/invites",
|
||||
"admin",
|
||||
]
|
||||
redirect_paths = ["auth/logout"]
|
||||
chatgpt_paths = ["c/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"]
|
||||
|
||||
|
||||
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"])
|
||||
async def reverse_proxy(request: Request, path: str):
|
||||
if re.match("/v1/rgstr", path):
|
||||
return Response(status_code=202, content=b'{"success":true}')
|
||||
|
||||
if re.match("ces/v1", path):
|
||||
return {"success": True}
|
||||
|
||||
if re.match("backend-api/edge", path):
|
||||
return Response(status_code=204)
|
||||
|
||||
for chatgpt_path in chatgpt_paths:
|
||||
if re.match(chatgpt_path, path):
|
||||
return await chatgpt_html(request)
|
||||
|
||||
for banned_path in banned_paths:
|
||||
if re.match(banned_path, path):
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
|
||||
for redirect_path in redirect_paths:
|
||||
if re.match(redirect_path, path):
|
||||
redirect_url = str(request.base_url)
|
||||
response = RedirectResponse(url=f"{redirect_url}login", status_code=302)
|
||||
return response
|
||||
|
||||
return await chatgpt_reverse_proxy(request, path)
|
||||
else:
|
||||
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"])
|
||||
async def reverse_proxy():
|
||||
raise HTTPException(status_code=404, detail="Gateway is disabled")
|
||||
@ -1,13 +1,24 @@
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import StreamingResponse, Response
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
from chatgpt.authorization import verify_token, get_req_token, get_ua
|
||||
from chatgpt.authorization import verify_token, get_req_token, get_fp
|
||||
import utils.globals as globals
|
||||
from utils.Client import Client
|
||||
from utils.config import chatgpt_base_url_list, proxy_url_list, enable_gateway
|
||||
from utils.Logger import logger
|
||||
from utils.configs import chatgpt_base_url_list, proxy_url_list
|
||||
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
def generate_current_time():
|
||||
current_time = datetime.now(timezone.utc)
|
||||
formatted_time = current_time.isoformat(timespec='microseconds').replace('+00:00', 'Z')
|
||||
return formatted_time
|
||||
|
||||
headers_reject_list = [
|
||||
"x-real-ip",
|
||||
@ -70,6 +81,60 @@ async def get_real_req_token(token):
|
||||
return req_token
|
||||
|
||||
|
||||
async def content_generator(r, token):
|
||||
conversation_id = None
|
||||
title = None
|
||||
async for chunk in r.aiter_content():
|
||||
try:
|
||||
if (len(token) != 45 and not token.startswith("eyJhbGciOi")) and (not conversation_id or not title):
|
||||
chat_chunk = chunk.decode('utf-8')
|
||||
if "title" in chat_chunk:
|
||||
pass
|
||||
if chat_chunk.startswith("data: {"):
|
||||
if "\n\nevent: delta" in chat_chunk:
|
||||
index = chat_chunk.find("\n\nevent: delta")
|
||||
chunk_data = chat_chunk[6:index]
|
||||
elif "\n\ndata: {" in chat_chunk:
|
||||
index = chat_chunk.find("\n\ndata: {")
|
||||
chunk_data = chat_chunk[6:index]
|
||||
else:
|
||||
chunk_data = chat_chunk[6:]
|
||||
chunk_data = chunk_data.strip()
|
||||
if conversation_id is None:
|
||||
conversation_id = json.loads(chunk_data).get("conversation_id")
|
||||
if conversation_id in globals.conversation_map:
|
||||
title = globals.conversation_map[conversation_id].get("title")
|
||||
if title is None:
|
||||
title = json.loads(chunk_data).get("title")
|
||||
|
||||
if conversation_id and title:
|
||||
conversation_detail = {
|
||||
"id": conversation_id,
|
||||
"title": title,
|
||||
"update_time": generate_current_time(),
|
||||
"workspace_id": None,
|
||||
}
|
||||
if conversation_id not in globals.conversation_map:
|
||||
globals.conversation_map[conversation_id] = conversation_detail
|
||||
else:
|
||||
globals.conversation_map[conversation_id]["update_time"] = generate_current_time()
|
||||
if conversation_id not in globals.seed_map[token]["conversations"]:
|
||||
globals.seed_map[token]["conversations"].insert(0, conversation_id)
|
||||
else:
|
||||
globals.seed_map[token]["conversations"].remove(conversation_id)
|
||||
globals.seed_map[token]["conversations"].insert(0, conversation_id)
|
||||
with open(globals.CONVERSATION_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.conversation_map, f, indent=4)
|
||||
with open(globals.SEED_MAP_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(globals.seed_map, f, indent=4)
|
||||
logger.info(f"Conversation ID: {conversation_id}, Title: {title}")
|
||||
except Exception as e:
|
||||
# logger.error(e)
|
||||
# logger.error(chunk.decode('utf-8'))
|
||||
pass
|
||||
yield chunk
|
||||
|
||||
|
||||
async def chatgpt_reverse_proxy(request: Request, path: str):
|
||||
try:
|
||||
origin_host = request.url.netloc
|
||||
@ -96,11 +161,16 @@ async def chatgpt_reverse_proxy(request: Request, path: str):
|
||||
base_url = "https://cdn.oaistatic.com"
|
||||
if "file-" in path and "backend-api" not in path:
|
||||
base_url = "https://files.oaiusercontent.com"
|
||||
if "v1/" in path:
|
||||
base_url = "https://ab.chatgpt.com"
|
||||
|
||||
token = request.cookies.get("token")
|
||||
token = request.cookies.get("token", "")
|
||||
req_token = await get_real_req_token(token)
|
||||
ua = get_ua(req_token)
|
||||
headers.update(ua)
|
||||
fp = get_fp(req_token)
|
||||
proxy_url = fp.get("proxy_url")
|
||||
user_agent = fp.get("user-agent")
|
||||
impersonate = fp.get("impersonate", "safari15_3")
|
||||
headers.update(fp)
|
||||
|
||||
headers.update({
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
@ -108,6 +178,13 @@ async def chatgpt_reverse_proxy(request: Request, path: str):
|
||||
"origin": base_url,
|
||||
"referer": f"{base_url}/"
|
||||
})
|
||||
if "ab.chatgpt.com" in base_url:
|
||||
headers.update({
|
||||
"statsig-sdk-type": "js-client",
|
||||
"statsig-api-key": "client-tnE5GCU2F2cTxRiMbvTczMDT1jpwIigZHsZSdqiy4u",
|
||||
"statsig-sdk-version": "5.1.0",
|
||||
"statsig-client-time": int(time.time() * 1000)
|
||||
})
|
||||
|
||||
token = headers.get("authorization", "").replace("Bearer ", "")
|
||||
if token:
|
||||
@ -117,28 +194,39 @@ async def chatgpt_reverse_proxy(request: Request, path: str):
|
||||
|
||||
data = await request.body()
|
||||
|
||||
client = Client(proxy=random.choice(proxy_url_list) if proxy_url_list else None)
|
||||
client = Client(proxy=proxy_url, impersonate=impersonate)
|
||||
try:
|
||||
background = BackgroundTask(client.close)
|
||||
r = await client.request(request.method, f"{base_url}/{path}", params=params, headers=headers,
|
||||
cookies=request_cookies, data=data, stream=True, allow_redirects=False)
|
||||
|
||||
if r.status_code == 307:
|
||||
if "bing" in path:
|
||||
return Response(status_code=307,
|
||||
headers={"Location": r.headers.get("Location").replace("chatgpt.com", origin_host)
|
||||
.replace("https", petrol)}, background=background)
|
||||
if r.status_code == 302:
|
||||
return Response(status_code=302,
|
||||
headers={"Location": r.headers.get("Location").replace("chatgpt.com", origin_host)
|
||||
.replace("cdn.oaistatic.com", origin_host)
|
||||
.replace("https", petrol)}, background=background)
|
||||
elif 'stream' in r.headers.get("content-type", ""):
|
||||
return StreamingResponse(r.aiter_content(), media_type=r.headers.get("content-type", ""),
|
||||
logger.info(f"Request token: {req_token}")
|
||||
logger.info(f"Request proxy: {proxy_url}")
|
||||
logger.info(f"Request UA: {user_agent}")
|
||||
logger.info(f"Request impersonate: {impersonate}")
|
||||
return StreamingResponse(content_generator(r, token), media_type=r.headers.get("content-type", ""),
|
||||
background=background)
|
||||
else:
|
||||
if "/backend-api/conversation" in path or "/register-websocket" in path:
|
||||
response = Response(content=(await r.atext()), media_type=r.headers.get("content-type"),
|
||||
status_code=r.status_code, background=background)
|
||||
else:
|
||||
content = ((await r.atext()).replace("chatgpt.com", origin_host)
|
||||
content = await r.atext()
|
||||
content = (content
|
||||
.replace("ab.chatgpt.com", origin_host)
|
||||
.replace("cdn.oaistatic.com", origin_host)
|
||||
# .replace("files.oaiusercontent.com", origin_host)
|
||||
.replace("chatgpt.com", origin_host)
|
||||
.replace("https", petrol))
|
||||
rheaders = dict(r.headers)
|
||||
content_type = rheaders.get("content-type", "")
|
||||
247
gateway/share.py
Normal file
247
gateway/share.py
Normal file
@ -0,0 +1,247 @@
|
||||
import json
|
||||
import random
|
||||
import uuid
|
||||
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import Response
|
||||
from starlette.background import BackgroundTask
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from app import app
|
||||
from chatgpt.authorization import get_fp, verify_token
|
||||
from chatgpt.proofofWork import get_config, get_requirements_token, get_answer_token
|
||||
from gateway.reverseProxy import get_real_req_token, content_generator
|
||||
from utils.Client import Client
|
||||
from utils.Logger import logger
|
||||
from utils.configs import proxy_url_list, chatgpt_base_url_list, turnstile_solver_url, x_sign, no_sentinel
|
||||
|
||||
base_headers = {
|
||||
'accept': '*/*',
|
||||
'accept-encoding': 'gzip, deflate, br, zstd',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'content-type': 'application/json',
|
||||
'oai-language': 'en-US',
|
||||
'priority': 'u=1, i',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
}
|
||||
|
||||
|
||||
async def chatgpt_account_check(access_token):
|
||||
auth_info = {}
|
||||
client = Client(proxy=random.choice(proxy_url_list) if proxy_url_list else None)
|
||||
try:
|
||||
proxy_url = random.choice(proxy_url_list) if proxy_url_list else None
|
||||
host_url = random.choice(chatgpt_base_url_list) if chatgpt_base_url_list else "https://chatgpt.com"
|
||||
req_token = await get_real_req_token(access_token)
|
||||
access_token = await verify_token(req_token)
|
||||
fp = get_fp(req_token)
|
||||
|
||||
headers = base_headers.copy()
|
||||
headers.update({"authorization": f"Bearer {access_token}"})
|
||||
headers.update(fp)
|
||||
|
||||
client = Client(proxy=proxy_url, impersonate=fp.get("impersonate", "safari15_3"))
|
||||
r = await client.get(f"{host_url}/backend-api/models?history_and_training_disabled=false", headers=headers,
|
||||
timeout=10)
|
||||
if r.status_code != 200:
|
||||
raise HTTPException(status_code=r.status_code, detail=r.text)
|
||||
models = r.json()
|
||||
r = await client.get(f"{host_url}/backend-api/accounts/check/v4-2023-04-27", headers=headers, timeout=10)
|
||||
if r.status_code != 200:
|
||||
raise HTTPException(status_code=r.status_code, detail=r.text)
|
||||
accounts_info = r.json()
|
||||
|
||||
auth_info.update({"models": models["models"]})
|
||||
auth_info.update({"accounts_info": accounts_info})
|
||||
|
||||
account_ordering = accounts_info.get("account_ordering", [])
|
||||
is_deactivated = None
|
||||
plan_type = None
|
||||
team_ids = []
|
||||
for account in account_ordering:
|
||||
this_is_deactivated = accounts_info['accounts'].get(account, {}).get("account", {}).get("is_deactivated",
|
||||
False)
|
||||
this_plan_type = accounts_info['accounts'].get(account, {}).get("account", {}).get("plan_type", "free")
|
||||
|
||||
if this_is_deactivated and is_deactivated is None:
|
||||
is_deactivated = True
|
||||
else:
|
||||
is_deactivated = False
|
||||
|
||||
if "team" in this_plan_type:
|
||||
plan_type = this_plan_type
|
||||
team_ids.append(account)
|
||||
elif plan_type is None:
|
||||
plan_type = this_plan_type
|
||||
|
||||
auth_info.update({"accountCheckInfo": {
|
||||
"is_deactivated": is_deactivated,
|
||||
"plan_type": plan_type,
|
||||
"team_ids": team_ids
|
||||
}})
|
||||
|
||||
return auth_info
|
||||
except Exception as e:
|
||||
logger.error(f"chatgpt_account_check: {e}")
|
||||
return {}
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
async def chatgpt_refresh(refresh_token):
|
||||
client = Client(proxy=random.choice(proxy_url_list) if proxy_url_list else None)
|
||||
try:
|
||||
data = {
|
||||
"client_id": "pdlLIX2Y72MIl2rhLhTE9VV9bN905kBh",
|
||||
"grant_type": "refresh_token",
|
||||
"redirect_uri": "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
|
||||
"refresh_token": refresh_token
|
||||
}
|
||||
r = await client.post("https://auth0.openai.com/oauth/token", json=data, timeout=10)
|
||||
if r.status_code != 200:
|
||||
raise HTTPException(status_code=r.status_code, detail=r.text)
|
||||
res = r.json()
|
||||
auth_info = {}
|
||||
auth_info.update(res)
|
||||
auth_info.update({"refresh_token": refresh_token})
|
||||
auth_info.update({"accessToken": res.get("access_token", "")})
|
||||
return auth_info
|
||||
except Exception as e:
|
||||
logger.error(f"chatgpt_refresh: {e}")
|
||||
return {}
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
@app.post("/auth/refresh")
|
||||
async def refresh(request: Request):
|
||||
auth_info = {}
|
||||
form_data = await request.form()
|
||||
|
||||
auth_info.update(form_data)
|
||||
|
||||
access_token = auth_info.get("access_token", auth_info.get("accessToken", ""))
|
||||
refresh_token = auth_info.get("refresh_token", "")
|
||||
|
||||
if not refresh_token and not access_token:
|
||||
raise HTTPException(status_code=401, detail="refresh_token or access_token is required")
|
||||
|
||||
if access_token:
|
||||
account_check_info = await chatgpt_account_check(access_token)
|
||||
if account_check_info:
|
||||
auth_info.update(account_check_info)
|
||||
auth_info.update({"accessToken": access_token})
|
||||
return Response(content=json.dumps(auth_info), media_type="application/json")
|
||||
|
||||
if refresh_token:
|
||||
chatgpt_refresh_info = await chatgpt_refresh(refresh_token)
|
||||
if chatgpt_refresh_info:
|
||||
auth_info.update(chatgpt_refresh_info)
|
||||
access_token = auth_info.get("accessToken", "")
|
||||
account_check_info = await chatgpt_account_check(access_token)
|
||||
if account_check_info:
|
||||
auth_info.update(account_check_info)
|
||||
auth_info.update({"accessToken": access_token})
|
||||
return Response(content=json.dumps(auth_info), media_type="application/json")
|
||||
raise HTTPException(status_code=401, detail="Unauthorized")
|
||||
|
||||
|
||||
if no_sentinel:
|
||||
@app.post("/backend-api/sentinel/chat-requirements")
|
||||
async def sentinel_chat_conversations():
|
||||
return {
|
||||
"arkose": {
|
||||
"dx": None,
|
||||
"required": False
|
||||
},
|
||||
"persona": "chatgpt-paid",
|
||||
"proofofwork": {
|
||||
"difficulty": None,
|
||||
"required": False,
|
||||
"seed": None
|
||||
},
|
||||
"token": str(uuid.uuid4()),
|
||||
"turnstile": {
|
||||
"dx": None,
|
||||
"required": False
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.post("/backend-api/conversation")
|
||||
async def chat_conversations(request: Request):
|
||||
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||
req_token = await get_real_req_token(token)
|
||||
access_token = await verify_token(req_token)
|
||||
fp = get_fp(req_token)
|
||||
proxy_url = fp.get("proxy_url", None)
|
||||
user_agent = fp.get("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")
|
||||
impersonate = fp.get("impersonate", "safari15_3")
|
||||
|
||||
host_url = random.choice(chatgpt_base_url_list) if chatgpt_base_url_list else "https://chatgpt.com"
|
||||
proof_token = None
|
||||
turnstile_token = None
|
||||
|
||||
headers = base_headers.copy()
|
||||
headers.update(fp)
|
||||
headers.update({"authorization": f"Bearer {access_token}"})
|
||||
|
||||
client = Client(proxy=proxy_url, impersonate=impersonate)
|
||||
|
||||
config = get_config(user_agent)
|
||||
p = get_requirements_token(config)
|
||||
data = {'p': p}
|
||||
r = await client.post(f'{host_url}/backend-api/sentinel/chat-requirements', headers=headers, json=data,
|
||||
timeout=10)
|
||||
resp = r.json()
|
||||
turnstile = resp.get('turnstile', {})
|
||||
turnstile_required = turnstile.get('required')
|
||||
if turnstile_required:
|
||||
turnstile_dx = turnstile.get("dx")
|
||||
try:
|
||||
if turnstile_solver_url:
|
||||
res = await client.post(turnstile_solver_url,
|
||||
json={"url": "https://chatgpt.com", "p": p, "dx": turnstile_dx})
|
||||
turnstile_token = res.json().get("t")
|
||||
except Exception as e:
|
||||
logger.info(f"Turnstile ignored: {e}")
|
||||
|
||||
proofofwork = resp.get('proofofwork', {})
|
||||
proofofwork_required = proofofwork.get('required')
|
||||
if proofofwork_required:
|
||||
proofofwork_diff = proofofwork.get("difficulty")
|
||||
proofofwork_seed = proofofwork.get("seed")
|
||||
proof_token, solved = await run_in_threadpool(
|
||||
get_answer_token, proofofwork_seed, proofofwork_diff, config
|
||||
)
|
||||
if not solved:
|
||||
raise HTTPException(status_code=403, detail="Failed to solve proof of work")
|
||||
chat_token = resp.get('token')
|
||||
headers.update({
|
||||
"openai-sentinel-chat-requirements-token": chat_token,
|
||||
"openai-sentinel-proof-token": proof_token,
|
||||
"openai-sentinel-turnstile-token": turnstile_token,
|
||||
})
|
||||
|
||||
params = dict(request.query_params)
|
||||
data = await request.body()
|
||||
request_cookies = dict(request.cookies)
|
||||
background = BackgroundTask(client.close)
|
||||
r = await client.post_stream(f"{host_url}/backend-api/conversation", params=params, headers=headers,
|
||||
cookies=request_cookies, data=data, stream=True, allow_redirects=False)
|
||||
rheaders = r.headers
|
||||
if x_sign:
|
||||
rheaders.update({"x-sign": x_sign})
|
||||
if 'stream' in rheaders.get("content-type", ""):
|
||||
logger.info(f"Request token: {req_token}")
|
||||
logger.info(f"Request proxy: {proxy_url}")
|
||||
logger.info(f"Request UA: {user_agent}")
|
||||
logger.info(f"Request impersonate: {impersonate}")
|
||||
return StreamingResponse(content_generator(r, token), headers=rheaders,
|
||||
media_type=rheaders.get("content-type"), background=background)
|
||||
else:
|
||||
return Response(content=(await r.atext()), headers=rheaders, media_type=rheaders.get("content-type"),
|
||||
status_code=r.status_code, background=background)
|
||||
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,10 @@
|
||||
|
||||
<div id="popup" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
||||
<div class="bg-white p-8 rounded-xl shadow-lg max-w-lg w-full h-auto">
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-4 text-center">请输入您的 Token</h3>
|
||||
<!-- <h3 class="text-xl font-bold text-gray-800 mb-4 text-center">请输入您的 Token</h3>-->
|
||||
<p class="font-semibold text-gray-800 text-center mb-1">直接点击
|
||||
<span class="font-bold text-indigo-600">开始</span>
|
||||
进入最近用过的账号</p>
|
||||
<label for="popup-input"></label>
|
||||
<textarea
|
||||
id="popup-input"
|
||||
@ -71,13 +74,8 @@
|
||||
|
||||
function submitToken() {
|
||||
var inputValue = document.getElementById('popup-input').value;
|
||||
if (inputValue) {
|
||||
window.location.href = '/?token=' + inputValue;
|
||||
closePopup();
|
||||
} else {
|
||||
window.location.href = '/?token=' + Math.random().toString(36);
|
||||
closePopup();
|
||||
}
|
||||
window.location.href = '/?token=' + inputValue;
|
||||
closePopup();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
4852
templates/remix_context.json
Normal file
4852
templates/remix_context.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,17 +20,19 @@ def is_true(x):
|
||||
|
||||
|
||||
api_prefix = os.getenv('API_PREFIX', None)
|
||||
auto_seed = os.getenv('AUTO_SEED', True)
|
||||
authorization = os.getenv('AUTHORIZATION', '').replace(' ', '')
|
||||
chatgpt_base_url = os.getenv('CHATGPT_BASE_URL', 'https://chatgpt.com').replace(' ', '')
|
||||
auth_key = os.getenv('AUTH_KEY', None)
|
||||
user_agents = os.getenv('USER_AGENTS', '[]')
|
||||
random_token = is_true(os.getenv('RANDOM_TOKEN', True))
|
||||
x_sign = os.getenv('X_SIGN', None)
|
||||
|
||||
ark0se_token_url = os.getenv('ARK' + 'OSE_TOKEN_URL', '').replace(' ', '')
|
||||
if not ark0se_token_url:
|
||||
ark0se_token_url = os.getenv('ARK0SE_TOKEN_URL', None)
|
||||
proxy_url = os.getenv('PROXY_URL', '').replace(' ', '')
|
||||
export_proxy_url = os.getenv('EXPORT_PROXY_URL', None)
|
||||
impersonate_list_str = os.getenv('IMPERSONATE', '[]')
|
||||
user_agents_list_str = os.getenv('USER_AGENTS', '[]')
|
||||
|
||||
cf_file_url = os.getenv('CF_FILE_URL', None)
|
||||
turnstile_solver_url = os.getenv('TURNSTILE_SOLVER_URL', None)
|
||||
@ -38,18 +40,22 @@ turnstile_solver_url = os.getenv('TURNSTILE_SOLVER_URL', None)
|
||||
history_disabled = is_true(os.getenv('HISTORY_DISABLED', True))
|
||||
pow_difficulty = os.getenv('POW_DIFFICULTY', '000032')
|
||||
retry_times = int(os.getenv('RETRY_TIMES', 3))
|
||||
enable_gateway = is_true(os.getenv('ENABLE_GATEWAY', False))
|
||||
conversation_only = is_true(os.getenv('CONVERSATION_ONLY', False))
|
||||
enable_limit = is_true(os.getenv('ENABLE_LIMIT', True))
|
||||
upload_by_url = is_true(os.getenv('UPLOAD_BY_URL', False))
|
||||
check_model = is_true(os.getenv('CHECK_MODEL', False))
|
||||
scheduled_refresh = is_true(os.getenv('SCHEDULED_REFRESH', False))
|
||||
random_token = is_true(os.getenv('RANDOM_TOKEN', True))
|
||||
|
||||
authorization_list = authorization.split(',') if authorization else []
|
||||
chatgpt_base_url_list = chatgpt_base_url.split(',') if chatgpt_base_url else []
|
||||
ark0se_token_url_list = ark0se_token_url.split(',') if ark0se_token_url else []
|
||||
proxy_url_list = proxy_url.split(',') if proxy_url else []
|
||||
user_agents_list = ast.literal_eval(user_agents)
|
||||
impersonate_list = ast.literal_eval(impersonate_list_str)
|
||||
user_agents_list = ast.literal_eval(user_agents_list_str)
|
||||
|
||||
enable_gateway = is_true(os.getenv('ENABLE_GATEWAY', False))
|
||||
no_sentinel = is_true(os.getenv('NO_SENTINEL', False))
|
||||
|
||||
with open('version.txt') as f:
|
||||
version = f.read().strip()
|
||||
@ -66,6 +72,8 @@ logger.info("------------------------- Request --------------------------")
|
||||
logger.info("CHATGPT_BASE_URL: " + str(chatgpt_base_url_list))
|
||||
logger.info("PROXY_URL: " + str(proxy_url_list))
|
||||
logger.info("EXPORT_PROXY_URL: " + str(export_proxy_url))
|
||||
logger.info("IMPERSONATE: " + str(impersonate_list))
|
||||
logger.info("USER_AGENTS: " + str(user_agents_list))
|
||||
logger.info("---------------------- Functionality -----------------------")
|
||||
logger.info("HISTORY_DISABLED: " + str(history_disabled))
|
||||
logger.info("POW_DIFFICULTY: " + str(pow_difficulty))
|
||||
@ -78,5 +86,5 @@ logger.info("SCHEDULED_REFRESH: " + str(scheduled_refresh))
|
||||
logger.info("RANDOM_TOKEN: " + str(random_token))
|
||||
logger.info("------------------------- Gateway --------------------------")
|
||||
logger.info("ENABLE_GATEWAY: " + str(enable_gateway))
|
||||
|
||||
logger.info("AUTO_SEED: " + str(auto_seed))
|
||||
logger.info("-" * 60)
|
||||
107
utils/globals.py
Normal file
107
utils/globals.py
Normal file
@ -0,0 +1,107 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import utils.configs as configs
|
||||
from utils.Logger import logger
|
||||
|
||||
DATA_FOLDER = "data"
|
||||
TOKENS_FILE = os.path.join(DATA_FOLDER, "token.txt")
|
||||
REFRESH_MAP_FILE = os.path.join(DATA_FOLDER, "refresh_map.json")
|
||||
ERROR_TOKENS_FILE = os.path.join(DATA_FOLDER, "error_token.txt")
|
||||
WSS_MAP_FILE = os.path.join(DATA_FOLDER, "wss_map.json")
|
||||
FP_FILE = os.path.join(DATA_FOLDER, "fp_map.json")
|
||||
SEED_MAP_FILE = os.path.join(DATA_FOLDER, "seed_map.json")
|
||||
CONVERSATION_MAP_FILE = os.path.join(DATA_FOLDER, "conversation_map.json")
|
||||
|
||||
count = 0
|
||||
token_list = []
|
||||
error_token_list = []
|
||||
refresh_map = {}
|
||||
wss_map = {}
|
||||
user_agent_map = {}
|
||||
seed_map = {}
|
||||
conversation_map = {}
|
||||
impersonate_list = [
|
||||
"chrome99",
|
||||
"chrome100",
|
||||
"chrome101",
|
||||
"chrome104",
|
||||
"chrome107",
|
||||
"chrome110",
|
||||
"chrome116",
|
||||
"chrome119",
|
||||
"chrome120",
|
||||
"chrome123",
|
||||
"edge99",
|
||||
"edge101",
|
||||
] if not configs.impersonate_list else configs.impersonate_list
|
||||
|
||||
if not os.path.exists(DATA_FOLDER):
|
||||
os.makedirs(DATA_FOLDER)
|
||||
|
||||
if os.path.exists(REFRESH_MAP_FILE):
|
||||
with open(REFRESH_MAP_FILE, "r") as f:
|
||||
try:
|
||||
refresh_map = json.load(f)
|
||||
except:
|
||||
refresh_map = {}
|
||||
else:
|
||||
refresh_map = {}
|
||||
|
||||
if os.path.exists(WSS_MAP_FILE):
|
||||
with open(WSS_MAP_FILE, "r") as f:
|
||||
try:
|
||||
wss_map = json.load(f)
|
||||
except:
|
||||
wss_map = {}
|
||||
else:
|
||||
wss_map = {}
|
||||
|
||||
if os.path.exists(FP_FILE):
|
||||
with open(FP_FILE, "r", encoding="utf-8") as f:
|
||||
try:
|
||||
user_agent_map = json.load(f)
|
||||
except:
|
||||
user_agent_map = {}
|
||||
else:
|
||||
user_agent_map = {}
|
||||
|
||||
if os.path.exists(SEED_MAP_FILE):
|
||||
with open(SEED_MAP_FILE, "r") as f:
|
||||
try:
|
||||
seed_map = json.load(f)
|
||||
except:
|
||||
seed_map = {}
|
||||
else:
|
||||
seed_map = {}
|
||||
|
||||
if os.path.exists(CONVERSATION_MAP_FILE):
|
||||
with open(CONVERSATION_MAP_FILE, "r") as f:
|
||||
try:
|
||||
conversation_map = json.load(f)
|
||||
except:
|
||||
conversation_map = {}
|
||||
else:
|
||||
conversation_map = {}
|
||||
|
||||
if os.path.exists(TOKENS_FILE):
|
||||
with open(TOKENS_FILE, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
token_list.append(line.strip())
|
||||
else:
|
||||
with open(TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
pass
|
||||
|
||||
if os.path.exists(ERROR_TOKENS_FILE):
|
||||
with open(ERROR_TOKENS_FILE, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip() and not line.startswith("#"):
|
||||
error_token_list.append(line.strip())
|
||||
else:
|
||||
with open(ERROR_TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
pass
|
||||
|
||||
if token_list:
|
||||
logger.info(f"Token list count: {len(token_list)}, Error token list count: {len(error_token_list)}")
|
||||
logger.info("-" * 60)
|
||||
@ -1,7 +1,7 @@
|
||||
from fastapi import HTTPException
|
||||
|
||||
from utils.Logger import logger
|
||||
from utils.config import retry_times
|
||||
from utils.configs import retry_times
|
||||
|
||||
|
||||
async def async_retry(func, *args, max_retries=retry_times, **kwargs):
|
||||
|
||||
@ -1 +1 @@
|
||||
1.5.11
|
||||
1.6.8-beta1
|
||||
Loading…
Reference in New Issue
Block a user