docs: add game-status-by-session guide and external downstream clients reference

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-05-07 20:43:10 -04:00
parent 195448644a
commit 1c9f0ef280
3 changed files with 272 additions and 1 deletions

View File

@@ -179,4 +179,4 @@ Most list endpoints return full result sets. The exception is `GET /api/votes`,
- [OpenAPI Spec](openapi.yaml) - [OpenAPI Spec](openapi.yaml)
- **Endpoint docs**: [Auth](endpoints/auth.md), [Games](endpoints/games.md), [Sessions](endpoints/sessions.md), [Picker](endpoints/picker.md), [Stats](endpoints/stats.md), [Votes](endpoints/votes.md), [Webhooks](endpoints/webhooks.md) - **Endpoint docs**: [Auth](endpoints/auth.md), [Games](endpoints/games.md), [Sessions](endpoints/sessions.md), [Picker](endpoints/picker.md), [Stats](endpoints/stats.md), [Votes](endpoints/votes.md), [Webhooks](endpoints/webhooks.md)
- [WebSocket Protocol](websocket.md) - [WebSocket Protocol](websocket.md)
- **Guides**: [Getting Started](guides/getting-started.md), [Session Lifecycle](guides/session-lifecycle.md), [Voting & Popularity](guides/voting-and-popularity.md), [Webhooks & Events](guides/webhooks-and-events.md) - **Guides**: [Getting Started](guides/getting-started.md), [Session Lifecycle](guides/session-lifecycle.md), [Game Status by Session](guides/game-status-by-session.md), [Voting & Popularity](guides/voting-and-popularity.md), [Webhooks & Events](guides/webhooks-and-events.md)

View File

@@ -0,0 +1,187 @@
# Game Status by Session
How to build a complete view of all enabled games—with popularity data and whether each game has been played in the current session. No single endpoint returns all three pieces today; this guide shows how to combine two public endpoints and join the results client-side.
---
## 1. Fetch Enabled Games
`GET /api/games?enabled=true` returns every enabled game in the catalog. Each row includes `popularity_score`, `upvotes`, `downvotes`, `play_count`, and `favor_bias`.
**Why:** This gives you the full list of games that are available for play, along with their cumulative popularity data across all sessions.
See [Games endpoints](../endpoints/games.md) for all available filters (`playerCount`, `drawing`, `length`, `familyFriendly`, `pack`).
```bash
curl "http://localhost:5000/api/games?enabled=true"
```
**Sample response (200 OK):**
```json
[
{
"id": 1,
"pack_name": "Jackbox Party Pack 7",
"title": "Quiplash 3",
"min_players": 3,
"max_players": 8,
"length_minutes": 15,
"has_audience": 1,
"family_friendly": 0,
"game_type": "Writing",
"secondary_type": null,
"play_count": 4,
"popularity_score": 7,
"upvotes": 10,
"downvotes": 3,
"enabled": 1,
"favor_bias": 0,
"created_at": "2024-01-15T12:00:00.000Z"
},
{
"id": 5,
"pack_name": "Jackbox Party Pack 9",
"title": "Fibbage 4",
"min_players": 2,
"max_players": 8,
"length_minutes": 15,
"has_audience": 1,
"family_friendly": 1,
"game_type": "Trivia",
"secondary_type": null,
"play_count": 2,
"popularity_score": 3,
"upvotes": 5,
"downvotes": 2,
"enabled": 1,
"favor_bias": 1,
"created_at": "2024-01-15T12:00:00.000Z"
}
]
```
---
## 2. Fetch Session Games
`GET /api/sessions/{id}/games` returns games that have been added to a specific session. Each row includes `status` (`playing`, `played`, or `skipped`), `played_at`, and joined popularity fields from the game catalog.
**Why:** This tells you which games have already been played (or are currently playing) in the session, so you can mark them in the catalog.
See [Sessions endpoints](../endpoints/sessions.md) for full details on session game fields.
```bash
curl "http://localhost:5000/api/sessions/5/games"
```
**Sample response (200 OK):**
```json
[
{
"id": 14,
"session_id": 5,
"game_id": 1,
"played_at": "2026-03-15T20:30:00.000Z",
"manually_added": 0,
"status": "played",
"room_code": "LSBN",
"player_count": 6,
"player_count_check_status": "completed",
"pack_name": "Jackbox Party Pack 7",
"title": "Quiplash 3",
"game_type": "Writing",
"min_players": 3,
"max_players": 8,
"popularity_score": 7,
"upvotes": 10,
"downvotes": 3
}
]
```
Key fields for the join:
| Field | Purpose |
|-------|---------|
| `game_id` | Matches `id` in the games catalog |
| `status` | `playing`, `played`, or `skipped` |
| `played_at` | When the game was added to the session |
---
## 3. Combine Client-Side
Join the two responses by matching `game_id` (from session games) to `id` (from the catalog). This produces a single list of enabled games annotated with their session play status.
```javascript
const gamesRes = await fetch('/api/games?enabled=true');
const games = await gamesRes.json();
const sessionGamesRes = await fetch('/api/sessions/5/games');
const sessionGames = await sessionGamesRes.json();
const sessionGameMap = new Map(
sessionGames.map(sg => [sg.game_id, sg])
);
const combined = games.map(game => {
const sessionEntry = sessionGameMap.get(game.id);
return {
...game,
playedInSession: !!sessionEntry,
sessionStatus: sessionEntry?.status ?? null,
sessionPlayedAt: sessionEntry?.played_at ?? null,
};
});
```
**Sample merged output (one entry):**
```json
{
"id": 1,
"pack_name": "Jackbox Party Pack 7",
"title": "Quiplash 3",
"min_players": 3,
"max_players": 8,
"length_minutes": 15,
"has_audience": 1,
"family_friendly": 0,
"game_type": "Writing",
"play_count": 4,
"popularity_score": 7,
"upvotes": 10,
"downvotes": 3,
"enabled": 1,
"favor_bias": 0,
"playedInSession": true,
"sessionStatus": "played",
"sessionPlayedAt": "2026-03-15T20:30:00.000Z"
}
```
Games that have not been played in the session will have `playedInSession: false`, `sessionStatus: null`, and `sessionPlayedAt: null`.
---
## Quick Reference
| Data point | `GET /api/games` | `GET /api/sessions/{id}/games` |
|------------|------------------|-------------------------------|
| Enabled status | `enabled` field + `?enabled=true` filter | Not included |
| Popularity score | `popularity_score`, `upvotes`, `downvotes` | `popularity_score`, `upvotes`, `downvotes` (joined) |
| All-time play count | `play_count` | Not included |
| Played in session | Not included | `status` field (`playing`/`played`/`skipped`) |
| Auth required | No | No |
---
## Related Documentation
- [Games endpoints](../endpoints/games.md) — full catalog CRUD, filters, favor bias
- [Sessions endpoints](../endpoints/sessions.md) — session games, status updates, room codes
- [Picker endpoint](../endpoints/picker.md) — weighted random selection with session exclusion
- [Stats endpoint](../endpoints/stats.md) — `mostPlayedGames`, `topRatedGames`
- [Voting & Popularity guide](voting-and-popularity.md) — how votes and popularity scores work

