fuzzyworld

dm / commit

feat: send player and game IDs as API metadata for analytics

343826a9d262d4d5f7aafa90b1e75041821a6d01 · Pavel Safronov · 2026-05-27 17:51:28 -0700 · parents 6b2c8b7f9f64

feat: send player and game IDs as API metadata for analytics

Generates a persistent player ID (stored in localStorage) and a
per-adventure game ID (stored in gameState). Both are sent as
metadata.user_id on every API call in the format p_<id>:g_<id>,
which appears in the Anthropic usage dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
 public/index.html | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/public/index.html b/public/index.html
index 910edca..94e0a64 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1281,6 +1281,21 @@ let gameState = null;
 let conversationHistory = [];
 let isBusy = false;
 
+// ── IDs ───────────────────────────────────────────────
+// player_id persists across games; game_id is fresh each adventure.
+// Both are sent as metadata.user_id so they appear in the Anthropic
+// usage dashboard for usage analytics.
+
+function randomId() {
+  return Array.from(crypto.getRandomValues(new Uint8Array(8)), b => b.toString(16).padStart(2, '0')).join('');
+}
+
+function getOrCreatePlayerId() {
+  let id = localStorage.getItem('dm_player_id');
+  if (!id) { id = randomId(); localStorage.setItem('dm_player_id', id); }
+  return id;
+}
+
 // Pricing in USD per 1M tokens — verify at anthropic.com/pricing
 const MODEL_PRICING = {
   'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },
@@ -1505,6 +1520,7 @@ async function beginAdventure() {
 
   gameState = {
     apiKey,
+    gameId: randomId(),
     character: {
       name,
       race: selectedRace.name,
@@ -1804,6 +1820,7 @@ async function sendMessage(text, showAsPlayer = true) {
         system: buildSystemPrompt(gameState.character, gameState.theme),
         tools: [UPDATE_TOOL],
         messages: apiMessages,
+        metadata: { user_id: `p_${getOrCreatePlayerId()}:g_${gameState.gameId}` },
       };
 
       console.group(`🎲 DM API call #${loopIteration} (turn ${conversationHistory.length})`);