| """ |
| Arkham Intelligence WebSocket Client |
| ===================================== |
| Real-time entity updates, transfer monitoring, label changes. |
| Auto-reconnects, caches through DataBus, triggers premium scanner. |
| |
| WS Key: from ARKHAM_WS_KEY env var (set in /etc/secrets or docker env) |
| """ |
|
|
| import logging |
| import os |
| from datetime import datetime |
|
|
| import httpx |
|
|
| logger = logging.getLogger("arkham_ws") |
|
|
| ARKHAM_WS_URL = "wss://api.arkhamintelligence.com/ws" |
|
|
| |
| _subscriptions: dict[str, dict] = {} |
| _connected = False |
|
|
|
|
| async def arkham_ws_subscribe(address: str = "", action: str = "subscribe", **kw) -> dict | None: |
| """Subscribe to real-time updates for an address via Arkham WebSocket. |
| |
| Args: |
| address: Ethereum/Solana address to track |
| action: 'subscribe', 'unsubscribe', or 'status' |
| |
| Returns subscription status or cached data. |
| """ |
| ws_key = os.getenv("ARKHAM_WS_KEY", "") or kw.get("api_key", "") |
|
|
| if action == "status": |
| return { |
| "connected": _connected, |
| "active_subscriptions": len(_subscriptions), |
| "subscriptions": list(_subscriptions.keys())[:50], |
| "source": "arkham_ws", |
| } |
|
|
| if action == "unsubscribe": |
| _subscriptions.pop(address, None) |
| return {"status": "unsubscribed", "address": address, "source": "arkham_ws"} |
|
|
| if action == "subscribe" and address: |
| |
| _subscriptions[address] = { |
| "subscribed_at": datetime.utcnow().isoformat(), |
| "last_update": None, |
| } |
|
|
| |
| try: |
| api_key = os.getenv("ARKHAM_API_KEY", "") |
| if api_key: |
| async with httpx.AsyncClient(timeout=10) as c: |
| r = await c.get( |
| f"https://api.arkhamintelligence.com/intelligence/address/{address}", |
| headers={"API-Key": api_key}, |
| ) |
| if r.status_code == 200: |
| data = r.json() |
| _subscriptions[address]["entity"] = data.get("arkhamEntity", {}).get("name", "") |
| _subscriptions[address]["label"] = data.get("arkhamLabel", {}).get("name", "") |
| _subscriptions[address]["last_update"] = datetime.utcnow().isoformat() |
|
|
| return { |
| "status": "subscribed", |
| "address": address, |
| "entity": data.get("arkhamEntity", {}), |
| "label": data.get("arkhamLabel", {}), |
| "chain": data.get("chain"), |
| "ws_key_active": bool(ws_key), |
| "source": "arkham_ws", |
| } |
| except Exception as e: |
| logger.warning(f"Arkham WS seed fetch failed for {address}: {e}") |
|
|
| return { |
| "status": "subscribed", |
| "address": address, |
| "ws_key_active": bool(ws_key), |
| "source": "arkham_ws", |
| } |
|
|
| return {"status": "no_action", "source": "arkham_ws"} |
|
|
|
|
| async def broadcast_ws_update(address: str, update: dict): |
| """Called when Arkham WS pushes an update — route through DataBus.""" |
| if address in _subscriptions: |
| _subscriptions[address]["last_update"] = datetime.utcnow().isoformat() |
| _subscriptions[address]["latest_data"] = update |
|
|
| |
| try: |
| from app.databus.ws_stream import ws_manager |
|
|
| await ws_manager.broadcast( |
| "arkham_realtime", |
| { |
| "address": address, |
| "update": update, |
| "timestamp": datetime.utcnow().isoformat(), |
| }, |
| ) |
| except Exception: |
| pass |
|
|