Reorganize project: move docs to docs/ and test scripts to tests/
Moved 9 documentation .md files from root into docs/. Moved 4 test scripts from root into tests/. Updated cross-references in README.md and docs to reflect new paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
310
docs/WEBSOCKET_SUBSCRIPTION_GUIDE.md
Normal file
310
docs/WEBSOCKET_SUBSCRIPTION_GUIDE.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# WebSocket Subscription Guide
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
This guide explains how WebSocket subscriptions work in the Jackbox Game Picker and which events require subscriptions.
|
||||
|
||||
## 🔌 Connection & Authentication
|
||||
|
||||
### 1. Connect to WebSocket
|
||||
|
||||
```javascript
|
||||
const ws = new WebSocket('ws://localhost:5000/api/sessions/live');
|
||||
```
|
||||
|
||||
### 2. Authenticate
|
||||
|
||||
```javascript
|
||||
ws.send(JSON.stringify({
|
||||
type: 'auth',
|
||||
token: 'YOUR_JWT_TOKEN'
|
||||
}));
|
||||
```
|
||||
|
||||
### 3. Wait for Auth Success
|
||||
|
||||
```javascript
|
||||
ws.on('message', (data) => {
|
||||
const msg = JSON.parse(data.toString());
|
||||
if (msg.type === 'auth_success') {
|
||||
console.log('Authenticated!');
|
||||
// Now you can subscribe to sessions
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 📨 Event Types & Subscription Requirements
|
||||
|
||||
| Event Type | Requires Subscription? | Broadcast To | When to Subscribe |
|
||||
|------------|------------------------|--------------|-------------------|
|
||||
| `session.started` | ❌ **NO** | All authenticated clients | N/A - Automatic |
|
||||
| `game.added` | ✅ **YES** | Subscribed clients only | After session.started |
|
||||
| `vote.received` | ✅ **YES** | Subscribed clients only | After session.started |
|
||||
| `session.ended` | ✅ **YES** | Subscribed clients only | After session.started |
|
||||
|
||||
## 🎯 Subscription Strategy
|
||||
|
||||
### Strategy 1: Auto-Subscribe to New Sessions (Recommended for Bots)
|
||||
|
||||
```javascript
|
||||
ws.on('message', (data) => {
|
||||
const msg = JSON.parse(data.toString());
|
||||
|
||||
// After authentication
|
||||
if (msg.type === 'auth_success') {
|
||||
console.log('✅ Authenticated');
|
||||
}
|
||||
|
||||
// Auto-subscribe to new sessions
|
||||
if (msg.type === 'session.started') {
|
||||
const sessionId = msg.data.session.id;
|
||||
console.log(`🎮 New session ${sessionId} started!`);
|
||||
|
||||
// Subscribe to this session
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
sessionId: sessionId
|
||||
}));
|
||||
}
|
||||
|
||||
// Now you'll receive game.added, vote.received, and session.ended
|
||||
if (msg.type === 'game.added') {
|
||||
console.log(`🎲 Game: ${msg.data.game.title}`);
|
||||
}
|
||||
|
||||
if (msg.type === 'session.ended') {
|
||||
console.log(`🌙 Session ended! ${msg.data.session.games_played} games played`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Strategy 2: Subscribe to Active Session on Connect
|
||||
|
||||
```javascript
|
||||
ws.on('message', (data) => {
|
||||
const msg = JSON.parse(data.toString());
|
||||
|
||||
if (msg.type === 'auth_success') {
|
||||
console.log('✅ Authenticated');
|
||||
|
||||
// Fetch active session from API
|
||||
fetch('http://localhost:5000/api/sessions/active')
|
||||
.then(res => res.json())
|
||||
.then(session => {
|
||||
if (session && session.id) {
|
||||
// Subscribe to active session
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
sessionId: session.id
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Strategy 3: Subscribe to Specific Session
|
||||
|
||||
```javascript
|
||||
ws.on('message', (data) => {
|
||||
const msg = JSON.parse(data.toString());
|
||||
|
||||
if (msg.type === 'auth_success') {
|
||||
console.log('✅ Authenticated');
|
||||
|
||||
// Subscribe to specific session
|
||||
const sessionId = 17;
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
sessionId: sessionId
|
||||
}));
|
||||
}
|
||||
|
||||
if (msg.type === 'subscribed') {
|
||||
console.log(`✅ Subscribed to session ${msg.sessionId}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 🔄 Complete Bot Flow
|
||||
|
||||
```javascript
|
||||
const WebSocket = require('ws');
|
||||
|
||||
class JackboxBot {
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
this.ws = null;
|
||||
this.currentSessionId = null;
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket('ws://localhost:5000/api/sessions/live');
|
||||
|
||||
this.ws.on('open', () => {
|
||||
console.log('🔌 Connected to WebSocket');
|
||||
this.authenticate();
|
||||
});
|
||||
|
||||
this.ws.on('message', (data) => {
|
||||
this.handleMessage(JSON.parse(data.toString()));
|
||||
});
|
||||
}
|
||||
|
||||
authenticate() {
|
||||
this.ws.send(JSON.stringify({
|
||||
type: 'auth',
|
||||
token: this.token
|
||||
}));
|
||||
}
|
||||
|
||||
handleMessage(msg) {
|
||||
switch (msg.type) {
|
||||
case 'auth_success':
|
||||
console.log('✅ Authenticated');
|
||||
// Don't subscribe yet - wait for session.started
|
||||
break;
|
||||
|
||||
case 'session.started':
|
||||
this.currentSessionId = msg.data.session.id;
|
||||
console.log(`🎮 Session ${this.currentSessionId} started!`);
|
||||
|
||||
// Auto-subscribe to this session
|
||||
this.ws.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
sessionId: this.currentSessionId
|
||||
}));
|
||||
|
||||
// Announce to users
|
||||
this.announce(`Game Night has started! Session #${this.currentSessionId}`);
|
||||
break;
|
||||
|
||||
case 'subscribed':
|
||||
console.log(`✅ Subscribed to session ${msg.sessionId}`);
|
||||
break;
|
||||
|
||||
case 'game.added':
|
||||
console.log(`🎲 Game: ${msg.data.game.title}`);
|
||||
this.announce(`Now playing: ${msg.data.game.title}`);
|
||||
break;
|
||||
|
||||
case 'vote.received':
|
||||
console.log(`🗳️ Vote: ${msg.data.vote.type}`);
|
||||
break;
|
||||
|
||||
case 'session.ended':
|
||||
console.log(`🌙 Session ${msg.data.session.id} ended`);
|
||||
this.announce(`Game Night ended! ${msg.data.session.games_played} games played`);
|
||||
this.currentSessionId = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
announce(message) {
|
||||
// Send to IRC/Discord/Kosmi/etc
|
||||
console.log(`📢 ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const bot = new JackboxBot('YOUR_JWT_TOKEN');
|
||||
bot.connect();
|
||||
```
|
||||
|
||||
## 📊 Subscription Lifecycle
|
||||
|
||||
```
|
||||
1. Connect to WebSocket
|
||||
↓
|
||||
2. Send auth message
|
||||
↓
|
||||
3. Receive auth_success
|
||||
↓
|
||||
4. Wait for session.started (no subscription needed)
|
||||
↓
|
||||
5. Receive session.started
|
||||
↓
|
||||
6. Send subscribe message with sessionId
|
||||
↓
|
||||
7. Receive subscribed confirmation
|
||||
↓
|
||||
8. Now receive: game.added, vote.received, session.ended
|
||||
↓
|
||||
9. Receive session.ended
|
||||
↓
|
||||
10. Wait for next session.started (repeat from step 4)
|
||||
```
|
||||
|
||||
## 🔍 Debugging
|
||||
|
||||
### Check What You're Subscribed To
|
||||
|
||||
The WebSocket manager tracks subscriptions. Check backend logs:
|
||||
|
||||
```
|
||||
[WebSocket] Client subscribed to session 17
|
||||
[WebSocket] Client unsubscribed from session 17
|
||||
```
|
||||
|
||||
### Verify Event Reception
|
||||
|
||||
**session.started** - Should receive immediately after authentication (no subscription needed):
|
||||
```
|
||||
[WebSocket] Broadcasted session.started to 2 authenticated client(s)
|
||||
```
|
||||
|
||||
**game.added, vote.received, session.ended** - Only after subscribing:
|
||||
```
|
||||
[WebSocket] Broadcasted game.added to 1 client(s) for session 17
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Not receiving session.started:**
|
||||
- ✅ Are you authenticated?
|
||||
- ✅ Is your WebSocket connection open?
|
||||
- ✅ Check backend logs for broadcast confirmation
|
||||
|
||||
2. **Not receiving game.added:**
|
||||
- ✅ Did you subscribe to the session?
|
||||
- ✅ Did you receive `subscribed` confirmation?
|
||||
- ✅ Is the session ID correct?
|
||||
|
||||
3. **Not receiving session.ended:**
|
||||
- ✅ Are you still subscribed to the session?
|
||||
- ✅ Did the session actually close?
|
||||
- ✅ Check backend logs
|
||||
|
||||
## 🎯 Best Practices
|
||||
|
||||
1. **Auto-subscribe to new sessions** when you receive `session.started`
|
||||
2. **Don't subscribe before session.started** - there's nothing to subscribe to yet
|
||||
3. **Handle reconnections** - re-authenticate and re-subscribe on reconnect
|
||||
4. **Use polling as fallback** - poll `/api/sessions/active` every 30s as backup
|
||||
5. **Unsubscribe when done** - clean up subscriptions when you're done with a session
|
||||
6. **Validate session IDs** - make sure the session exists before subscribing
|
||||
|
||||
## 📝 Summary
|
||||
|
||||
### ❌ No Subscription Required
|
||||
- `session.started` - Broadcast to **all authenticated clients**
|
||||
|
||||
### ✅ Subscription Required
|
||||
- `game.added` - Only to **subscribed clients**
|
||||
- `vote.received` - Only to **subscribed clients**
|
||||
- `session.ended` - Only to **subscribed clients**
|
||||
|
||||
### 🎯 Recommended Flow
|
||||
1. Authenticate
|
||||
2. Wait for `session.started` (automatic)
|
||||
3. Subscribe to the session
|
||||
4. Receive `game.added`, `vote.received`, `session.ended`
|
||||
5. Repeat from step 2 for next session
|
||||
|
||||
## 🔗 Related Documentation
|
||||
|
||||
- [SESSION_START_WEBSOCKET.md](SESSION_START_WEBSOCKET.md) - session.started event details
|
||||
- [SESSION_END_WEBSOCKET.md](SESSION_END_WEBSOCKET.md) - session.ended event details
|
||||
- [API_QUICK_REFERENCE.md](API_QUICK_REFERENCE.md) - API reference
|
||||
- [BOT_INTEGRATION.md](BOT_INTEGRATION.md) - Bot integration guide
|
||||
|
||||
Reference in New Issue
Block a user