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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"mime/multipart"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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() {
|
func main() {
|
||||||
var reader io.Reader
|
// Check if asciinema CLI is authenticated
|
||||||
var filePath string
|
if !isAuthenticated() {
|
||||||
|
log.Println("Asciinema CLI is not authenticated. Please run 'asciinema auth' to authenticate.")
|
||||||
// Determine the source of input
|
err := runAsciinemaAuth()
|
||||||
if len(os.Args) == 2 {
|
|
||||||
// Read from the file specified in the argument
|
|
||||||
filePath = os.Args[1]
|
|
||||||
file, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error opening file: %s\n", err)
|
log.Fatalf("Failed to authenticate asciinema CLI: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
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
|
// Generate a unique filename for the asciinema recording
|
||||||
scanner := bufio.NewScanner(reader)
|
timestamp := time.Now().Format("20060102150405")
|
||||||
var content strings.Builder
|
filename := fmt.Sprintf("asciinema-%s.cast", timestamp)
|
||||||
for scanner.Scan() {
|
|
||||||
content.WriteString(scanner.Text())
|
|
||||||
content.WriteString("\n") // Preserve line breaks
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
// Start asciinema recording
|
||||||
fmt.Fprintf(os.Stderr, "Error reading input: %s\n", err)
|
cmd := exec.Command("asciinema", "rec", "--title", "Debug Report", "--command", "bash", filename)
|
||||||
os.Exit(1)
|
cmd.Env = append(os.Environ(), "ASCIINEMA_API_URL="+asciinemaServerURL)
|
||||||
}
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
logMessage := content.String()
|
log.Println("Starting asciinema recording...")
|
||||||
sendToSentry(logMessage)
|
err := cmd.Run()
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to check if there is data being piped to stdin
|
|
||||||
func isInputFromPipe() bool {
|
|
||||||
fileInfo, err := os.Stdin.Stat()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
log.Fatalf("Failed to start asciinema recording: %v", err)
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
log.Println("Asciinema recording finished:", filename)
|
||||||
Dsn: dsn,
|
|
||||||
})
|
// Collect system details
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
currentUser, _ := user.Current()
|
||||||
|
ipAddress, err := getExternalIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error initializing GlitchTip: %s\n", err)
|
log.Fatalf("Failed to get external IP address: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
// Create the message with system details
|
||||||
success := sentry.Flush(5 * time.Second)
|
message := fmt.Sprintf("New asciinema recording from host: %s, user: %s, IP: %s", hostname, currentUser.Username, ipAddress)
|
||||||
if !success {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to flush GlitchTip buffer within the expected time.\n")
|
|
||||||
} else {
|
|
||||||
fmt.Println("GlitchTip buffer flushed successfully.")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
eventID := sentry.CaptureMessage(logMessage)
|
// Ask the user if this is a priority notification
|
||||||
if eventID != nil {
|
isPriority := askPriority()
|
||||||
fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID)
|
|
||||||
} else {
|
// Send the recording to Pushover
|
||||||
fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n")
|
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