"""robinx.py — the RobinX pre-trade check for a Robinhood Chain (4663) token, in one call, standard library only
(Python 3.8+). https://robinx.io/api#check

    from robinx import check            # copy this file next to your bot
    r = check("0x020bfc650a365f8bb26819deaabf3e21291018b4")
    if r.get("paywall"):
        print("402:", r["price_usd"], r["key_offer"])   # past the free 100 a day: an offer, not an exception
    elif not r["clear"]:
        print("blocked:", r["blockers"])   # rule-based blockers: never a recommendation
    else:
        print("warnings:", r["warnings"], "best caller:", r["checks"]["callers"]["best"])

Free by default, 100 checks a day per IP. Past that the API answers 402 with an x402 payment offer ($0.02 USDC on Base)
and the key offer; a RobinX plan key (ROBINX_KEY, or key=...) covers it. The answer is rule-based observation from public
on-chain and X data: blockers and warnings are rules firing, `clear` means none fired. Not advice.

check() returns the API's data dict. On 402 it returns {"paywall": True, "status": 402, "price_usd": ..., "accepts": ...,
"key_offer": ...} instead of raising, so a bot can decide what to do; any other non-2xx raises RobinXError.
"""
import json
import os
import re
import urllib.error
import urllib.request

BASE = "https://api.robinx.io"
_ADDR = re.compile(r"^0x[0-9a-fA-F]{40}$")


class RobinXError(Exception):
    def __init__(self, status, body):
        super().__init__("robinx: HTTP %s %s" % (status, (body or {}).get("error", "") if isinstance(body, dict) else ""))
        self.status = status
        self.body = body


def check(token, key=None, base=None, timeout=15.0, user_agent="robinx-check/1 (+https://robinx.io/api#check)"):
    addr = str(token or "").strip().lower()
    if not _ADDR.match(addr):
        raise ValueError("robinx: pass a Robinhood Chain token contract address (0x + 40 hex)")
    base = (base or BASE).rstrip("/")
    key = key or os.environ.get("ROBINX_KEY")
    headers = {"Accept": "application/json", "User-Agent": user_agent}
    if key:
        headers["x-robinx-key"] = key
    req = urllib.request.Request("%s/check/%s" % (base, addr), headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as res:
            status, text = res.status, res.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as e:
        status, text = e.code, e.read().decode("utf-8", "replace")
    try:
        body = json.loads(text)
    except ValueError:
        body = {"raw": text[:500]}
    if status == 402:
        acc = (body.get("accepts") or [None])[0] if isinstance(body, dict) else None
        price = None
        units = (acc.get("amount") or acc.get("maxAmountRequired")) if isinstance(acc, dict) else None  # x402 v2 says amount; USDC has 6 decimals
        if units is not None:
            try:
                price = float(units) / 1e6
            except (TypeError, ValueError):
                price = None
        elif isinstance(body, dict) and body.get("price_usd") is not None:
            price = body["price_usd"]
        return {"paywall": True, "status": 402, "price_usd": price, "accepts": body.get("accepts") if isinstance(body, dict) else None,
                "key_offer": body.get("key_offer") if isinstance(body, dict) else None, "body": body}
    if status == 404 and isinstance(body, dict) and body.get("clear") is False:
        return body  # a token the index has not seen: an answer with an `indexed` blocker, built for a launch minutes old
    if status < 200 or status >= 300:
        raise RobinXError(status, body)
    if isinstance(body, dict) and isinstance(body.get("data"), dict):
        out = dict(body["data"])
        out.update({"signal": body.get("signal"), "confidence": body.get("confidence"), "timestamp": body.get("timestamp")})
        return out
    return body


def clear(token, **kw):
    """True when the check ran and no rule-based blocker fired; False when one fired; None at the paywall."""
    r = check(token, **kw)
    if r.get("paywall"):
        return None
    return r.get("clear") is True


if __name__ == "__main__":
    import sys
    print(json.dumps(check(sys.argv[1]), indent=1))
