cottongin c756d45e24 fix: guard shard event emissions on both manuallyStopped and gameFinished
Prevent stale events from shards that ended naturally (not via
stopMonitor). handleMessage now gates on gameFinished in addition to
manuallyStopped. handleEntityUpdate properly cleans up on gameFinished
by emitting room.disconnected, removing from activeShards, and calling
disconnect. handleError also removes from activeShards. Probe message
handler and status broadcast bail out when the shard is stopped or the
game has finished.

Made-with: Cursor
2026-03-21 00:07:10 -04:00
2025-10-30 19:27:23 -04:00
2025-10-30 04:27:43 -04:00

Jackbox Party Pack Game Picker

A full-stack web application that helps groups pick games to play from various Jackbox Party Packs. Features include random game selection with filters, session tracking, game management, and popularity scoring through chat log imports.

Features

Progressive Web App (PWA)

  • Installable: Add to home screen on mobile and desktop devices
  • Offline Support: Service worker provides offline functionality
  • Native Experience: Runs like a native app when installed
  • Auto-updates: Seamlessly updates to new versions

Admin Features

  • Game Picker: Randomly select games with intelligent filters

    • Filter by player count, drawing games, game length, and family-friendly status
    • Automatic repeat avoidance (prevents same game or alternating pattern)
    • Manual game selection option
    • Real-time session tracking
  • Game Manager: Complete CRUD operations for games and packs

    • Enable/disable individual games or entire packs
    • Import/export games via CSV
    • View statistics (play counts, popularity scores)
    • Add, edit, and delete games
  • Session Management: Track gaming sessions over time

    • Create and close sessions
    • View session history
    • Import chat logs to calculate game popularity
    • Track which games were played when
  • Chat Log Import: Process chat messages to assess game popularity

    • Supports "thisgame++" and "thisgame--" voting
    • Automatically matches votes to games based on timestamps
    • Updates popularity scores across sessions
  • Live Voting API: Real-time vote processing from external bots

    • Accept live votes via REST API
    • Real-time vote.received WebSocket event for stream overlays
    • Per-session vote breakdown and paginated global vote history
    • Automatic deduplication (1-second window)
    • Timestamp-based game matching
    • JWT authentication for security
  • Webhook System: Notify external services of events

    • Send notifications when games are added to sessions
    • HMAC-SHA256 signature verification
    • Webhook management (CRUD operations)
    • Delivery logging and testing

Public Features

  • View active session and games currently being played
  • Browse session history
  • See game statistics and popularity

Tech Stack

  • Frontend: React 18 with Vite, Tailwind CSS, React Router
  • Backend: Node.js with Express
  • Database: SQLite with better-sqlite3
  • Authentication: JWT-based admin authentication
  • Deployment: Docker with docker-compose

Prerequisites

  • Docker and Docker Compose (recommended)
  • OR Node.js 18+ and npm (for local development)

Quick Start with Docker

  1. Clone the repository

    git clone <repository-url>
    cd jackboxpartypack-gamepicker
    
  2. Create environment file

    Create a .env file in the root directory:

    PORT=5000
    NODE_ENV=production
    DB_PATH=/app/data/jackbox.db
    JWT_SECRET=your-secret-jwt-key-change-this
    ADMIN_KEY=your-admin-key-here
    
  3. Build and start the containers

    docker-compose up -d
    
  4. Access the application

  5. Login as admin

    • Navigate to the login page
    • Enter your ADMIN_KEY from the .env file

The database will be automatically initialized and populated with games from games-list.csv on first run.

Local Development Setup

Backend Setup

  1. Navigate to backend directory

    cd backend
    
  2. Install dependencies

    npm install
    
  3. Create environment file

    Create backend/.env:

    PORT=5000
    DB_PATH=./data/jackbox.db
    JWT_SECRET=your-secret-jwt-key
    ADMIN_KEY=admin123
    
  4. Start the backend server

    npm run dev
    

The backend will run on http://localhost:5000

Frontend Setup

  1. Navigate to frontend directory

    cd frontend
    
  2. Install dependencies

    npm install
    
  3. Start the development server

    npm run dev
    

The frontend will run on http://localhost:3000 and proxy API requests to the backend.

Configuration

Branding and Metadata

All app branding, metadata, and PWA configuration is centralized in frontend/src/config/branding.js. Edit this file to customize:

  • App Name and Short Name - Displayed in UI and when installed as PWA
  • Description - Shown in search engines and app stores
  • Version - Current app version
  • Theme Color - Primary color for browser chrome and PWA theme
  • Keywords - SEO metadata
  • Author - Creator/maintainer information
  • Links - GitHub repo, support contact, etc.

When you update branding.js, the following are automatically synchronized:

  1. PWA Manifest (manifest.json) - Generated at build time via generate-manifest.js
  2. HTML Meta Tags - Updated via Vite HTML transformation plugin
  3. App UI - Components import branding directly

