diff --git a/README.md b/README.md index 1c76347..b503c1a 100644 --- a/README.md +++ b/README.md @@ -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] message` - - **IRC → Kosmi**: `[IRC] 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 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) - diff --git a/docs/IRC.md b/docs/IRC.md new file mode 100644 index 0000000..774584b --- /dev/null +++ b/docs/IRC.md @@ -0,0 +1,182 @@ +# IRC Command Reference + +This document covers everything available to IRC users when interacting with the Kosmi-IRC relay bot. All commands and vote syntax work in any channel the bot is present in. + +## Commands + +Commands are typed as regular messages in IRC. They are **not** relayed to other bridges -- the bot consumes them and acts on them directly. + +There is no permission system; any user in the channel can issue any command. + +### `!kreconnect` + +Force-reconnect the Kosmi bridge. Useful if the Kosmi WebSocket has silently dropped. + +**Response:** `Reconnecting Kosmi...` (sent to the channel immediately) + +The reconnection happens asynchronously. If it fails, errors appear only in the bot's logs. + +### `!jreconnect` + +Force-reconnect the Jackbox Game Picker WebSocket. Only meaningful if the Jackbox integration is enabled. + +**Response:** `Reconnecting Jackbox...` + +### `!reconnect` + +Reconnect all non-IRC services (Kosmi and Jackbox) at once. + +**Response:** `Reconnecting all services...` + +### `!votes` + +Query the current vote tally for the game being played right now in the active Jackbox session. + +**Response** (broadcast to all bridges -- IRC, Kosmi, etc.): + +``` +🗳️ Fibbage 4 • Today: 5👍 2👎 (Score: 3) • All-time: 46👍 3👎 (Score: 43) +``` + +**Requirements:** +- Jackbox integration must be enabled +- An active Jackbox session must exist +- A game must currently have status "playing" in that session + +If any of these conditions are not met, the command is silently consumed (errors appear only in bot logs). + +--- + +## Voting + +Vote detection runs automatically on every non-relayed message. Votes are sent to the Jackbox Game Picker API when the integration is enabled and a session is active. + +### Current Game Votes + +Type anywhere in a message: + +| Syntax | Effect | +|--------|--------| +| `thisgame++` | Upvote the currently playing game | +| `thisgame--` | Downvote the currently playing game | + +- Case-insensitive (`ThisGame++`, `THISGAME++`, etc. all work) +- Can appear anywhere in the message text +- The message is still relayed to other bridges as normal chat + +### Ticker Symbol Votes + +Vote on a specific game by its ticker symbol: + +| Syntax | Effect | +|--------|--------| +| `SYMBOL++` | Upvote the game identified by SYMBOL | +| `SYMBOL--` | Downvote the game identified by SYMBOL | + +- Symbols are 2-4 alphanumeric characters +- Case-insensitive (`qpl3++` and `QPL3++` are equivalent) +- The symbol must exist in the ticker table below +- The message is still relayed to other bridges as normal chat + +### Ticker Symbol Table + +| Symbol | Game | +|--------|------| +| `BC` | Bomb Corp. | +| `BDTS` | Bidiots | +| `BR` | Blather 'Round | +| `BRKT` | Bracketeering | +| `CH` | Cookie Haus | +| `CU` | Champ'd Up | +| `CVDL` | Civic Doodle | +| `DD` | Dirty Drawful | +| `DCTN` | Dictionarium | +| `DOOM` | Doominate | +| `DRM` | Dodo Re Mi | +| `DRWA` | Drawful Animate | +| `DRWF` | Drawful | +| `EW` | Earwax | +| `FANL` | Fakin' It All Night Long | +| `FBG2` | Fibbage 2 | +| `FBG3` | Fibbage 3 | +| `FBG4` | Fibbage 4 | +| `FBXL` | Fibbage XL | +| `FI` | Fakin' It! | +| `FT` | Fixy Text | +| `GSPN` | Guesspionage | +| `HPNT` | Hypnotorious | +| `HRSY` | Hear Say | +| `JB` | Joke Boat | +| `JJ` | Job Job | +| `JNKT` | Junktopia | +| `LMF` | Let Me Finish | +| `LOT` | Legends of Trivia | +| `LS` | Lie Swatter | +| `MSM` | Monster Seeking Monster | +| `MVC` | Mad Verse City | +| `NNSR` | Nonsensory | +| `PS` | Patently Stupid | +| `PTB` | Push the Button | +| `QLXL` | Quiplash XL | +| `QPL2` | Quiplash 2 | +| `QPL3` | Quiplash 3 | +| `QXRT` | Quixort | +| `RM` | Role Models | +| `ROOM` | Roomerang | +| `SS` | Survey Scramble | +| `SPCT` | Suspectives | +| `STI` | Survive the Internet | +| `STR` | Split the Room | +| `TJ` | Time Jinx | +| `TKO2` | Tee K.O. 2 | +| `TKOX` | Tee K.O. T-Shirt Knock Out | +| `TMP1` | Trivia Murder Party | +| `TMP2` | Trivia Murder Party 2 | +| `TP` | Talking Points | +| `TPM` | The Poll Mine | +| `TWEP` | The Wheel of Enormous Proportions | +| `WD` | Weapons Drawn | +| `WS` | Word Spud | +| `YDK1` | You Don't Know Jack 2015 | +| `YDKJ` | You Don't Know Jack Full Stream | +| `ZPDM` | Zeeple Dome | + +--- + +## Behavior Notes + +### Loop Prevention + +Messages that begin with `[irc]` or `[kosmi]` (case-insensitive) are treated as relayed from another bridge. Vote detection is skipped for these messages to prevent duplicate votes when the same text passes through multiple bridges. + +### Command vs. Vote Processing Order + +1. Vote detection runs first (on all non-relayed messages) +2. `!` commands are checked next +3. If the message is a `!` command, it is consumed and **not** relayed +4. If the message is not a command, it is relayed to other bridges + +This means a vote phrase in a normal message (like "nice game thisgame++") both registers the vote and gets relayed. But `!votes` only triggers the votes query -- it is not relayed. + +### What IRC Users See from Other Bridges + +| Source | Format | +|--------|--------| +| Kosmi user message | `[kosmi] message text` | +| Jackbox game announcement | System message (e.g. `🎮 Coming up next: Fibbage 4!`) | +| Jackbox room code | System message with room code (may include image link) | +| `!votes` result | `🗳️ Title • Today: X👍 Y👎 (Score: Z) • All-time: X👍 Y👎 (Score: Z)` | +| Reconnect confirmation | Plain text from "system" (e.g. `Reconnecting Kosmi...`) | + +The `[kosmi]` prefix format is configurable via `RemoteNickFormat` in `matterbridge.toml`. + +### Requirements for Jackbox Features + +All Jackbox-related functionality (votes, `!votes`, game announcements, room codes) requires: + +1. `[jackbox]` section in config with `Enabled=true` +2. Valid `APIURL` and `AdminPassword` +3. An active Jackbox session on the API +4. A game currently being played (for `!votes` and vote detection) + +When the Jackbox integration is disabled or no session is active, vote syntax is ignored and `!votes` produces no output. diff --git a/docs/ASYNC_FIX.md b/docs/archive/ASYNC_FIX.md similarity index 100% rename from docs/ASYNC_FIX.md rename to docs/archive/ASYNC_FIX.md diff --git a/docs/AUTHENTICATION_ISSUE.md b/docs/archive/AUTHENTICATION_ISSUE.md similarity index 100% rename from docs/AUTHENTICATION_ISSUE.md rename to docs/archive/AUTHENTICATION_ISSUE.md diff --git a/docs/AUTHENTICATION_STATUS.md b/docs/archive/AUTHENTICATION_STATUS.md similarity index 100% rename from docs/AUTHENTICATION_STATUS.md rename to docs/archive/AUTHENTICATION_STATUS.md diff --git a/docs/AUTH_DISCOVERY.md b/docs/archive/AUTH_DISCOVERY.md similarity index 100% rename from docs/AUTH_DISCOVERY.md rename to docs/archive/AUTH_DISCOVERY.md diff --git a/docs/AUTH_FINDINGS.md b/docs/archive/AUTH_FINDINGS.md similarity index 100% rename from docs/AUTH_FINDINGS.md rename to docs/archive/AUTH_FINDINGS.md diff --git a/docs/CAPTURE_UPLOAD_MANUALLY.md b/docs/archive/CAPTURE_UPLOAD_MANUALLY.md similarity index 100% rename from docs/CAPTURE_UPLOAD_MANUALLY.md rename to docs/archive/CAPTURE_UPLOAD_MANUALLY.md diff --git a/docs/CHROMEDP_IMPLEMENTATION.md b/docs/archive/CHROMEDP_IMPLEMENTATION.md similarity index 100% rename from docs/CHROMEDP_IMPLEMENTATION.md rename to docs/archive/CHROMEDP_IMPLEMENTATION.md diff --git a/docs/CRITICAL_FIX_OPERATION_ORDER.md b/docs/archive/CRITICAL_FIX_OPERATION_ORDER.md similarity index 100% rename from docs/CRITICAL_FIX_OPERATION_ORDER.md rename to docs/archive/CRITICAL_FIX_OPERATION_ORDER.md diff --git a/docs/DOCKER_SETUP_COMPLETE.md b/docs/archive/DOCKER_SETUP_COMPLETE.md similarity index 100% rename from docs/DOCKER_SETUP_COMPLETE.md rename to docs/archive/DOCKER_SETUP_COMPLETE.md diff --git a/docs/FINDINGS.md b/docs/archive/FINDINGS.md similarity index 100% rename from docs/FINDINGS.md rename to docs/archive/FINDINGS.md diff --git a/docs/GATEWAY_TIMING_FIX.md b/docs/archive/GATEWAY_TIMING_FIX.md similarity index 100% rename from docs/GATEWAY_TIMING_FIX.md rename to docs/archive/GATEWAY_TIMING_FIX.md diff --git a/docs/GRAPHQL_OPERATIONS_AUDIT.md b/docs/archive/GRAPHQL_OPERATIONS_AUDIT.md similarity index 100% rename from docs/GRAPHQL_OPERATIONS_AUDIT.md rename to docs/archive/GRAPHQL_OPERATIONS_AUDIT.md diff --git a/docs/IMPLEMENTATION_SUMMARY.md b/docs/archive/IMPLEMENTATION_SUMMARY.md similarity index 100% rename from docs/IMPLEMENTATION_SUMMARY.md rename to docs/archive/IMPLEMENTATION_SUMMARY.md diff --git a/docs/INTEGRATION.md b/docs/archive/INTEGRATION.md similarity index 100% rename from docs/INTEGRATION.md rename to docs/archive/INTEGRATION.md diff --git a/docs/IRC_TROUBLESHOOTING.md b/docs/archive/IRC_TROUBLESHOOTING.md similarity index 100% rename from docs/IRC_TROUBLESHOOTING.md rename to docs/archive/IRC_TROUBLESHOOTING.md diff --git a/docs/JACKBOX_TESTING.md b/docs/archive/JACKBOX_TESTING.md similarity index 100% rename from docs/JACKBOX_TESTING.md rename to docs/archive/JACKBOX_TESTING.md diff --git a/docs/KOSMI_IMAGE_UPLOAD.md b/docs/archive/KOSMI_IMAGE_UPLOAD.md similarity index 100% rename from docs/KOSMI_IMAGE_UPLOAD.md rename to docs/archive/KOSMI_IMAGE_UPLOAD.md diff --git a/docs/LESSONS_LEARNED.md b/docs/archive/LESSONS_LEARNED.md similarity index 100% rename from docs/LESSONS_LEARNED.md rename to docs/archive/LESSONS_LEARNED.md diff --git a/docs/MESSAGE_QUEUE_FIX.md b/docs/archive/MESSAGE_QUEUE_FIX.md similarity index 100% rename from docs/MESSAGE_QUEUE_FIX.md rename to docs/archive/MESSAGE_QUEUE_FIX.md diff --git a/docs/MISSING_OPERATIONS.md b/docs/archive/MISSING_OPERATIONS.md similarity index 100% rename from docs/MISSING_OPERATIONS.md rename to docs/archive/MISSING_OPERATIONS.md diff --git a/docs/NATIVE_WEBSOCKET_IMPLEMENTATION.md b/docs/archive/NATIVE_WEBSOCKET_IMPLEMENTATION.md similarity index 100% rename from docs/NATIVE_WEBSOCKET_IMPLEMENTATION.md rename to docs/archive/NATIVE_WEBSOCKET_IMPLEMENTATION.md diff --git a/docs/PLAYWRIGHT_NATIVE_CLIENT.md b/docs/archive/PLAYWRIGHT_NATIVE_CLIENT.md similarity index 100% rename from docs/PLAYWRIGHT_NATIVE_CLIENT.md rename to docs/archive/PLAYWRIGHT_NATIVE_CLIENT.md diff --git a/docs/PROJECT_STATUS.txt b/docs/archive/PROJECT_STATUS.txt similarity index 100% rename from docs/PROJECT_STATUS.txt rename to docs/archive/PROJECT_STATUS.txt diff --git a/docs/archive/README.md.2026-05-10.bak b/docs/archive/README.md.2026-05-10.bak new file mode 100644 index 0000000..1c76347 --- /dev/null +++ b/docs/archive/README.md.2026-05-10.bak @@ -0,0 +1,279 @@ +> [!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. + +## 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 + +## Architecture + +This implementation extends Matterbridge with a custom Kosmi bridge that: + +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] message` + - **IRC → Kosmi**: `[IRC] message` + +### Why Headless Chrome? + +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 + +## Installation + +### Option 1: Docker (Recommended) 🐳 + +The easiest way to run the bridge: + +```bash +# 1. Edit configuration +nano matterbridge.toml + +# 2. Build and run +docker-compose up -d + +# 3. View logs +docker-compose logs -f +``` + +**See**: `DOCKER_QUICKSTART.md` for 5-minute setup guide + +### Option 2: Build from Source + +#### Prerequisites + +- Go 1.21 or higher +- Chrome or Chromium browser installed +- Access to an IRC server +- A Kosmi room URL + +#### Building + +```bash +# Clone the repository +git clone +cd irc-kosmi-relay + +# Download dependencies +go mod download + +# Build the bridge +go build -o matterbridge +``` + +## Configuration + +Edit `matterbridge.toml` to configure your bridge: + +```toml +# Kosmi configuration +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" + +# IRC configuration +[irc.libera] +Server="irc.libera.chat:6667" +Nick="kosmi-relay" +UseTLS=false + +# Gateway to connect Kosmi and IRC +[[gateway]] +name="kosmi-irc-gateway" +enable=true + +[[gateway.inout]] +account="kosmi.hyperspaceout" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#your-channel" +``` + +### Configuration Options + +#### 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 + +#### IRC Settings + +See [Matterbridge IRC documentation](https://github.com/42wim/matterbridge/wiki/Section-IRC-(basic)) for full IRC configuration options. + +## Usage + +```bash +# Run the bridge +./matterbridge -conf matterbridge.toml + +# Run with debug logging +./matterbridge -conf matterbridge.toml -debug +``` + +## How It Works + +### Kosmi Connection + +The bridge connects to Kosmi using headless Chrome automation: + +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 + +### Critical Implementation Detail + +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. + +### Message Flow + +``` +IRC User → IRC Server → Matterbridge → Headless Chrome → Kosmi Room +Kosmi User → Kosmi Room → WebSocket → Chrome Interceptor → Matterbridge → IRC Server → IRC Channel +``` + +### Message Filtering + +- The bridge ignores its own messages by checking for the `[IRC]` prefix +- This prevents message loops between Kosmi and IRC + +## Technical Details + +### 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 + +``` +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 +``` + +## Troubleshooting + +### Connection Issues + +**Problem**: 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 + +### Message Not Relaying + +**Problem**: Messages aren't being relayed between Kosmi and IRC + +**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 + +### Authentication Errors + +**Problem**: Kosmi connection fails with authentication error + +**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 + +## Development + +### Adding Features + +The bridge follows Matterbridge's bridge interface: + +```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 + +## Credits + +- Based on [Matterbridge](https://github.com/42wim/matterbridge) by 42wim +- Kosmi API reverse engineering from chrome extension analysis + +## License + +Same as Matterbridge (Apache 2.0) + diff --git a/docs/ROOM_CODE_IMAGE_STATUS.md b/docs/archive/ROOM_CODE_IMAGE_STATUS.md similarity index 100% rename from docs/ROOM_CODE_IMAGE_STATUS.md rename to docs/archive/ROOM_CODE_IMAGE_STATUS.md diff --git a/docs/ROOM_CODE_IMPLEMENTATION_SUMMARY.md b/docs/archive/ROOM_CODE_IMPLEMENTATION_SUMMARY.md similarity index 100% rename from docs/ROOM_CODE_IMPLEMENTATION_SUMMARY.md rename to docs/archive/ROOM_CODE_IMPLEMENTATION_SUMMARY.md diff --git a/docs/TESTING_NOTES.md b/docs/archive/TESTING_NOTES.md similarity index 100% rename from docs/TESTING_NOTES.md rename to docs/archive/TESTING_NOTES.md diff --git a/docs/TESTING_STATUS.md b/docs/archive/TESTING_STATUS.md similarity index 100% rename from docs/TESTING_STATUS.md rename to docs/archive/TESTING_STATUS.md diff --git a/docs/TYPING_INDICATORS.md b/docs/archive/TYPING_INDICATORS.md similarity index 100% rename from docs/TYPING_INDICATORS.md rename to docs/archive/TYPING_INDICATORS.md diff --git a/docs/WEBSOCKET_403_ANALYSIS.md b/docs/archive/WEBSOCKET_403_ANALYSIS.md similarity index 100% rename from docs/WEBSOCKET_403_ANALYSIS.md rename to docs/archive/WEBSOCKET_403_ANALYSIS.md diff --git a/docs/WEBSOCKET_EVENT_FLOW.md b/docs/archive/WEBSOCKET_EVENT_FLOW.md similarity index 100% rename from docs/WEBSOCKET_EVENT_FLOW.md rename to docs/archive/WEBSOCKET_EVENT_FLOW.md diff --git a/docs/WEBSOCKET_FINDINGS.md b/docs/archive/WEBSOCKET_FINDINGS.md similarity index 100% rename from docs/WEBSOCKET_FINDINGS.md rename to docs/archive/WEBSOCKET_FINDINGS.md diff --git a/docs/archive/setup-backup-2026-05-10/BOT_features.md b/docs/archive/setup-backup-2026-05-10/BOT_features.md new file mode 100644 index 0000000..c7ac6d2 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/BOT_features.md @@ -0,0 +1,414 @@ +# Bot Integration Guide + +This guide explains how to integrate your bot with the Jackbox Game Picker API for live voting and game notifications. + +## Table of Contents + +1. [Live Voting (Bot → API)](#live-voting-bot--api) +2. [Game Notifications (API → Bot)](#game-notifications-api--bot) +3. [Webhook Management](#webhook-management) +4. [Testing](#testing) + +--- + +## Live Voting (Bot → API) + +Your bot can send real-time votes to the API when it detects "thisgame++" or "thisgame--" in Kosmi chat. + +### Endpoint + +``` +POST /api/votes/live +``` + +### Authentication + +Requires JWT token in Authorization header: + +``` +Authorization: Bearer YOUR_JWT_TOKEN +``` + +### Request Body + +```json +{ + "username": "string", // Username of the voter + "vote": "up" | "down", // "up" for thisgame++, "down" for thisgame-- + "timestamp": "string" // ISO 8601 timestamp (e.g., "2025-11-01T20:30:00Z") +} +``` + +### Response (Success) + +```json +{ + "success": true, + "message": "Vote recorded successfully", + "session": { + "id": 123, + "games_played": 5 + }, + "game": { + "id": 45, + "title": "Fibbage 4", + "upvotes": 46, + "downvotes": 3, + "popularity_score": 43 + }, + "vote": { + "username": "TestUser", + "type": "up", + "timestamp": "2025-11-01T20:30:00Z" + } +} +``` + +### Error Responses + +- **400 Bad Request**: Invalid payload or timestamp format +- **404 Not Found**: No active session or timestamp doesn't match any game +- **409 Conflict**: Duplicate vote (within 1 second of previous vote from same user) +- **500 Internal Server Error**: Server error + +### Example Implementation (Node.js) + +```javascript +// When bot detects "thisgame++" or "thisgame--" in Kosmi chat +async function handleVote(username, message) { + const isUpvote = message.includes('thisgame++'); + const isDownvote = message.includes('thisgame--'); + + if (!isUpvote && !isDownvote) return; + + try { + const response = await fetch('http://your-api-url/api/votes/live', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${process.env.JWT_TOKEN}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: username, + vote: isUpvote ? 'up' : 'down', + timestamp: new Date().toISOString() + }) + }); + + const data = await response.json(); + + if (response.ok) { + console.log(`Vote recorded for ${data.game.title}: ${data.game.upvotes}👍 ${data.game.downvotes}👎`); + } else { + console.error('Vote failed:', data.error); + } + } catch (error) { + console.error('Error sending vote:', error); + } +} +``` + +### Important Notes + +- **Deduplication**: Votes from the same user within 1 second are automatically rejected to prevent spam +- **Timestamp Matching**: The API matches the vote timestamp to the correct game based on when games were played +- **Active Session Required**: Votes can only be recorded when there's an active session with games played + +--- + +## Game Notifications (API → Bot) + +The API can send webhooks to your bot when games are added to a session, allowing you to announce "Coming up next: Game Title!" in Kosmi chat. + +### Webhook Event: `game.added` + +Triggered whenever a game is added to an active session (either via picker or manual selection). + +### Webhook Payload + +```json +{ + "event": "game.added", + "timestamp": "2025-11-01T20:30:00Z", + "data": { + "session": { + "id": 123, + "is_active": true, + "games_played": 5 + }, + "game": { + "id": 45, + "title": "Fibbage 4", + "pack_name": "The Jackbox Party Pack 9", + "min_players": 2, + "max_players": 8, + "manually_added": false + } + } +} +``` + +### Webhook Headers + +The API sends the following headers with each webhook: + +- `Content-Type: application/json` +- `X-Webhook-Signature: sha256=` - HMAC-SHA256 signature for verification +- `X-Webhook-Event: game.added` - Event type +- `User-Agent: Jackbox-Game-Picker-Webhook/1.0` + +### Signature Verification + +**IMPORTANT**: Always verify the webhook signature to ensure the request is authentic. + +```javascript +const crypto = require('crypto'); + +function verifyWebhookSignature(signature, payload, secret) { + if (!signature || !signature.startsWith('sha256=')) { + return false; + } + + const expectedSignature = 'sha256=' + crypto + .createHmac('sha256', secret) + .update(JSON.stringify(payload)) + .digest('hex'); + + // Use timing-safe comparison + try { + return crypto.timingSafeEqual( + Buffer.from(signature), + Buffer.from(expectedSignature) + ); + } catch (err) { + return false; + } +} +``` + +### Example Implementation (Express.js) + +```javascript +const express = require('express'); +const crypto = require('crypto'); + +const app = express(); + +// IMPORTANT: Use express.json() with verify option to get raw body +app.use(express.json({ + verify: (req, res, buf) => { + req.rawBody = buf.toString('utf8'); + } +})); + +app.post('/webhook/jackbox', (req, res) => { + const signature = req.headers['x-webhook-signature']; + const secret = process.env.WEBHOOK_SECRET; // Your webhook secret + + // Verify signature + if (!signature || !signature.startsWith('sha256=')) { + return res.status(401).send('Missing or invalid signature'); + } + + const expectedSignature = 'sha256=' + crypto + .createHmac('sha256', secret) + .update(req.rawBody) + .digest('hex'); + + // Timing-safe comparison + try { + if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { + return res.status(401).send('Invalid signature'); + } + } catch (err) { + return res.status(401).send('Invalid signature'); + } + + // Handle the event + if (req.body.event === 'game.added') { + const game = req.body.data.game; + + // Send message to Kosmi chat + sendKosmiMessage(`🎮 Coming up next: ${game.title}!`); + + console.log(`Announced game: ${game.title} from ${game.pack_name}`); + } + + // Always respond with 200 OK + res.status(200).send('OK'); +}); + +function sendKosmiMessage(message) { + // Your Kosmi chat integration here + console.log('Sending to Kosmi:', message); +} + +app.listen(3001, () => { + console.log('Webhook receiver listening on port 3001'); +}); +``` + +--- + +## Webhook Management + +You can manage webhooks through the API using the following endpoints (all require JWT authentication). + +### List All Webhooks + +```bash +GET /api/webhooks +Authorization: Bearer YOUR_JWT_TOKEN +``` + +### Create Webhook + +```bash +POST /api/webhooks +Authorization: Bearer YOUR_JWT_TOKEN +Content-Type: application/json + +{ + "name": "Kosmi Bot", + "url": "http://your-bot-url/webhook/jackbox", + "secret": "your_shared_secret_key", + "events": ["game.added"] +} +``` + +### Update Webhook + +```bash +PATCH /api/webhooks/:id +Authorization: Bearer YOUR_JWT_TOKEN +Content-Type: application/json + +{ + "enabled": false // Disable webhook +} +``` + +### Delete Webhook + +```bash +DELETE /api/webhooks/:id +Authorization: Bearer YOUR_JWT_TOKEN +``` + +### Test Webhook + +```bash +POST /api/webhooks/test/:id +Authorization: Bearer YOUR_JWT_TOKEN +``` + +Sends a test `game.added` event to verify your webhook is working. + +### View Webhook Logs + +```bash +GET /api/webhooks/:id/logs?limit=50 +Authorization: Bearer YOUR_JWT_TOKEN +``` + +Returns recent webhook delivery attempts with status codes and errors. + +--- + +## Testing + +### Test Live Voting + +```bash +# Get your JWT token first +curl -X POST "http://localhost:5000/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"apiKey": "YOUR_API_KEY"}' + +# Send a test vote +curl -X POST "http://localhost:5000/api/votes/live" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "username": "TestUser", + "vote": "up", + "timestamp": "2025-11-01T20:30:00Z" + }' +``` + +### Test Webhooks + +```bash +# Create a webhook +curl -X POST "http://localhost:5000/api/webhooks" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Webhook", + "url": "http://localhost:3001/webhook/jackbox", + "secret": "test_secret_123", + "events": ["game.added"] + }' + +# Test the webhook +curl -X POST "http://localhost:5000/api/webhooks/test/1" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" + +# Check webhook logs +curl -X GET "http://localhost:5000/api/webhooks/1/logs" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +--- + +## Available Events + +Currently supported webhook events: + +- `game.added` - Triggered when a game is added to an active session + +More events may be added in the future (e.g., `session.started`, `session.ended`, `vote.recorded`). + +--- + +## Security Best Practices + +1. **Always verify webhook signatures** - Never trust webhook payloads without verification +2. **Use HTTPS in production** - Webhook URLs should use HTTPS to prevent man-in-the-middle attacks +3. **Keep secrets secure** - Store webhook secrets in environment variables, never in code +4. **Implement rate limiting** - Protect your webhook endpoints from abuse +5. **Log webhook activity** - Keep logs of webhook deliveries for debugging +6. **Use strong secrets** - Generate cryptographically secure random strings for webhook secrets + +--- + +## Troubleshooting + +### Votes Not Being Recorded + +- Check that there's an active session with games played +- Verify the timestamp is within the timeframe of a played game +- Ensure you're not sending duplicate votes within 1 second +- Check API logs for error messages + +### Webhooks Not Being Received + +- Verify your webhook URL is publicly accessible +- Check webhook logs via `/api/webhooks/:id/logs` +- Test with `ngrok` or similar tool if developing locally +- Ensure your webhook endpoint responds with 200 OK +- Check that webhook is enabled in the database + +### Signature Verification Failing + +- Ensure you're using the raw request body for signature verification +- Check that the secret matches what's stored in the database +- Verify you're using HMAC-SHA256 algorithm +- Make sure to prefix with "sha256=" when comparing + +--- + +## Support + +For issues or questions, contact: cottongin@cottongin.xyz + diff --git a/docs/archive/setup-backup-2026-05-10/BROWSER_AUTH_GUIDE.md b/docs/archive/setup-backup-2026-05-10/BROWSER_AUTH_GUIDE.md new file mode 100644 index 0000000..e1b23d7 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/BROWSER_AUTH_GUIDE.md @@ -0,0 +1,277 @@ +# Browser-Based Authentication Guide + +## Overview +The Kosmi bridge now supports **fully automated email/password authentication** using headless Chrome via chromedp. No manual token extraction needed! + +## Quick Start + +### 1. Configure Email/Password +Edit `matterbridge.toml`: + +```toml +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" +Email="your-email@example.com" +Password="your-password" +``` + +### 2. Run the Bot +```bash +./irc-kosmi-relay -conf matterbridge.toml +``` + +That's it! The bot will: +1. Launch headless Chrome +2. Navigate to Kosmi +3. Log in with your credentials +4. Extract the JWT token from localStorage +5. Use the token for authenticated connections +6. Automatically refresh the token daily (checks for expiry 7 days in advance) + +## How It Works + +### Initial Login +When you start the bot with Email/Password configured: +1. **Browser Launch**: Headless Chrome starts (no visible window) +2. **Navigation**: Goes to https://app.kosmi.io +3. **Login Flow**: + - Clicks "Login" button + - Clicks "Login with Email" + - Fills in email and password + - Submits the form +4. **Token Extraction**: Reads `localStorage.getItem('token')` +5. **Token Parsing**: Extracts expiry time from JWT +6. **Connection**: Uses token for WebSocket authentication + +### Automatic Token Refresh +- **Daily Check**: Every 24 hours, the bot checks if the token is still valid +- **Expiry Buffer**: Refreshes 7 days before expiration +- **Seamless**: Happens in the background without disconnecting +- **Logging**: You'll see "Checking token expiry..." in debug logs + +## Requirements + +### System Requirements +- **Chrome/Chromium**: Must be installed on your system + - macOS: Usually pre-installed or via Homebrew: `brew install chromium` + - Linux: `sudo apt install chromium-browser` or `sudo yum install chromium` + - Windows: Download from https://www.chromium.org/getting-involved/download-chromium/ + +### Go Dependencies +- `github.com/chromedp/chromedp` - Automatically installed with `go get` + +## Configuration Options + +### Option 1: Email/Password (Recommended) +```toml +Email="your-email@example.com" +Password="your-password" +``` +- ✅ Fully automated +- ✅ Auto-refresh +- ✅ No manual intervention +- ⚠️ Requires Chrome/Chromium +- ⚠️ Stores credentials in config file + +### Option 2: Manual Token +```toml +Token="eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..." +``` +- ✅ No browser required +- ✅ No credentials in config +- ❌ Manual extraction needed +- ❌ Must update when expired (~1 year) + +### Option 3: Anonymous +```toml +# Leave both empty +Email="" +Password="" +Token="" +``` +- ✅ No setup needed +- ❌ Limited permissions +- ❌ Can't access private rooms + +## Troubleshooting + +### "Browser automation failed" +**Possible causes:** +1. Chrome/Chromium not installed +2. Chrome/Chromium not in PATH +3. Network issues +4. Kosmi website changed + +**Solutions:** +```bash +# Check if Chrome is installed +which chromium || which google-chrome || which chrome + +# Install Chrome (macOS) +brew install chromium + +# Install Chrome (Ubuntu/Debian) +sudo apt install chromium-browser + +# Install Chrome (CentOS/RHEL) +sudo yum install chromium +``` + +### "No token found in localStorage after login" +**Possible causes:** +1. Login failed (wrong credentials) +2. Kosmi's login flow changed +3. Page didn't fully load + +**Solutions:** +- Verify credentials are correct +- Check bot logs for detailed error messages +- Try manual token extraction as fallback + +### "Token expired or expiring soon" +This is normal! The bot will automatically refresh the token. If refresh fails: +- Check Chrome is still installed +- Check network connectivity +- Restart the bot to force a fresh login + +### Headless Chrome Issues +If you see Chrome-related errors: + +```bash +# Set environment variable for debugging +export CHROMEDP_DISABLE_GPU=1 + +# Or run with visible browser (for debugging) +export CHROMEDP_NO_HEADLESS=1 +``` + +## Security Considerations + +### Credential Storage +- Credentials are stored in **plain text** in `matterbridge.toml` +- Ensure config file has restrictive permissions: + ```bash + chmod 600 matterbridge.toml + ``` +- Do not commit config with real credentials to version control +- Consider using environment variables: + ```bash + export KOSMI_EMAIL="your-email@example.com" + export KOSMI_PASSWORD="your-password" + ``` + +### Browser Automation +- Headless Chrome runs with minimal privileges +- No data is stored or cached +- Browser closes immediately after token extraction +- Token is kept in memory only + +### Token Security +- Tokens are JWT (JSON Web Tokens) signed by Kosmi +- Valid for ~1 year +- Can be revoked by logging out in browser +- Treat tokens like passwords + +## Advanced Configuration + +### Custom Chrome Path +If Chrome is installed in a non-standard location: + +```go +// In browser_auth.go, modify NewContext call: +opts := append(chromedp.DefaultExecAllocatorOptions[:], + chromedp.ExecPath("/path/to/chrome"), +) +ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) +``` + +### Timeout Adjustment +If login takes longer than 60 seconds: + +```go +// In browser_auth.go, modify timeout: +ctx, cancel = context.WithTimeout(ctx, 120*time.Second) +``` + +### Refresh Interval +To check token more/less frequently: + +```go +// In kosmi.go, modify ticker: +ticker := time.NewTicker(12 * time.Hour) // Check twice daily +``` + +## Comparison with Manual Token + +| Feature | Browser Auth | Manual Token | +|---------|-------------|--------------| +| Setup Complexity | Easy | Medium | +| Automation | Full | None | +| Token Refresh | Automatic | Manual | +| Dependencies | Chrome | None | +| Security | Credentials in config | Token in config | +| Maintenance | Low | Medium | + +## Logs to Expect + +### Successful Login +``` +INFO Using browser automation for email/password authentication +INFO Obtaining authentication token via browser automation... +INFO ✅ Successfully obtained token via browser automation +INFO Token expires in: 365d +INFO ✅ Browser authentication successful +INFO Successfully connected to Kosmi +``` + +### Daily Token Check +``` +DEBUG Checking token expiry... +DEBUG Token check complete +``` + +### Token Refresh (when expiring) +``` +INFO Token expired or expiring soon, will refresh +INFO Obtaining authentication token via browser automation... +INFO ✅ Successfully obtained token via browser automation +INFO Token expires in: 365d +``` + +## Migration from Manual Token + +If you're currently using manual token: + +1. **Add credentials** to config: + ```toml + Email="your-email@example.com" + Password="your-password" + ``` + +2. **Remove or comment out Token**: + ```toml + #Token="..." + ``` + +3. **Restart the bot** + +The bot will automatically switch to browser-based auth! + +## Performance Impact + +- **Initial Login**: ~5-10 seconds (one-time per start) +- **Token Refresh**: ~5-10 seconds (once per year, or when expiring) +- **Daily Check**: <1ms (just checks expiry time) +- **Memory**: +50-100MB during browser operation (released after) +- **CPU**: Minimal (browser runs briefly) + +## Conclusion + +Browser-based authentication provides the best balance of: +- ✅ Full automation +- ✅ Reliable token refresh +- ✅ Simple configuration +- ✅ Low maintenance + +For production use, this is the **recommended authentication method**. + diff --git a/docs/archive/setup-backup-2026-05-10/DOCKER_DEPLOYMENT.md b/docs/archive/setup-backup-2026-05-10/DOCKER_DEPLOYMENT.md new file mode 100644 index 0000000..8eb6049 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/DOCKER_DEPLOYMENT.md @@ -0,0 +1,534 @@ +# Docker Deployment Guide + +Complete guide for deploying the Kosmi-IRC bridge using Docker. + +## Quick Start + +```bash +# 1. Edit configuration +nano matterbridge.toml + +# 2. Build and run +docker-compose up -d + +# 3. View logs +docker-compose logs -f +``` + +## Prerequisites + +- Docker (20.10+) +- Docker Compose (1.29+) +- A Kosmi room URL +- IRC server access + +## Step-by-Step Setup + +### 1. Configure the Bridge + +Edit `matterbridge.toml` and update these settings: + +```toml +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" # ← Change this +Debug=false + +[irc.libera] +Server="irc.libera.chat:6667" # ← Change to your IRC server +Nick="kosmi-relay" # ← Change your bot's nickname + +[[gateway.inout]] +account="kosmi.hyperspaceout" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#your-channel" # ← Change to your IRC channel +``` + +### 2. Build the Docker Image + +```bash +docker-compose build +``` + +This will: +- Install Chrome/Chromium in the container +- Build the Matterbridge binary with Kosmi support +- Create an optimized production image + +**Build time**: ~5-10 minutes (first time) + +### 3. Run the Container + +```bash +# Start in detached mode +docker-compose up -d + +# Or start with logs visible +docker-compose up +``` + +### 4. Verify It's Working + +```bash +# Check container status +docker-compose ps + +# View logs +docker-compose logs -f matterbridge + +# Look for these messages: +# INFO Successfully connected to Kosmi via Chrome +# INFO Successfully connected to IRC +# INFO Gateway(s) started successfully +``` + +### 5. Test Message Relay + +1. **Kosmi → IRC**: Send a message in your Kosmi room + - Should appear in IRC as: `[Kosmi] message` + +2. **IRC → Kosmi**: Send a message in your IRC channel + - Should appear in Kosmi as: `[IRC] message` + +## Docker Commands Reference + +### Container Management + +```bash +# Start the bridge +docker-compose up -d + +# Stop the bridge +docker-compose down + +# Restart the bridge +docker-compose restart + +# View logs +docker-compose logs -f + +# View last 100 lines of logs +docker-compose logs --tail=100 + +# Check container status +docker-compose ps + +# Execute commands in running container +docker-compose exec matterbridge sh +``` + +### Debugging + +```bash +# Enable debug logging (edit docker-compose.yml first) +# Set Debug=true in matterbridge.toml, then: +docker-compose restart + +# Check Chrome is installed +docker-compose exec matterbridge which chromium + +# Check configuration +docker-compose exec matterbridge cat /app/matterbridge.toml + +# Test connectivity +docker-compose exec matterbridge ping -c 3 app.kosmi.io +docker-compose exec matterbridge ping -c 3 irc.libera.chat +``` + +### Updating + +```bash +# Pull latest code +git pull + +# Rebuild image +docker-compose build --no-cache + +# Restart with new image +docker-compose up -d +``` + +## Configuration Options + +### docker-compose.yml + +```yaml +services: + matterbridge: + build: + context: . + dockerfile: Dockerfile + container_name: kosmi-irc-relay + restart: unless-stopped + volumes: + - ./matterbridge.toml:/app/matterbridge.toml:ro + - ./logs:/app/logs + environment: + - CHROME_BIN=/usr/bin/chromium + - CHROME_PATH=/usr/bin/chromium + - TZ=America/New_York # ← Change to your timezone + security_opt: + - seccomp:unconfined # Required for Chrome +``` + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `CHROME_BIN` | Path to Chrome binary | `/usr/bin/chromium` | +| `CHROME_PATH` | Chrome executable path | `/usr/bin/chromium` | +| `TZ` | Timezone for logs | `America/New_York` | +| `DEBUG` | Enable debug logging | `0` | + +### Volume Mounts + +| Host Path | Container Path | Purpose | +|-----------|----------------|---------| +| `./matterbridge.toml` | `/app/matterbridge.toml` | Configuration file (read-only) | +| `./logs` | `/app/logs` | Log files (optional) | + +## Troubleshooting + +### Container Won't Start + +**Check logs**: +```bash +docker-compose logs +``` + +**Common issues**: +- Configuration file syntax error +- Missing `matterbridge.toml` +- Port already in use + +**Solution**: +```bash +# Validate TOML syntax +docker run --rm -v $(pwd)/matterbridge.toml:/config.toml alpine sh -c "apk add --no-cache go && go install github.com/pelletier/go-toml/cmd/tomll@latest && tomll /config.toml" + +# Check if file exists +ls -la matterbridge.toml +``` + +### Chrome/Chromium Not Found + +**Symptoms**: +``` +ERROR Chrome binary not found +``` + +**Solution**: +```bash +# Rebuild image +docker-compose build --no-cache + +# Verify Chrome is installed +docker-compose run --rm matterbridge which chromium +``` + +### WebSocket Connection Failed + +**Symptoms**: +``` +ERROR Failed to connect to Kosmi +ERROR WebSocket connection failed +``` + +**Solution**: +```bash +# Test network connectivity +docker-compose exec matterbridge ping -c 3 app.kosmi.io + +# Check if room URL is correct +docker-compose exec matterbridge cat /app/matterbridge.toml | grep RoomURL + +# Enable debug logging +# Edit matterbridge.toml: Debug=true +docker-compose restart +``` + +### IRC Connection Failed + +**Symptoms**: +``` +ERROR Failed to connect to IRC +ERROR Connection refused +``` + +**Solution**: +```bash +# Test IRC connectivity +docker-compose exec matterbridge nc -zv irc.libera.chat 6667 + +# Check IRC configuration +docker-compose exec matterbridge cat /app/matterbridge.toml | grep -A 10 "\[irc\]" + +# Verify nickname isn't already in use +# Try changing Nick in matterbridge.toml +``` + +### Messages Not Relaying + +**Symptoms**: +- Container running +- Both bridges connected +- But messages don't appear + +**Solution**: +```bash +# Enable debug logging +# Edit matterbridge.toml: Debug=true +docker-compose restart + +# Watch logs for message flow +docker-compose logs -f | grep -E "Received|Sending|Forwarding" + +# Verify gateway configuration +docker-compose exec matterbridge cat /app/matterbridge.toml | grep -A 20 "\[\[gateway\]\]" + +# Check channel names match exactly +# Kosmi channel should be "main" +# IRC channel should include # (e.g., "#your-channel") +``` + +### High Memory Usage + +**Symptoms**: +- Container using >500MB RAM +- System slowdown + +**Solution**: +```bash +# Add memory limits to docker-compose.yml +services: + matterbridge: + mem_limit: 512m + mem_reservation: 256m + +# Restart +docker-compose up -d +``` + +### Permission Denied Errors + +**Symptoms**: +``` +ERROR Permission denied writing to /app/logs +``` + +**Solution**: +```bash +# Create logs directory with correct permissions +mkdir -p logs +chmod 777 logs + +# Or run container as root (not recommended) +# Edit docker-compose.yml: +# user: root +``` + +## Production Deployment + +### Using Docker Swarm + +```bash +# Initialize swarm +docker swarm init + +# Deploy stack +docker stack deploy -c docker-compose.yml kosmi-relay + +# Check status +docker stack services kosmi-relay + +# View logs +docker service logs -f kosmi-relay_matterbridge +``` + +### Using Kubernetes + +Create `kosmi-relay.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kosmi-irc-relay +spec: + replicas: 1 + selector: + matchLabels: + app: kosmi-irc-relay + template: + metadata: + labels: + app: kosmi-irc-relay + spec: + containers: + - name: matterbridge + image: kosmi-irc-relay:latest + volumeMounts: + - name: config + mountPath: /app/matterbridge.toml + subPath: matterbridge.toml + env: + - name: CHROME_BIN + value: /usr/bin/chromium + - name: TZ + value: America/New_York + securityContext: + capabilities: + add: + - SYS_ADMIN # Required for Chrome + volumes: + - name: config + configMap: + name: matterbridge-config +``` + +Deploy: +```bash +kubectl create configmap matterbridge-config --from-file=matterbridge.toml +kubectl apply -f kosmi-relay.yaml +``` + +### Monitoring + +#### Health Check + +Add to `docker-compose.yml`: + +```yaml +services: + matterbridge: + healthcheck: + test: ["CMD", "pgrep", "-f", "matterbridge"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +``` + +#### Prometheus Metrics + +Matterbridge doesn't expose Prometheus metrics by default, but you can monitor: + +```bash +# Container metrics +docker stats kosmi-irc-relay + +# Log-based monitoring +docker-compose logs -f | grep -E "ERROR|WARN" +``` + +### Backup and Restore + +```bash +# Backup configuration +cp matterbridge.toml matterbridge.toml.backup + +# Backup logs +tar -czf logs-$(date +%Y%m%d).tar.gz logs/ + +# Restore configuration +cp matterbridge.toml.backup matterbridge.toml +docker-compose restart +``` + +## Security Best Practices + +1. **Run as non-root user** (already configured in Dockerfile) +2. **Use read-only configuration mount** +3. **Limit container resources** +4. **Keep Docker images updated** +5. **Use secrets for sensitive data** (e.g., IRC passwords) + +### Using Docker Secrets + +```bash +# Create secret +echo "your_irc_password" | docker secret create irc_password - + +# Update docker-compose.yml +services: + matterbridge: + secrets: + - irc_password + +secrets: + irc_password: + external: true +``` + +## Performance Tuning + +### Resource Limits + +```yaml +services: + matterbridge: + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + reservations: + cpus: '0.5' + memory: 256M +``` + +### Chrome Optimization + +Add to `docker-compose.yml`: + +```yaml +services: + matterbridge: + environment: + - CHROME_FLAGS=--disable-dev-shm-usage --no-sandbox --disable-setuid-sandbox +``` + +## Next Steps + +- ✅ Bridge is running in Docker +- 🔄 Set up monitoring and alerts +- 🔄 Configure log rotation +- 🔄 Set up automatic backups +- 🔄 Add more bridges (Discord, Slack, etc.) + +## Getting Help + +- Check logs: `docker-compose logs -f` +- Enable debug: Set `Debug=true` in `matterbridge.toml` +- Review `LESSONS_LEARNED.md` for common issues +- Check `QUICK_REFERENCE.md` for troubleshooting tips + +## Example: Complete Setup + +```bash +# 1. Clone repository +git clone kosmi-irc-relay +cd kosmi-irc-relay + +# 2. Edit configuration +nano matterbridge.toml +# Update RoomURL, IRC server, channel + +# 3. Build and start +docker-compose up -d + +# 4. Watch logs +docker-compose logs -f + +# 5. Test by sending messages in both Kosmi and IRC + +# 6. If issues, enable debug +nano matterbridge.toml # Set Debug=true +docker-compose restart +docker-compose logs -f +``` + +That's it! Your Kosmi-IRC bridge is now running in Docker! 🎉 + diff --git a/docs/archive/setup-backup-2026-05-10/DOCKER_QUICKSTART.md b/docs/archive/setup-backup-2026-05-10/DOCKER_QUICKSTART.md new file mode 100644 index 0000000..74bc015 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/DOCKER_QUICKSTART.md @@ -0,0 +1,129 @@ +# Docker Quick Start - 5 Minutes to Running Bridge + +Get your Kosmi-IRC bridge running in Docker in 5 minutes! + +## Prerequisites + +- Docker installed +- Docker Compose installed +- A Kosmi room URL +- An IRC channel + +## Steps + +### 1. Edit Configuration (2 minutes) + +Open `matterbridge.toml` and change these 3 things: + +```toml +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" # ← Your Kosmi room + +[irc.libera] +Server="irc.libera.chat:6667" # ← Your IRC server +Nick="kosmi-relay" # ← Your bot's nickname + +[[gateway.inout]] +account="irc.libera" +channel="#your-channel" # ← Your IRC channel +``` + +### 2. Build & Run (2 minutes) + +```bash +docker-compose up -d +``` + +### 3. Check It's Working (1 minute) + +```bash +docker-compose logs -f +``` + +Look for: +``` +INFO Successfully connected to Kosmi via Chrome +INFO Successfully connected to IRC +INFO Gateway(s) started successfully +``` + +### 4. Test It! + +- Send a message in Kosmi → should appear in IRC +- Send a message in IRC → should appear in Kosmi + +## That's It! 🎉 + +Your bridge is running! + +## Common Commands + +```bash +# View logs +docker-compose logs -f + +# Stop bridge +docker-compose down + +# Restart bridge +docker-compose restart + +# Rebuild after code changes +docker-compose build && docker-compose up -d +``` + +## Troubleshooting + +### "Connection failed" + +1. Check your configuration: + ```bash + cat matterbridge.toml | grep -E "RoomURL|Server|channel" + ``` + +2. Enable debug logging: + - Edit `matterbridge.toml`: Set `Debug=true` + - Restart: `docker-compose restart` + - Watch logs: `docker-compose logs -f` + +### "Chrome not found" + +```bash +# Rebuild image +docker-compose build --no-cache +docker-compose up -d +``` + +### "Messages not relaying" + +1. Check both bridges are connected: + ```bash + docker-compose logs | grep -i "connected" + ``` + +2. Verify channel names: + - Kosmi channel must be `"main"` + - IRC channel must include `#` (e.g., `"#your-channel"`) + +## Need More Help? + +- Full guide: See `DOCKER_DEPLOYMENT.md` +- Troubleshooting: See `QUICK_REFERENCE.md` +- Implementation details: See `LESSONS_LEARNED.md` + +## Example Output (Success) + +``` +INFO[...] Starting Matterbridge +INFO[...] Launching headless Chrome for Kosmi connection +INFO[...] Injecting WebSocket interceptor (runs before page load)... +INFO[...] ✓ WebSocket hook confirmed installed +INFO[...] Status: WebSocket connection intercepted +INFO[...] Successfully connected to Kosmi via Chrome +INFO[...] Connecting to IRC server irc.libera.chat:6667 +INFO[...] Successfully connected to IRC +INFO[...] Gateway(s) started successfully. Now relaying messages +``` + +Now send a test message and watch it relay! 🚀 + diff --git a/docs/archive/setup-backup-2026-05-10/JACKBOX_INTEGRATION.md b/docs/archive/setup-backup-2026-05-10/JACKBOX_INTEGRATION.md new file mode 100644 index 0000000..42c3975 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/JACKBOX_INTEGRATION.md @@ -0,0 +1,203 @@ +# Jackbox Game Picker API Integration + +This document describes how the Kosmi/IRC relay integrates with the Jackbox Game Picker API for live voting and game notifications. + +## Features + +### 1. Vote Detection +The relay automatically detects when users vote on games using the `thisgame++` or `thisgame--` syntax in either Kosmi or IRC chat. + +**How it works:** +- Users type `thisgame++` to upvote the current game +- Users type `thisgame--` to downvote the current game +- Votes are case-insensitive +- The relay filters out relayed messages (messages with `[irc]` or `[kosmi]` prefix) to prevent duplicate votes +- Only votes from actual users in each chat are sent to the API + +### 2. Game Notifications +When a new game is added to the Jackbox session via the API, the relay receives a webhook notification and broadcasts it to both Kosmi and IRC chats. + +**Example notification:** +``` +🎮 Coming up next: Fibbage 4! +``` + +## Configuration + +Add the following section to your `matterbridge.toml`: + +```toml +[jackbox] +# Enable Jackbox integration for vote detection and game notifications +Enabled=true + +# Jackbox API URL +APIURL="http://localhost:5000" + +# Admin password for API authentication +AdminPassword="your_admin_password_here" + +# Webhook server port (for receiving game notifications) +WebhookPort=3001 + +# Webhook secret for signature verification +WebhookSecret="your_webhook_secret_here" +``` + +### Configuration Options + +- **Enabled**: Set to `true` to enable the integration, `false` to disable +- **APIURL**: The URL of your Jackbox Game Picker API (e.g., `http://localhost:5000`) +- **AdminPassword**: Your API admin password (used to authenticate and get a JWT token) +- **WebhookPort**: Port for the webhook server to listen on (default: 3001) +- **WebhookSecret**: Shared secret for webhook signature verification (must match the secret configured in the API) + +## Setup Steps + +### 1. Configure the Relay + +Edit `matterbridge.toml` and add the Jackbox configuration section with your API URL, admin password, and webhook secret. + +### 2. Register the Webhook + +After starting the relay, register the webhook with the Jackbox API: + +```bash +# Get JWT token +curl -X POST "http://localhost:5000/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"apiKey": "your_admin_password"}' + +# Register webhook (use the JWT token from above) +curl -X POST "http://localhost:5000/api/webhooks" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Kosmi/IRC Relay", + "url": "http://your-relay-host:3001/webhook/jackbox", + "secret": "your_webhook_secret_here", + "events": ["game.added"] + }' +``` + +**Important:** Replace `your-relay-host` with the actual hostname or IP address where your relay is running. If the API and relay are on the same machine, you can use `localhost`. + +### 3. Test the Integration + +#### Test Vote Detection + +1. Start an active session in the Jackbox API with some games played +2. Send a message in Kosmi or IRC: `thisgame++` +3. Check the relay logs for: `Detected vote from : up` +4. Verify the vote was recorded in the API + +#### Test Game Notifications + +1. Add a game to the active session via the Jackbox API +2. Both Kosmi and IRC chats should receive a notification: `🎮 Coming up next: !` + +## How It Works + +### Vote Flow + +1. User sends a message containing `thisgame++` or `thisgame--` in Kosmi or IRC +2. The bridge detects the vote pattern (case-insensitive) +3. The bridge checks if the message is relayed (has `[irc]` or `[kosmi]` prefix) +4. If not relayed, the bridge extracts the username and vote type +5. The bridge sends the vote to the Jackbox API via HTTP POST to `/api/votes/live` +6. The API records the vote and associates it with the current game based on timestamp + +### Notification Flow + +1. A game is added to an active session in the Jackbox API +2. The API sends a webhook POST request to `http://your-relay-host:3001/webhook/jackbox` +3. The webhook includes an HMAC-SHA256 signature in the `X-Webhook-Signature` header +4. The relay verifies the signature using the configured webhook secret +5. If valid, the relay parses the `game.added` event +6. The relay broadcasts the game announcement to all connected bridges (Kosmi and IRC) + +## Security + +### Webhook Signature Verification + +All incoming webhooks are verified using HMAC-SHA256 signatures to ensure they come from the legitimate Jackbox API. + +**How it works:** +1. The API computes `HMAC-SHA256(webhook_secret, request_body)` +2. The signature is sent in the `X-Webhook-Signature` header as `sha256=` +3. The relay computes the expected signature using the same method +4. The relay uses timing-safe comparison to verify the signatures match +5. If verification fails, the webhook is rejected with a 401 Unauthorized response + +### JWT Authentication + +The relay authenticates with the Jackbox API using the admin password to obtain a JWT token. This token is: +- Cached to avoid re-authentication on every vote +- Automatically refreshed if it expires (detected via 401 response) +- Valid for 24 hours (configurable in the API) + +## Troubleshooting + +### Votes Not Being Recorded + +**Possible causes:** +- No active session in the Jackbox API +- Vote timestamp doesn't match any played game +- Duplicate vote within 1 second +- Authentication failure + +**Check:** +1. Relay logs for vote detection: `grep "Detected vote" logs/matterbridge.log` +2. Relay logs for API errors: `grep "Failed to send vote" logs/matterbridge.log` +3. API logs for incoming vote requests + +### Webhooks Not Being Received + +**Possible causes:** +- Webhook URL is not accessible from the API server +- Webhook not registered in the API +- Signature verification failing +- Firewall blocking the webhook port + +**Check:** +1. Verify webhook is registered: `GET /api/webhooks` (with JWT token) +2. Test webhook manually: `POST /api/webhooks/test/:id` (with JWT token) +3. Check webhook logs in API: `GET /api/webhooks/:id/logs` (with JWT token) +4. Verify webhook server is listening: `curl http://localhost:3001/health` +5. Check relay logs for signature verification errors + +### Authentication Failures + +**Possible causes:** +- Incorrect admin password in configuration +- API is not running or not accessible + +**Check:** +1. Verify API URL is correct and accessible +2. Test authentication manually: + ```bash + curl -X POST "http://localhost:5000/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"apiKey": "your_admin_password"}' + ``` +3. Check relay logs for authentication errors + +## Logs + +The relay logs all Jackbox-related activity with the `jackbox` prefix: + +``` +[jackbox] Initializing Jackbox integration... +[jackbox] Successfully authenticated with Jackbox API +[jackbox] Starting Jackbox webhook server on port 3001 +[kosmi] Detected vote from Anonymous Llama: up +[jackbox] Vote recorded for Fibbage 4: Anonymous Llama - 5👍 2👎 +[jackbox] Broadcasting Jackbox message: 🎮 Coming up next: Quiplash 3! +``` + +## Disabling the Integration + +To disable the Jackbox integration, set `Enabled=false` in the `[jackbox]` section of `matterbridge.toml` and restart the relay. + +The relay will continue to function normally for Kosmi ↔ IRC message relay without any Jackbox features. + diff --git a/docs/archive/setup-backup-2026-05-10/MUTE_CONTROL.md b/docs/archive/setup-backup-2026-05-10/MUTE_CONTROL.md new file mode 100644 index 0000000..d4fcda4 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/MUTE_CONTROL.md @@ -0,0 +1,225 @@ +# Mute Control for Jackbox Announcements + +The bot supports muting Jackbox announcements without restarting. This is useful when you want to test the Jackbox API or run games without spamming the chat. + +## Features + +- ✅ Start the bot muted +- ✅ Toggle mute/unmute while running +- ✅ Works in terminal and Docker +- ✅ Vote detection still works (votes are sent to API even when muted) +- ✅ Only Jackbox announcements are muted (IRC ↔ Kosmi relay still works) + +## Starting Muted + +### Terminal +```bash +./matterbridge -conf matterbridge.toml -muted +``` + +### Docker +Update `docker-compose.yml`: +```yaml +services: + kosmi-irc-relay: + command: ["/app/matterbridge", "-conf", "/app/matterbridge.toml", "-muted"] +``` + +Or run with override: +```bash +docker-compose run --rm kosmi-irc-relay /app/matterbridge -conf /app/matterbridge.toml -muted +``` + +## Toggling Mute While Running + +The bot listens for the `SIGUSR1` signal to toggle mute status. + +### Terminal (Local Process) + +**Find the process ID:** +```bash +ps aux | grep matterbridge +# or +pgrep matterbridge +``` + +**Toggle mute:** +```bash +kill -SIGUSR1 +``` + +**Example:** +```bash +$ pgrep matterbridge +12345 + +$ kill -SIGUSR1 12345 +# Bot logs: 🔇 Jackbox announcements MUTED + +$ kill -SIGUSR1 12345 +# Bot logs: 🔊 Jackbox announcements UNMUTED +``` + +### Docker + +**Find the container name:** +```bash +docker ps +``` + +**Toggle mute:** +```bash +docker kill -s SIGUSR1 +``` + +**Example:** +```bash +$ docker ps +CONTAINER ID IMAGE COMMAND NAMES +abc123def456 kosmi-irc-relay "/app/matterbridge -…" kosmi-irc-relay + +$ docker kill -s SIGUSR1 kosmi-irc-relay +# Bot logs: 🔇 Jackbox announcements MUTED + +$ docker kill -s SIGUSR1 kosmi-irc-relay +# Bot logs: 🔊 Jackbox announcements UNMUTED +``` + +## What Gets Muted? + +### Muted Messages ❌ +- 🎮 Game Night is starting! +- 🎮 Coming up next: [Game] - Room Code [CODE] +- 🗳️ Final votes for [Game]: X👍 Y👎 +- 🌙 Game Night has ended! Thanks for playing! + +### Still Works ✅ +- Vote detection (`thisgame++` / `thisgame--`) +- Votes sent to Jackbox API +- IRC ↔ Kosmi message relay +- All other bot functionality + +## Log Messages + +**When starting muted:** +``` +INFO Jackbox announcements started MUTED (use SIGUSR1 to toggle) +INFO Signal handler ready: Send SIGUSR1 to toggle mute (kill -SIGUSR1 or docker kill -s SIGUSR1 ) +``` + +**When toggling to muted:** +``` +WARN 🔇 Jackbox announcements MUTED +``` + +**When toggling to unmuted:** +``` +INFO 🔊 Jackbox announcements UNMUTED +``` + +**When a message is suppressed:** +``` +DEBUG Jackbox message suppressed (muted): 🎮 Coming up next: Drawful 2 - Room Code C0D3 +``` + +## Use Cases + +### Testing Jackbox API +```bash +# Start muted +docker-compose up -d + +# Test vote detection without spamming chat +# (votes are still sent to API) +# In chat: "thisgame++" + +# Check logs to see votes are being processed +docker logs kosmi-irc-relay -f + +# Unmute when ready +docker kill -s SIGUSR1 kosmi-irc-relay +``` + +### Running Private Games +```bash +# Mute during game setup +docker kill -s SIGUSR1 kosmi-irc-relay + +# Play games without announcements +# (useful if you're testing or running a private session) + +# Unmute for public game night +docker kill -s SIGUSR1 kosmi-irc-relay +``` + +### Quick Mute Script +Create a helper script `mute-toggle.sh`: +```bash +#!/bin/bash +docker kill -s SIGUSR1 kosmi-irc-relay +docker logs kosmi-irc-relay --tail 1 +``` + +Make it executable: +```bash +chmod +x mute-toggle.sh +``` + +Use it: +```bash +./mute-toggle.sh +# 🔇 Jackbox announcements MUTED + +./mute-toggle.sh +# 🔊 Jackbox announcements UNMUTED +``` + +## Troubleshooting + +### Signal not working in Docker +Make sure your Docker container is running: +```bash +docker ps | grep kosmi-irc-relay +``` + +If the container is restarting, check logs: +```bash +docker logs kosmi-irc-relay +``` + +### Signal not working locally +Make sure the process is running: +```bash +ps aux | grep matterbridge +``` + +Check you're using the correct PID: +```bash +pgrep -f matterbridge +``` + +### Mute state not persisting after restart +Mute state is **not persisted** across restarts. If you restart the bot: +- Without `-muted` flag: Bot starts unmuted +- With `-muted` flag: Bot starts muted + +This is intentional - you probably want announcements by default. + +## Advanced: Systemd Service + +If running as a systemd service, you can use `systemctl`: + +**Create a mute toggle script:** +```bash +#!/bin/bash +# /usr/local/bin/matterbridge-mute-toggle +PID=$(systemctl show -p MainPID --value matterbridge.service) +kill -SIGUSR1 $PID +journalctl -u matterbridge.service -n 1 --no-pager +``` + +**Use it:** +```bash +sudo /usr/local/bin/matterbridge-mute-toggle +``` + diff --git a/docs/archive/setup-backup-2026-05-10/QUICKSTART.md b/docs/archive/setup-backup-2026-05-10/QUICKSTART.md new file mode 100644 index 0000000..c9d5609 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/QUICKSTART.md @@ -0,0 +1,301 @@ +# Quick Start Guide + +Get the Kosmi-IRC bridge running in minutes! + +## Prerequisites + +- Go 1.21 or higher +- Chrome/Chromium browser installed (for headless browser automation) +- Access to a Kosmi room +- (Optional) IRC server access for full relay + +## Step 1: Test Kosmi Connection + +First, verify the bridge can connect to Kosmi: + +```bash +# Build the test program +go build -o test-kosmi ./cmd/test-kosmi + +# Run with your Kosmi room URL +./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug +``` + +You should see output like: +``` +INFO[...] Starting Kosmi bridge test +INFO[...] Launching headless Chrome for Kosmi connection +INFO[...] Injecting WebSocket interceptor (runs before page load)... +INFO[...] Navigating to Kosmi room: https://app.kosmi.io/room/@hyperspaceout +INFO[...] ✓ WebSocket hook confirmed installed +INFO[...] Status: WebSocket connection intercepted +INFO[...] Successfully connected to Kosmi via Chrome +INFO[...] Listening for messages... Press Ctrl+C to exit +``` + +**Test it**: Send a message in the Kosmi room from your browser. You should see it appear in the terminal like: +``` +INFO[...] Received message: [00:02:51] username: [Kosmi] your message here +``` + +## Step 2: Integrate with Full Matterbridge + +### Option A: Copy into Existing Matterbridge + +If you already have Matterbridge: + +```bash +# Navigate to your Matterbridge directory +cd /path/to/matterbridge + +# Copy the Kosmi bridge +cp -r /path/to/irc-kosmi-relay/bridge/kosmi bridge/ + +# Copy the bridge registration +cp /path/to/irc-kosmi-relay/gateway/bridgemap/bkosmi.go gateway/bridgemap/ + +# Add dependencies +go get github.com/chromedp/chromedp@v0.11.2 +go mod tidy + +# Build +go build +``` + +### Option B: Use This Repository + +This repository has a minimal Matterbridge structure. To add IRC support: + +1. Copy IRC bridge from Matterbridge: + ```bash + # From the Matterbridge repo + cp -r bridge/irc /path/to/irc-kosmi-relay/bridge/ + cp gateway/bridgemap/birc.go /path/to/irc-kosmi-relay/gateway/bridgemap/ + ``` + +2. Update dependencies: + ```bash + cd /path/to/irc-kosmi-relay + go mod tidy + ``` + +## Step 3: Configure + +Edit `matterbridge.toml`: + +```toml +# Kosmi configuration +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" + +# IRC configuration +[irc.libera] +Server="irc.libera.chat:6667" +Nick="kosmi-bot" +UseTLS=false + +# Gateway to connect them +[[gateway]] +name="kosmi-irc-relay" +enable=true + +[[gateway.inout]] +account="kosmi.hyperspaceout" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#your-channel" +``` + +**Important**: Replace: +- `https://app.kosmi.io/room/@hyperspaceout` with your Kosmi room URL +- `#your-channel` with your IRC channel + +## Step 4: Run + +```bash +./matterbridge -conf matterbridge.toml +``` + +Or with debug logging: +```bash +./matterbridge -conf matterbridge.toml -debug +``` + +## Step 5: Test the Relay + +1. **Kosmi → IRC**: Send a message in Kosmi. It should appear in IRC as: + ``` + [Kosmi] your message here + ``` + +2. **IRC → Kosmi**: Send a message in IRC. It should appear in Kosmi as: + ``` + [IRC] your message here + ``` + +## Troubleshooting + +### Test program doesn't connect + +**Check**: +- Is Chrome/Chromium installed and accessible? +- Is the room URL correct? +- Can you access `app.kosmi.io` from your network? +- Try with `-debug` flag for more details + +**Solution**: +```bash +# Test Chrome installation +which google-chrome chromium chromium-browser + +# Test network connectivity +curl -I https://app.kosmi.io + +# Run with debug +./test-kosmi -room "YOUR_ROOM_URL" -debug +``` + +### Messages not relaying + +**Check**: +- Are both bridges connected? Look for "Successfully connected" in logs +- Is the gateway configuration correct? +- Are the channel names correct? + +**Solution**: +```bash +# Run with debug to see message flow +./matterbridge -conf matterbridge.toml -debug + +# Look for lines like: +# "Received message from Kosmi" +# "Forwarding to Matterbridge" +# "Sending to IRC" +``` + +### "Room ID extraction failed" + +**Check**: Room URL format + +**Supported formats**: +- `https://app.kosmi.io/room/@roomname` +- `https://app.kosmi.io/room/roomid` +- `@roomname` +- `roomid` + +**Solution**: Use the full URL from your browser's address bar + +### Messages not appearing from Kosmi + +**Check**: +- Is the WebSocket hook installed? Look for "✓ WebSocket hook confirmed installed" +- Is the WebSocket connection detected? Look for "Status: WebSocket connection intercepted" +- Are messages being captured? Enable debug logging to see message processing + +**Solution**: +The bridge uses headless Chrome with a WebSocket interceptor that runs **before page load**. This is critical for capturing messages. The implementation uses `Page.addScriptToEvaluateOnNewDocument` to ensure the hook is installed before any page JavaScript executes. + +If messages still aren't appearing: +1. Check Chrome console logs in debug mode +2. Verify the room URL is correct +3. Try sending a test message and watch the debug output + +### Cannot send messages to Kosmi + +The send functionality uses the headless Chrome instance to inject messages into the Kosmi chat input field. + +**To debug**: +1. Enable debug logging with `-debug` flag +2. Look for "Sending message to Kosmi" in logs +3. Check for any JavaScript errors in the browser console logs + +## Next Steps + +- **Customize message format**: Edit the format strings in `kosmi.go` +- **Add more bridges**: Matterbridge supports Discord, Slack, Telegram, etc. +- **Set up as a service**: Use systemd or similar to run automatically +- **Monitor logs**: Set up log rotation and monitoring + +## Getting Help + +- Check `INTEGRATION.md` for detailed integration steps +- Check `README.md` for architecture details +- Enable debug logging for detailed troubleshooting +- Review the chrome extension code in `.examples/` for API details + +## Common Use Cases + +### Home Server Setup + +```toml +# Bridge your Kosmi room with your home IRC server +[kosmi.gamenight] +RoomURL="https://app.kosmi.io/room/@gamenight" + +[irc.home] +Server="irc.home.local:6667" +Nick="kosmi-relay" +UseTLS=false + +[[gateway]] +name="gamenight" +enable=true + +[[gateway.inout]] +account="kosmi.gamenight" +channel="main" + +[[gateway.inout]] +account="irc.home" +channel="#gamenight" +``` + +### Multiple Rooms + +```toml +# Bridge multiple Kosmi rooms +[kosmi.room1] +RoomURL="https://app.kosmi.io/room/@room1" + +[kosmi.room2] +RoomURL="https://app.kosmi.io/room/@room2" + +[irc.libera] +Server="irc.libera.chat:6667" +Nick="kosmi-bot" + +# Gateway for room1 +[[gateway]] +name="gateway1" +enable=true + +[[gateway.inout]] +account="kosmi.room1" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#room1" + +# Gateway for room2 +[[gateway]] +name="gateway2" +enable=true + +[[gateway.inout]] +account="kosmi.room2" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#room2" +``` + +## Success! + +If you see messages flowing both ways, congratulations! Your Kosmi-IRC bridge is working. 🎉 + +For advanced configuration and features, see the full documentation in `README.md` and `INTEGRATION.md`. + diff --git a/docs/archive/setup-backup-2026-05-10/QUICK_REFERENCE.md b/docs/archive/setup-backup-2026-05-10/QUICK_REFERENCE.md new file mode 100644 index 0000000..c786e72 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/QUICK_REFERENCE.md @@ -0,0 +1,237 @@ +# Quick Reference Guide + +## Testing the Bridge + +### Build and Run Test Program + +```bash +cd /Users/erikfredericks/dev-ai/HSO/irc-kosmi-relay +go build -o test-kosmi ./cmd/test-kosmi +./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug +``` + +### Expected Output (Success) + +``` +INFO Launching headless Chrome for Kosmi connection +INFO Injecting WebSocket interceptor (runs before page load)... +INFO Navigating to Kosmi room: https://app.kosmi.io/room/@hyperspaceout +INFO ✓ WebSocket hook confirmed installed +INFO Status: WebSocket connection intercepted +INFO Successfully connected to Kosmi via Chrome +INFO Listening for messages... Press Ctrl+C to exit +``` + +### When Messages Arrive + +``` +INFO Processing 1 messages from queue +INFO Received message: [00:02:51] username: [Kosmi] message text +``` + +## Key Status Indicators + +| Status Message | Meaning | Action | +|---------------|---------|--------| +| `✓ WebSocket hook confirmed installed` | Hook script is active | ✅ Good | +| `Status: WebSocket connection intercepted` | WebSocket is being captured | ✅ Good | +| `Status: No WebSocket connection detected yet` | Hook missed the WebSocket | ❌ Check injection timing | +| `Processing N messages from queue` | Messages are being captured | ✅ Good | + +## Common Issues + +### Issue: "No WebSocket connection detected yet" + +**Cause**: WebSocket hook was injected too late + +**Fix**: Verify `injectWebSocketHookBeforeLoad()` uses `page.AddScriptToEvaluateOnNewDocument` + +### Issue: "Chrome not found" + +**Cause**: Chrome/Chromium not installed or not in PATH + +**Fix**: +```bash +# macOS +brew install --cask google-chrome + +# Ubuntu/Debian +sudo apt install chromium-browser + +# Verify installation +which google-chrome chromium chromium-browser +``` + +### Issue: Messages not appearing + +**Cause**: Multiple possibilities + +**Debug**: +1. Check for "✓ WebSocket hook confirmed installed" ✓ +2. Check for "Status: WebSocket connection intercepted" ✓ +3. Enable debug logging: `-debug` flag +4. Send a test message in the Kosmi room +5. Look for "Processing N messages from queue" + +## Configuration + +### Minimal Test Configuration + +```toml +[kosmi.test] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" +Debug=true +``` + +### Full Matterbridge Configuration + +```toml +# Kosmi +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" + +# IRC +[irc.libera] +Server="irc.libera.chat:6667" +Nick="kosmi-relay" +UseTLS=false + +# Gateway +[[gateway]] +name="kosmi-irc" +enable=true + +[[gateway.inout]] +account="kosmi.hyperspaceout" +channel="main" + +[[gateway.inout]] +account="irc.libera" +channel="#your-channel" +``` + +## Message Format + +### Kosmi → IRC + +``` +[Kosmi] message text +``` + +### IRC → Kosmi + +``` +[IRC] message text +``` + +## File Locations + +| File | Purpose | +|------|---------| +| `bridge/kosmi/kosmi.go` | Main bridge implementation | +| `bridge/kosmi/chromedp_client.go` | Headless Chrome client | +| `bridge/kosmi/graphql.go` | GraphQL structures (legacy) | +| `cmd/test-kosmi/main.go` | Standalone test program | +| `matterbridge.toml` | Configuration file | + +## Key Implementation Details + +### WebSocket Hook Injection + +**MUST** use `page.AddScriptToEvaluateOnNewDocument` to inject **before page load**: + +```go +chromedp.ActionFunc(func(ctx context.Context) error { + _, err := page.AddScriptToEvaluateOnNewDocument(script).Do(ctx) + return err +}) +``` + +### Hook Script + +Wraps `window.WebSocket` constructor to intercept all WebSocket connections: + +```javascript +window.WebSocket = function(url, protocols) { + const socket = new OriginalWebSocket(url, protocols); + // ... interception logic ... + return socket; +}; +``` + +## Debugging Commands + +```bash +# Test Chrome installation +which google-chrome chromium chromium-browser + +# Test network connectivity +curl -I https://app.kosmi.io + +# Build with verbose output +go build -v -o test-kosmi ./cmd/test-kosmi + +# Run with debug logging +./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug + +# Check for linter errors +go vet ./... +``` + +## Performance Notes + +- **Chrome Startup**: ~1-2 seconds +- **Page Load**: ~1-2 seconds +- **Message Latency**: ~100-500ms +- **Memory Usage**: ~100-200MB (Chrome process) + +## Security Considerations + +- Bridge runs Chrome in headless mode (no GUI) +- No credentials stored (anonymous access) +- WebSocket traffic is intercepted in memory only +- Messages are not logged to disk (unless debug logging enabled) + +## Production Deployment + +### systemd Service Example + +```ini +[Unit] +Description=Kosmi-IRC Relay +After=network.target + +[Service] +Type=simple +User=matterbridge +WorkingDirectory=/opt/matterbridge +ExecStart=/opt/matterbridge/matterbridge -conf /etc/matterbridge/matterbridge.toml +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +### Docker Considerations + +When running in Docker, ensure: +- Chrome/Chromium is installed in the container +- `--no-sandbox` flag may be needed for Chrome +- Sufficient memory allocation (512MB minimum) + +## Resources + +- **Documentation**: See `README.md`, `QUICKSTART.md`, `LESSONS_LEARNED.md` +- **Integration Guide**: See `INTEGRATION.md` +- **Implementation Details**: See `IMPLEMENTATION_SUMMARY.md` +- **ChromeDP Guide**: See `CHROMEDP_IMPLEMENTATION.md` + +## Support + +For issues: +1. Check this quick reference +2. Review `LESSONS_LEARNED.md` for common patterns +3. Enable debug logging for detailed output +4. Check Chrome console logs in debug mode + diff --git a/docs/archive/setup-backup-2026-05-10/QUICK_START_AUTH.md b/docs/archive/setup-backup-2026-05-10/QUICK_START_AUTH.md new file mode 100644 index 0000000..796a638 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/QUICK_START_AUTH.md @@ -0,0 +1,94 @@ +# Quick Start: Testing Authentication + +## Step 1: Create Bot Account + +1. Go to https://app.kosmi.io +2. Sign up with a dedicated email (e.g., `your-bot@example.com`) +3. Choose a display name (e.g., "HSO Relay Bot") +4. Save the credentials securely + +## Step 2: Test with Monitor Script + +```bash +cd /Users/erikfredericks/dev-ai/HSO/irc-kosmi-relay + +# Run monitor in login mode +./bin/monitor-auth -login + +# In the browser that opens: +# 1. Log in with your bot credentials +# 2. Navigate to a room +# 3. Press Ctrl+C to stop + +# Review the captured data +cat auth-monitor.log | grep -A 5 "login" +``` + +## Step 3: Configure Matterbridge + +Edit `matterbridge.toml`: + +```toml +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@hyperspaceout" +Email="your-bot@example.com" +Password="your-secure-password" +``` + +## Step 4: Test Connection + +```bash +# Build the bridge +go build + +# Run with your config +./matterbridge -conf matterbridge.toml + +# Watch the logs for: +# - "Using authenticated connection" +# - "Logged in as: HSO Relay Bot" +# - "Successfully connected to Kosmi" +``` + +## Verification Checklist + +- [ ] Bot account created manually +- [ ] Credentials documented securely +- [ ] Monitor script captured login flow +- [ ] Config file updated with credentials +- [ ] Bridge logs show authenticated connection +- [ ] Bot display name appears correctly in chat +- [ ] Messages relay successfully + +## Troubleshooting + +### Wrong account logged in + +Check the log for "Logged in as: {name}". If it doesn't match your bot: +- Verify email/password in config +- Check for typos +- Ensure you're using the correct credentials + +### Anonymous connection despite credentials + +Check that both Email AND Password are set: +```bash +grep -A 2 "Email=" matterbridge.toml +``` + +### Token expired + +The bridge should auto-refresh. If not: +- Check logs for "Token refresh failed" +- Verify credentials are still valid +- Try manual login at app.kosmi.io + +## Next Steps + +Once authenticated connection works: +- Test reconnection (simulate network failure) +- Monitor for token refresh (wait 24 hours) +- Test with multiple rooms +- Set up as systemd service + +See the monitoring script output and logs for detailed information about Kosmi's authentication behavior. diff --git a/docs/archive/setup-backup-2026-05-10/ROOM_CODE_IMAGE_FEATURE.md b/docs/archive/setup-backup-2026-05-10/ROOM_CODE_IMAGE_FEATURE.md new file mode 100644 index 0000000..fd62267 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/ROOM_CODE_IMAGE_FEATURE.md @@ -0,0 +1,141 @@ +# Room Code Image Feature + +## Overview + +The bot now supports displaying Jackbox room codes as images in Kosmi chat (with fallback to IRC text formatting for IRC chat). + +## How It Works + +### For Kosmi Chat (with `EnableRoomCodeImage=true`) + +1. **Generate**: When a new game is added, the bot generates a PNG image of the room code using a monospace font (black background, white text) +2. **Upload**: The image is uploaded to Kosmi's CDN at `https://img.kosmi.io/` +3. **Broadcast**: The bot sends two messages: + - First: The game announcement (e.g., "🎮 Coming up next: Drawful 2!") + - Second: The image URL (Kosmi automatically displays it as a thumbnail) + +### For IRC Chat (always) + +Room codes are displayed with IRC formatting: +- **Bold** (`\x02`) +- **Monospace** (`\x11`) +- **Reset** (`\x0F`) + +Example: `\x02\x11ROOM42\x0F` displays as **`ROOM42`** in IRC clients + +### Fallback Behavior + +If image generation or upload fails: +- The bot falls back to IRC text formatting for all chats +- An error is logged but the announcement still goes through + +## Configuration + +In `matterbridge.toml`: + +```toml +[jackbox] +Enabled=true +APIURL="https://your-jackbox-api.com" +AdminPassword="your-password" +UseWebSocket=true +EnableRoomCodeImage=false # Set to true to enable image uploads +``` + +## Files Involved + +### New Files +- `bridge/jackbox/roomcode_image.go` - PNG image generation +- `bridge/jackbox/image_upload.go` - HTTP upload to Kosmi CDN +- `bridge/irc/formatting.go` - IRC formatting helpers +- `bridge/kosmi/image_upload.go` - (Duplicate, can be removed) +- `KOSMI_IMAGE_UPLOAD.md` - Protocol documentation + +### Modified Files +- `bridge/jackbox/websocket_client.go` - Image upload integration +- `bridge/jackbox/manager.go` - Config passing +- `matterbridge.toml` - Added `EnableRoomCodeImage` setting + +### Test Files +- `cmd/test-roomcode-image/main.go` - Test image generation +- `cmd/test-image-upload/main.go` - Test full upload flow + +## Testing + +### Test Image Generation +```bash +./test-roomcode-image +# Generates: roomcode_ABCD.png, roomcode_TEST.png, etc. +``` + +### Test Image Upload +```bash +./test-image-upload +# Generates image, uploads to Kosmi CDN, displays URL +``` + +### Test Full Integration +1. Set `EnableRoomCodeImage=true` in `matterbridge.toml` +2. Start the bot: `./matterbridge -conf matterbridge.toml` +3. Add a game in the Jackbox Picker with a room code +4. Verify: + - Kosmi chat shows the game announcement + image thumbnail + - IRC chat shows the game announcement + formatted room code text + +## Technical Details + +### Image Specifications +- Format: PNG +- Size: 200x80 pixels +- Background: Black (`#000000`) +- Text: White (`#FFFFFF`) +- Font: `basicfont.Face7x13` (monospace) +- Typical file size: ~400-500 bytes + +### Upload Endpoint +- URL: `https://img.kosmi.io/` +- Method: `POST` +- Content-Type: `multipart/form-data` +- Authentication: None required (CORS-protected) +- Response: `{"filename": "uuid.webp"}` +- Full URL: `https://img.kosmi.io/{filename}` + +### Performance +- Image generation: <1ms +- Image upload: ~300-600ms (network dependent) +- Total delay: Minimal, upload happens asynchronously + +## Future Enhancements + +Potential improvements: +1. **Custom fonts**: Use a better monospace font (requires embedding TTF) +2. **Styling**: Add Jackbox branding, colors, or borders +3. **Caching**: Cache uploaded images to avoid re-uploading identical room codes +4. **Retry logic**: Add retry mechanism for failed uploads +5. **Compression**: Optimize PNG compression for smaller file sizes + +## Troubleshooting + +### Images not appearing in Kosmi +- Check that `EnableRoomCodeImage=true` in config +- Check logs for upload errors +- Verify network connectivity to `https://img.kosmi.io/` +- Test manually with `./test-image-upload` + +### IRC formatting not working +- Ensure your IRC client supports formatting codes +- Some clients require enabling "Show colors/formatting" +- Fallback: The room code is still readable without formatting + +### Build errors +- Ensure all dependencies are installed: `go mod tidy` +- Check Go version: Requires Go 1.19+ +- Verify `golang.org/x/image` is available + +## References + +- [Kosmi Image Upload Protocol](KOSMI_IMAGE_UPLOAD.md) +- [IRC Formatting Codes](https://modern.ircdocs.horse/formatting.html) +- [Go image package](https://pkg.go.dev/image) +- [Jackbox Integration](JACKBOX_INTEGRATION.md) + diff --git a/docs/archive/setup-backup-2026-05-10/TEST_INSTRUCTIONS.md b/docs/archive/setup-backup-2026-05-10/TEST_INSTRUCTIONS.md new file mode 100644 index 0000000..d86e396 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/TEST_INSTRUCTIONS.md @@ -0,0 +1,255 @@ +# Message Relay Testing Instructions + +**Bridge Status**: ✅ **ACTIVE AND READY** + +Both Kosmi and IRC are connected. The gateway is relaying messages. + +## Current Configuration + +- **Kosmi Room**: https://app.kosmi.io/room/@hyperspaceout +- **IRC Server**: irc.zeronode.net:6697 +- **IRC Channel**: #cottongin +- **Status**: Both bridges connected and relaying + +## Test 1: Kosmi → IRC + +### Steps: +1. Open the Kosmi room in your browser: https://app.kosmi.io/room/@hyperspaceout +2. Type a message in the chat (e.g., "Test message from Kosmi 🚀") +3. Press Enter to send + +### Expected Result: +- The message should appear in IRC channel #cottongin +- The logs will show: + ``` + level=info msg="📨 Received message from Kosmi: ..." prefix=kosmi + level=info msg="Relaying message from kosmi to irc" + ``` + +### How to Verify: +Connect to IRC and join #cottongin: +```bash +# Using an IRC client (e.g., irssi, weechat, hexchat) +/connect irc.zeronode.net 6697 +/join #cottongin +``` + +Or watch the Docker logs: +```bash +docker-compose logs -f | grep -E "(Received|Relaying|Sent)" +``` + +--- + +## Test 2: IRC → Kosmi + +### Steps: +1. Connect to IRC: `irc.zeronode.net:6697` +2. Join channel: `/join #cottongin` +3. Send a message: "Test message from IRC 👋" + +### Expected Result: +- The message should appear in the Kosmi chat room +- The logs will show: + ``` + level=info msg="Received message from IRC: ..." prefix=irc + level=info msg="Relaying message from irc to kosmi" + level=info msg="✅ Sent message via Playwright-assisted WebSocket: ..." prefix=kosmi + ``` + +### How to Verify: +- Open Kosmi room in browser: https://app.kosmi.io/room/@hyperspaceout +- Check if your IRC message appears in the chat + +--- + +## Watch Logs in Real-Time + +```bash +# All logs +docker-compose logs -f + +# Only message-related logs +docker-compose logs -f | grep -E "(message|Message|Received|Sent|Relaying)" + +# Last 50 lines +docker-compose logs --tail=50 +``` + +--- + +## Quick IRC Connection Methods + +### Method 1: Web IRC Client +1. Go to: https://web.libera.chat/ +2. Connect to: irc.zeronode.net (port 6697, SSL) +3. Join: #cottongin + +### Method 2: Command-Line (irssi) +```bash +# Install irssi (if not installed) +brew install irssi # macOS +# or +apt-get install irssi # Linux + +# Connect +irssi -c irc.zeronode.net -p 6697 +/join #cottongin +``` + +### Method 3: Command-Line (nc for quick test) +```bash +# Simple netcat connection (read-only) +openssl s_client -connect irc.zeronode.net:6697 +# Then type: +NICK testuser +USER testuser 0 * :Test User +JOIN #cottongin +``` + +--- + +## What You Should See + +### In Docker Logs (Normal Operation): + +``` +✅ WebSocket established and ready! +✅ Native client fully connected! +Successfully connected to Kosmi +Connection succeeded (IRC) +irc.libera: joining #cottongin +Gateway(s) started successfully. Now relaying messages +``` + +### When a Message is Relayed (Kosmi → IRC): + +``` +level=info msg="📨 Received message from Kosmi: username: message text" prefix=kosmi +level=info msg="Handling message from kosmi.hyperspaceout" prefix=router +level=info msg="Relaying message to irc.libera" prefix=router +level=info msg="Sent message to #cottongin" prefix=irc +``` + +### When a Message is Relayed (IRC → Kosmi): + +``` +level=info msg="Received message from #cottongin: username: message text" prefix=irc +level=info msg="Handling message from irc.libera" prefix=router +level=info msg="Relaying message to kosmi.hyperspaceout" prefix=router +level=info msg="✅ Sent message via Playwright-assisted WebSocket: message text" prefix=kosmi +``` + +--- + +## Troubleshooting + +### No Messages Appearing + +1. **Check bridge is running**: + ```bash + docker-compose ps + ``` + Should show `kosmi-irc-relay` as "Up" + +2. **Check logs for errors**: + ```bash + docker-compose logs --tail=100 + ``` + +3. **Verify connections**: + ```bash + docker-compose logs | grep -E "(Connected|connected|joined)" + ``` + Should show both Kosmi WebSocket and IRC connected + +4. **Restart if needed**: + ```bash + docker-compose restart + docker-compose logs -f + ``` + +### Messages Only Going One Direction + +- **If Kosmi → IRC works but IRC → Kosmi doesn't**: + - Check Kosmi WebSocket is still connected: `docker-compose logs | grep WebSocket` + - The native client might have disconnected + - Restart: `docker-compose restart` + +- **If IRC → Kosmi works but Kosmi → IRC doesn't**: + - Check IRC connection: `docker-compose logs | grep irc` + - Verify IRC authentication + +### Message Formatting Issues + +Messages are formatted as: +``` + message text +``` + +This is configurable in `matterbridge.toml` under the gateway settings. + +--- + +## Success Criteria + +✅ **Full Success** means: +1. Message sent in Kosmi appears in IRC #cottongin +2. Message sent in IRC #cottongin appears in Kosmi room +3. Usernames are preserved/displayed correctly +4. Messages appear within 1-2 seconds +5. No errors in logs + +--- + +## Next Steps After Testing + +Once both directions work: + +1. **Monitor stability** - Let it run for a few hours +2. **Check memory usage**: `docker stats kosmi-irc-relay` +3. **Review message formatting** - Adjust in `matterbridge.toml` if needed +4. **Set up log rotation** - For long-term operation +5. **Consider adding more channels** - Edit `matterbridge.toml` + +--- + +## Quick Commands Reference + +```bash +# Start in background +docker-compose up -d + +# Follow logs +docker-compose logs -f + +# Stop +docker-compose down + +# Restart +docker-compose restart + +# Rebuild +docker-compose up --build -d + +# Check status +docker-compose ps + +# View resource usage +docker stats kosmi-irc-relay +``` + +--- + +## Ready to Test! + +The bridge is **running and connected**. You can start testing immediately: + +1. 🌐 Open: https://app.kosmi.io/room/@hyperspaceout +2. 💬 Send a test message +3. 👀 Watch the logs: `docker-compose logs -f` +4. 🔗 Connect to IRC and verify the message appeared +5. 🔄 Send a message from IRC and check Kosmi + +Good luck! 🎉 + diff --git a/docs/archive/setup-backup-2026-05-10/TOKEN_PERSISTENCE.md b/docs/archive/setup-backup-2026-05-10/TOKEN_PERSISTENCE.md new file mode 100644 index 0000000..e179161 --- /dev/null +++ b/docs/archive/setup-backup-2026-05-10/TOKEN_PERSISTENCE.md @@ -0,0 +1,181 @@ +# Token Persistence Guide + +## Overview + +The Kosmi bridge now caches JWT authentication tokens to avoid repeated browser automation on every startup. The token is stored in a local directory that persists across Docker container rebuilds and restarts. + +## How It Works + +### Token Cache Location + +The token cache is stored in a file called `kosmi_token_cache.json` in the following locations: + +- **Docker (Production)**: `./data/kosmi_token_cache.json` (mounted from your host machine) +- **Local Development**: `~/.matterbridge/kosmi_token_cache.json` + +### Token Cache Structure + +The cache file contains: + +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "email": "your-email@example.com", + "expires_at": "2026-11-02T15:56:23Z", + "saved_at": "2025-11-02T15:56:23Z" +} +``` + +### Token Lifecycle + +1. **On Startup**: The bridge checks for a cached token + - If found and valid, it uses the cached token (no browser automation needed) + - If expired or expiring within 7 days, it performs fresh authentication + - If not found, it performs fresh authentication + +2. **Token Expiry**: Kosmi JWT tokens expire after 1 year + - The bridge automatically refreshes tokens that expire within 7 days + - You'll see a log message indicating how long until the token expires + +3. **Token Storage**: After successful authentication, the token is saved to the cache file + - File permissions are set to `0600` (read/write for owner only) + - The cache directory is created automatically if it doesn't exist + +## Docker Configuration + +### Volume Mount + +The `docker-compose.yml` includes a volume mount for persistent storage: + +```yaml +volumes: + - ./data:/app/data:z +``` + +This mounts the `./data` directory from your host machine into the container at `/app/data`. + +### Environment Variable + +The container sets the `MATTERBRIDGE_DATA_DIR` environment variable: + +```yaml +environment: + - MATTERBRIDGE_DATA_DIR=/app/data +``` + +This tells the bridge where to store persistent data like the token cache. + +## Usage + +### First Run + +On the first run with email/password configured: + +1. The bridge will launch a headless browser +2. Authenticate with Kosmi using your credentials +3. Extract and cache the JWT token +4. Save it to `./data/kosmi_token_cache.json` + +You'll see logs like: + +``` +level=info msg="No cached token found, performing authentication..." +level=info msg="Starting browser automation for authentication..." +level=info msg="💾 Token cached (expires in 8760h)" +``` + +### Subsequent Runs + +On subsequent runs (container restarts, rebuilds, etc.): + +1. The bridge checks the cached token +2. If valid, uses it immediately (no browser needed) +3. Connects to Kosmi in seconds + +You'll see logs like: + +``` +level=info msg="✅ Using cached token (expires in 8736h)" +``` + +### Token Refresh + +When the token is close to expiring (within 7 days): + +1. The bridge automatically performs fresh authentication +2. Updates the cached token +3. Continues normal operation + +You'll see logs like: + +``` +level=info msg="Cached token expires soon (2025-11-09T15:56:23Z), will refresh" +level=info msg="Starting browser automation for authentication..." +level=info msg="💾 Token cached (expires in 8760h)" +``` + +## File Structure + +After running with authentication, your directory structure will look like: + +``` +irc-kosmi-relay/ +├── data/ # Persistent data directory +│ └── kosmi_token_cache.json # Cached JWT token +├── docker-compose.yml +├── matterbridge.toml +└── ... +``` + +## Troubleshooting + +### Token Cache Not Persisting + +If the token cache doesn't persist across container restarts: + +1. Check that the `./data` directory exists and is writable +2. Verify the volume mount in `docker-compose.yml` is correct +3. Check container logs for permission errors + +### Force Token Refresh + +To force a fresh authentication (e.g., if credentials changed): + +```bash +# Stop the container +docker-compose down + +# Remove the cached token +rm ./data/kosmi_token_cache.json + +# Start the container +docker-compose up -d +``` + +### Check Token Status + +To view the current cached token: + +```bash +cat ./data/kosmi_token_cache.json | jq . +``` + +This will show you: +- When the token was saved +- When it expires +- Which email it's associated with + +## Security Notes + +- The token cache file has restricted permissions (`0600`) for security +- The token is a JWT that expires after 1 year +- The cache file is stored locally and never transmitted +- If you commit your code to version control, add `data/` to `.gitignore` + +## Benefits + +1. **Faster Startup**: No browser automation on every restart (saves 10-15 seconds) +2. **Reduced Resource Usage**: No need to launch Chromium on every startup +3. **Persistence**: Token survives container rebuilds, restarts, and host reboots +4. **Automatic Refresh**: Token is automatically refreshed before expiry +5. **Local Storage**: Token is stored on your host machine, not in the container diff --git a/docs/setup/BOT_features.md b/docs/setup/BOT_features.md index c7ac6d2..b76aa64 100644 --- a/docs/setup/BOT_features.md +++ b/docs/setup/BOT_features.md @@ -1,414 +1,145 @@ -# Bot Integration Guide +# Bot Features Guide -This guide explains how to integrate your bot with the Jackbox Game Picker API for live voting and game notifications. +This document describes the features available in the Kosmi-IRC relay bot when the Jackbox Game Picker integration is enabled. -## Table of Contents +## Feature Summary -1. [Live Voting (Bot → API)](#live-voting-bot--api) -2. [Game Notifications (API → Bot)](#game-notifications-api--bot) -3. [Webhook Management](#webhook-management) -4. [Testing](#testing) +| Feature | Where | Description | +|---------|-------|-------------| +| Message relay | IRC + Kosmi | Bidirectional chat relay | +| Vote detection | IRC + Kosmi | Automatic `thisgame++/--` and ticker votes | +| `!votes` command | IRC + Kosmi | Query current game vote tally | +| `!kreconnect` | IRC | Reconnect Kosmi bridge | +| `!jreconnect` | IRC | Reconnect Jackbox WebSocket | +| `!reconnect` | IRC | Reconnect all non-IRC services | +| Game announcements | All bridges | Jackbox game/session notifications | +| Room code images | All bridges | Generated room code images in chat | +| Mute toggle | Operator (SIGUSR1) | Suppress Jackbox announcements | ---- +## Vote Detection -## Live Voting (Bot → API) +The bot automatically detects votes in chat messages from both IRC and Kosmi. Votes are submitted to the Jackbox Game Picker API. -Your bot can send real-time votes to the API when it detects "thisgame++" or "thisgame--" in Kosmi chat. +### Current Game Votes -### Endpoint +- `thisgame++` -- Upvote the currently playing game +- `thisgame--` -- Downvote the currently playing game -``` -POST /api/votes/live -``` +Case-insensitive. Can appear anywhere in the message. The message is still relayed normally. + +### Ticker Symbol Votes + +- `SYMBOL++` -- Upvote a specific game by ticker symbol +- `SYMBOL--` -- Downvote a specific game by ticker symbol + +Symbols are 2-4 alphanumeric characters. See [IRC.md](../IRC.md) for the complete ticker table. + +### Loop Prevention + +Messages prefixed with `[irc]` or `[kosmi]` (relayed messages) are excluded from vote detection to prevent duplicate counting. + +### Deduplication + +The API rejects duplicate votes from the same user within 1 second. + +## Game Announcements + +When connected to the Jackbox API via WebSocket (default) or webhook, the bot broadcasts events to all bridges: + +| Event | Example Message | +|-------|----------------| +| Session started | `🎮 Game Night is starting!` | +| Game added | `🎮 Coming up next: Fibbage 4 - Room Code ABCD` | +| Session ended | `🌙 Game Night has ended! Thanks for playing!` | + +### Room Code Images + +When `EnableRoomCodeImage=true`, game announcements include an uploaded image of the room code. The image is uploaded to the Kosmi CDN. Timing is controlled by `RoomCodeImageDelay` and `RoomCodePlaintextDelay` in config. + +If image upload fails, the bot falls back to plaintext. + +## IRC Commands + +All commands are available to any user in the channel. See [IRC.md](../IRC.md) for complete details. + +| Command | Effect | +|---------|--------| +| `!kreconnect` | Reconnect Kosmi bridge | +| `!jreconnect` | Reconnect Jackbox WebSocket | +| `!reconnect` | Reconnect all non-IRC services | +| `!votes` | Show current game vote tally | + +`!votes` is also available from Kosmi chat. + +## Mute Control + +Jackbox announcements can be suppressed without stopping the bot: + +- **Start muted**: `./matterbridge -conf matterbridge.toml -muted` +- **Toggle at runtime**: `kill -SIGUSR1 ` or `docker kill -s SIGUSR1 ` + +When muted, vote detection and normal message relay continue to work. Only Jackbox-driven announcements (session start/end, game added) are suppressed. + +See [MUTE_CONTROL.md](MUTE_CONTROL.md) for full details. + +## API Communication ### Authentication -Requires JWT token in Authorization header: +The bot authenticates with `POST /api/auth/login` using the `AdminPassword` from config. The JWT is cached in memory and refreshed on 401 responses. -``` -Authorization: Bearer YOUR_JWT_TOKEN -``` +### Vote Submission -### Request Body +Votes are submitted via `POST /api/votes/live` with: ```json { - "username": "string", // Username of the voter - "vote": "up" | "down", // "up" for thisgame++, "down" for thisgame-- - "timestamp": "string" // ISO 8601 timestamp (e.g., "2025-11-01T20:30:00Z") -} -``` - -### Response (Success) - -```json -{ - "success": true, - "message": "Vote recorded successfully", - "session": { - "id": 123, - "games_played": 5 - }, - "game": { - "id": 45, - "title": "Fibbage 4", - "upvotes": 46, - "downvotes": 3, - "popularity_score": 43 - }, - "vote": { - "username": "TestUser", - "type": "up", - "timestamp": "2025-11-01T20:30:00Z" - } -} -``` - -### Error Responses - -- **400 Bad Request**: Invalid payload or timestamp format -- **404 Not Found**: No active session or timestamp doesn't match any game -- **409 Conflict**: Duplicate vote (within 1 second of previous vote from same user) -- **500 Internal Server Error**: Server error - -### Example Implementation (Node.js) - -```javascript -// When bot detects "thisgame++" or "thisgame--" in Kosmi chat -async function handleVote(username, message) { - const isUpvote = message.includes('thisgame++'); - const isDownvote = message.includes('thisgame--'); - - if (!isUpvote && !isDownvote) return; - - try { - const response = await fetch('http://your-api-url/api/votes/live', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${process.env.JWT_TOKEN}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - username: username, - vote: isUpvote ? 'up' : 'down', - timestamp: new Date().toISOString() - }) - }); - - const data = await response.json(); - - if (response.ok) { - console.log(`Vote recorded for ${data.game.title}: ${data.game.upvotes}👍 ${data.game.downvotes}👎`); - } else { - console.error('Vote failed:', data.error); - } - } catch (error) { - console.error('Error sending vote:', error); - } -} -``` - -### Important Notes - -- **Deduplication**: Votes from the same user within 1 second are automatically rejected to prevent spam -- **Timestamp Matching**: The API matches the vote timestamp to the correct game based on when games were played -- **Active Session Required**: Votes can only be recorded when there's an active session with games played - ---- - -## Game Notifications (API → Bot) - -The API can send webhooks to your bot when games are added to a session, allowing you to announce "Coming up next: Game Title!" in Kosmi chat. - -### Webhook Event: `game.added` - -Triggered whenever a game is added to an active session (either via picker or manual selection). - -### Webhook Payload - -```json -{ - "event": "game.added", + "username": "voter_name", + "vote": "up", "timestamp": "2025-11-01T20:30:00Z", - "data": { - "session": { - "id": 123, - "is_active": true, - "games_played": 5 - }, - "game": { - "id": 45, - "title": "Fibbage 4", - "pack_name": "The Jackbox Party Pack 9", - "min_players": 2, - "max_players": 8, - "manually_added": false - } - } + "ticker": "FBG4" } ``` -### Webhook Headers +The `ticker` field is omitted for `thisgame++/--` votes. -The API sends the following headers with each webhook: +### Event Transport -- `Content-Type: application/json` -- `X-Webhook-Signature: sha256=` - HMAC-SHA256 signature for verification -- `X-Webhook-Event: game.added` - Event type -- `User-Agent: Jackbox-Game-Picker-Webhook/1.0` +**WebSocket (default)**: Connects to `wss:///api/sessions/live`, authenticates, and subscribes to the active session. Events arrive in real-time with auto-reconnect. -### Signature Verification +**Webhook (fallback)**: Starts an HTTP server listening for `POST /webhook/jackbox` with HMAC-SHA256 signature verification. -**IMPORTANT**: Always verify the webhook signature to ensure the request is authentic. +## Configuration -```javascript -const crypto = require('crypto'); +See [JACKBOX_INTEGRATION.md](JACKBOX_INTEGRATION.md) for the full configuration reference. -function verifyWebhookSignature(signature, payload, secret) { - if (!signature || !signature.startsWith('sha256=')) { - return false; - } - - const expectedSignature = 'sha256=' + crypto - .createHmac('sha256', secret) - .update(JSON.stringify(payload)) - .digest('hex'); - - // Use timing-safe comparison - try { - return crypto.timingSafeEqual( - Buffer.from(signature), - Buffer.from(expectedSignature) - ); - } catch (err) { - return false; - } -} +Minimal setup: + +```toml +[jackbox] +Enabled=true +APIURL="https://your-jackbox-api.example.com" +AdminPassword="your_password" +UseWebSocket=true ``` -### Example Implementation (Express.js) - -```javascript -const express = require('express'); -const crypto = require('crypto'); - -const app = express(); - -// IMPORTANT: Use express.json() with verify option to get raw body -app.use(express.json({ - verify: (req, res, buf) => { - req.rawBody = buf.toString('utf8'); - } -})); - -app.post('/webhook/jackbox', (req, res) => { - const signature = req.headers['x-webhook-signature']; - const secret = process.env.WEBHOOK_SECRET; // Your webhook secret - - // Verify signature - if (!signature || !signature.startsWith('sha256=')) { - return res.status(401).send('Missing or invalid signature'); - } - - const expectedSignature = 'sha256=' + crypto - .createHmac('sha256', secret) - .update(req.rawBody) - .digest('hex'); - - // Timing-safe comparison - try { - if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { - return res.status(401).send('Invalid signature'); - } - } catch (err) { - return res.status(401).send('Invalid signature'); - } - - // Handle the event - if (req.body.event === 'game.added') { - const game = req.body.data.game; - - // Send message to Kosmi chat - sendKosmiMessage(`🎮 Coming up next: ${game.title}!`); - - console.log(`Announced game: ${game.title} from ${game.pack_name}`); - } - - // Always respond with 200 OK - res.status(200).send('OK'); -}); - -function sendKosmiMessage(message) { - // Your Kosmi chat integration here - console.log('Sending to Kosmi:', message); -} - -app.listen(3001, () => { - console.log('Webhook receiver listening on port 3001'); -}); -``` - ---- - -## Webhook Management - -You can manage webhooks through the API using the following endpoints (all require JWT authentication). - -### List All Webhooks - -```bash -GET /api/webhooks -Authorization: Bearer YOUR_JWT_TOKEN -``` - -### Create Webhook - -```bash -POST /api/webhooks -Authorization: Bearer YOUR_JWT_TOKEN -Content-Type: application/json - -{ - "name": "Kosmi Bot", - "url": "http://your-bot-url/webhook/jackbox", - "secret": "your_shared_secret_key", - "events": ["game.added"] -} -``` - -### Update Webhook - -```bash -PATCH /api/webhooks/:id -Authorization: Bearer YOUR_JWT_TOKEN -Content-Type: application/json - -{ - "enabled": false // Disable webhook -} -``` - -### Delete Webhook - -```bash -DELETE /api/webhooks/:id -Authorization: Bearer YOUR_JWT_TOKEN -``` - -### Test Webhook - -```bash -POST /api/webhooks/test/:id -Authorization: Bearer YOUR_JWT_TOKEN -``` - -Sends a test `game.added` event to verify your webhook is working. - -### View Webhook Logs - -```bash -GET /api/webhooks/:id/logs?limit=50 -Authorization: Bearer YOUR_JWT_TOKEN -``` - -Returns recent webhook delivery attempts with status codes and errors. - ---- - -## Testing - -### Test Live Voting - -```bash -# Get your JWT token first -curl -X POST "http://localhost:5000/api/auth/login" \ - -H "Content-Type: application/json" \ - -d '{"apiKey": "YOUR_API_KEY"}' - -# Send a test vote -curl -X POST "http://localhost:5000/api/votes/live" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "username": "TestUser", - "vote": "up", - "timestamp": "2025-11-01T20:30:00Z" - }' -``` - -### Test Webhooks - -```bash -# Create a webhook -curl -X POST "http://localhost:5000/api/webhooks" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Test Webhook", - "url": "http://localhost:3001/webhook/jackbox", - "secret": "test_secret_123", - "events": ["game.added"] - }' - -# Test the webhook -curl -X POST "http://localhost:5000/api/webhooks/test/1" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" - -# Check webhook logs -curl -X GET "http://localhost:5000/api/webhooks/1/logs" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" -``` - ---- - -## Available Events - -Currently supported webhook events: - -- `game.added` - Triggered when a game is added to an active session - -More events may be added in the future (e.g., `session.started`, `session.ended`, `vote.recorded`). - ---- - -## Security Best Practices - -1. **Always verify webhook signatures** - Never trust webhook payloads without verification -2. **Use HTTPS in production** - Webhook URLs should use HTTPS to prevent man-in-the-middle attacks -3. **Keep secrets secure** - Store webhook secrets in environment variables, never in code -4. **Implement rate limiting** - Protect your webhook endpoints from abuse -5. **Log webhook activity** - Keep logs of webhook deliveries for debugging -6. **Use strong secrets** - Generate cryptographically secure random strings for webhook secrets - ---- - ## Troubleshooting -### Votes Not Being Recorded +### Votes not registering -- Check that there's an active session with games played -- Verify the timestamp is within the timeframe of a played game -- Ensure you're not sending duplicate votes within 1 second -- Check API logs for error messages +- Check that Jackbox integration is enabled +- Verify an active session exists +- Look for `"Detected vote from"` in debug logs +- Look for `"Failed to send vote"` errors -### Webhooks Not Being Received +### Announcements not appearing -- Verify your webhook URL is publicly accessible -- Check webhook logs via `/api/webhooks/:id/logs` -- Test with `ngrok` or similar tool if developing locally -- Ensure your webhook endpoint responds with 200 OK -- Check that webhook is enabled in the database +- Check that the Jackbox WebSocket is connected +- Verify the bot is not muted (check logs for `MUTED`) +- Use `!jreconnect` to force a WebSocket reconnection -### Signature Verification Failing - -- Ensure you're using the raw request body for signature verification -- Check that the secret matches what's stored in the database -- Verify you're using HMAC-SHA256 algorithm -- Make sure to prefix with "sha256=" when comparing - ---- - -## Support - -For issues or questions, contact: cottongin@cottongin.xyz +### `!votes` returns nothing +- An active session must exist +- A game must currently have "playing" status +- The Jackbox integration must be enabled and authenticated diff --git a/docs/setup/BROWSER_AUTH_GUIDE.md b/docs/setup/BROWSER_AUTH_GUIDE.md index e1b23d7..5fcb804 100644 --- a/docs/setup/BROWSER_AUTH_GUIDE.md +++ b/docs/setup/BROWSER_AUTH_GUIDE.md @@ -1,277 +1,160 @@ # Browser-Based Authentication Guide ## Overview -The Kosmi bridge now supports **fully automated email/password authentication** using headless Chrome via chromedp. No manual token extraction needed! + +The Kosmi bridge supports **email/password authentication** using headless Chrome (chromedp). This is only needed if you want to connect with a specific Kosmi account instead of anonymous access. The JWT obtained via browser login is cached on disk, so the browser is only launched when needed. ## Quick Start ### 1. Configure Email/Password + Edit `matterbridge.toml`: ```toml [kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@hyperspaceout" +RoomURL="https://app.kosmi.io/room/@yourroom" Email="your-email@example.com" Password="your-password" ``` ### 2. Run the Bot + ```bash -./irc-kosmi-relay -conf matterbridge.toml +./matterbridge -conf matterbridge.toml ``` -That's it! The bot will: -1. Launch headless Chrome -2. Navigate to Kosmi -3. Log in with your credentials -4. Extract the JWT token from localStorage -5. Use the token for authenticated connections -6. Automatically refresh the token daily (checks for expiry 7 days in advance) +The bot will: +1. Check for a cached token in the data directory +2. If no valid cache exists, launch headless Chrome +3. Navigate to Kosmi and log in with your credentials +4. Extract the JWT from localStorage +5. Cache the token for future use +6. Connect to Kosmi with the authenticated token -## How It Works +## Authentication Modes -### Initial Login -When you start the bot with Email/Password configured: -1. **Browser Launch**: Headless Chrome starts (no visible window) -2. **Navigation**: Goes to https://app.kosmi.io -3. **Login Flow**: - - Clicks "Login" button - - Clicks "Login with Email" - - Fills in email and password - - Submits the form -4. **Token Extraction**: Reads `localStorage.getItem('token')` -5. **Token Parsing**: Extracts expiry time from JWT -6. **Connection**: Uses token for WebSocket authentication +### Option 1: Email/Password (Recommended for Authenticated Access) -### Automatic Token Refresh -- **Daily Check**: Every 24 hours, the bot checks if the token is still valid -- **Expiry Buffer**: Refreshes 7 days before expiration -- **Seamless**: Happens in the background without disconnecting -- **Logging**: You'll see "Checking token expiry..." in debug logs - -## Requirements - -### System Requirements -- **Chrome/Chromium**: Must be installed on your system - - macOS: Usually pre-installed or via Homebrew: `brew install chromium` - - Linux: `sudo apt install chromium-browser` or `sudo yum install chromium` - - Windows: Download from https://www.chromium.org/getting-involved/download-chromium/ - -### Go Dependencies -- `github.com/chromedp/chromedp` - Automatically installed with `go get` - -## Configuration Options - -### Option 1: Email/Password (Recommended) ```toml Email="your-email@example.com" Password="your-password" ``` -- ✅ Fully automated -- ✅ Auto-refresh -- ✅ No manual intervention -- ⚠️ Requires Chrome/Chromium -- ⚠️ Stores credentials in config file -### Option 2: Manual Token -```toml -Token="eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..." -``` -- ✅ No browser required -- ✅ No credentials in config -- ❌ Manual extraction needed -- ❌ Must update when expired (~1 year) +- Fully automated with token caching +- Browser only launches when cache is missing or expired +- Token refreshes automatically before expiry (7-day buffer) +- Requires Chrome/Chromium installed + +### Option 2: Anonymous (Default) -### Option 3: Anonymous ```toml -# Leave both empty +# Leave both empty or omit entirely Email="" Password="" -Token="" ``` -- ✅ No setup needed -- ❌ Limited permissions -- ❌ Can't access private rooms + +- No setup needed, no browser required +- Connects with an anonymous Kosmi identity +- Cannot access private rooms + +## Token Caching + +Authenticated tokens are cached to avoid launching Chrome on every startup: + +- **Docker**: `./data/kosmi_token_cache.json` (via `MATTERBRIDGE_DATA_DIR=/app/data`) +- **Local**: `~/.matterbridge/kosmi_token_cache.json` + +On startup, the bridge: +1. Loads the cached token +2. If valid (and not expiring within 7 days), uses it directly -- no browser +3. If expired or expiring soon, performs browser authentication +4. Saves the new token to cache + +Kosmi JWT tokens are valid for approximately 1 year. + +See [TOKEN_PERSISTENCE.md](TOKEN_PERSISTENCE.md) for full details on caching. + +## Requirements + +### Chrome/Chromium + +Must be installed and in PATH: + +```bash +# macOS +brew install --cask chromium + +# Ubuntu/Debian +sudo apt install chromium-browser + +# Verify +which chromium chromium-browser google-chrome +``` + +In Docker, Chromium is pre-installed in the image. ## Troubleshooting -### "Browser automation failed" -**Possible causes:** -1. Chrome/Chromium not installed -2. Chrome/Chromium not in PATH -3. Network issues -4. Kosmi website changed +### "Browser automation failed" or "authentication failed" -**Solutions:** -```bash -# Check if Chrome is installed -which chromium || which google-chrome || which chrome +1. Check Chrome/Chromium is installed and accessible +2. Verify credentials by logging in manually at `app.kosmi.io` +3. Delete the cached token to force fresh auth: `rm data/kosmi_token_cache.json` +4. Enable debug logging for detailed browser automation output -# Install Chrome (macOS) -brew install chromium +### Token cache not persisting (Docker) -# Install Chrome (Ubuntu/Debian) -sudo apt install chromium-browser +- Verify the `./data` volume mount exists in `docker-compose.yml` +- Check directory permissions: `ls -la ./data/` +- Confirm `MATTERBRIDGE_DATA_DIR` is set to `/app/data` -# Install Chrome (CentOS/RHEL) -sudo yum install chromium -``` - -### "No token found in localStorage after login" -**Possible causes:** -1. Login failed (wrong credentials) -2. Kosmi's login flow changed -3. Page didn't fully load - -**Solutions:** -- Verify credentials are correct -- Check bot logs for detailed error messages -- Try manual token extraction as fallback - -### "Token expired or expiring soon" -This is normal! The bot will automatically refresh the token. If refresh fails: -- Check Chrome is still installed -- Check network connectivity -- Restart the bot to force a fresh login - -### Headless Chrome Issues -If you see Chrome-related errors: +### Force token refresh ```bash -# Set environment variable for debugging -export CHROMEDP_DISABLE_GPU=1 +# Local +rm ~/.matterbridge/kosmi_token_cache.json +./matterbridge -conf matterbridge.toml -# Or run with visible browser (for debugging) -export CHROMEDP_NO_HEADLESS=1 +# Docker +rm ./data/kosmi_token_cache.json +docker-compose restart ``` ## Security Considerations -### Credential Storage -- Credentials are stored in **plain text** in `matterbridge.toml` -- Ensure config file has restrictive permissions: +- Credentials are stored in **plain text** in `matterbridge.toml` -- restrict permissions: ```bash chmod 600 matterbridge.toml ``` -- Do not commit config with real credentials to version control -- Consider using environment variables: - ```bash - export KOSMI_EMAIL="your-email@example.com" - export KOSMI_PASSWORD="your-password" - ``` - -### Browser Automation -- Headless Chrome runs with minimal privileges -- No data is stored or cached -- Browser closes immediately after token extraction -- Token is kept in memory only - -### Token Security -- Tokens are JWT (JSON Web Tokens) signed by Kosmi -- Valid for ~1 year -- Can be revoked by logging out in browser -- Treat tokens like passwords - -## Advanced Configuration - -### Custom Chrome Path -If Chrome is installed in a non-standard location: - -```go -// In browser_auth.go, modify NewContext call: -opts := append(chromedp.DefaultExecAllocatorOptions[:], - chromedp.ExecPath("/path/to/chrome"), -) -ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) -``` - -### Timeout Adjustment -If login takes longer than 60 seconds: - -```go -// In browser_auth.go, modify timeout: -ctx, cancel = context.WithTimeout(ctx, 120*time.Second) -``` - -### Refresh Interval -To check token more/less frequently: - -```go -// In kosmi.go, modify ticker: -ticker := time.NewTicker(12 * time.Hour) // Check twice daily -``` - -## Comparison with Manual Token - -| Feature | Browser Auth | Manual Token | -|---------|-------------|--------------| -| Setup Complexity | Easy | Medium | -| Automation | Full | None | -| Token Refresh | Automatic | Manual | -| Dependencies | Chrome | None | -| Security | Credentials in config | Token in config | -| Maintenance | Low | Medium | +- Do not commit config files with real credentials to version control +- The cached token file has restricted permissions (`0600`) +- Headless Chrome runs briefly and closes after token extraction +- Tokens can be revoked by logging out in a browser ## Logs to Expect -### Successful Login +### First run (no cache) + ``` -INFO Using browser automation for email/password authentication -INFO Obtaining authentication token via browser automation... -INFO ✅ Successfully obtained token via browser automation -INFO Token expires in: 365d -INFO ✅ Browser authentication successful +INFO No cached token found, performing authentication... +INFO Authenticating with email/password... +INFO ✅ Authentication successful +INFO 💾 Token cached (expires in 8760h) INFO Successfully connected to Kosmi ``` -### Daily Token Check +### Subsequent runs (cached token) + ``` -DEBUG Checking token expiry... -DEBUG Token check complete +INFO ✅ Using cached token (expires in 8736h) +INFO Successfully connected to Kosmi ``` -### Token Refresh (when expiring) +### Token refresh (near expiry) + ``` -INFO Token expired or expiring soon, will refresh -INFO Obtaining authentication token via browser automation... -INFO ✅ Successfully obtained token via browser automation -INFO Token expires in: 365d +INFO Cached token expires soon, will refresh +INFO Authenticating with email/password... +INFO ✅ Authentication successful +INFO 💾 Token cached (expires in 8760h) ``` - -## Migration from Manual Token - -If you're currently using manual token: - -1. **Add credentials** to config: - ```toml - Email="your-email@example.com" - Password="your-password" - ``` - -2. **Remove or comment out Token**: - ```toml - #Token="..." - ``` - -3. **Restart the bot** - -The bot will automatically switch to browser-based auth! - -## Performance Impact - -- **Initial Login**: ~5-10 seconds (one-time per start) -- **Token Refresh**: ~5-10 seconds (once per year, or when expiring) -- **Daily Check**: <1ms (just checks expiry time) -- **Memory**: +50-100MB during browser operation (released after) -- **CPU**: Minimal (browser runs briefly) - -## Conclusion - -Browser-based authentication provides the best balance of: -- ✅ Full automation -- ✅ Reliable token refresh -- ✅ Simple configuration -- ✅ Low maintenance - -For production use, this is the **recommended authentication method**. - diff --git a/docs/setup/DOCKER_DEPLOYMENT.md b/docs/setup/DOCKER_DEPLOYMENT.md index 8eb6049..2dd3f63 100644 --- a/docs/setup/DOCKER_DEPLOYMENT.md +++ b/docs/setup/DOCKER_DEPLOYMENT.md @@ -5,13 +5,9 @@ Complete guide for deploying the Kosmi-IRC bridge using Docker. ## Quick Start ```bash -# 1. 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 ``` @@ -26,24 +22,20 @@ docker-compose logs -f ### 1. Configure the Bridge -Edit `matterbridge.toml` and update these settings: +Copy `matterbridge.toml.example` to `matterbridge.toml` and update these settings: ```toml [kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" # ← Change this -Debug=false +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" -[irc.libera] -Server="irc.libera.chat:6667" # ← Change to your IRC server -Nick="kosmi-relay" # ← Change your bot's nickname +[irc.zeronode] +Server="irc.libera.chat:6697" +Nick="kosmi-relay" +UseTLS=true [[gateway.inout]] -account="kosmi.hyperspaceout" -channel="main" - -[[gateway.inout]] -account="irc.libera" -channel="#your-channel" # ← Change to your IRC channel +account="irc.zeronode" +channel="#your-channel" ``` ### 2. Build the Docker Image @@ -53,9 +45,10 @@ docker-compose build ``` This will: -- Install Chrome/Chromium in the container -- Build the Matterbridge binary with Kosmi support -- Create an optimized production image +- Use the Go 1.23 Alpine base image +- Install Chromium (used only for email/password authentication) +- Build the Matterbridge binary +- Create a production image **Build time**: ~5-10 minutes (first time) @@ -72,89 +65,51 @@ docker-compose up ### 4. Verify It's Working ```bash -# Check container status -docker-compose ps +docker-compose logs -f +``` -# View logs -docker-compose logs -f matterbridge - -# Look for these messages: -# INFO Successfully connected to Kosmi via Chrome -# INFO Successfully connected to IRC -# INFO Gateway(s) started successfully +Look for: +``` +INFO Successfully connected to Kosmi +INFO Connection succeeded (IRC) +INFO Gateway(s) started successfully. Now relaying messages ``` ### 5. Test Message Relay -1. **Kosmi → IRC**: Send a message in your Kosmi room - - Should appear in IRC as: `[Kosmi] message` +1. **Kosmi --> IRC**: Send a message in your Kosmi room + - Should appear in IRC as: `[kosmi] message` -2. **IRC → Kosmi**: Send a message in your IRC channel - - Should appear in Kosmi as: `[IRC] message` +2. **IRC --> Kosmi**: Send a message in your IRC channel + - Should appear in Kosmi as: `[irc] message` ## Docker Commands Reference ### Container Management ```bash -# Start the bridge -docker-compose up -d - -# Stop the bridge -docker-compose down - -# Restart the bridge -docker-compose restart - -# View logs -docker-compose logs -f - -# View last 100 lines of logs -docker-compose logs --tail=100 - -# Check container status -docker-compose ps - -# Execute commands in running container -docker-compose exec matterbridge sh -``` - -### Debugging - -```bash -# Enable debug logging (edit docker-compose.yml first) -# Set Debug=true in matterbridge.toml, then: -docker-compose restart - -# Check Chrome is installed -docker-compose exec matterbridge which chromium - -# Check configuration -docker-compose exec matterbridge cat /app/matterbridge.toml - -# Test connectivity -docker-compose exec matterbridge ping -c 3 app.kosmi.io -docker-compose exec matterbridge ping -c 3 irc.libera.chat +docker-compose up -d # Start +docker-compose down # Stop +docker-compose restart # Restart +docker-compose logs -f # Follow logs +docker-compose logs --tail=100 # Last 100 lines +docker-compose ps # Container status +docker-compose exec matterbridge sh # Shell into container ``` ### Updating ```bash -# Pull latest code git pull - -# Rebuild image docker-compose build --no-cache - -# Restart with new image docker-compose up -d ``` -## Configuration Options - -### docker-compose.yml +## docker-compose.yml Reference ```yaml +version: '3.8' + services: matterbridge: build: @@ -162,241 +117,154 @@ services: dockerfile: Dockerfile container_name: kosmi-irc-relay restart: unless-stopped + command: ["-conf", "/app/matterbridge.toml"] volumes: - - ./matterbridge.toml:/app/matterbridge.toml:ro - - ./logs:/app/logs + - ./matterbridge.toml:/app/matterbridge.toml:ro,z + - ./logs:/app/logs:z + - ./data:/app/data:z environment: - - CHROME_BIN=/usr/bin/chromium - - CHROME_PATH=/usr/bin/chromium - - TZ=America/New_York # ← Change to your timezone - security_opt: - - seccomp:unconfined # Required for Chrome + - TZ=America/New_York + - MATTERBRIDGE_DATA_DIR=/app/data + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +``` + +### Starting Muted + +To start with Jackbox announcements suppressed: + +```yaml + command: ["-conf", "/app/matterbridge.toml", "-muted"] ``` ### Environment Variables | Variable | Description | Default | |----------|-------------|---------| -| `CHROME_BIN` | Path to Chrome binary | `/usr/bin/chromium` | -| `CHROME_PATH` | Chrome executable path | `/usr/bin/chromium` | -| `TZ` | Timezone for logs | `America/New_York` | -| `DEBUG` | Enable debug logging | `0` | +| `TZ` | Timezone for log timestamps | `America/New_York` | +| `MATTERBRIDGE_DATA_DIR` | Directory for persistent data (token cache) | `/app/data` | +| `DEBUG` | Set to `1` for debug logging | `0` | +| `CHROME_BIN` | Path to Chrome binary (set in Dockerfile) | `/usr/bin/chromium-browser` | + +Note: `CHROME_BIN` and `CHROME_PATH` are set in the Dockerfile. Chromium is installed in the container for email/password authentication. If you only use anonymous access, Chrome is present but unused. ### Volume Mounts | Host Path | Container Path | Purpose | |-----------|----------------|---------| -| `./matterbridge.toml` | `/app/matterbridge.toml` | Configuration file (read-only) | +| `./matterbridge.toml` | `/app/matterbridge.toml` | Configuration (read-only) | | `./logs` | `/app/logs` | Log files (optional) | +| `./data` | `/app/data` | Persistent data: token cache | + +The `./data` volume is important for email/password auth -- it stores the cached JWT so the bot doesn't need to re-authenticate on every restart. See [TOKEN_PERSISTENCE.md](TOKEN_PERSISTENCE.md). ## Troubleshooting ### Container Won't Start -**Check logs**: ```bash docker-compose logs ``` -**Common issues**: -- Configuration file syntax error -- Missing `matterbridge.toml` -- Port already in use +Common causes: +- TOML syntax error in `matterbridge.toml` +- Missing configuration file +- Port conflict (if webhook port is exposed) + +### Kosmi Connection Fails -**Solution**: ```bash -# Validate TOML syntax -docker run --rm -v $(pwd)/matterbridge.toml:/config.toml alpine sh -c "apk add --no-cache go && go install github.com/pelletier/go-toml/cmd/tomll@latest && tomll /config.toml" - -# Check if file exists -ls -la matterbridge.toml +# Check connectivity from inside the container +docker-compose exec matterbridge wget -q -O - https://app.kosmi.io > /dev/null && echo "OK" ``` -### Chrome/Chromium Not Found +- Verify `RoomURL` is correct +- Enable debug logging (`Debug=true` in the Kosmi config section) -**Symptoms**: -``` -ERROR Chrome binary not found -``` +### IRC Connection Fails -**Solution**: -```bash -# Rebuild image -docker-compose build --no-cache - -# Verify Chrome is installed -docker-compose run --rm matterbridge which chromium -``` - -### WebSocket Connection Failed - -**Symptoms**: -``` -ERROR Failed to connect to Kosmi -ERROR WebSocket connection failed -``` - -**Solution**: -```bash -# Test network connectivity -docker-compose exec matterbridge ping -c 3 app.kosmi.io - -# Check if room URL is correct -docker-compose exec matterbridge cat /app/matterbridge.toml | grep RoomURL - -# Enable debug logging -# Edit matterbridge.toml: Debug=true -docker-compose restart -``` - -### IRC Connection Failed - -**Symptoms**: -``` -ERROR Failed to connect to IRC -ERROR Connection refused -``` - -**Solution**: ```bash # Test IRC connectivity -docker-compose exec matterbridge nc -zv irc.libera.chat 6667 - -# Check IRC configuration -docker-compose exec matterbridge cat /app/matterbridge.toml | grep -A 10 "\[irc\]" - -# Verify nickname isn't already in use -# Try changing Nick in matterbridge.toml +docker-compose exec matterbridge nc -zv irc.libera.chat 6697 ``` +- Verify server address and port +- Check TLS settings +- Try a different nick if the current one is registered/in use + ### Messages Not Relaying -**Symptoms**: -- Container running -- Both bridges connected -- But messages don't appear +1. Confirm both bridges are connected in logs +2. Verify gateway config: Kosmi channel = `"main"`, IRC channel includes `#` +3. Enable debug logging and watch for message routing + +### Authentication Issues + +If using email/password: +- Delete cached token: `rm ./data/kosmi_token_cache.json` +- Rebuild container: `docker-compose build --no-cache` +- Check Chromium is working: `docker-compose exec matterbridge chromium-browser --version` + +## Runtime Operations + +### Mute Toggle + +Toggle Jackbox announcements without restarting: -**Solution**: ```bash -# Enable debug logging -# Edit matterbridge.toml: Debug=true -docker-compose restart - -# Watch logs for message flow -docker-compose logs -f | grep -E "Received|Sending|Forwarding" - -# Verify gateway configuration -docker-compose exec matterbridge cat /app/matterbridge.toml | grep -A 20 "\[\[gateway\]\]" - -# Check channel names match exactly -# Kosmi channel should be "main" -# IRC channel should include # (e.g., "#your-channel") +docker kill -s SIGUSR1 kosmi-irc-relay ``` -### High Memory Usage +See [MUTE_CONTROL.md](MUTE_CONTROL.md) for details. -**Symptoms**: -- Container using >500MB RAM -- System slowdown +### Force Token Refresh -**Solution**: ```bash -# Add memory limits to docker-compose.yml -services: - matterbridge: - mem_limit: 512m - mem_reservation: 256m - -# Restart +docker-compose down +rm ./data/kosmi_token_cache.json docker-compose up -d ``` -### Permission Denied Errors - -**Symptoms**: -``` -ERROR Permission denied writing to /app/logs -``` - -**Solution**: -```bash -# Create logs directory with correct permissions -mkdir -p logs -chmod 777 logs - -# Or run container as root (not recommended) -# Edit docker-compose.yml: -# user: root -``` - -## Production Deployment - -### Using Docker Swarm +### View Token Status ```bash -# Initialize swarm -docker swarm init - -# Deploy stack -docker stack deploy -c docker-compose.yml kosmi-relay - -# Check status -docker stack services kosmi-relay - -# View logs -docker service logs -f kosmi-relay_matterbridge +cat ./data/kosmi_token_cache.json | python3 -m json.tool ``` -### Using Kubernetes +## Resource Usage -Create `kosmi-relay.yaml`: +The bridge uses a native WebSocket connection, so resource requirements are modest: + +- **Memory**: ~50-100MB typical (lower than browser-based approaches) +- **CPU**: Minimal (event-driven, no polling) +- **Network**: WebSocket to Kosmi + IRC connection + +### Memory Limits (Optional) ```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: kosmi-irc-relay -spec: - replicas: 1 - selector: - matchLabels: - app: kosmi-irc-relay - template: - metadata: - labels: - app: kosmi-irc-relay - spec: - containers: - - name: matterbridge - image: kosmi-irc-relay:latest - volumeMounts: - - name: config - mountPath: /app/matterbridge.toml - subPath: matterbridge.toml - env: - - name: CHROME_BIN - value: /usr/bin/chromium - - name: TZ - value: America/New_York - securityContext: - capabilities: - add: - - SYS_ADMIN # Required for Chrome - volumes: - - name: config - configMap: - name: matterbridge-config +services: + matterbridge: + mem_limit: 256m + mem_reservation: 64m ``` -Deploy: -```bash -kubectl create configmap matterbridge-config --from-file=matterbridge.toml -kubectl apply -f kosmi-relay.yaml +## Production Considerations + +### Log Rotation + +Already configured in `docker-compose.yml`: +```yaml +logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" ``` -### Monitoring - -#### Health Check +### Health Check Add to `docker-compose.yml`: @@ -408,127 +276,20 @@ services: interval: 30s timeout: 10s retries: 3 - start_period: 40s + start_period: 30s ``` -#### Prometheus Metrics +### Security -Matterbridge doesn't expose Prometheus metrics by default, but you can monitor: +- Configuration is mounted read-only (`:ro`) +- Token cache directory has restricted access inside the container +- Credentials in `matterbridge.toml` should be protected: `chmod 600 matterbridge.toml` +- Do not commit `matterbridge.toml` with real credentials to version control -```bash -# Container metrics -docker stats kosmi-irc-relay - -# Log-based monitoring -docker-compose logs -f | grep -E "ERROR|WARN" -``` - -### Backup and Restore - -```bash -# Backup configuration -cp matterbridge.toml matterbridge.toml.backup - -# Backup logs -tar -czf logs-$(date +%Y%m%d).tar.gz logs/ - -# Restore configuration -cp matterbridge.toml.backup matterbridge.toml -docker-compose restart -``` - -## Security Best Practices - -1. **Run as non-root user** (already configured in Dockerfile) -2. **Use read-only configuration mount** -3. **Limit container resources** -4. **Keep Docker images updated** -5. **Use secrets for sensitive data** (e.g., IRC passwords) - -### Using Docker Secrets - -```bash -# Create secret -echo "your_irc_password" | docker secret create irc_password - - -# Update docker-compose.yml -services: - matterbridge: - secrets: - - irc_password - -secrets: - irc_password: - external: true -``` - -## Performance Tuning - -### Resource Limits - -```yaml -services: - matterbridge: - deploy: - resources: - limits: - cpus: '1.0' - memory: 512M - reservations: - cpus: '0.5' - memory: 256M -``` - -### Chrome Optimization - -Add to `docker-compose.yml`: - -```yaml -services: - matterbridge: - environment: - - CHROME_FLAGS=--disable-dev-shm-usage --no-sandbox --disable-setuid-sandbox -``` - -## Next Steps - -- ✅ Bridge is running in Docker -- 🔄 Set up monitoring and alerts -- 🔄 Configure log rotation -- 🔄 Set up automatic backups -- 🔄 Add more bridges (Discord, Slack, etc.) - -## Getting Help - -- Check logs: `docker-compose logs -f` -- Enable debug: Set `Debug=true` in `matterbridge.toml` -- Review `LESSONS_LEARNED.md` for common issues -- Check `QUICK_REFERENCE.md` for troubleshooting tips - -## Example: Complete Setup - -```bash -# 1. Clone repository -git clone kosmi-irc-relay -cd kosmi-irc-relay - -# 2. Edit configuration -nano matterbridge.toml -# Update RoomURL, IRC server, channel - -# 3. Build and start -docker-compose up -d - -# 4. Watch logs -docker-compose logs -f - -# 5. Test by sending messages in both Kosmi and IRC - -# 6. If issues, enable debug -nano matterbridge.toml # Set Debug=true -docker-compose restart -docker-compose logs -f -``` - -That's it! Your Kosmi-IRC bridge is now running in Docker! 🎉 +## Further Reading +- [README.md](../../README.md) -- Project overview +- [DOCKER_QUICKSTART.md](DOCKER_QUICKSTART.md) -- 5-minute quick start +- [JACKBOX_INTEGRATION.md](JACKBOX_INTEGRATION.md) -- Jackbox Game Picker setup +- [TOKEN_PERSISTENCE.md](TOKEN_PERSISTENCE.md) -- Token caching details +- [IRC.md](../IRC.md) -- IRC commands and voting diff --git a/docs/setup/DOCKER_QUICKSTART.md b/docs/setup/DOCKER_QUICKSTART.md index 74bc015..5ea0ece 100644 --- a/docs/setup/DOCKER_QUICKSTART.md +++ b/docs/setup/DOCKER_QUICKSTART.md @@ -1,6 +1,6 @@ -# Docker Quick Start - 5 Minutes to Running Bridge +# Docker Quick Start -- 5 Minutes to Running Bridge -Get your Kosmi-IRC bridge running in Docker in 5 minutes! +Get your Kosmi-IRC bridge running in Docker in 5 minutes. ## Prerequisites @@ -13,22 +13,27 @@ Get your Kosmi-IRC bridge running in Docker in 5 minutes! ### 1. Edit Configuration (2 minutes) -Open `matterbridge.toml` and change these 3 things: +```bash +cp matterbridge.toml.example matterbridge.toml +nano matterbridge.toml +``` + +Change these 3 things: ```toml [kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" # ← Your Kosmi room +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" # <-- Your Kosmi room -[irc.libera] -Server="irc.libera.chat:6667" # ← Your IRC server -Nick="kosmi-relay" # ← Your bot's nickname +[irc.zeronode] +Server="irc.libera.chat:6697" # <-- Your IRC server +Nick="kosmi-relay" # <-- Your bot's nickname [[gateway.inout]] -account="irc.libera" -channel="#your-channel" # ← Your IRC channel +account="irc.zeronode" +channel="#your-channel" # <-- Your IRC channel ``` -### 2. Build & Run (2 minutes) +### 2. Build and Run (2 minutes) ```bash docker-compose up -d @@ -42,19 +47,15 @@ docker-compose logs -f Look for: ``` -INFO Successfully connected to Kosmi via Chrome -INFO Successfully connected to IRC -INFO Gateway(s) started successfully +INFO Successfully connected to Kosmi +INFO Connection succeeded (IRC) +INFO Gateway(s) started successfully. Now relaying messages ``` -### 4. Test It! +### 4. Test It -- Send a message in Kosmi → should appear in IRC -- Send a message in IRC → should appear in Kosmi - -## That's It! 🎉 - -Your bridge is running! +- Send a message in Kosmi --> should appear in IRC +- Send a message in IRC --> should appear in Kosmi ## Common Commands @@ -78,52 +79,31 @@ docker-compose build && docker-compose up -d 1. Check your configuration: ```bash - cat matterbridge.toml | grep -E "RoomURL|Server|channel" + grep -E "RoomURL|Server|channel" matterbridge.toml ``` 2. Enable debug logging: - - Edit `matterbridge.toml`: Set `Debug=true` + - Edit `matterbridge.toml`: Set `Debug=true` under the appropriate bridge section - Restart: `docker-compose restart` - Watch logs: `docker-compose logs -f` -### "Chrome not found" - -```bash -# Rebuild image -docker-compose build --no-cache -docker-compose up -d -``` - ### "Messages not relaying" 1. Check both bridges are connected: ```bash - docker-compose logs | grep -i "connected" + docker-compose logs | grep -i "connected\|succeeded" ``` 2. Verify channel names: - Kosmi channel must be `"main"` - IRC channel must include `#` (e.g., `"#your-channel"`) +## Persistent Token Cache + +If using email/password authentication, the JWT is cached in `./data/` which is mounted into the container. This survives container rebuilds and restarts. See [TOKEN_PERSISTENCE.md](TOKEN_PERSISTENCE.md) for details. + ## Need More Help? -- Full guide: See `DOCKER_DEPLOYMENT.md` -- Troubleshooting: See `QUICK_REFERENCE.md` -- Implementation details: See `LESSONS_LEARNED.md` - -## Example Output (Success) - -``` -INFO[...] Starting Matterbridge -INFO[...] Launching headless Chrome for Kosmi connection -INFO[...] Injecting WebSocket interceptor (runs before page load)... -INFO[...] ✓ WebSocket hook confirmed installed -INFO[...] Status: WebSocket connection intercepted -INFO[...] Successfully connected to Kosmi via Chrome -INFO[...] Connecting to IRC server irc.libera.chat:6667 -INFO[...] Successfully connected to IRC -INFO[...] Gateway(s) started successfully. Now relaying messages -``` - -Now send a test message and watch it relay! 🚀 - +- Full Docker guide: [DOCKER_DEPLOYMENT.md](DOCKER_DEPLOYMENT.md) +- Quick reference: [QUICK_REFERENCE.md](QUICK_REFERENCE.md) +- IRC commands: [IRC.md](../IRC.md) diff --git a/docs/setup/JACKBOX_INTEGRATION.md b/docs/setup/JACKBOX_INTEGRATION.md index 42c3975..2ee3f1d 100644 --- a/docs/setup/JACKBOX_INTEGRATION.md +++ b/docs/setup/JACKBOX_INTEGRATION.md @@ -1,203 +1,226 @@ # Jackbox Game Picker API Integration -This document describes how the Kosmi/IRC relay integrates with the Jackbox Game Picker API for live voting and game notifications. +This document describes how the Kosmi/IRC relay integrates with the Jackbox Game Picker API for live voting, game announcements, and room code display. -## Features +## Overview -### 1. Vote Detection -The relay automatically detects when users vote on games using the `thisgame++` or `thisgame--` syntax in either Kosmi or IRC chat. +When enabled, the relay: -**How it works:** -- Users type `thisgame++` to upvote the current game -- Users type `thisgame--` to downvote the current game -- Votes are case-insensitive -- The relay filters out relayed messages (messages with `[irc]` or `[kosmi]` prefix) to prevent duplicate votes -- Only votes from actual users in each chat are sent to the API - -### 2. Game Notifications -When a new game is added to the Jackbox session via the API, the relay receives a webhook notification and broadcasts it to both Kosmi and IRC chats. - -**Example notification:** -``` -🎮 Coming up next: Fibbage 4! -``` +1. **Detects votes** in chat (`thisgame++`/`thisgame--` and ticker-symbol votes) and submits them to the API +2. **Receives game events** via WebSocket (or webhook fallback) and announces them to all bridges +3. **Responds to `!votes`** with the current game's vote tally +4. **Optionally generates room code images** and uploads them to Kosmi ## Configuration -Add the following section to your `matterbridge.toml`: +Add the `[jackbox]` section to `matterbridge.toml`: ```toml [jackbox] -# Enable Jackbox integration for vote detection and game notifications +# Enable Jackbox integration Enabled=true # Jackbox API URL -APIURL="http://localhost:5000" +APIURL="https://your-jackbox-api.example.com" -# Admin password for API authentication -AdminPassword="your_admin_password_here" +# Admin password for API authentication (exchanged for JWT) +AdminPassword="your_admin_password" -# Webhook server port (for receiving game notifications) +# Use WebSocket for real-time game notifications (recommended, default: true) +# Set to false to use webhook HTTP server instead +UseWebSocket=true + +# --- Webhook settings (only if UseWebSocket=false) --- +# Port for the webhook HTTP server WebhookPort=3001 +# HMAC-SHA256 secret for webhook signature verification +WebhookSecret="your_webhook_secret" -# Webhook secret for signature verification -WebhookSecret="your_webhook_secret_here" +# --- Room code image settings --- +# Generate and upload a room code image to Kosmi chat +EnableRoomCodeImage=true +# Seconds to wait before sending the image announcement (default: 0) +RoomCodeImageDelay=28 +# Seconds to wait before sending the plaintext room code follow-up (default: 29) +RoomCodePlaintextDelay=22 ``` -### Configuration Options +### Configuration Reference -- **Enabled**: Set to `true` to enable the integration, `false` to disable -- **APIURL**: The URL of your Jackbox Game Picker API (e.g., `http://localhost:5000`) -- **AdminPassword**: Your API admin password (used to authenticate and get a JWT token) -- **WebhookPort**: Port for the webhook server to listen on (default: 3001) -- **WebhookSecret**: Shared secret for webhook signature verification (must match the secret configured in the API) +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `Enabled` | bool | `false` | Master switch for the integration | +| `APIURL` | string | | Base URL of the Jackbox Game Picker API | +| `AdminPassword` | string | | Admin password, exchanged for a JWT via `POST /api/auth/login` | +| `UseWebSocket` | bool | `true` | Use WebSocket transport for events (recommended) | +| `WebhookPort` | int | `3001` | HTTP listen port for webhook server | +| `WebhookSecret` | string | | HMAC secret for webhook signature verification | +| `EnableRoomCodeImage` | bool | `false` | Generate/upload room code images | +| `RoomCodeImageDelay` | int | `0` | Seconds before image announcement | +| `RoomCodePlaintextDelay` | int | `29` | Seconds before plaintext room code | -## Setup Steps +## Transport: WebSocket vs. Webhook -### 1. Configure the Relay +### WebSocket (Recommended) -Edit `matterbridge.toml` and add the Jackbox configuration section with your API URL, admin password, and webhook secret. +When `UseWebSocket=true`, the relay connects to `wss:///api/sessions/live` and authenticates with its JWT. Events are received in real-time with automatic reconnection. -### 2. Register the Webhook +Handled events: +- `session.started` -- announces game night start +- `game.added` -- announces next game with room code +- `session.ended` -- announces game night end -After starting the relay, register the webhook with the Jackbox API: +No additional setup is required beyond the config above. + +### Webhook (Fallback) + +When `UseWebSocket=false`, the relay starts an HTTP server on `WebhookPort` and listens for `POST /webhook/jackbox` requests from the API. + +You must register the webhook with the API: ```bash # Get JWT token -curl -X POST "http://localhost:5000/api/auth/login" \ +TOKEN=$(curl -s -X POST "$API_URL/api/auth/login" \ -H "Content-Type: application/json" \ - -d '{"apiKey": "your_admin_password"}' + -d '{"key": "your_admin_password"}' | jq -r .token) -# Register webhook (use the JWT token from above) -curl -X POST "http://localhost:5000/api/webhooks" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ +# Register webhook +curl -X POST "$API_URL/api/webhooks" \ + -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Kosmi/IRC Relay", "url": "http://your-relay-host:3001/webhook/jackbox", - "secret": "your_webhook_secret_here", + "secret": "your_webhook_secret", "events": ["game.added"] }' ``` -**Important:** Replace `your-relay-host` with the actual hostname or IP address where your relay is running. If the API and relay are on the same machine, you can use `localhost`. +Webhook requests include an `X-Webhook-Signature` header with an HMAC-SHA256 signature for verification. -### 3. Test the Integration +## Vote Detection -#### Test Vote Detection +The relay automatically detects vote patterns in non-relayed messages from both IRC and Kosmi. -1. Start an active session in the Jackbox API with some games played -2. Send a message in Kosmi or IRC: `thisgame++` -3. Check the relay logs for: `Detected vote from : up` -4. Verify the vote was recorded in the API +### Supported Syntax -#### Test Game Notifications +| Pattern | Effect | +|---------|--------| +| `thisgame++` | Upvote currently playing game | +| `thisgame--` | Downvote currently playing game | +| `SYMBOL++` | Upvote game by ticker symbol | +| `SYMBOL--` | Downvote game by ticker symbol | -1. Add a game to the active session via the Jackbox API -2. Both Kosmi and IRC chats should receive a notification: `🎮 Coming up next: !` +- All patterns are case-insensitive +- `thisgame` votes can appear anywhere in the message +- Ticker symbols are 2-4 alphanumeric characters that map to specific games (see [IRC.md](../IRC.md) for the full ticker table) +- Messages prefixed with `[irc]` or `[kosmi]` are treated as relayed and skip vote detection + +Votes are submitted to `POST /api/votes/live` with the voter's username, vote type, timestamp, and optional ticker symbol. + +### Deduplication + +The API rejects duplicate votes from the same user within 1 second. + +## IRC/Kosmi Commands + +### `!votes` + +Available in both IRC and Kosmi chat. Queries the current game's vote tally and broadcasts the result to all bridges: + +``` +🗳️ Fibbage 4 • Today: 5👍 2👎 (Score: 3) • All-time: 46👍 3👎 (Score: 43) +``` + +Requires an active session with a game currently "playing". + +### Reconnect Commands (IRC only) + +| Command | Effect | +|---------|--------| +| `!jreconnect` | Reconnect the Jackbox WebSocket | +| `!kreconnect` | Reconnect the Kosmi bridge | +| `!reconnect` | Reconnect all non-IRC services | + +## Room Code Images + +When `EnableRoomCodeImage=true` and a `game.added` event includes a room code, the relay: + +1. Generates an image of the room code +2. Uploads it to the Kosmi CDN (`img.kosmi.io`) +3. Sends the image + announcement message after `RoomCodeImageDelay` seconds +4. Sends a plaintext room code follow-up after `RoomCodePlaintextDelay` seconds + +If image generation or upload fails, the relay falls back to plaintext-only announcement. + +## Mute Control + +Jackbox announcements (game start, game added, session end) can be suppressed without stopping the relay: + +- Start muted: `./matterbridge -conf matterbridge.toml -muted` +- Toggle at runtime: `kill -SIGUSR1 ` or `docker kill -s SIGUSR1 ` + +When muted: +- Announcements are suppressed +- Vote detection still works (votes are still sent to the API) +- Normal IRC/Kosmi message relay is unaffected +- `!votes` still works + +See [MUTE_CONTROL.md](MUTE_CONTROL.md) for full details. ## How It Works ### Vote Flow -1. User sends a message containing `thisgame++` or `thisgame--` in Kosmi or IRC -2. The bridge detects the vote pattern (case-insensitive) -3. The bridge checks if the message is relayed (has `[irc]` or `[kosmi]` prefix) -4. If not relayed, the bridge extracts the username and vote type -5. The bridge sends the vote to the Jackbox API via HTTP POST to `/api/votes/live` -6. The API records the vote and associates it with the current game based on timestamp +1. User sends a message containing `thisgame++` (or similar) in IRC or Kosmi +2. The bridge detects the vote pattern (skipping relayed messages) +3. The bridge submits `POST /api/votes/live` with username, vote type, and timestamp +4. The API records the vote against the currently playing game -### Notification Flow +### Event Flow (WebSocket) -1. A game is added to an active session in the Jackbox API -2. The API sends a webhook POST request to `http://your-relay-host:3001/webhook/jackbox` -3. The webhook includes an HMAC-SHA256 signature in the `X-Webhook-Signature` header -4. The relay verifies the signature using the configured webhook secret -5. If valid, the relay parses the `game.added` event -6. The relay broadcasts the game announcement to all connected bridges (Kosmi and IRC) +1. The relay authenticates with the Jackbox API and opens a WebSocket +2. When a session starts, the relay subscribes to that session's events +3. `game.added` events trigger game announcements broadcast to all bridges +4. `session.ended` triggers the end-of-night message -## Security +### Authentication -### Webhook Signature Verification - -All incoming webhooks are verified using HMAC-SHA256 signatures to ensure they come from the legitimate Jackbox API. - -**How it works:** -1. The API computes `HMAC-SHA256(webhook_secret, request_body)` -2. The signature is sent in the `X-Webhook-Signature` header as `sha256=` -3. The relay computes the expected signature using the same method -4. The relay uses timing-safe comparison to verify the signatures match -5. If verification fails, the webhook is rejected with a 401 Unauthorized response - -### JWT Authentication - -The relay authenticates with the Jackbox API using the admin password to obtain a JWT token. This token is: -- Cached to avoid re-authentication on every vote -- Automatically refreshed if it expires (detected via 401 response) -- Valid for 24 hours (configurable in the API) +The relay authenticates with `POST /api/auth/login` using the `AdminPassword` to get a JWT. The token is cached in memory and refreshed automatically on 401 responses. ## Troubleshooting ### Votes Not Being Recorded -**Possible causes:** -- No active session in the Jackbox API -- Vote timestamp doesn't match any played game -- Duplicate vote within 1 second -- Authentication failure +- Verify an active session exists with games played +- Check that the vote timestamp matches a played game's timeframe +- Look for `"Detected vote from"` in debug logs +- Look for `"Failed to send vote"` errors +- Confirm the message isn't prefixed with `[irc]`/`[kosmi]` (relayed messages skip vote detection) -**Check:** -1. Relay logs for vote detection: `grep "Detected vote" logs/matterbridge.log` -2. Relay logs for API errors: `grep "Failed to send vote" logs/matterbridge.log` -3. API logs for incoming vote requests +### Announcements Not Appearing -### Webhooks Not Being Received +- Verify `Enabled=true` in config +- Check `APIURL` is accessible and `AdminPassword` is correct +- Look for `"Jackbox WebSocket connected"` in logs (if using WebSocket) +- If using webhooks, verify the webhook is registered and the port is accessible +- Check mute state: look for `"MUTED"` in logs -**Possible causes:** -- Webhook URL is not accessible from the API server -- Webhook not registered in the API -- Signature verification failing -- Firewall blocking the webhook port +### WebSocket Disconnects -**Check:** -1. Verify webhook is registered: `GET /api/webhooks` (with JWT token) -2. Test webhook manually: `POST /api/webhooks/test/:id` (with JWT token) -3. Check webhook logs in API: `GET /api/webhooks/:id/logs` (with JWT token) -4. Verify webhook server is listening: `curl http://localhost:3001/health` -5. Check relay logs for signature verification errors +- The relay auto-reconnects with exponential backoff +- Use `!jreconnect` from IRC to force an immediate reconnection +- Check API server availability ### Authentication Failures -**Possible causes:** -- Incorrect admin password in configuration -- API is not running or not accessible - -**Check:** -1. Verify API URL is correct and accessible -2. Test authentication manually: - ```bash - curl -X POST "http://localhost:5000/api/auth/login" \ - -H "Content-Type: application/json" \ - -d '{"apiKey": "your_admin_password"}' - ``` -3. Check relay logs for authentication errors - -## Logs - -The relay logs all Jackbox-related activity with the `jackbox` prefix: - -``` -[jackbox] Initializing Jackbox integration... -[jackbox] Successfully authenticated with Jackbox API -[jackbox] Starting Jackbox webhook server on port 3001 -[kosmi] Detected vote from Anonymous Llama: up -[jackbox] Vote recorded for Fibbage 4: Anonymous Llama - 5👍 2👎 -[jackbox] Broadcasting Jackbox message: 🎮 Coming up next: Quiplash 3! +```bash +# Test authentication manually +curl -X POST "$API_URL/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"key": "your_admin_password"}' ``` -## Disabling the Integration - -To disable the Jackbox integration, set `Enabled=false` in the `[jackbox]` section of `matterbridge.toml` and restart the relay. - -The relay will continue to function normally for Kosmi ↔ IRC message relay without any Jackbox features. +## Disabling +Set `Enabled=false` in the `[jackbox]` section and restart. The relay continues to function normally for IRC/Kosmi message relay without any Jackbox features. diff --git a/docs/setup/QUICKSTART.md b/docs/setup/QUICKSTART.md index c9d5609..7d6bd80 100644 --- a/docs/setup/QUICKSTART.md +++ b/docs/setup/QUICKSTART.md @@ -1,301 +1,130 @@ # Quick Start Guide -Get the Kosmi-IRC bridge running in minutes! +Get the Kosmi-IRC bridge running in minutes. ## Prerequisites -- Go 1.21 or higher -- Chrome/Chromium browser installed (for headless browser automation) -- Access to a Kosmi room +- Go 1.23 or higher +- Chrome/Chromium (only needed for email/password authentication) +- A Kosmi room URL - (Optional) IRC server access for full relay -## Step 1: Test Kosmi Connection - -First, verify the bridge can connect to Kosmi: +## Step 1: Build the Bridge ```bash -# Build the test program -go build -o test-kosmi ./cmd/test-kosmi +cd irc-kosmi-relay -# Run with your Kosmi room URL -./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug -``` - -You should see output like: -``` -INFO[...] Starting Kosmi bridge test -INFO[...] Launching headless Chrome for Kosmi connection -INFO[...] Injecting WebSocket interceptor (runs before page load)... -INFO[...] Navigating to Kosmi room: https://app.kosmi.io/room/@hyperspaceout -INFO[...] ✓ WebSocket hook confirmed installed -INFO[...] Status: WebSocket connection intercepted -INFO[...] Successfully connected to Kosmi via Chrome -INFO[...] Listening for messages... Press Ctrl+C to exit -``` - -**Test it**: Send a message in the Kosmi room from your browser. You should see it appear in the terminal like: -``` -INFO[...] Received message: [00:02:51] username: [Kosmi] your message here -``` - -## Step 2: Integrate with Full Matterbridge - -### Option A: Copy into Existing Matterbridge - -If you already have Matterbridge: - -```bash -# Navigate to your Matterbridge directory -cd /path/to/matterbridge - -# Copy the Kosmi bridge -cp -r /path/to/irc-kosmi-relay/bridge/kosmi bridge/ - -# Copy the bridge registration -cp /path/to/irc-kosmi-relay/gateway/bridgemap/bkosmi.go gateway/bridgemap/ - -# Add dependencies -go get github.com/chromedp/chromedp@v0.11.2 -go mod tidy +# Download dependencies +go mod download # Build -go build +go build -o matterbridge . ``` -### Option B: Use This Repository +## Step 2: Configure -This repository has a minimal Matterbridge structure. To add IRC support: +Copy and edit the example configuration: -1. Copy IRC bridge from Matterbridge: - ```bash - # From the Matterbridge repo - cp -r bridge/irc /path/to/irc-kosmi-relay/bridge/ - cp gateway/bridgemap/birc.go /path/to/irc-kosmi-relay/gateway/bridgemap/ - ``` +```bash +cp matterbridge.toml.example matterbridge.toml +nano matterbridge.toml +``` -2. Update dependencies: - ```bash - cd /path/to/irc-kosmi-relay - go mod tidy - ``` - -## Step 3: Configure - -Edit `matterbridge.toml`: +Update these values: ```toml -# Kosmi configuration [kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@hyperspaceout" +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" -# IRC configuration -[irc.libera] -Server="irc.libera.chat:6667" -Nick="kosmi-bot" -UseTLS=false - -# Gateway to connect them -[[gateway]] -name="kosmi-irc-relay" -enable=true +[irc.zeronode] +Server="irc.libera.chat:6697" +Nick="your-bot-nick" +UseTLS=true [[gateway.inout]] -account="kosmi.hyperspaceout" -channel="main" - -[[gateway.inout]] -account="irc.libera" +account="irc.zeronode" channel="#your-channel" ``` -**Important**: Replace: -- `https://app.kosmi.io/room/@hyperspaceout` with your Kosmi room URL -- `#your-channel` with your IRC channel +**Important**: The Kosmi channel is always `"main"` (one channel per room). The IRC channel must include `#`. -## Step 4: Run +### Optional: Authenticated Kosmi Access + +To use a Kosmi account instead of anonymous access, add credentials: + +```toml +[kosmi.hyperspaceout] +RoomURL="https://app.kosmi.io/room/@YOUR_ROOM" +Email="your-email@example.com" +Password="your-password" +``` + +This requires Chrome/Chromium for the initial browser-based login. The JWT is cached locally for subsequent runs. + +## Step 3: Run ```bash ./matterbridge -conf matterbridge.toml ``` Or with debug logging: + ```bash ./matterbridge -conf matterbridge.toml -debug ``` -## Step 5: Test the Relay +### Expected Output (Success) -1. **Kosmi → IRC**: Send a message in Kosmi. It should appear in IRC as: +``` +INFO Running version ... +INFO Connecting to Kosmi +INFO Extracted room ID: @YOUR_ROOM +INFO No credentials provided, using anonymous access +INFO Successfully connected to Kosmi +INFO Connection succeeded (IRC) +INFO Gateway(s) started successfully. Now relaying messages +``` + +## Step 4: Test the Relay + +1. **Kosmi --> IRC**: Send a message in your Kosmi room. It should appear in IRC as: ``` - [Kosmi] your message here + [kosmi] your message here ``` -2. **IRC → Kosmi**: Send a message in IRC. It should appear in Kosmi as: +2. **IRC --> Kosmi**: Send a message in your IRC channel. It should appear in Kosmi as: ``` - [IRC] your message here + [irc] your message here ``` ## Troubleshooting -### Test program doesn't connect +### Kosmi connection fails -**Check**: -- Is Chrome/Chromium installed and accessible? -- Is the room URL correct? -- Can you access `app.kosmi.io` from your network? -- Try with `-debug` flag for more details +- Verify the room URL is correct (use the full URL from your browser) +- Check network connectivity: `curl -I https://app.kosmi.io` +- Run with `-debug` for detailed WebSocket connection logs -**Solution**: -```bash -# Test Chrome installation -which google-chrome chromium chromium-browser +**Supported room URL formats:** +- `https://app.kosmi.io/room/@roomname` +- `https://app.kosmi.io/room/roomid` -# Test network connectivity -curl -I https://app.kosmi.io +### Authentication issues -# Run with debug -./test-kosmi -room "YOUR_ROOM_URL" -debug -``` +- Verify Chrome/Chromium is installed: `which chromium chromium-browser google-chrome` +- Check credentials are correct by logging in manually at `app.kosmi.io` +- Delete the token cache to force a fresh login: `rm -f data/kosmi_token_cache.json` +- See [BROWSER_AUTH_GUIDE.md](BROWSER_AUTH_GUIDE.md) for detailed auth troubleshooting ### Messages not relaying -**Check**: -- Are both bridges connected? Look for "Successfully connected" in logs -- Is the gateway configuration correct? -- Are the channel names correct? - -**Solution**: -```bash -# Run with debug to see message flow -./matterbridge -conf matterbridge.toml -debug - -# Look for lines like: -# "Received message from Kosmi" -# "Forwarding to Matterbridge" -# "Sending to IRC" -``` - -### "Room ID extraction failed" - -**Check**: Room URL format - -**Supported formats**: -- `https://app.kosmi.io/room/@roomname` -- `https://app.kosmi.io/room/roomid` -- `@roomname` -- `roomid` - -**Solution**: Use the full URL from your browser's address bar - -### Messages not appearing from Kosmi - -**Check**: -- Is the WebSocket hook installed? Look for "✓ WebSocket hook confirmed installed" -- Is the WebSocket connection detected? Look for "Status: WebSocket connection intercepted" -- Are messages being captured? Enable debug logging to see message processing - -**Solution**: -The bridge uses headless Chrome with a WebSocket interceptor that runs **before page load**. This is critical for capturing messages. The implementation uses `Page.addScriptToEvaluateOnNewDocument` to ensure the hook is installed before any page JavaScript executes. - -If messages still aren't appearing: -1. Check Chrome console logs in debug mode -2. Verify the room URL is correct -3. Try sending a test message and watch the debug output - -### Cannot send messages to Kosmi - -The send functionality uses the headless Chrome instance to inject messages into the Kosmi chat input field. - -**To debug**: -1. Enable debug logging with `-debug` flag -2. Look for "Sending message to Kosmi" in logs -3. Check for any JavaScript errors in the browser console logs +- Confirm both bridges are connected (look for success messages in logs) +- Verify channel names match in the gateway configuration +- Enable debug logging to trace message flow ## Next Steps -- **Customize message format**: Edit the format strings in `kosmi.go` -- **Add more bridges**: Matterbridge supports Discord, Slack, Telegram, etc. -- **Set up as a service**: Use systemd or similar to run automatically -- **Monitor logs**: Set up log rotation and monitoring - -## Getting Help - -- Check `INTEGRATION.md` for detailed integration steps -- Check `README.md` for architecture details -- Enable debug logging for detailed troubleshooting -- Review the chrome extension code in `.examples/` for API details - -## Common Use Cases - -### Home Server Setup - -```toml -# Bridge your Kosmi room with your home IRC server -[kosmi.gamenight] -RoomURL="https://app.kosmi.io/room/@gamenight" - -[irc.home] -Server="irc.home.local:6667" -Nick="kosmi-relay" -UseTLS=false - -[[gateway]] -name="gamenight" -enable=true - -[[gateway.inout]] -account="kosmi.gamenight" -channel="main" - -[[gateway.inout]] -account="irc.home" -channel="#gamenight" -``` - -### Multiple Rooms - -```toml -# Bridge multiple Kosmi rooms -[kosmi.room1] -RoomURL="https://app.kosmi.io/room/@room1" - -[kosmi.room2] -RoomURL="https://app.kosmi.io/room/@room2" - -[irc.libera] -Server="irc.libera.chat:6667" -Nick="kosmi-bot" - -# Gateway for room1 -[[gateway]] -name="gateway1" -enable=true - -[[gateway.inout]] -account="kosmi.room1" -channel="main" - -[[gateway.inout]] -account="irc.libera" -channel="#room1" - -# Gateway for room2 -[[gateway]] -name="gateway2" -enable=true - -[[gateway.inout]] -account="kosmi.room2" -channel="main" - -[[gateway.inout]] -account="irc.libera" -channel="#room2" -``` - -## Success! - -If you see messages flowing both ways, congratulations! Your Kosmi-IRC bridge is working. 🎉 - -For advanced configuration and features, see the full documentation in `README.md` and `INTEGRATION.md`. - +- [DOCKER_QUICKSTART.md](DOCKER_QUICKSTART.md) -- Deploy with Docker +- [JACKBOX_INTEGRATION.md](JACKBOX_INTEGRATION.md) -- Enable Jackbox Game Picker +- [IRC.md](../IRC.md) -- IRC commands and voting reference +- [MUTE_CONTROL.md](MUTE_CONTROL.md) -- Control Jackbox announcement muting diff --git a/docs/setup/QUICK_REFERENCE.md b/docs/setup/QUICK_REFERENCE.md index c786e72..b881efb 100644 --- a/docs/setup/QUICK_REFERENCE.md +++ b/docs/setup/QUICK_REFERENCE.md @@ -1,237 +1,204 @@ # Quick Reference Guide -## Testing the Bridge +## Running the Bridge -### Build and Run Test Program +### Build and Run Locally ```bash cd /Users/erikfredericks/dev-ai/HSO/irc-kosmi-relay -go build -o test-kosmi ./cmd/test-kosmi -./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug +go build -o matterbridge . +./matterbridge -conf matterbridge.toml +``` + +### Run with Docker + +```bash +docker-compose up -d +docker-compose logs -f ``` ### Expected Output (Success) ``` -INFO Launching headless Chrome for Kosmi connection -INFO Injecting WebSocket interceptor (runs before page load)... -INFO Navigating to Kosmi room: https://app.kosmi.io/room/@hyperspaceout -INFO ✓ WebSocket hook confirmed installed -INFO Status: WebSocket connection intercepted -INFO Successfully connected to Kosmi via Chrome -INFO Listening for messages... Press Ctrl+C to exit +INFO Connecting to Kosmi +INFO Extracted room ID: @yourroom +INFO No credentials provided, using anonymous access +INFO Successfully connected to Kosmi +INFO Connection succeeded (IRC) +INFO Gateway(s) started successfully. Now relaying messages ``` -### When Messages Arrive - +If using email/password authentication: ``` -INFO Processing 1 messages from queue -INFO Received message: [00:02:51] username: [Kosmi] message text +INFO Authenticating with email/password... +INFO ✅ Authentication successful +INFO Successfully connected to Kosmi ``` ## Key Status Indicators -| Status Message | Meaning | Action | -|---------------|---------|--------| -| `✓ WebSocket hook confirmed installed` | Hook script is active | ✅ Good | -| `Status: WebSocket connection intercepted` | WebSocket is being captured | ✅ Good | -| `Status: No WebSocket connection detected yet` | Hook missed the WebSocket | ❌ Check injection timing | -| `Processing N messages from queue` | Messages are being captured | ✅ Good | +| Log Message | Meaning | +|-------------|---------| +| `Successfully connected to Kosmi` | Native WebSocket connection established | +| `Connection succeeded (IRC)` | IRC bridge is connected | +| `Gateway(s) started successfully` | All bridges up, relay is active | +| `✅ Using cached token` | Reusing saved JWT (no browser needed) | +| `✅ Authentication successful` | Browser login completed | +| `Kosmi connection lost unexpectedly` | WebSocket dropped, auto-reconnect triggered | -## Common Issues +## IRC Commands -### Issue: "No WebSocket connection detected yet" +| Command | Action | +|---------|--------| +| `!kreconnect` | Reconnect Kosmi bridge | +| `!jreconnect` | Reconnect Jackbox WebSocket | +| `!reconnect` | Reconnect all non-IRC services | +| `!votes` | Show current game vote tally | -**Cause**: WebSocket hook was injected too late - -**Fix**: Verify `injectWebSocketHookBeforeLoad()` uses `page.AddScriptToEvaluateOnNewDocument` - -### Issue: "Chrome not found" - -**Cause**: Chrome/Chromium not installed or not in PATH - -**Fix**: -```bash -# macOS -brew install --cask google-chrome - -# Ubuntu/Debian -sudo apt install chromium-browser - -# Verify installation -which google-chrome chromium chromium-browser -``` - -### Issue: Messages not appearing - -**Cause**: Multiple possibilities - -**Debug**: -1. Check for "✓ WebSocket hook confirmed installed" ✓ -2. Check for "Status: WebSocket connection intercepted" ✓ -3. Enable debug logging: `-debug` flag -4. Send a test message in the Kosmi room -5. Look for "Processing N messages from queue" +See [IRC.md](../IRC.md) for full details including vote syntax and ticker symbols. ## Configuration -### Minimal Test Configuration +### Minimal Configuration ```toml -[kosmi.test] -RoomURL="https://app.kosmi.io/room/@hyperspaceout" -Debug=true -``` +[kosmi.myroom] +RoomURL="https://app.kosmi.io/room/@yourroom" -### Full Matterbridge Configuration - -```toml -# Kosmi -[kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@hyperspaceout" - -# IRC -[irc.libera] -Server="irc.libera.chat:6667" +[irc.myserver] +Server="irc.libera.chat:6697" Nick="kosmi-relay" -UseTLS=false +UseTLS=true -# Gateway [[gateway]] name="kosmi-irc" enable=true [[gateway.inout]] -account="kosmi.hyperspaceout" +account="kosmi.myroom" channel="main" [[gateway.inout]] -account="irc.libera" +account="irc.myserver" channel="#your-channel" ``` +### With Authentication + +```toml +[kosmi.myroom] +RoomURL="https://app.kosmi.io/room/@yourroom" +Email="your-email@example.com" +Password="your-password" +``` + +### With Jackbox Integration + +```toml +[jackbox] +Enabled=true +APIURL="https://your-jackbox-api.example.com" +AdminPassword="your_password" +UseWebSocket=true +EnableRoomCodeImage=true +``` + ## Message Format -### Kosmi → IRC +### Kosmi --> IRC ``` -[Kosmi] message text +[kosmi] message text ``` -### IRC → Kosmi +### IRC --> Kosmi ``` -[IRC] message text +[irc] message text ``` +Format is controlled by `RemoteNickFormat` in each bridge's config section. + ## File Locations | File | Purpose | |------|---------| -| `bridge/kosmi/kosmi.go` | Main bridge implementation | -| `bridge/kosmi/chromedp_client.go` | Headless Chrome client | -| `bridge/kosmi/graphql.go` | GraphQL structures (legacy) | -| `cmd/test-kosmi/main.go` | Standalone test program | -| `matterbridge.toml` | Configuration file | +| `bridge/kosmi/kosmi.go` | Main Kosmi bridge | +| `bridge/kosmi/graphql_ws_client.go` | Native GraphQL WebSocket client | +| `bridge/kosmi/auth.go` | Anonymous login | +| `bridge/kosmi/browser_auth.go` | Chromedp browser login | +| `bridge/kosmi/token_cache.go` | JWT token caching | +| `bridge/irc/handlers.go` | IRC commands and vote detection | +| `bridge/jackbox/client.go` | Jackbox API client | +| `bridge/jackbox/votes.go` | Vote detection logic | +| `matterbridge.toml` | Runtime configuration | -## Key Implementation Details +## Common Issues -### WebSocket Hook Injection +### Kosmi connection fails -**MUST** use `page.AddScriptToEvaluateOnNewDocument` to inject **before page load**: +1. Verify `RoomURL` is correct and the room exists +2. Check network connectivity: `curl -I https://app.kosmi.io` +3. Enable debug logging: `-debug` flag +4. Check logs for WebSocket handshake errors -```go -chromedp.ActionFunc(func(ctx context.Context) error { - _, err := page.AddScriptToEvaluateOnNewDocument(script).Do(ctx) - return err -}) -``` +### Authentication fails -### Hook Script +1. Verify Chrome/Chromium is installed: `which chromium chromium-browser google-chrome` +2. Check credentials are correct +3. Delete cached token to force fresh login: `rm data/kosmi_token_cache.json` +4. Check for Kosmi UI changes that may break browser automation -Wraps `window.WebSocket` constructor to intercept all WebSocket connections: +### Messages not appearing -```javascript -window.WebSocket = function(url, protocols) { - const socket = new OriginalWebSocket(url, protocols); - // ... interception logic ... - return socket; -}; -``` +1. Confirm both bridges are connected (check logs) +2. Verify gateway config: Kosmi channel must be `"main"`, IRC channel must have `#` +3. Enable debug logging to trace message routing +4. Check for `RemoteNickFormat` issues -## Debugging Commands +### Jackbox votes not registering + +1. Verify Jackbox integration is enabled in config +2. Check that an active session exists with games played +3. Confirm messages don't start with `[irc]` or `[kosmi]` prefix (relayed messages skip vote detection) + +## CLI Flags ```bash -# Test Chrome installation -which google-chrome chromium chromium-browser - -# Test network connectivity -curl -I https://app.kosmi.io - -# Build with verbose output -go build -v -o test-kosmi ./cmd/test-kosmi - -# Run with debug logging -./test-kosmi -room "https://app.kosmi.io/room/@hyperspaceout" -debug - -# Check for linter errors -go vet ./... +./matterbridge -conf matterbridge.toml # Specify config file +./matterbridge -debug # Enable debug logging +./matterbridge -muted # Start with Jackbox announcements muted +./matterbridge -version # Show version info ``` -## Performance Notes +## Mute Toggle -- **Chrome Startup**: ~1-2 seconds -- **Page Load**: ~1-2 seconds -- **Message Latency**: ~100-500ms -- **Memory Usage**: ~100-200MB (Chrome process) +Toggle Jackbox announcements at runtime: -## Security Considerations +```bash +# Local +kill -SIGUSR1 $(pgrep matterbridge) -- Bridge runs Chrome in headless mode (no GUI) -- No credentials stored (anonymous access) -- WebSocket traffic is intercepted in memory only -- Messages are not logged to disk (unless debug logging enabled) - -## Production Deployment - -### systemd Service Example - -```ini -[Unit] -Description=Kosmi-IRC Relay -After=network.target - -[Service] -Type=simple -User=matterbridge -WorkingDirectory=/opt/matterbridge -ExecStart=/opt/matterbridge/matterbridge -conf /etc/matterbridge/matterbridge.toml -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target +# Docker +docker kill -s SIGUSR1 kosmi-irc-relay ``` -### Docker Considerations +## Docker Commands -When running in Docker, ensure: -- Chrome/Chromium is installed in the container -- `--no-sandbox` flag may be needed for Chrome -- Sufficient memory allocation (512MB minimum) +```bash +docker-compose up -d # Start +docker-compose down # Stop +docker-compose restart # Restart +docker-compose logs -f # Follow logs +docker-compose logs --tail=100 # Last 100 lines +docker-compose build --no-cache # Rebuild +``` -## Resources - -- **Documentation**: See `README.md`, `QUICKSTART.md`, `LESSONS_LEARNED.md` -- **Integration Guide**: See `INTEGRATION.md` -- **Implementation Details**: See `IMPLEMENTATION_SUMMARY.md` -- **ChromeDP Guide**: See `CHROMEDP_IMPLEMENTATION.md` - -## Support - -For issues: -1. Check this quick reference -2. Review `LESSONS_LEARNED.md` for common patterns -3. Enable debug logging for detailed output -4. Check Chrome console logs in debug mode +## Further Reading +- [README.md](../../README.md) -- Project overview and architecture +- [IRC.md](../IRC.md) -- IRC command and voting reference +- [DOCKER_QUICKSTART.md](DOCKER_QUICKSTART.md) -- 5-minute Docker setup +- [JACKBOX_INTEGRATION.md](JACKBOX_INTEGRATION.md) -- Jackbox Game Picker setup +- [BROWSER_AUTH_GUIDE.md](BROWSER_AUTH_GUIDE.md) -- Email/password authentication +- [MUTE_CONTROL.md](MUTE_CONTROL.md) -- Mute toggle guide diff --git a/docs/setup/QUICK_START_AUTH.md b/docs/setup/QUICK_START_AUTH.md index 796a638..178910f 100644 --- a/docs/setup/QUICK_START_AUTH.md +++ b/docs/setup/QUICK_START_AUTH.md @@ -1,4 +1,4 @@ -# Quick Start: Testing Authentication +# Quick Start: Authenticated Kosmi Access ## Step 1: Create Bot Account @@ -7,88 +7,72 @@ 3. Choose a display name (e.g., "HSO Relay Bot") 4. Save the credentials securely -## Step 2: Test with Monitor Script - -```bash -cd /Users/erikfredericks/dev-ai/HSO/irc-kosmi-relay - -# Run monitor in login mode -./bin/monitor-auth -login - -# In the browser that opens: -# 1. Log in with your bot credentials -# 2. Navigate to a room -# 3. Press Ctrl+C to stop - -# Review the captured data -cat auth-monitor.log | grep -A 5 "login" -``` - -## Step 3: Configure Matterbridge +## Step 2: Configure Matterbridge Edit `matterbridge.toml`: ```toml [kosmi.hyperspaceout] -RoomURL="https://app.kosmi.io/room/@hyperspaceout" +RoomURL="https://app.kosmi.io/room/@yourroom" Email="your-bot@example.com" Password="your-secure-password" ``` -## Step 4: Test Connection +## Step 3: Run and Verify ```bash -# Build the bridge -go build - -# Run with your config ./matterbridge -conf matterbridge.toml - -# Watch the logs for: -# - "Using authenticated connection" -# - "Logged in as: HSO Relay Bot" -# - "Successfully connected to Kosmi" ``` +Watch the logs for: +- `"Authenticating with email/password..."` -- browser login starting +- `"✅ Authentication successful"` -- login completed +- `"💾 Token cached"` -- token saved for reuse +- `"Successfully connected to Kosmi"` -- connected to room + +On subsequent runs with a cached token: +- `"✅ Using cached token"` -- no browser needed + +## Step 4: Test + +1. Send a message in the Kosmi room from a browser +2. Verify it appears in your IRC channel +3. Send a message from IRC +4. Verify it appears in Kosmi with the bot's display name + ## Verification Checklist -- [ ] Bot account created manually -- [ ] Credentials documented securely -- [ ] Monitor script captured login flow -- [ ] Config file updated with credentials +- [ ] Bot account created at app.kosmi.io +- [ ] Credentials added to `matterbridge.toml` - [ ] Bridge logs show authenticated connection - [ ] Bot display name appears correctly in chat -- [ ] Messages relay successfully +- [ ] Messages relay bidirectionally +- [ ] Token cache file exists (`data/kosmi_token_cache.json` or `~/.matterbridge/kosmi_token_cache.json`) ## Troubleshooting -### Wrong account logged in - -Check the log for "Logged in as: {name}". If it doesn't match your bot: -- Verify email/password in config -- Check for typos -- Ensure you're using the correct credentials - ### Anonymous connection despite credentials -Check that both Email AND Password are set: +Check that both `Email` and `Password` are set and non-empty: ```bash -grep -A 2 "Email=" matterbridge.toml +grep -E "Email|Password" matterbridge.toml ``` +### Authentication fails + +- Verify Chrome/Chromium is installed +- Test credentials by logging in manually at app.kosmi.io +- Delete cached token to force fresh auth: `rm data/kosmi_token_cache.json` +- Enable debug logging: `-debug` + ### Token expired -The bridge should auto-refresh. If not: -- Check logs for "Token refresh failed" +The bridge automatically refreshes tokens expiring within 7 days. If refresh fails: +- Check logs for authentication errors - Verify credentials are still valid -- Try manual login at app.kosmi.io +- Delete the token cache and restart ## Next Steps -Once authenticated connection works: -- Test reconnection (simulate network failure) -- Monitor for token refresh (wait 24 hours) -- Test with multiple rooms -- Set up as systemd service - -See the monitoring script output and logs for detailed information about Kosmi's authentication behavior. +- See [BROWSER_AUTH_GUIDE.md](BROWSER_AUTH_GUIDE.md) for full authentication details +- See [TOKEN_PERSISTENCE.md](TOKEN_PERSISTENCE.md) for token caching behavior diff --git a/docs/setup/ROOM_CODE_IMAGE_FEATURE.md b/docs/setup/ROOM_CODE_IMAGE_FEATURE.md index fd62267..12dd4a5 100644 --- a/docs/setup/ROOM_CODE_IMAGE_FEATURE.md +++ b/docs/setup/ROOM_CODE_IMAGE_FEATURE.md @@ -129,7 +129,7 @@ Potential improvements: ### Build errors - Ensure all dependencies are installed: `go mod tidy` -- Check Go version: Requires Go 1.19+ +- Check Go version: Requires Go 1.23+ - Verify `golang.org/x/image` is available ## References diff --git a/docs/setup/TEST_INSTRUCTIONS.md b/docs/setup/TEST_INSTRUCTIONS.md index d86e396..89dbcc4 100644 --- a/docs/setup/TEST_INSTRUCTIONS.md +++ b/docs/setup/TEST_INSTRUCTIONS.md @@ -1,255 +1,126 @@ # Message Relay Testing Instructions -**Bridge Status**: ✅ **ACTIVE AND READY** - -Both Kosmi and IRC are connected. The gateway is relaying messages. - ## Current Configuration -- **Kosmi Room**: https://app.kosmi.io/room/@hyperspaceout -- **IRC Server**: irc.zeronode.net:6697 -- **IRC Channel**: #cottongin -- **Status**: Both bridges connected and relaying +- **Kosmi Room**: Configured via `RoomURL` in `matterbridge.toml` +- **IRC Server**: Configured via `Server` in `matterbridge.toml` +- **IRC Channel**: Configured via gateway `[[gateway.inout]]` -## Test 1: Kosmi → IRC +## Test 1: Kosmi --> IRC -### Steps: -1. Open the Kosmi room in your browser: https://app.kosmi.io/room/@hyperspaceout -2. Type a message in the chat (e.g., "Test message from Kosmi 🚀") +### Steps + +1. Open your Kosmi room in a browser +2. Type a test message (e.g., "Test message from Kosmi") 3. Press Enter to send -### Expected Result: -- The message should appear in IRC channel #cottongin -- The logs will show: - ``` - level=info msg="📨 Received message from Kosmi: ..." prefix=kosmi - level=info msg="Relaying message from kosmi to irc" - ``` +### Expected Logs -### How to Verify: -Connect to IRC and join #cottongin: -```bash -# Using an IRC client (e.g., irssi, weechat, hexchat) -/connect irc.zeronode.net 6697 -/join #cottongin +``` +INFO Received message from Kosmi: [] username: message text +DEBUG Forwarding to Matterbridge channel=main account=kosmi.hyperspaceout ``` -Or watch the Docker logs: -```bash -docker-compose logs -f | grep -E "(Received|Relaying|Sent)" +### Verification + +The message should appear in your IRC channel with the configured `RemoteNickFormat`, typically: +``` +[kosmi] Test message from Kosmi ``` ---- +## Test 2: IRC --> Kosmi -## Test 2: IRC → Kosmi +### Steps -### Steps: -1. Connect to IRC: `irc.zeronode.net:6697` -2. Join channel: `/join #cottongin` -3. Send a message: "Test message from IRC 👋" +1. Connect to your IRC server and join the configured channel +2. Send a test message (e.g., "Test message from IRC") -### Expected Result: -- The message should appear in the Kosmi chat room -- The logs will show: - ``` - level=info msg="Received message from IRC: ..." prefix=irc - level=info msg="Relaying message from irc to kosmi" - level=info msg="✅ Sent message via Playwright-assisted WebSocket: ..." prefix=kosmi - ``` +### Expected Logs -### How to Verify: -- Open Kosmi room in browser: https://app.kosmi.io/room/@hyperspaceout -- Check if your IRC message appears in the chat +``` +DEBUG <= Sending message from #channel on irc.account to gateway +DEBUG => Sending message to Kosmi +``` ---- +### Verification -## Watch Logs in Real-Time +The message should appear in the Kosmi room with the configured format, typically: +``` +[irc] Test message from IRC +``` + +## Test 3: Jackbox Vote Detection (if enabled) + +### Steps + +1. In IRC or Kosmi, type: `thisgame++` +2. Check bot logs + +### Expected Logs + +``` +DEBUG Detected vote from username: up (ticker="") +``` + +## Test 4: IRC Commands + +### Steps + +1. In IRC, type: `!votes` +2. Check for broadcast response + +### Expected Response (if active session) + +``` +🗳️ Game Title • Today: X👍 Y👎 (Score: Z) • All-time: X👍 Y👎 (Score: Z) +``` + +## Watching Logs ```bash # All logs docker-compose logs -f -# Only message-related logs -docker-compose logs -f | grep -E "(message|Message|Received|Sent|Relaying)" +# Message-related logs only +docker-compose logs -f | grep -E "(Received|Sending|Forwarding|Detected)" # Last 50 lines docker-compose logs --tail=50 ``` ---- +## Success Criteria -## Quick IRC Connection Methods - -### Method 1: Web IRC Client -1. Go to: https://web.libera.chat/ -2. Connect to: irc.zeronode.net (port 6697, SSL) -3. Join: #cottongin - -### Method 2: Command-Line (irssi) -```bash -# Install irssi (if not installed) -brew install irssi # macOS -# or -apt-get install irssi # Linux - -# Connect -irssi -c irc.zeronode.net -p 6697 -/join #cottongin -``` - -### Method 3: Command-Line (nc for quick test) -```bash -# Simple netcat connection (read-only) -openssl s_client -connect irc.zeronode.net:6697 -# Then type: -NICK testuser -USER testuser 0 * :Test User -JOIN #cottongin -``` - ---- - -## What You Should See - -### In Docker Logs (Normal Operation): - -``` -✅ WebSocket established and ready! -✅ Native client fully connected! -Successfully connected to Kosmi -Connection succeeded (IRC) -irc.libera: joining #cottongin -Gateway(s) started successfully. Now relaying messages -``` - -### When a Message is Relayed (Kosmi → IRC): - -``` -level=info msg="📨 Received message from Kosmi: username: message text" prefix=kosmi -level=info msg="Handling message from kosmi.hyperspaceout" prefix=router -level=info msg="Relaying message to irc.libera" prefix=router -level=info msg="Sent message to #cottongin" prefix=irc -``` - -### When a Message is Relayed (IRC → Kosmi): - -``` -level=info msg="Received message from #cottongin: username: message text" prefix=irc -level=info msg="Handling message from irc.libera" prefix=router -level=info msg="Relaying message to kosmi.hyperspaceout" prefix=router -level=info msg="✅ Sent message via Playwright-assisted WebSocket: message text" prefix=kosmi -``` - ---- +1. Messages sent in Kosmi appear in IRC +2. Messages sent in IRC appear in Kosmi +3. Usernames are displayed correctly with bridge prefix +4. Messages appear within 1-2 seconds +5. No errors in logs during normal relay ## Troubleshooting ### No Messages Appearing -1. **Check bridge is running**: - ```bash - docker-compose ps - ``` - Should show `kosmi-irc-relay` as "Up" - -2. **Check logs for errors**: - ```bash - docker-compose logs --tail=100 - ``` - -3. **Verify connections**: - ```bash - docker-compose logs | grep -E "(Connected|connected|joined)" - ``` - Should show both Kosmi WebSocket and IRC connected - -4. **Restart if needed**: - ```bash - docker-compose restart - docker-compose logs -f - ``` +1. Check the container is running: `docker-compose ps` +2. Check for errors: `docker-compose logs --tail=100` +3. Verify both bridges are connected (look for success messages) +4. Restart if needed: `docker-compose restart` ### Messages Only Going One Direction -- **If Kosmi → IRC works but IRC → Kosmi doesn't**: - - Check Kosmi WebSocket is still connected: `docker-compose logs | grep WebSocket` - - The native client might have disconnected - - Restart: `docker-compose restart` - -- **If IRC → Kosmi works but Kosmi → IRC doesn't**: - - Check IRC connection: `docker-compose logs | grep irc` - - Verify IRC authentication +- **Kosmi --> IRC works, IRC --> Kosmi doesn't**: Check Kosmi WebSocket is connected +- **IRC --> Kosmi works, Kosmi --> IRC doesn't**: Check IRC connection status ### Message Formatting Issues -Messages are formatted as: -``` - message text -``` +Message format is controlled by `RemoteNickFormat` in each bridge's config section. Default: `[{PROTOCOL}] <{NICK}> ` -This is configurable in `matterbridge.toml` under the gateway settings. - ---- - -## Success Criteria - -✅ **Full Success** means: -1. Message sent in Kosmi appears in IRC #cottongin -2. Message sent in IRC #cottongin appears in Kosmi room -3. Usernames are preserved/displayed correctly -4. Messages appear within 1-2 seconds -5. No errors in logs - ---- - -## Next Steps After Testing - -Once both directions work: - -1. **Monitor stability** - Let it run for a few hours -2. **Check memory usage**: `docker stats kosmi-irc-relay` -3. **Review message formatting** - Adjust in `matterbridge.toml` if needed -4. **Set up log rotation** - For long-term operation -5. **Consider adding more channels** - Edit `matterbridge.toml` - ---- - -## Quick Commands Reference +## Quick Commands ```bash -# Start in background -docker-compose up -d - -# Follow logs -docker-compose logs -f - -# Stop -docker-compose down - -# Restart -docker-compose restart - -# Rebuild -docker-compose up --build -d - -# Check status -docker-compose ps - -# View resource usage -docker stats kosmi-irc-relay +docker-compose up -d # Start +docker-compose logs -f # Follow logs +docker-compose down # Stop +docker-compose restart # Restart +docker-compose ps # Status +docker stats kosmi-irc-relay # Resource usage ``` - ---- - -## Ready to Test! - -The bridge is **running and connected**. You can start testing immediately: - -1. 🌐 Open: https://app.kosmi.io/room/@hyperspaceout -2. 💬 Send a test message -3. 👀 Watch the logs: `docker-compose logs -f` -4. 🔗 Connect to IRC and verify the message appeared -5. 🔄 Send a message from IRC and check Kosmi - -Good luck! 🎉 - diff --git a/docs/setup/TOKEN_PERSISTENCE.md b/docs/setup/TOKEN_PERSISTENCE.md index e179161..196d747 100644 --- a/docs/setup/TOKEN_PERSISTENCE.md +++ b/docs/setup/TOKEN_PERSISTENCE.md @@ -8,10 +8,10 @@ The Kosmi bridge now caches JWT authentication tokens to avoid repeated browser ### Token Cache Location -The token cache is stored in a file called `kosmi_token_cache.json` in the following locations: +The token cache is stored in a file called `kosmi_token_cache.json`: -- **Docker (Production)**: `./data/kosmi_token_cache.json` (mounted from your host machine) -- **Local Development**: `~/.matterbridge/kosmi_token_cache.json` +- **Docker**: `./data/kosmi_token_cache.json` (mounted from host via `MATTERBRIDGE_DATA_DIR=/app/data`) +- **Local**: `~/.matterbridge/kosmi_token_cache.json` (or `$MATTERBRIDGE_DATA_DIR/kosmi_token_cache.json` if set) ### Token Cache Structure @@ -29,9 +29,9 @@ The cache file contains: ### Token Lifecycle 1. **On Startup**: The bridge checks for a cached token - - If found and valid, it uses the cached token (no browser automation needed) - - If expired or expiring within 7 days, it performs fresh authentication - - If not found, it performs fresh authentication + - If found and valid, it uses the cached token (no browser launch needed) + - If expired or expiring within 7 days, it performs fresh browser authentication + - If not found, it performs fresh browser authentication 2. **Token Expiry**: Kosmi JWT tokens expire after 1 year - The bridge automatically refreshes tokens that expire within 7 days