arshitmalik commited on
Commit
8674b75
·
verified ·
1 Parent(s): 9f7b8f9

Deploy v3.0 key tester with multi-source scraping + per-provider testing

Browse files
Files changed (1) hide show
  1. key_tester.py +250 -150
key_tester.py CHANGED
@@ -1,188 +1,288 @@
1
  #!/usr/bin/env python3
2
- import json, os, re, subprocess, time, urllib.request, urllib.error, ssl, random
 
3
  from pathlib import Path
4
 
5
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
6
- DATASET_REPO = os.environ.get("OPENCLAW_DATASET_REPO", "arshitmalik/yt-pipeline-data")
7
  POOL_KEY = "verified_keys.json"
8
  LOG_FILE = Path("/app/tester.log")
9
- KEYSCAN_DIR = Path("/app/keyscan")
10
- PROXY_URLS = [
11
- "https://aiapiv2.pekpik.com/v1",
12
- "https://api.chatanywhere.tech/v1",
13
- "https://api.chatanywhere.org/v1",
14
- "https://free.gpt.ge/v1",
15
- ]
16
- TEST_ENDPOINT = "/models"
 
17
 
18
  ctx = ssl.create_default_context()
19
  ctx.check_hostname = False
20
- ctx.verify_mode = ssl.CERT_NONE
21
 
22
  def log(msg):
23
  line = f"[{time.strftime('%H:%M:%S')}] {msg}"
24
- print(line)
25
  with open(LOG_FILE, "a") as f: f.write(line + "\n")
26
 
27
- def fetch_url(url, timeout=15):
 
 
 
 
 
28
  try:
29
- req = urllib.request.Request(url, headers={"User-Agent": "yt-keys/2.0"})
30
  with urllib.request.urlopen(req, timeout=timeout, context=ctx) as r:
31
  return r.read().decode("utf-8", errors="replace")
32
- except Exception:
33
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  def guess_provider(key):
36
- if key.startswith("sk-ant-api"): return "anthropic"
37
- if key.startswith("AIza"): return "google"
38
- if key.startswith("xai-"): return "xai"
39
- if key.startswith("sk-"): return "openai"
 
 
 
 
40
  return "others"
41
 
42
- def test_key(key, proxy_url):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  try:
44
- req = urllib.request.Request(f"{proxy_url}{TEST_ENDPOINT}",
45
- headers={"Authorization": f"Bearer {key}"})
46
- with urllib.request.urlopen(req, timeout=8, context=ctx) as r:
47
- return 'valid' if r.status == 200 else 'invalid'
48
  except urllib.error.HTTPError as e:
49
- return 'rate_limited' if e.code == 429 else 'invalid'
50
- except Exception:
51
- return 'invalid'
52
 
53
- def upload_verified(pool):
 
 
 
54
  try:
55
  from huggingface_hub import HfApi
56
- api = HfApi(token=HF_TOKEN)
57
- api.upload_file(
58
- path_or_fileobj=json.dumps(pool, indent=2).encode(),
59
- path_in_repo=POOL_KEY, repo_id=DATASET_REPO, repo_type="dataset")
60
- log(f"Uploaded {sum(len(v) for v in pool.values())} verified keys")
61
  except Exception as e:
62
  log(f"Upload failed: {e}")
63
 
64
- def run_keyscan():
65
- if not KEYSCAN_DIR.exists():
66
- log("keyscan directory missing, skipping")
67
- return []
68
  try:
69
- result = subprocess.run(
70
- ["node", str(KEYSCAN_DIR / "index.js"), "--json"],
71
- capture_output=True, text=True, timeout=120, cwd=str(KEYSCAN_DIR)
72
- )
73
- if result.returncode != 0:
74
- log(f"keyscan error: {result.stderr[:200]}")
75
- return []
76
- data = json.loads(result.stdout)
77
- keys = [item["key"] for item in data if item.get("key")]
78
- log(f"keyscan found {len(keys)} keys")
79
- return keys
80
  except Exception as e:
