- Introduced NOTIFICATION_DRY_RUN configuration option in .env.example and k8s/secret.example.yaml. - Updated README.md to include usage instructions for the new dry run feature. - Implemented logic in app.go to preview notifications without sending them when the dry run option is enabled. - Enhanced config.go to load the new configuration option and validate notification types accordingly. - Added a new function in notify.go to generate manual-needed notification messages for preview.
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package notify
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/mail"
|
|
"net/smtp"
|
|
"strings"
|
|
"time"
|
|
|
|
"ncore-hnr/internal/model"
|
|
)
|
|
|
|
const manualNeededSubject = "nCore HnR manual work"
|
|
|
|
type Sender interface {
|
|
SendManualNeeded(results []model.ActionResult) error
|
|
}
|
|
|
|
type NotificationNTFY struct {
|
|
URL string
|
|
HTTPClient *http.Client
|
|
}
|
|
|
|
func (n NotificationNTFY) SendManualNeeded(results []model.ActionResult) error {
|
|
body, ok := manualNeededText(results)
|
|
if strings.TrimSpace(n.URL) == "" || !ok {
|
|
return nil
|
|
}
|
|
client := n.HTTPClient
|
|
if client == nil {
|
|
client = &http.Client{Timeout: 15 * time.Second}
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPost, n.URL, bytes.NewBufferString(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "text/plain; charset=utf-8")
|
|
req.Header.Set("Title", manualNeededSubject)
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("notify returned %s", resp.Status)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type NotificationSMTP struct {
|
|
Host string
|
|
Port string
|
|
Username string
|
|
Password string
|
|
From string
|
|
To string
|
|
}
|
|
|
|
func (s NotificationSMTP) SendManualNeeded(results []model.ActionResult) error {
|
|
body, ok := manualNeededText(results)
|
|
if strings.TrimSpace(s.Host) == "" || !ok {
|
|
return nil
|
|
}
|
|
|
|
host := strings.TrimSpace(s.Host)
|
|
addr := net.JoinHostPort(host, strings.TrimSpace(s.Port))
|
|
|
|
from, err := mail.ParseAddress(strings.TrimSpace(s.From))
|
|
if err != nil {
|
|
return fmt.Errorf("parse smtp from: %w", err)
|
|
}
|
|
recipients, err := mail.ParseAddressList(strings.TrimSpace(s.To))
|
|
if err != nil {
|
|
return fmt.Errorf("parse smtp to: %w", err)
|
|
}
|
|
|
|
to := make([]string, 0, len(recipients))
|
|
for _, recipient := range recipients {
|
|
to = append(to, recipient.Address)
|
|
}
|
|
|
|
var auth smtp.Auth
|
|
if strings.TrimSpace(s.Username) != "" || strings.TrimSpace(s.Password) != "" {
|
|
auth = smtp.PlainAuth("", strings.TrimSpace(s.Username), strings.TrimSpace(s.Password), host)
|
|
}
|
|
|
|
message := strings.Builder{}
|
|
message.WriteString(fmt.Sprintf("From: %s\r\n", from.String()))
|
|
message.WriteString(fmt.Sprintf("To: %s\r\n", formatAddressList(recipients)))
|
|
message.WriteString(fmt.Sprintf("Subject: %s\r\n", manualNeededSubject))
|
|
message.WriteString("MIME-Version: 1.0\r\n")
|
|
message.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
|
|
message.WriteString("\r\n")
|
|
message.WriteString(body)
|
|
|
|
if err := smtp.SendMail(addr, auth, from.Address, to, []byte(message.String())); err != nil {
|
|
return fmt.Errorf("send smtp notification: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ManualNeededMessage(results []model.ActionResult) (string, string, bool) {
|
|
body, ok := manualNeededText(results)
|
|
return manualNeededSubject, body, ok
|
|
}
|
|
|
|
func manualNeededText(results []model.ActionResult) (string, bool) {
|
|
var body strings.Builder
|
|
manualCount := 0
|
|
body.WriteString("nCore HnR torrents need manual work:\n")
|
|
for _, result := range results {
|
|
if !result.ManualNeeded {
|
|
continue
|
|
}
|
|
manualCount++
|
|
body.WriteString(fmt.Sprintf("- %s", result.Torrent.Name))
|
|
if result.QBit != nil {
|
|
body.WriteString(fmt.Sprintf(" (qBit: %s, %.1f%%)", result.QBit.State, result.QBit.Progress*100))
|
|
}
|
|
body.WriteString("\n")
|
|
}
|
|
return body.String(), manualCount > 0
|
|
}
|
|
|
|
func formatAddressList(addresses []*mail.Address) string {
|
|
formatted := make([]string, 0, len(addresses))
|
|
for _, address := range addresses {
|
|
formatted = append(formatted, address.String())
|
|
}
|
|
return strings.Join(formatted, ", ")
|
|
}
|