Files
AetherForge/server/internal/alerts/notify.go
AetherForge d52479c9a6 feat: Telegram fleet alerts, forge sigil scramble, UI polish, agent ops
- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help
- Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete
- Sigil scramble post-forge uniquification and Dispense Reveal ceremony
- Full system check, desktop push, BITS/host-binary persistence, Path Tracer
- Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav
- README documents alerts, sigil scramble, and pack-usb workflow
- USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
2026-06-03 20:32:59 -07:00

96 lines
2.3 KiB
Go

package alerts
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/smtp"
"strings"
"time"
)
type NotifyConfig struct {
TelegramBotToken string
TelegramChatID string
EmailEnabled bool
SMTPHost string
SMTPPort int
SMTPUser string
SMTPPassword string
EmailTo string
EmailFrom string
}
func SendTelegram(cfg NotifyConfig, text string) error {
if cfg.TelegramBotToken == "" || cfg.TelegramChatID == "" {
return nil
}
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", cfg.TelegramBotToken)
payload, _ := json.Marshal(map[string]string{
"chat_id": cfg.TelegramChatID,
"text": text,
})
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 300 {
msg := strings.TrimSpace(string(body))
if msg == "" {
return fmt.Errorf("telegram API status %d", resp.StatusCode)
}
var apiErr struct {
Description string `json:"description"`
}
if json.Unmarshal(body, &apiErr) == nil && apiErr.Description != "" {
return fmt.Errorf("telegram: %s", apiErr.Description)
}
return fmt.Errorf("telegram API status %d: %s", resp.StatusCode, msg)
}
return nil
}
func SendEmail(cfg NotifyConfig, subject, body string) error {
if !cfg.EmailEnabled || cfg.SMTPHost == "" || cfg.EmailTo == "" {
return nil
}
from := cfg.EmailFrom
if from == "" {
from = cfg.SMTPUser
}
port := cfg.SMTPPort
if port <= 0 {
port = 587
}
addr := fmt.Sprintf("%s:%d", cfg.SMTPHost, port)
msg := strings.Join([]string{
fmt.Sprintf("From: %s", from),
fmt.Sprintf("To: %s", cfg.EmailTo),
fmt.Sprintf("Subject: %s", subject),
"MIME-Version: 1.0",
"Content-Type: text/plain; charset=UTF-8",
"",
body,
}, "\r\n")
var auth smtp.Auth
if cfg.SMTPUser != "" {
auth = smtp.PlainAuth("", cfg.SMTPUser, cfg.SMTPPassword, cfg.SMTPHost)
}
return smtp.SendMail(addr, auth, from, []string{cfg.EmailTo}, []byte(msg))
}
func NotifyAll(cfg NotifyConfig, subject, text string) {
_ = SendTelegram(cfg, subject+": "+text)
_ = SendEmail(cfg, subject, text)
}