157 lines
5.6 KiB
Python
157 lines
5.6 KiB
Python
"""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)}') |