81
- log(f"keyscan failed: {e}")
82
- return []
83
-
84
- SOURCES = [
85
- "https://raw.githubusercontent.com/alistaitsacle/free-llm-api-keys/main/README.md",
86
- "https://raw.githubusercontent.com/chatanywhere/GPT_API_free/main/README.md",
87
- "https://raw.githubusercontent.com/creazycar/GPT_API_free/main/README.md",
88
- "https://raw.githubusercontent.com/ReimagineTruth/gpt_api_free/main/README.md",
89
- "https://raw.githubusercontent.com/OrrieZ/gpt_api_free/main/README.md",
90
- "https://raw.githubusercontent.com/FairyWorld/free_ai_GPT_API_free/main/README.md",
91
- "https://raw.githubusercontent.com/taurusduan/GPT_API_free-lvyou/main/README.md",
92
- "https://raw.githubusercontent.com/Poghappy/gpt_api_free/main/README.md",
93
- "https://raw.githubusercontent.com/popjane/free_chatgpt_api/main/README.md",
94
- "https://raw.githubusercontent.com/onlywyl/free_chatgpt_api/main/README.md",
95
- "https://raw.githubusercontent.com/dan1471/FREE-openai-api-keys/main/README.md",
96
- "https://raw.githubusercontent.com/racalado/free-openai-api-keys/main/README.md",
97
- "https://raw.githubusercontent.com/CyYxl2024/freeai/main/README.md",
98
- "https://raw.githubusercontent.com/qwq202/One-FAS-API/main/README.md",
99
- "https://raw.githubusercontent.com/vibheksoni/free-ai/main/README.md",
100
- "https://raw.githubusercontent.com/Mr-Abood/GPTFree/main/README.md",
101
- "https://raw.githubusercontent.com/BuluBulugege/Free-BAI/main/README.md",
102
- "https://raw.githubusercontent.com/thesecondchance/freeflow-llm/main/README.md",
103
- "https://raw.githubusercontent.com/tashfeenahmed/freellmapi/main/README.md",
104
- "https://raw.githubusercontent.com/mufeng510/Free-ChatGPT-API/main/README.md",
105
- "https://raw.githubusercontent.com/lizhenmiao/gpt4free-ts/main/README.md",
106
- "https://raw.githubusercontent.com/ling-drag0n/api-pool/main/README.md",
107
- "https://raw.githubusercontent.com/fengqiaozhu/openrouter-free-pool/main/README.md",
108
- "https://raw.githubusercontent.com/mahdi-marjani/free-llm-api/main/README.md",
109
- "https://raw.githubusercontent.com/realasfngl/ChatGPT/main/README.md",
110
- "https://raw.githubusercontent.com/Butakov-Andrey/freegpt/main/README.md",
111
- "https://raw.githubusercontent.com/elphen-wang/FreeAI/main/README.md",
112
- "https://raw.githubusercontent.com/devashish2024/freegpt/main/README.md",
113
- "https://alistaitsacle.github.io/free-llm-api-keys/",
114
- "https://free.v36.cm",
115
- "https://api.zaochuanqiu.online",
116
- "https://aipipe.org/",
117
- "https://freetheai.xyz",
118
- "https://api.algion.dev",
119
- "https://freeapi.geekgpt.site",
120
- ]
121
 
122
- def manual_scrape():
123
- all_keys = set()
124
- for url in SOURCES:
125
- log(f"Scraping {url}")
126
- text = fetch_url(url)
127
- if not text: continue
128
- matches = re.findall(r'(?:sk-[A-Za-z0-9]{48,}|AIza[0-9A-Za-z\-_]{35}|xai-[A-Za-z0-9]{20,}|sk-ant-api[A-Za-z0-9\-_]{40,})', text)
129
- for m in matches:
130
- if re.search(r'(x{5,}|qwerty|placeholder|example|0{10,})', m, re.I): continue
131
- all_keys.add(m)
132
- log(f" -> {len(matches)} candidate keys")
133
- return list(all_keys)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  def main():
136
- log("Key tester 2.0 started (keyscan priority)")
 
 
137
  while True:
