package notifications import ( "bytes" "encoding/json" "fmt" "log" "net/http" "time" ) const emailHTTPTimeoutSeconds = 10 var emailHTTPClient = &http.Client{ //nolint:gochecknoglobals Timeout: emailHTTPTimeoutSeconds * time.Second, } type resendPayload struct { From string `json:"from"` To string `json:"to"` Subject string `json:"subject"` HTML string `json:"html"` } func SendEmailNotification(apiKey, fromAddress, toAddress, message string, meta AlertMetadata) (bool, error) { if apiKey == "" { log.Printf("Skipping email notification: RESEND_API_KEY not configured") return false, nil } subject := fmt.Sprintf("Koin Ping Alert: %s", alertTypeLabel(meta.AlertType)) txLink := "" if meta.TxHash != "" { txLink = fmt.Sprintf( `
`, meta.TxHash, ) } html := fmt.Sprintf(`%s
| Address | %s |
| Blockchain | %s |
Sent by Koin Ping
Your email alerts are configured correctly!
Sent by Koin Ping
`, } body, err := json.Marshal(payload) if err != nil { return false, err } req, err := http.NewRequest(http.MethodPost, "https://api.resend.com/emails", bytes.NewReader(body)) if err != nil { return false, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) resp, err := emailHTTPClient.Do(req) if err != nil { log.Printf("Email test failed: %v", err) return false, err } defer resp.Body.Close() return resp.StatusCode >= 200 && resp.StatusCode < 300, nil } func alertTypeLabel(alertType string) string { switch alertType { case "incoming_tx": return "Incoming Transaction" case "outgoing_tx": return "Outgoing Transaction" case "large_transfer": return "Large Transfer" case "balance_below": return "Balance Below Threshold" default: return "Alert" } }