Files
drjones 3678b199d0
Some checks failed
Test / test (push) Has been cancelled
Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
2026-07-04 09:31:23 +00:00

99 lines
2.2 KiB
Go

package alerts
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"time"
)
// Config holds Telegram bot settings from config.json.
type Config struct {
Enabled bool `json:"enabled"`
BotToken string `json:"bot_token"`
ChatID string `json:"chat_id"`
}
// Notifier sends operator alerts via Telegram Bot API.
type Notifier struct {
cfg Config
client *http.Client
mu sync.Mutex
last time.Time
}
func New(cfg Config) *Notifier {
return &Notifier{
cfg: cfg,
client: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// Enabled reports whether Telegram alerts are configured.
func (n *Notifier) Enabled() bool {
return n != nil && n.cfg.Enabled && n.cfg.BotToken != "" && n.cfg.ChatID != ""
}
// Send posts a message to the configured chat (best-effort, non-blocking caller).
func (n *Notifier) Send(text string) {
if !n.Enabled() {
return
}
go func() {
if err := n.sendSync(text); err != nil {
log.Printf("telegram alert: %v", err)
}
}()
}
func (n *Notifier) sendSync(text string) error {
n.mu.Lock()
if time.Since(n.last) < 500*time.Millisecond {
n.mu.Unlock()
time.Sleep(500 * time.Millisecond)
n.mu.Lock()
}
n.last = time.Now()
n.mu.Unlock()
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.cfg.BotToken)
body, _ := json.Marshal(map[string]string{
"chat_id": n.cfg.ChatID,
"text": text,
"parse_mode": "Markdown",
})
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := n.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("telegram API status %d", resp.StatusCode)
}
return nil
}
// FleetEvent formats a fleet alert message.
func FleetEvent(action, hostID, detail string) string {
return fmt.Sprintf("🔧 *forge-mesh* · `%s`\nhost: `%s`\n%s", action, hostID, detail)
}
// CrucibleEvent formats a batch terminal alert.
func CrucibleEvent(jobID string, hostCount int, cmd string) string {
return fmt.Sprintf("⚗️ *crucible batch* · `%s`\nhosts: %d\ncmd: `%s`", jobID, hostCount, cmd)
}