138
- log("Running keyscan...")
139
- candidates = run_keyscan()
140
- if not candidates:
141
- log("keyscan returned nothing, falling back to manual scraping")
142
- candidates = manual_scrape()
143
- if not candidates:
144
- log("No candidates at all. Sleeping 600s")
145
- time.sleep(600)
146
- continue
147
-
148
- random.shuffle(candidates)
149
- test_queue = candidates[:50]
150
- verified = {p: [] for p in ["anthropic", "openai", "google", "mistral", "xai", "others"]}
151
- proxy_idx = 0
152
- global_rate_limited = False
153
-
154
- for key in test_queue:
155
- prov = guess_provider(key)
156
- if global_rate_limited: break
157
- for _ in range(len(PROXY_URLS)):
158
- p_url = PROXY_URLS[proxy_idx % len(PROXY_URLS)]
159
- res = test_key(key, p_url)
160
- if res == 'valid':
161
- verified[prov].append(key)
162
- log(f" OK [{prov}] {key[:14]}...")
163
- proxy_idx += 1
164
- break
165
- elif res == 'rate_limited':
166
- proxy_idx += 1
167
- if proxy_idx % len(PROXY_URLS) == 0:
168
- global_rate_limited = True
169
- log("All proxies rate-limited")
170
- continue
171
- else:
172
- proxy_idx += 1
173
- else:
174
- pass
175
- if global_rate_limited: break
176
-
177
- if global_rate_limited:
178
- log("Rate-limited on all proxies, will retry after sleep")
179
- else:
180
- if sum(len(v) for v in verified.values()) > 0:
181
- upload_verified(verified)
182
- else:
183
- log("No verified keys this cycle")
184
- log("Sleeping 600s...")
185
- time.sleep(600)
186
 
187
  if __name__ == "__main__":
188
  main()
 
1
  #!/usr/bin/env python3
2
+ """key_tester.py – yt-keys HF Space (v3.0 – premium model version)"""
3
+ import json, os, re, time, math, urllib.request, urllib.error, ssl
4
  from pathlib import Path
5
 
6
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
7
+ DATASET = os.environ.get("OPENCLAW_DATASET_REPO", "arshitmalik/yt-pipeline-data")
8
  POOL_KEY = "verified_keys.json"
9
  LOG_FILE = Path("/app/tester.log")
10
+ TESTER_SPACE = "arshitmalik/yt-keys"
11
+ PRIMARY_PROXY = "https://aiapiv2.pekpik.com/v1"
12
+ PROVIDER_PRIORITY = ["anthropic", "openai", "google", "mistral", "xai", "others"]
13
+ MAX_PER_PROVIDER = 20
14
+ MAX_TESTS_PER_CYCLE = 60
15
+ INTER_TEST_DELAY = 0.8
16
+ INTER_FETCH_DELAY = 1.0
17
+ SCRAPE_INTERVAL = 600
18
+ RATE_LIMIT_STRIKES = 2
19
 
20
  ctx = ssl.create_default_context()
21
  ctx.check_hostname = False
22
+ ctx.verify_mode = False
23
 
24
  def log(msg):
25
  line = f"[{time.strftime('%H:%M:%S')}] {msg}"
26
+ print(line, flush=True)
27
  with open(LOG_FILE, "a") as f: f.write(line + "\n")
28
 
29
+ def fetch(url, timeout=20, extra_headers=None):
30
+ headers = {
31
+ "User-Agent": "Mozilla/5.0 (compatible; FreeKeyBot/3.0; +https://github.com)",
32
+ "Accept": "text/html,application/json,text/plain,*/*",
33
+ }
34
+ if extra_headers: headers.update(extra_headers)
35
  try:
36
+ req = urllib.request.Request(url, headers=headers)
37
  with urllib.request.urlopen(req, timeout=timeout, context=ctx) as r:
38
  return r.read().decode("utf-8", errors="replace")
