581 lines
37 KiB
Python
581 lines
37 KiB
Python
#!/usr/bin/env python3
|
||
"""AURIGA — homelab toolbox: IP intel, card validator, SMS rentals, proxy tools."""
|
||
import base64, binascii, hashlib, json, os, re, secrets, socket, sqlite3, string, struct, time, uuid
|
||
import urllib.request, urllib.parse
|
||
from flask import Flask, request, jsonify, render_template_string, g
|
||
|
||
app = Flask(__name__)
|
||
DB_PATH = os.environ.get("AURIGA_DB", "/opt/auriga/auriga.db")
|
||
SMSP_KEY = os.environ.get("SMSP_KEY", "")
|
||
PLEIADES_GW = os.environ.get("PLEIADES_GW", "10.30.20.178:8080")
|
||
PLEIADES_APP = os.environ.get("PLEIADES_APP", "http://10.30.20.178:5000")
|
||
BMAC = "https://buymeacoffee.com/r26xrthzttg"
|
||
|
||
def db():
|
||
con = sqlite3.connect(DB_PATH); con.row_factory = sqlite3.Row
|
||
con.execute("""CREATE TABLE IF NOT EXISTS sms_rentals(
|
||
id INTEGER PRIMARY KEY, phone TEXT, service TEXT, country TEXT,
|
||
purchase_id TEXT, cost REAL, status TEXT, created INTEGER, expires INTEGER)""")
|
||
con.execute("""CREATE TABLE IF NOT EXISTS proxy_checks(
|
||
id INTEGER PRIMARY KEY, user_key TEXT, egress_ip TEXT, geo TEXT, ok INTEGER, ts INTEGER)""")
|
||
return con
|
||
|
||
def http(url, headers=None, data=None, method="GET", timeout=12):
|
||
h = {"User-Agent": "Mozilla/5.0 (Auriga toolbox)"}
|
||
h.update(headers or {})
|
||
req = urllib.request.Request(url, headers=h, data=data, method=method)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=timeout) as r:
|
||
return r.status, r.read().decode("utf-8", "replace")
|
||
except urllib.error.HTTPError as e:
|
||
return e.code, e.read().decode("utf-8", "replace")
|
||
except Exception as e:
|
||
return 0, str(e)
|
||
|
||
def jf(b):
|
||
try: return json.loads(b)
|
||
except Exception: return None
|
||
|
||
BASE = """<!doctype html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1">
|
||
<meta name=robots content="noindex"><title>AURIGA — Toolbox</title>
|
||
<style>
|
||
:root{--bg:#0b0e17;--card:#141a2b;--fg:#e8ecf7;--dim:#8b95b3;--acc:#f0b429;--ok:#3ecf8e;--bad:#ff5d5d}
|
||
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.5 ui-monospace,Menlo,Consolas,monospace}
|
||
nav{display:flex;flex-wrap:wrap;gap:.4rem;padding:1rem 1.4rem;border-bottom:1px solid #232b45;position:sticky;top:0;background:var(--bg);z-index:5}
|
||
nav a{color:var(--dim);text-decoration:none;padding:.45rem .9rem;border:1px solid #232b45;border-radius:999px}
|
||
nav a.on,nav a:hover{color:var(--acc);border-color:var(--acc)}
|
||
main{max-width:960px;margin:0 auto;padding:2rem 1.4rem}
|
||
h1{font-size:1.7rem;letter-spacing:.2em}h1 span{color:var(--acc)}
|
||
.sub{color:var(--dim);margin:.3rem 0 1.6rem}
|
||
.card{background:var(--card);border:1px solid #232b45;border-radius:14px;padding:1.2rem 1.4rem;margin:1rem 0}
|
||
.kv{display:grid;grid-template-columns:minmax(140px,220px) 1fr;gap:.3rem 1rem}
|
||
.kv div:nth-child(odd){color:var(--dim)}
|
||
input,select,button,textarea{font:inherit;background:#0e1424;color:var(--fg);border:1px solid #2a3555;border-radius:8px;padding:.6rem .8rem}
|
||
button{background:var(--acc);color:#111;border:0;font-weight:700;cursor:pointer}
|
||
button:hover{filter:brightness(1.1)}
|
||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
|
||
@media(max-width:700px){.grid2{grid-template-columns:1fr}.kv{grid-template-columns:1fr}}
|
||
.tag{display:inline-block;padding:.15rem .6rem;border-radius:999px;font-size:.8rem;border:1px solid}
|
||
.tag.ok{color:var(--ok);border-color:var(--ok)}.tag.bad{color:var(--bad);border-color:var(--bad)}.tag.warn{color:var(--acc);border-color:var(--acc)}
|
||
table{width:100%;border-collapse:collapse}td,th{padding:.4rem;border-bottom:1px solid #232b45;text-align:left;font-size:.9rem}
|
||
footer{color:var(--dim);text-align:center;padding:2rem;font-size:.85rem}footer a{color:var(--acc)}
|
||
.spin{display:inline-block;animation:r 1s linear infinite}@keyframes r{to{transform:rotate(360deg)}}
|
||
</style></head><body>
|
||
<nav>
|
||
<a href=/ class={{o(ip)}}>◈ WHATS-MY-IP</a><a href=/card class={{o(card)}}>◈ CARD CHECK</a>
|
||
<a href=/sms class={{o(sms)}}>◈ SMS RENTAL</a><a href=/proxy class={{o(proxy)}}>◈ PROXY LAB</a>
|
||
<a href=/tools class={{o(tools)}}>◈ FREE TOOLS</a>
|
||
</nav><main>{{body}}</main>
|
||
<footer>Built for agents & humans · <a href="{{bmac}}" target=_blank rel=noopener>☕ fuel the lab</a></footer>
|
||
<script defer src="https://analytics.thetempleofdoom.com/script.js" data-website-id="953c15df-ba4c-453a-a7c6-465fa9e3f202"></script>
|
||
<script>
|
||
function cp(t){navigator.clipboard.writeText(t).then(function(){toast('Copied ✓')})}
|
||
function toast(m){var d=document.createElement('div');d.textContent=m;d.style.cssText='position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:var(--acc);color:#111;padding:.5rem 1rem;border-radius:8px;font-weight:700;z-index:99';document.body.appendChild(d);setTimeout(function(){d.remove()},1500)}
|
||
</script>
|
||
</body></html>"""
|
||
|
||
def page(sec, body):
|
||
return render_template_string(BASE, body=body, bmac=BMAC, o=lambda s: "on" if s == sec else "")
|
||
|
||
def kv(pairs):
|
||
rows = "".join(f"<div>{k}</div><div>{v}</div>" for k, v in pairs)
|
||
return f'<div class="card"><div class="kv">{rows}</div></div>'
|
||
|
||
# ---------- 0. AGENT DISCOVERY ----------
|
||
API_INDEX = {
|
||
"service": "AURIGA toolbox",
|
||
"description": "IP intel, card BIN validation, 30-min SMS rentals, residential proxy lab, free utilities. Human UI at /, all tools also JSON APIs.",
|
||
"endpoints": [
|
||
{"method": "GET", "path": "/api/ip", "desc": "Everything about the caller's IP: geo, ASN, ISP, VPN/proxy/hosting flags, rDNS, request headers."},
|
||
{"method": "POST", "path": "/api/card", "params": {"num": "card number (digits or formatted)"}, "desc": "Luhn + BIN intel: brand, issuer, country, type, prepaid risk flags. Nothing stored/charged."},
|
||
{"method": "POST", "path": "/api/sms/rent", "params": {"service": "SMSPool service id or keyword (273=discord, 395=google, telegram, whatsapp, other)", "country": "country id (1=US,2=UK,4=NL,150=DE)"}, "desc": "Rent a disposable number for 30 min. Returns number + orderid. Cancel before a code = full refund."},
|
||
{"method": "GET", "path": "/api/sms/check", "params": {"pid": "orderid"}, "desc": "Poll for the received SMS code."},
|
||
{"method": "GET", "path": "/api/sms/cancel", "params": {"pid": "orderid"}, "desc": "Cancel order + refund."},
|
||
{"method": "GET", "path": "/api/sms/history", "desc": "Your rental history from this site."},
|
||
{"method": "POST", "path": "/api/proxy/test", "params": {"user": "Pleiades gateway username", "pass": "password (geo suffixes allowed)"}, "desc": "Tunnel CONNECT through the Pleiades gateway, return real egress IP + geo."},
|
||
{"method": "GET", "path": "/api/hash", "params": {"s": "string"}, "desc": "md5/sha1/sha256/sha512."},
|
||
{"method": "GET", "path": "/api/hdr", "params": {"url": "target URL"}, "desc": "Fetch URL, return status + all response headers."},
|
||
],
|
||
"payment": "SMS meters against the house SMSPool account; proxy plans at the Pleiades storefront. BTCPay BTC only — no Stripe.",
|
||
}
|
||
|
||
@app.route("/api")
|
||
def api_index(): return jsonify(API_INDEX)
|
||
|
||
@app.route("/robots.txt")
|
||
def robots():
|
||
return "User-agent: *\nAllow: /\n", 200, {"Content-Type": "text/plain"}
|
||
|
||
@app.route("/llms.txt")
|
||
def llms():
|
||
eps = "\n".join(f"- `{e['method']} {e['path']}` — {e['desc']} Params: {e.get('params','-')}" for e in API_INDEX["endpoints"])
|
||
body = f"# AURIGA toolbox\n\nNetwork toolbox for humans and agents. Base: https://auriga.thetempleofdoom.com\n\n## API\n{eps}\n\nAll responses JSON. POST bodies are form-encoded. Human pages at /, /card, /sms, /proxy, /tools.\n"
|
||
return body, 200, {"Content-Type": "text/plain"}
|
||
|
||
@app.route("/ai-plugin.json")
|
||
def aiplugin():
|
||
return jsonify({"name_for_model": "auriga", "schema_version": "v1",
|
||
"description_for_model": "IP intelligence, card BIN validation, disposable SMS number rentals (30 min, refundable), residential proxy egress testing, hashing/URL/DNS utilities.",
|
||
"api": {"type": "openapi", "url": "https://auriga.thetempleofdoom.com/openapi.json"},
|
||
"auth": {"type": "none"}, "contact_email": "indianaholmes1@icloud.com"})
|
||
|
||
@app.route("/openapi.json")
|
||
def openapi():
|
||
B = "https://auriga.thetempleofdoom.com"
|
||
ps = {"openapi": "3.0.0", "info": {"title": "AURIGA", "version": "1.1.0"}, "paths": {}}
|
||
def add(path, method, desc, params=None, req=False):
|
||
item = {"summary": desc}
|
||
if params:
|
||
if method == "get":
|
||
item["parameters"] = [{"name": k, "in": "query", "required": req, "schema": {"type": "string"}, "description": v} for k, v in params.items()]
|
||
else:
|
||
item["requestBody"] = {"content": {"application/x-www-form-urlencoded": {"schema": {"type": "object", "properties": {k: {"type": "string", "description": v} for k, v in params.items()}, "required": [k for k in params] if req else []}}}}
|
||
ps["paths"][path] = ps["paths"].get(path, {}) | {method: {"responses": {"200": {"description": "ok"}}, **item}}
|
||
add("/api/ip", "get", "Caller IP intel: geo/ASN/ISP/VPN flags/headers")
|
||
add("/api/card", "post", "Luhn + BIN validation", {"num": "card number"}, req=True)
|
||
add("/api/sms/rent", "post", "Rent disposable number, 30 min", {"service": "service id/keyword", "country": "country id"}, req=True)
|
||
add("/api/sms/check", "get", "Poll SMS code", {"pid": "orderid"}, req=True)
|
||
add("/api/sms/cancel", "get", "Cancel + refund", {"pid": "orderid"}, req=True)
|
||
add("/api/sms/history", "get", "Rental history")
|
||
add("/api/proxy/test", "post", "Test Pleiades gateway creds", {"user": "username", "pass": "password"}, req=True)
|
||
add("/api/hash", "get", "Hashes of s", {"s": "string"}, req=True)
|
||
add("/api/hdr", "get", "HTTP response headers of url", {"url": "url"}, req=True)
|
||
return jsonify(ps)
|
||
|
||
def param(name):
|
||
return request.form.get(name) or request.args.get(name) or request.args.get("orderid") or request.form.get("orderid")
|
||
|
||
def human(n):
|
||
return n
|
||
|
||
# ---------- 1. WHAT'S MY IP ----------
|
||
@app.route("/ip")
|
||
def ip_route(): return ip_lookup()
|
||
|
||
def ip_lookup():
|
||
ip = request.headers.get("X-Real-IP") or request.remote_addr or ""
|
||
st, b = http(f"http://ip-api.com/json/{ip}?fields=66846719")
|
||
d = jf(b) or {}
|
||
hdrs = {k: v for k, v in request.headers.items() if k.lower() in
|
||
("user-agent","accept-language","x-forwarded-for","cf-connecting-ip","cf-ipcountry","x-real-ip")}
|
||
proxy_hint = any(k.lower().startswith(("x-forwarded","via","proxy")) for k in request.headers.keys())
|
||
rows = [
|
||
("YOUR IP", f"<b style='font-size:1.3rem;color:var(--acc)'>{d.get('query', ip)}</b>"),
|
||
("Country", f"{d.get('country','?')} ({d.get('countryCode','?')}) 🏴 {d.get('flag') if 'flag' in d else ''}".strip()),
|
||
("Region / City", f"{d.get('regionName','?')} / {d.get('city','?')} {d.get('zip','')}"),
|
||
("Lat, Lon", f"{d.get('lat','?')}, {d.get('lon','?')} · TZ: {d.get('timezone','?')} · UTC offset {d.get('offset','?')}s"),
|
||
("ISP", d.get('isp','?')), ("Organization", d.get('org','?')), ("AS", d.get('as','?') if d.get('as') else d.get('asname','?')),
|
||
("Reverse DNS", str(d.get('reverse','—'))),
|
||
("Connection", f"mobile: {d.get('mobile')} · proxy/VPN: {d.get('proxy')} · hosting: {d.get('hosting')}"),
|
||
("Local guess", d.get('district') or '—'),
|
||
("Proxy headers seen", "YES ⚠ (you're behind a relay)" if proxy_hint else "none — looks direct"),
|
||
("Currency", d.get('currency','?')),
|
||
]
|
||
hdr_rows = "".join(f"<tr><td>{k}</td><td>{v}</td></tr>" for k, v in hdrs.items())
|
||
body = f"""
|
||
<h1>WHATS <span>MY IP</span></h1><p class=sub>Every drop of intel we can legally scrape, always on.</p>
|
||
{kv(rows)}
|
||
<div class=card><b>Headers you sent</b><table>{hdr_rows}</table></div>
|
||
<div class=card><b>Live check</b> — your browser also resolved this page in
|
||
<span id=ms>…</span>. <script>t=performance.timing.responseStart-performance.timing.requestStart;
|
||
document.getElementById('ms').textContent=Math.max(0,t)+' ms';</script></div>
|
||
<div class=card>API: <code>GET /api/ip</code> → same data as JSON. Agent-friendly.</div>"""
|
||
return page("ip", body)
|
||
|
||
@app.route("/api/ip")
|
||
def api_ip():
|
||
ip = request.headers.get("X-Real-IP") or request.remote_addr or ""
|
||
st, b = http(f"http://ip-api.com/json/{ip}?fields=66846719")
|
||
d = jf(b) or {}
|
||
d["headers_seen"] = {k: v for k, v in request.headers.items()}
|
||
return jsonify(d)
|
||
|
||
# ---------- 2. CARD CHECK ----------
|
||
def luhn_ok(num):
|
||
digits = [int(c) for c in num]
|
||
odd = digits[-1::-2]; even = digits[-2::-2]
|
||
s = sum(odd)
|
||
for d in even:
|
||
d *= 2
|
||
if d > 9: d -= 9
|
||
s += d
|
||
return s % 10 == 0
|
||
|
||
BRANDS = [("4","Visa"),("51","Mastercard"),("52","Mastercard"),("53","Mastercard"),("54","Mastercard"),("55","Mastercard"),
|
||
("22","Mastercard"),("23","Mastercard"),("24","Mastercard"),("25","Mastercard"),("26","Mastercard"),("27","Mastercard"),
|
||
("34","Amex"),("37","Amex"),("6011","Discover"),("65","Discover"),("644","Discover"),("645","Discover"),("646","Discover"),("647","Discover"),("648","Discover"),("649","Discover"),
|
||
("50","Maestro"),("56","Maestro"),("57","Maestro"),("58","Maestro"),("63","Maestro"),("67","Maestro"),
|
||
("30","Diners"),("36","Diners"),("38","Diners"),("39","Diners"),
|
||
("35","JCB"),("62","UnionPay"),("7","Mir")]
|
||
|
||
def brand_of(num):
|
||
for pfx, b in BRANDS:
|
||
if num.startswith(pfx): return b
|
||
return "Unknown"
|
||
|
||
@app.route("/card", methods=["GET", "POST"])
|
||
def card():
|
||
result = ""
|
||
num = re.sub(r"\D", "", request.form.get("num", ""))[:19]
|
||
if num:
|
||
tags = []
|
||
ok = luhn_ok(num)
|
||
tags.append(('<span class="tag ok">LUHN VALID</span>' if ok else '<span class="tag bad">LUHN INVALID — fake/dead number</span>'))
|
||
brand = brand_of(num)
|
||
bin8 = num[:8]
|
||
bl = bin_lookup(bin8)
|
||
bank = (bl.get("bank") or {}).get("name", "—")
|
||
country = (bl.get("country") or {}).get("name", "—")
|
||
ctype = bl.get("type", "—")
|
||
prepaid = bl.get("prepaid", "—")
|
||
if not bl: tags.append('<span class="tag warn">BIN DB no data — structure-only result</span>')
|
||
flags = []
|
||
if ctype == "prepaid" or prepaid is True: flags.append("PREPAID — commonly flagged by merchants")
|
||
if ctype == "debit": flags.append("DEBIT")
|
||
if ctype == "credit": flags.append("CREDIT")
|
||
length = len(num)
|
||
rng = {"Visa":(13,16,19),"Mastercard":(16,),"Amex":(15,),}.get(brand, (13,15,16,19))
|
||
if length not in rng: tags.append(f'<span class="tag bad">LENGTH {length} WRONG for {brand}</span>')
|
||
else: tags.append(f'<span class="tag ok">length {length} valid for {brand}</span>')
|
||
result = f"""
|
||
{kv([("Brand",brand),("BIN",bin8),("Bank / Issuer",bank),("Country",country),("Type",str(ctype)),("Prepaid",str(prepaid))])}
|
||
<div class=card><b>Fraud & structure flags</b><br>{' '.join(tags)}
|
||
{'<br>⚠ ' + ' · '.join(flags) if flags else ''}</div>
|
||
<div class=card style=color:var(--dim)>Nothing is stored. No charge, no auth, no $0 check — this is BIN + math validation only.
|
||
It cannot tell you if a card has available funds. Fraud "flagged" status lives at the issuer, not in any database we can legally query.</div>"""
|
||
body = f"""
|
||
<h1>CARD <span>CHECK</span></h1><p class=sub>Is it real? Luhn + BIN intelligence: issuer, brand, type, country, prepaid risk flags.</p>
|
||
<div class=card><form method=post><input id=cardnum name=num placeholder="4539 1488 0343 6467" style="width:70%" value="{num}" autocomplete=off inputmode=numeric> <button>Check</button></form>
|
||
<div style=color:var(--dim);font-size:.85rem;margin-top:.4rem>Paste anything — spaces, dashes, junk all stripped. Nothing stored.</div></div>
|
||
<script>
|
||
var cn=document.getElementById('cardnum');
|
||
cn.addEventListener('input',function(){{
|
||
var v=this.value.replace(/\\D/g,'').slice(0,19);this.value=v.replace(/(.{{4}})/g,'$1 ').trim()}});
|
||
</script>
|
||
{result}"""
|
||
return page("card", body)
|
||
|
||
def bin_lookup(bin8):
|
||
st, b = http(f"https://lookup.binlist.net/{bin8}", headers={"Accept-Version": "3"})
|
||
bl = jf(b) or {}
|
||
if not bl.get("bank") and not bl.get("type") and not bl.get("scheme"):
|
||
st, b = http(f"https://data.handyapi.com/bin/{bin8}")
|
||
h = jf(b) or {}
|
||
if h.get("Status") == "SUCCESS":
|
||
return {"bank": {"name": h.get("Issuer")}, "country": {"name": h.get("Country", {}).get("Name") if isinstance(h.get("Country"), dict) else h.get("Country")},
|
||
"type": str(h.get("Type", "")).lower() or None, "prepaid": "prepaid" in str(h.get("Type","")).lower() or None, "scheme": h.get("Scheme")}
|
||
return bl
|
||
|
||
@app.route("/api/card", methods=["POST"])
|
||
def api_card():
|
||
num = re.sub(r"\D", "", param("num") or "")[:19]
|
||
if not num: return jsonify({"ok": False, "error": "num required"})
|
||
ok = luhn_ok(num)
|
||
bl = bin_lookup(num[:8])
|
||
return jsonify({"ok": True, "luhn": ok, "brand": brand_of(num), "length_ok": len(num) in
|
||
{"Visa":(13,16,19),"Mastercard":(16,),"Amex":(15,)}.get(brand_of(num),(13,15,16,19)),
|
||
"bin": {"issuer": (bl.get("bank") or {}).get("name"), "country": (bl.get("country") or {}).get("name"),
|
||
"type": bl.get("type"), "prepaid": bl.get("prepaid")},
|
||
"flags": (["prepaid-risk"] if (bl.get("type")=="prepaid" or bl.get("prepaid") is True) else []) + (["luhn-invalid"] if not ok else [])})
|
||
|
||
# ---------- SMS ABUSE GUARD ----------
|
||
def sms_guard():
|
||
"""Return None if allowed, else a JSON-able reason string."""
|
||
con = db(); now = int(time.time())
|
||
ip = request.headers.get("X-Real-IP") or request.remote_addr or "?"
|
||
# balance: refuse if house SMSPool balance below $5
|
||
st, b = sms_api("balance")
|
||
bal = jf(b) or {}
|
||
try: bal = float(bal.get("balance", 0))
|
||
except Exception: bal = 0
|
||
if bal < 5: return f"house balance too low (${bal:.2f}) — rentals paused"
|
||
# per-IP: max 1 active rental, 3/hour, 8/day
|
||
row = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE status='active' AND purchase_id IN (SELECT purchase_id FROM sms_rentals WHERE created > ?)", (now-86400*7,)).fetchone()
|
||
# active count overall (any IP) cap 3 concurrent
|
||
act = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE status='active' AND expires > ?", (now,)).fetchone()["c"]
|
||
if act >= 3: return "too many active rentals right now — try again later"
|
||
h = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE created > ?", (now-3600,)).fetchone()["c"]
|
||
if h >= 6: return "hourly rental cap reached"
|
||
d = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE created > ?", (now-86400,)).fetchone()["c"]
|
||
if d >= 15: return "daily rental cap reached"
|
||
return None
|
||
|
||
# ---------- 3. SMS RENTALS ----------
|
||
SMSP = "https://api.smspool.net"
|
||
SERVICES = [("google","Google"),("discord","Discord"),("telegram","Telegram"),("whatsapp","WhatsApp"),("other","Other/Any")]
|
||
COUNTRIES = [("1","United States"),("2","United Kingdom"),("4","Netherlands"),("22","Russia"),("150","Germany")]
|
||
|
||
def sms_api(path, **kw):
|
||
if kw:
|
||
kw["key"] = SMSP_KEY
|
||
data = urllib.parse.urlencode(kw).encode()
|
||
return http(f"{SMSP}/{path}", data=data, method="POST")
|
||
return http(f"{SMSP}/{path}?key={SMSP_KEY}")
|
||
|
||
@app.route("/sms", methods=["GET", "POST"])
|
||
def sms():
|
||
msg, listing = "", ""
|
||
if request.method == "POST":
|
||
act = request.form.get("act")
|
||
if act == "rent":
|
||
guard = sms_guard()
|
||
if guard:
|
||
msg = f'<div class="card"><span class="tag warn">PAUSED</span> {guard}</div>'
|
||
else:
|
||
svc, ctry = request.form["service"], request.form["country"]
|
||
st, b = sms_api("purchase/sms", service=svc, country=ctry)
|
||
d = jf(b) or {}
|
||
if d.get("success") == 1:
|
||
con = db()
|
||
now = int(time.time())
|
||
con.execute("INSERT INTO sms_rentals(phone,service,country,purchase_id,cost,status,created,expires) VALUES(?,?,?,?,?,?,?,?)",
|
||
(d.get("number"), svc, ctry, str(d.get("purchase_id")), d.get("cost",0), "active", now, now+1800))
|
||
con.commit()
|
||
msg = f'<div class="card"><span class="tag ok">RENTED</span> Your number: <b id=bignum style="font-size:1.2rem;color:var(--acc)">+{d.get("number")}</b> <button style="padding:.2rem .6rem;font-size:.8rem" onclick="cp(\'+{d.get("number")}\')">copy</button> · expires in 30 min · order #{d.get("purchase_id")}</div>'
|
||
else:
|
||
msg = f'<div class="card"><span class="tag bad">RENT FAILED</span><br><pre>{b[:400]}</pre></div>'
|
||
elif act == "check":
|
||
pid = request.form["pid"]
|
||
st, b = sms_api("sms/check", orderid=pid)
|
||
d = jf(b) or {}
|
||
sms_txt = d.get("sms") or d.get("code") or ""
|
||
status = d.get("status", "?")
|
||
color = "ok" if sms_txt else "warn"
|
||
msg = f'<div class="card"><span class="tag {color}">STATUS: {status}</span> {"<b style=color:var(--ok)>" + str(sms_txt) + "</b>" if sms_txt else "no code yet — poll again in 10s"}</div>'
|
||
elif act == "cancel":
|
||
pid = request.form["pid"]
|
||
st, b = sms_api("sms/cancel", orderid=pid)
|
||
d = jf(b) or {}
|
||
ok = d.get("success") == 1
|
||
con = db(); con.execute("UPDATE sms_rentals SET status=? WHERE purchase_id=?", ("refunded" if ok else "cancel-failed", pid)); con.commit()
|
||
msg = f'<div class="card"><span class="tag {"ok" if ok else "bad"}">{"CANCELLED + REFUNDED" if ok else "CANCEL FAILED"}</span></div>'
|
||
st, b = sms_api("sms/instructions")
|
||
con = db()
|
||
hist = con.execute("SELECT * FROM sms_rentals ORDER BY id DESC LIMIT 8").fetchall()
|
||
hist_rows = "".join(f"<tr><td>+{h['phone']} <a href=# onclick=\"cp('+{h['phone']});return false\" style=color:var(--acc)>copy</a></td><td>{h['service']}</td><td>{h['status']}</td><td>#{h['purchase_id']}</td><td class=cdown data-exp={h['expires']}>…</td></tr>" for h in hist)
|
||
body = f"""
|
||
<h1>SMS <span>RENTAL</span></h1><p class=sub>Disposable numbers, 30-minute windows. Cancel before a code arrives = full refund.</p>
|
||
<div class="grid2">
|
||
<div class=card><b>Rent a number</b>
|
||
<form method=post><input type=hidden name=act value=rent>
|
||
<select name=service style="width:100%">{''.join(f'<option value={v}>{n}</option>' for v,n in SERVICES)}</select>
|
||
<select name=country style="width:100%;margin:.5rem 0">{''.join(f'<option value={v}>{n}</option>' for v,n in COUNTRIES)}</select>
|
||
<button>Rent — 30 min</button></form>
|
||
<div style=color:var(--dim);margin-top:.5rem;font-size:.85rem>Numbers auto-appear below. Codes auto-poll every 10s.</div></div>
|
||
<div class=card><b>Check / manage</b>
|
||
<form method=post><input type=hidden name=act value=check><input name=pid placeholder="order #" style="width:100%"><button style="margin:.5rem 0">Poll for code</button></form>
|
||
<form method=post><input type=hidden name=act value=cancel><input name=pid placeholder="order #" style="width:100%"><button style="background:var(--bad);color:#fff">Cancel & refund</button></form></div>
|
||
</div>{msg}
|
||
<div class=card><b>Recent rentals</b><table><tr><th>Number</th><th>Service</th><th>Status</th><th>Order</th><th>Window</th></tr>{hist_rows or '<tr><td colspan=5 style=color:var(--dim)>none yet</td></tr>'}</table></div>
|
||
<script>
|
||
setInterval(function(){{
|
||
var els=document.querySelectorAll('.cdown');var now=Math.floor(Date.now()/1000);
|
||
els.forEach(function(e){{var s=e.dataset.exp-now;if(s>0){{var m=Math.floor(s/60);e.textContent=m+'m '+(s%60)+'s left'}}else e.textContent='expired'}});
|
||
}},1000);
|
||
var lastMsg='';
|
||
setInterval(function(){{
|
||
fetch('/api/sms/history').then(r=>r.json()).then(rows=>{{
|
||
rows.filter(r=>r.status==='active').forEach(r=>{{
|
||
fetch('/api/sms/check?pid='+r.purchase_id).then(x=>x.json()).then(d=>{{
|
||
if(d.sms||d.code){{toast('CODE: '+(d.sms||d.code));document.title='✉ CODE '+(d.sms||d.code);}}
|
||
}})}});
|
||
}})}},10000);
|
||
</script>
|
||
<div class=card style=color:var(--dim)>API: POST /api/sms/rent (service,country) · GET /api/sms/check?pid= · GET /api/sms/cancel?pid= · GET /api/sms/history</div>"""
|
||
return page("sms", body)
|
||
|
||
@app.route("/api/sms/rent", methods=["POST"])
|
||
def api_sms_rent():
|
||
guard = sms_guard()
|
||
if guard: return jsonify({"success": 0, "message": guard, "paused": True})
|
||
st, b = sms_api("purchase/sms", service=param("service"), country=param("country"))
|
||
d = jf(b) or {}
|
||
if d.get("success") == 1:
|
||
con = db(); now = int(time.time())
|
||
con.execute("INSERT INTO sms_rentals(phone,service,country,purchase_id,cost,status,created,expires) VALUES(?,?,?,?,?,?,?,?)",
|
||
(d.get("number"), param("service"), param("country"), str(d.get("purchase_id")), d.get("cost",0), "active", now, now+1800))
|
||
con.commit()
|
||
return jsonify(d)
|
||
|
||
@app.route("/api/sms/check", methods=["GET", "POST"])
|
||
def api_sms_check():
|
||
st, b = sms_api("sms/check", orderid=param("pid"))
|
||
return jf(b) or jsonify({"error": b[:200]})
|
||
|
||
@app.route("/api/sms/cancel", methods=["GET", "POST"])
|
||
def api_sms_cancel():
|
||
st, b = sms_api("sms/cancel", orderid=param("pid"))
|
||
return jf(b) or jsonify({"error": b[:200]})
|
||
|
||
@app.route("/api/sms/history")
|
||
def api_sms_history():
|
||
con = db(); now = int(time.time())
|
||
con.execute("UPDATE sms_rentals SET status='expired' WHERE status='active' AND expires < ?", (now,))
|
||
con.commit()
|
||
rows = con.execute("SELECT * FROM sms_rentals ORDER BY id DESC LIMIT 50").fetchall()
|
||
return jsonify([dict(r) for r in rows])
|
||
|
||
# ---------- 4. PROXY LAB ----------
|
||
@app.route("/proxy", methods=["GET", "POST"])
|
||
def proxy():
|
||
result = ""
|
||
if request.method == "POST" and request.form.get("act") == "test":
|
||
user, pw = request.form.get("user",""), request.form.get("pass","")
|
||
test_url = request.form.get("target", "http://ip-api.com/json/?fields=66846719")
|
||
pauth = base64.b64encode(f"{user}:{pw}".encode()).decode()
|
||
try:
|
||
pu = urllib.parse.urlparse(test_url if "://" in test_url else "http://"+test_url)
|
||
req_line = f"GET {pu.path or '/'} HTTP/1.1\r\nHost: {pu.hostname}\r\nConnection: close\r\n\r\n"
|
||
s = socket.create_connection((PLEIADES_GW.split(":")[0], int(PLEIADES_GW.split(":")[1])), timeout=15)
|
||
s.sendall(f"CONNECT {pu.hostname}:80 HTTP/1.1\r\nHost: {pu.hostname}:80\r\nProxy-Authorization: Basic {pauth}\r\n\r\n".encode())
|
||
resp = s.recv(4096)
|
||
if b"200" in resp.split(b"\r\n")[0]:
|
||
s.sendall(req_line.encode())
|
||
data = b""
|
||
while True:
|
||
c = s.recv(8192)
|
||
if not c: break
|
||
data += c
|
||
s.close()
|
||
body = data.split(b"\r\n\r\n",1)[-1].decode("utf-8","replace")
|
||
j = jf(body) or {}
|
||
con = db()
|
||
con.execute("INSERT INTO proxy_checks(user_key,egress_ip,geo,ok,ts) VALUES(?,?,?,?,?)",
|
||
(user[:12], j.get("query","?"), f"{j.get('country')}/{j.get('city')}", 1, int(time.time())))
|
||
con.commit()
|
||
result = f'<div class="card"><span class="tag ok">PROXY LIVE</span> Egress: <b style=color:var(--acc)>{j.get("query")}</b> — {j.get("country")} / {j.get("city")} · ISP {j.get("isp")} · tz {j.get("timezone")}</div>'
|
||
else:
|
||
con = db()
|
||
con.execute("INSERT INTO proxy_checks(user_key,egress_ip,geo,ok,ts) VALUES(?,?,?,?,?)", (user[:12], "", "", 0, int(time.time())))
|
||
con.commit()
|
||
result = f'<div class="card"><span class="tag bad">AUTH/TUNNEL FAILED</span><pre>{resp[:200]!r}</pre></div>'
|
||
except Exception as e:
|
||
result = f'<div class="card"><span class="tag bad">ERROR</span> {e}</div>'
|
||
body = f"""
|
||
<h1>PROXY <span>LAB</span></h1><p class=sub>Rent residential proxies on the Pleiades rail — your existing gateway <code>user:pass</code> works here, same keys as everywhere.</p>
|
||
<div class=card><form method=post><input type=hidden name=act value=test>
|
||
<label>Gateway user</label><br><input name=user style="width:100%" placeholder="gw_… or your Pleiades username"><br>
|
||
<label style=color:var(--dim)>Password</label><br><input name=pass type=password style="width:100%"><br>
|
||
<button style=margin-top:.6rem>Test egress now</button></form></div>
|
||
{result}
|
||
<div class=card><b>Geo session builder</b> — append these to your password to steer the egress:
|
||
<select id=geoK onchange="gb()"><option value="">none</option><option value="_region-us">region US</option><option value="_region-eu">region EU</option><option value="_country-gb">country GB</option><option value="_country-de">country DE</option><option value="_city-london">city London</option></select>
|
||
<select id=geoS onchange="gb()"><option value="">rotating</option><option value="_session-a7x9_lifetime-30m">sticky 30-min</option></select>
|
||
<div style=margin-top:.5rem><code id=geoOut style=color:var(--acc)>yourpassword</code> <button style="padding:.2rem .6rem;font-size:.8rem" onclick="cp(document.getElementById('geoOut').textContent)">copy</button></div>
|
||
<div style=color:var(--dim);font-size:.85rem;margin-top:.4rem>Chain them: <code>pass_region-us_session-x9k2_lifetime-30m</code>. Same gateway keys as the storefront.</div>
|
||
<script>function gb(){{var p='yourpassword'+document.getElementById('geoK').value+document.getElementById('geoS').value;document.getElementById('geoOut').textContent=p}}</script></div>
|
||
<div class=card><b>Rent more</b> — buy GB plans & geo-targeted sessions at the storefront:
|
||
<a href="{PLEIADES_APP}" style=color:var(--acc)>{PLEIADES_APP}</a>.</div>
|
||
<div class=card style=color:var(--dim)>API: POST /api/proxy/test (user, pass) → egress IP + geo JSON.</div>"""
|
||
return page("proxy", body)
|
||
|
||
@app.route("/api/proxy/test", methods=["POST"])
|
||
def api_proxy_test():
|
||
user, pw = request.form.get("user",""), request.form.get("pass","")
|
||
pauth = base64.b64encode(f"{user}:{pw}".encode()).decode()
|
||
try:
|
||
s = socket.create_connection((PLEIADES_GW.split(":")[0], int(PLEIADES_GW.split(":")[1])), timeout=15)
|
||
s.sendall(f"CONNECT ip-api.com:80 HTTP/1.1\r\nHost: ip-api.com:80\r\nProxy-Authorization: Basic {pauth}\r\n\r\n".encode())
|
||
resp = s.recv(4096)
|
||
if b"200" not in resp.split(b"\r\n")[0]: return jsonify({"ok": False, "raw": resp[:120].decode("utf-8","replace")})
|
||
s.sendall(b"GET /json/?fields=66846719 HTTP/1.1\r\nHost: ip-api.com\r\nConnection: close\r\n\r\n")
|
||
data = b""
|
||
while True:
|
||
c = s.recv(8192)
|
||
if not c: break
|
||
data += c
|
||
s.close()
|
||
j = jf(data.split(b"\r\n\r\n",1)[-1].decode("utf-8","replace")) or {}
|
||
return jsonify({"ok": True, "egress": j})
|
||
except Exception as e:
|
||
return jsonify({"ok": False, "error": str(e)})
|
||
|
||
# ---------- 5. FREE TOOLS ----------
|
||
TOOLS_JS = """
|
||
function tab(n){document.querySelectorAll('.pane').forEach(p=>p.style.display='none');document.getElementById(n).style.display='block';
|
||
document.querySelectorAll('.tbtn').forEach(b=>b.classList.remove('on'));event.target.classList.add('on')}
|
||
async function dns(){const d=document.getElementById('dq').value;const o=await (await fetch('https://dns.google/resolve?name='+encodeURIComponent(d)+'&type=A')).json();document.getElementById('do').textContent=JSON.stringify(o,null,1)}
|
||
async function hdr(){const u=document.getElementById('hq').value;const r=await (await fetch('/api/hdr?url='+encodeURIComponent(u))).json();document.getElementById('ho').textContent=JSON.stringify(r,null,1)}
|
||
function jwt(){try{const t=document.getElementById('jq').value.trim().split('.');const d=s=>JSON.stringify(JSON.parse(atob(s.replace(/-/g,'+').replace(/_/g,'/'))),null,1);document.getElementById('jo').textContent='HEADER\\n'+d(t[0])+'\\n\\nPAYLOAD\\n'+d(t[1])+'\\n\\n(signature: '+t[2]+')'}catch(e){document.getElementById('jo').textContent='Invalid JWT: '+e}}
|
||
function genhash(){const i=document.getElementById('hq2').value;['md5','sha1','sha256','sha512'].forEach(a=>{document.getElementById('h_'+a).textContent=hashlib(a,i)})}
|
||
function hashlib(a,s){return a}
|
||
async function genhash2(){const i=document.getElementById('hq2').value;const r=await(await fetch('/api/hash?s='+encodeURIComponent(i))).json();for(const k of ['md5','sha1','sha256','sha512'])document.getElementById('h_'+k).textContent=r[k]}
|
||
function uuids(){let o='';for(let i=0;i<5;i++)o+=crypto.randomUUID()+'\\n';document.getElementById('uo').textContent=o}
|
||
function pwgen(){const l=+document.getElementById('pl').value||24;const cs='abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%^&*-_=+';const a=new Uint32Array(l);crypto.getRandomValues(a);document.getElementById('po').textContent=Array.from(a,x=>cs[x%cs.length]).join('')}
|
||
"""
|
||
|
||
@app.route("/api/hdr")
|
||
def api_hdr():
|
||
url = request.args.get("url","")
|
||
if "://" not in url: url = "http://" + url
|
||
try:
|
||
req = urllib.request.Request(url, headers={"User-Agent":"Auriga/1.0"})
|
||
with urllib.request.urlopen(req, timeout=12) as r:
|
||
return jsonify({"status": r.status, "final_url": r.url, "headers": dict(r.headers)})
|
||
except Exception as e:
|
||
return jsonify({"error": str(e)})
|
||
|
||
@app.route("/api/hash")
|
||
def api_hash():
|
||
s = request.args.get("s","").encode()
|
||
return jsonify({"md5": hashlib.md5(s).hexdigest(), "sha1": hashlib.sha1(s).hexdigest(),
|
||
"sha256": hashlib.sha256(s).hexdigest(), "sha512": hashlib.sha512(s).hexdigest()})
|
||
|
||
@app.route("/tools")
|
||
def tools():
|
||
body = f"""
|
||
<h1>FREE <span>TOOLS</span></h1><p class=sub>High-value, zero-cost, no signup. Agent-friendly APIs underneath each.</p>
|
||
<style>.tbtn.on{{background:var(--acc);color:#111}}</style>
|
||
<div style=margin-bottom:1rem>
|
||
<button class="tbtn on" onclick="tab('dns_p')">DNS Lookup</button>
|
||
<button class=tbtn onclick="tab('hdr_p')">HTTP Headers</button>
|
||
<button class=tbtn onclick="tab('jwt_p')">JWT Decoder</button>
|
||
<button class=tbtn onclick="tab('hash_p')">Hasher</button>
|
||
<button class=tbtn onclick="tab('gen_p')">Generators</button></div>
|
||
<script>{TOOLS_JS}</script>
|
||
<div id=dns_p class="card pane"><b>DNS Lookup</b> <span style=color:var(--dim)>(Google DoH)</span><br>
|
||
<input id=dq placeholder=thetempleofdoom.com style=width:70%><button onclick=dns()>Resolve</button>
|
||
<pre id=do style=white-space:pre-wrap></pre><div style=color:var(--dim)>API: /api/hdr style JSON via dns.google</div></div>
|
||
<div id=hdr_p class="card pane" style=display:none><b>HTTP Header Inspector</b><br>
|
||
<input id=hq placeholder=https://lynx.thetempleofdoom.com style=width:70%><button onclick=hdr()>Inspect</button>
|
||
<pre id=ho style=white-space:pre-wrap></pre><div style=color:var(--dim)>API: GET /api/hdr?url=…</div></div>
|
||
<div id=jwt_p class="card pane" style=display:none><b>JWT Decoder</b> (client-side, token never leaves your browser)<br>
|
||
<textarea id=jq rows=3 style="width:100%">paste eyJ…</textarea><button onclick=jwt()>Decode</button>
|
||
<pre id=jo style=white-space:pre-wrap></pre></div>
|
||
<div id=hash_p class="card pane" style=display:none><b>Hasher</b><br>
|
||
<input id=hq2 placeholder="any string" style=width:70%><button onclick=genhash2()>Hash</button>
|
||
<table><tr><th>md5</th><td id=h_md5></td></tr><tr><th>sha1</th><td id=h_sha1></td></tr>
|
||
<tr><th>sha256</th><td id=h_sha256></td></tr><tr><th>sha512</th><td id=h_sha512></td></tr></table>
|
||
<div style=color:var(--dim)>API: GET /api/hash?s=…</div></div>
|
||
<div id=gen_p class="card pane" style=display:none><b>Generators</b><br>
|
||
<button onclick=uuids()>5× UUIDv4</button><pre id=uo></pre>
|
||
<label>password length</label> <input id=pl value=24 style=width:80px><button onclick=pwgen()>Generate</button>
|
||
<pre id=po style="font-size:1.2rem;color:var(--acc)"></pre></div>"""
|
||
return page("tools", body)
|
||
|
||
@app.route("/")
|
||
def index():
|
||
st, b = http(f"http://ip-api.com/json/{request.headers.get('X-Real-IP') or request.remote_addr}?fields=66846719")
|
||
d = jf(b) or {}
|
||
con = db()
|
||
n_sms = con.execute("SELECT COUNT(*) c FROM sms_rentals").fetchone()["c"]
|
||
n_px = con.execute("SELECT COUNT(*) c FROM proxy_checks").fetchone()["c"]
|
||
body = f"""
|
||
<h1>AURIGA <span>TOOLBOX</span></h1>
|
||
<p class=sub>One page. Every network weapon you actually use. No signup, no fluff.</p>
|
||
<div class=grid2>
|
||
<div class=card><h3>◈ Whats-My-IP MAX</h3><p style=color:var(--dim)>You're connecting from <b style=color:var(--acc)>{d.get('query','?')}</b> — {d.get('city','')}, {d.get('country','')}. Full dump: geo, ASN, ISP, VPN flags, rDNS, headers.</p><a href=/ip><button>Open</button></a> <a href=/api/ip><button style=background:#232b45;color:var(--fg)>API</button></a></div>
|
||
<div class=card><h3>◈ Card Check</h3><p style=color:var(--dim)>Luhn + BIN: brand, issuer, country, type, prepaid risk flags. Nothing stored, nothing charged.</p><a href=/card><button>Open</button></a></div>
|
||
<div class=card><h3>◈ SMS Rental</h3><p style=color:var(--dim)>Disposable numbers, 30-minute windows, cancel = refund. {n_sms} rentals served.</p><a href=/sms><button>Open</button></a></div>
|
||
<div class=card><h3>◈ Proxy Lab</h3><p style=color:var(--dim)>Test + rent residential proxies. Same gateway keys as the other sites. {n_px} checks run.</p><a href=/proxy><button>Open</button></a></div>
|
||
<div class=card><h3>◈ Free Tools</h3><p style=color:var(--dim)>DNS, HTTP headers, JWT, hasher, UUID/password generators.</p><a href=/tools><button>Open</button></a></div>
|
||
<div class=card><h3>◈ API-first</h3><p style=color:var(--dim)>Every tool has a JSON API. Agents welcome — that's the point.</p></div>
|
||
</div>"""
|
||
return page("ip", body)
|
||
|
||
@app.route("/health")
|
||
def health(): return jsonify({"ok": True, "service": "auriga"})
|
||
|
||
if __name__ == "__main__":
|
||
app.run(host="0.0.0.0", port=5000, threaded=True)
|