Module gametime_watcher.cli
Command-line interface for the Gametime ticket watcher.
Functions
def build_parser() ‑> argparse.ArgumentParser-
Expand source code
def build_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser( prog="gametime_watcher", description=( "List current Gametime ticket prices for an event and alert when " "seats in chosen sections drop below a per-ticket price.\n\n" "Use 'gametime_watcher search <query>' to find event links by " "team or performer name." ), formatter_class=argparse.RawDescriptionHelpFormatter, ) p.add_argument("event", help="Gametime event/listing URL, short link, or 24-char event id") p.add_argument( "-s", "--sections", action="append", help="Section filter: ranges (200-299), exact (119), or group names " '("Solon Club"); comma-separated. May be given multiple times ' "to combine filters. Default: all sections.", ) p.add_argument( "-p", "--max-price", type=float, help="Maximum all-in price PER TICKET, in dollars.", ) p.add_argument( "-q", "--quantity", type=int, default=1, help="Number of seats wanted together (default: 1). Only listings offering this lot size are kept.", ) p.add_argument( "--allow-larger", action="store_true", help="Also keep listings that only offer a larger lot than --quantity.", ) p.add_argument("--json", action="store_true", help="Output JSON instead of text.") p.add_argument( "--watch", action="store_true", help="Poll repeatedly and alert only on newly-seen matching listings.", ) p.add_argument( "--interval", type=float, default=300.0, help="Polling interval in seconds when --watch is set (default: 300).", ) p.add_argument("--webhook", help="POST a JSON payload to this URL on new matches.") p.add_argument( "--command", help="Run this command on new matches; the JSON payload is sent on stdin.", ) p.add_argument("--user-agent", default=None, help="Override the HTTP User-Agent.") p.add_argument("--timeout", type=float, default=30.0, help="HTTP timeout in seconds.") return p def main(argv: Optional[List[str]] = None) ‑> int-
Expand source code
def main(argv: Optional[List[str]] = None) -> int: raw = argv if argv is not None else sys.argv[1:] # Dispatch to subcommands. if raw and raw[0] == "search": return _run_search(raw[1:]) if raw and raw[0] == "scan-all": return _run_scan_all(raw[1:]) parser = build_parser() args = parser.parse_args(argv) if args.user_agent is None: from .api import DEFAULT_USER_AGENT args.user_agent = DEFAULT_USER_AGENT if args.quantity is not None and args.quantity < 1: parser.error("--quantity must be >= 1") if not args.watch: try: event, all_listings, matches = _scan(args.event, args) except GametimeError as exc: print(f"error: {exc}", file=sys.stderr) return 2 if args.json: print(_emit_json(event, matches)) else: title = event.name if event and event.name else args.event when = f" @ {event.datetime_local}" if event and event.datetime_local else "" print(f"{title}{when}") print(f"Criteria: {_criteria_text(args)}") print(f"Found {len(matches)} of {len(all_listings)} listings:") for listing in matches: print(" " + _format_listing(listing)) # Exit 0 when matches found, 1 when none (useful for cron/scripts). return 0 if matches else 1 # --watch mode: poll and alert on new matches. seen: set = set() print( f"Watching {args.event} every {args.interval:g}s for {_criteria_text(args)} (Ctrl-C to stop)...", file=sys.stderr, ) try: while True: try: event, all_listings, matches = _scan(args.event, args) except GametimeError as exc: print(f"[warn] scan failed: {exc}", file=sys.stderr) else: new = [m for m in matches if (m.id, m.price_total) not in seen] for m in matches: seen.add((m.id, m.price_total)) if new: stamp = time.strftime("%Y-%m-%d %H:%M:%S") print(f"[{stamp}] {len(new)} new matching listing(s):") for listing in new: print(" " + _format_listing(listing)) _notify(event, new, args) time.sleep(max(1.0, args.interval)) except KeyboardInterrupt: print("\nStopped.", file=sys.stderr) return 0