Mid-game invite links and a surprise-report slip
The masthead's room code is now a button: one click copies the /join link, which seats newcomers in a waiting room and the gallery once the boards flip. And a 🐞 report opens a two-question slip — what happened, what was expected — that lands in feedback.jsonl beside the rooms with the room code, move number, round, and rules rev attached, so any surprise can be replayed to its exact moment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
a1fdb37c54
commit
553754a596
@@ -9,6 +9,7 @@
|
||||
// {type:"claimTransfer", code} claim a seat on a new device
|
||||
// {type:"catchUp", sinceSeq} replay of moves missed while away
|
||||
// {type:"chat", text} table talk to the room
|
||||
// {type:"feedback", happened, expected} a surprise report, pinned to room+seq
|
||||
// {type:"rollDie"} the tabletop D4, published as talk
|
||||
// {type:"addBot", style?, tier?} host seats an automaton
|
||||
// {type:"watch", roomId} join the Peanut Gallery: nameless, read-only
|
||||
@@ -25,7 +26,7 @@
|
||||
// {type:"chat", player, text, at} one line of table talk
|
||||
// {type:"watching", roomId} you are seated in the gallery
|
||||
// {type:"audience", count} how many watch from the gallery
|
||||
// {type:"transferCode"|"transferClaimed"|"catchUp"|"games"|"stats"}
|
||||
// {type:"transferCode"|"transferClaimed"|"catchUp"|"games"|"stats"|"feedbackReceived"}
|
||||
// {type:"error", message}
|
||||
|
||||
import { createServer } from "node:http";
|
||||
@@ -62,6 +63,7 @@ import {
|
||||
abandonRoom,
|
||||
} from "./rooms";
|
||||
import { engagementStats, recordHotseat } from "./stats";
|
||||
import { appendFeedback } from "./store";
|
||||
import { getShare, loadShares, mintShare } from "./shares";
|
||||
import { renderSharePng } from "./ogimage";
|
||||
import { BOT_LINES, type BanterTrigger } from "./banter";
|
||||
@@ -796,6 +798,26 @@ wss.on("connection", (socket) => {
|
||||
broadcastRoomState(room);
|
||||
break;
|
||||
}
|
||||
case "feedback": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room) return send(socket, { type: "error", message: "join a room first — a report rides its ledger" });
|
||||
const clean = (raw: unknown) =>
|
||||
String(raw ?? "").replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g, " ").trim().slice(0, 2000);
|
||||
const happened = clean(msg.happened);
|
||||
if (!happened) return send(socket, { type: "error", message: "say what happened" });
|
||||
appendFeedback({
|
||||
at: new Date().toISOString(),
|
||||
roomId: room.id,
|
||||
player: session.playerId ?? "(gallery)",
|
||||
seq: room.log.length,
|
||||
round: room.state?.turn.round ?? null,
|
||||
deckRev: room.state?.config.deckRev ?? null,
|
||||
happened,
|
||||
expected: clean(msg.expected),
|
||||
});
|
||||
send(socket, { type: "feedbackReceived" });
|
||||
break;
|
||||
}
|
||||
case "hotseatReport": {
|
||||
// A device finishes a handful of games at most; a firehose is abuse.
|
||||
if (++session.hotseatReports > 20) return;
|
||||
|
||||
@@ -148,6 +148,13 @@ export function readAllRooms(): Map<string, RoomLine[]> {
|
||||
return rooms;
|
||||
}
|
||||
|
||||
/** Player surprise reports, one JSONL line each, beside the rooms
|
||||
* directory — the room id and seq pin the exact moment to replay. */
|
||||
export function appendFeedback(entry: Record<string, unknown>): void {
|
||||
ensureDataDir();
|
||||
appendFileSync(join(DATA_DIR, "..", "feedback.jsonl"), JSON.stringify(entry) + "\n", "utf8");
|
||||
}
|
||||
|
||||
/** Retire an abandoned room's ledger to the graveyard — never deleted,
|
||||
* only moved out of the living rooms directory. */
|
||||
export function archiveRoomFile(roomId: string): void {
|
||||
|
||||
Reference in New Issue
Block a user