219 lines
7.8 KiB
Markdown
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
|
||
|
|
|