View File

@@ -0,0 +1,84 @@
# Manual Poll Start — Upstream Integration Guide
## Overview
The vote-app now supports a `poll.start` WebSocket message that explicitly triggers poll generation. This replaces the previous behavior where polls were always auto-generated on `game.ended`.
## Poll Modes
The vote-app has two poll modes:
| Mode | `game.ended` behavior | `poll.start` behavior |
|------|----------------------|----------------------|
| **manual** (default) | Logged but no poll generated | Generates a new poll |
| **auto** | Generates a new poll (legacy behavior) | Generates a new poll |
The mode is persisted in the vote-app database and survives restarts. It can be toggled from the debug panel (Game Control section).
## Message Format
Send this JSON message over the existing upstream WebSocket connection:
```json
{
"type": "poll.start",
"sessionId": 3
}
```
### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | yes | Must be `"poll.start"` |
| `sessionId` | number | no | Included for consistency; the vote-app uses its internally tracked session ID |
The `sessionId` field is optional in practice — the vote-app tracks the active session from `session.started` / subscription events and uses that to determine which games to exclude from the poll. Including it is recommended for protocol consistency.
## When to Send
- **After `game.ended`** — if the vote-app is in `manual` mode, `game.ended` alone will not create a poll. Send `poll.start` when you're ready for viewers to vote on the next game.
- **At any time** — you can send `poll.start` to force a new poll regardless of mode. Any existing active poll is deactivated and replaced.
## What Happens on Receipt
1. The vote-app fetches enabled games from the upstream API (`GET /api/games?enabled=true`)
2. It fetches the current session's games (`GET /api/sessions/{id}/games`) to exclude recently played titles
3. It picks 3 random games (weighted by favor bias) plus an "Other" option
4. The previous active poll (if any) is deactivated
5. The new poll is created and broadcast to all connected browser clients via WebSocket
## Example (Node.js)
```javascript
// Assuming `ws` is your existing WebSocket connection to the vote-app upstream
// and you've already authenticated and subscribed to the session
function startPoll(sessionId) {
ws.send(JSON.stringify({
type: 'poll.start',
sessionId: sessionId
}));
}
// Typical flow: game ends, wait for the right moment, then start the poll
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'game.ended') {
// Game just ended — start poll when ready
// (in manual mode, vote-app won't auto-generate one)
startPoll(msg.data.sessionId);
}
});
```
## Existing Messages (Unchanged)
These upstream messages continue to work as before:
- `game.ended` — in `auto` mode, still triggers poll generation; in `manual` mode, logged but no poll created
- `voting.ended` — deactivates the active poll and broadcasts the winner
- `game.started` — deactivates the active poll and hides the overlay
- `room.connected` — deactivates the active poll and broadcasts room info
- `poll.leading` — still sent by the vote-app to upstream when the leading vote changes