214 lines
5.9 KiB
Go
214 lines
5.9 KiB
Go
|
|
package notification
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// DiscordService handles Discord notifications
|
|
type DiscordService struct{}
|
|
|
|
// NewDiscordService creates a new Discord notification service
|
|
func NewDiscordService() *DiscordService {
|
|
return &DiscordService{}
|
|
}
|
|
|
|
// DiscordPayload represents the payload for Discord webhook
|
|
type DiscordPayload struct {
|
|
Content string `json:"content,omitempty"`
|
|
Embeds []DiscordEmbed `json:"embeds,omitempty"`
|
|
}
|
|
|
|
// DiscordEmbed represents a Discord embed
|
|
type DiscordEmbed struct {
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Color int `json:"color,omitempty"`
|
|
Timestamp string `json:"timestamp,omitempty"`
|
|
Fields []DiscordEmbedField `json:"fields,omitempty"`
|
|
Footer *DiscordEmbedFooter `json:"footer,omitempty"`
|
|
}
|
|
|
|
// DiscordEmbedField represents a field in Discord embed
|
|
type DiscordEmbedField struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
Inline bool `json:"inline,omitempty"`
|
|
}
|
|
|
|
// DiscordEmbedFooter represents footer in Discord embed
|
|
type DiscordEmbedFooter struct {
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// SendNotification sends a notification via Discord webhook
|
|
func (ds *DiscordService) SendNotification(config *AlertConfiguration, message string) error {
|
|
if config.DiscordWebhookURL == "" {
|
|
return fmt.Errorf("discord webhook URL is required")
|
|
}
|
|
|
|
// Create rich embed for better formatting
|
|
embed := ds.createDiscordEmbed(message)
|
|
|
|
payload := DiscordPayload{
|
|
Embeds: []DiscordEmbed{embed},
|
|
}
|
|
|
|
jsonData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal Discord payload: %v", err)
|
|
}
|
|
|
|
resp, err := http.Post(config.DiscordWebhookURL, "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return fmt.Errorf("discord webhook request failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("discord webhook error, status: %d", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createDiscordEmbed creates a formatted Discord embed from the message
|
|
func (ds *DiscordService) createDiscordEmbed(message string) DiscordEmbed {
|
|
// Parse the message to extract structured information
|
|
lines := strings.Split(message, "\n")
|
|
|
|
if len(lines) == 0 {
|
|
return DiscordEmbed{
|
|
Description: message,
|
|
Color: ds.getDefaultColor(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
}
|
|
}
|
|
|
|
// Extract title from first line and determine status
|
|
title := strings.TrimSpace(lines[0])
|
|
color := ds.determineColorFromMessage(title)
|
|
|
|
// Create fields from the remaining lines
|
|
var fields []DiscordEmbedField
|
|
|
|
for i, line := range lines {
|
|
if i == 0 {
|
|
continue // Skip title line
|
|
}
|
|
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
// Parse field from "• Key: Value" format
|
|
if strings.HasPrefix(line, "•") || strings.HasPrefix(line, "-") {
|
|
line = strings.TrimPrefix(line, "•")
|
|
line = strings.TrimPrefix(line, "-")
|
|
line = strings.TrimSpace(line)
|
|
|
|
parts := strings.SplitN(line, ":", 2)
|
|
if len(parts) == 2 {
|
|
fieldName := strings.TrimSpace(parts[0])
|
|
fieldValue := strings.TrimSpace(parts[1])
|
|
|
|
if fieldName != "" && fieldValue != "" {
|
|
fields = append(fields, DiscordEmbedField{
|
|
Name: fieldName,
|
|
Value: fieldValue,
|
|
Inline: true,
|
|
})
|
|
}
|
|
} else {
|
|
// If not in key:value format, add as description field
|
|
fields = append(fields, DiscordEmbedField{
|
|
Name: "Details",
|
|
Value: line,
|
|
Inline: false,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no structured fields found, use the entire message as description
|
|
if len(fields) == 0 {
|
|
// Remove the first line from description since it's used as title
|
|
description := ""
|
|
if len(lines) > 1 {
|
|
description = strings.Join(lines[1:], "\n")
|
|
}
|
|
|
|
return DiscordEmbed{
|
|
Title: title,
|
|
Description: description,
|
|
Color: color,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
Footer: &DiscordEmbedFooter{
|
|
Text: "CheckCle System Alert",
|
|
},
|
|
}
|
|
}
|
|
|
|
return DiscordEmbed{
|
|
Title: title,
|
|
Color: color,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
Fields: fields,
|
|
Footer: &DiscordEmbedFooter{
|
|
Text: "CheckCle System Alert",
|
|
},
|
|
}
|
|
}
|
|
|
|
// determineColorFromMessage determines Discord embed color based on message content
|
|
func (ds *DiscordService) determineColorFromMessage(message string) int {
|
|
messageLower := strings.ToLower(message)
|
|
|
|
// Red for critical/error states
|
|
if strings.Contains(messageLower, "expired") ||
|
|
strings.Contains(messageLower, "down") ||
|
|
strings.Contains(messageLower, "failed") ||
|
|
strings.Contains(messageLower, "error") ||
|
|
strings.Contains(messageLower, "critical") ||
|
|
strings.Contains(messageLower, "🚨") ||
|
|
strings.Contains(messageLower, "🔴") {
|
|
return 15158332 // Red (#E74C3C)
|
|
}
|
|
|
|
// Orange for warnings
|
|
if strings.Contains(messageLower, "expiring_soon") ||
|
|
strings.Contains(messageLower, "expiring soon") ||
|
|
strings.Contains(messageLower, "warning") ||
|
|
strings.Contains(messageLower, "maintenance") ||
|
|
strings.Contains(messageLower, "paused") ||
|
|
strings.Contains(messageLower, "⚠️") ||
|
|
strings.Contains(messageLower, "🟡") ||
|
|
strings.Contains(messageLower, "🟠") {
|
|
return 15105570 // Orange (#E67E22)
|
|
}
|
|
|
|
// Green for success/up states
|
|
if strings.Contains(messageLower, "up") ||
|
|
strings.Contains(messageLower, "resolved") ||
|
|
strings.Contains(messageLower, "success") ||
|
|
strings.Contains(messageLower, "restored") ||
|
|
strings.Contains(messageLower, "valid") ||
|
|
strings.Contains(messageLower, "🟢") ||
|
|
strings.Contains(messageLower, "✅") {
|
|
return 3066993 // Green (#2ECC71)
|
|
}
|
|
|
|
// Blue for info/default
|
|
return 3447003 // Blue (#3498DB)
|
|
}
|
|
|
|
// getDefaultColor returns the default Discord embed color (blue)
|
|
func (ds *DiscordService) getDefaultColor() int {
|
|
return 3447003 // Blue (#3498DB)
|
|
} |