World Vibe Web
The distributed app store for vibe-coded projects. Browse, discover, and install open-source apps from across GitHub.
Monitor North Jersey retailers for Pokémon TCG stock and get Discord alerts
Translation pending
AI summary · website and README
CommercePython · JavaScriptGitHub @poncema4OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Pokémon TCG Stock & Listing Alert Bot Personal North Jersey Pokémon TCG monitor. It watches Target, Walmart, Best Buy and GameStop for online Pokémon listings/stock, sends verified Discord alerts, maintains a short-lived live-hit map, and provides a school-day store route. The bot does not buy products, bypass retailer protections, or guarantee stock after an alert. Retailer inventory can change between detection and your click.Read the full README ↗
A personal Pokémon TCG restock alert bot for North Jersey. It watches Target, Walmart, Best Buy, GameStop and Pokémon Center, pings Discord when something is really in stock, says what the price means against live TCGplayer data, and shows the stores near home on a map.
It exists to help a collector get product at fair prices ahead of resellers: confirmed restock alerts within about a minute, a price check so you know whether a listing is a good deal, the nearest open store, and a route for the school-day sweep.
Site: https://poncema4.github.io/pokemon-alert-bot/ (Map, Route, 30th guide).
data-available flag overrides its JSON-LD (which says InStock for items that are not). A disabled "Add to cart" button (Target's loading placeholder) is not stock.bot/sellers.py), so a first-party restock is never missed; a reseller-only page is recorded as out of stock with reason marketplace_only.rearm_minutes); unknown readings (blocked or half-loaded pages) never re-arm it, so a flickering page cannot ping twice for the same stay.One embed per alert, coloured by the price verdict, with @everyone in the message when DISCORD_PING is on:
| Part | Example |
|---|---|
| Title | 🟢 IN STOCK · GameStop (🆕 NEW LISTING IN STOCK, 🧪 TEST ALERT) |
| Product | the name, as a bold hyperlink (no giant image preview) |
| Verdict | BUY: LOW, FAIR PRICE, ABOVE MARKET, or AT RETAIL / ABOVE MSRP when no market price exists |
| Fields | price, retail (and above MSRP (+70%)), TCGplayer market with its age, proof, links: Product page, Add to cart (Walmart and Best Buy only: tapping it puts the item in your cart, you check out yourself), Map, TCGplayer price |
The verdict compares the listed price with the live TCGplayer market price: 10% or more under is BUY: LOW, within 10% is FAIR PRICE, more than 10% over is ABOVE MARKET. The market price is looked up by product name (bot/advisor.py), cached in docs/market.json and never older than 10 minutes when an alert is sent. If TCGplayer cannot be reached the alert simply omits the comparison.
Blind-spot notices (below) use the same card style.
Measured from GitHub's runner. The map's status lamps show the live state and explain each one on hover.
| Retailer | Reading? | Why |
|---|---|---|
| GameStop | yes | its page carries its own availability flag |
| Walmart | sometimes | redirects automated visitors to a robot check ("Robot or human?"); read whenever it lets the bot in |
| Target | no | server page shows a disabled placeholder; real stock comes from a captcha-protected API |
| Best Buy | spotty | read in a real browser; its bot defence serves an empty page about half the time (an empty page is "unknown", never "in stock"; a half-loaded page is reloaded once, a robot wall never is), and only the store's own offers count, not marketplace resellers |
| Pokémon Center | no | 403 or a robot check |
A retailer that cannot be read looks exactly like "nothing in stock", so each run records per-retailer counts in docs/health.json. After 24 hours unreadable the bot posts one BLIND SPOT notice, and one RECOVERED notice when it can read again. Listings never readable for 7 days are pruned (seed URLs stay).
docs/, served by GitHub Pages)Open/closed comes from each store's weekly schedule in docs/stores.json (hours, with hours_source and hours_checked), read in Eastern time by one shared reader in docs/js/common.js; the pages re-read the clock every 15 seconds so a store flips at its opening and closing minute. A store whose schedule cannot be read shows "Hours?", never "Open".
Add to cart links. Walmart's is built from the item id in the product URL. Best Buy's needs the 7-digit SKU (its product codes such as JJG2TL8XCJ are rejected as "Invalid SKU"): the browser reader takes it from the page and the watcher remembers it in state.json; bestbuy_skus in config/search_config.json holds the ones verified against Best Buy's own /sku/ pages. Target, GameStop and Pokémon Center publish no such link, so their alerts use the product page's own button.
Every script and stylesheet link in docs/*.html carries a content hash (js/common.js?v=1a2b3c4d) so a browser can never pair an old script with a new page. After changing anything in docs/js or docs/css, run python tools/stamp_assets.py (the tests fail if you forget).
Home is hard-set in docs/stores.json. Store pins come from each store's own OpenStreetMap record where one exists (otherwise the exact street-address point), and pin_source says which; tools/check_pins.py re-checks them.
.github/workflows/monitor.yml runs the tests on every push and pull request, and, separately and without waiting for them, one long-lived watcher job (bot/monitor_loop.py, about 55 minutes) on schedule or dispatch that starts its own successor. The tests gate code changes only: a scheduled run that waited for them left nobody watching for about three minutes an hour, and a data-dependent test failure there once silenced every alert for two weeks. GitHub's cron is best effort (measured median gap between scheduled runs was 239 minutes), so the job polls inside itself every 30 seconds and the 30-minute cron is only a safety net.
If a watcher job is killed outright (a lost runner, the job timeout) its hand-over step never runs, so watchdog.yml runs whenever any monitor run ends and tools/ensure_watcher.py starts a watcher if none is running or queued. Hand-over, watchdog and the 30-minute cron are three independent ways the watcher comes back.
The watcher runs in its own concurrency group (pokeping-watcher), separate from the test runs on push and PR. GitHub keeps only one pending run per group, so when they shared one, every merge to main replaced the watcher's queued handover and the chain broke until the next cron run (the data went stale after each merge).
Each cycle checks the seed and recently readable listings, refreshes the live-hit list and announces new listings. Every 10th cycle also searches by keyword for new listings and refreshes market prices, and about every 20 cycles the 30th guide prices refresh from TCGplayer. State is committed every 5 minutes as a heartbeat (even when nothing changed, so the site's "checked N min ago" stays current) and at once when the live-hit list changes. On an always-on machine, python bot/monitor_loop.py does the same.
| Workflow | Purpose |
|---|---|
monitor.yml | tests on every PR and push (they gate code, not the watcher); the watcher on schedule or manual dispatch |
30th-prices.yml | hourly fallback refresh of the 30th guide prices |
snapshot.yml | manual: saves the retailer pages exactly as the runner receives them |
watchdog.yml | whenever a monitor run ends: starts a watcher if none is running or queued |
bestbuy-api-check.yml | manual: asks Best Buy's official API about every known SKU (needs the BESTBUY_API_KEY secret) |
verify-deployed.yml | after every Pages build: loads the live site in a real browser and checks it works and matches the repo (tools/verify_deployed.py) |
reader-probe.yml | manual: shows what the browser reader sees on Best Buy from a runner |
test-alert.yml | manual: posts clearly labelled TEST alerts and prints Discord's answer |
Secrets: DISCORD_WEBHOOK_URL (required for alerts), DISCORD_PING=true (optional, adds @everyone).
Best Buy publishes a free product API that answers "can this be ordered online right now?" for its own stock, with no browser and no bot wall in the way. With a key, the watcher asks it for every known SKU in one request each cycle and uses the browser only for products it does not cover (or if the API is down).
BESTBUY_API_KEY (GitHub > Settings > Secrets and variables > Actions, or gh secret set BESTBUY_API_KEY).bestbuy_skus in config/search_config.json. The key is never printed.Heads-up (learned 2026-10-05): Best Buy's developer portal refuses personal addresses ("Free email and .edu addresses are not allowed at this time"), so a Gmail account cannot get a key; it needs an address on a company or personal domain. Until one exists, Best Buy alerts come from the browser reader and the email route below. A product counts as in stock only when the API says it can be ordered online and its ordering status is exactly "Available"; sold-out and coming-soon statuses are out of stock, and anything unrecognised is unknown (never an alert). Without a key nothing changes: Best Buy is read in a browser as before.
bot/ everything that runs: monitor, loop, advisor, notify, new-listing and live-hit steps, 30th price updater, accuracy maths
config/ search_config.json: keywords, seed URLs, retail price (MSRP) rules, market-search rules
data/ state.json (what the bot has seen), ground_truth.json (human-checked outcomes for measuring accuracy)
docs/ the site and the public JSON it reads: stores, alerts, health, market, 30th prices
tools/ pin checker, page snapshotter, test-alert sender
tests/ Python and Node tests, plus captured real retailer and TCGplayer responses in tests/fixtures
.github/ workflows
pip install -r requirements.txt
python tests/test_accuracy.py # detection, health, advisor, embeds, loop, pins, site structure
node tests/web/common.test.js # the page helpers
python tests/test_end_to_end.py # a restock goes through the real watcher code to a (local) Discord webhook and onto the real site in Chromium
The end-to-end test stages a restock at Target, Walmart, Best Buy and GameStop and checks that Discord and Live online agree (title, price, link), that nothing pings twice for the same stay (even through a blocked reading), that a sell-out clears Live online, and that Pokémon Center is shown as "can't read" and never alerts. CI runs it on every PR.
Tests run against captured real responses (retailer pages, TCGplayer searches), and the habit is to prove a check can fail: change one thing, watch the right test fail, restore it. Never trust a check that cannot fail.
bot/accuracy.py computes precision, recall, F1 and detection latency from data/ground_truth.json. Record what you actually saw when you clicked an alert (retailer, URL, alert type, whether it was purchasable, when). Never label an event from the bot's own prediction; the point is independent ground truth.