181 lines
5.5 KiB
Python
181 lines
5.5 KiB
Python
"""grepapp_farm.py — validate 446 grep.app dashscope hits.
|
|
Fetches each file from raw.githubusercontent.com, scans for keys, writes keys.db.
|
|
One-shot (not eternal). Run: python grepapp_farm.py
|
|
"""
|
|
import os, sys, json, time, hashlib, sqlite3, threading
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
import requests
|
|
|
|
KF_BASE = os.path.dirname(os.path.abspath(__file__))
|
|
os.makedirs(os.path.join(KF_BASE, "data"), exist_ok=True)
|
|
sys.path.insert(0, KF_BASE)
|
|
|
|
DB = os.environ.get("KF_DB") or os.path.join(KF_BASE, "data/keys.db")
|
|
INPUT_FILE = os.path.join(KF_BASE, "grepapp_farm_input.json")
|
|
LOG_FILE = os.path.join(KF_BASE, "grepapp_farm.log")
|
|
N_THREADS = 16
|
|
FETCH_TIMEOUT = 15
|
|
RATE_BURST = 8
|
|
RATE_PAUSE = 2.0
|
|
|
|
from key_patterns import PATTERNS
|
|
|
|
GARBAGE = ("xxxx", "your", "YOUR", "EXAMPLE", "example", "n0tr3al", "FAKE",
|
|
"AQUI", "sk-xxxx", "sk-XXXX", "sk-your", "sk-YOUR", "sk-your-",
|
|
"not_a_real", "REPLACE", "replace", "CHANGE", "change", "YOUR_KEY",
|
|
"your_key", "your-key", "MY_KEY", "my_key", "PUT_YOUR", "put_your",
|
|
"PASTE", "paste", "HERE", "here", "PLACEHOLDER", "placeholder",
|
|
"SKIP_ME", "skip_me", "00000000", "12345678", "YOUR_SECRET",
|
|
"your_secret", "*", "•", "xxxxxxxx", "DEADBEEF", "deadbeef")
|
|
|
|
session = requests.Session()
|
|
session.trust_env = False
|
|
session.headers["User-Agent"] = "grepapp-farm/1.0"
|
|
|
|
_burst_lock = threading.Lock()
|
|
_burst_count = 0
|
|
_burst_ts = time.time()
|
|
|
|
|
|
def log(msg):
|
|
line = f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}"
|
|
print(line, flush=True)
|
|
try:
|
|
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
f.write(line + "\n")
|
|
except:
|
|
pass
|
|
|
|
|
|
def _rate_wait():
|
|
global _burst_count, _burst_ts
|
|
with _burst_lock:
|
|
_burst_count += 1
|
|
if _burst_count >= RATE_BURST:
|
|
elapsed = time.time() - _burst_ts
|
|
if elapsed < RATE_PAUSE:
|
|
time.sleep(RATE_PAUSE - elapsed)
|
|
_burst_count = 0
|
|
_burst_ts = time.time()
|
|
|
|
|
|
def url_for(hit):
|
|
repo = hit["repo"].strip()
|
|
branch = hit["branch"].strip()
|
|
path = hit["path"].strip()
|
|
return f"https://raw.githubusercontent.com/{repo}/{branch}/{path}"
|
|
|
|
|
|
def fetch_and_scan(hit):
|
|
_rate_wait()
|
|
url = url_for(hit)
|
|
repo = hit["repo"]
|
|
try:
|
|
r = session.get(url, timeout=FETCH_TIMEOUT, allow_redirects=True)
|
|
if r.status_code != 200:
|
|
return []
|
|
text = r.text
|
|
except Exception:
|
|
return []
|
|
|
|
found = []
|
|
for prov, pat in PATTERNS:
|
|
for m in pat.finditer(text):
|
|
val = m.group(0)
|
|
if len(val) < 6:
|
|
continue
|
|
if any(g in val for g in GARBAGE):
|
|
continue
|
|
digits = sum(c.isdigit() for c in val)
|
|
if digits > len(val) * 0.8 and len(val) < 20:
|
|
continue
|
|
found.append((prov, val.strip()))
|
|
return found
|
|
|
|
|
|
def write_keys(db_conn, hits_found):
|
|
cur = db_conn.cursor()
|
|
n = 0
|
|
for prov, val, repo in hits_found:
|
|
h = hashlib.sha256(val.encode()).hexdigest()
|
|
try:
|
|
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')",
|
|
(h, val, prov, "NEW", time.strftime("%Y-%m-%d %H:%M"), repo)
|
|
)
|
|
n += 1
|
|
except Exception as e:
|
|
log(f"DB write error: {e}")
|
|
db_conn.commit()
|
|
return n
|
|
|
|
|
|
def main():
|
|
log("=== grepapp_farm START ===")
|
|
|
|
if not os.path.exists(INPUT_FILE):
|
|
log(f"ERROR: input file not found: {INPUT_FILE}")
|
|
sys.exit(1)
|
|
|
|
with open(INPUT_FILE, encoding="utf-8") as f:
|
|
hits = json.load(f)
|
|
log(f"Loaded {len(hits)} hits from {INPUT_FILE}")
|
|
|
|
db = sqlite3.connect(DB, timeout=120, check_same_thread=False)
|
|
db.execute("""CREATE TABLE IF NOT EXISTS keys (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
hash TEXT UNIQUE, val TEXT, prov TEXT, status TEXT DEFAULT 'NEW',
|
|
plan TEXT, price TEXT, remaining TEXT, found TEXT, repo TEXT)""")
|
|
db.commit()
|
|
|
|
total_keys = 0
|
|
done = 0
|
|
batch = []
|
|
|
|
with ThreadPoolExecutor(max_workers=N_THREADS) as ex:
|
|
futures = {ex.submit(fetch_and_scan, h): h for h in hits}
|
|
for f in as_completed(futures):
|
|
hit = futures[f]
|
|
done += 1
|
|
try:
|
|
results = f.result()
|
|
except Exception as e:
|
|
results = []
|
|
log(f"ERR {hit['repo']}/{hit['path']}: {e}")
|
|
|
|
for prov, val in results:
|
|
batch.append((prov, val, hit["repo"]))
|
|
total_keys += 1
|
|
|
|
if len(batch) >= 20:
|
|
n = write_keys(db, batch)
|
|
if n:
|
|
log(f" +{n} keys ({done}/{len(hits)} files)")
|
|
batch = []
|
|
|
|
if done % 50 == 0:
|
|
log(f"Progress: {done}/{len(hits)} files, {total_keys} keys")
|
|
|
|
if batch:
|
|
n = write_keys(db, batch)
|
|
if n:
|
|
log(f" +{n} keys (final flush)")
|
|
|
|
db.close()
|
|
log(f"=== grepapp_farm DONE: {done} files, {total_keys} keys ===")
|
|
|
|
db2 = sqlite3.connect(DB)
|
|
cur = db2.execute("SELECT prov, COUNT(*) FROM keys WHERE status='NEW' GROUP BY prov ORDER BY 2 DESC LIMIT 20")
|
|
log("Top providers in DB:")
|
|
for row in cur.fetchall():
|
|
log(f" {row[0]}: {row[1]}")
|
|
db2.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |