// Package alerts : surveillance des pods des démos + notification (Discord, // Telegram) quand une plateforme tombe en erreur ou se rétablit. package alerts import ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" "time" ) // Notifier envoie un message d'alerte vers un canal externe. type Notifier interface { Notify(ctx context.Context, message string) error } // MultiNotifier diffuse vers plusieurs canaux ; une erreur sur l'un // n'empêche pas la notification des autres. type MultiNotifier []Notifier func (m MultiNotifier) Notify(ctx context.Context, message string) error { var errs []error for _, n := range m { if err := n.Notify(ctx, message); err != nil { errs = append(errs, err) } } return errors.Join(errs...) } var httpClient = &http.Client{Timeout: 10 * time.Second} // DiscordNotifier envoie le message sur un webhook Discord. type DiscordNotifier struct { WebhookURL string } func (d *DiscordNotifier) Notify(ctx context.Context, message string) error { body, err := json.Marshal(map[string]string{"content": message}) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, d.WebhookURL, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("discord: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { return fmt.Errorf("discord: statut %d", resp.StatusCode) } return nil } // TelegramNotifier envoie le message via un bot Telegram (sendMessage). type TelegramNotifier struct { BotToken string ChatID string } func (t *TelegramNotifier) Notify(ctx context.Context, message string) error { endpoint := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", t.BotToken) body, err := json.Marshal(map[string]string{"chat_id": t.ChatID, "text": message}) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("telegram: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { return fmt.Errorf("telegram: statut %d", resp.StatusCode) } return nil }