196 lines
6.3 KiB
Python
196 lines
6.3 KiB
Python
"""ETERNAL grep.app hunt: public code-search API, independent rate lane.
|
|
Cycles queries against https://grep.app/api/search, extracts keys, writes keys.db.
|
|
"""
|
|
import os
|
|
KF_BASE = os.path.dirname(os.path.abspath(__file__))
|
|
os.makedirs(os.path.join(KF_BASE, "data"), exist_ok=True)
|
|
import hashlib
|
|
import random
|
|
import re
|
|
import sqlite3
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
import requests
|
|
|
|
DB = os.environ.get("KF_DB") or os.path.join(KF_BASE, "data/keys.db")
|
|
LOG = os.path.join(KF_BASE, "grepapp_eternal.log")
|
|
API = "https://grep.app/api/search"
|
|
REST = 300 # grep.app rate limits are aggressive
|
|
TOKENS_FILE = os.environ.get("KF_TOKENS_FILE") or os.path.join(KF_BASE, "gh_tokens.txt")
|
|
|
|
# Token pool — passive for grep.app (no auth enforced), rotates as fallback
|
|
class TokenPool:
|
|
def __init__(self):
|
|
self.tokens = []
|
|
self._i = 0
|
|
self._lock = threading.Lock()
|
|
self._load()
|
|
|
|
def _load(self):
|
|
try:
|
|
with open(TOKENS_FILE, encoding="utf-8") as f:
|
|
for line in f:
|
|
t = line.strip()
|
|
if t:
|
|
self.tokens.append(t)
|
|
except Exception:
|
|
pass
|
|
log(f"TokenPool: {len(self.tokens)} tokens from {TOKENS_FILE}")
|
|
|
|
def next(self):
|
|
with self._lock:
|
|
if not self.tokens:
|
|
return ""
|
|
t = self.tokens[self._i % len(self.tokens)]
|
|
self._i += 1
|
|
return t
|
|
|
|
|
|
def _backoff_sleep(attempt):
|
|
"""Exponential backoff + jitter. 2^attempt * 10-30s, capped 600s."""
|
|
delay = min(600, (2 ** attempt) * random.uniform(10, 30))
|
|
log(f"backoff #{attempt}: sleep {delay:.0f}s")
|
|
time.sleep(delay)
|
|
|
|
|
|
QUERIES = [
|
|
"ANTHROPIC_API_KEY",
|
|
"sk-ant-api03",
|
|
"OPENAI_API_KEY",
|
|
"sk-or-v1",
|
|
"OPENROUTER_API_KEY",
|
|
"DASHSCOPE_API_KEY",
|
|
"DEEPSEEK_API_KEY",
|
|
"ZHIPU_API_KEY",
|
|
"ZHIPUAI_API_KEY",
|
|
"GLM_API_KEY",
|
|
"KIMI_API_KEY",
|
|
"MOONSHOT_API_KEY",
|
|
"REPLICATE_API_TOKEN",
|
|
"ELEVENLABS_API_KEY",
|
|
"GROQ_API_KEY",
|
|
"TOGETHER_API_KEY",
|
|
"FIREWORKS_API_KEY",
|
|
"PERPLEXITY_API_KEY",
|
|
"GEMINI_API_KEY",
|
|
"HF_TOKEN",
|
|
"HUGGINGFACE_API_TOKEN",
|
|
"SILICONFLOW_API_KEY",
|
|
"TELEGRAM_BOT_TOKEN",
|
|
"sk_c434e9eed6b6",
|
|
"NOVITA_API_KEY",
|
|
"STABILITY_API_KEY",
|
|
"NVIDIA_API_KEY",
|
|
"MISTRAL_API_KEY",
|
|
"COHERE_API_KEY",
|
|
"TOGETHER_API_TOKEN",
|
|
"tgp_",
|
|
"pplx-",
|
|
"nvapi-",
|
|
"gsk_",
|
|
"QWEN_API_KEY",
|
|
"TONGYI_QIANWEN_API_KEY",
|
|
"DASHSCOPE_API_TOKEN",
|
|
"api.dashscope.cn",
|
|
]
|
|
|
|
from key_patterns import PATTERNS
|
|
|
|
GARBAGE = ("xxxx", "your", "YOUR", "EXAMPLE", "example", "n0tr3al", "FAKE",
|
|
"fake", "test", "Test", "TEST", "demo", "Demo", "aaaaaaaa", "abcdef",
|
|
"1234567890", "abcdefghij", "placehol", "Placehol", "changeme",
|
|
"deadbeef", "fffffff", "0000000000", "INSERT", "REEMPLAZA", "AQUI")
|
|
|
|
|
|
def log(msg):
|
|
line = f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n"
|
|
print(line, flush=True)
|
|
with open(LOG, "a", encoding="utf-8", errors="replace") as f:
|
|
f.write(line)
|
|
|
|
|
|
TOKEN_POOL = TokenPool()
|
|
|
|
|
|
def search(q, page=0):
|
|
for attempt in range(10):
|
|
try:
|
|
r = requests.get(API, params={"q": q, "page": page},
|
|
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
"Accept": "application/json, text/plain, */*",
|
|
"Referer": "https://grep.app/",
|
|
"Origin": "https://grep.app"}, timeout=30)
|
|
if r.status_code == 429:
|
|
log(f" {q!r}: HTTP 429 (attempt {attempt+1}/10)")
|
|
_backoff_sleep(attempt)
|
|
if attempt >= 3:
|
|
TOKEN_POOL.next() # rotate on repeated 429
|
|
continue
|
|
if r.status_code != 200:
|
|
log(f" {q!r}: HTTP {r.status_code}")
|
|
return []
|
|
j = r.json()
|
|
return j.get("hits", {}).get("hits", [])
|
|
except Exception as e:
|
|
log(f" {q!r} error (attempt {attempt+1}/10): {e}")
|
|
_backoff_sleep(attempt)
|
|
log(f" {q!r}: all retries exhausted")
|
|
return []
|
|
|
|
|
|
def cycle(db, seen):
|
|
cur = db.cursor()
|
|
new_total, by_prov = 0, {}
|
|
for q in QUERIES:
|
|
for page in (0,):
|
|
hits = search(q, page)
|
|
qn = 0
|
|
for h in hits:
|
|
snip = h.get("content", {}).get("snippet", "") or ""
|
|
repo = h.get("repo", {}).get("raw", "unknown")
|
|
path = h.get("path", {}).get("raw", "")
|
|
for prov, rx in PATTERNS:
|
|
for key in rx.findall(snip):
|
|
if isinstance(key, tuple):
|
|
key = key[0]
|
|
if key in seen or any(g in key for g in GARBAGE) or len(key) < 30:
|
|
continue
|
|
seen.add(key)
|
|
cur.execute(
|
|
"INSERT INTO keys (hash, val, prov, status, found, repo) "
|
|
"VALUES (?,?,?,?,?,?) "
|
|
"ON CONFLICT(hash) DO UPDATE SET status='NEW', found=excluded.found, repo=excluded.repo "
|
|
"WHERE keys.status IN ('DEAD','ERR')",
|
|
(hashlib.sha1(key.encode()).hexdigest()[:16], key, prov,
|
|
"NEW", "grepapp", f"{repo}/{path}"))
|
|
new_total += 1
|
|
qn += 1
|
|
by_prov[prov] = by_prov.get(prov, 0) + 1
|
|
log(f"{q!r} p{page}: {len(hits)} hits, +{qn} new")
|
|
time.sleep(20)
|
|
db.commit()
|
|
return new_total, by_prov
|
|
|
|
|
|
def main():
|
|
log(f"=== grep.app eternal hunt START pid={__import__('os').getpid()} ===")
|
|
db = sqlite3.connect(DB, timeout=120, check_same_thread=False, isolation_level=None)
|
|
while True:
|
|
try:
|
|
cur = db.cursor()
|
|
cur.execute("SELECT val FROM keys")
|
|
seen = {r[0] for r in cur.fetchall()}
|
|
n, prov = cycle(db, seen)
|
|
log(f"CYCLE done: +{n} new {prov}; resting {REST}s")
|
|
except Exception as e:
|
|
log(f"CYCLE ERROR: {e!r}")
|
|
time.sleep(REST)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
sys.exit(0) |