diff --git a/plugins/clone-app/skills/clone-app/scripts/check-appstore.py b/plugins/clone-app/skills/clone-app/scripts/check-appstore.py new file mode 100644 index 0000000..435c938 --- /dev/null +++ b/plugins/clone-app/skills/clone-app/scripts/check-appstore.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Best-effort check whether an iOS App Store equivalent exists, via the +public iTunes Search API. There is no reliable Android-package → App-Store-ID +mapping, so we search by term (app title) and return the top matches.""" +import sys, json, argparse, urllib.request, urllib.parse + +def fetch(term): + q = urllib.parse.urlencode({"term": term, "entity": "software", "limit": 5}) + url = f"https://itunes.apple.com/search?{q}" + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=30) as r: + return json.loads(r.read().decode("utf-8", "replace")) + +def shape(data): + results = [] + for r in data.get("results", []): + results.append({ + "name": r.get("trackName"), + "seller": r.get("sellerName"), + "rating": r.get("averageUserRating"), + "rating_count": r.get("userRatingCount"), + "price": r.get("formattedPrice"), + "url": r.get("trackViewUrl"), + }) + return {"found": len(results) > 0, "source": "app-store", "results": results} + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("term") + ap.add_argument("--json-file") + args = ap.parse_args() + + if args.json_file: + with open(args.json_file, encoding="utf-8") as f: + data = json.load(f) + else: + try: + data = fetch(args.term) + except Exception as e: + print(json.dumps({"found": False, "source": "app-store", + "results": [], "error": str(e)}, indent=2)) + sys.exit(0) + + print(json.dumps(shape(data), indent=2)) + +if __name__ == "__main__": + main() diff --git a/plugins/clone-app/tests/fixtures/itunes-sample.json b/plugins/clone-app/tests/fixtures/itunes-sample.json new file mode 100644 index 0000000..8b31e29 --- /dev/null +++ b/plugins/clone-app/tests/fixtures/itunes-sample.json @@ -0,0 +1,13 @@ +{ + "resultCount": 1, + "results": [ + { + "trackName": "Example App", + "sellerName": "Example Studio", + "averageUserRating": 4.5, + "userRatingCount": 8421, + "formattedPrice": "Free", + "trackViewUrl": "https://apps.apple.com/app/id123456789" + } + ] +} diff --git a/plugins/clone-app/tests/test-check-appstore.py b/plugins/clone-app/tests/test-check-appstore.py new file mode 100644 index 0000000..cb433c8 --- /dev/null +++ b/plugins/clone-app/tests/test-check-appstore.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +import json, subprocess, sys, os + +HERE = os.path.dirname(os.path.abspath(__file__)) +SCRIPT = os.path.join(HERE, "..", "skills", "clone-app", "scripts", "check-appstore.py") +FIXTURE = os.path.join(HERE, "fixtures", "itunes-sample.json") + +def main(): + out = subprocess.check_output( + [sys.executable, SCRIPT, "Example App", "--json-file", FIXTURE]) + d = json.loads(out) + fails = [] + def check(name, cond): + print(f"{'PASS' if cond else 'FAIL'}: {name}") + if not cond: fails.append(name) + check("found true", d["found"] is True) + check("source", d["source"] == "app-store") + check("one result", len(d["results"]) == 1) + r = d["results"][0] + check("name", r["name"] == "Example App") + check("seller", r["seller"] == "Example Studio") + check("rating", abs(r["rating"] - 4.5) < 0.001) + check("rating_count", r["rating_count"] == 8421) + check("price", r["price"] == "Free") + check("url", r["url"].endswith("id123456789")) + sys.exit(1 if fails else 0) + +main()