The template notes a seat's address at most once in ten minutes, so a seat open on two networks writes no line per request

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-11-10 21:32:27 -05:00
1 parent 1a46d611a0
commit 210db2d5c3
2 files changed
+13 -2

No files matched your search

+11 -2
View File
@@ -36,6 +36,8 @@ export interface Seat {
/** A table whose ledger has not been written in this long is not looked through for a lost seat. */
const LOST_SEAT_LOOKBACK_MS = 90 * 24 * 60 * 60 * 1000;
/** A seat's address is noted at most this often: one seat open on two networks at once must not write a line per request. */
const SEEN_EVERY_MS = 10 * 60 * 1000;
function hashToken(token: string): string {
return createHash('sha256').update(token).digest('hex');
@@ -323,7 +325,10 @@ export class Rooms<State, Input> {
return { phrase, expiresAt };
}
/** The address a seat is used from, noted when it changes. Not a change the table need hear of: nothing wakes for it. */
/** When each seat's address was last noted, by room and seat. */
private seenAt = new Map<string, number>();
/** The address a seat is used from, noted when it changes, and not more often than SEEN_EVERY_MS. Not a change the table need hear of: nothing wakes for it. */
seen(room: Room<State>, token: string | undefined, addr: string): void {
let seat: Seat;
try {
@@ -332,7 +337,11 @@ export class Rooms<State, Input> {
return;
}
if (seat.addr === addr) return;
const line: LedgerLine = { t: 'seen', at: Date.now(), id: seat.id, addr };
const key = `${room.id}/${seat.id}`;
const now = Date.now();
if ((this.seenAt.get(key) ?? 0) > now - SEEN_EVERY_MS) return;
this.seenAt.set(key, now);
const line: LedgerLine = { t: 'seen', at: now, id: seat.id, addr };
this.store.append(room.id, line);
this.apply(room, line);
}