From fd15ba3bb450f922751603869ed7d43c7ab614c8 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 18 Jun 2024 21:16:00 +0000 Subject: [PATCH] Update main.go --- main.go | 217 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 150 insertions(+), 67 deletions(-) diff --git a/main.go b/main.go index 27f9dae..0f16b19 100644 --- a/main.go +++ b/main.go @@ -2,93 +2,176 @@ package main import ( "bufio" + "bytes" "fmt" "io" + "log" + "mime/multipart" + "net" + "net/http" "os" + "os/exec" + "os/user" + "path/filepath" "strings" "time" - - "github.com/getsentry/sentry-go" ) +// 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() { - var reader io.Reader - var filePath string - - // Determine the source of input - if len(os.Args) == 2 { - // Read from the file specified in the argument - filePath = os.Args[1] - file, err := os.Open(filePath) + // 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 { - fmt.Fprintf(os.Stderr, "Error opening file: %s\n", err) - os.Exit(1) + log.Fatalf("Failed to authenticate asciinema CLI: %v", err) } - defer file.Close() - reader = file - } else if len(os.Args) == 1 && !isInputFromPipe() { - fmt.Fprintf(os.Stderr, "Usage: %s OR pipe input\n", os.Args[0]) - os.Exit(1) - } else { - // Read from stdin if data is being piped - reader = os.Stdin } - // Read content from the chosen input - scanner := bufio.NewScanner(reader) - var content strings.Builder - for scanner.Scan() { - content.WriteString(scanner.Text()) - content.WriteString("\n") // Preserve line breaks - } + // Generate a unique filename for the asciinema recording + timestamp := time.Now().Format("20060102150405") + filename := fmt.Sprintf("asciinema-%s.cast", timestamp) - if err := scanner.Err(); err != nil { - fmt.Fprintf(os.Stderr, "Error reading input: %s\n", err) - os.Exit(1) - } + // 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 - logMessage := content.String() - sendToSentry(logMessage) -} - -// Helper function to check if there is data being piped to stdin -func isInputFromPipe() bool { - fileInfo, err := os.Stdin.Stat() + log.Println("Starting asciinema recording...") + err := cmd.Run() if err != nil { - return false - } - return fileInfo.Mode()&os.ModeCharDevice == 0 -} - -// Function to handle sending data to Glitchtip -func sendToSentry(logMessage string) { - dsn := os.Getenv("GLITCHTIP_DSN") - if dsn == "" { - fmt.Fprintf(os.Stderr, "Error: GLITCHTIP_DSN environment variable is not set.\n") - os.Exit(1) + log.Fatalf("Failed to start asciinema recording: %v", err) } - err := sentry.Init(sentry.ClientOptions{ - Dsn: dsn, - }) + log.Println("Asciinema recording finished:", filename) + + // Collect system details + hostname, _ := os.Hostname() + currentUser, _ := user.Current() + ipAddress, err := getExternalIP() if err != nil { - fmt.Fprintf(os.Stderr, "Error initializing GlitchTip: %s\n", err) - os.Exit(1) + log.Fatalf("Failed to get external IP address: %v", err) } - defer func() { - success := sentry.Flush(5 * time.Second) - if !success { - fmt.Fprintf(os.Stderr, "Failed to flush GlitchTip buffer within the expected time.\n") - } else { - fmt.Println("GlitchTip buffer flushed successfully.") - } - }() + // Create the message with system details + message := fmt.Sprintf("New asciinema recording from host: %s, user: %s, IP: %s", hostname, currentUser.Username, ipAddress) - eventID := sentry.CaptureMessage(logMessage) - if eventID != nil { - fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID) - } else { - fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n") + // 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 }