39
+ except: return None
40
+
41
+ _PLACEHOLDER = re.compile(
42
+ r"(x{4,}|0{6,}|1{6,}|a{4,}|qwerty|asdfgh|placeholder|your[\-_]?key|"
43
+ r"insert|replace|changeme|sk-test|sk-demo|sk-fake|12345|abcde|"
44
+ r"sk-proj-[X]{4,}|YOUR|EXAMPLE|SAMPLE|REDACTED)", re.I)
45
+ _KEY_PATTERNS = [
46
+ re.compile(r"(sk-[A-Za-z0-9]{48,})"),
47
+ re.compile(r"(sk-ant-api\d{2}-[A-Za-z0-9\-_]{64,})"),
48
+ re.compile(r"(AIza[0-9A-Za-z\-_]{35})"),
49
+ re.compile(r"(xai-[A-Za-z0-9]{20,})"),
50
+ re.compile(r"\b(fk[A-Za-z0-9]{40,})\b"),
51
+ re.compile(r"(sk-or-v1-[A-Za-z0-9]{48,})"),
52
+ re.compile(r"\b(gsk_[A-Za-z0-9]{40,})\b"),
53
+ re.compile(r"\b([A-Za-z0-9]{32})\b"),
54
+ ]
55
+
56
+ def shannon_entropy(s):
57
+ if not s: return 0.0
58
+ counts = [s.count(c)/len(s) for c in set(s)]
59
+ return -sum(p*math.log2(p) for p in counts)
60
+
61
+ def is_plausible_key(key):
62
+ if len(key) < 32 or len(key) > 512: return False
63
+ if _PLACEHOLDER.search(key): return False
64
+ if shannon_entropy(key) < 3.5: return False
65
+ if max(key.count(c) for c in set(key)) > len(key)*0.65: return False
66
+ return True
67
+
68
+ def extract_keys(text):
69
+ found = set()
70
+ for pat in _KEY_PATTERNS:
71
+ for m in pat.finditer(text):
72
+ k = m.group(1)
73
+ if is_plausible_key(k): found.add(k)
74
+ return found
75
 
76
  def guess_provider(key):
77
+ k = key.lower()
78
+ if k.startswith("sk-ant-api"): return "anthropic"
79
+ if k.startswith("aiza"): return "google"
80
+ if k.startswith("xai-"): return "xai"
81
+ if k.startswith("sk-or-v1"): return "others"
82
+ if k.startswith("gsk_"): return "others"
83
+ if k.startswith("mistral"): return "mistral"
84
+ if k.startswith("sk-") or k.startswith("fk"): return "openai"
85
  return "others"
86
 
