A price check can report itself, and the dialog is the disclosure - #35
Merged
Conversation
ADDED: `worker/`, a Cloudflare Worker relaying a bug report from the app to one Discord channel as an embed plus a `report.md` written to be pasted into an issue unedited. - The app ships this Worker's public URL, never a credential: the webhook is a Cloudflare secret, so extracting the URL from the bundle yields a rate-limited endpoint and nothing more. - Nothing in the binary sends a report yet, so `PRIVACY.md` is deliberately unchanged — it is part of the app-side change, where the claim that there is no server on the other end stops holding. ADDED: A payload of item text, parse output, an optional comment and an optional PNG screenshot, with per-field caps that refuse rather than truncate — half an item is a worse report than none. ADDED: Mentions disabled with `allowed_mentions`, every user-written string fenced, bidi overrides and control characters stripped, and only known fields read, so a report cannot ping anyone, link anywhere, or hijack the message shape. ADDED: A screenshot must decode to a real PNG and is attached under a filename we choose. ADDED: Optional per-IP and per-day caps in KV. - They fail open: losing a real report to a KV blip is worse than letting one extra through. ADDED: `publish.sh` and `rotate-webhook.sh`, reading a gitignored `worker/.env`, with tracing suspended across the read so `bash -x` cannot print the credentials. - That file is the whole access control for deploying, and is why no workflow in this repo or the data repo may call these scripts: neither public repo holds an Actions secret, and adding the first would change the threat model for every workflow at once. ADDED: `send-test.sh`, whose `--hostile` mode sends mentions, a masked link, a fence break and a right-to-left override, and which identifies itself so Cloudflare's edge does not answer 1010. ADDED: 18 contract tests in `worker/test.mjs`, run with `node --test`. CHANGED: `.gitignore` keeps `.env`, `.dev.vars`, `node_modules/` and `.wrangler/` out of the repo. CHANGED: `CLAUDE.md`'s doc table points at `worker/README.md`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CHANGED: The relay posts each report as its own forum thread rather than a message in a text channel, so triage is Discord's own open/resolved state and its tags instead of a convention. - A forum rejects a message with no `thread_name` and a text channel rejects one that has it, so `DISCORD_FORUM` exists to match whichever the webhook points at. CHANGED: The thread is named from the item plus the report id, capped at Discord's 100 characters with the id's room reserved before the name is trimmed. - The name is built from the item's own text, never from anything the reporter wrote, so a report cannot choose what a thread in the server is called; the id keeps two reports about the same base from becoming one post title. CHANGED: A Discord rejection now logs the response body, not just the status — the cause is a channel/payload mismatch that the status alone never names. ADDED: Four tests covering the thread name, its cap, its single line, and the text-channel fallback. CHANGED: `README.md` says the channel must be created as a forum, since Discord cannot convert one later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ADDED: A **Report a Bug** button on the price-check panel, opening a dialog that shows the whole payload — the clipboard capture, what this tool made of it, the build line and the screenshot — before anything is sent. - Nothing may reach the relay that the preview does not draw, which is why `report::to_json` writes five keys and a test asserts the count. - The payload is frozen when the dialog opens: the updater can swap a bundle in and a search can land while the user is still reading, and a preview a frame behind what will be sent says nothing. ADDED: `report::describe`, the parse dump a mispricing is actually diagnosed from: what was read off the clipboard, what it resolved to in the bundle, which modifier matched which stat record — and, the line that earns the rest, which matched none. ADDED: An optional screenshot, read back from our own framebuffer with `glReadPixels` between the draw and the swap. - It is this process's own pixels and cannot be anything else, so the game behind the transparent parts of the overlay is not in it and no other window can be. - Rows are flipped and the colour is un-premultiplied on the way out: ImGui blends onto a transparent clear, which is right for a compositor and a shade too dark for every PNG reader. ADDED: A masked frame between the press and the dialog, which is the frame that gets photographed: every seller's account name is replaced by its position and no button shows its tooltip. - Masking in place would work only for as long as the action bar keeps being drawn before the results table, and that failure would be silent and would consist of publishing somebody's name. ADDED: `util/png`, an encoder with one `IDAT`, no interlacing and no ancillary chunks at all — no timestamp, no text, no gamma, which on a picture leaving a user's machine is a property rather than a shortcut. Every row is filtered Sub, which is what fits a panel under the relay's cap. ADDED: `ReportService`, off the UI thread and deliberately not behind `trade::request` — the shared limiter is GGG's policy and nothing else belongs there. ADDED: A monospace face chosen at startup, for the two payload previews. A capture is read down the line and Fontin has no figure alignment at all. ADDED: Three Font Awesome glyphs, and `report_test.cpp`. CHANGED: Search and Open in browser are square glyph buttons against the panel's right edge instead of two words, so the row is one toolbar and fits beside the listings. CHANGED: `PRIVACY.md` no longer says there is no server on the other end — there is now exactly one, reached only by a press, and a section says what a report carries and what it cannot. CHANGED: zlib becomes `ppc_core`'s one link dependency besides nlohmann/json, and a CI package. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release notes
ADDED: Report a Bug on the price-check panel, for a check that read an item wrong — an
unmatched modifier, the wrong base, a search that asked for the wrong thing.
game copied, everything this tool made of it, the version line, a box to describe the problem,
and a screenshot you can attach or not. What is on that screen is what is sent, and nothing is
sent until you press Send.
kept on disk afterwards. Reports go to a small relay of the project's own, described in full in
PRIVACY.md.
not in it and cannot be, and every seller's name in a search result is replaced by its position
before the picture is taken. The box starts unticked either way.
you typed is lost.
CHANGED: Search and Open in browser are now icons at the right of the panel rather than two
labelled buttons, which is what makes room for the third.
Review notes
ADDED: A Report a Bug button on the price-check panel, opening a dialog that shows the whole
payload — the clipboard capture, what this tool made of it, the build line and the screenshot —
before anything is sent.
report::to_jsonwrites five keys and a test asserts the count.
land while the user is still reading, and a preview a frame behind what will be sent says
nothing.
ADDED:
report::describe, the parse dump a mispricing is actually diagnosed from: what was readoff the clipboard, what it resolved to in the bundle, which modifier matched which stat record —
and, the line that earns the rest, which matched none.
ADDED: An optional screenshot, read back from our own framebuffer with
glReadPixelsbetween thedraw and the swap.
parts of the overlay is not in it and no other window can be.
transparent clear, which is right for a compositor and a shade too dark for every PNG reader.
ADDED: A masked frame between the press and the dialog, which is the frame that gets photographed:
every seller's account name is replaced by its position and no button shows its tooltip.
results table, and that failure would be silent and would consist of publishing somebody's name.
ADDED:
util/png, an encoder with oneIDAT, no interlacing and no ancillary chunks at all — notimestamp, no text, no gamma. Every row is filtered Sub, which is what fits a 900×1080 panel into
~190 KB against the relay's 5 MB cap.
ADDED:
ReportService, off the UI thread and deliberately not behindtrade::request— theshared limiter is GGG's policy and nothing else belongs there. The screenshot reaches the worker
as raw pixels, because encoding and serialising it is most of a second.
ADDED: A monospace face chosen at startup, for the two payload previews. A capture is read down
the line and Fontin has no figure alignment at all.
ADDED: Three Font Awesome glyphs,
docs/reporting.md, andtests/report_test.cpp.ADDED:
worker/, the Cloudflare Worker the report is posted to, with its deploy scripts and 22contract tests of its own — every field fenced, mentions disabled, bidi overrides stripped, only
known fields read, and a screenshot that must decode to a real PNG.
Cloudflare secret, so reading the URL out of the binary yields a rate-limited endpoint.
is Discord's own open/resolved state rather than a convention.
CHANGED: Search and Open in browser are square glyph buttons against the panel's right edge
instead of two words, so the row is one toolbar and fits beside the listings.
CHANGED:
PRIVACY.mdno longer says there is no server on the other end — there is now exactlyone, reached only by a press, and a section says what a report carries and what it cannot.
CHANGED: zlib becomes
ppc_core's one link dependency besides nlohmann/json, and a CI package.🤖 Generated with Claude Code