To regenerate the manifest manually:

cd frontend
npm run generate-manifest

The manifest is automatically generated during the build process, so you don't need to edit it directly.

Project Structure

/
├── backend/
│   ├── routes/           # API route handlers
│   │   ├── auth.js       # Authentication endpoints
│   │   ├── games.js      # Game CRUD and management
│   │   ├── sessions.js   # Session management
│   │   ├── picker.js     # Game picker algorithm
│   │   ├── stats.js      # Statistics endpoints
│   │   ├── votes.js      # Live voting endpoint
│   │   └── webhooks.js   # Webhook management
│   ├── middleware/       # Express middleware
│   │   └── auth.js       # JWT authentication
│   ├── utils/            # Utility functions
│   │   └── webhooks.js   # Webhook trigger and signature
│   ├── database.js       # SQLite database setup
│   ├── bootstrap.js      # Database initialization
│   ├── server.js         # Express app entry point
│   ├── package.json
│   └── Dockerfile
├── frontend/
│   ├── src/
│   │   ├── pages/        # React page components
│   │   │   ├── Home.jsx
│   │   │   ├── Login.jsx
│   │   │   ├── Picker.jsx
│   │   │   ├── Manager.jsx
│   │   │   └── History.jsx
│   │   ├── context/      # React context providers
│   │   │   └── AuthContext.jsx
│   │   ├── api/          # API client
│   │   │   └── axios.js
│   │   ├── App.jsx
│   │   ├── main.jsx
│   │   └── index.css
│   ├── index.html
│   ├── vite.config.js
│   ├── tailwind.config.js
│   ├── package.json
│   ├── nginx.conf        # Nginx config for Docker
│   └── Dockerfile
├── docker-compose.yml
├── games-list.csv        # Initial game data
└── README.md

API Endpoints

Authentication

  • POST /api/auth/login - Login with admin key
  • POST /api/auth/verify - Verify JWT token

Games

  • GET /api/games - List all games (with filters)
  • GET /api/games/:id - Get single game
  • POST /api/games - Create game (admin)
  • PUT /api/games/:id - Update game (admin)
  • DELETE /api/games/:id - Delete game (admin)
  • PATCH /api/games/:id/toggle - Toggle game enabled status (admin)
  • GET /api/games/meta/packs - Get pack list with stats
  • PATCH /api/games/packs/:name/toggle - Toggle entire pack (admin)
  • GET /api/games/export/csv - Export games to CSV (admin)
  • POST /api/games/import/csv - Import games from CSV (admin)

Sessions

  • GET /api/sessions - List all sessions
  • GET /api/sessions/active - Get active session
  • GET /api/sessions/:id - Get single session
  • POST /api/sessions - Create new session (admin)
  • POST /api/sessions/:id/close - Close session (admin)
  • GET /api/sessions/:id/games - Get games in session
  • GET /api/sessions/:id/votes - Get per-game vote breakdown for a session
  • POST /api/sessions/:id/games - Add game to session (admin)
  • POST /api/sessions/:id/chat-import - Import chat log (admin)

Game Picker

  • POST /api/pick - Pick random game with filters

Statistics

  • GET /api/stats - Get overall statistics

Votes

  • GET /api/votes - Paginated vote history with filtering
  • POST /api/votes/live - Submit real-time vote (admin)

Webhooks

  • GET /api/webhooks - List all webhooks (admin)
  • GET /api/webhooks/:id - Get single webhook (admin)
  • POST /api/webhooks - Create webhook (admin)
  • PATCH /api/webhooks/:id - Update webhook (admin)
  • DELETE /api/webhooks/:id - Delete webhook (admin)
  • POST /api/webhooks/test/:id - Test webhook (admin)
  • GET /api/webhooks/:id/logs - Get webhook logs (admin)

Usage Guide

Starting a Game Session

  1. Login as admin
  2. Navigate to the Picker page
  3. A new session will be created automatically if none exists
  4. Set filters (player count, drawing preference, length, family-friendly)
  5. Click "Roll the Dice" to get a random game
  6. Review the suggested game and either:
    • Click "Play This Game" to accept
    • Click "Re-roll" to get a different game
  7. The game is added to the session and play count is incremented

Managing Games

  1. Login as admin
  2. Navigate to the Manager page
  3. View statistics and pack information
  4. Toggle individual games or entire packs on/off
  5. Add new games with the "+ Add Game" button
  6. Edit or delete existing games
  7. Import/Export games via CSV

