Night shift: tweet analyzer, data connectors, feed monitor, market watch portal

This commit is contained in:
2026-02-12 00:16:41 -06:00
parent f623cba45c
commit 07f1448d57
20 changed files with 1825 additions and 388 deletions

167
tools/data_sources/arkham.py Executable file
View File

@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""Arkham Intelligence connector — whale tracking, token flows, address intelligence.
Requires API key for most endpoints. Set ARKHAM_API_KEY env var.
Sign up at https://platform.arkhamintelligence.com
"""
import argparse
import json
import os
import sys
from typing import Any
import requests
BASE = "https://api.arkhamintelligence.com"
TIMEOUT = 30
NOTABLE_ADDRESSES = {
"vitalik": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"justin-sun": "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296",
"binance-hot": "0x28C6c06298d514Db089934071355E5743bf21d60",
"coinbase-prime": "0xA9D1e08C7793af67e9d92fe308d5697FB81d3E43",
"aave-treasury": "0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c",
"uniswap-deployer": "0x41653c7d61609D856f29355E404F310Ec4142Cfb",
}
def _get(path: str, params: dict | None = None) -> Any:
key = os.environ.get("ARKHAM_API_KEY")
headers = {"User-Agent": "Mozilla/5.0"}
if key:
headers["API-Key"] = key
r = requests.get(f"{BASE}/{path}", params=params, headers=headers, timeout=TIMEOUT)
if r.status_code in (401, 403) or "api key" in r.text.lower():
raise EnvironmentError(
"Arkham API key required. Set ARKHAM_API_KEY env var.\n"
"Sign up at https://platform.arkhamintelligence.com"
)
r.raise_for_status()
return r.json()
def resolve_address(name_or_addr: str) -> str:
return NOTABLE_ADDRESSES.get(name_or_addr.lower(), name_or_addr)
# ── Data fetchers ───────────────────────────────────────────────────────────
def get_address_info(address: str) -> dict:
return _get(f"intelligence/address/{resolve_address(address)}")
def get_transfers(address: str, limit: int = 20) -> dict:
return _get("token/transfers", {"address": resolve_address(address), "limit": limit})
def search_entity(query: str) -> dict:
return _get("intelligence/search", {"query": query})
# ── Summary helpers ─────────────────────────────────────────────────────────
def summary_address(data: dict) -> str:
lines = ["═══ Address Intelligence ═══", ""]
if isinstance(data, dict):
entity = data.get("entity", {}) or {}
if entity:
lines.append(f" Entity: {entity.get('name', 'Unknown')}")
lines.append(f" Type: {entity.get('type', 'Unknown')}")
lines.append(f" Address: {data.get('address', '?')}")
labels = data.get("labels", [])
if labels:
lines.append(f" Labels: {', '.join(str(l) for l in labels)}")
else:
lines.append(f" {data}")
return "\n".join(lines)
def summary_transfers(data) -> str:
lines = ["═══ Recent Transfers ═══", ""]
transfers = data if isinstance(data, list) else (data.get("transfers", data.get("data", [])) if isinstance(data, dict) else [])
if not transfers:
lines.append(" No transfers found.")
return "\n".join(lines)
for t in transfers[:15]:
token = t.get("token", {}).get("symbol", "?") if isinstance(t.get("token"), dict) else "?"
amount = t.get("amount", t.get("value", "?"))
fr = t.get("from", {})
to = t.get("to", {})
fl = (fr.get("label") or fr.get("address", "?")[:12]) if isinstance(fr, dict) else str(fr)[:12]
tl = (to.get("label") or to.get("address", "?")[:12]) if isinstance(to, dict) else str(to)[:12]
lines.append(f" {token:<8} {str(amount):>15} {fl}{tl}")
return "\n".join(lines)
def summary_notable() -> str:
lines = ["═══ Notable/Whale Addresses ═══", ""]
for name, addr in NOTABLE_ADDRESSES.items():
lines.append(f" {name:<20} {addr}")
lines.append("")
lines.append(" Use these as shortcuts: arkham.py address vitalik")
return "\n".join(lines)
# ── CLI ─────────────────────────────────────────────────────────────────────
def main():
common = argparse.ArgumentParser(add_help=False)
common.add_argument("--pretty", action="store_true", help="Pretty-print JSON output")
common.add_argument("--summary", action="store_true", help="Human-readable summary")
parser = argparse.ArgumentParser(description="Arkham Intelligence connector", parents=[common])
sub = parser.add_subparsers(dest="command", required=True)
p_addr = sub.add_parser("address", help="Address intelligence", parents=[common])
p_addr.add_argument("address", help="Ethereum address or notable name")
p_tx = sub.add_parser("transfers", help="Recent token transfers", parents=[common])
p_tx.add_argument("address")
p_tx.add_argument("--limit", type=int, default=20)
p_search = sub.add_parser("search", help="Search entities", parents=[common])
p_search.add_argument("query")
sub.add_parser("notable", help="List notable/whale addresses", parents=[common])
args = parser.parse_args()
try:
if args.command == "notable":
if args.summary:
print(summary_notable())
else:
json.dump(NOTABLE_ADDRESSES, sys.stdout, indent=2 if args.pretty else None)
print()
return
if args.command == "address":
data = get_address_info(args.address)
if args.summary:
print(summary_address(data)); return
result = data
elif args.command == "transfers":
data = get_transfers(args.address, args.limit)
if args.summary:
print(summary_transfers(data)); return
result = data
elif args.command == "search":
result = search_entity(args.query)
else:
parser.print_help(); return
json.dump(result, sys.stdout, indent=2 if args.pretty else None)
print()
except EnvironmentError as e:
print(str(e), file=sys.stderr); sys.exit(1)
except requests.HTTPError as e:
detail = e.response.text[:200] if e.response is not None else ""
print(json.dumps({"error": str(e), "detail": detail}), file=sys.stderr); sys.exit(1)
except Exception as e:
print(json.dumps({"error": f"{type(e).__name__}: {e}"}), file=sys.stderr); sys.exit(1)
if __name__ == "__main__":
main()