| |
| """ |
| Ingest CryptoScamDB entries into RAG known_scams collection. |
| API: https://api.cryptoscamdb.org/v1/scams |
| """ |
|
|
| import asyncio |
| import hashlib |
| import logging |
| import os |
| import sys |
|
|
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") |
| logger = logging.getLogger("ingest_cryptoscamdb") |
|
|
| API_URLS = [ |
| "https://api.cryptoscamdb.org/v1/scams", |
| "https://api.cryptoscamdb.org/v1/all", |
| ] |
| RAG_API = "http://localhost:8000/api/v1/rag/ingest" |
| COLLECTION = "known_scams" |
|
|
|
|
| def make_doc_id(address: str, source: str) -> str: |
| return hashlib.sha256(f"known_scams:{address.lower()}:{source}".encode()).hexdigest()[:16] |
|
|
|
|
| async def ingest_via_api(content: str, metadata: dict) -> bool: |
| try: |
| import httpx |
|
|
| async with httpx.AsyncClient(timeout=15) as client: |
| resp = await client.post(RAG_API, json={"collection": COLLECTION, "content": content, "metadata": metadata}) |
| return resp.status_code == 200 |
| except Exception as e: |
| logger.debug(f"API ingest failed: {e}") |
| return False |
|
|
|
|
| async def main(): |
| import httpx |
|
|
| |
| data = None |
| for url in API_URLS: |
| try: |
| logger.info(f"Fetching from {url}...") |
| async with httpx.AsyncClient(timeout=30) as client: |
| resp = await client.get(url) |
| if resp.status_code == 200: |
| data = resp.json() |
| logger.info(f"Got response from {url}") |
| break |
| else: |
| logger.warning(f"{url} returned {resp.status_code}") |
| except Exception as e: |
| logger.warning(f"Failed to fetch {url}: {e}") |
|
|
| if not data: |
| logger.error("All CryptoScamDB API endpoints failed") |
| return |
|
|
| |
| scams = [] |
| if isinstance(data, dict): |
| scams = data.get("result", data.get("scams", data.get("data", []))) |
| elif isinstance(data, list): |
| scams = data |
|
|
| if not scams: |
| logger.error( |
| f"No scam entries found in response. Keys: {list(data.keys()) if isinstance(data, dict) else 'list'}" |
| ) |
| return |
|
|
| logger.info(f"Found {len(scams)} scam entries") |
|
|
| ingested = 0 |
| errors = 0 |
|
|
| for i, scam in enumerate(scams): |
| if not isinstance(scam, dict): |
| continue |
|
|
| name = scam.get("name", "") |
| category = scam.get("category", "unknown") |
| status = scam.get("status", "unknown") |
| description = scam.get("description", "") |
| url = scam.get("url", "") |
| addresses = scam.get("addresses", []) |
|
|
| if not addresses: |
| |
| content = f"CryptoScamDB entry: {name}. Category: {category}. Status: {status}. URL: {url}. Description: {description}" |
| metadata = { |
| "source": "cryptoscamdb", |
| "category": category, |
| "status": status, |
| "name": name, |
| "url": url, |
| "severity": "high" if status == "Active" else "medium", |
| } |
| make_doc_id(name or url or str(i), "cryptoscamdb") |
| if await ingest_via_api(content, metadata): |
| ingested += 1 |
| else: |
| errors += 1 |
| else: |
| |
| for addr in addresses: |
| if not addr or len(addr) < 10: |
| continue |
| content = f"CryptoScamDB flagged address: {addr}. Project: {name}. Category: {category}. Status: {status}. Description: {description}" |
| metadata = { |
| "source": "cryptoscamdb", |
| "address": addr.lower(), |
| "category": category, |
| "status": status, |
| "name": name, |
| "severity": "high" if status == "Active" else "medium", |
| } |
| if await ingest_via_api(content, metadata): |
| ingested += 1 |
| else: |
| errors += 1 |
|
|
| if (i + 1) % 100 == 0: |
| logger.info(f"Progress: {i + 1}/{len(scams)} | ingested: {ingested}, errors: {errors}") |
| await asyncio.sleep(0.1) |
|
|
| logger.info(f"COMPLETE: ingested={ingested}, errors={errors}") |
|
|
|
|
| if __name__ == "__main__": |
| asyncio.run(main()) |
|
|