A seat lived in the browser's storage alone, and Safari clears that after a week away, or at a word from the person on another device. Now every seat taken is banked as well in an HttpOnly cookie the server writes, which such clearing leaves alone, and the wallet takes its seats back from the bank on arrival. For a browser that lost even the cookie, the ledger notes the address each seat was last used from, as a salted hash, at most once in ten minutes, and a name given in the lobby or from the gallery gets its seats back from the same address, with a new token; where the address differs, two people at the table share it, or the host gave the seat to the clockwork, the table is only counted, and the host's phrase or the keeper's link is the way back. A name refused as taken is looked for the same way before the refusal shows. The keeper's script finds a name across tables. The seat endpoints answer only this site's own pages, under a limit of their own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
47 lines
2.5 KiB
Bash
Executable File
47 lines
2.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# A way back for a player whose browser lost their seat: the seat gets a new
|
|
# token, the old one stops working, and the link printed carries the new one.
|
|
# Whether the one asking holds that seat is the keeper's call; send the link
|
|
# only to them. Runs ON the droplet, through the game server's keeper door,
|
|
# which listens on the loopback alone.
|
|
# wizwar-reseat.sh find NAME every table not finished where a wizard of that name sits, for one who lost the codes too
|
|
# wizwar-reseat.sh ROOM who sits at the table, and when each person last moved or spoke
|
|
# wizwar-reseat.sh ROOM NAME re-seat the wizard NAME and print the link
|
|
set -euo pipefail
|
|
ROOM="${1:?usage: wizwar-reseat.sh find NAME | ROOM [NAME]}"
|
|
DOOR="http://127.0.0.1:${KEEPER_PORT:-9787}/keeper/rooms/${ROOM^^}"
|
|
KEY_FILE="${KEEPER_KEY_FILE:-/etc/wizwar/keeper.key}"
|
|
[ -s "$KEY_FILE" ] || { echo "No keeper's key at $KEY_FILE; a deploy makes it." >&2; exit 1; }
|
|
# The key goes as a header read from a file descriptor, never on a command line another user could see.
|
|
KEY="@/dev/fd/3"
|
|
exec 3< <(printf 'x-keeper-key: %s\n' "$(cat "$KEY_FILE")")
|
|
if [ "$ROOM" = find ]; then
|
|
curl -s -H "$KEY" --get --data-urlencode "name=${2:?a name to look for}" "http://127.0.0.1:${KEEPER_PORT:-9787}/keeper/seats" | python3 -c '
|
|
import json, sys
|
|
r = json.load(sys.stdin)
|
|
if "error" in r: sys.exit(r["error"])
|
|
if not r["tables"]: print("no table still in play has a seat under that name")
|
|
for t in r["tables"]:
|
|
print(" %s %s %s with %s, last moved %s" % (t["roomId"], t["name"], "(the clockwork stands in)" if t["standIn"] else "", ", ".join(t["players"]) or "nobody yet", t["lastMoveAt"] or "never"))
|
|
'
|
|
elif [ $# -lt 2 ]; then
|
|
curl -s -H "$KEY" "$DOOR" | python3 -c '
|
|
import json, sys
|
|
r = json.load(sys.stdin)
|
|
if "error" in r: sys.exit(r["error"])
|
|
print("room %s, %s" % (r["roomId"], "finished" if r["finished"] else "in play" if r["started"] else "not begun"))
|
|
for s in r["seats"]:
|
|
when = "automaton" if s["bot"] else "never moved or spoke" if s["lastActed"] is None else "last acted " + s["lastActed"][:16].replace("T", " ") + " UTC"
|
|
print(" %-24s %-5s %s" % (s["name"], "host" if s["host"] else "", when))
|
|
'
|
|
else
|
|
python3 -c 'import json, sys; print(json.dumps({"seat": sys.argv[1]}))' "$2" \
|
|
| curl -s -X POST -H "$KEY" -H 'content-type: application/json' --data-binary @- "$DOOR/reseat" | python3 -c '
|
|
import json, sys
|
|
r = json.load(sys.stdin)
|
|
if "error" in r: sys.exit(r["error"])
|
|
print("%s has a new token; their old one opens nothing now. Send them this link:" % r["name"])
|
|
print(r["link"])
|
|
'
|
|
fi
|