package main import ( "context" "encoding/base64" "flag" "fmt" "net/http" "os" "time" "github.com/gorilla/websocket" ) const ( userAgent = "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" appVersion = "4364" wsURL = "wss://engine.kosmi.io/gql-ws" ) func main() { token := flag.String("token", "", "JWT token from captured session") roomID := flag.String("room", "@hyperspaceout", "Room ID") flag.Parse() if *token == "" { fmt.Fprintf(os.Stderr, "Error: -token is required\n") fmt.Fprintf(os.Stderr, "Usage: %s -token -room \n", os.Args[0]) fmt.Fprintf(os.Stderr, "\nTo get a token, run: ./capture-auth and extract it from auth-data.json\n") os.Exit(1) } fmt.Println("šŸ”Œ Testing direct WebSocket connection with JWT token") fmt.Printf(" Token: %s...\n", truncate(*token, 50)) fmt.Printf(" Room: %s\n\n", *roomID) // Connect WebSocket fmt.Println("1ļøāƒ£ Connecting to WebSocket...") conn, err := connectWebSocket(*token) if err != nil { fmt.Fprintf(os.Stderr, "āŒ Failed to connect: %v\n", err) os.Exit(1) } defer conn.Close() fmt.Println("āœ… WebSocket connected!") // Wait for connection_ack fmt.Println("\n2ļøāƒ£ Waiting for connection_ack...") if err := waitForAck(conn); err != nil { fmt.Fprintf(os.Stderr, "āŒ Failed to receive ack: %v\n", err) os.Exit(1) } fmt.Println("āœ… Received connection_ack!") // Subscribe to messages fmt.Printf("\n3ļøāƒ£ Subscribing to messages in room %s...\n", *roomID) if err := subscribeToMessages(conn, *roomID); err != nil { fmt.Fprintf(os.Stderr, "āŒ Failed to subscribe: %v\n", err) os.Exit(1) } fmt.Println("āœ… Subscribed!") // Listen for messages fmt.Println("\nšŸ‘‚ Listening for messages (press Ctrl+C to exit)...\n") messageCount := 0 for { var msg map[string]interface{} if err := conn.ReadJSON(&msg); err != nil { fmt.Fprintf(os.Stderr, "Error reading message: %v\n", err) break } msgType, _ := msg["type"].(string) switch msgType { case "next": payload, _ := msg["payload"].(map[string]interface{}) data, _ := payload["data"].(map[string]interface{}) if newMessage, ok := data["newMessage"].(map[string]interface{}); ok { messageCount++ body, _ := newMessage["body"].(string) user, _ := newMessage["user"].(map[string]interface{}) username, _ := user["displayName"].(string) if username == "" { username, _ = user["username"].(string) } timestamp, _ := newMessage["time"].(float64) t := time.Unix(int64(timestamp), 0) fmt.Printf("[%s] %s: %s\n", t.Format("15:04:05"), username, body) } case "complete": id, _ := msg["id"].(string) fmt.Printf(" [Subscription %s completed]\n", id) case "error": fmt.Printf(" āš ļø Error: %+v\n", msg) default: // Ignore other message types (ka, etc) } } fmt.Printf("\nšŸ“Š Total messages received: %d\n", messageCount) } func connectWebSocket(token string) (*websocket.Conn, error) { dialer := websocket.Dialer{ Subprotocols: []string{"graphql-ws"}, ReadBufferSize: 1024, WriteBufferSize: 1024, } headers := http.Header{ "Origin": []string{"https://app.kosmi.io"}, "User-Agent": []string{userAgent}, "Sec-WebSocket-Protocol": []string{"graphql-ws"}, "Cache-Control": []string{"no-cache"}, "Pragma": []string{"no-cache"}, } conn, resp, err := dialer.Dial(wsURL, headers) if err != nil { if resp != nil { fmt.Printf(" Response status: %d\n", resp.StatusCode) } return nil, err } // Send connection_init with token uaEncoded := base64.StdEncoding.EncodeToString([]byte(userAgent)) initMsg := map[string]interface{}{ "type": "connection_init", "payload": map[string]interface{}{ "token": token, "ua": uaEncoded, "v": appVersion, "r": "", }, } if err := conn.WriteJSON(initMsg); err != nil { conn.Close() return nil, fmt.Errorf("failed to send connection_init: %w", err) } return conn, nil } func waitForAck(conn *websocket.Conn) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() done := make(chan error, 1) go func() { for { var msg map[string]interface{} if err := conn.ReadJSON(&msg); err != nil { done <- err return } msgType, _ := msg["type"].(string) fmt.Printf(" Received: %s\n", msgType) if msgType == "connection_ack" { done <- nil return } // Keep reading other messages (like ka) } }() select { case err := <-done: return err case <-ctx.Done(): return fmt.Errorf("timeout waiting for connection_ack") } } func subscribeToMessages(conn *websocket.Conn, roomID string) error { query := fmt.Sprintf(` subscription { newMessage(roomId: "%s") { body time user { displayName username } } } `, roomID) subMsg := map[string]interface{}{ "id": "newMessage-subscription", "type": "subscribe", "payload": map[string]interface{}{ "query": query, "variables": map[string]interface{}{}, }, } return conn.WriteJSON(subMsg) } func truncate(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen] + "..." }