package infra import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) var validParseModes = map[string]bool{ "": true, "Markdown": true, "MarkdownV2": true, "HTML": true, } // NotifyTelegram envía un mensaje a un chat de Telegram via Bot API. // botToken: token del bot sin prefijo "bot". chatID: ID numérico o @channelname. // parseMode: "" (plain), "Markdown", "MarkdownV2" o "HTML". // Textos superiores a 4096 chars se truncan a 4093 + "...". func NotifyTelegram(botToken string, chatID string, text string, parseMode string) error { if !validParseModes[parseMode] { return fmt.Errorf("notify_telegram: invalid parseMode %q (must be \"\", \"Markdown\", \"MarkdownV2\" or \"HTML\")", parseMode) } const maxLen = 4096 if len(text) > maxLen { text = text[:4093] + "..." } payload := map[string]any{ "chat_id": chatID, "text": text, } if parseMode != "" { payload["parse_mode"] = parseMode } data, err := json.Marshal(payload) if err != nil { return fmt.Errorf("notify_telegram: marshal payload: %w", err) } url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken) client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data)) if err != nil { return fmt.Errorf("notify_telegram: build request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return fmt.Errorf("notify_telegram: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("notify_telegram: read response: %w", err) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("notify_telegram: telegram api: status=%d body=%s", resp.StatusCode, body) } var result struct { OK bool `json:"ok"` Description string `json:"description"` } if err := json.Unmarshal(body, &result); err != nil { return fmt.Errorf("notify_telegram: parse response: %w", err) } if !result.OK { return fmt.Errorf("notify_telegram: telegram: %s", result.Description) } return nil }