8.2 KiB
Session End WebSocket Event
This document describes the session.ended WebSocket event that is broadcast when a game session is closed.
📋 Event Overview
When a session is closed (either manually or through timeout), the backend broadcasts a session.ended event to all subscribed WebSocket clients. This allows bots and other integrations to react immediately to session closures.
🔌 WebSocket Connection
Endpoint: ws://localhost:5000/api/sessions/live
Authentication: Required (JWT token)
📨 Event Format
Event Type
session.ended
Full Message Structure
{
"type": "session.ended",
"timestamp": "2025-11-01T02:30:45.123Z",
"data": {
"session": {
"id": 17,
"is_active": 0,
"games_played": 5
}
}
}
Field Descriptions
| Field | Type | Description |
|---|---|---|
type |
string | Always "session.ended" |
timestamp |
string | ISO 8601 timestamp when the event was generated |
data.session.id |
number | The ID of the session that ended |
data.session.is_active |
number | Always 0 (inactive) for ended sessions |
data.session.games_played |
number | Total number of games played in the session |
🚀 Implementation
Backend Implementation
The session.ended event is automatically broadcast when:
- Manual Session Close: Admin closes a session via
POST /api/sessions/:id/close - Session Timeout: (If implemented) When a session times out
Code Location: backend/routes/sessions.js - POST /:id/close endpoint
// Broadcast session.ended event via WebSocket
const wsManager = getWebSocketManager();
if (wsManager) {
const eventData = {
session: {
id: closedSession.id,
is_active: 0,
games_played: closedSession.games_played
}
};
wsManager.broadcastEvent('session.ended', eventData, parseInt(req.params.id));
}
Client Implementation Example
Node.js with ws library
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:5000/api/sessions/live');
ws.on('open', () => {
// Authenticate
ws.send(JSON.stringify({
type: 'auth',
token: 'your-jwt-token'
}));
});
ws.on('message', (data) => {
const message = JSON.parse(data.toString());
switch (message.type) {
case 'auth_success':
// Subscribe to session
ws.send(JSON.stringify({
type: 'subscribe',
sessionId: 17
}));
break;
case 'session.ended':
console.log('Session ended!');
console.log(`Session ID: ${message.data.session.id}`);
console.log(`Games played: ${message.data.session.games_played}`);
// Handle session end (e.g., announce in IRC, Discord, etc.)
break;
}
});
Python with websockets library
import asyncio
import json
import websockets
async def listen_for_session_end():
uri = "ws://localhost:5000/api/sessions/live"
async with websockets.connect(uri) as websocket:
# Authenticate
await websocket.send(json.dumps({
"type": "auth",
"token": "your-jwt-token"
}))
async for message in websocket:
data = json.loads(message)
if data["type"] == "auth_success":
# Subscribe to session
await websocket.send(json.dumps({
"type": "subscribe",
"sessionId": 17
}))
elif data["type"] == "session.ended":
session = data["data"]["session"]
print(f"Session {session['id']} ended!")
print(f"Games played: {session['games_played']}")
# Handle session end
asyncio.run(listen_for_session_end())
🧪 Testing
Using the Test Script
A test script is provided to verify the session.ended event:
node test-session-end-websocket.js <session_id> <jwt_token>
Example:
node test-session-end-websocket.js 17 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Manual Testing Steps
-
Start the backend server:
cd backend npm start -
Run the test script in another terminal:
node test-session-end-websocket.js 17 <your-jwt-token> -
Close the session in the Picker UI or via API:
curl -X POST http://localhost:5000/api/sessions/17/close \ -H "Authorization: Bearer <your-jwt-token>" \ -H "Content-Type: application/json" -
Verify the event is received in the test script output
Expected Output
🚀 Testing session.ended WebSocket event
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📡 Connecting to: ws://localhost:5000/api/sessions/live
🎮 Session ID: 17
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Connected to WebSocket server
🔐 Authenticating...
✅ Authentication successful
📻 Subscribing to session 17...
✅ Subscribed to session 17
👂 Listening for session.ended events...
(Close the session in the Picker to trigger the event)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 SESSION.ENDED EVENT RECEIVED!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📦 Event Data:
{
"type": "session.ended",
"timestamp": "2025-11-01T02:30:45.123Z",
"data": {
"session": {
"id": 17,
"is_active": 0,
"games_played": 5
}
}
}
✨ Event Details:
Session ID: 17
Active: No
Games Played: 5
Timestamp: 2025-11-01T02:30:45.123Z
✅ Test successful! The bot should now announce the session end.
🤖 Bot Integration
IRC/Kosmi Bot Example
When the bot receives a session.ended event, it should:
- Announce the final vote counts for the last game played
- Announce that the game night has ended
- Optionally display session statistics
Example bot response:
🗳️ Final votes for Quiplash 3: 5👍 1👎 (Score: +4)
🌙 Game Night has ended! Thanks for playing!
📊 Session Stats: 5 games played
Fallback Behavior
The bot should also implement polling detection as a fallback in case the WebSocket connection fails or the event is not received:
- Poll
GET /api/sessions/activeevery 30 seconds - If a previously active session becomes inactive, treat it as a session end
- This ensures the bot will always detect session endings, even without WebSocket
🔍 Debugging
Check WebSocket Logs
The backend logs WebSocket events:
[WebSocket] Client subscribed to session 17
[Sessions] Broadcasted session.ended event for session 17
[WebSocket] Broadcasted session.ended to 1 client(s) for session 17
Common Issues
-
Event not received:
- Verify the client is authenticated (
auth_successreceived) - Verify the client is subscribed to the correct session
- Check backend logs for broadcast confirmation
- Verify the client is authenticated (
-
Connection drops:
- Implement ping/pong heartbeat (send
{"type": "ping"}every 30s) - Handle reconnection logic in your client
- Implement ping/pong heartbeat (send
-
Multiple events received:
- This is normal if multiple clients are subscribed
- Each client receives its own copy of the event
📚 Related Documentation
🔗 Related Events
| Event Type | Description | When Triggered |
|---|---|---|
session.started |
A new session was created | When session is created |
game.added |
A new game was added to the session | When a game starts |
session.ended |
The session has ended | When session is closed |
vote.received |
A vote was cast for a game | When a user votes |
📝 Notes
- The
session.endedevent is broadcast to all clients subscribed to that session - The event includes the final
games_playedcount for the session - The
is_activefield will always be0for ended sessions - The timestamp is in ISO 8601 format with timezone (UTC)