Files
http_logger/notifier.go

166 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bytes"
"encoding/json"
"fmt"
"html"
"io"
"net"
"net/http"
"net/mail"
"net/smtp"
"strings"
"time"
"gitea.mediatoday.ru/mt/notify"
)
// Notifier — канал отправки уведомлений
type Notifier interface {
Send(data RequestData) error
}
// formatPlain собирает текстовое тело без обёртки.
func formatPlain(d RequestData) string {
var b strings.Builder
fmt.Fprintf(&b, "[%s] %s %s%s\n", d.Time.Format("2006-01-02T15:04:05Z07:00"), d.Method, d.Host, d.URL)
fmt.Fprintf(&b, "RemoteAddr: %s\n", d.RemoteAddr)
b.WriteString("Headers:\n")
for name, values := range d.Header {
for _, v := range values {
fmt.Fprintf(&b, " %s: %s\n", name, v)
}
}
fmt.Fprintf(&b, "Body:\n%s\n", d.Body)
b.WriteString("----\n")
return b.String()
}
// formatMarkdown — обёртка в код-блок для Telegram/Pachca.
func formatMarkdown(d RequestData) string {
return "```" + formatPlain(d) + "```"
}
// formatHTML — обёртка в <pre> с экранированием для Email.
func formatHTML(d RequestData) string {
return "<pre>" + html.EscapeString(formatPlain(d)) + "</pre>"
}
// --- Telegram ---
type telegramNotifier struct {
bot *notify.Bot
chatID int64
}
func (t *telegramNotifier) Send(d RequestData) error {
_, err := t.bot.SendTextMessage(t.chatID, formatMarkdown(d))
return err
}
// --- Pachca (входящий вебхук) ---
type pachcaNotifier struct {
webhookURL string
client *http.Client
}
func (p *pachcaNotifier) Send(d RequestData) error {
payload, err := json.Marshal(map[string]string{"message": formatMarkdown(d)})
if err != nil {
return err
}
resp, err := p.client.Post(p.webhookURL, "application/json", bytes.NewReader(payload))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("pachca webhook: HTTP %d: %s", resp.StatusCode, string(body))
}
return nil
}
// --- Email ---
type emailNotifier struct {
auth notify.SmtpAuth
from mail.Address
to []mail.Address
subject string
}
func (e *emailNotifier) Send(d RequestData) error {
return notify.SendEmailHTML(e.auth, formatHTML(d), e.subject, e.from, e.to...)
}
func splitHostPortLenient(addr string) (host, port string, err error) {
return net.SplitHostPort(addr)
}
// BuildNotifiers собирает map каналов из конфига. Добавляет только заданные секции.
func BuildNotifiers(cfg *Config) (map[string]Notifier, error) {
notifiers := map[string]Notifier{}
if cfg.Telegram != nil {
disableIPV6 := true
if cfg.Telegram.DisableIPV6 != nil {
disableIPV6 = *cfg.Telegram.DisableIPV6
}
bot, err := notify.NewTelegram(cfg.Telegram.Token, disableIPV6)
if err != nil {
return nil, fmt.Errorf("telegram: %w", err)
}
notifiers["telegram"] = &telegramNotifier{bot: bot, chatID: cfg.Telegram.GroupID}
}
if cfg.Pachca != nil {
if cfg.Pachca.WebhookURL == "" {
return nil, fmt.Errorf("pachca: не задан webhook_url")
}
notifiers["pachca"] = &pachcaNotifier{
webhookURL: cfg.Pachca.WebhookURL,
client: &http.Client{Timeout: 30 * time.Second},
}
}
if cfg.Email != nil {
host, _, err := splitHostPortLenient(cfg.Email.SMTPAddr)
if err != nil {
return nil, fmt.Errorf("email smtp_addr: %w", err)
}
from, err := mail.ParseAddress(cfg.Email.From)
if err != nil {
return nil, fmt.Errorf("email from: %w", err)
}
var to []mail.Address
for _, addr := range cfg.Email.To {
a, err := mail.ParseAddress(addr)
if err != nil {
return nil, fmt.Errorf("email to %q: %w", addr, err)
}
to = append(to, *a)
}
if len(to) == 0 {
return nil, fmt.Errorf("email: не задан ни один получатель (to)")
}
subject := cfg.Email.Subject
if subject == "" {
subject = "HTTP Logger"
}
notifiers["email"] = &emailNotifier{
auth: notify.SmtpAuth{
Addr: cfg.Email.SMTPAddr,
Auth: smtp.PlainAuth("", cfg.Email.Username, cfg.Email.Password, host),
},
from: *from,
to: to,
subject: subject,
}
}
return notifiers, nil
}