The visitors digest: who is here, who came today, how far they got
/usr/local/bin/wizwar-visitors.sh prints one screen — live sockets and rooms touched in the hour, today's traffic row, the names seated today with the first-time ones marked, every room opened today with its state, move counts, span, and last human move, humans' chat verbatim, and the desk's count — and the wizwar-visitors skill reads it aloud. It replaces the four hand-written versions of the same report from announcement day. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
9d2c66f0bd
commit
2b5c2c4884
Executable
+92
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
# The visitors digest: who is here right now and who came today — the
|
||||
# rooms opened, how far each got, what humans said, what they filed —
|
||||
# with today's traffic so far. Read by the wizwar-visitors skill.
|
||||
# wizwar-visitors.sh [YYYY-MM-DD] (default: today, UTC)
|
||||
set -u
|
||||
DAY="${1:-$(date -u +%F)}"
|
||||
/usr/local/bin/wizwar-rollup.sh "$DAY" >/dev/null 2>&1
|
||||
python3 - "$DAY" <<'PY'
|
||||
import json, sys, glob, os, datetime, subprocess
|
||||
day = sys.argv[1]
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
print("as of %s UTC, day %s" % (now.strftime("%H:%M"), day))
|
||||
|
||||
def sh(cmd):
|
||||
try: return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=20).stdout.strip()
|
||||
except Exception: return "?"
|
||||
print("live sockets: %s | rooms touched in the last hour: %s" % (
|
||||
sh("ss -tn state established '( sport = :8787 )' | tail -n +2 | wc -l"),
|
||||
sh("find /var/lib/wizwar/rooms -name '*.jsonl' -mmin -60 | wc -l")))
|
||||
|
||||
# --- traffic so far, from the rollup ---
|
||||
try:
|
||||
rows = [json.loads(l) for l in open("/var/lib/wizwar/rollup.jsonl")]
|
||||
r = [x for x in rows if x["day"] == day][-1]
|
||||
p = r["paths"]
|
||||
print("traffic: %d addresses, %d requests from people (%d bot); front %d, gallery %d, replays %d; campaigns %s; referrers %s" % (
|
||||
r["humanAddresses"], r["human"], r["bot"], p.get("/", 0), p.get("/clips", 0), p.get("/watch", 0),
|
||||
json.dumps(r["campaigns"]), json.dumps(r["referrers"])))
|
||||
except Exception as e:
|
||||
print("traffic: (no rollup row: %s)" % e)
|
||||
|
||||
# --- every room, for first sightings; today's rooms in detail ---
|
||||
first_seen = {}
|
||||
rooms = []
|
||||
for f in glob.glob("/var/lib/wizwar/rooms/*.jsonl"):
|
||||
try:
|
||||
lines = open(f).read().splitlines()
|
||||
meta = json.loads(lines[0])
|
||||
except Exception: continue
|
||||
ca = meta.get("createdAt", "")
|
||||
if not ca: continue
|
||||
humans = [meta["hostId"]]; bots = []; started = False; cmds = 0; chat = []; last = ca; steps = []
|
||||
for l in lines[1:]:
|
||||
try: x = json.loads(l)
|
||||
except Exception: continue
|
||||
k = x.get("kind")
|
||||
if k == "join":
|
||||
(bots if x.get("bot") else humans).append(x["name"])
|
||||
elif k == "start": started = True
|
||||
elif k == "command":
|
||||
cmds += 1; last = x.get("at", last)
|
||||
steps.append((x.get("playerId"), x["command"].get("type"), x.get("at", "")))
|
||||
elif k == "chat":
|
||||
last = x.get("at", last)
|
||||
if x["player"] in humans: chat.append((x.get("at", "")[11:16], x["player"], x["text"]))
|
||||
for h in humans:
|
||||
if h not in first_seen or ca < first_seen[h]: first_seen[h] = ca
|
||||
rooms.append(dict(id=meta["id"], created=ca, humans=humans, bots=bots, started=started, cmds=cmds, last=last, chat=chat, steps=steps))
|
||||
|
||||
today = sorted([r for r in rooms if r["created"].startswith(day)], key=lambda r: r["created"])
|
||||
names = sorted({h for r in today for h in r["humans"]})
|
||||
new = [n for n in names if first_seen[n].startswith(day)]
|
||||
print("players today: %s | NEW today: %s" % (", ".join(names) or "none", ", ".join(new) or "none"))
|
||||
print("rooms today: %d" % len(today))
|
||||
for r in today:
|
||||
span = ""
|
||||
if r["cmds"]:
|
||||
t0 = datetime.datetime.fromisoformat(r["created"].replace("Z", "+00:00")); t1 = datetime.datetime.fromisoformat(r["last"].replace("Z", "+00:00"))
|
||||
span = "%d min" % round((t1 - t0).total_seconds() / 60)
|
||||
human_cmds = [s for s in r["steps"] if s[0] in r["humans"]]
|
||||
last_human = ("last human move: %s %s at %s" % (human_cmds[-1][0], human_cmds[-1][1], human_cmds[-1][2][11:16])) if human_cmds else "no human move"
|
||||
state = "lobby only" if not r["started"] else ("started, no moves" if not r["cmds"] else "in play")
|
||||
print(" %s %s %s%s %s %d moves (%d human) %s %s" % (
|
||||
r["created"][11:16], r["id"], "+".join(r["humans"]), (" vs %d bot%s" % (len(r["bots"]), "" if len(r["bots"]) == 1 else "s")) if r["bots"] else "",
|
||||
state, r["cmds"], len(human_cmds), span, last_human))
|
||||
for at, who, text in r["chat"]: print(" %s %s: %s" % (at, who, text))
|
||||
|
||||
# --- the desk ---
|
||||
reports = 0; unanswered = 0; answered = set()
|
||||
try:
|
||||
rows = [json.loads(l) for l in open("/var/lib/wizwar/feedback.jsonl") if l.strip()]
|
||||
answered = {x["reportId"] for x in rows if "reportId" in x}
|
||||
for x in rows:
|
||||
if "happened" not in x: continue
|
||||
if (x.get("id") or x["at"]) not in answered: unanswered += 1
|
||||
if x["at"].startswith(day):
|
||||
reports += 1
|
||||
print(" REPORT %s %s (%s): %s / expected: %s" % (x["at"][11:16], x["player"], x["roomId"], x["happened"], x["expected"]))
|
||||
except OSError: pass
|
||||
print("feedback: %d filed today, %d unanswered overall" % (reports, unanswered))
|
||||
PY
|
||||
Reference in New Issue
Block a user