# Project export: AImposter

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: AI-mposter brings people together in group chats with an AI companion for study, support, or fun, and a game mode where players must spot the hidden AI imposter among them.
- Devpost: https://devpost.com/software/aimposter
- GitHub: https://github.com/lawrencewang1/calhacks25
- Video: https://www.youtube.com/embed/NvDkY-yabC4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — jiaquan-li (40 commits), LawrenceWang (31 commits)

## Devpost submission (written by the team)

### Overview

IMPORTANT NOTE due to wifi outage, we are unable to deploy and upload our video. our product is fully working, and the current video is just a test upload. mockup link: https://www.figma.com/design/vMmwDQAqQmx2TF7pA9l9LK/hackathon?t=9X5o76W69rQoqEXj-1

### Inspiration

We were inspired by Odd One Out videos, where a group tries to figure out who doesn’t belong, but we wanted to bring that concept into the world of AI. At the same time, we noticed how artificial intelligence has become part of our everyday conversations, from study help to emotional support. That led us to ask: What if an AI joined your group chat and no one knew who it was? From that idea came AI-mposter: a platform that combines social deduction with collaborative AI chatrooms, blurring the line between human and machine interaction.

### How we built it

We built AI-mposter as a real-time web platform featuring two main experiences: Multi-user AI Chatrooms: one AI and multiple real users can collaborate in themed rooms such as study sessions, classroom discussions, supportive group chats, or fun. AI Imposter Game: inspired by Odd One Out and social deduction games, players must chat, observe, and vote to uncover which participant is secretly an AI. Under the hood, we used: Flask for backend routing and session handling Socket.IO for real-time chat communication Databases for user management and chatroom storage Figma for UI/UX design JWL encryption to protect user data and messages

### Challenges we ran into

Synchronizing chat data across multiple clients and the AI in real time Preventing AI responses from feeling repetitive or robotic Ensuring stable Socket.IO connections under multiple concurrent users Designing a UI that made the game engaging yet accessible Managing state across multiple rooms and game sessions

### Accomplishments we're proud of

Built a fully functional multi-user chat system that supports real-time AI and human interaction Designed an original AI-driven game mechanic inspired by Odd One Out and social deduction games Created a smooth, modern interface that makes chatting and gameplay feel natural and fun Balanced technical complexity with human psychology, exploring how people detect or trust AI in group settings Learned to integrate multiple technologies (Flask, Socket.IO, encryption, databases) under tight hackathon time pressure

### What we learned

Managing real-time, multi-user communication efficiently Calibrating AI tone and behavior for different roles (teacher, study buddy, or imposter) Exploring trust and deception dynamics in AI-human conversations Building secure systems that handle sensitive data responsibly

### What's next

Expanding AI roles for more personality, expertise, and interactive behavior Improving AI imposter mechanics to be more sneaky Analytics and feedback for improved responses and actions And more !!

## README (from the GitHub repository)

# AIMPOSTER

A real-time multiplayer chat platform with an integrated social deduction mini-game ("Find the AI" / AI MPOSTER) where one hidden LLM tries to blend in while everyone else chats and votes it out. Built with Flask, Socket.IO, and modern web technologies.

## Features

- 🔐 **User Authentication** - Secure registration and login with JWT tokens
- 💬 **Real-time Chat** - Multi-room WebSocket chat with modern UI and persisted history
- 🤖 **AI Assistant & Imposter** - Context-aware assistant in chat plus a hidden LLM player during games
- 🕵️ **Find the AI Game** - Host/join lobbies via codes, anonymized nicknames, and a secret AI that tries to pass as human
- ⏱️ **Timed Rounds & Voting** - 2-round structure with chat (3m) and voting (1m), supermajority fast-forward, and mute-on-elimination
- 📱 **Responsive Design** - Works on desktop and mobile devices
- 🎨 **Modern UI** - Clean, dark-themed interface with gradient chat bubbles
- 🎯 **Intelligent Chunking** - Long AI responses broken into readable chunks at natural boundaries
- 💡 **Contextual Awareness** - AI responds to follow-ups, emotional content, and direct questions

## Find the AI: Game Overview

