mirror of
https://github.com/sums001/Windows-Copilot-API
synced 2026-08-09 13:11:24 +00:00
Merge branch 'dev'
This commit is contained in:
+14
-15
@@ -38,6 +38,7 @@ from urllib.parse import parse_qs, urlparse
|
||||
from playwright.sync_api import sync_playwright, Error as PlaywrightError
|
||||
|
||||
from .auth import DEFAULT_AUTH_FILE, DEFAULT_PROFILE_DIR
|
||||
from .useragent import CHROME_UA
|
||||
|
||||
COPILOT_URL = "https://copilot.microsoft.com/"
|
||||
|
||||
@@ -46,17 +47,14 @@ COPILOT_URL = "https://copilot.microsoft.com/"
|
||||
# We reach into that frame to click its checkbox — see _click_turnstile.
|
||||
_TURNSTILE_IFRAME = "iframe[src*='challenges.cloudflare.com'], iframe[src*='turnstile']"
|
||||
|
||||
# A stock desktop Chrome UA used in headless mode. Headless Chromium otherwise
|
||||
# advertises "HeadlessChrome/..." in both the request UA header and
|
||||
# navigator.userAgent — a blatant bot signal to Cloudflare Turnstile, and a UA
|
||||
# that can mismatch the cf_clearance UA-binding the curl_cffi driver relies on
|
||||
# (clearance is bound to the UA that earned it). Pinning a normal Chrome UA makes
|
||||
# the session look ordinary and keeps the earned clearance reusable. Bump the
|
||||
# version occasionally to stay current.
|
||||
_STEALTH_UA = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
||||
)
|
||||
# The one UA every browser context advertises — the same string the curl_cffi
|
||||
# driver presents (see copilot/useragent.py). Applied to *both* headless and
|
||||
# visible launches so clearance earned by either is reusable by the driver:
|
||||
# cf_clearance is bound to the earning UA, so they must all match. It also hides
|
||||
# the "HeadlessChrome/..." token headless Chromium otherwise leaks (a blatant bot
|
||||
# tell). Because CHROME_UA tracks Playwright's bundled Chromium version, the
|
||||
# override doesn't contradict the browser's native Sec-CH-UA client hint.
|
||||
_STEALTH_UA = CHROME_UA
|
||||
|
||||
# Injected into every frame to hide the residual automation tell that survives
|
||||
# --disable-blink-features=AutomationControlled in some Chromium builds.
|
||||
@@ -189,10 +187,11 @@ class BrowserCopilot:
|
||||
# switch; its presence is a cheap bot tell Turnstile reads.
|
||||
ignore_default_args=["--enable-automation"],
|
||||
)
|
||||
# Hide the "HeadlessChrome" UA only when actually headless; the visible
|
||||
# window already uses a normal Chrome UA (and that path works today).
|
||||
if self.headless:
|
||||
launch_kwargs["user_agent"] = _STEALTH_UA
|
||||
# Pin the UA on every launch, headless or visible. Both must earn
|
||||
# cf_clearance under the exact string the curl_cffi driver replays;
|
||||
# leaving the visible window on Playwright's native UA would bind the
|
||||
# clearance to a version the driver doesn't present, re-gating chat.
|
||||
launch_kwargs["user_agent"] = _STEALTH_UA
|
||||
if self.proxy:
|
||||
launch_kwargs["proxy"] = self._parse_proxy(self.proxy)
|
||||
self._context = self._pw.chromium.launch_persistent_context(
|
||||
|
||||
+8
-1
@@ -25,6 +25,7 @@ _CURL_SOCKET_BAD = -1
|
||||
from .challenges import solve_copilot_challenge, solve_hashcash
|
||||
from .models import AbstractProvider, Conversation, ImageResponse, ImageType
|
||||
from .protocol import CHAT_WEBSOCKET_URL, CONSENTS_FRAME, SET_OPTIONS_FRAME
|
||||
from .useragent import CHROME_CLIENT_HINTS, CHROME_UA, IMPERSONATE_TARGET
|
||||
from .utils import drain_json, is_accepted_format, raise_for_status, to_bytes
|
||||
|
||||
|
||||
@@ -119,7 +120,13 @@ class Copilot(AbstractProvider):
|
||||
with Session(
|
||||
timeout=timeout,
|
||||
proxy=proxy,
|
||||
impersonate="chrome",
|
||||
# Pin the TLS/HTTP2 fingerprint, then override the UA + client hints so
|
||||
# the wire presentation is a fixed Windows Chrome. cf_clearance is bound
|
||||
# to the earning UA; the browsers that earn it present this same string,
|
||||
# so the driver must too — otherwise every turn is gated behind a
|
||||
# Cloudflare Turnstile. See copilot/useragent.py.
|
||||
impersonate=IMPERSONATE_TARGET,
|
||||
headers={"User-Agent": CHROME_UA, **CHROME_CLIENT_HINTS},
|
||||
cookies=cookies,
|
||||
) as session:
|
||||
# Establish cookies + Cloudflare clearance (anonymous is fine).
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
"""The single User-Agent the whole bridge presents to Cloudflare.
|
||||
|
||||
Cloudflare binds ``cf_clearance`` to the *exact* User-Agent string that earned
|
||||
it. The bridge touches that one cookie from three places — the curl_cffi chat
|
||||
driver (which *uses* it), the headless refresh, and the interactive login (which
|
||||
*earn* it) — so all three must present a byte-identical UA or the clearance is
|
||||
distrusted and the chat socket gates every turn behind a Cloudflare Turnstile.
|
||||
|
||||
Keeping the string here (imported by both :mod:`copilot.driver` and
|
||||
:mod:`copilot.browser`) makes drift impossible.
|
||||
|
||||
Why these exact values:
|
||||
|
||||
* ``CHROME_UA`` is a real desktop **Windows** Chrome UA. We standardise on the
|
||||
same major version Playwright actually bundles (see the maintenance note), so
|
||||
overriding a launched Chromium's UA to this string does *not* contradict the
|
||||
browser's native ``Sec-CH-UA`` client hint — both say the same version.
|
||||
* ``IMPERSONATE_TARGET`` pins curl_cffi to a fixed TLS/HTTP2 fingerprint. Left as
|
||||
the bare ``"chrome"`` alias it tracks curl_cffi's ``DEFAULT_CHROME``, which
|
||||
advances on every upgrade (and ships a *macOS* UA) — a moving target that
|
||||
silently re-breaks the UA match. Pin to the closest stable profile instead; the
|
||||
driver overrides the UA + client hints on top so the wire presentation stays
|
||||
Windows/``CHROME_UA`` regardless of the profile's native UA.
|
||||
|
||||
MAINTENANCE: bump ``CHROME_UA``'s major version whenever ``playwright install``
|
||||
upgrades the bundled Chromium (check ``chromium.launch().version``). If the
|
||||
constant lags the real browser, the browser's native ``Sec-CH-UA`` out-drifts the
|
||||
spoofed UA and Turnstile sees the mismatch. One line, one place.
|
||||
"""
|
||||
|
||||
# Real desktop Windows Chrome. Must match Playwright's bundled Chromium major
|
||||
# version (currently 148) so the UA override introduces no client-hint conflict.
|
||||
CHROME_UA = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
# Client hints that must accompany CHROME_UA so the platform/version a server
|
||||
# reads from the hints agrees with the UA line. Used by the curl_cffi driver,
|
||||
# which otherwise emits the impersonation profile's native (macOS) hints.
|
||||
CHROME_CLIENT_HINTS = {
|
||||
"sec-ch-ua-platform": '"Windows"',
|
||||
"sec-ch-ua": '"Google Chrome";v="148", "Chromium";v="148", "Not_A Brand";v="24"',
|
||||
}
|
||||
|
||||
# Pinned curl_cffi impersonation profile (TLS/HTTP2 fingerprint). Closest stable
|
||||
# profile to CHROME_UA's version; the UA itself is overridden on top.
|
||||
IMPERSONATE_TARGET = "chrome146"
|
||||
Reference in New Issue
Block a user