Parcourir la source

telegram: Send notifications with an inline image

If image upload is enabled, but there is no public image repository,
fall back to sending an image via telegram's API.
Robin Burchell il y a 8 ans
Parent
commit
43109dc8ca
1 fichiers modifiés avec 76 ajouts et 18 suppressions
  1. 76 18
      pkg/services/alerting/notifiers/telegram.go

+ 76 - 18
pkg/services/alerting/notifiers/telegram.go

@@ -7,7 +7,9 @@ import (
 	"github.com/grafana/grafana/pkg/log"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/alerting"
+	"io"
 	"mime/multipart"
+	"os"
 )
 
 var (
@@ -47,9 +49,10 @@ func init() {
 
 type TelegramNotifier struct {
 	NotifierBase
-	BotToken string
-	ChatID   string
-	log      log.Logger
+	BotToken    string
+	ChatID      string
+	UploadImage bool
+	log         log.Logger
 }
 
 func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
@@ -59,6 +62,7 @@ func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error)
 
 	botToken := model.Settings.Get("bottoken").MustString()
 	chatId := model.Settings.Get("chatid").MustString()
+	uploadImage := model.Settings.Get("uploadImage").MustBool()
 
 	if botToken == "" {
 		return nil, alerting.ValidationError{Reason: "Could not find Bot Token in settings"}
@@ -72,26 +76,42 @@ func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error)
 		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
 		BotToken:     botToken,
 		ChatID:       chatId,
+		UploadImage:  uploadImage,
 		log:          log.New("alerting.notifier.telegram"),
 	}, nil
 }
 
-func (this *TelegramNotifier) ShouldNotify(context *alerting.EvalContext) bool {
-	return defaultShouldNotify(context)
-}
+func (this *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *m.SendWebhookSync {
+	var imageFile *os.File
+	var err error
 
-func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
-	this.log.Info("Sending alert notification to", "bot_token", this.BotToken)
-	this.log.Info("Sending alert notification to", "chat_id", this.ChatID)
+	if sendImageInline {
+		imageFile, err = os.Open(evalContext.ImageOnDiskPath)
+		defer imageFile.Close()
+		if err != nil {
+			sendImageInline = false // fall back to text message
+		}
+	}
 
-	message := fmt.Sprintf("<b>%s</b>\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
+	message := ""
+
+	if sendImageInline {
+		// Telegram's API does not allow HTML formatting for image captions.
+		message = fmt.Sprintf("%s\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
+	} else {
+		message = fmt.Sprintf("<b>%s</b>\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
+	}
 
 	ruleUrl, err := evalContext.GetRuleUrl()
 	if err == nil {
 		message = message + fmt.Sprintf("URL: %s\n", ruleUrl)
 	}
-	if evalContext.ImagePublicUrl != "" {
-		message = message + fmt.Sprintf("Image: %s\n", evalContext.ImagePublicUrl)
+
+	if !sendImageInline {
+		// only attach this if we are not sending it inline.
+		if evalContext.ImagePublicUrl != "" {
+			message = message + fmt.Sprintf("Image: %s\n", evalContext.ImagePublicUrl)
+		}
 	}
 
 	metrics := ""
@@ -102,24 +122,48 @@ func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
 			break
 		}
 	}
+
 	if metrics != "" {
-		message = message + fmt.Sprintf("\n<i>Metrics:</i>%s", metrics)
+		if sendImageInline {
+			// Telegram's API does not allow HTML formatting for image captions.
+			message = message + fmt.Sprintf("\nMetrics:%s", metrics)
+		} else {
+			message = message + fmt.Sprintf("\n<i>Metrics:</i>%s", metrics)
+		}
 	}
 
 	var body bytes.Buffer
+
 	w := multipart.NewWriter(&body)
 	fw, _ := w.CreateFormField("chat_id")
 	fw.Write([]byte(this.ChatID))
 
-	fw, _ = w.CreateFormField("parse_mode")
-	fw.Write([]byte("html"))
+	if sendImageInline {
+		fw, _ = w.CreateFormField("caption")
+		fw.Write([]byte(message))
 
-	fw, _ = w.CreateFormField("text")
-	fw.Write([]byte(message))
+		fw, _ = w.CreateFormFile("photo", evalContext.ImageOnDiskPath)
+		io.Copy(fw, imageFile)
+	} else {
+		fw, _ = w.CreateFormField("text")
+		fw.Write([]byte(message))
+
+		fw, _ = w.CreateFormField("parse_mode")
+		fw.Write([]byte("html"))
+	}
 
 	w.Close()
 
-	url := fmt.Sprintf(telegramApiUrl, this.BotToken, "sendMessage")
+	apiMethod := ""
+	if sendImageInline {
+		this.log.Info("Sending telegram image notification", "photo", evalContext.ImageOnDiskPath, "chat_id", this.ChatID, "bot_token", this.BotToken)
+		apiMethod = "sendPhoto"
+	} else {
+		this.log.Info("Sending telegram text notification", "chat_id", this.ChatID, "bot_token", this.BotToken)
+		apiMethod = "sendMessage"
+	}
+
+	url := fmt.Sprintf(telegramApiUrl, this.BotToken, apiMethod)
 	cmd := &m.SendWebhookSync{
 		Url:        url,
 		Body:       body.String(),
@@ -128,6 +172,20 @@ func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
 			"Content-Type": w.FormDataContentType(),
 		},
 	}
+	return cmd
+}
+
+func (this *TelegramNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
+func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
+	var cmd *m.SendWebhookSync
+	if evalContext.ImagePublicUrl == "" && this.UploadImage == true {
+		cmd = this.buildMessage(evalContext, true)
+	} else {
+		cmd = this.buildMessage(evalContext, false)
+	}
 
 	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
 		this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name)