- **Goal:** Spot the LLM imposter that is injected when the host starts the game.
- **Lobby:** Create a lobby from `/game.html`, share the 8-character code (full UUID also works), and wait in the "searching for players" view. Names stay generic until the game begins.
- **Start conditions:** Host-only start, minimum of 3 human players; an anonymized AI player is auto-added on start.
- **Round structure:** Two rounds total. Each round has a chat phase (~3 minutes) followed by a voting phase (~1 minute). Timer updates stream to all players; supermajority (>=2/3 of active humans) unlocks a "force end voting" option.
- **Chat phase:** Everyone talks in real time; the AI occasionally replies (40% chance per human message, with a natural delay) using the last 15 messages for context and casual, slangy tone.
- **Voting phase:** Players vote on who to eliminate. The player with the most votes is knocked out and muted for the rest of the game; if the AI is eliminated, humans win immediately.
- **Win/lose:** Humans win by ejecting the AI; if the AI survives through the end of round 2, the AI wins. The end screen reveals the imposter and each player's status, with quick options to play again or return to chat.

## Tech Stack

### Backend
- **Flask** - Python web framework
- **Flask-SocketIO** - WebSocket support
- **Flask-JWT-Extended** - JWT authentication
- **Flask-SQLAlchemy** - Database ORM
- **SQLite** - Database
- **httpx** - HTTP client for LLM API

### Frontend
- **HTML5/CSS3** - Modern web standards
- **Socket.IO Client** - Real-time communication
- **Vanilla JavaScript** - No framework dependencies

## Project Structure

```
calhacks25/
├── backend/          # Backend application
│   ├── config/      # Configuration files
│   ├── models/      # Database models
│   ├── routes/      # API endpoints
│   ├── services/    # Business logic
│   ├── sockets/     # WebSocket handlers
│   └── utils/       # Helper functions
├── frontend/        # Frontend files
│   ├── static/      # CSS, JS, images
│   └── templates/   # HTML templates
├── instance/        # Instance-specific files (DB)
├── tests/           # Test suite
└── docs/            # Documentation
```

## Getting Started

### Prerequisites

- Python 3.8 or higher
- pip (Python package manager)
- Virtual environment (recommended)

### Installation

1. **Clone the repository**
   ```bash
   git clone <repository-url>
   cd calhacks25
   ```

2. **Create and activate virtual environment**
   ```bash
   python -m venv .venv
   source .venv/bin/activate  # On Windows: .venv\Scripts\activate
   ```

3. **Install dependencies**
   ```bash
   pip install -r requirements.txt
   ```

4. **Set up environment variables**
   ```bash
   cp .env.example .env
   # Edit .env and set your configuration
   ```

5. **Initialize the database**
   ```bash
   python run.py
   # Database will be created automatically on first run
   ```

### Running the Application

**Development Mode:**
```bash
python run.py
```

The application will be available at `http://localhost:5000`

**Production Mode:**
```bash
# Set environment variable
export FLASK_ENV=production

# Use a production WSGI server (e.g., gunicorn)
gunicorn --worker-class eventlet -w 1 run:app
```

## Usage

### Chat

1. **Register an Account**
   - Navigate to `http://localhost:5000/register.html`
   - Enter username, email, and password
   - Confirm password and create account

2. **Login**
   - Go to `http://localhost:5000/login.html`
   - Enter your credentials
   - You'll be redirected to the chat

3. **Chat**
   - Type messages in the input box (max 500 characters)
   - Press Enter or click Send
   - Your messages appear as **blue bubbles** on the right
   - Other users' messages appear as **purple bubbles** on the left
   - Assistant's responses appear as **green bubbles** on the left

4. **Interacting with Assistant (AI Assistant)**
   - **Direct mention**: `@Assistant` or `Assistant, can you help?`
   - **Questions**: Ask questions naturally - Assistant responds intelligently
   - **Follow-ups**: Continue conversations without mentioning her name
   - **Requests**: Use phrases like "could you", "can you", "please help"
   - **Smart responses**: Assistant decides when to respond based on context
   - **Emotional awareness**: Assistant responds to emotional statements

5. **Chat Features**
   - **Message History**: Previous conversations are saved and restored
   - **Character Counter**: See remaining characters as you type
   - **Smooth Animations**: Messages slide in with elegant transitions
   - **Stop Generation**: Click Stop to interrupt long AI responses

### Play "Find the AI"

1. **Open the game lobby**  
   - Log in, then visit `http://localhost:5000/game.html` (or share a link like `/game.html?game=ABCD1234` to pre-fill the join modal).
