instanum-api / app.py
nonamearyan's picture
Update app.py
2d674a1 verified
Raw
History Blame Contribute Delete
5.53 kB
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