package main import ( "bufio" "bytes" "fmt" "io" "log" "mime/multipart" "net" "net/http" "os" "os/exec" "os/user" "path/filepath" "strings" "time" ) // Hardcoded Pushover credentials const pushoverURL = "https://api.pushover.net/1/messages.json" const pushoverToken = "aunhi15sq2ervgjzyxm1msnnmucv41" const pushoverUserKey = "ujFeJyjYFJd2Lwbygfw9cSCoeYiLBi" // Asciinema server URL const asciinemaServerURL = "https://asciinema.nixc.us/" // Path to the asciinema installation ID file const asciinemaInstallIDFile = "$HOME/.config/asciinema/install-id" func main() { // Check if asciinema CLI is authenticated if !isAuthenticated() { log.Println("Asciinema CLI is not authenticated. Please run 'asciinema auth' to authenticate.") err := runAsciinemaAuth() if err != nil { log.Fatalf("Failed to authenticate asciinema CLI: %v", err) } } // Generate a unique filename for the asciinema recording timestamp := time.Now().Format("20060102150405") filename := fmt.Sprintf("asciinema-%s.cast", timestamp) // Start asciinema recording cmd := exec.Command("asciinema", "rec", "--title", "Debug Report", "--command", "bash", filename) cmd.Env = append(os.Environ(), "ASCIINEMA_API_URL="+asciinemaServerURL) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr log.Println("Starting asciinema recording...") err := cmd.Run() if err != nil { log.Fatalf("Failed to start asciinema recording: %v", err) } log.Println("Asciinema recording finished:", filename) // Collect system details hostname, _ := os.Hostname() currentUser, _ := user.Current() ipAddress, err := getExternalIP() if err != nil { log.Fatalf("Failed to get external IP address: %v", err) } // Create the message with system details message := fmt.Sprintf("New asciinema recording from host: %s, user: %s, IP: %s", hostname, currentUser.Username, ipAddress) // Ask the user if this is a priority notification isPriority := askPriority() // Send the recording to Pushover err = sendToPushover(filename, message, isPriority) if err != nil { log.Fatalf("Failed to send recording to Pushover: %v", err) } log.Println("Recording sent to Pushover successfully.") } // isAuthenticated checks if the asciinema CLI is authenticated func isAuthenticated() bool { installIDPath := os.ExpandEnv(asciinemaInstallIDFile) _, err := os.Stat(installIDPath) return !os.IsNotExist(err) } // runAsciinemaAuth runs the asciinema auth command to authenticate the CLI func runAsciinemaAuth() error { cmd := exec.Command("asciinema", "auth") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } // sendToPushover sends the recording file and message to the Pushover URL func sendToPushover(filename, message string, priority bool) error { file, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open recording file: %w", err) } defer file.Close() body := &bytes.Buffer{} writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("file", filepath.Base(file.Name())) if err != nil { return fmt.Errorf("failed to create form file: %w", err) } _, err = io.Copy(part, file) if err != nil { return fmt.Errorf("failed to copy file content: %w", err) } // Add Pushover parameters writer.WriteField("token", pushoverToken) writer.WriteField("user", pushoverUserKey) writer.WriteField("message", message) if priority { writer.WriteField("priority", "1") } err = writer.Close() if err != nil { return fmt.Errorf("failed to close writer: %w", err) } req, err := http.NewRequest("POST", pushoverURL, body) if err != nil { return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("received non-200 response: %d", resp.StatusCode) } return nil } // askPriority prompts the user to specify if the notification is a priority func askPriority() bool { reader := bufio.NewReader(os.Stdin) fmt.Print("Is this a priority notification? (yes/no): ") response, err := reader.ReadString('\n') if err != nil { log.Fatalf("Failed to read user input: %v", err) } response = strings.TrimSpace(strings.ToLower(response)) return response == "yes" } // getExternalIP fetches the current external IP address func getExternalIP() (string, error) { resp, err := http.Get("https://api.ipify.org") if err != nil { return "", fmt.Errorf("failed to get external IP: %w", err) } defer resp.Body.Close() ip, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read IP response: %w", err) } return strings.TrimSpace(string(ip)), nil }