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

7.8 KiB

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:

// ❌ 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:

// ✅ 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

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

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

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