mirror of
https://github.com/crocofied/CoreControl.git
synced 2025-12-17 15:36:50 +00:00
267 lines
7.1 KiB
Go
267 lines
7.1 KiB
Go
package notifications
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/corecontrol/agent/internal/models"
|
|
|
|
"gopkg.in/gomail.v2"
|
|
)
|
|
|
|
type NotificationSender struct {
|
|
notifications []models.Notification
|
|
notifMutex sync.RWMutex
|
|
}
|
|
|
|
// NewNotificationSender creates a new notification sender
|
|
func NewNotificationSender() *NotificationSender {
|
|
return &NotificationSender{
|
|
notifications: []models.Notification{},
|
|
notifMutex: sync.RWMutex{},
|
|
}
|
|
}
|
|
|
|
// UpdateNotifications updates the stored notifications
|
|
func (ns *NotificationSender) UpdateNotifications(notifs []models.Notification) {
|
|
ns.notifMutex.Lock()
|
|
defer ns.notifMutex.Unlock()
|
|
|
|
copyDst := make([]models.Notification, len(notifs))
|
|
copy(copyDst, notifs)
|
|
ns.notifications = copyDst
|
|
}
|
|
|
|
// GetNotifications returns a safe copy of current notifications
|
|
func (ns *NotificationSender) GetNotifications() []models.Notification {
|
|
ns.notifMutex.RLock()
|
|
defer ns.notifMutex.RUnlock()
|
|
|
|
copyDst := make([]models.Notification, len(ns.notifications))
|
|
copy(copyDst, ns.notifications)
|
|
return copyDst
|
|
}
|
|
|
|
// SendNotifications sends a message to all configured notifications
|
|
func (ns *NotificationSender) SendNotifications(message string) {
|
|
notifs := ns.GetNotifications()
|
|
|
|
for _, n := range notifs {
|
|
ns.SendSpecificNotification(n, message)
|
|
}
|
|
}
|
|
|
|
// SendSpecificNotification sends a message to a specific notification
|
|
func (ns *NotificationSender) SendSpecificNotification(n models.Notification, message string) {
|
|
fmt.Println("Sending specific notification..." + n.Type)
|
|
switch n.Type {
|
|
case "smtp":
|
|
if n.SMTPHost.Valid && n.SMTPTo.Valid {
|
|
ns.sendEmail(n, message)
|
|
}
|
|
case "telegram":
|
|
if n.TelegramToken.Valid && n.TelegramChatID.Valid {
|
|
ns.sendTelegram(n, message)
|
|
}
|
|
case "discord":
|
|
if n.DiscordWebhook.Valid {
|
|
ns.sendDiscord(n, message)
|
|
}
|
|
case "gotify":
|
|
if n.GotifyUrl.Valid && n.GotifyToken.Valid {
|
|
ns.sendGotify(n, message)
|
|
}
|
|
case "ntfy":
|
|
if n.NtfyUrl.Valid && n.NtfyToken.Valid {
|
|
ns.sendNtfy(n, message)
|
|
}
|
|
case "pushover":
|
|
if n.PushoverUrl.Valid && n.PushoverToken.Valid && n.PushoverUser.Valid {
|
|
ns.sendPushover(n, message)
|
|
}
|
|
case "echobell":
|
|
if n.EchobellURL.Valid {
|
|
ns.sendEchobell(n, message)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to check if a host is an IP address
|
|
func (ns *NotificationSender) isIPAddress(host string) bool {
|
|
ip := net.ParseIP(host)
|
|
return ip != nil
|
|
}
|
|
|
|
// Individual notification methods
|
|
func (ns *NotificationSender) sendEmail(n models.Notification, body string) {
|
|
// Initialize SMTP dialer with host, port, user, pass
|
|
d := gomail.NewDialer(
|
|
n.SMTPHost.String,
|
|
int(n.SMTPPort.Int64),
|
|
n.SMTPUser.String,
|
|
n.SMTPPass.String,
|
|
)
|
|
if n.SMTPSecure.Valid && n.SMTPSecure.Bool {
|
|
d.SSL = true
|
|
}
|
|
|
|
m := gomail.NewMessage()
|
|
m.SetHeader("From", n.SMTPFrom.String)
|
|
m.SetHeader("To", n.SMTPTo.String)
|
|
m.SetHeader("Subject", "Uptime Notification")
|
|
m.SetBody("text/plain", body)
|
|
|
|
if err := d.DialAndSend(m); err != nil {
|
|
fmt.Printf("Email send failed: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func (ns *NotificationSender) sendTelegram(n models.Notification, message string) {
|
|
apiUrl := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s",
|
|
n.TelegramToken.String,
|
|
n.TelegramChatID.String,
|
|
message,
|
|
)
|
|
resp, err := http.Get(apiUrl)
|
|
if err != nil {
|
|
fmt.Printf("Telegram send failed: %v\n", err)
|
|
return
|
|
}
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (ns *NotificationSender) sendDiscord(n models.Notification, message string) {
|
|
payload := fmt.Sprintf(`{"content": "%s"}`, message)
|
|
req, err := http.NewRequest("POST", n.DiscordWebhook.String, strings.NewReader(payload))
|
|
if err != nil {
|
|
fmt.Printf("Discord request creation failed: %v\n", err)
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("Discord send failed: %v\n", err)
|
|
return
|
|
}
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (ns *NotificationSender) sendGotify(n models.Notification, message string) {
|
|
baseURL := strings.TrimSuffix(n.GotifyUrl.String, "/")
|
|
targetURL := fmt.Sprintf("%s/message", baseURL)
|
|
|
|
form := url.Values{}
|
|
form.Add("message", message)
|
|
form.Add("priority", "5")
|
|
|
|
req, err := http.NewRequest("POST", targetURL, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
fmt.Printf("Gotify: ERROR creating request: %v\n", err)
|
|
return
|
|
}
|
|
|
|
req.Header.Set("X-Gotify-Key", n.GotifyToken.String)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("Gotify: ERROR sending request: %v\n", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Gotify: ERROR status code: %d\n", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func (ns *NotificationSender) sendNtfy(n models.Notification, message string) {
|
|
fmt.Println("Sending Ntfy notification...")
|
|
baseURL := strings.TrimSuffix(n.NtfyUrl.String, "/")
|
|
|
|
// Don't append a topic to the URL - the URL itself should have the correct endpoint
|
|
requestURL := baseURL
|
|
|
|
// Send message directly as request body instead of JSON
|
|
req, err := http.NewRequest("POST", requestURL, strings.NewReader(message))
|
|
if err != nil {
|
|
fmt.Printf("Ntfy: ERROR creating request: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if n.NtfyToken.Valid {
|
|
req.Header.Set("Authorization", "Bearer "+n.NtfyToken.String)
|
|
}
|
|
// Use text/plain instead of application/json
|
|
req.Header.Set("Content-Type", "text/plain")
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("Ntfy: ERROR sending request: %v\n", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Ntfy: ERROR status code: %d\n", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func (ns *NotificationSender) sendPushover(n models.Notification, message string) {
|
|
form := url.Values{}
|
|
form.Add("token", n.PushoverToken.String)
|
|
form.Add("user", n.PushoverUser.String)
|
|
form.Add("message", message)
|
|
|
|
req, err := http.NewRequest("POST", n.PushoverUrl.String, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
fmt.Printf("Pushover: ERROR creating request: %v\n", err)
|
|
return
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("Pushover: ERROR sending request: %v\n", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Pushover: ERROR status code: %d\n", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func (ns *NotificationSender) sendEchobell(n models.Notification, message string) {
|
|
jsonData := fmt.Sprintf(`{"message": "%s"}`, message)
|
|
req, err := http.NewRequest("POST", n.EchobellURL.String, strings.NewReader(jsonData))
|
|
if err != nil {
|
|
fmt.Printf("Echobell: ERROR creating request: %v\n", err)
|
|
return
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("Echobell: ERROR sending request: %v\n", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Echobell: ERROR status code: %d\n", resp.StatusCode)
|
|
}
|
|
}
|