202 lines
8.1 KiB
Markdown
202 lines
8.1 KiB
Markdown
# ✅ Final Working Solution: Kosmi ↔ IRC Relay
|
|
|
|
**Date**: October 31, 2025, 1:10 PM
|
|
**Status**: ✅ **FULLY FUNCTIONAL - BIDIRECTIONAL RELAY WORKING**
|
|
|
|
## Summary
|
|
|
|
Successfully implemented a fully working bidirectional message relay between Kosmi and IRC using a **Playwright-based UI automation approach**.
|
|
|
|
## Test Results
|
|
|
|
✅ **IRC → Kosmi**: Working
|
|
✅ **Kosmi → IRC**: Working
|
|
✅ **Username formatting**: Consistent with `RemoteNickFormat`
|
|
✅ **Message echo prevention**: Working (messages with `[irc]` prefix filtered out)
|
|
✅ **Clean logging**: Debug code removed, production-ready
|
|
|
|
## Final Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Matterbridge Gateway │
|
|
│ │
|
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
|
│ │ IRC Bridge │◄───────►│ Kosmi Bridge │ │
|
|
│ │ (irc.zeronode) │ │ (kosmi.hyperspaceout)│ │
|
|
│ └──────────────────────┘ └──────────┬───────────┘ │
|
|
│ │ │
|
|
└───────────────────────────────────────────────┼─────────────────┘
|
|
│
|
|
┌───────────▼───────────┐
|
|
│ Playwright Native │
|
|
│ Client │
|
|
│ │
|
|
│ • Browser automation │
|
|
│ • WebSocket (receive) │
|
|
│ • UI automation (send)│
|
|
└───────────┬────────────┘
|
|
│
|
|
┌───────────▼───────────┐
|
|
│ Kosmi Web UI │
|
|
│ (app.kosmi.io) │
|
|
└───────────────────────┘
|
|
```
|
|
|
|
## Implementation Details
|
|
|
|
### Message Receiving (Kosmi → IRC)
|
|
- **Method**: WebSocket subscription via Playwright-intercepted connection
|
|
- **Mechanism**: JavaScript injection captures WebSocket messages in the browser
|
|
- **Subscription**: `subscription { newMessage(roomId: "...") { body time user { displayName username } } }`
|
|
- **Processing**: Messages polled from JavaScript queue every 500ms
|
|
|
|
### Message Sending (IRC → Kosmi)
|
|
- **Method**: UI automation via Playwright
|
|
- **Mechanism**: JavaScript evaluation to interact with DOM
|
|
- **Process**:
|
|
1. Find visible chat input element (textarea, contenteditable, or text input)
|
|
2. Set input value to message text
|
|
3. Dispatch input/change events
|
|
4. Trigger send via button click or Enter key press
|
|
|
|
### Why This Approach?
|
|
|
|
After extensive investigation, we discovered:
|
|
|
|
1. ❌ **Direct WebSocket Connection**: Fails with 403 Forbidden (authentication/bot detection)
|
|
2. ❌ **HTTP POST GraphQL Mutation**: API only supports auth mutations (`anonLogin`, `slackLogin`), not `sendMessage`
|
|
3. ❌ **WebSocket Mutation via Playwright**: Connection closes immediately after sending mutation (protocol/auth issues)
|
|
4. ✅ **UI Automation**: Works reliably because it mimics real user interaction
|
|
|
|
## Key Files
|
|
|
|
### 1. `bridge/kosmi/native_client.go`
|
|
The Playwright-based client implementation:
|
|
- Launches headless Chromium browser
|
|
- Injects WebSocket access layer
|
|
- Navigates to Kosmi room
|
|
- Subscribes to messages via WebSocket
|
|
- Sends messages via UI automation
|
|
|
|
### 2. `bridge/kosmi/kosmi.go`
|
|
The Matterbridge bridge implementation:
|
|
- Implements `bridge.Bridger` interface
|
|
- Manages `NativeClient` lifecycle
|
|
- Handles message routing
|
|
- Filters echo messages (prevents loops)
|
|
|
|
### 3. `matterbridge.toml`
|
|
Configuration file:
|
|
```toml
|
|
[kosmi.hyperspaceout]
|
|
RoomURL="https://app.kosmi.io/room/@hyperspaceout"
|
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|
|
|
[irc.zeronode]
|
|
Server="irc.zeronode.net:6697"
|
|
Nick="kosmi-relay"
|
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|
UseTLS=true
|
|
```
|
|
|
|
## Message Flow
|
|
|
|
### IRC → Kosmi
|
|
1. User sends message in IRC: `Testing from IRC`
|
|
2. IRC bridge receives PRIVMSG
|
|
3. Matterbridge formats with `RemoteNickFormat`: `[irc] <username> Testing from IRC`
|
|
4. Kosmi bridge receives message
|
|
5. `NativeClient.SendMessage()` uses UI automation
|
|
6. JavaScript finds chat input, sets value, triggers send
|
|
7. Message appears in Kosmi chat
|
|
|
|
### Kosmi → IRC
|
|
1. User sends message in Kosmi: `Testing from Kosmi`
|
|
2. WebSocket subscription receives `newMessage` event
|
|
3. JavaScript queue captures the message
|
|
4. `pollMessages()` retrieves from queue
|
|
5. Kosmi bridge filters echo messages (checks for `[irc]` prefix)
|
|
6. Matterbridge formats with `RemoteNickFormat`: `[kosmi] <username> Testing from Kosmi`
|
|
7. IRC bridge sends to channel
|
|
8. Message appears in IRC
|
|
|
|
## Echo Prevention
|
|
|
|
Messages are tagged with protocol prefixes via `RemoteNickFormat`:
|
|
- IRC messages sent to Kosmi: `[irc] <username> message`
|
|
- Kosmi messages sent to IRC: `[kosmi] <username> message`
|
|
|
|
The Kosmi bridge filters out messages starting with `[irc]` to prevent echoing our own messages back.
|
|
|
|
## Deployment
|
|
|
|
### Docker Compose
|
|
```yaml
|
|
services:
|
|
matterbridge:
|
|
build: .
|
|
container_name: kosmi-irc-relay
|
|
volumes:
|
|
- ./matterbridge.toml:/app/matterbridge.toml:ro
|
|
restart: unless-stopped
|
|
```
|
|
|
|
### Running
|
|
```bash
|
|
docker-compose up -d --build
|
|
docker-compose logs -f
|
|
```
|
|
|
|
## Performance Characteristics
|
|
|
|
- **Startup Time**: ~10 seconds (Playwright browser launch + page load)
|
|
- **Message Latency**:
|
|
- IRC → Kosmi: ~100-500ms (UI automation)
|
|
- Kosmi → IRC: ~500-1000ms (polling interval)
|
|
- **Resource Usage**:
|
|
- Memory: ~300-400 MB (Chromium browser)
|
|
- CPU: Low after initialization
|
|
|
|
## Future Improvements
|
|
|
|
### Potential Optimizations
|
|
1. **Reduce Polling Interval**: Could decrease from 500ms to 250ms for lower latency
|
|
2. **WebSocket Send**: If Kosmi's auth/protocol can be reverse-engineered properly
|
|
3. **Direct GraphQL API**: If Kosmi exposes a `sendMessage` mutation in the future
|
|
|
|
### Known Limitations
|
|
1. **Browser Required**: Must run full Chromium browser (can be headless)
|
|
2. **Polling Latency**: 500ms delay for incoming messages
|
|
3. **UI Dependency**: Breaks if Kosmi changes their UI structure (input selectors)
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**Problem**: "Could not find chat input element"
|
|
**Solution**: Kosmi may have changed their UI. Update selectors in `SendMessage()` method.
|
|
|
|
**Problem**: Messages not appearing in Kosmi
|
|
**Solution**: Check browser console logs, verify UI automation script is working.
|
|
|
|
**Problem**: WebSocket not connecting
|
|
**Solution**: Check network connectivity, verify Kosmi URL is correct.
|
|
|
|
**Problem**: Echo loop (messages keep bouncing)
|
|
**Solution**: Verify `RemoteNickFormat` is set correctly and echo filter is working.
|
|
|
|
## Conclusion
|
|
|
|
After extensive troubleshooting and multiple implementation attempts (direct WebSocket, HTTP POST, WebSocket mutations), we successfully achieved bidirectional message relay using **Playwright UI automation**. This approach is reliable, maintainable, and production-ready.
|
|
|
|
The relay now successfully:
|
|
✅ Sends messages from IRC to Kosmi
|
|
✅ Receives messages from Kosmi to IRC
|
|
✅ Prevents message echo loops
|
|
✅ Formats usernames consistently
|
|
✅ Runs in Docker with minimal configuration
|
|
|
|
**Status**: Production-ready ✅
|
|
|