docs: comprehensive documentation overhaul

Rewrite README.md and all setup guides to reflect the current native
GraphQL WebSocket architecture (replacing stale headless Chrome/WebSocket
interception descriptions). Add new docs/IRC.md with complete IRC command
reference, vote syntax, and ticker symbol table.

Archive pre-edit docs to docs/archive/setup-backup-2026-05-10/ and move
historical development notes from docs/ root into docs/archive/.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-05-12 22:01:25 -04:00
parent 4188ae29af
commit c88b75f30d
57 changed files with 4530 additions and 1994 deletions

369
README.md
View File

@@ -1,47 +1,55 @@
> [!IMPORTANT]
> [!IMPORTANT]
> This project was developed entirely with AI coding assistance (Claude Opus 4.6 via Cursor IDE) and has not undergone rigorous review. It is provided as-is and may require adjustments for other environments.
# Kosmi-IRC Relay via Matterbridge
A Matterbridge plugin that bridges Kosmi chat rooms with IRC channels, enabling bidirectional message relay.
A Matterbridge-based bridge that relays messages bidirectionally between Kosmi chat rooms and IRC channels, with optional Jackbox Game Picker integration for live voting and game announcements.
## Features
- Real-time message relay between Kosmi and IRC
- ✅ Headless Chrome automation for reliable Kosmi connection
- ✅ WebSocket interception using Chrome DevTools Protocol
- ✅ Anonymous Kosmi access (no authentication required)
- ✅ Message formatting with source indicators
- ✅ Automatic reconnection handling
- ✅ Support for any Kosmi room via URL configuration
- Real-time bidirectional message relay between Kosmi and IRC
- Native GraphQL-over-WebSocket connection to Kosmi (no browser required for basic use)
- Anonymous or authenticated Kosmi access (email/password login with token caching)
- Jackbox Game Picker integration: live vote detection, game announcements, room code images
- IRC commands: `!kreconnect`, `!jreconnect`, `!reconnect`, `!votes`
- Ticker-symbol voting for specific games (e.g. `QPL3++`, `TMP2--`)
- SIGUSR1 mute toggle for Jackbox announcements
- Automatic reconnection on connection loss
- Docker deployment with persistent token cache
## Architecture
This implementation extends Matterbridge with a custom Kosmi bridge that:
This project is a trimmed fork of [Matterbridge](https://github.com/42wim/matterbridge) with two registered bridges: **Kosmi** and **IRC**. The Kosmi bridge connects directly to `wss://engine.kosmi.io/gql-ws` using the `graphql-transport-ws` protocol -- no headless browser is needed for the primary connection path.
1. Launches a headless Chrome instance using `chromedp`
2. Navigates to the Kosmi room and injects a WebSocket interceptor **before page load**
3. Captures GraphQL WebSocket messages (`wss://engine.kosmi.io/gql-ws`) from the page
4. Relays messages bidirectionally with proper formatting:
- **Kosmi → IRC**: `[Kosmi] <username> message`
- **IRC → Kosmi**: `[IRC] <username> message`
### How It Works
### Why Headless Chrome?
1. **Anonymous access** (default): The bridge calls Kosmi's `anonLogin` HTTP mutation to obtain a JWT, then opens a native WebSocket connection.
2. **Authenticated access** (optional): If `Email` and `Password` are configured, headless Chrome (chromedp) performs a one-time browser login to extract a JWT. The token is cached locally and reused across restarts.
3. **Room subscription**: After connecting, the bridge subscribes to `newMessage` events via GraphQL and joins the configured room.
4. **Message relay**: Incoming Kosmi messages are forwarded to IRC (and vice versa) through the Matterbridge gateway, with configurable nick formatting.
Kosmi's WebSocket API requires browser session cookies and context that are difficult to replicate with a native WebSocket client. Using headless Chrome automation ensures:
- ✅ Automatic session management
- ✅ Proper cookie handling
- ✅ Reliable WebSocket connection
- ✅ No authentication complexity
### Message Flow
```
IRC User --> IRC Server --> Matterbridge Gateway --> Kosmi GraphQL WS --> Kosmi Room
Kosmi User --> Kosmi Room --> GraphQL subscription --> Matterbridge Gateway --> IRC Channel
```
### Jackbox Integration (Optional)
When enabled, the relay connects to a Jackbox Game Picker API via WebSocket (or webhook fallback) to:
- Detect `thisgame++`/`thisgame--` votes and ticker-symbol votes in chat
- Announce upcoming games with room codes (optionally as uploaded images)
- Respond to `!votes` queries with current vote tallies
## Installation
### Option 1: Docker (Recommended) 🐳
The easiest way to run the bridge:
### Option 1: Docker (Recommended)
```bash
# 1. Edit configuration
# 1. Copy and edit configuration
cp matterbridge.toml.example matterbridge.toml
nano matterbridge.toml
# 2. Build and run
@@ -51,45 +59,47 @@ docker-compose up -d
docker-compose logs -f
```
**See**: `DOCKER_QUICKSTART.md` for 5-minute setup guide
See [DOCKER_QUICKSTART.md](docs/setup/DOCKER_QUICKSTART.md) for a 5-minute setup guide.
### Option 2: Build from Source
#### Prerequisites
- Go 1.21 or higher
- Chrome or Chromium browser installed
- Go 1.23 or higher
- Chrome/Chromium (only required if using email/password authentication)
- Access to an IRC server
- A Kosmi room URL
#### Building
```bash
# Clone the repository
git clone <repository-url>
cd irc-kosmi-relay
# Download dependencies
go mod download
# Build the bridge
go build -o matterbridge
go build -o matterbridge .
```
## Configuration
Edit `matterbridge.toml` to configure your bridge:
Copy `matterbridge.toml.example` to `matterbridge.toml` and edit it. The key sections:
```toml
# Kosmi configuration
[kosmi.hyperspaceout]
RoomURL="https://app.kosmi.io/room/@hyperspaceout"
RoomURL="https://app.kosmi.io/room/@yourroom"
# Optional: Email/password for authenticated access
# Requires Chrome/Chromium for browser automation
Email=""
Password=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
# IRC configuration
[irc.libera]
Server="irc.libera.chat:6667"
[irc.zeronode]
Server="irc.libera.chat:6697"
Nick="kosmi-relay"
UseTLS=false
UseTLS=true
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
# Gateway to connect Kosmi and IRC
[[gateway]]
@@ -101,22 +111,58 @@ account="kosmi.hyperspaceout"
channel="main"
[[gateway.inout]]
account="irc.libera"
account="irc.zeronode"
channel="#your-channel"
# Jackbox integration (optional)
[jackbox]
Enabled=false
APIURL="https://your-jackbox-api.example.com"
AdminPassword=""
UseWebSocket=true
EnableRoomCodeImage=true
RoomCodeImageDelay=28
RoomCodePlaintextDelay=22
```
### Configuration Options
### Configuration Reference
#### Kosmi Settings
- `RoomURL` (required): Full URL to the Kosmi room
- Format: `https://app.kosmi.io/room/@roomname` or `https://app.kosmi.io/room/roomid`
- `Server` (optional): WebSocket endpoint (default: `wss://engine.kosmi.io/gql-ws`)
- `Debug` (optional): Enable debug logging
| Key | Required | Description |
|-----|----------|-------------|
| `RoomURL` | Yes | Full URL to the Kosmi room |
| `Email` | No | Email for authenticated access |
| `Password` | No | Password for authenticated access |
| `RemoteNickFormat` | No | Format for nicks from other bridges |
#### IRC Settings
See [Matterbridge IRC documentation](https://github.com/42wim/matterbridge/wiki/Section-IRC-(basic)) for full IRC configuration options.
| Key | Required | Description |
|-----|----------|-------------|
| `Server` | Yes | IRC server address with port |
| `Nick` | Yes | Bot's IRC nickname |
| `UseTLS` | No | Enable TLS (recommended) |
| `SkipTLSVerify` | No | Skip TLS cert verification |
| `NickServNick` | No | NickServ service nick |
| `NickServPassword` | No | NickServ password |
| `UseSASL` | No | Use SASL authentication |
| `RemoteNickFormat` | No | Format for nicks from other bridges |
| `Channels` | No | Channels to auto-join |
#### Jackbox Settings
| Key | Required | Description |
|-----|----------|-------------|
| `Enabled` | Yes | Enable/disable Jackbox integration |
| `APIURL` | Yes | Jackbox Game Picker API URL |
| `AdminPassword` | Yes | API admin password for JWT auth |
| `UseWebSocket` | No | Use WebSocket transport (default: true) |
| `WebhookPort` | No | Webhook listen port (if not using WS) |
| `WebhookSecret` | No | HMAC secret for webhook verification |
| `EnableRoomCodeImage` | No | Generate room code images |
| `RoomCodeImageDelay` | No | Seconds before sending image announcement |
| `RoomCodePlaintextDelay` | No | Seconds before sending plaintext room code |
## Usage
@@ -126,154 +172,163 @@ See [Matterbridge IRC documentation](https://github.com/42wim/matterbridge/wiki/
# Run with debug logging
./matterbridge -conf matterbridge.toml -debug
# Start with Jackbox announcements muted
./matterbridge -conf matterbridge.toml -muted
```
## How It Works
### CLI Flags
### Kosmi Connection
| Flag | Description |
|------|-------------|
| `-conf` | Path to config file (default: `matterbridge.toml`) |
| `-debug` | Enable debug logging |
| `-version` | Show version |
| `-muted` | Start with Jackbox announcements muted |
| `-gops` | Enable gops diagnostic agent |
The bridge connects to Kosmi using headless Chrome automation:
### IRC Commands
1. **Launch Chrome**: Starts a headless Chrome instance via `chromedp`
2. **Inject Hook**: Uses `Page.addScriptToEvaluateOnNewDocument` to inject a WebSocket interceptor **before any page scripts run**
3. **Navigate**: Loads the Kosmi room URL
4. **Intercept**: The injected script hooks `window.WebSocket` constructor to capture all WebSocket messages
5. **Poll**: Continuously polls the message queue populated by the interceptor
6. **Process**: Extracts chat messages from GraphQL subscription data
Users can issue commands in IRC chat. See [docs/IRC.md](docs/IRC.md) for full details.
### Critical Implementation Detail
| Command | Description |
|---------|-------------|
| `!kreconnect` | Reconnect the Kosmi bridge |
| `!jreconnect` | Reconnect the Jackbox WebSocket |
| `!reconnect` | Reconnect all non-IRC services |
| `!votes` | Show vote tally for the current game |
| `thisgame++` / `thisgame--` | Vote on the current game |
| `SYMBOL++` / `SYMBOL--` | Vote on a game by ticker symbol |
The WebSocket hook **must** be injected before page load using `Page.addScriptToEvaluateOnNewDocument`. This ensures the hook is active when Kosmi's JavaScript creates the WebSocket connection. If injected after page load, the WebSocket will already be established and messages won't be captured.
### Mute Toggle
### Message Flow
Jackbox announcements can be toggled at runtime without restarting:
```
IRC User → IRC Server → Matterbridge → Headless Chrome → Kosmi Room
Kosmi User → Kosmi Room → WebSocket → Chrome Interceptor → Matterbridge → IRC Server → IRC Channel
```bash
# Local process
kill -SIGUSR1 $(pgrep matterbridge)
# Docker
docker kill -s SIGUSR1 kosmi-irc-relay
```
### Message Filtering
See [MUTE_CONTROL.md](docs/setup/MUTE_CONTROL.md) for details.
- The bridge ignores its own messages by checking for the `[IRC]` prefix
- This prevents message loops between Kosmi and IRC
## Environment Variables
## Technical Details
| Variable | Description |
|----------|-------------|
| `DEBUG=1` | Enable debug logging |
| `MATTERBRIDGE_DATA_DIR` | Directory for persistent data (token cache) |
| `TZ` | Timezone for container logs |
| `CHROME_BIN` | Path to Chrome binary (for auth only) |
### GraphQL API
The Kosmi bridge uses the following GraphQL operations:
**Subscription** (receiving messages):
```graphql
subscription {
newMessage(roomId: "roomId") {
body
time
user {
displayName
username
}
}
}
```
**Mutation** (sending messages):
```graphql
mutation {
sendMessage(roomId: "roomId", body: "message text") {
id
}
}
```
### File Structure
## File Structure
```
bridge/kosmi/
├── kosmi.go # Main bridge implementation
├── chromedp_client.go # Headless Chrome client with WebSocket interception
── graphql.go # GraphQL message structures (deprecated native client)
gateway/bridgemap/
── bkosmi.go # Bridge registration
cmd/test-kosmi/
└── main.go # Standalone test program
matterbridge.toml # Configuration file
go.mod # Go module dependencies
irc-kosmi-relay/
├── matterbridge.go # Entry point: flags, logger, router, mute toggle
├── matterbridge.toml.example # Example configuration
── Dockerfile # Docker build (Go 1.23 + Chromium for auth)
├── docker-compose.yml # Docker Compose deployment
── bridge/
│ ├── config/
│ │ └── config.go # Message types, events, protocol config
│ ├── kosmi/
│ │ ├── kosmi.go # Kosmi bridge: Connect, Send, message handling
├── graphql_ws_client.go # Native GraphQL-over-WebSocket client
├── graphql.go # GraphQL message/payload structs
│ │ ├── auth.go # Anonymous login (anonLogin HTTP mutation)
│ │ ├── browser_auth.go # Chromedp browser login for email/password
│ │ ├── token_cache.go # JWT token caching (file-based)
│ │ ├── image_upload.go # Image upload to Kosmi CDN
│ │ └── errors.go # Error types
│ ├── irc/
│ │ ├── irc.go # IRC bridge: Connect, Send, NAMES handling
│ │ ├── handlers.go # PRIVMSG handler, commands, vote detection
│ │ └── formatting.go # IRC formatting helpers
│ └── jackbox/
│ ├── client.go # Jackbox API client (REST + WebSocket)
│ ├── votes.go # Vote detection (thisgame/ticker parsing)
│ ├── tickers.go # Ticker symbol -> game title lookup table
│ ├── webhook.go # Webhook transport (alternative to WS)
│ ├── image_upload.go # Room code image generation + upload
│ └── roomcode_image.go # Room code GIF rendering
├── gateway/
│ ├── gateway.go # Gateway message routing and transformation
│ ├── router.go # Router: message bus, Jackbox manager, broadcast
│ ├── handlers.go # Event handlers: reconnect, votes, files
│ └── bridgemap/
│ ├── bkosmi.go # Registers "kosmi" protocol
│ └── birc.go # Registers "irc" protocol
├── docs/
│ ├── IRC.md # IRC command reference
│ ├── setup/ # Setup and deployment guides
│ └── archive/ # Historical development notes
└── cmd/ # Standalone test/debug tools
```
## Troubleshooting
### Connection Issues
### Kosmi Connection Issues
**Problem**: Bridge fails to connect to Kosmi
**Bridge fails to connect to Kosmi**
**Solutions**:
- Verify Chrome/Chromium is installed: `which google-chrome chromium chromium-browser`
- Verify the room URL is correct
- Check network connectivity to `app.kosmi.io`
- Enable debug logging to see detailed connection logs
- Look for "✓ WebSocket hook confirmed installed" in logs
- Look for "Status: WebSocket connection intercepted" in logs
- Verify `RoomURL` is correct and the room exists
- Check network connectivity to `app.kosmi.io` and `engine.kosmi.io`
- Enable debug logging (`-debug`) to see WebSocket handshake details
- Look for `"Successfully connected to Kosmi"` in logs
### Message Not Relaying
**Authentication fails (email/password)**
**Problem**: Messages aren't being relayed between Kosmi and IRC
- Verify Chrome/Chromium is installed and accessible
- Check credentials are correct
- Delete `data/kosmi_token_cache.json` to force a fresh login
- See [BROWSER_AUTH_GUIDE.md](docs/setup/BROWSER_AUTH_GUIDE.md)
**Solutions**:
- Verify both Kosmi and IRC connections are established
- Check the gateway configuration in `matterbridge.toml`
- Ensure channel names match in the gateway configuration
- Check logs for errors
### IRC Connection Issues
### Authentication Errors
- Verify server address and port
- Check TLS settings match the server's requirements
- Confirm the bot's nick is not already in use
- Check NickServ/SASL credentials if authentication is configured
**Problem**: Kosmi connection fails with authentication error
### Messages Not Relaying
**Note**: Kosmi doesn't require authentication. If you see auth errors, verify:
- The WebSocket URL is correct
- The room ID is valid
- Network isn't blocking WebSocket connections
- Verify both bridges show as connected in logs
- Check gateway configuration: Kosmi channel must be `"main"`, IRC channel must include `#`
- Enable debug logging to trace message flow
- Look for `"Forwarding to Matterbridge"` and `"Sending message"` in logs
## Development
### Jackbox Integration Issues
### Adding Features
- Verify `APIURL` is accessible and `AdminPassword` is correct
- Check that a Jackbox session is active with games being played
- Confirm `Enabled=true` in the `[jackbox]` config section
- See [JACKBOX_INTEGRATION.md](docs/setup/JACKBOX_INTEGRATION.md)
The bridge follows Matterbridge's bridge interface:
## Documentation
```go
type Bridger interface {
Send(msg config.Message) (string, error)
Connect() error
JoinChannel(channel config.ChannelInfo) error
Disconnect() error
}
```
### Testing
To test the bridge:
1. Start the bridge with debug logging
2. Send a message in the Kosmi room
3. Verify it appears in IRC with `[Kosmi]` prefix
4. Send a message in IRC
5. Verify it appears in Kosmi with `[IRC]` prefix
## Known Limitations
1. **Anonymous Access**: The bridge connects anonymously to Kosmi, so it will have a randomly assigned username
2. **Message Sending**: The GraphQL mutation for sending messages may need adjustment based on Kosmi's actual API
3. **Room Discovery**: The bridge connects to a specific room; it doesn't support room discovery or listing
| Document | Description |
|----------|-------------|
| [IRC.md](docs/IRC.md) | IRC commands, voting syntax, ticker symbols |
| [DOCKER_QUICKSTART.md](docs/setup/DOCKER_QUICKSTART.md) | 5-minute Docker setup |
| [DOCKER_DEPLOYMENT.md](docs/setup/DOCKER_DEPLOYMENT.md) | Full Docker deployment guide |
| [QUICKSTART.md](docs/setup/QUICKSTART.md) | Build-from-source quickstart |
| [JACKBOX_INTEGRATION.md](docs/setup/JACKBOX_INTEGRATION.md) | Jackbox Game Picker setup |
| [BROWSER_AUTH_GUIDE.md](docs/setup/BROWSER_AUTH_GUIDE.md) | Email/password authentication |
| [TOKEN_PERSISTENCE.md](docs/setup/TOKEN_PERSISTENCE.md) | Token caching details |
| [MUTE_CONTROL.md](docs/setup/MUTE_CONTROL.md) | Mute toggle guide |
## Credits
- Based on [Matterbridge](https://github.com/42wim/matterbridge) by 42wim
- Kosmi API reverse engineering from chrome extension analysis
- Kosmi API integration via reverse-engineered GraphQL protocol
## License
Same as Matterbridge (Apache 2.0)