docs: comprehensive API documentation from source code
Replace existing docs with fresh documentation built entirely from source code analysis. OpenAPI 3.1 spec as source of truth, plus human-readable Markdown with curl examples, response samples, and workflow guides. - OpenAPI 3.1 spec covering all 42 endpoints (validated against source) - 7 endpoint reference docs (auth, games, sessions, picker, stats, votes, webhooks) - WebSocket protocol documentation (auth, subscriptions, 4 event types) - 4 guide documents (getting started, session lifecycle, voting, webhooks) - API README with overview, auth docs, and quick reference table - Old docs archived to docs/archive/ Made-with: Cursor
This commit is contained in:
207
docs/api/guides/voting-and-popularity.md
Normal file
207
docs/api/guides/voting-and-popularity.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Voting and Popularity
|
||||
|
||||
A narrative guide to how the Jackbox Game Picker handles community voting and game popularity. This system lets viewers and stream chat influence which games rise to the top—without directly controlling the random picker.
|
||||
|
||||
---
|
||||
|
||||
## 1. How Popularity Works
|
||||
|
||||
Every game has a **popularity score** stored in the database:
|
||||
|
||||
```
|
||||
popularity_score = upvotes - downvotes
|
||||
```
|
||||
|
||||
The score is computed from `upvotes` and `downvotes` and persisted per game. As votes accumulate across sessions, the score reflects community sentiment over time.
|
||||
|
||||
**Important:** Popularity is used for **rankings** (e.g., "top rated games" in stats) but **does not directly affect picker weights**. The random picker uses favor bias, not popularity, when selecting games.
|
||||
|
||||
---
|
||||
|
||||
## 2. Favor Bias vs Popularity
|
||||
|
||||
Two separate systems govern how games are treated:
|
||||
|
||||
| Aspect | **Favor Bias** | **Popularity** |
|
||||
|--------|----------------|----------------|
|
||||
| Who controls it | Admin (via API) | Community (via votes) |
|
||||
| Values | `-1` (disfavor), `0` (neutral), `1` (favor) | `upvotes - downvotes` (unbounded) |
|
||||
| Affects picker? | Yes — directly changes weights | No |
|
||||
| Purpose | Manual curation; push/penalize specific games | Community sentiment; rankings |
|
||||
|
||||
**Favor bias** affects picker probability directly. Setting `favor_bias` to `1` on a game boosts its weight; `-1` reduces it. See [Games favor endpoint](../endpoints/games.md#patch-apigamesidfavor) and [Picker weighted selection](../endpoints/picker.md#weighted-selection).
|
||||
|
||||
**Popularity** is driven entirely by viewer votes. It surfaces in stats (e.g., `topRatedGames`) and session game lists, but the picker does not read it. These systems are independent.
|
||||
|
||||
---
|
||||
|
||||
## 3. Two Voting Mechanisms
|
||||
|
||||
The API supports two ways to record votes: batch chat import (after the fact) and live votes (real-time from bots).
|
||||
|
||||
### Chat Import (Batch, After-the-Fact)
|
||||
|
||||
Collect Twitch or YouTube chat logs containing `thisgame++` (upvote) and `thisgame--` (downvote), then submit them in bulk.
|
||||
|
||||
**Flow:**
|
||||
1. Export chat logs with `username`, `message`, and `timestamp` for each message.
|
||||
2. Filter or pass messages; the API parses `thisgame++` and `thisgame--` from the `message` field.
|
||||
3. POST to `POST /api/sessions/{id}/chat-import` with a `chatData` array of `{ username, message, timestamp }`.
|
||||
4. The API matches each vote’s timestamp to the game that was playing at that time (using `played_at` intervals).
|
||||
5. Votes are deduplicated by SHA-256 hash of `username:message:timestamp`.
|
||||
6. Response includes `votesByGame` breakdown and `debug` info (e.g., session timeline, vote matches).
|
||||
|
||||
See [Sessions chat-import endpoint](../endpoints/sessions.md#post-apisessionsidchat-import).
|
||||
|
||||
### Live Votes (Real-Time, from Bots)
|
||||
|
||||
A bot sends individual votes during the stream. Each vote is processed immediately.
|
||||
|
||||
**Flow:**
|
||||
1. Bot detects `thisgame++` or `thisgame--` (or equivalent) in chat.
|
||||
2. Bot sends `POST /api/votes/live` with `{ username, vote, timestamp }`.
|
||||
3. `vote` must be `"up"` or `"down"`.
|
||||
4. `timestamp` must be ISO 8601 (e.g., `2026-03-15T20:30:00Z`).
|
||||
5. The API finds the active session and matches the vote timestamp to the game playing at that time.
|
||||
6. **Deduplication:** Votes from the same username within 1 second are rejected with `409 Conflict`.
|
||||
|
||||
See [Votes live endpoint](../endpoints/votes.md#post-apivoteslive).
|
||||
|
||||
---
|
||||
|
||||
## 4. Timestamp Matching Explained
|
||||
|
||||
Games in a session have a `played_at` timestamp. A vote’s timestamp determines which game it belongs to.
|
||||
|
||||
**Rule:** A vote belongs to the game whose `played_at` is the **most recent one before** the vote timestamp.
|
||||
|
||||
Example session timeline:
|
||||
|
||||
- Game A: `played_at` 20:00
|
||||
- Game B: `played_at` 20:15
|
||||
- Game C: `played_at` 20:30
|
||||
|
||||
- Vote at 20:10 → Game A (last `played_at` before 20:10)
|
||||
- Vote at 20:20 → Game B
|
||||
- Vote at 20:45 → Game C (last game in session; captures all votes after it started)
|
||||
|
||||
The **last game** in the session captures all votes that occur after its `played_at`.
|
||||
|
||||
---
|
||||
|
||||
## 5. How Stats Reflect Popularity
|
||||
|
||||
`GET /api/stats` returns aggregate statistics, including:
|
||||
|
||||
- **mostPlayedGames** — top 10 by `play_count` (games with `play_count` > 0).
|
||||
- **topRatedGames** — top 10 by `popularity_score` (games with `popularity_score` > 0).
|
||||
|
||||
Both are limited to the top 10 and exclude games with score/count ≤ 0. See [Stats endpoint](../endpoints/stats.md).
|
||||
|
||||
---
|
||||
|
||||
## 6. Example Requests
|
||||
|
||||
### Chat Import
|
||||
|
||||
Import a batch of chat messages for session `5`:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:5000/api/sessions/5/chat-import" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"chatData": [
|
||||
{
|
||||
"username": "viewer1",
|
||||
"message": "thisgame++",
|
||||
"timestamp": "2026-03-15T20:30:00Z"
|
||||
},
|
||||
{
|
||||
"username": "viewer2",
|
||||
"message": "thisgame--",
|
||||
"timestamp": "2026-03-15T20:31:00Z"
|
||||
},
|
||||
{
|
||||
"username": "viewer3",
|
||||
"message": "thisgame++",
|
||||
"timestamp": "2026-03-15T20:32:00Z"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Sample response (200 OK):**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Chat log imported and processed successfully",
|
||||
"messagesImported": 3,
|
||||
"duplicatesSkipped": 0,
|
||||
"votesProcessed": 3,
|
||||
"votesByGame": {
|
||||
"42": {
|
||||
"title": "Quiplash 3",
|
||||
"upvotes": 2,
|
||||
"downvotes": 1
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"sessionGamesTimeline": [
|
||||
{
|
||||
"title": "Quiplash 3",
|
||||
"played_at": "2026-03-15T20:00:00.000Z",
|
||||
"played_at_ms": 1742068800000
|
||||
}
|
||||
],
|
||||
"voteMatches": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Live Vote
|
||||
|
||||
Submit a single live vote (requires active session):
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:5000/api/votes/live" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "viewer123",
|
||||
"vote": "up",
|
||||
"timestamp": "2026-03-15T20:30:00Z"
|
||||
}'
|
||||
```
|
||||
|
||||
**Sample response (200 OK):**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Vote recorded successfully",
|
||||
"session": { "id": 3, "games_played": 5 },
|
||||
"game": {
|
||||
"id": 42,
|
||||
"title": "Quiplash 3",
|
||||
"upvotes": 11,
|
||||
"downvotes": 2,
|
||||
"popularity_score": 9
|
||||
},
|
||||
"vote": {
|
||||
"username": "viewer123",
|
||||
"type": "up",
|
||||
"timestamp": "2026-03-15T20:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Sessions endpoints](../endpoints/sessions.md) — chat import, session games, `played_at`
|
||||
- [Votes endpoints](../endpoints/votes.md) — live votes, deduplication, errors
|
||||
- [Stats endpoints](../endpoints/stats.md) — `mostPlayedGames`, `topRatedGames`
|
||||
- [Picker endpoints](../endpoints/picker.md) — weighted selection, favor bias (no popularity)
|
||||
- [Games endpoints](../endpoints/games.md) — favor bias per game and pack
|
||||
Reference in New Issue
Block a user