# 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

```json
{
  "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:

1. **Manual Session Close**: Admin closes a session via `POST /api/sessions/:id/close`
2. **Session Timeout**: (If implemented) When a session times out

**Code Location:** `backend/routes/sessions.js` - `POST /:id/close` endpoint

```javascript
// 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

```javascript
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

```python
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:

```bash
node ../tests/test-session-end-websocket.js <session_id> <jwt_token>
```

**Example:**
```bash
node ../tests/test-session-end-websocket.js 17 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

### Manual Testing Steps

1. **Start the backend server:**
   ```bash
   cd backend
   npm start
   ```

2. **Run the test script in another terminal:**
   ```bash
   node ../tests/test-session-end-websocket.js 17 <your-jwt-token>
   ```

3. **Close the session in the Picker UI** or via API:
   ```bash
   curl -X POST http://localhost:5000/api/sessions/17/close \
     -H "Authorization: Bearer <your-jwt-token>" \
     -H "Content-Type: application/json"
   ```

4. **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:

1. **Announce the final vote counts** for the last game played
2. **Announce that the game night has ended**
3. **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/active` every 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

1. **Event not received:**
   - Verify the client is authenticated (`auth_success` received)
   - Verify the client is subscribed to the correct session
   - Check backend logs for broadcast confirmation

2. **Connection drops:**
   - Implement ping/pong heartbeat (send `{"type": "ping"}` every 30s)
   - Handle reconnection logic in your client

3. **Multiple events received:**
   - This is normal if multiple clients are subscribed
   - Each client receives its own copy of the event

## 📚 Related Documentation

- [WebSocket Testing Guide](WEBSOCKET_TESTING.md)
- [Bot Integration Guide](BOT_INTEGRATION.md)
- [API Quick Reference](API_QUICK_REFERENCE.md)

## 🔗 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.ended` event is broadcast to **all clients subscribed to that session**
- The event includes the final `games_played` count for the session
- The `is_active` field will always be `0` for ended sessions
- The timestamp is in ISO 8601 format with timezone (UTC)

