> [!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-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 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 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. ### How It Works 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. ### 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) ```bash # 1. Copy and edit configuration cp matterbridge.toml.example matterbridge.toml nano matterbridge.toml # 2. Build and run docker-compose up -d # 3. View logs docker-compose logs -f ``` See [DOCKER_QUICKSTART.md](docs/setup/DOCKER_QUICKSTART.md) for a 5-minute setup guide. ### Option 2: Build from Source #### Prerequisites - 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 git clone cd irc-kosmi-relay go mod download go build -o matterbridge . ``` ## Configuration Copy `matterbridge.toml.example` to `matterbridge.toml` and edit it. The key sections: ```toml # Kosmi configuration [kosmi.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.zeronode] Server="irc.libera.chat:6697" Nick="kosmi-relay" UseTLS=true RemoteNickFormat="[{PROTOCOL}] <{NICK}> " # Gateway to connect Kosmi and IRC [[gateway]] name="kosmi-irc-gateway" enable=true [[gateway.inout]] account="kosmi.hyperspaceout" channel="main" [[gateway.inout]] 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 Reference #### Kosmi Settings | 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 | 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 ```bash # Run the bridge ./matterbridge -conf matterbridge.toml # Run with debug logging ./matterbridge -conf matterbridge.toml -debug # Start with Jackbox announcements muted ./matterbridge -conf matterbridge.toml -muted ``` ### CLI Flags | 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 | ### IRC Commands Users can issue commands in IRC chat. See [docs/IRC.md](docs/IRC.md) for full details. | 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 | ### Mute Toggle Jackbox announcements can be toggled at runtime without restarting: ```bash # Local process kill -SIGUSR1 $(pgrep matterbridge) # Docker docker kill -s SIGUSR1 kosmi-irc-relay ``` See [MUTE_CONTROL.md](docs/setup/MUTE_CONTROL.md) for details. ## Environment Variables | 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) | ## File Structure ``` 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 ### Kosmi Connection Issues **Bridge fails to connect to Kosmi** - 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 **Authentication fails (email/password)** - 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) ### IRC Connection Issues - 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 ### Messages Not Relaying - 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 ### Jackbox Integration Issues - 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) ## Documentation | 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 integration via reverse-engineered GraphQL protocol ## License Same as Matterbridge (Apache 2.0)