Refactor OpenaiChat authentication flow; replace get_nodriver with async context manager and improve error handling

Update backend_anon_url in har_file.py for correct endpoint
Add async context manager for get_nodriver_session in requests module
Fix start-browser.sh to remove stale cookie file before launching Chrome
This commit is contained in:
hlohaus 2025-10-02 02:08:20 +02:00
parent 7d184411e2
commit 4399b432c4
4 changed files with 27 additions and 12 deletions

View file

@ -21,7 +21,7 @@ from ..base_provider import AsyncAuthedProvider, ProviderModelMixin
from ...typing import AsyncResult, Messages, Cookies, MediaListType from ...typing import AsyncResult, Messages, Cookies, MediaListType
from ...requests.raise_for_status import raise_for_status from ...requests.raise_for_status import raise_for_status
from ...requests import StreamSession from ...requests import StreamSession
from ...requests import get_nodriver from ...requests import get_nodriver_session
from ...image import ImageRequest, to_image, to_bytes, is_accepted_format, detect_file_type from ...image import ImageRequest, to_image, to_bytes, is_accepted_format, detect_file_type
from ...errors import MissingAuthError, NoValidHarFileError, ModelNotFoundError from ...errors import MissingAuthError, NoValidHarFileError, ModelNotFoundError
from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult, ImageResponse, ImagePreview, ResponseType, format_link from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult, ImageResponse, ImagePreview, ResponseType, format_link
@ -852,8 +852,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
@classmethod @classmethod
async def nodriver_auth(cls, proxy: str = None): async def nodriver_auth(cls, proxy: str = None):
browser, stop_browser = await get_nodriver(proxy=proxy) async with get_nodriver_session(proxy=proxy) as browser:
try:
page = await browser.get(cls.url) page = await browser.get(cls.url)
def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None): def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
if event.request.url == start_url or event.request.url.startswith(conversation_url): if event.request.url == start_url or event.request.url.startswith(conversation_url):
@ -882,14 +881,21 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request) page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
await page.reload() await page.reload()
user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True) user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True)
if cls.needs_auth: debug.log(f"OpenaiChat: User-Agent: {user_agent}")
await page.select('[data-testid="accounts-profile-button"]', 300) for _ in range(3):
textarea = await page.select("#prompt-textarea", 300) try:
await textarea.send_keys("Hello") if cls.needs_auth:
await asyncio.sleep(1) await page.select('[data-testid="accounts-profile-button"]', 300)
textarea = await page.select("#prompt-textarea", 300)
await textarea.send_keys("Hello")
await asyncio.sleep(1)
except nodriver.core.connection.ProtocolException:
continue
break
button = await page.select("[data-testid=\"send-button\"]") button = await page.select("[data-testid=\"send-button\"]")
if button: if button:
await button.click() await button.click()
debug.log("OpenaiChat: 'Hello' sended")
while True: while True:
body = await page.evaluate("JSON.stringify(window.__remixContext)", return_by_value=True) body = await page.evaluate("JSON.stringify(window.__remixContext)", return_by_value=True)
if hasattr(body, "value"): if hasattr(body, "value"):
@ -902,17 +908,19 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
if cls._api_key is not None or not cls.needs_auth: if cls._api_key is not None or not cls.needs_auth:
break break
await asyncio.sleep(1) await asyncio.sleep(1)
debug.log(f"OpenaiChat: Access token: {'False' if cls._api_key is None else cls._api_key[:12]+'...'}")
while True: while True:
if cls.request_config.proof_token: if cls.request_config.proof_token:
break break
await asyncio.sleep(1) await asyncio.sleep(1)
debug.log(f"OpenaiChat: Proof token: Yes")
cls.request_config.data_build = await page.evaluate("document.documentElement.getAttribute('data-build')") cls.request_config.data_build = await page.evaluate("document.documentElement.getAttribute('data-build')")
cls.request_config.cookies = await page.send(get_cookies([cls.url])) cls.request_config.cookies = await page.send(get_cookies([cls.url]))
await page.close() await page.close()
cls._create_request_args(cls.request_config.cookies, cls.request_config.headers, user_agent=user_agent) cls._create_request_args(cls.request_config.cookies, cls.request_config.headers, user_agent=user_agent)
cls._set_api_key(cls._api_key) cls._set_api_key(cls._api_key)
finally: debug.log(f"OpenaiChat: Sleep 10s")
stop_browser() await asyncio.sleep(10)
@staticmethod @staticmethod
def get_default_headers() -> Dict[str, str]: def get_default_headers() -> Dict[str, str]:

View file

@ -19,7 +19,7 @@ from ... import debug
arkose_url = "https://tcr9i.chat.openai.com/fc/gt2/public_key/35536E1E-65B4-4D96-9D97-6ADB7EFF8147" arkose_url = "https://tcr9i.chat.openai.com/fc/gt2/public_key/35536E1E-65B4-4D96-9D97-6ADB7EFF8147"
backend_url = "https://chatgpt.com/backend-api/f/conversation" backend_url = "https://chatgpt.com/backend-api/f/conversation"
prepare_url = "https://chatgpt.com/backend-api/f/conversation/prepare" prepare_url = "https://chatgpt.com/backend-api/f/conversation/prepare"
backend_anon_url = "https://chatgpt.com/backend-anon/conversation" backend_anon_url = "https://chatgpt.com/backend-anon/f/conversation"
start_url = "https://chatgpt.com/" start_url = "https://chatgpt.com/"
conversation_url = "https://chatgpt.com/c/" conversation_url = "https://chatgpt.com/c/"

View file

@ -8,6 +8,7 @@ from urllib.parse import urlparse
from typing import Iterator, AsyncIterator from typing import Iterator, AsyncIterator
from http.cookies import Morsel from http.cookies import Morsel
from pathlib import Path from pathlib import Path
from contextlib import asynccontextmanager
import asyncio import asyncio
try: try:
from curl_cffi.requests import Session, Response from curl_cffi.requests import Session, Response
@ -218,6 +219,12 @@ async def get_nodriver(
BrowserConfig.stop_browser = on_stop BrowserConfig.stop_browser = on_stop
return browser, on_stop return browser, on_stop
@asynccontextmanager
async def get_nodriver_session(**kwargs):
browser, stop_browser = await get_nodriver(**kwargs)
yield browser
stop_browser()
async def sse_stream(iter_lines: AsyncIterator[bytes]) -> AsyncIterator[dict]: async def sse_stream(iter_lines: AsyncIterator[bytes]) -> AsyncIterator[dict]:
if hasattr(iter_lines, "content"): if hasattr(iter_lines, "content"):
iter_lines = iter_lines.content iter_lines = iter_lines.content

View file

@ -2,5 +2,5 @@
while true; do while true; do
sleep 5 sleep 5
rm ~/.config/g4f/cookies/.nodriver_is_open rm ~/.config/g4f/cookies/.nodriver_is_open
google-chrome --remote-allow-origins=* --no-first-run --no-service-autorun --no-default-browser-check --homepage=about:blank --no-pings --password-store=basic --disable-infobars --disable-breakpad --disable-dev-shm-usage --disable-session-crashed-bubble --disable-search-engine-choice-screen --user-data-dir="~/.config/g4f-nodriver" --disable-session-crashed-bubble --disable-features=IsolateOrigins,site-per-process --no-sandbox --remote-debugging-host=127.0.0.1 --remote-debugging-port=57011 google-chrome --remote-allow-origins=* --no-first-run --no-service-autorun --no-default-browser-check --homepage=about:blank --no-pings --password-store=basic --disable-infobars --disable-breakpad --disable-dev-shm-usage --disable-session-crashed-bubble --disable-search-engine-choice-screen --user-data-dir="~/.config/g4f-nodriver" --disable-session-crashed-bubble --disable-features=IsolateOrigins,site-per-process --remote-debugging-host=127.0.0.1 --remote-debugging-port=57011
done done