import os, sqlite3, threading from fastapi import FastAPI, Query from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from huggingface_hub import hf_hub_download import uvicorn app = FastAPI(title="InstaNum API v3") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["GET"]) DB1_PATH = "instadb.db" DB2_PATH = "Instagram.db" db1_ready = threading.Event() db1_error = None def download_db(): global db1_error try: print("Downloading instadb.db from HF Dataset...") hf_hub_download( repo_id="nonamearyan/instanum", filename="instadb.db", repo_type="dataset", local_dir="." ) print("DB1 ready!") db1_ready.set() except Exception as e: db1_error = str(e) print(f"DB1 download failed: {e}") db1_ready.set() threading.Thread(target=download_db, daemon=True).start() def get_columns(db_path): try: conn = sqlite3.connect(db_path) cur = conn.cursor() cur.execute("PRAGMA table_info(users)") cols = [r[1] for r in cur.fetchall()] conn.close() return cols except: return [] def query_db(db_path, field, val, limit): if not os.path.exists(db_path): return [] try: cols = get_columns(db_path) col = field if field == "instagram_id": if "instagram_id" in cols: col = "instagram_id" elif "username" in cols: col = "username" val = val.lstrip("@") else: return [] if col not in cols: return [] conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row cur = conn.cursor() if col == "phone": cur.execute( f"SELECT * FROM users WHERE {col} = ? OR {col} = ? LIMIT ?", (val, f"'{val}'", limit) ) elif col == "username": clean = val.lstrip("@") cur.execute( f"SELECT * FROM users WHERE {col} = ? OR {col} = ? LIMIT ?", (clean, "@" + clean, limit) ) else: cur.execute(f"SELECT * FROM users WHERE {col} = ? LIMIT ?", (val, limit)) rows = [dict(r) for r in cur.fetchall()] conn.close() return rows except Exception as e: print(f"Query error on {db_path}: {e}") return [] def normalize_row(row): """Normalize row to match insta.js expectations""" # Get username from instagram_id or username column username = row.get("instagram_id") or row.get("username") or "" # Build full name fname = row.get("fname") or row.get("name") or "" lname = row.get("lname") or "" name = f"{fname} {lname}".strip() or None # Strip quotes from phone phone = row.get("phone") or "" phone = phone.replace("'", "").strip() or None return { "id": row.get("id"), "username": username, "name": name, "email": row.get("email") or None, "phone": phone, "address": row.get("address") or None, } def merge_results(r1, r2, limit): seen = set() merged = [] for row in r1 + r2: key = (row.get("username", ""), row.get("phone", ""), row.get("email", "")) if key not in seen: seen.add(key) merged.append(row) if len(merged) >= limit: break return merged @app.get("/") def root(): db1_ok = db1_ready.is_set() and not db1_error db2_ok = os.path.exists(DB2_PATH) return { "status": "ready", "db1_17M": "ready" if db1_ok else ("error: " + str(db1_error) if db1_error else "loading"), "db2_4.9M": "ready" if db2_ok else "not found", "endpoints": { "/api?username=": "search by instagram username", "/api?phone=": "search by phone", "/api?email=": "search by email", "/api?name=": "search by name", } } @app.get("/status") def status(): return { "db1_ready": db1_ready.is_set(), "db1_error": db1_error, "db1_exists": os.path.exists(DB1_PATH), "db1_columns": get_columns(DB1_PATH), "db2_exists": os.path.exists(DB2_PATH), "db2_columns": get_columns(DB2_PATH), } @app.get("/api") def search( username: str = Query(None), phone: str = Query(None), email: str = Query(None), name: str = Query(None), limit: int = Query(10, ge=1, le=50) ): fields = { "instagram_id": "@" + username.lstrip("@") if username else None, "phone": phone, "email": email, "fname": name, } col, val = next(((k, v) for k, v in fields.items() if v), (None, None)) if not col: return {"success": False, "error": "no_query", "hint": "Use ?username= ?phone= ?email= ?name="} val = val.strip() r1 = query_db(DB1_PATH, col, val, limit) if db1_ready.is_set() and not db1_error else [] r2 = query_db(DB2_PATH, col, val, limit) raw = merge_results(r1, r2, limit) # Normalize for insta.js compatibility results = [normalize_row(r) for r in raw] return { "success": True, "count": len(results), "sources": {"db1_17M": len(r1), "db2_4.9M": len(r2)}, "results": results } if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860) # 1774199147