package main import ( "bufio" "flag" "fmt" "log" "os" "os/signal" "strings" "syscall" "time" ) var ( token = flag.String("token", "", "JWT token for authentication (get from browser DevTools)") room = flag.String("room", "", "Room ID to join (e.g., @hyperspaceout)") email = flag.String("email", "", "Email for login (requires --password, not yet implemented)") password = flag.String("password", "", "Password for login (requires --email, not yet implemented)") ) func main() { flag.Parse() // Validate required flags if *room == "" { log.Fatal("Error: --room is required") } // Get authentication token authToken, err := GetToken(*token, *email, *password) if err != nil { log.Fatalf("Authentication error: %v", err) } // Create client client := NewKosmiClient(authToken, *room) // Set up message handler to print incoming messages client.SetMessageHandler(func(msg Message) { timestamp := formatTime(msg.Time) fmt.Printf("\r[%s] %s: %s\n> ", timestamp, msg.DisplayName, msg.Body) }) // Connect to Kosmi if err := client.Connect(); err != nil { log.Fatalf("Connection error: %v", err) } // Set up graceful shutdown sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) // Start goroutine to handle shutdown signal shutdownChan := make(chan struct{}) go func() { <-sigChan fmt.Println("\n\nReceived interrupt signal, shutting down gracefully...") close(shutdownChan) }() // Print usage instructions fmt.Println("\n" + strings.Repeat("=", 60)) fmt.Println("Connected! Type messages and press Enter to send.") fmt.Println("Press Ctrl+C to exit.") fmt.Println(strings.Repeat("=", 60) + "\n") // Start interactive CLI loop go func() { scanner := bufio.NewScanner(os.Stdin) fmt.Print("> ") for scanner.Scan() { select { case <-shutdownChan: return default: } text := strings.TrimSpace(scanner.Text()) // Skip empty messages if text == "" { fmt.Print("> ") continue } // Handle special commands if text == "/quit" || text == "/exit" { fmt.Println("Exiting...") close(shutdownChan) return } if text == "/help" { printHelp() fmt.Print("> ") continue } // Send the message if err := client.SendMessage(text); err != nil { fmt.Printf("\rError sending message: %v\n> ", err) } else { // Clear the line and reprint prompt fmt.Print("> ") } } if err := scanner.Err(); err != nil { log.Printf("Scanner error: %v", err) } }() // Wait for shutdown signal <-shutdownChan // Close the client gracefully if err := client.Close(); err != nil { log.Printf("Error closing client: %v", err) } // Give a moment for cleanup time.Sleep(200 * time.Millisecond) fmt.Println("Goodbye!") } // formatTime converts ISO timestamp to readable format func formatTime(isoTime string) string { t, err := time.Parse(time.RFC3339, isoTime) if err != nil { return isoTime } return t.Format("15:04:05") } // printHelp displays available commands func printHelp() { fmt.Println("\nAvailable commands:") fmt.Println(" /help - Show this help message") fmt.Println(" /quit - Exit the client") fmt.Println(" /exit - Exit the client") fmt.Println("\nJust type any text and press Enter to send a message.") }