Files
IRC-kosmi-relay/chat-summaries/2025-10-31_00-06-47_websocket-hook-fix.md
2025-10-31 16:17:04 -04:00

219 lines
7.8 KiB
Markdown

# Chat Summary: WebSocket Hook Fix - 2025-10-31 00:06:47
## Session Overview
**Date**: October 31, 2025, 00:06:47
**Task**: Fix message interception in the Kosmi bridge to ensure messages are captured correctly
**Status**: ✅ **COMPLETED AND VERIFIED**
## Problem Statement
The Kosmi bridge was successfully connecting to the room via headless Chrome, but messages sent in the Kosmi chat were not appearing in the bridge output. The logs showed:
```
INFO ✓ WebSocket hook confirmed installed
INFO Status: No WebSocket connection detected yet
```
This indicated that while the WebSocket interception script was being injected, it was not capturing the WebSocket connection that Kosmi was creating.
## Root Cause
The WebSocket hook was being injected **after** the page loaded, which meant:
1. Kosmi's JavaScript had already created the WebSocket connection
2. Our hook script ran too late to intercept the `window.WebSocket` constructor
3. Messages were flowing through the WebSocket but our interceptor never saw them
## Solution
### Key Insight from Chrome Extension
Examining `.examples/chrome-extension/inject.js` revealed the correct approach:
1. **Hook the raw `window.WebSocket` constructor** (not Apollo Client or other abstractions)
2. **Wrap both `addEventListener` and `onmessage`** to capture messages regardless of how Kosmi's code listens
3. **Inject the hook BEFORE any page scripts run**
### Critical Implementation Change
Changed from post-load injection:
```go
// ❌ WRONG - Too late!
chromedp.Run(ctx,
chromedp.Navigate(roomURL),
chromedp.WaitReady("body"),
chromedp.Evaluate(hookScript, nil), // WebSocket already created!
)
```
To pre-load injection using Chrome DevTools Protocol:
```go
// ✅ CORRECT - Runs before page scripts!
chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
_, err := page.AddScriptToEvaluateOnNewDocument(hookScript).Do(ctx)
return err
}))
chromedp.Run(ctx,
chromedp.Navigate(roomURL),
chromedp.WaitReady("body"),
)
```
### Updated Method in chromedp_client.go
```go
func (c *ChromeDPClient) injectWebSocketHookBeforeLoad() error {
script := c.getWebSocketHookScript()
return chromedp.Run(c.ctx, chromedp.ActionFunc(func(ctx context.Context) error {
// Use Page.addScriptToEvaluateOnNewDocument to inject before page load
// This is the proper way to inject scripts that run before page JavaScript
_, err := page.AddScriptToEvaluateOnNewDocument(script).Do(ctx)
return err
}))
}
```
## Verification
After applying the fix, the test program showed:
```
INFO[2025-10-31T00:02:39-04:00] Injecting WebSocket interceptor (runs before page load)...
INFO[2025-10-31T00:02:40-04:00] Navigating to Kosmi room: https://app.kosmi.io/room/@hyperspaceout
INFO[2025-10-31T00:02:41-04:00] ✓ WebSocket hook confirmed installed
INFO[2025-10-31T00:02:44-04:00] Status: WebSocket connection intercepted ← SUCCESS!
INFO[2025-10-31T00:02:44-04:00] Successfully connected to Kosmi via Chrome
INFO[2025-10-31T00:02:45-04:00] Processing 43 messages from queue
INFO[2025-10-31T00:02:51-04:00] Received message: [00:02:51] cottongin: [Kosmi] <cottongin> okay
INFO[2025-10-31T00:02:55-04:00] Received message: [00:02:55] cottongin: [Kosmi] <cottongin> it works
```
✅ Messages now appear in real-time!
## Files Modified
### 1. bridge/kosmi/chromedp_client.go
**Change**: Updated `injectWebSocketHookBeforeLoad()` to use `page.AddScriptToEvaluateOnNewDocument`
```go
func (c *ChromeDPClient) injectWebSocketHookBeforeLoad() error {
script := c.getWebSocketHookScript()
return chromedp.Run(c.ctx, chromedp.ActionFunc(func(ctx context.Context) error {
_, err := page.AddScriptToEvaluateOnNewDocument(script).Do(ctx)
return err
}))
}
```
**Impact**: This is the core fix that ensures the WebSocket hook runs before any page JavaScript.
### 2. QUICKSTART.md
**Changes**:
- Added Chrome/Chromium as a prerequisite
- Updated expected output to show ChromeDP-specific messages
- Updated troubleshooting section with Chrome-specific checks
- Added new troubleshooting section for message interception issues
- Updated dependency installation to use `chromedp` instead of `gorilla/websocket`
### 3. README.md
**Changes**:
- Added "Headless Chrome automation" and "WebSocket interception using Chrome DevTools Protocol" to features
- Updated architecture section to explain the ChromeDP approach
- Added "Why Headless Chrome?" section explaining the rationale
- Added Chrome/Chromium to prerequisites
- Updated "How It Works" section to describe the ChromeDP flow
- Added "Critical Implementation Detail" section about pre-load injection
- Updated message flow diagram
- Updated file structure to include `chromedp_client.go`
- Updated troubleshooting to include Chrome-specific checks
### 4. LESSONS_LEARNED.md (NEW)
**Purpose**: Comprehensive documentation of the WebSocket interception problem and solution
**Contents**:
- Problem description and evolution of approaches
- Detailed explanation of why post-load injection fails
- Complete code examples of wrong vs. correct approaches
- Implementation details in chromedp_client.go
- Verification steps
- Key takeaways
- How to apply this pattern to other projects
## Key Takeaways
1. **Timing is Critical**: WebSocket interception must happen before the WebSocket is created
2. **Use the Right CDP Method**: `Page.addScriptToEvaluateOnNewDocument` is specifically designed for pre-page-load injection
3. **Hook at the Lowest Level**: Hook `window.WebSocket` constructor, not higher-level abstractions
4. **Reference Working Code**: The Chrome extension's `inject.js` was the key to understanding the correct approach
5. **Verify with Diagnostics**: Status checks like "WebSocket connection intercepted" are essential for debugging
## Impact on Full Matterbridge Integration
**No additional changes needed!**
The fix in `chromedp_client.go` automatically applies to:
- The test program (`cmd/test-kosmi/main.go`)
- The full Matterbridge integration (`bridge/kosmi/kosmi.go`)
Both use the same `ChromeDPClient` implementation, so the fix works everywhere.
## Testing Recommendations
To verify the bridge is working correctly:
1. **Check connection status**:
```
✓ WebSocket hook confirmed installed
Status: WebSocket connection intercepted
```
2. **Send a test message** in the Kosmi room from a browser
3. **Verify message appears** in the bridge output:
```
INFO Received message: [HH:MM:SS] username: [Kosmi] <username> message
```
## References
- Chrome DevTools Protocol: https://chromedevtools.github.io/devtools-protocol/
- `Page.addScriptToEvaluateOnNewDocument`: https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-addScriptToEvaluateOnNewDocument
- chromedp documentation: https://pkg.go.dev/github.com/chromedp/chromedp
- Original Chrome extension: `.examples/chrome-extension/inject.js`
## Next Steps
With message reception now working, the bridge is ready for:
1. ✅ **Testing message relay**: Kosmi → IRC (receiving works)
2. 🔄 **Testing message sending**: IRC → Kosmi (needs testing)
3. 🔄 **Full integration**: Setting up with real IRC server
4. 🔄 **Production deployment**: Running as a service
## Conclusion
The fix was a single-line change to use the correct Chrome DevTools Protocol method, but it required deep understanding of:
- Browser execution order
- WebSocket lifecycle
- Chrome DevTools Protocol capabilities
- The difference between post-load and pre-load script injection
This lesson learned is now documented in `LESSONS_LEARNED.md` for future reference and can be applied to any project requiring browser API interception in headless automation.
---
**Session Duration**: ~30 minutes
**Messages Exchanged**: 1 user message requesting the fix be applied to the full relay
**Outcome**: ✅ Complete success - messages now flow correctly through the bridge