#!/usr/bin/env python3 """MySkyDex outbound HTTPS feeder; reads readsb aircraft.json and never touches the SDR.""" import json, logging, os, re, time, urllib.error, urllib.request logging.basicConfig(level=os.getenv("MYSKYDEX_LOG_LEVEL", "INFO"), format="%(asctime)s %(levelname)s %(message)s") LOG = logging.getLogger("myskydex-feed") UUID_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$") def snapshot(url): with urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent":"MySkyDex-Feeder/1"}), timeout=5) as r: raw = r.read(5242881) if len(raw) > 5242880: raise ValueError("aircraft.json exceeds 5 MB") source = json.loads(raw); rows = [] fields = ("hex","flight","lat","lon","alt_baro","alt_geom","gs","track","baro_rate","geom_rate","squawk","category","seen","seen_pos","emergency") for item in source.get("aircraft", []): if not isinstance(item, dict) or item.get("lat") is None or item.get("lon") is None: continue if float(item.get("seen_pos", item.get("seen", 0))) > 30: continue rows.append({k:item[k] for k in fields if k in item and item[k] is not None}) return {"generated_at":source.get("now", time.time()), "aircraft":rows[:200]} def main(): uuid = os.getenv("MYSKYDEX_UUID", "").strip().lower() if not UUID_RE.fullmatch(uuid): raise SystemExit("MYSKYDEX_UUID must be a standard UUID") source = os.getenv("MYSKYDEX_AIRCRAFT_URL", "http://ultrafeeder/tar1090/data/aircraft.json") endpoint = os.getenv("MYSKYDEX_INGEST_URL", "https://myskydex.co.uk/api/receivers/anonymous-ingest.php") LOG.info("receiver UUID %s starting", uuid) while True: started = time.monotonic() try: body = snapshot(source); body["uuid"] = uuid req = urllib.request.Request(endpoint, json.dumps(body,separators=(",",":")).encode(), method="POST", headers={"Content-Type":"application/json","Accept":"application/json","User-Agent":"MySkyDex-Feeder/1"}) with urllib.request.urlopen(req, timeout=10) as response: result = json.load(response) LOG.info("connected UUID=%s received=%s valid=%s passed=%s", uuid, result.get("received"), result.get("valid"), result.get("processed")) except Exception as error: LOG.warning("UUID=%s cycle failed: %s", uuid, error) time.sleep(max(.2, 2 - (time.monotonic() - started))) if __name__ == "__main__": main()