87
+ def _fetch_raw_github(repo, filename):
88
+ return fetch(f"https://raw.githubusercontent.com/{repo}/main/{filename}", timeout=15)
89
+
90
+ def scrape_alistaitsacle():
91
+ keys = set()
92
+ for fname in ["README.md","README_CN.md","README_JA.md","README_KO.md","README_ES.md","README_PT.md"]:
93
+ text = _fetch_raw_github("alistaitsacle/free-llm-api-keys", fname)
94
+ if text:
95
+ found = extract_keys(text)
96
+ if found: log(f" alistaitsacle/{fname}: {len(found)} candidates")
97
+ keys.update(found)
98
+ time.sleep(INTER_FETCH_DELAY)
99
+ return keys
100
+
101
+ _GITHUB_REPOS = [
102
+ ("chatanywhere/GPT_API_free",["README.md","README_EN.md"]),
103
+ ("creazycar/GPT_API_free",["README.md"]),
104
+ ("ReimagineTruth/gpt_api_free",["README.md"]),
105
+ ("OrrieZ/gpt_api_free",["README.md"]),
106
+ ("FairyWorld/free_ai_GPT_API_free",["README.md"]),
107
+ ("taurusduan/GPT_API_free-lvyou",["README.md"]),
108
+ ("Poghappy/gpt_api_free",["README.md"]),
109
+ ("popjane/free_chatgpt_api",["README.md"]),
110
+ ("onlywyl/free_chatgpt_api",["README.md"]),
111
+ ("dan1471/FREE-openai-api-keys",["README.md","keys.txt","free_keys.md"]),
112
+ ("racalado/free-openai-api-keys",["README.md","keys.md"]),
113
+ ("CyYxl2024/freeai",["README.md"]),
114
+ ("qwq202/One-FAS-API",["README.md"]),
115
+ ("vibheksoni/free-ai",["README.md","keys.txt"]),
116
+ ("Mr-Abood/GPTFree",["README.md"]),
117
+ ("BuluBulugege/Free-BAI",["README.md"]),
118
+ ("thesecondchance/freeflow-llm",["README.md","keys.md"]),
119
+ ("tashfeenahmed/freellmapi",["README.md"]),
120
+ ("mufeng510/Free-ChatGPT-API",["README.md"]),
121
+ ("lizhenmiao/gpt4free-ts",["README.md"]),
122
+ ("ling-drag0n/api-pool",["README.md","pool.json","keys.json"]),
123
+ ("fengqiaozhu/openrouter-free-pool",["README.md","keys.json","pool.json"]),
124
+ ("mahdi-marjani/free-llm-api",["README.md","keys.txt"]),
125
+ ("realasfngl/ChatGPT",["README.md"]),
126
+ ("Butakov-Andrey/freegpt",["README.md"]),
127
+ ("elphen-wang/FreeAI",["README.md","keys.md"]),
128
+ ("devashish2024/freegpt",["README.md"]),
129
+ ("msmarkgu/RelayFreeLLM",["README.md"]),
130
+ ("piyush-tyagi-13/llm-keypool",["README.md","keys.json"]),
131
+ ("Mirrowel/LLM-API-Key-Proxy",["README.md"]),
132
+ ("p32929/rotato",["README.md"]),
133
+ ("SreejanPersonal/Free-Unoffical-OpenAI-API",["README.md","keys.txt"]),
134
+ ("suyashb734/cliagents",["README.md"]),
135
+ ]
136
+
137
+ def scrape_github_repos():
138
+ keys = set()
139
+ for repo, files in _GITHUB_REPOS:
140
+ for fname in files:
141
+ text = _fetch_raw_github(repo, fname)
142
+ if text:
143
+ found = extract_keys(text)
144
+ if found: log(f" {repo}/{fname}: {len(found)} candidates")
145
+ keys.update(found)
146
+ time.sleep(INTER_FETCH_DELAY)
147
+ return keys
148
+
149
+ _WEB_ENDPOINTS = [
150
+ "https://alistaitsacle.github.io/free-llm-api-keys/",
151
+ "https://api.zaochuanqiu.online/",
152
+ "https://api.zaochuanqiu.online/v1/keys",
153
+ "https://api.zaochuanqiu.online/keys",
154
+ "https://api.algion.dev/",
155
+ "https://api.algion.dev/keys",
156
+ "https://api.algion.dev/v1/models",
157
+ "https://freeapi.geekgpt.site/",
158
+ "https://freeapi.geekgpt.site/keys",
159
+ "https://freeapi.geekgpt.site/api/keys",
160
+ "https://free.gpt.ge/",
161
+ "https://aipipe.org/",
162
+ "https://freetheai.xyz/",
163
+ ]
164
+
165
+ def scrape_web_endpoints():
166
+ keys = set()
167
+ for url in _WEB_ENDPOINTS:
168
+ text = fetch(url, timeout=12)
169
+ if text:
170
+ found = extract_keys(text)
171
+ if found: log(f" {url}: {len(found)} candidates")
172
+ keys.update(found)
173
+ time.sleep(0.5)
174
+ return keys
175
+
176
+ def test_key(key, provider):
177
+ if provider == "anthropic":
178
+ url = "https://api.anthropic.com/v1/models"
179
+ headers = {"x-api-key": key, "anthropic-version": "2023-06-01"}
180
+ elif provider == "google":
181
+ url = f"https://generativelanguage.googleapis.com/v1beta/models?key={key}"
182
+ headers = {}
183
+ elif provider == "mistral":
184
+ url = "https://api.mistral.ai/v1/models"
185
+ headers = {"Authorization": f"Bearer {key}"}
186
+ elif provider == "xai":
187
+ url = "https://api.x.ai/v1/models"
188
+ headers = {"Authorization": f"Bearer {key}"}
189
+ else:
190
+ url = f"{PRIMARY_PROXY}/models"
191
+ headers = {"Authorization": f"Bearer {key}"}
192
  try:
193
+ req = urllib.request.Request(url, headers=headers)
194
+ with urllib.request.urlopen(req, timeout=10, context=ctx) as r:
195
+ return r.status == 200
 
196
  except urllib.error.HTTPError as e:
197
+ return None if e.code == 429 else False
198
+ except: return None
 
199
 
200
+ def upload_pool(verified):
201
+ ordered = {p: verified.get(p, []) for p in PROVIDER_PRIORITY}
202
+ total = sum(len(v) for v in ordered.values())
203
+ summary = ", ".join(f"{p}:{len(ordered[p])}" for p in PROVIDER_PRIORITY if ordered[p])
204
  try:
205
  from huggingface_hub import HfApi
206
+ HfApi(token=HF_TOKEN).upload_file(
207
+ path_or_fileobj=json.dumps(ordered, indent=2).encode(),
208
+ path_in_repo=POOL_KEY, repo_id=DATASET, repo_type="dataset",
209
+ commit_message=f"keypool: {total} verified keys ({summary})")
210
+ log(f"Uploaded {total} keys to dataset [{summary}]")
211
  except Exception as e:
212
  log(f"Upload failed: {e}")
213
 
214
+ def factory_rebuild_self():
 
 
 
215
  try:
216
+ from huggingface_hub import HfApi
217
+ HfApi(token=HF_TOKEN).restart_space(TESTER_SPACE, factory_reboot=True)
218
+ log("Factory rebuild triggered – space will restart")
 
 
 
 
 
 
 
 
219
  except Exception as e:
220
+ log(f"Factory rebuild failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ def run_cycle():
223
+ log("="*60)
224
+ log("SCRAPE CYCLE STARTING")
225
+ log("="*60)
226
+ log("[1/4] Scraping alistaitsacle (primary source)...")
227
+ candidates = scrape_alistaitsacle()
228
+ log(f" -> {len(candidates)} candidates so far")
229
+ log("[2/4] Scraping secondary GitHub repos...")
230
+ candidates.update(scrape_github_repos())
231
+ log(f" -> {len(candidates)} candidates total")
232
+ log("[3/4] Probing web endpoints...")
233
+ candidates.update(scrape_web_endpoints())
234
+ log(f" -> {len(candidates)} candidates total")
235
+ if not candidates:
236
+ log("No candidates found in any source. Sleeping until next cycle.")
237
+ return
238
+ log(f"[4/4] Testing up to {MAX_TESTS_PER_CYCLE} keys (delay: {INTER_TEST_DELAY}s each)...")
239
+ verified = {p: [] for p in PROVIDER_PRIORITY}
240
+ rate_limit_strikes = {p: 0 for p in PROVIDER_PRIORITY}
241
+ tested = 0
242
+ sorted_candidates = sorted(candidates, key=lambda k: PROVIDER_PRIORITY.index(guess_provider(k)) if guess_provider(k) in PROVIDER_PRIORITY else 99)
243
+ for key in sorted_candidates:
244
+ prov = guess_provider(key)
245
+ if prov not in verified: prov = "others"
246
+ if len(verified[prov]) >= MAX_PER_PROVIDER: continue
247
+ if rate_limit_strikes.get(prov, 0) >= RATE_LIMIT_STRIKES: continue
248
+ if tested >= MAX_TESTS_PER_CYCLE:
249
+ log(f"Hit test cap ({MAX_TESTS_PER_CYCLE}). Saving what we have.")
250
+ break
251
+ result = test_key(key, prov)
252
+ tested += 1
253
+ if result is True:
254
+ verified[prov].append(key)
255
+ log(f" OK [{prov}] {key[:18]}...")
256
+ elif result is None:
257
+ rate_limit_strikes[prov] = rate_limit_strikes.get(prov, 0) + 1
258
+ if rate_limit_strikes[prov] >= RATE_LIMIT_STRIKES:
259
+ log(f" RATE [{prov}] x{rate_limit_strikes[prov]} — skipping provider")
260
+ time.sleep(INTER_TEST_DELAY)
261
+ total = sum(len(v) for v in verified.values())
262
+ log(f"Cycle complete: {tested} keys tested, {total} verified")
263
+ if total > 0:
264
+ upload_pool(verified)
265
+ else:
266
+ log("No verified keys this cycle.")
267
+ providers_seen = set(guess_provider(k) for k in candidates)
268
+ providers_rate_limited = {p for p, strikes in rate_limit_strikes.items() if strikes >= RATE_LIMIT_STRIKES}
269
+ if providers_seen and providers_seen <= providers_rate_limited:
270
+ log("All provider endpoints rate-limited. Triggering factory rebuild...")
271
+ factory_rebuild_self()
272
+ return
273
 
274
  def main():
275
+ log("yt-keys Key Tester v3.0 — starting")
276
+ log(f"Primary proxy: {PRIMARY_PROXY}")
277
+ log(f"Cycle interval: {SCRAPE_INTERVAL}s | Max tests/cycle: {MAX_TESTS_PER_CYCLE}")
278
  while True:
279
+ try:
280
+ run_cycle()
281
+ except Exception as e:
282
+ import traceback
283
+ log(f"ERROR in cycle: {e}\n{traceback.format_exc()}")
284
+ log(f"Sleeping {SCRAPE_INTERVAL}s until next cycle...")
285
+ time.sleep(SCRAPE_INTERVAL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  if __name__ == "__main__":
288
  main()