Add ban tester, signup prep, exit IP intel, and sticky-exit hold.
New GUI tabs probe popular sites for exit-IP bans and prep signup autofill through the chain; Live tab shows geo/ASN/datacenter flags, and sticky-exit keeps the same egress IP during signup flows. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
92
proxy_chain_manager/ban_tester.py
Normal file
92
proxy_chain_manager/ban_tester.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable
|
||||
|
||||
import httpx
|
||||
|
||||
# High-traffic destinations that often reveal proxy bans quickly.
|
||||
POPULAR_SITES: tuple[tuple[str, str], ...] = (
|
||||
("Google", "https://www.google.com/generate_204"),
|
||||
("YouTube", "https://www.youtube.com/"),
|
||||
("Facebook", "https://www.facebook.com/"),
|
||||
("Instagram", "https://www.instagram.com/"),
|
||||
("X", "https://x.com/"),
|
||||
("Reddit", "https://www.reddit.com/"),
|
||||
("Wikipedia", "https://www.wikipedia.org/"),
|
||||
("Amazon", "https://www.amazon.com/"),
|
||||
("Netflix", "https://www.netflix.com/"),
|
||||
("GitHub", "https://github.com/"),
|
||||
("Cloudflare", "https://www.cloudflare.com/"),
|
||||
("Microsoft", "https://www.microsoft.com/"),
|
||||
("TikTok", "https://www.tiktok.com/"),
|
||||
("BBC", "https://www.bbc.com/"),
|
||||
("DuckDuckGo", "https://duckduckgo.com/"),
|
||||
)
|
||||
|
||||
_BANNED_STATUS_CODES = {401, 403, 407, 418, 429, 451}
|
||||
_BANNED_TEXT_HINTS = (
|
||||
"access denied",
|
||||
"forbidden",
|
||||
"temporarily blocked",
|
||||
"request blocked",
|
||||
"unusual traffic",
|
||||
"captcha",
|
||||
"challenge required",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BanTestResult:
|
||||
site: str
|
||||
url: str
|
||||
status: str # ok | banned | error
|
||||
code: int | None
|
||||
detail: str
|
||||
|
||||
|
||||
def _looks_banned_body(body: str) -> bool:
|
||||
t = (body or "").lower()
|
||||
return any(h in t for h in _BANNED_TEXT_HINTS)
|
||||
|
||||
|
||||
def _test_one(proxy_url: str, site: str, url: str, timeout_seconds: float) -> BanTestResult:
|
||||
timeout = httpx.Timeout(timeout_seconds, connect=min(8.0, timeout_seconds))
|
||||
try:
|
||||
with httpx.Client(
|
||||
proxy=proxy_url,
|
||||
timeout=timeout,
|
||||
verify=False,
|
||||
follow_redirects=True,
|
||||
headers={"User-Agent": "Mozilla/5.0"},
|
||||
) as c:
|
||||
r = c.get(url)
|
||||
body = r.text[:1800] if r.text else ""
|
||||
if r.status_code in _BANNED_STATUS_CODES or _looks_banned_body(body):
|
||||
return BanTestResult(site, url, "banned", r.status_code, f"HTTP {r.status_code}")
|
||||
if 200 <= r.status_code < 400:
|
||||
return BanTestResult(site, url, "ok", r.status_code, f"HTTP {r.status_code}")
|
||||
return BanTestResult(site, url, "error", r.status_code, f"HTTP {r.status_code}")
|
||||
except Exception as e:
|
||||
return BanTestResult(site, url, "error", None, f"{type(e).__name__}: {e}")
|
||||
|
||||
|
||||
def run_ban_tests(
|
||||
proxy_url: str,
|
||||
timeout_seconds: float = 12.0,
|
||||
sites: Iterable[tuple[str, str]] = POPULAR_SITES,
|
||||
) -> list[BanTestResult]:
|
||||
out: list[BanTestResult] = []
|
||||
for site, url in sites:
|
||||
out.append(_test_one(proxy_url, site, url, timeout_seconds))
|
||||
return out
|
||||
|
||||
|
||||
def test_single_site(
|
||||
proxy_url: str,
|
||||
site: str,
|
||||
url: str,
|
||||
timeout_seconds: float = 10.0,
|
||||
) -> BanTestResult:
|
||||
"""Probe one URL through the proxy. Used by pre-flight checks."""
|
||||
return _test_one(proxy_url, site, url, timeout_seconds)
|
||||
Reference in New Issue
Block a user