From c9c45fdfec2a7f2dc1c5d39b95fae6a63519cc8d Mon Sep 17 00:00:00 2001 From: Dr Jones Date: Thu, 16 Apr 2026 00:44:42 -0700 Subject: [PATCH] Fix empty proxy sources (empty pool), first-run bat, elite hint - load_settings/sanitize_settings: treat sources=[] as invalid; restore defaults (all([]) was true) - _build_pool: defensive default sources + clearer empty-fetch log; elite-only empty fetch message - FirstRun_Build_And_Install.bat: Python check + setup_and_build.ps1 - README + test for empty sources sanitize Made-with: Cursor --- FirstRun_Build_And_Install.bat | 31 +++++++++++++++++++++++++++++++ README.md | 2 +- proxy_chain_manager/config.py | 12 ++++++++++-- proxy_chain_manager/service.py | 25 +++++++++++++++++++++++-- tests/test_proxy_god.py | 7 +++++++ 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 FirstRun_Build_And_Install.bat diff --git a/FirstRun_Build_And_Install.bat b/FirstRun_Build_And_Install.bat new file mode 100644 index 0000000..f69d008 --- /dev/null +++ b/FirstRun_Build_And_Install.bat @@ -0,0 +1,31 @@ +@echo off +setlocal +title Proxy God — first-time build +cd /d "%~dp0" + +echo. +echo === Proxy God: upgrade pip, install deps, build exe, Desktop shortcut === +echo. + +where py >nul 2>nul && py -3 -c "import sys; assert sys.version_info>=(3,10)" 2>nul && goto RUN +where python >nul 2>nul && python -c "import sys; assert sys.version_info>=(3,10)" 2>nul && goto RUN + +echo [X] Python 3.10+ not found on PATH. +echo winget install Python.Python.3.12 --accept-package-agreements --accept-source-agreements +echo Or https://www.python.org/downloads/ ^(enable "Add python.exe to PATH"^) +echo. +pause +exit /b 1 + +:RUN +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\setup_and_build.ps1" +if errorlevel 1 ( + echo. + echo Build failed — see messages above. + pause + exit /b 1 +) +echo. +echo === Done. Use Desktop: "Proxy God.lnk" === +pause +endlocal diff --git a/README.md b/README.md index 4a510cf..b31bb2f 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Typical flow: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\setup_and_build.ps1 ``` - Same steps from **`build_exe.bat`** in the repo root (it calls that script). + Same steps from **`build_exe.bat`** or double‑click **`FirstRun_Build_And_Install.bat`** (checks Python 3.10+, then runs the same script; pauses at the end so you can read errors). | Where | What | |--------|------| diff --git a/proxy_chain_manager/config.py b/proxy_chain_manager/config.py index 1adcb19..1d2aee1 100644 --- a/proxy_chain_manager/config.py +++ b/proxy_chain_manager/config.py @@ -217,6 +217,10 @@ def sanitize_settings(s: Settings) -> tuple[Settings, bool]: except (TypeError, ValueError): s.validation_timeout_seconds = 12.0 changed = True + # Empty list survives "all(isinstance…)" — would skip all fetches and yield an empty pool. + if not isinstance(s.sources, list) or not all(isinstance(x, str) for x in s.sources) or len(s.sources) == 0: + s.sources = list(Settings().sources) + changed = True return s, changed @@ -240,8 +244,12 @@ def load_settings() -> Settings: for k, v in raw.items(): if hasattr(s, k): setattr(s, k, v) - if not isinstance(s.sources, list) or not all(isinstance(x, str) for x in s.sources): - s.sources = Settings().sources + if ( + not isinstance(s.sources, list) + or not all(isinstance(x, str) for x in s.sources) + or len(s.sources) == 0 + ): + s.sources = list(Settings().sources) if s.obfuscation_mode not in OBFUSCATION_MODES: s.obfuscation_mode = "auto" if not isinstance(s.pinned_chain, list): diff --git a/proxy_chain_manager/service.py b/proxy_chain_manager/service.py index 8545b64..7fff1cf 100644 --- a/proxy_chain_manager/service.py +++ b/proxy_chain_manager/service.py @@ -469,6 +469,14 @@ class ChainService: timeout=55.0, ) entries = normalize_entries(rows, s.prefer_elite) + if s.prefer_elite and rows and not entries: + self._notify({ + "type": "log", + "text": ( + f"Source returned {len(rows)} proxies but none marked “elite” — all skipped. " + "Turn off “Elite proxies only” in Chain Builder if every fetch is empty." + ), + }) self._notify({"type": "log", "text": f"Fetched {len(entries)} from source."}) return entries except asyncio.TimeoutError: @@ -478,13 +486,26 @@ class ChainService: self._notify({"type": "log", "text": f"Fetch error: {e!s}"}) return [] - results = await asyncio.gather(*(_fetch_one(u) for u in s.sources)) + src_urls = list(s.sources) if s.sources else list(Settings().sources) + if not s.sources: + self._notify({ + "type": "log", + "text": "Settings had no proxy sources — using built-in defaults (save Settings to persist).", + }) + if not src_urls: + self._notify({"type": "log", "text": "No proxy source URLs configured — cannot build a pool."}) + return [] + + results = await asyncio.gather(*(_fetch_one(u) for u in src_urls)) raw_urls: list[str] = [] for chunk in results: raw_urls.extend(chunk) if not raw_urls: - self._notify({"type": "log", "text": "No proxies fetched from any source!"}) + self._notify({ + "type": "log", + "text": "No proxies fetched from any source (check network, URLs, or turn off “Elite only” if every list is filtered empty).", + }) return [] # Deduplicate, remove blacklisted, shuffle before capping diff --git a/tests/test_proxy_god.py b/tests/test_proxy_god.py index bed1383..4484ac2 100644 --- a/tests/test_proxy_god.py +++ b/tests/test_proxy_god.py @@ -46,6 +46,13 @@ class TestConfig(unittest.TestCase): self.assertNotIn("secret", r) self.assertNotIn("alice", r) + def test_sanitize_restores_empty_sources(self) -> None: + s = Settings() + s.sources = [] + s2, changed = sanitize_settings(s) + self.assertTrue(changed) + self.assertGreater(len(s2.sources), 0) + def test_sanitize_clamps_extremes(self) -> None: s = Settings( chain_length=0,