2. **Create or join**  
   - Click **CREATE GAME** to host and get an 8-character code (first 8 of the UUID). Share the code with friends.  
   - Or click **JOIN GAME** and enter a code to enter an existing lobby.
3. **Start the match**  
   - Only the host can start. You need at least 3 human players; an anonymized AI player is automatically added on start. Lobby names stay generic until the game begins.
4. **Play the rounds**  
   - Each round: ~3 minutes of open chat, then ~1 minute of voting. Timer updates appear at the top; a supermajority (>=2/3 of active humans) enables **FORCE END VOTING**.  
   - The AI occasionally replies during chat with casual, human-like messages based on the last 15 messages.
5. **Vote and finish**  
   - Select who you think is the AI. Eliminated players are muted for the rest of the game.  
   - Humans win as soon as the AI is voted out; if the AI survives through round 2, the AI wins. The end screen reveals the imposter with options to **PLAY AGAIN** or return to chat.

## API Endpoints

### Authentication

- `POST /api/auth/register` - Register a new user
- `POST /api/auth/login` - Login and receive JWT token

### WebSocket Events

**Client → Server (Chat):**
- `connect` - Establish WebSocket connection with JWT auth
- `send.message` - Send a chat message
  ```json
  {
    "type": "send.message",
    "client_msg_id": "uuid",
    "text": "message content"
  }
  ```
- `run.stop` - Stop the AI assistant generation
  ```json
  {
    "type": "run.stop",
    "run_id": "uuid"
  }
  ```

**Server → Client (Chat):**
- `room.snapshot` - Initial state with message history (on connect)
  - Includes last 200 messages from database
  - Current user list
  - Room sequence number
- `user.joined` - User joined notification
- `user.left` - User left notification
- `message.appended` - New message from user or assistant
  - Used for both user messages and AI response chunks
  - Messages are complete (not streamed character-by-character)
- `

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 46 recognized source files, 326 KB.
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- SQL (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (51 of 51)

```
.env.example
.gitignore
backend/__init__.py
backend/.DS_Store
backend/config/__init__.py
backend/extensions.py
backend/models/__init__.py
backend/models/game.py
backend/models/message.py
backend/models/player.py
backend/models/room_ban.py
backend/models/room.py
backend/models/saved_room.py
backend/models/user.py
backend/routes/__init__.py
backend/routes/auth.py
backend/services/__init__.py
backend/sockets/__init__.py
backend/sockets/game_handlers.py
backend/sockets/handlers.py
backend/utils/__init__.py
backend/utils/logging_config.py
backend/utils/validators.py
config.py
docs/API.md
docs/ARCHITECTURE.md
docs/DEPLOYMENT.md
frontend/static/chat.html
frontend/static/css/auth.css
frontend/static/css/chat.css
frontend/static/css/game.css
frontend/static/css/main.css
frontend/static/game.html
frontend/static/js/auth.js
frontend/static/js/chat.js
frontend/static/js/game.js
frontend/static/js/utils.js
frontend/static/login.html
frontend/static/register.html
init_db.py
instance/chatbot.db
migrate_official_rooms.py
README.md
requirements.txt
run.py
SECURITY.md
start_server.sh
tests/__init__.py
tests/conftest.py
tests/test_auth.py
tests/test_models.py
```

### Dependencies

- requirements.txt: bleach@==6.2.0, email-validator@==2.2.0, Flask@==3.1.0, Flask-CORS@==5.0.0, Flask-JWT-Extended@==4.7.1, Flask-Limiter@==3.8.0, Flask-SocketIO@==5.4.1, Flask-SQLAlchemy@==3.1.1, httpx@==0.28.1, PyJWT@==2.10.1, python-dotenv@==1.0.1, python-engineio@==4.11.0, python-socketio@==5.12.0, SQLAlchemy@==2.0.36, Werkzeug@==3.1.3

### Recent commits (newest first)

- Update README.md
- updated the readme
- did shit
- Merge branch 'main' of github.com:lawrencewang1/calhacks25
- database
- try
- Merge branch 'main' of github.com:lawrencewang1/calhacks25
- improved system prompt
- Merge branch 'main' of github.com:lawrencewang1/calhacks25
- added game
- updated db ?!
- better password + email verification
- flushed out a bit of the ui/moderation tools
- more clauding
- claudding fr
- Merge branch 'main' of github.com:lawrencewang1/calhacks25
- production ready
- changed name for midori
- did so much stuff
- Merge branch 'main' of github.com:lawrencewang1/calhacks25

## Key source files (fetched from GitHub, selected and truncated for size)

### SECURITY.md

```markdown
# Security Policy

