A player answers the desk under its reply from their lobby, proven by the seat the report was filed from; the lobby shows the exchange in order and says whose word is pending. A new report, a player's answer, and a desk reply each raise a Sentry issue on the keeper's phone — the first two from the server, the last from the reply script itself, straight to Sentry from the droplet. The Desktop digest lists the answers, and the desk's rule reads a report as reopened when the last word is the player's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
37 lines
2.4 KiB
Bash
Executable File
37 lines
2.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# Answer a player's feedback report; the reply appears under it in their
|
|
# lobby ledger. Legacy reports without an id answer to their timestamp.
|
|
# deploy/feedback-reply.sh <host> <reportId> <resolved|by-design|open> <text...>
|
|
set -euo pipefail
|
|
HOST="${1:?usage: feedback-reply.sh <host> <reportId> <status> <text...>}"
|
|
REPORT_ID="${2:?usage: feedback-reply.sh <host> <reportId> <status> <text...>}"
|
|
STATUS="${3:?usage: feedback-reply.sh <host> <reportId> <status> <text...>}"
|
|
shift 3
|
|
TEXT="$*"
|
|
case "$STATUS" in resolved|by-design|open) ;; *) echo "status must be resolved, by-design, or open" >&2; exit 1;; esac
|
|
LINE=$(REPORT_ID="$REPORT_ID" STATUS="$STATUS" TEXT="$TEXT" node -e '
|
|
console.log(JSON.stringify({
|
|
reportId: process.env.REPORT_ID,
|
|
at: new Date().toISOString(),
|
|
status: process.env.STATUS,
|
|
text: process.env.TEXT,
|
|
}));')
|
|
printf '%s\n' "$LINE" | ssh "root@$HOST" 'cat >> /var/lib/wizwar/feedback.jsonl && tail -1 /var/lib/wizwar/feedback.jsonl'
|
|
# The desk's bell rings for the reply too, so Eric sees the exchange move:
|
|
# an event straight to Sentry from the droplet, with the DSN the service holds.
|
|
ssh "root@$HOST" "REPORT_ID='$REPORT_ID' STATUS='$STATUS' TEXT=\"\$(cat)\" python3 - <<'PY'
|
|
import json, os, re, uuid, urllib.request, datetime
|
|
svc = open('/etc/systemd/system/wizwar.service').read()
|
|
m = re.search(r'SENTRY_DSN=https://([0-9a-f]+)@([^/]+)/(\\d+)', svc)
|
|
if not m: raise SystemExit(0)
|
|
key, host, project = m.groups()
|
|
text = os.environ['TEXT']; rid = os.environ['REPORT_ID']; status = os.environ['STATUS']
|
|
event = {'event_id': uuid.uuid4().hex, 'timestamp': datetime.datetime.now(datetime.UTC).isoformat(), 'level': 'error', 'platform': 'other',
|
|
'message': {'formatted': f'Desk replies on report {rid} ({status}): {text[:100]}'}, 'fingerprint': ['report-reply', rid, datetime.datetime.now(datetime.UTC).isoformat()],
|
|
'tags': {'report': rid, 'status': status}, 'extra': {'text': text}}
|
|
body = json.dumps({'event_id': event['event_id'], 'sent_at': event['timestamp']}) + '\\n' + json.dumps({'type': 'event'}) + '\\n' + json.dumps(event)
|
|
req = urllib.request.Request(f'https://{host}/api/{project}/envelope/', data=body.encode(), headers={'Content-Type': 'application/x-sentry-envelope', 'X-Sentry-Auth': f'Sentry sentry_version=7, sentry_client=wizwar-desk/1, sentry_key={key}'})
|
|
try: urllib.request.urlopen(req, timeout=10); print('bell rung')
|
|
except Exception as e: print('bell failed:', e)
|
|
PY" <<< "$TEXT"
|