#!/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, 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