Compare commits
2 Commits
4188ae29af
...
c5e0fe06b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5e0fe06b0
|
||
|
|
c88b75f30d
|
367
README.md
367
README.md
@@ -3,45 +3,53 @@
|
||||
|
||||
# Kosmi-IRC Relay via Matterbridge
|
||||
|
||||
A Matterbridge plugin that bridges Kosmi chat rooms with IRC channels, enabling bidirectional message relay.
|
||||
A Matterbridge-based bridge that relays messages bidirectionally between Kosmi chat rooms and IRC channels, with optional Jackbox Game Picker integration for live voting and game announcements.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Real-time message relay between Kosmi and IRC
|
||||
- ✅ Headless Chrome automation for reliable Kosmi connection
|
||||
- ✅ WebSocket interception using Chrome DevTools Protocol
|
||||
- ✅ Anonymous Kosmi access (no authentication required)
|
||||
- ✅ Message formatting with source indicators
|
||||
- ✅ Automatic reconnection handling
|
||||
- ✅ Support for any Kosmi room via URL configuration
|
||||
- Real-time bidirectional message relay between Kosmi and IRC
|
||||
- Native GraphQL-over-WebSocket connection to Kosmi (no browser required for basic use)
|
||||
- Anonymous or authenticated Kosmi access (email/password login with token caching)
|
||||
- Jackbox Game Picker integration: live vote detection, game announcements, room code images
|
||||
- IRC commands: `!kreconnect`, `!jreconnect`, `!reconnect`, `!votes`
|
||||
- Ticker-symbol voting for specific games (e.g. `QPL3++`, `TMP2--`)
|
||||
- SIGUSR1 mute toggle for Jackbox announcements
|
||||
- Automatic reconnection on connection loss
|
||||
- Docker deployment with persistent token cache
|
||||
|
||||
## Architecture
|
||||
|
||||
This implementation extends Matterbridge with a custom Kosmi bridge that:
|
||||
This project is a trimmed fork of [Matterbridge](https://github.com/42wim/matterbridge) with two registered bridges: **Kosmi** and **IRC**. The Kosmi bridge connects directly to `wss://engine.kosmi.io/gql-ws` using the `graphql-transport-ws` protocol -- no headless browser is needed for the primary connection path.
|
||||
|
||||
1. Launches a headless Chrome instance using `chromedp`
|
||||
2. Navigates to the Kosmi room and injects a WebSocket interceptor **before page load**
|
||||
3. Captures GraphQL WebSocket messages (`wss://engine.kosmi.io/gql-ws`) from the page
|
||||
4. Relays messages bidirectionally with proper formatting:
|
||||
- **Kosmi → IRC**: `[Kosmi] <username> message`
|
||||
- **IRC → Kosmi**: `[IRC] <username> message`
|
||||
### How It Works
|
||||
|
||||
### Why Headless Chrome?
|
||||
1. **Anonymous access** (default): The bridge calls Kosmi's `anonLogin` HTTP mutation to obtain a JWT, then opens a native WebSocket connection.
|
||||
2. **Authenticated access** (optional): If `Email` and `Password` are configured, headless Chrome (chromedp) performs a one-time browser login to extract a JWT. The token is cached locally and reused across restarts.
|
||||
3. **Room subscription**: After connecting, the bridge subscribes to `newMessage` events via GraphQL and joins the configured room.
|
||||
4. **Message relay**: Incoming Kosmi messages are forwarded to IRC (and vice versa) through the Matterbridge gateway, with configurable nick formatting.
|
||||
|
||||
Kosmi's WebSocket API requires browser session cookies and context that are difficult to replicate with a native WebSocket client. Using headless Chrome automation ensures:
|
||||
- ✅ Automatic session management
|
||||
- ✅ Proper cookie handling
|
||||
- ✅ Reliable WebSocket connection
|
||||
- ✅ No authentication complexity
|
||||
### Message Flow
|
||||
|
||||
```
|
||||
IRC User --> IRC Server --> Matterbridge Gateway --> Kosmi GraphQL WS --> Kosmi Room
|
||||
Kosmi User --> Kosmi Room --> GraphQL subscription --> Matterbridge Gateway --> IRC Channel
|
||||
```
|
||||
|
||||
### Jackbox Integration (Optional)
|
||||
|
||||
When enabled, the relay connects to a Jackbox Game Picker API via WebSocket (or webhook fallback) to:
|
||||
|
||||
- Detect `thisgame++`/`thisgame--` votes and ticker-symbol votes in chat
|
||||
- Announce upcoming games with room codes (optionally as uploaded images)
|
||||
- Respond to `!votes` queries with current vote tallies
|
||||
|
||||
## Installation
|
||||
|
||||
### Option 1: Docker (Recommended) 🐳
|
||||
|
||||
The easiest way to run the bridge:
|
||||
### Option 1: Docker (Recommended)
|
||||
|
||||
```bash
|
||||
# 1. Edit configuration
|
||||
# 1. Copy and edit configuration
|
||||
cp matterbridge.toml.example matterbridge.toml
|
||||
nano matterbridge.toml
|
||||
|
||||
# 2. Build and run
|
||||
@@ -51,45 +59,47 @@ docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
**See**: `DOCKER_QUICKSTART.md` for 5-minute setup guide
|
||||
See [DOCKER_QUICKSTART.md](docs/setup/DOCKER_QUICKSTART.md) for a 5-minute setup guide.
|
||||
|
||||
### Option 2: Build from Source
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Go 1.21 or higher
|
||||
- Chrome or Chromium browser installed
|
||||
- Go 1.23 or higher
|
||||
- Chrome/Chromium (only required if using email/password authentication)
|
||||
- Access to an IRC server
|
||||
- A Kosmi room URL
|
||||
|
||||
#### Building
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone <repository-url>
|
||||
cd irc-kosmi-relay
|
||||
|
||||
# Download dependencies
|
||||
go mod download
|
||||
|
||||
# Build the bridge
|
||||
go build -o matterbridge
|
||||
go build -o matterbridge .
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `matterbridge.toml` to configure your bridge:
|
||||
Copy `matterbridge.toml.example` to `matterbridge.toml` and edit it. The key sections:
|
||||
|
||||
```toml
|
||||
# Kosmi configuration
|
||||
[kosmi.hyperspaceout]
|
||||
RoomURL="https://app.kosmi.io/room/@hyperspaceout"
|
||||
RoomURL="https://app.kosmi.io/room/@yourroom"
|
||||
# Optional: Email/password for authenticated access
|
||||
# Requires Chrome/Chromium for browser automation
|
||||
Email=""
|
||||
Password=""
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
# IRC configuration
|
||||
[irc.libera]
|
||||
Server="irc.libera.chat:6667"
|
||||
[irc.zeronode]
|
||||
Server="irc.libera.chat:6697"
|
||||
Nick="kosmi-relay"
|
||||
UseTLS=false
|
||||
UseTLS=true
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
# Gateway to connect Kosmi and IRC
|
||||
[[gateway]]
|
||||
@@ -101,22 +111,58 @@ account="kosmi.hyperspaceout"
|
||||
channel="main"
|
||||
|
||||
[[gateway.inout]]
|
||||
account="irc.libera"
|
||||
account="irc.zeronode"
|
||||
channel="#your-channel"
|
||||
|
||||
# Jackbox integration (optional)
|
||||
[jackbox]
|
||||
Enabled=false
|
||||
APIURL="https://your-jackbox-api.example.com"
|
||||
AdminPassword=""
|
||||
UseWebSocket=true
|
||||
EnableRoomCodeImage=true
|
||||
RoomCodeImageDelay=28
|
||||
RoomCodePlaintextDelay=22
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
### Configuration Reference
|
||||
|
||||
#### Kosmi Settings
|
||||
|
||||
- `RoomURL` (required): Full URL to the Kosmi room
|
||||
- Format: `https://app.kosmi.io/room/@roomname` or `https://app.kosmi.io/room/roomid`
|
||||
- `Server` (optional): WebSocket endpoint (default: `wss://engine.kosmi.io/gql-ws`)
|
||||
- `Debug` (optional): Enable debug logging
|
||||
| Key | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| `RoomURL` | Yes | Full URL to the Kosmi room |
|
||||
| `Email` | No | Email for authenticated access |
|
||||
| `Password` | No | Password for authenticated access |
|
||||
| `RemoteNickFormat` | No | Format for nicks from other bridges |
|
||||
|
||||
#### IRC Settings
|
||||
|
||||
See [Matterbridge IRC documentation](https://github.com/42wim/matterbridge/wiki/Section-IRC-(basic)) for full IRC configuration options.
|
||||
| Key | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| `Server` | Yes | IRC server address with port |
|
||||
| `Nick` | Yes | Bot's IRC nickname |
|
||||
| `UseTLS` | No | Enable TLS (recommended) |
|
||||
| `SkipTLSVerify` | No | Skip TLS cert verification |
|
||||
| `NickServNick` | No | NickServ service nick |
|
||||
| `NickServPassword` | No | NickServ password |
|
||||
| `UseSASL` | No | Use SASL authentication |
|
||||
| `RemoteNickFormat` | No | Format for nicks from other bridges |
|
||||
| `Channels` | No | Channels to auto-join |
|
||||
|
||||
#### Jackbox Settings
|
||||
|
||||
| Key | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| `Enabled` | Yes | Enable/disable Jackbox integration |
|
||||
| `APIURL` | Yes | Jackbox Game Picker API URL |
|
||||
| `AdminPassword` | Yes | API admin password for JWT auth |
|
||||
| `UseWebSocket` | No | Use WebSocket transport (default: true) |
|
||||
| `WebhookPort` | No | Webhook listen port (if not using WS) |
|
||||
| `WebhookSecret` | No | HMAC secret for webhook verification |
|
||||
| `EnableRoomCodeImage` | No | Generate room code images |
|
||||
| `RoomCodeImageDelay` | No | Seconds before sending image announcement |
|
||||
| `RoomCodePlaintextDelay` | No | Seconds before sending plaintext room code |
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -126,154 +172,163 @@ See [Matterbridge IRC documentation](https://github.com/42wim/matterbridge/wiki/
|
||||
|
||||
# Run with debug logging
|
||||
./matterbridge -conf matterbridge.toml -debug
|
||||
|
||||
# Start with Jackbox announcements muted
|
||||
./matterbridge -conf matterbridge.toml -muted
|
||||
```
|
||||
|
||||
## How It Works
|
||||
### CLI Flags
|
||||
|
||||
### Kosmi Connection
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-conf` | Path to config file (default: `matterbridge.toml`) |
|
||||
| `-debug` | Enable debug logging |
|
||||
| `-version` | Show version |
|
||||
| `-muted` | Start with Jackbox announcements muted |
|
||||
| `-gops` | Enable gops diagnostic agent |
|
||||
|
||||
The bridge connects to Kosmi using headless Chrome automation:
|
||||
### IRC Commands
|
||||
|
||||
1. **Launch Chrome**: Starts a headless Chrome instance via `chromedp`
|
||||
2. **Inject Hook**: Uses `Page.addScriptToEvaluateOnNewDocument` to inject a WebSocket interceptor **before any page scripts run**
|
||||
3. **Navigate**: Loads the Kosmi room URL
|
||||
4. **Intercept**: The injected script hooks `window.WebSocket` constructor to capture all WebSocket messages
|
||||
5. **Poll**: Continuously polls the message queue populated by the interceptor
|
||||
6. **Process**: Extracts chat messages from GraphQL subscription data
|
||||
Users can issue commands in IRC chat. See [docs/IRC.md](docs/IRC.md) for full details.
|
||||
|
||||
### Critical Implementation Detail
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `!kreconnect` | Reconnect the Kosmi bridge |
|
||||
| `!jreconnect` | Reconnect the Jackbox WebSocket |
|
||||
| `!reconnect` | Reconnect all non-IRC services |
|
||||
| `!votes` | Show vote tally for the current game |
|
||||
| `thisgame++` / `thisgame--` | Vote on the current game |
|
||||
| `SYMBOL++` / `SYMBOL--` | Vote on a game by ticker symbol |
|
||||
|
||||
The WebSocket hook **must** be injected before page load using `Page.addScriptToEvaluateOnNewDocument`. This ensures the hook is active when Kosmi's JavaScript creates the WebSocket connection. If injected after page load, the WebSocket will already be established and messages won't be captured.
|
||||
### Mute Toggle
|
||||
|
||||
### Message Flow
|
||||
Jackbox announcements can be toggled at runtime without restarting:
|
||||
|
||||
```
|
||||
IRC User → IRC Server → Matterbridge → Headless Chrome → Kosmi Room
|
||||
Kosmi User → Kosmi Room → WebSocket → Chrome Interceptor → Matterbridge → IRC Server → IRC Channel
|
||||
```bash
|
||||
# Local process
|
||||
kill -SIGUSR1 $(pgrep matterbridge)
|
||||
|
||||
# Docker
|
||||
docker kill -s SIGUSR1 kosmi-irc-relay
|
||||
```
|
||||
|
||||
### Message Filtering
|
||||
See [MUTE_CONTROL.md](docs/setup/MUTE_CONTROL.md) for details.
|
||||
|
||||
- The bridge ignores its own messages by checking for the `[IRC]` prefix
|
||||
- This prevents message loops between Kosmi and IRC
|
||||
## Environment Variables
|
||||
|
||||
## Technical Details
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `DEBUG=1` | Enable debug logging |
|
||||
| `MATTERBRIDGE_DATA_DIR` | Directory for persistent data (token cache) |
|
||||
| `TZ` | Timezone for container logs |
|
||||
| `CHROME_BIN` | Path to Chrome binary (for auth only) |
|
||||
|
||||
### GraphQL API
|
||||
|
||||
The Kosmi bridge uses the following GraphQL operations:
|
||||
|
||||
**Subscription** (receiving messages):
|
||||
```graphql
|
||||
subscription {
|
||||
newMessage(roomId: "roomId") {
|
||||
body
|
||||
time
|
||||
user {
|
||||
displayName
|
||||
username
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Mutation** (sending messages):
|
||||
```graphql
|
||||
mutation {
|
||||
sendMessage(roomId: "roomId", body: "message text") {
|
||||
id
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### File Structure
|
||||
## File Structure
|
||||
|
||||
```
|
||||
bridge/kosmi/
|
||||
├── kosmi.go # Main bridge implementation
|
||||
├── chromedp_client.go # Headless Chrome client with WebSocket interception
|
||||
└── graphql.go # GraphQL message structures (deprecated native client)
|
||||
|
||||
gateway/bridgemap/
|
||||
└── bkosmi.go # Bridge registration
|
||||
|
||||
cmd/test-kosmi/
|
||||
└── main.go # Standalone test program
|
||||
|
||||
matterbridge.toml # Configuration file
|
||||
go.mod # Go module dependencies
|
||||
irc-kosmi-relay/
|
||||
├── matterbridge.go # Entry point: flags, logger, router, mute toggle
|
||||
├── matterbridge.toml.example # Example configuration
|
||||
├── Dockerfile # Docker build (Go 1.23 + Chromium for auth)
|
||||
├── docker-compose.yml # Docker Compose deployment
|
||||
│
|
||||
├── bridge/
|
||||
│ ├── config/
|
||||
│ │ └── config.go # Message types, events, protocol config
|
||||
│ ├── kosmi/
|
||||
│ │ ├── kosmi.go # Kosmi bridge: Connect, Send, message handling
|
||||
│ │ ├── graphql_ws_client.go # Native GraphQL-over-WebSocket client
|
||||
│ │ ├── graphql.go # GraphQL message/payload structs
|
||||
│ │ ├── auth.go # Anonymous login (anonLogin HTTP mutation)
|
||||
│ │ ├── browser_auth.go # Chromedp browser login for email/password
|
||||
│ │ ├── token_cache.go # JWT token caching (file-based)
|
||||
│ │ ├── image_upload.go # Image upload to Kosmi CDN
|
||||
│ │ └── errors.go # Error types
|
||||
│ ├── irc/
|
||||
│ │ ├── irc.go # IRC bridge: Connect, Send, NAMES handling
|
||||
│ │ ├── handlers.go # PRIVMSG handler, commands, vote detection
|
||||
│ │ └── formatting.go # IRC formatting helpers
|
||||
│ └── jackbox/
|
||||
│ ├── client.go # Jackbox API client (REST + WebSocket)
|
||||
│ ├── votes.go # Vote detection (thisgame/ticker parsing)
|
||||
│ ├── tickers.go # Ticker symbol -> game title lookup table
|
||||
│ ├── webhook.go # Webhook transport (alternative to WS)
|
||||
│ ├── image_upload.go # Room code image generation + upload
|
||||
│ └── roomcode_image.go # Room code GIF rendering
|
||||
│
|
||||
├── gateway/
|
||||
│ ├── gateway.go # Gateway message routing and transformation
|
||||
│ ├── router.go # Router: message bus, Jackbox manager, broadcast
|
||||
│ ├── handlers.go # Event handlers: reconnect, votes, files
|
||||
│ └── bridgemap/
|
||||
│ ├── bkosmi.go # Registers "kosmi" protocol
|
||||
│ └── birc.go # Registers "irc" protocol
|
||||
│
|
||||
├── docs/
|
||||
│ ├── IRC.md # IRC command reference
|
||||
│ ├── setup/ # Setup and deployment guides
|
||||
│ └── archive/ # Historical development notes
|
||||
│
|
||||
└── cmd/ # Standalone test/debug tools
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
### Kosmi Connection Issues
|
||||
|
||||
**Problem**: Bridge fails to connect to Kosmi
|
||||
**Bridge fails to connect to Kosmi**
|
||||
|
||||
**Solutions**:
|
||||
- Verify Chrome/Chromium is installed: `which google-chrome chromium chromium-browser`
|
||||
- Verify the room URL is correct
|
||||
- Check network connectivity to `app.kosmi.io`
|
||||
- Enable debug logging to see detailed connection logs
|
||||
- Look for "✓ WebSocket hook confirmed installed" in logs
|
||||
- Look for "Status: WebSocket connection intercepted" in logs
|
||||
- Verify `RoomURL` is correct and the room exists
|
||||
- Check network connectivity to `app.kosmi.io` and `engine.kosmi.io`
|
||||
- Enable debug logging (`-debug`) to see WebSocket handshake details
|
||||
- Look for `"Successfully connected to Kosmi"` in logs
|
||||
|
||||
### Message Not Relaying
|
||||
**Authentication fails (email/password)**
|
||||
|
||||
**Problem**: Messages aren't being relayed between Kosmi and IRC
|
||||
- Verify Chrome/Chromium is installed and accessible
|
||||
- Check credentials are correct
|
||||
- Delete `data/kosmi_token_cache.json` to force a fresh login
|
||||
- See [BROWSER_AUTH_GUIDE.md](docs/setup/BROWSER_AUTH_GUIDE.md)
|
||||
|
||||
**Solutions**:
|
||||
- Verify both Kosmi and IRC connections are established
|
||||
- Check the gateway configuration in `matterbridge.toml`
|
||||
- Ensure channel names match in the gateway configuration
|
||||
- Check logs for errors
|
||||
### IRC Connection Issues
|
||||
|
||||
### Authentication Errors
|
||||
- Verify server address and port
|
||||
- Check TLS settings match the server's requirements
|
||||
- Confirm the bot's nick is not already in use
|
||||
- Check NickServ/SASL credentials if authentication is configured
|
||||
|
||||
**Problem**: Kosmi connection fails with authentication error
|
||||
### Messages Not Relaying
|
||||
|
||||
**Note**: Kosmi doesn't require authentication. If you see auth errors, verify:
|
||||
- The WebSocket URL is correct
|
||||
- The room ID is valid
|
||||
- Network isn't blocking WebSocket connections
|
||||
- Verify both bridges show as connected in logs
|
||||
- Check gateway configuration: Kosmi channel must be `"main"`, IRC channel must include `#`
|
||||
- Enable debug logging to trace message flow
|
||||
- Look for `"Forwarding to Matterbridge"` and `"Sending message"` in logs
|
||||
|
||||
## Development
|
||||
### Jackbox Integration Issues
|
||||
|
||||
### Adding Features
|
||||
- Verify `APIURL` is accessible and `AdminPassword` is correct
|
||||
- Check that a Jackbox session is active with games being played
|
||||
- Confirm `Enabled=true` in the `[jackbox]` config section
|
||||
- See [JACKBOX_INTEGRATION.md](docs/setup/JACKBOX_INTEGRATION.md)
|
||||
|
||||
The bridge follows Matterbridge's bridge interface:
|
||||
## Documentation
|
||||
|
||||
```go
|
||||
type Bridger interface {
|
||||
Send(msg config.Message) (string, error)
|
||||
Connect() error
|
||||
JoinChannel(channel config.ChannelInfo) error
|
||||
Disconnect() error
|
||||
}
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
To test the bridge:
|
||||
|
||||
1. Start the bridge with debug logging
|
||||
2. Send a message in the Kosmi room
|
||||
3. Verify it appears in IRC with `[Kosmi]` prefix
|
||||
4. Send a message in IRC
|
||||
5. Verify it appears in Kosmi with `[IRC]` prefix
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Anonymous Access**: The bridge connects anonymously to Kosmi, so it will have a randomly assigned username
|
||||
2. **Message Sending**: The GraphQL mutation for sending messages may need adjustment based on Kosmi's actual API
|
||||
3. **Room Discovery**: The bridge connects to a specific room; it doesn't support room discovery or listing
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [IRC.md](docs/IRC.md) | IRC commands, voting syntax, ticker symbols |
|
||||
| [DOCKER_QUICKSTART.md](docs/setup/DOCKER_QUICKSTART.md) | 5-minute Docker setup |
|
||||
| [DOCKER_DEPLOYMENT.md](docs/setup/DOCKER_DEPLOYMENT.md) | Full Docker deployment guide |
|
||||
| [QUICKSTART.md](docs/setup/QUICKSTART.md) | Build-from-source quickstart |
|
||||
| [JACKBOX_INTEGRATION.md](docs/setup/JACKBOX_INTEGRATION.md) | Jackbox Game Picker setup |
|
||||
| [BROWSER_AUTH_GUIDE.md](docs/setup/BROWSER_AUTH_GUIDE.md) | Email/password authentication |
|
||||
| [TOKEN_PERSISTENCE.md](docs/setup/TOKEN_PERSISTENCE.md) | Token caching details |
|
||||
| [MUTE_CONTROL.md](docs/setup/MUTE_CONTROL.md) | Mute toggle guide |
|
||||
|
||||
## Credits
|
||||
|
||||
- Based on [Matterbridge](https://github.com/42wim/matterbridge) by 42wim
|
||||
- Kosmi API reverse engineering from chrome extension analysis
|
||||
- Kosmi API integration via reverse-engineered GraphQL protocol
|
||||
|
||||
## License
|
||||
|
||||
Same as Matterbridge (Apache 2.0)
|
||||
|
||||
|
||||
@@ -11,16 +11,17 @@ import (
|
||||
|
||||
// Manager handles the Jackbox integration lifecycle
|
||||
type Manager struct {
|
||||
client *Client
|
||||
webhookServer *WebhookServer
|
||||
wsClient *WebSocketClient
|
||||
config config.Config
|
||||
log *logrus.Entry
|
||||
enabled bool
|
||||
useWebSocket bool
|
||||
messageCallback func(string)
|
||||
muted bool
|
||||
mu sync.RWMutex
|
||||
client *Client
|
||||
webhookServer *WebhookServer
|
||||
wsClient *WebSocketClient
|
||||
config config.Config
|
||||
log *logrus.Entry
|
||||
enabled bool
|
||||
useWebSocket bool
|
||||
messageCallback func(string)
|
||||
formattedMessageCallback func(string, string)
|
||||
muted bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager creates a new Jackbox manager
|
||||
@@ -67,15 +68,15 @@ func (m *Manager) Initialize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartWebhookServer starts the webhook server with the provided message callback
|
||||
func (m *Manager) StartWebhookServer(messageCallback func(string)) error {
|
||||
// StartWebhookServer starts the webhook server with the provided message callbacks
|
||||
func (m *Manager) StartWebhookServer(messageCallback func(string), formattedCallback func(string, string)) error {
|
||||
if !m.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use WebSocket if enabled, otherwise fall back to webhook
|
||||
if m.useWebSocket {
|
||||
return m.startWebSocketClient(messageCallback)
|
||||
return m.startWebSocketClient(messageCallback, formattedCallback)
|
||||
}
|
||||
|
||||
webhookPort := m.config.Viper().GetInt("jackbox.WebhookPort")
|
||||
@@ -102,11 +103,12 @@ func (m *Manager) StartWebhookServer(messageCallback func(string)) error {
|
||||
}
|
||||
|
||||
// startWebSocketClient starts the WebSocket client connection
|
||||
func (m *Manager) startWebSocketClient(messageCallback func(string)) error {
|
||||
func (m *Manager) startWebSocketClient(messageCallback func(string), formattedCallback func(string, string)) error {
|
||||
apiURL := m.config.Viper().GetString("jackbox.APIURL")
|
||||
|
||||
// Store the callback for use in monitoring
|
||||
// Store the callbacks for use in monitoring and reconnection
|
||||
m.messageCallback = messageCallback
|
||||
m.formattedMessageCallback = formattedCallback
|
||||
|
||||
// Wrap the callback to check mute status
|
||||
wrappedCallback := func(message string) {
|
||||
@@ -117,6 +119,15 @@ func (m *Manager) startWebSocketClient(messageCallback func(string)) error {
|
||||
messageCallback(message)
|
||||
}
|
||||
|
||||
// Wrap the formatted callback to check mute status
|
||||
wrappedFormattedCallback := func(ircMsg, plainMsg string) {
|
||||
if m.IsMuted() {
|
||||
m.log.Debugf("Jackbox formatted message suppressed (muted): %s", plainMsg)
|
||||
return
|
||||
}
|
||||
formattedCallback(ircMsg, plainMsg)
|
||||
}
|
||||
|
||||
// Set wrapped callback on client for vote broadcasts
|
||||
m.client.SetMessageCallback(wrappedCallback)
|
||||
|
||||
@@ -141,7 +152,7 @@ func (m *Manager) startWebSocketClient(messageCallback func(string)) error {
|
||||
m.config.Viper().GetInt("jackbox.RoomCodePlaintextDelay"))
|
||||
|
||||
// Create WebSocket client (pass the API client for vote tracking)
|
||||
m.wsClient = NewWebSocketClient(apiURL, token, wrappedCallback, m.client, enableRoomCodeImage, imageDelay, plaintextDelay, m.log)
|
||||
m.wsClient = NewWebSocketClient(apiURL, token, wrappedCallback, wrappedFormattedCallback, m.client, enableRoomCodeImage, imageDelay, plaintextDelay, m.log)
|
||||
|
||||
// Connect to WebSocket
|
||||
if err := m.wsClient.Connect(); err != nil {
|
||||
@@ -265,12 +276,12 @@ func (m *Manager) Reconnect() error {
|
||||
return fmt.Errorf("re-authentication failed: %w", err)
|
||||
}
|
||||
|
||||
// Rebuild the WebSocket client using the original callback
|
||||
// Rebuild the WebSocket client using the original callbacks
|
||||
if m.messageCallback == nil {
|
||||
return fmt.Errorf("no message callback registered")
|
||||
}
|
||||
|
||||
return m.startWebSocketClient(m.messageCallback)
|
||||
return m.startWebSocketClient(m.messageCallback, m.formattedMessageCallback)
|
||||
}
|
||||
|
||||
// GetClient returns the Jackbox API client (may be nil if disabled)
|
||||
|
||||
@@ -17,23 +17,24 @@ const (
|
||||
|
||||
// WebSocketClient handles WebSocket connection to Jackbox API
|
||||
type WebSocketClient struct {
|
||||
apiURL string
|
||||
token string
|
||||
conn *websocket.Conn
|
||||
messageCallback func(string)
|
||||
apiClient *Client // Reference to API client for vote tracking
|
||||
log *logrus.Entry
|
||||
mu sync.Mutex
|
||||
reconnectDelay time.Duration
|
||||
maxReconnect time.Duration
|
||||
stopChan chan struct{}
|
||||
listenDone chan struct{} // closed when the current listen() goroutine exits
|
||||
connected bool
|
||||
authenticated bool
|
||||
subscribedSession int
|
||||
enableRoomCodeImage bool // Whether to upload room code images to Kosmi
|
||||
roomCodeImageDelay time.Duration // Delay before sending image announcement
|
||||
roomCodePlaintextDelay time.Duration // Delay before sending plaintext room code
|
||||
apiURL string
|
||||
token string
|
||||
conn *websocket.Conn
|
||||
messageCallback func(string)
|
||||
formattedMessageCallback func(string, string) // (ircMsg, plainMsg)
|
||||
apiClient *Client // Reference to API client for vote tracking
|
||||
log *logrus.Entry
|
||||
mu sync.Mutex
|
||||
reconnectDelay time.Duration
|
||||
maxReconnect time.Duration
|
||||
stopChan chan struct{}
|
||||
listenDone chan struct{} // closed when the current listen() goroutine exits
|
||||
connected bool
|
||||
authenticated bool
|
||||
subscribedSession int
|
||||
enableRoomCodeImage bool // Whether to upload room code images to Kosmi
|
||||
roomCodeImageDelay time.Duration // Delay before sending image announcement
|
||||
roomCodePlaintextDelay time.Duration // Delay before sending plaintext room code
|
||||
}
|
||||
|
||||
// WebSocket message types
|
||||
@@ -74,20 +75,28 @@ type GameAddedData struct {
|
||||
} `json:"game"`
|
||||
}
|
||||
|
||||
// PollEndingData represents the poll.ending event data
|
||||
type PollEndingData struct {
|
||||
SessionID int `json:"sessionId"`
|
||||
EndsAt string `json:"endsAt"`
|
||||
DelaySeconds int `json:"delaySeconds"`
|
||||
}
|
||||
|
||||
// NewWebSocketClient creates a new WebSocket client
|
||||
func NewWebSocketClient(apiURL, token string, messageCallback func(string), apiClient *Client, enableRoomCodeImage bool, roomCodeImageDelay, roomCodePlaintextDelay time.Duration, log *logrus.Entry) *WebSocketClient {
|
||||
func NewWebSocketClient(apiURL, token string, messageCallback func(string), formattedMessageCallback func(string, string), apiClient *Client, enableRoomCodeImage bool, roomCodeImageDelay, roomCodePlaintextDelay time.Duration, log *logrus.Entry) *WebSocketClient {
|
||||
return &WebSocketClient{
|
||||
apiURL: apiURL,
|
||||
token: token,
|
||||
messageCallback: messageCallback,
|
||||
apiClient: apiClient,
|
||||
enableRoomCodeImage: enableRoomCodeImage,
|
||||
roomCodeImageDelay: roomCodeImageDelay,
|
||||
roomCodePlaintextDelay: roomCodePlaintextDelay,
|
||||
log: log,
|
||||
reconnectDelay: 1 * time.Second,
|
||||
maxReconnect: 30 * time.Second,
|
||||
stopChan: make(chan struct{}),
|
||||
apiURL: apiURL,
|
||||
token: token,
|
||||
messageCallback: messageCallback,
|
||||
formattedMessageCallback: formattedMessageCallback,
|
||||
apiClient: apiClient,
|
||||
enableRoomCodeImage: enableRoomCodeImage,
|
||||
roomCodeImageDelay: roomCodeImageDelay,
|
||||
roomCodePlaintextDelay: roomCodePlaintextDelay,
|
||||
log: log,
|
||||
reconnectDelay: 1 * time.Second,
|
||||
maxReconnect: 30 * time.Second,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +258,18 @@ func (c *WebSocketClient) handleMessage(data []byte) {
|
||||
case "session.ended":
|
||||
c.handleSessionEnded(msg.Data)
|
||||
|
||||
case "poll.start":
|
||||
c.handlePollStart()
|
||||
|
||||
case "poll.ending":
|
||||
c.handlePollEnding(msg.Data)
|
||||
|
||||
case "voting.ended":
|
||||
c.handleVotingEnded()
|
||||
|
||||
case "poll.ending.cancelled":
|
||||
c.log.Info("Poll ending cancelled, voting remains open")
|
||||
|
||||
case "pong":
|
||||
c.log.Debug("Heartbeat pong received")
|
||||
|
||||
@@ -450,6 +471,46 @@ func (c *WebSocketClient) AnnounceSessionEnd() {
|
||||
}
|
||||
}
|
||||
|
||||
// handlePollStart announces that a new poll has opened
|
||||
func (c *WebSocketClient) handlePollStart() {
|
||||
c.log.Info("Poll started")
|
||||
|
||||
ircMsg := "🗳️ There is a new poll open! Your vote is \x02REQUIRED\x02 https://hso.ehsf.cc/vote"
|
||||
plainMsg := "🗳️ There is a new poll open! Your vote is *REQUIRED* https://hso.ehsf.cc/vote"
|
||||
|
||||
if c.formattedMessageCallback != nil {
|
||||
c.formattedMessageCallback(ircMsg, plainMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// handlePollEnding announces that the poll is about to close
|
||||
func (c *WebSocketClient) handlePollEnding(data json.RawMessage) {
|
||||
var pollData PollEndingData
|
||||
if err := json.Unmarshal(data, &pollData); err != nil {
|
||||
c.log.Errorf("Failed to parse poll.ending data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.log.Infof("Poll ending in %d seconds", pollData.DelaySeconds)
|
||||
|
||||
ircMsg := fmt.Sprintf("🗳️ \x02HURRY!\x02 https://hso.ehsf.cc/vote Go vote now, the poll is closing in %d seconds", pollData.DelaySeconds)
|
||||
plainMsg := fmt.Sprintf("🗳️ *HURRY!* https://hso.ehsf.cc/vote Go vote now, the poll is closing in %d seconds", pollData.DelaySeconds)
|
||||
|
||||
if c.formattedMessageCallback != nil {
|
||||
c.formattedMessageCallback(ircMsg, plainMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// handleVotingEnded announces that the poll has closed
|
||||
func (c *WebSocketClient) handleVotingEnded() {
|
||||
c.log.Info("Voting ended")
|
||||
|
||||
message := "🗳️ You missed your chance to vote, do your duty next time"
|
||||
if c.messageCallback != nil {
|
||||
c.messageCallback(message)
|
||||
}
|
||||
}
|
||||
|
||||
// startHeartbeat sends ping messages periodically. It exits when listenDone
|
||||
// is closed (current connection ended) or stopChan is closed (full shutdown).
|
||||
func (c *WebSocketClient) startHeartbeat(listenDone <-chan struct{}) {
|
||||
|
||||
182
docs/IRC.md
Normal file
182
docs/IRC.md
Normal file
@@ -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] <username> 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.
|
||||
137
docs/POLL.md
Normal file
137
docs/POLL.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Poll WebSocket Messages
|
||||
|
||||
## Overview
|
||||
|
||||
Poll messages manage the lifecycle of viewer polls over the upstream WebSocket connection. They are session-scoped — you must be subscribed to the relevant session to send or receive them.
|
||||
|
||||
All messages use the standard envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "<message-type>",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Message Flow
|
||||
|
||||
```
|
||||
poll.start ──► (poll is open, viewers are voting)
|
||||
│
|
||||
poll.ending ──► (countdown active)
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ │
|
||||
voting.ended poll.ending.cancelled
|
||||
(poll is closed) (countdown aborted,
|
||||
poll stays open)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Client → Server
|
||||
|
||||
### poll.start
|
||||
|
||||
Triggers poll generation for the active session.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "poll.start",
|
||||
"sessionId": 3
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------------|--------|----------|-----------------------------------------------------------------------------|
|
||||
| `type` | string | yes | `"poll.start"` |
|
||||
| `sessionId` | number | no | Included for protocol consistency. The server uses its internally tracked session ID. |
|
||||
|
||||
Any existing active poll is deactivated and replaced.
|
||||
|
||||
### poll.leading
|
||||
|
||||
Sent by the client to report the current leading option. Typically sent whenever the lead changes.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "poll.leading",
|
||||
"sessionId": 3,
|
||||
"gameId": 17,
|
||||
"label": "Quiplash 3",
|
||||
"votes": 12
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------------|--------|----------|---------------------------------------|
|
||||
| `type` | string | yes | `"poll.leading"` |
|
||||
| `sessionId` | number | yes | Active session ID |
|
||||
| `gameId` | number | yes | Game ID of the leading option (0 for "Other") |
|
||||
| `label` | string | yes | Display label of the leading option |
|
||||
| `votes` | number | yes | Current vote count for the leader |
|
||||
|
||||
---
|
||||
|
||||
## Server → Client
|
||||
|
||||
### poll.ending
|
||||
|
||||
Signals that voting will close after a countdown. Sent when the server initiates poll closure.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "poll.ending",
|
||||
"data": {
|
||||
"sessionId": 3,
|
||||
"endsAt": "2026-05-13T02:20:30.123456789Z",
|
||||
"delaySeconds": 30
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|----------------|--------|--------------------------------------------------|
|
||||
| `sessionId` | number | Active session ID |
|
||||
| `endsAt` | string | RFC 3339 timestamp when voting closes |
|
||||
| `delaySeconds` | number | Seconds remaining until voting closes |
|
||||
|
||||
### poll.ending.cancelled
|
||||
|
||||
The previously announced countdown has been cancelled. Voting remains open.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "poll.ending.cancelled",
|
||||
"data": {
|
||||
"sessionId": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------------|--------|-------------------|
|
||||
| `sessionId` | number | Active session ID |
|
||||
|
||||
### voting.ended
|
||||
|
||||
Voting has closed. The active poll is finalized.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "voting.ended"
|
||||
}
|
||||
```
|
||||
|
||||
No `data` payload. The server does not include results — consumers should query their own tallies or rely on the preceding `poll.leading` updates for the final state.
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle Notes
|
||||
|
||||
- A `poll.start` always replaces any existing active poll.
|
||||
- `poll.ending` may be followed by either `voting.ended` (normal close) or `poll.ending.cancelled` (countdown aborted).
|
||||
- After `voting.ended`, no further vote-related messages are sent until the next `poll.start`.
|
||||
- `game.started` implicitly deactivates any active poll — no explicit poll message is sent.
|
||||
279
docs/archive/README.md.2026-05-10.bak
Normal file
279
docs/archive/README.md.2026-05-10.bak
Normal file
@@ -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] <username> message`
|
||||
- **IRC → Kosmi**: `[IRC] <username> 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 <repository-url>
|
||||
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)
|
||||
|
||||
414
docs/archive/setup-backup-2026-05-10/BOT_features.md
Normal file
414
docs/archive/setup-backup-2026-05-10/BOT_features.md
Normal file
@@ -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_signature>` - 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
|
||||
|
||||
277
docs/archive/setup-backup-2026-05-10/BROWSER_AUTH_GUIDE.md
Normal file
277
docs/archive/setup-backup-2026-05-10/BROWSER_AUTH_GUIDE.md
Normal file
@@ -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**.
|
||||
|
||||
534
docs/archive/setup-backup-2026-05-10/DOCKER_DEPLOYMENT.md
Normal file
534
docs/archive/setup-backup-2026-05-10/DOCKER_DEPLOYMENT.md
Normal file
@@ -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] <username> message`
|
||||
|
||||
2. **IRC → Kosmi**: Send a message in your IRC channel
|
||||
- Should appear in Kosmi as: `[IRC] <username> 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 <your-repo> 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! 🎉
|
||||
|
||||
129
docs/archive/setup-backup-2026-05-10/DOCKER_QUICKSTART.md
Normal file
129
docs/archive/setup-backup-2026-05-10/DOCKER_QUICKSTART.md
Normal file
@@ -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! 🚀
|
||||
|
||||
203
docs/archive/setup-backup-2026-05-10/JACKBOX_INTEGRATION.md
Normal file
203
docs/archive/setup-backup-2026-05-10/JACKBOX_INTEGRATION.md
Normal file
@@ -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 <username>: 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: <game title>!`
|
||||
|
||||
## 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=<hex_signature>`
|
||||
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.
|
||||
|
||||
225
docs/archive/setup-backup-2026-05-10/MUTE_CONTROL.md
Normal file
225
docs/archive/setup-backup-2026-05-10/MUTE_CONTROL.md
Normal file
@@ -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 <pid>
|
||||
```
|
||||
|
||||
**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 <container_name>
|
||||
```
|
||||
|
||||
**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 <pid> or docker kill -s SIGUSR1 <container>)
|
||||
```
|
||||
|
||||
**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
|
||||
```
|
||||
|
||||
301
docs/archive/setup-backup-2026-05-10/QUICKSTART.md
Normal file
301
docs/archive/setup-backup-2026-05-10/QUICKSTART.md
Normal file
@@ -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] <username> 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] <username> your message here
|
||||
```
|
||||
|
||||
2. **IRC → Kosmi**: Send a message in IRC. It should appear in Kosmi as:
|
||||
```
|
||||
[IRC] <username> 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`.
|
||||
|
||||
237
docs/archive/setup-backup-2026-05-10/QUICK_REFERENCE.md
Normal file
237
docs/archive/setup-backup-2026-05-10/QUICK_REFERENCE.md
Normal file
@@ -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] <username> 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] <username> message text
|
||||
```
|
||||
|
||||
### IRC → Kosmi
|
||||
|
||||
```
|
||||
[IRC] <username> 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
|
||||
|
||||
94
docs/archive/setup-backup-2026-05-10/QUICK_START_AUTH.md
Normal file
94
docs/archive/setup-backup-2026-05-10/QUICK_START_AUTH.md
Normal file
@@ -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.
|
||||
141
docs/archive/setup-backup-2026-05-10/ROOM_CODE_IMAGE_FEATURE.md
Normal file
141
docs/archive/setup-backup-2026-05-10/ROOM_CODE_IMAGE_FEATURE.md
Normal file
@@ -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)
|
||||
|
||||
255
docs/archive/setup-backup-2026-05-10/TEST_INSTRUCTIONS.md
Normal file
255
docs/archive/setup-backup-2026-05-10/TEST_INSTRUCTIONS.md
Normal file
@@ -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:
|
||||
```
|
||||
<username> 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! 🎉
|
||||
|
||||
181
docs/archive/setup-backup-2026-05-10/TOKEN_PERSISTENCE.md
Normal file
181
docs/archive/setup-backup-2026-05-10/TOKEN_PERSISTENCE.md
Normal file
@@ -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
|
||||
@@ -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 <pid>` or `docker kill -s SIGUSR1 <container>`
|
||||
|
||||
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_signature>` - 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>/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;
|
||||
}
|
||||
Minimal setup:
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
```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
|
||||
|
||||
@@ -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**.
|
||||
|
||||
|
||||
@@ -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] <username> message`
|
||||
1. **Kosmi --> IRC**: Send a message in your Kosmi room
|
||||
- Should appear in IRC as: `[kosmi] <username> message`
|
||||
|
||||
2. **IRC → Kosmi**: Send a message in your IRC channel
|
||||
- Should appear in Kosmi as: `[IRC] <username> message`
|
||||
2. **IRC --> Kosmi**: Send a message in your IRC channel
|
||||
- Should appear in Kosmi as: `[irc] <username> 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 <your-repo> 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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://<APIURL>/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 <username>: 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: <game title>!`
|
||||
- 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 <pid>` or `docker kill -s SIGUSR1 <container>`
|
||||
|
||||
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=<hex_signature>`
|
||||
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.
|
||||
|
||||
@@ -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] <username> 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] <username> your message here
|
||||
[kosmi] <username> 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] <username> your message here
|
||||
[irc] <username> 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
|
||||
|
||||
@@ -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] <username> 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] <username> message text
|
||||
[kosmi] <username> message text
|
||||
```
|
||||
|
||||
### IRC → Kosmi
|
||||
### IRC --> Kosmi
|
||||
|
||||
```
|
||||
[IRC] <username> message text
|
||||
[irc] <username> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: [<timestamp>] 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] <username> 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] <username> 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:
|
||||
```
|
||||
<username> 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! 🎉
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -119,7 +119,7 @@ func (r *Router) Start() error {
|
||||
|
||||
// Start webhook server if Jackbox is enabled
|
||||
if r.JackboxManager.IsEnabled() {
|
||||
if err := r.JackboxManager.StartWebhookServer(r.broadcastJackboxMessage); err != nil {
|
||||
if err := r.JackboxManager.StartWebhookServer(r.broadcastJackboxMessage, r.broadcastJackboxFormattedMessage); err != nil {
|
||||
r.logger.Errorf("Failed to start Jackbox webhook server: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -274,3 +274,34 @@ func (r *Router) broadcastJackboxMessage(message string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcastJackboxFormattedMessage broadcasts per-bridge formatted messages.
|
||||
// IRC bridges receive ircMsg (with IRC control codes), all others receive plainMsg.
|
||||
func (r *Router) broadcastJackboxFormattedMessage(ircMsg, plainMsg string) {
|
||||
if r.JackboxManager != nil && r.JackboxManager.IsMuted() {
|
||||
r.logger.Debugf("Jackbox formatted message suppressed (muted): %s", plainMsg)
|
||||
return
|
||||
}
|
||||
|
||||
r.logger.Infof("Broadcasting formatted Jackbox message: %s", plainMsg)
|
||||
|
||||
for _, gw := range r.Gateways {
|
||||
for _, br := range gw.Bridges {
|
||||
text := plainMsg
|
||||
if br.Protocol == "irc" {
|
||||
text = ircMsg
|
||||
}
|
||||
|
||||
msg := config.Message{
|
||||
Text: " " + text,
|
||||
Username: "Jackbox",
|
||||
Account: "jackbox",
|
||||
Event: config.EventUserAction,
|
||||
}
|
||||
|
||||
if _, err := br.Send(msg); err != nil {
|
||||
r.logger.Errorf("Failed to send formatted Jackbox message to %s: %v", br.Account, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user