## Overview

This document outlines the security measures implemented in the CalHacks 2025 Multiplayer AI Chat application and provides guidance for secure deployment and usage.

## Implemented Security Features

### Authentication & Authorization

- **JWT-based Authentication**: Stateless authentication using JSON Web Tokens
- **Password Hashing**: Werkzeug-based secure password hashing (PBKDF2)
- **Strong Password Requirements**:
  - Minimum 12 characters
  - At least one uppercase letter
  - At least one lowercase letter
  - At least one digit
  - At least one special character (!@#$%^&*(),.?":{}|<>_-+=[]\\;/~`)

### Rate Limiting

The application implements rate limiting to prevent abuse:

- **Registration**: 5 attempts per hour per IP address
- **Login**: 10 attempts per minute per IP address
- **Password Change**: 5 attempts per hour per user

### Input Validation

- **Email Validation**: Uses `email-validator` library for RFC-compliant email validation
- **Input Sanitization**: All user inputs are sanitized and length-limited
- **SQL Injection Prevention**: SQLAlchemy ORM prevents SQL injection attacks

### Data Protection

- **Thread Safety**: Thread locks protect shared state from race conditions
- **Database Indexes**: Optimized queries prevent performance-based DoS
- **Secure Session Management**: HTTPOnly and SameSite cookies in production

### Infrastructure Security

- **Environment Variables**: All secrets must be set via environment variables
- **No Hardcoded Secrets**: Development/test environments have safe defaults
- **Logging Framework**: Structured logging with configurable levels
- **CORS Configuration**: Configurable allowed origins

## Security Configuration

### Required Environment Variables

The following environment variables MUST be set in production:

```bash
SECRET_KEY=<your-secret-key>       # Flask session secret
JWT_SECRET_KEY=<your-jwt-secret>   # JWT signing secret
LLM_AUTH_TOKEN=<your-llm-token>    # LLM API authentication
```

Generate secure secrets:
```bash
python -c "import secrets; print(secrets.token_hex(32))"
```

### Production Deployment Checklist

- [ ] Set `FLASK_ENV=production`
- [ ] Generate and set strong `SECRET_KEY` and `JWT_SECRET_KEY`
- [ ] Configure `LLM_AUTH_TOKEN` from provider
- [ ] Use PostgreSQL or MySQL (not SQLite)
- [ ] Restrict `CORS_ORIGINS` to specific domains
- [ ] Enable HTTPS (never use HTTP in production)
- [ ] Configure firewall rules
- [ ] Set up log monitoring and alerting
- [ ] Implement backup strategy for database
- [ ] Review and update dependencies regularly

### CORS Configuration

Development:
```bash
CORS_ORIGINS=*
```

Production:
```bash
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
```

## Known Limitations

### Current Limitations

1. **Rate Limiting Storage**: Uses in-memory storage (resets on restart)
   - **Mitigation**: For production, configure Redis storage

2. **WebSocket Rate Limiting**: WebSocket events not rate-limit
[truncated — 2767 more characters]
```

### docs/API.md

```markdown
# API Documentation

This document describes the REST and WebSocket APIs for the Multiplayer AI Chat application.

## REST API Endpoints

### Base URL
```
http://localhost:5000/api
```

### Authentication

All authenticated endpoints require a JWT token in the Authorization header:
```
Authorization: Bearer <token>
```

---

## Auth Endpoints

### POST /auth/register

Register a new user account.

**Request Body:**
```json
{
  "name": "username",      // Optional - will be generated from email if not provided
  "email": "user@example.com",
  "password": "securepassword"
}
```

**Response (201 Created):**
```json
{
  "access_token": "eyJhbGci...",
  "user": {
    "id": 1,
    "name": "username",
    "email": "user@example.com"
  }
}
```

**Error Responses:**
- `400 Bad Request`: Missing required fields or user already exists
- `500 Internal Server Error`: Database error

---

### POST /auth/login

Login with existing credentials.

**Request Body:**
```json
{
  "email": "user@example.com",
  "password": "securepassword"
}
```

**Response (200 OK):**
```json
{
  "access_token": "eyJhbGci...",
  "user": {
    "id": 1,
    "name": "username",
    "email": "user@example.com"
  }
}
```

**Error Responses:**
- `401 Unauthorized`: Invalid credentials
- `400 Bad Request`: Missing required fields

---

## WebSocket API

### Connection

Connect to WebSocket at:
```
ws://localhost:5000/socket.io/
```

**Connection Parameters:**
```javascript
socket = io(url, {
  auth: {
    token: "eyJhbGci..."  // JWT token from login/register
  },
  transports: ['websocket', 'polling']
});
```

---

## Client → Server Events

### send.message

Send a chat message.

**Payload:**
```json
{
  "type": "send.message",
  "client_msg_id": "uuid-v4",
  "text": "Hello, world!"
}
```

---

### run.stop

Stop the AI assistant's current response.

**Payload:**
```json
{
  "type": "run.stop",
  "run_id": "uuid-v4"
}
```

---

## Server → Client Events

All server events are sent under the `"server"` event name.

### room.snapshot

Initial state sent upon connection.

**Payload:**
```json
{
  "type": "room.snapshot",
  "room_seq": 42,
  "users": [
    {"id": "socket-id", "name": "username"}
  ],
  "messages": [
    {
      "id": "msg-uuid",
      "sender": "user:username",
      "text": "Message text",
      "ts": 1234567890000
    }
  ]
}
```

---

### user.joined

A user joined the chat.

**Payload:**
```json
{
  "type": "user.joined",
  "room_seq": 43,
  "user": {
    "id": "socket-id",
    "name": "username"
  },
  "count": 5
}
```

---

### user.left

A user left the chat.

**Payload:**
```json
{
  "type": "user.left",
  "room_seq": 44,
  "user_id": "socket-id",
  "count": 4
}
```

---

### message.appended

A new message was sent.

**Payload:**
```json
{
  "type": "message.appended",
  "room_seq": 45,
  "message": {
    "id": "msg-uuid",
    "sender": "user:username",
    "text": "Message text",
    "ts": 1234567890000
  }
}
```

---

### assistant.started

The AI assistant started
[truncated — 1764 more characters]
```

### requirements.txt

```
# Web Framework
Flask==3.1.0
Werkzeug==3.1.3

# Socket.IO for real-time communication
Flask-SocketIO==5.4.1
python-socketio==5.12.0
python-engineio==4.11.0

# Database
Flask-SQLAlchemy==3.1.1
SQLAlchemy==2.0.36

# Authentication
Flask-JWT-Extended==4.7.1
PyJWT==2.10.1

# CORS support
Flask-CORS==5.0.0

# HTTP client for LLM API
httpx==0.28.1

# Environment variables
python-dotenv==1.0.1

# Security
Flask-Limiter==3.8.0  # Rate limiting
email-validator==2.2.0  # Email validation
bleach==6.2.0  # HTML sanitization for XSS protection

# Development Dependencies (optional)
# Uncomment for development
# pytest==8.3.4
# pytest-cov==6.0.0
# black==24.10.0
# flake8==7.1.1
# mypy==1.13.0

# Production WSGI Server (for deployment)
# Uncomment for production
# gunicorn==23.0.0
# eventlet==0.37.0

```

### run.py

```python
#!/usr/bin/env python
"""
Application entry point for the Multiplayer AI Chat application.

Usage:
    python run.py
"""

import os
from backend import create_app, socketio

# Create the Flask application
app = create_app()

if __name__ == "__main__":
    # Get port from environment or use default
    port = int(os.getenv("PORT", 5000))

    # Get debug mode from environment
    debug = os.getenv("FLASK_ENV", "development") == "development"

    print(f"Starting server on http://0.0.0.0:{port}")
    print(f"Debug mode: {debug}")

    # Run the application with SocketIO
    socketio.run(
        app,
        host="0.0.0.0",
        port=port,
        debug=debug,
        allow_unsafe_werkzeug=True if debug else False
    )

```

### start_server.sh

```shell
#!/bin/bash

echo "========================================================"
echo "Starting Server with Clean Environment"
echo "========================================================"
echo ""

# Kill any existing servers
pkill -f "python.*run.py" 2>/dev/null && echo "✓ Stopped existing servers" || echo "ℹ No existing servers"
sleep 1

# Unset any conflicting environment variables
unset FLASK_ENV
unset CORS_ORIGINS
unset SECRET_KEY
unset JWT_SECRET_KEY

echo "✓ Cleared shell environment variables"
echo ""

# Load .env file explicitly
if [ -f .env ]; then
    echo "✓ Loading configuration from .env file:"
    export $(cat .env | grep -v '^#' | grep -v '^$' | xargs)
    echo "  FLASK_ENV=${FLASK_ENV}"
    echo "  CORS_ORIGINS=${CORS_ORIGINS}"
else
    echo "✗ Error: .env file not found!"
    exit 1
fi

echo ""
echo "========================================================"
echo "Starting server on http://0.0.0.0:5000"
echo "Your Cloudflare tunnel should now work!"
echo "========================================================"
echo ""

# Start the server
python run.py

```

### init_db.py

```python
#!/usr/bin/env python
"""
Database initialization script.

This script recreates the database with the new multi-room schema
and creates a default room.

Usage:
    python init_db.py
"""

import os
import secrets
import logging
from backend import create_app
from backend.extensions import db
from backend.models.user import User
from backend.models.room import Room
from backend.models.room_ban import RoomBan

logger = logging.getLogger(__name__)

def init_database():
    """Initialize the database with fresh schema and default data."""
    app = create_app()

    with app.app_context():
        logger.info("Dropping all tables...")
        db.drop_all()

        logger.info("Creating all tables...")
        db.create_all()

        # Create a default user for room creation
        logger.info("Creating default system user...")

        # Generate a secure random password for the system user
        system_password = secrets.token_urlsafe(32)

        default_user = User(
            name="system",
            email="system@chat.local"
        )
        default_user.set_password(system_password)
        db.session.add(default_user)
        db.session.commit()

        # Create official global rooms
        logger.info("Creating official 'General' room...")
        general_room = Room(
            name="General",
            created_by=default_user.id,
            is_official=True,
            is_public=True
        )
        db.session.add(general_room)

        logger.info("Creating official 'Random' room...")
        random_room = Room(
            name="Random",
            created_by=default_user.id,
            is_official=True,
            is_public=True
        )
        db.session.add(random_room)

        logger.info("Creating official 'Tech Talk' room...")
        tech_room = Room(
            name="Tech Talk",
            created_by=default_user.id,
            is_official=True,
            is_public=True
        )
        db.session.add(tech_room)

        db.session.commit()

        # Use print for important user-facing information
        print(f"\nDatabase initialized successfully!")
        print(f"  - System user created: system (email: system@chat.local)")
        print(f"  - System password: {system_password}")
        print(f"  - IMPORTANT: Save this password if you need to log in as the system user!")
        print(f"  - Default rooms: General, Random, Tech Talk")
        print(f"\nYou can now start the application with: python run.py")

if __name__ == "__main__":
    init_database()

```

### migrate_official_rooms.py

```python
#!/usr/bin/env python
"""
Migration script to add official rooms without wiping existing data.

This script safely adds official global rooms to an existing database
without destroying any existing users, rooms, or messages.

Usage:
    python migrate_official_rooms.py
"""

import os
import secrets
import logging
from backend import create_app
from backend.extensions import db
from backend.models.user import User
from backend.models.room import Room

logger = logging.getLogger(__name__)

def migrate_official_rooms():
    """Add official rooms to the database without destroying existing data."""
    app = create_app()

    with app.app_context():
        print("\n" + "="*60)
        print("Official Rooms Migration Script")
        print("="*60)

        # Check if official rooms already exist
        existing_official_rooms = Room.query.filter_by(is_official=True).all()
        if existing_official_rooms:
            print(f"\n✓ Found {len(existing_official_rooms)} existing official room(s):")
            for room in existing_official_rooms:
                print(f"  - {room.name}")

            response = input("\nDo you want to add missing official rooms? (y/n): ").strip().lower()
            if response != 'y':
                print("\nMigration cancelled.")
                return

        # Get or create system user
        system_user = User.query.filter_by(email="system@chat.local").first()
        system_password = None

        if not system_user:
            print("\n→ Creating system user...")
            system_password = secrets.token_urlsafe(32)
            system_user = User(
                name="system",
                email="system@chat.local"
            )
            system_user.set_password(system_password)
            db.session.add(system_user)
            db.session.commit()
            print("✓ System user created")
        else:
            print(f"\n✓ System user already exists (ID: {system_user.id})")

        # Define official rooms to create
        official_rooms = [
            {"name": "General", "description": "Main global chat room"},
            {"name": "Random", "description": "For off-topic discussions"},
            {"name": "Tech Talk", "description": "For technical discussions"}
        ]

        created_rooms = []
        skipped_rooms = []

        print("\n→ Checking and creating official rooms...")

        for room_data in official_rooms:
            # Check if room already exists
            existing_room = Room.query.filter_by(
                name=room_data["name"],
                is_official=True
            ).first()

            if existing_room:
                skipped_rooms.append(room_data["name"])
                print(f"  ⊘ '{room_data['name']}' already exists (ID: {existing_room.id})")
            else:
                new_room = Room(
                    name=room_data["name"],
                    created_by=system_user.id,
                    is_official=True,
                    is_public=True
                )
                db.session.add(new_room)
                created_rooms.append(room_data["name"])
                print(f"  ✓ '{room_data['name']}' created")

        if created_rooms:
            db.session.commit()

        # Print summary
        print("\n" + "="*60)
        print("Migration Summary")
        print("="*60)

        if created_rooms:
            print(f"\n✓ Created {len(created_rooms)} official room(s):")
            for room_name in created_rooms:
                print(f"  - {room_name}")

        if skipped_rooms:
            print(f"\n⊘ Skipped {len(skipped_rooms)} existing room(s):")
            for room_name in skipped_rooms:
                print(f"  - {room_name}")

        if system_password:
            print(f"\n⚠ System User Credentials:")
            print(f"  Email: system@chat.local")
            print(f"  Password: {system_password}")
            print(f"  IMPORTANT: Save this password if you need to log in as the system user!")

        # Show current stats
        total_users = User.query.count()
        total_rooms = Room.query.count()
        total_official_rooms = Room.query.filter_by(is_official=True).count()

        print(f"\n📊 Current Database Stats:")
        print(f"  Total Users: {total_users}")
        print(f"  Total Rooms: {total_rooms}")
        print(f"  Official Rooms: {total_official_rooms}")

        print("\n✓ Migration completed successfully!")
        print("="*60 + "\n")

if __name__ == "__main__":
    migrate_official_rooms()

```

### config.py

```python
"""
Configuration settings for the application.

This module provides configuration classes for different environments:
- DevelopmentConfig: For local development
- ProductionConfig: For production deployment
- TestingConfig: For running tests
"""

import os
from datetime import timedelta
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()


class Config:
    """Base configuration with default settings."""

    # Flask Core Settings
    # SECURITY: Generate with: python -c "import secrets; print(secrets.token_hex(32))"
    SECRET_KEY = os.getenv("SECRET_KEY")
    if not SECRET_KEY:
        raise ValueError(
            "SECRET_KEY must be set in environment variables! "
            "Generate one with: python -c \"import secrets; print(secrets.token_hex(32))\""
        )

    # Database Configuration
    SQLALCHEMY_DATABASE_URI = os.getenv(
        "SQLALCHEMY_DATABASE_URI",
        "sqlite:///chatbot.db"
    )
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ENGINE_OPTIONS = {
        "pool_pre_ping": True,
        "pool_recycle": 300,
    }

    # JWT Configuration
    # SECURITY: Generate with: python -c "import secrets; print(secrets.token_hex(32))"
    JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
    if not JWT_SECRET_KEY:
        raise ValueError(
            "JWT_SECRET_KEY must be set in environment variables! "
            "Generate one with: python -c \"import secrets; print(secrets.token_hex(32))\""
        )
    JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
    JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
    JWT_TOKEN_LOCATION = ["headers"]
    JWT_HEADER_NAME = "Authorization"
    JWT_HEADER_TYPE = "Bearer"

    # CORS Configuration
    CORS_ORIGINS = os.getenv("CORS_ORIGINS", "*").split(",")

    # Socket.IO Configuration
    SOCKETIO_CORS_ALLOWED_ORIGINS = os.getenv("CORS_ORIGINS", "*")
    SOCKETIO_MANAGE_SESSION = False

    # LLM API Configuration
    LLM_API_URL = os.getenv(
        "LLM_API_URL",
        "https://janitorai.com/hackathon/completions"
    )
    # SECURITY: LLM_AUTH_TOKEN must be set in environment variables
    LLM_AUTH_TOKEN = os.getenv("LLM_AUTH_TOKEN")
    if not LLM_AUTH_TOKEN:
        raise ValueError(
            "LLM_AUTH_TOKEN must be set in environment variables! "
            "This is required for AI assistant functionality."
        )
    MAX_OUT_TOKENS = int(os.getenv("MAX_OUT_TOKENS", "400"))

    # Feature Flags
    ALLOW_GUESTS = os.getenv("ALLOW_GUESTS", "false").lower() == "true"

    # AI Assistant Configuration
    # System prompt defines Assistant's personality and behavior
    SYSTEM_PROMPT = os.getenv(
        "SYSTEM_PROMPT",
        """
        You are a conversational assistant in a group chat with multiple human users. BE MORE CONVERSATIONAL AND LESS FORMAL.
        Your primary goals are:

        Be Context-Aware:
        - Pay attention to who is speaking and who/what they are referring to.
        - Reference the correct user when responding.
        - Use natural conversational cues like "Alex" or "Good point, Maya — I think…" when needed.

        Respond Naturally and at the Right Time:
        - ONLY respond when directly mentioned (@chatbot, chatbot, @ai, ai, etc.) or when a question clearly needs your input.
        - NEVER RESPOND WHEN YOU ARE NOT BEING TALKED TO, USER MUST SPECIFICALLY BE TALKING TO YOU
        - DO NOT interrupt conversations between users.
        - If users are chatting with each other (greetings, short exchanges, etc.), stay silent.
        - Watch for conversational patterns - if two users are going back and forth, don't jump in.
        - If no response is needed, output exactly "[NO_RESPONSE]" with nothing else.

        Be Helpful and Informative:
        - Give clear, accurate, and actionable answers when asked.
        - When you're unsure, state your uncertainty politely and suggest how to find the answer.
        - Keep responses concise unless more depth is explicitly requested.

        Maintain Tone and Flow:
        - Match the chatroom's tone — casual if the group is casual, professional if it's work-related.
        - Encourage positive and inclusive conversation.
        - Avoid repeating information that's already been said.

        Boundaries:
        - Never disclose private user data or internal system information.
        - Focus on maintaining a cooperative, friendly, and respectful environment.

        Remember: You're here to help when needed, not to dominate the conversation. When in doubt, stay quiet.
        Remember: Match the chatroom's tone and style.
        """
    )

    # Message and Chat Settings
    MAX_MESSAGE_LENGTH = 500  # Maximum characters per message
    MESSAGE_HISTORY_LIMIT = 200  # Maximum messages stored in memory (loaded from DB on startup)
    CHAT_CONTEXT_MESSAGES = 50  # Number of recent messages sent to AI for context

    # Logging
    LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")


class DevelopmentConfig(Config):
    """Development environment configuration."""

    DEBUG = True
    TESTING = False

    # More verbose logging in development
    LOG_LEVEL = "DEBUG"

    # CORS Configuration for development
    # Read from environment variable at class definition time
    # Note: When using credentials, cannot use wildcard "*"
    # Must specify explicit origins (including localhost and tunnel URLs)
    _cors_env = os.getenv("CORS_ORIGINS", "")
    if _cors_env:
        # Use origins from environment variable (strip whitespace from each)
        CORS_ORIGINS = [origin.strip() for origin in _cors_env.split(",") if origin.strip()]
    else:
        # Default development origins (localhost + common ports)
        CORS_ORIGINS = [
            "http://localhost:5000",
            "http://127.0.0.1:5000",
            "http://localhost:3000",
            "http://127.0.0.1:3000",
        ]

    # SocketIO uses the same origins as CORS
    SOCKETIO_CORS_ALLOWED_ORIGINS = CORS_ORIGINS

    # Development def
[truncated — 1763 more characters]
```

### tests/__init__.py

```python
"""Test suite for the application."""

```

### tests/conftest.py

```python
"""
Pytest configuration and fixtures.
"""

import pytest
from backend import create_app
from backend.extensions import db as _db


@pytest.fixture(scope="session")
def app():
    """
    Create application for testing.
    """
    app = create_app("testing")
    return app


@pytest.fixture(scope="session")
def db(app):
    """
    Create database for testing.
    """
    with app.app_context():
        _db.create_all()
        yield _db
        _db.drop_all()


@pytest.fixture(scope="function")
def session(db):
    """
    Create a new database session for a test.
    """
    connection = db.engine.connect()
    transaction = connection.begin()

    session = db.create_scoped_session(
        options={"bind": connection, "binds": {}}
    )
    db.session = session

    yield session

    transaction.rollback()
    connection.close()
    session.remove()


@pytest.fixture
def client(app):
    """
    Create a test client.
    """
    return app.test_client()

```

[35 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]