Files
any-auto-register/api/chatgpt.py
2026-03-31 19:57:13 +08:00

183 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""ChatGPT 专用功能 API"""
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session
from pydantic import BaseModel
from typing import Optional
from core.db import AccountModel, get_session
from services.chatgpt_account_state import apply_chatgpt_status_policy
import json, sys
router = APIRouter(prefix="/chatgpt", tags=["chatgpt"])
COUNTRIES = ["SG", "US", "TR", "JP", "HK", "GB", "AU", "CA", "IN", "BR", "MX"]
class UploadRequest(BaseModel):
account_ids: list[int]
cpa_api_url: Optional[str] = None
cpa_api_token: Optional[str] = None
team_manager_url: Optional[str] = None
team_manager_key: Optional[str] = None
def _get_account(account_id: int, session: Session) -> AccountModel:
acc = session.get(AccountModel, account_id)
if not acc or acc.platform != "chatgpt":
raise HTTPException(404, "账号不存在")
return acc
def _to_codex_account(acc: AccountModel):
"""转换为 codex-register 的 Account 对象duck-typing"""
extra = acc.get_extra()
class _Acc:
pass
a = _Acc()
a.email = acc.email
a.access_token = extra.get("access_token") or acc.token
a.refresh_token = extra.get("refresh_token", "")
a.id_token = extra.get("id_token", "")
a.session_token = extra.get("session_token", "")
a.client_id = extra.get("client_id", "app_EMoamEEZ73f0CkXaXp7hrann")
a.cookies = extra.get("cookies", "")
a.user_id = acc.user_id
return a
def _persist_local_probe(acc: AccountModel, probe: dict, session: Session) -> None:
extra = acc.get_extra()
extra["chatgpt_local"] = probe
acc.set_extra(extra)
apply_chatgpt_status_policy(acc, local_probe=probe)
from datetime import datetime
acc.updated_at = datetime.utcnow()
session.add(acc)
session.commit()
# ── Token 刷新 ──────────────────────────────────────────────
@router.post("/{account_id}/refresh-token")
def refresh_token(account_id: int, proxy: Optional[str] = None,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.token_refresh import TokenRefreshManager
manager = TokenRefreshManager(proxy_url=proxy)
result = manager.refresh_account(codex_acc)
if result.success:
extra = acc.get_extra()
extra["access_token"] = result.access_token
if result.refresh_token:
extra["refresh_token"] = result.refresh_token
acc.set_extra(extra)
acc.token = result.access_token
from datetime import datetime
acc.updated_at = datetime.utcnow()
session.add(acc)
session.commit()
return {"ok": True, "access_token": result.access_token[:40] + "..."}
raise HTTPException(400, result.error_message)
# ── 生成支付链接 ────────────────────────────────────────────
class PaymentReq(BaseModel):
plan: str = "plus" # plus | team
country: str = "SG"
proxy: Optional[str] = None
workspace_name: str = "MyTeam"
seat_quantity: int = 5
price_interval: str = "month"
@router.post("/{account_id}/payment-link")
def generate_payment_link(account_id: int, req: PaymentReq,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.payment import generate_plus_link, generate_team_link
if req.plan == "plus":
url = generate_plus_link(codex_acc, proxy=req.proxy, country=req.country)
else:
url = generate_team_link(
codex_acc, workspace_name=req.workspace_name,
price_interval=req.price_interval, seat_quantity=req.seat_quantity,
proxy=req.proxy, country=req.country
)
return {"url": url, "plan": req.plan, "country": req.country}
# ── 检查订阅状态 ────────────────────────────────────────────
@router.get("/{account_id}/subscription")
def check_subscription(account_id: int, proxy: Optional[str] = None,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.status_probe import probe_local_chatgpt_status
probe = probe_local_chatgpt_status(codex_acc, proxy=proxy)
_persist_local_probe(acc, probe, session)
return {
"email": acc.email,
"subscription": probe.get("subscription", {}).get("plan", "unknown"),
"probe": probe,
}
@router.post("/{account_id}/probe-local")
def probe_local_status(account_id: int, proxy: Optional[str] = None,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.status_probe import probe_local_chatgpt_status
probe = probe_local_chatgpt_status(codex_acc, proxy=proxy)
_persist_local_probe(acc, probe, session)
return {"ok": True, "email": acc.email, "probe": probe}
# ── CPA 上传 ────────────────────────────────────────────────
class CpaUploadReq(BaseModel):
api_url: str
api_key: str = ""
@router.post("/{account_id}/upload-cpa")
def upload_cpa(account_id: int, req: CpaUploadReq,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.cpa_upload import upload_to_cpa, generate_token_json
token_data = generate_token_json(codex_acc)
ok, msg = upload_to_cpa(token_data, api_url=req.api_url, api_key=req.api_key)
return {"ok": ok, "message": msg}
class Sub2ApiUploadReq(BaseModel):
api_url: str
api_key: str = ""
@router.post("/{account_id}/upload-sub2api")
def upload_sub2api(account_id: int, req: Sub2ApiUploadReq,
session: Session = Depends(get_session)):
acc = _get_account(account_id, session)
codex_acc = _to_codex_account(acc)
from platforms.chatgpt.sub2api_upload import upload_to_sub2api
ok, msg = upload_to_sub2api(
codex_acc,
api_url=req.api_url,
api_key=req.api_key,
)
return {"ok": ok, "message": msg}