227 lines
6.4 KiB
Go
227 lines
6.4 KiB
Go
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"log"
|
|||
|
|
"os"
|
|||
|
|
"os/signal"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/playwright-community/playwright-go"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
roomURL = "https://app.kosmi.io/room/@hyperspaceout"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
log.Println("🔍 Starting Kosmi WebSocket Monitor")
|
|||
|
|
log.Printf("📡 Room URL: %s", roomURL)
|
|||
|
|
log.Println("This will capture ALL WebSocket traffic from the browser...")
|
|||
|
|
log.Println()
|
|||
|
|
|
|||
|
|
// Set up interrupt handler
|
|||
|
|
interrupt := make(chan os.Signal, 1)
|
|||
|
|
signal.Notify(interrupt, os.Interrupt)
|
|||
|
|
|
|||
|
|
// Launch Playwright
|
|||
|
|
pw, err := playwright.Run()
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatalf("Failed to start Playwright: %v", err)
|
|||
|
|
}
|
|||
|
|
defer pw.Stop()
|
|||
|
|
|
|||
|
|
// Launch browser
|
|||
|
|
browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
|
|||
|
|
Headless: playwright.Bool(false), // Keep visible so we can see what's happening
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatalf("Failed to launch browser: %v", err)
|
|||
|
|
}
|
|||
|
|
defer browser.Close()
|
|||
|
|
|
|||
|
|
// Create context
|
|||
|
|
context, err := browser.NewContext(playwright.BrowserNewContextOptions{
|
|||
|
|
UserAgent: playwright.String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"),
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatalf("Failed to create context: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Create page
|
|||
|
|
page, err := context.NewPage()
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatalf("Failed to create page: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Inject WebSocket monitoring script BEFORE navigation
|
|||
|
|
log.Println("📝 Injecting WebSocket monitoring script...")
|
|||
|
|
if err := page.AddInitScript(playwright.Script{
|
|||
|
|
Content: playwright.String(`
|
|||
|
|
(function() {
|
|||
|
|
const OriginalWebSocket = window.WebSocket;
|
|||
|
|
let messageCount = 0;
|
|||
|
|
|
|||
|
|
window.WebSocket = function(url, protocols) {
|
|||
|
|
console.log('🔌 [WS MONITOR] WebSocket created:', url, 'protocols:', protocols);
|
|||
|
|
const socket = new OriginalWebSocket(url, protocols);
|
|||
|
|
|
|||
|
|
socket.addEventListener('open', (event) => {
|
|||
|
|
console.log('✅ [WS MONITOR] WebSocket OPENED');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
socket.addEventListener('close', (event) => {
|
|||
|
|
console.log('🔴 [WS MONITOR] WebSocket CLOSED:', event.code, event.reason);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
socket.addEventListener('error', (event) => {
|
|||
|
|
console.error('❌ [WS MONITOR] WebSocket ERROR:', event);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Intercept outgoing messages
|
|||
|
|
const originalSend = socket.send;
|
|||
|
|
socket.send = function(data) {
|
|||
|
|
messageCount++;
|
|||
|
|
console.log('📤 [WS MONITOR] SEND #' + messageCount + ':', data);
|
|||
|
|
try {
|
|||
|
|
const parsed = JSON.parse(data);
|
|||
|
|
console.log(' Type:', parsed.type, 'ID:', parsed.id);
|
|||
|
|
if (parsed.payload) {
|
|||
|
|
console.log(' Payload:', JSON.stringify(parsed.payload, null, 2));
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
// Not JSON
|
|||
|
|
}
|
|||
|
|
return originalSend.call(this, data);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Intercept incoming messages
|
|||
|
|
socket.addEventListener('message', (event) => {
|
|||
|
|
messageCount++;
|
|||
|
|
console.log('📥 [WS MONITOR] RECEIVE #' + messageCount + ':', event.data);
|
|||
|
|
try {
|
|||
|
|
const parsed = JSON.parse(event.data);
|
|||
|
|
console.log(' Type:', parsed.type, 'ID:', parsed.id);
|
|||
|
|
if (parsed.payload) {
|
|||
|
|
console.log(' Payload:', JSON.stringify(parsed.payload, null, 2));
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
// Not JSON
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return socket;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Preserve WebSocket properties
|
|||
|
|
window.WebSocket.prototype = OriginalWebSocket.prototype;
|
|||
|
|
window.WebSocket.CONNECTING = OriginalWebSocket.CONNECTING;
|
|||
|
|
window.WebSocket.OPEN = OriginalWebSocket.OPEN;
|
|||
|
|
window.WebSocket.CLOSING = OriginalWebSocket.CLOSING;
|
|||
|
|
window.WebSocket.CLOSED = OriginalWebSocket.CLOSED;
|
|||
|
|
})();
|
|||
|
|
`),
|
|||
|
|
}); err != nil {
|
|||
|
|
log.Fatalf("Failed to inject script: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Listen to console messages
|
|||
|
|
page.On("console", func(msg playwright.ConsoleMessage) {
|
|||
|
|
text := msg.Text()
|
|||
|
|
msgType := msg.Type()
|
|||
|
|
|
|||
|
|
// Format the output nicely
|
|||
|
|
prefix := "💬"
|
|||
|
|
switch msgType {
|
|||
|
|
case "log":
|
|||
|
|
prefix = "📋"
|
|||
|
|
case "error":
|
|||
|
|
prefix = "❌"
|
|||
|
|
case "warning":
|
|||
|
|
prefix = "⚠️"
|
|||
|
|
case "info":
|
|||
|
|
prefix = "ℹ️"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log.Printf("%s [BROWSER %s] %s", prefix, msgType, text)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// Navigate to room
|
|||
|
|
log.Printf("🌐 Navigating to %s...", roomURL)
|
|||
|
|
if _, err := page.Goto(roomURL, playwright.PageGotoOptions{
|
|||
|
|
WaitUntil: playwright.WaitUntilStateDomcontentloaded,
|
|||
|
|
}); err != nil {
|
|||
|
|
log.Fatalf("Failed to navigate: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log.Println("✅ Page loaded! Monitoring WebSocket traffic...")
|
|||
|
|
log.Println("Press Ctrl+C to stop monitoring")
|
|||
|
|
log.Println()
|
|||
|
|
|
|||
|
|
// Wait for a bit to see initial traffic
|
|||
|
|
time.Sleep(5 * time.Second)
|
|||
|
|
|
|||
|
|
// Try to send a test message
|
|||
|
|
log.Println("\n📝 Attempting to send a test message via UI...")
|
|||
|
|
result, err := page.Evaluate(`
|
|||
|
|
(async function() {
|
|||
|
|
// Find the chat input
|
|||
|
|
const textareas = document.querySelectorAll('textarea');
|
|||
|
|
for (let ta of textareas) {
|
|||
|
|
if (ta.offsetParent !== null) {
|
|||
|
|
ta.value = 'Test message from monitor script 🔍';
|
|||
|
|
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
|||
|
|
ta.dispatchEvent(new Event('change', { bubbles: true }));
|
|||
|
|
ta.focus();
|
|||
|
|
|
|||
|
|
// Wait a bit
|
|||
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|||
|
|
|
|||
|
|
// Try to find and click send button
|
|||
|
|
const buttons = document.querySelectorAll('button');
|
|||
|
|
for (let btn of buttons) {
|
|||
|
|
if (btn.textContent.toLowerCase().includes('send') ||
|
|||
|
|
btn.getAttribute('aria-label')?.toLowerCase().includes('send')) {
|
|||
|
|
btn.click();
|
|||
|
|
return { success: true, method: 'button' };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If no button, press Enter
|
|||
|
|
const enterEvent = new KeyboardEvent('keydown', {
|
|||
|
|
key: 'Enter',
|
|||
|
|
code: 'Enter',
|
|||
|
|
keyCode: 13,
|
|||
|
|
which: 13,
|
|||
|
|
bubbles: true,
|
|||
|
|
cancelable: true
|
|||
|
|
});
|
|||
|
|
ta.dispatchEvent(enterEvent);
|
|||
|
|
return { success: true, method: 'enter' };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return { success: false, error: 'No input found' };
|
|||
|
|
})();
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
if err != nil {
|
|||
|
|
log.Printf("❌ Failed to send test message: %v", err)
|
|||
|
|
} else {
|
|||
|
|
resultMap := result.(map[string]interface{})
|
|||
|
|
if success, ok := resultMap["success"].(bool); ok && success {
|
|||
|
|
method := resultMap["method"].(string)
|
|||
|
|
log.Printf("✅ Test message sent via %s", method)
|
|||
|
|
log.Println("📊 Check the console output above to see the WebSocket traffic!")
|
|||
|
|
} else {
|
|||
|
|
log.Printf("❌ Failed to send: %v", resultMap["error"])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Keep monitoring
|
|||
|
|
log.Println("\n⏳ Continuing to monitor... Press Ctrl+C to stop")
|
|||
|
|
|
|||
|
|
// Wait for interrupt
|
|||
|
|
<-interrupt
|
|||
|
|
log.Println("\n👋 Stopping monitor...")
|
|||
|
|
}
|
|||
|
|
|