Closing a Session and Importing Chat Logs

  1. Navigate to the History page
  2. Select the active session
  3. Click "Import Chat Log"
  4. Paste JSON array of chat messages with format:
    [
      {
        "username": "Alice",
        "message": "thisgame++",
        "timestamp": "2024-10-30T20:15:00Z"
      },
      {
        "username": "Bob",
        "message": "This is fun! thisgame++",
        "timestamp": "2024-10-30T20:16:30Z"
      }
    ]
    
  5. The system will match votes to games based on timestamps
  6. Click "Close Session" to finalize
  7. Add optional notes about the session

Chat Log Format

The chat import feature expects a JSON array where each message has:

  • username: String - Name of the chatter
  • message: String - The chat message (may contain "thisgame++" or "thisgame--")
  • timestamp: String - ISO 8601 timestamp

The system will:

  1. Parse each message for vote patterns
  2. Match the timestamp to the game being played at that time
  3. Update the game's popularity score (+1 for ++, -1 for --)
  4. Store the chat log in the database

Bot Integration

For integrating external bots (e.g., for live voting and game notifications), see BOT_INTEGRATION.md for detailed documentation including:

  • Live voting API usage
  • WebSocket integration (recommended) for real-time game notifications
  • Webhook setup and verification (alternative to WebSocket)
  • Example implementations in Node.js and Go
  • Security best practices

Jackbox Player Count Fetcher

The scripts/ directory contains utilities for inspecting Jackbox game lobbies:

See scripts/README.md for detailed usage instructions.

Quick Start

Go version (recommended for automation):

cd scripts
go run get-player-count.go JYET

Browser version (easiest for manual testing):

  1. Open scripts/get-player-count.html in any browser
  2. Enter a 4-letter room code
  3. View real-time player count and lobby status

How it works:

  • Automates joining jackbox.tv through Chrome/Chromium
  • Captures WebSocket messages containing player data
  • Extracts actual player count from lobby state

These tools retrieve:

  • Actual player count (not just max capacity)
  • List of current players and their roles (host/player)
  • Game state and lobby status
  • Audience count

Note: Direct WebSocket connection is not possible without authentication, so the tools join through jackbox.tv to capture the data.

Database Schema

games

  • id, pack_name, title, min_players, max_players, length_minutes
  • has_audience, family_friendly, game_type, secondary_type
  • play_count, popularity_score, upvotes, downvotes, enabled, created_at

sessions

  • id, created_at, closed_at, is_active, notes

session_games

  • id, session_id, game_id, played_at, manually_added, status

chat_logs

  • id, session_id, chatter_name, message, timestamp, parsed_vote, message_hash

live_votes

  • id, session_id, game_id, username, vote_type, timestamp, created_at

webhooks

  • id, name, url, secret, events, enabled, created_at

webhook_logs

  • id, webhook_id, event_type, payload, response_status, error_message, created_at

Game Selection Algorithm

The picker uses the following logic:

  1. Filter eligible games:

    • Only enabled games
    • Match player count range (if specified)
    • Filter by drawing type (if specified)
    • Filter by length range (if specified)
    • Filter by family-friendly status (if specified)
  2. Apply repeat avoidance:

    • Get last 2 games from current session
    • Exclude those games from selection pool
    • This prevents immediate repeats and alternating patterns
  3. Random selection:

    • Pick a random game from remaining eligible games
    • Return game details and pool size

Docker Deployment

Using Docker Compose

The provided docker-compose.yml sets up both frontend and backend services:

# Build and start
docker-compose up -d

# View logs
docker-compose logs -f

# Stop services
docker-compose down

# Rebuild after changes
docker-compose up -d --build

Environment Variables

Set these in your .env file or docker-compose environment:

  • PORT - Backend server port (default: 5000)
  • NODE_ENV - Environment (production/development)
  • DB_PATH - Path to SQLite database file
  • JWT_SECRET - Secret key for JWT tokens
  • ADMIN_KEY - Admin authentication key

Data Persistence

The SQLite database is stored in backend/data/ and is persisted via Docker volumes. To backup your data, copy the backend/data/ directory.

Troubleshooting

Database not initializing

  • Ensure games-list.csv is in the root directory
  • Check backend logs: docker-compose logs backend
  • Manually delete backend/data/jackbox.db to force re-initialization

Can't login as admin

  • Verify your ADMIN_KEY environment variable is set
  • Check that the .env file is loaded correctly
  • Try restarting the backend service

Frontend can't reach backend

  • Verify both containers are running: docker-compose ps
  • Check network connectivity: docker-compose logs frontend
  • Ensure nginx.conf proxy settings are correct

Games not showing up

  • Check if games are enabled in the Manager
  • Verify filters aren't excluding all games
  • Check database has games: View in Manager page

License

MIT

Contributing

Feel free to submit issues and pull requests!

Description
No description provided
Readme 2 MiB
Languages
JavaScript 87.9%
HTML 4.9%
Shell 3.3%
Go 3%
CSS 0.7%
Other 0.2%