#!/usr/bin/env python3 """Auto-rotate Google Scholar proxy for paper-search-mcp. Fetches fresh free proxies, tests them against scholar.google.com, and updates ~/.config/paper-search-mcp/.env with the first working one. Usage: python3 rotate_gs_proxy.py # one-shot test & update python3 rotate_gs_proxy.py --force # always replace (even if current works) """ import os import sys import time import requests ENV_PATH = os.path.expanduser("~/.config/paper-search-mcp/.env") SCHOLAR_URL = "https://scholar.google.com" TEST_TIMEOUT = 5 PROXY_LISTS = [ "https://raw.githubusercontent.com/TheSpeedX/SOCKS-List/master/http.txt", "https://raw.githubusercontent.com/ShiftyTR/Proxy-List/master/http.txt", "https://raw.githubusercontent.com/clarketm/proxy-list/master/proxy-list-raw.txt", ] MAX_TEST = 150 def read_env() -> dict: env = {} if os.path.exists(ENV_PATH): with open(ENV_PATH) as f: for line in f: line = line.strip() if "=" in line: k, v = line.split("=", 1) env[k.strip()] = v.strip() return env def write_env(updates: dict): lines = [] if os.path.exists(ENV_PATH): with open(ENV_PATH) as f: lines = f.readlines() # Update in-place new_lines = [] for line in lines: stripped = line.strip() for k, v in updates.items(): if stripped.startswith(f"{k}="): new_lines.append(f"{k}={v}\n") break else: new_lines.append(line) # Append missing keys for k, v in updates.items(): if not any(ln.strip().startswith(f"{k}=") for ln in new_lines): new_lines.append(f"{k}={v}\n") with open(ENV_PATH, "w") as f: f.writelines(new_lines) return len(updates) def test_proxy(proxy_url: str) -> bool: proxies = {"http": proxy_url, "https": proxy_url} try: r = requests.get(SCHOLAR_URL, proxies=proxies, timeout=TEST_TIMEOUT, headers={"User-Agent": "Mozilla/5.0"}) return r.status_code == 200 except Exception: return False def find_working_proxy() -> str | None: seen = set() for list_url in PROXY_LISTS: try: r = requests.get(list_url, timeout=10) raw = r.text except Exception: continue lines = [l.strip() for l in raw.split("\n") if l.strip() and ":" in l] count = 0 for entry in lines: if entry in seen: continue seen.add(entry) proxy_url = f"http://{entry}" if test_proxy(proxy_url): print(f"✓ Working proxy: {proxy_url}") return proxy_url count += 1 if count >= MAX_TEST: break return None def main(): force = "--force" in sys.argv env = read_env() current = env.get("PAPER_SEARCH_MCP_GOOGLE_SCHOLAR_PROXY_URL", "") # If current proxy works and not forcing, keep it if not force and current and test_proxy(current): print(f"✓ Current proxy works: {current}") return if current: print(f"✗ Current proxy failed: {current}") print("🔍 Searching for a working free proxy...") proxy = find_working_proxy() if proxy: write_env({"PAPER_SEARCH_MCP_GOOGLE_SCHOLAR_PROXY_URL": proxy}) print(f"✅ Updated to: {proxy}") else: print("❌ No working proxy found.") sys.exit(1) if __name__ == "__main__": main()