// Package service — Telegram 通知 Provider。
package service
import (
"context"
"fmt"
"strings"
"time"
)
// TelegramProvider 通过 Telegram Bot API 发送通知。
type TelegramProvider struct{}
// Send 发送 Telegram 消息。
func (p *TelegramProvider) Send(ctx context.Context, cfg map[string]string, event NotifyEvent) error {
botToken := cfg["bot_token"]
chatIDs := telegramTargetChatIDs(cfg)
parseMode := cfg["parse_mode"]
if parseMode == "" {
parseMode = "HTML"
}
if botToken == "" || len(chatIDs) == 0 {
return fmt.Errorf("telegram: bot_token and group_chat_id/channel_chat_id are required")
}
text := formatTelegramMessage(event, parseMode)
var firstErr error
for _, chatID := range chatIDs {
payload := map[string]string{
"chat_id": chatID,
"text": text,
"parse_mode": parseMode,
}
if err := telegramPostJSON(ctx, cfg, "sendMessage", payload, 15*time.Second); err != nil && firstErr == nil {
firstErr = err
}
}
return firstErr
}
// ValidateConfig 验证 Telegram 配置。
func (p *TelegramProvider) ValidateConfig(cfg map[string]string) error {
if cfg["bot_token"] == "" {
return fmt.Errorf("telegram: bot_token is required")
}
if len(telegramTargetChatIDs(cfg)) == 0 {
return fmt.Errorf("telegram: group_chat_id or channel_chat_id is required")
}
return nil
}
// formatTelegramMessage 格式化消息内容。
func formatTelegramMessage(event NotifyEvent, parseMode string) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("%s\n\n", escapeHTML(event.Title)))
sb.WriteString(escapeHTML(event.Message))
if len(event.Data) > 0 {
sb.WriteString("\n\n")
for k, v := range event.Data {
sb.WriteString(fmt.Sprintf("• %s: %v\n", escapeHTML(k), v))
}
}
if parseMode != "HTML" {
// Markdown 模式
result := sb.String()
result = strings.ReplaceAll(result, "", "**")
result = strings.ReplaceAll(result, "", "**")
result = strings.ReplaceAll(result, "<", "<")
result = strings.ReplaceAll(result, ">", ">")
result = strings.ReplaceAll(result, "&", "&")
return result
}
return sb.String()
}