package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"regexp"
"strings"
"time"
)
// mattermostSend отправляет сообщение в Mattermost через Bot API.
// HTML-теги из Telegram автоматически конвертируются в Markdown.
// Если бот не настроен — пишет в лог и не паникует.
func mattermostSend(message string) error {
if !isMattermostConfigured() {
log.Printf("[Mattermost] Не настроен (нужны MATTERMOST_URL, MATTERMOST_BOT_TOKEN, MATTERMOST_CHANNEL_ID) — пропускаем")
return fmt.Errorf("mattermost не настроен")
}
mdMessage := mmHTMLToMarkdown(message)
type mmPost struct {
ChannelID string `json:"channel_id"`
Message string `json:"message"`
}
payload := mmPost{
ChannelID: cfgMattermostChannelID,
Message: mdMessage,
}
jsonData, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("marshal error: %w", err)
}
apiURL := fmt.Sprintf("%s/api/v4/posts", strings.TrimRight(cfgMattermostURL, "/"))
log.Printf("[Mattermost] Отправка: %s (channel: %s)", apiURL, cfgMattermostChannelID)
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("ошибка создания запроса: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+cfgMattermostBotToken)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("ошибка отправки в Mattermost (%s): %w", apiURL, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("mattermost api error: status %d, body: %s", resp.StatusCode, string(body))
}
return nil
}
// mmHTMLToMarkdown конвертирует HTML-теги Telegram в Markdown для Mattermost.
//
// текст → **текст**
// текст → _текст_
// текст → `текст`
//
текст→ ```текст``` // ━━━ (разделители) → --- // Остальные теги → удаляются func mmHTMLToMarkdown(html string) string { s := html // ... → **...** s = regexp.MustCompile(`([\s\S]*?)`).ReplaceAllString(s, "**$1**") // ... → _..._ s = regexp.MustCompile(`([\s\S]*?)`).ReplaceAllString(s, "_$1_") //
... → `...`
s = regexp.MustCompile(`([\s\S]*?)`).ReplaceAllString(s, "`$1`")
// ...→ ```...``` s = regexp.MustCompile(`
([\s\S]*?)`).ReplaceAllString(s, "```$1```") // HTML-entities s = strings.ReplaceAll(s, "<", "<") s = strings.ReplaceAll(s, ">", ">") s = strings.ReplaceAll(s, "&", "&") s = strings.ReplaceAll(s, """, `"`) // Горизонтальные разделители (━━━...) → --- s = regexp.MustCompile(`━+`).ReplaceAllString(s, "---") // Убираем оставшиеся HTML-теги s = regexp.MustCompile(`<[^>]+>`).ReplaceAllString(s, "") // Убираем лишние пустые строки (более двух подряд) s = regexp.MustCompile(`\n{3,}`).ReplaceAllString(s, "\n\n") return strings.TrimSpace(s) }