chore: cleanup repo

This commit is contained in:
cottongin
2026-04-05 06:06:36 -04:00
parent 54dd9dc999
commit 4188ae29af
14 changed files with 3 additions and 0 deletions

414
docs/setup/BOT_features.md Normal file
View 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

View 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**.

View 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! 🎉

View 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! 🚀

View 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/setup/MUTE_CONTROL.md Normal file
View 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/setup/QUICKSTART.md Normal file
View 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`.

View 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

View 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.

View 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)

View 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! 🎉

View 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