Update main.go
This commit is contained in:
		
							parent
							
								
									f08b220ad5
								
							
						
					
					
						commit
						fd15ba3bb4
					
				
							
								
								
									
										217
									
								
								main.go
								
								
								
								
							
							
						
						
									
										217
									
								
								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 <path to log file> 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 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue