Spaces:
Paused
Paused
| 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 | |
| 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", | |
| } | |
| } | |
| 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), | |
| } | |
| 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 |