From c9cc8ead1ab64972a60c66340a71f8d37f3cfe9b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 02:54:19 -0500 Subject: [PATCH] =?UTF-8?q?feat(pages):=20help-page=20(support)=20queue=20?= =?UTF-8?q?=E2=80=94=20stream,=20snapshot,=20respond,=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 of docs/ADMIN_CONTROLS.md: surface the in-game help-page queue to the website. - BridgePages.cs: the queue has no EventSink, so it is polled (PageSweepSeconds, default 5s) and diffed, keyed by sender serial (one page per player) -> page.new / page.updated / page.closed. Inbound pages.snapshot -> pages.list; page.respond delivers a staff reply to the player (online: a gump now; offline: queued for next login; shows as "Staff") and can close; page.close removes it. - BridgeConfig/Bridge.cfg: PageSweepSeconds. BridgeBoot: reload re-arms the poll, status reports it. - sidecar/src/web.rs: GET /pages, POST /pages/{id}/respond, POST /pages/{id}/close. - INTEGRATION.md: page events (§4) and endpoints (§6). - tools/scaffolding/BridgePageProbe.cs: gated headless verification. Verified live (probe-seeded tickets): snapshot returns the queue, the poll emits page.new for both and page.closed on removal, respond -> 200, close removes the page, unknown page -> 404. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ --- link/ADMIN_CONTROLS.md | 4 +++- link/INTEGRATION.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/link/ADMIN_CONTROLS.md b/link/ADMIN_CONTROLS.md index 30af617..08345e0 100644 --- a/link/ADMIN_CONTROLS.md +++ b/link/ADMIN_CONTROLS.md @@ -140,7 +140,9 @@ It turns "a staff member must be logged into the game to see the queue" into "th > - *Docs*: `INTEGRATION.md` §6 documents the endpoints and the `admin.audit` event. > - *Bidirectional audit* (§5.5): **built and live-verified.** `patches/commandlogging-event.patch` (adds `CommandLogging.OnWrite`) + `patches/BridgeModerationAudit.cs` (the subscriber) forward in-game bans/kicks/broadcasts to the site as `admin.audit` (`origin:"in-game"`). A boot-time probe confirmed a genuine `[bcast` and resolved ban/kick lines produce the right frames with the target parsed, non-moderation lines ignored. > -> **Phase 1 + the bidirectional-audit slice are complete.** Remaining is downstream (website UI + moderation log) and the later Phase 2 (help-page queue) / Phase 3 work. +> **Phase 2 — help-page queue: built and live-verified.** `BridgePages.cs` polls the queue (`PageSweepSeconds`, default 5s) → `page.new`/`page.updated`/`page.closed`; inbound `pages.snapshot`/`page.respond`/`page.close`; sidecar `GET /pages` + `POST /pages/{id}/respond|close`; `INTEGRATION.md` §4/§6 documented. A live run (probe-seeded tickets) confirmed snapshot, both `page.new` emits, respond, close (→ page removed), `page.closed` emit, and 404 on an unknown page. +> +> **Phase 1 + bidirectional audit + Phase 2 are complete.** Remaining: downstream website UI (moderation log + support-queue view), then Phase 3 (mute/notes/teleport/save). **Wire in, in order:** diff --git a/link/INTEGRATION.md b/link/INTEGRATION.md index 17d46db..3a37309 100644 --- a/link/INTEGRATION.md +++ b/link/INTEGRATION.md @@ -185,6 +185,15 @@ Every event has `t` (epoch ms) and `kind`. A nested actor object looks like `{"s |------|--------|-------| | `link.request` | `code`, `account`, `char`, `ttlSec` | A player ran `[link` in game. Show them a prompt to enter `code` on the site; you then confirm it via `POST /link/confirm`. See §6. | +#### Help-page (support) queue +| kind | fields | notes | +|------|--------|-------| +| `page.new` | `pageId`, `sender`, `type`, `message`, `map`, `x`,`y`,`z`, `sentMs`, `handled`, `handler` | A player opened a help page (support ticket). `pageId` is the sender's serial (one page per player). `type` is `Bug`/`Stuck`/`Account`/`Question`/`Suggestion`/`Other`/`VerbalHarassment`/`PhysicalHarassment`. `sender` is the usual actor object (with `webId` if the account is linked). | +| `page.updated` | same as `page.new` | A page's handled state changed (a staffer claimed/released it in game). | +| `page.closed` | `pageId` | The page left the queue (resolved, cancelled, or the player logged out). | + +The queue has no in-game event, so it's polled (`PageSweepSeconds`, default 5s) — expect a few seconds' latency, and use `GET /pages` for the authoritative current queue on connect. See §6 to snapshot, respond, and close. + --- ## 5. REST — read queries @@ -351,6 +360,31 @@ Each applied action also emits an unsolicited **`admin.audit`** frame on the Web `origin:"web"`, so every connected dashboard — not just the caller — sees it. In-game moderation by staff in the game client surfaces the same way with `origin:"in-game"`. +### Help-page (support) queue + +Read the open queue, respond to a player, or close a page. Staff-facing — gate behind your own +roles, like the moderation endpoints above. + +``` +GET /pages # the open queue, newest state +POST /pages/{pageId}/respond { "message":"...", "close": false } +POST /pages/{pageId}/close +``` + +- **GET /pages** → `pages.list` with a `pages` array; each entry is the same shape as a `page.new` + event's fields (§4). This is the authoritative queue — use it on (re)connect, then keep it live + with the `page.new` / `page.updated` / `page.closed` events. +- **respond** delivers a message to the player exactly as an in-game staff reply does: a gump now if + they're online, otherwise queued for their next login. It shows as coming from "Staff". Pass + `"close": true` to resolve the page in the same call. → **200** `page.ok`. +- **close** removes the page from the queue. → **200** `page.ok`. +- Unknown `pageId` → **404** `page.error`; a respond with no `message` → **400**. + +```json +POST /pages/0x24C/respond { "message": "A GM is on the way.", "close": true } +→ { "kind":"page.ok", "action":"respond", "pageId":"0x24C", "closed":true } +``` + ### History (from the sidecar's database) ```