de8696d5d3
This commit fixes the following golint warnings: pkg/bus/bus.go:64:9: if block ends with a return statement, so drop this else and outdent its block pkg/bus/bus.go:84:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:137:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:177:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:183:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:199:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:208:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/components/dynmap/dynmap.go:236:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:242:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:257:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:263:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:278:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:284:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:299:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:331:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:350:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:356:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:366:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:390:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:396:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:405:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:427:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:433:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:442:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:459:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:465:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:474:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:491:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:497:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:506:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:523:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:529:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:538:12: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:555:9: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:561:10: if block ends with a return statement, so drop this else and outdent its block pkg/components/dynmap/dynmap.go:570:12: if block ends with a return statement, so drop this else and outdent its block pkg/login/ldap.go:55:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/login/ldap_test.go:372:10: if block ends with a return statement, so drop this else and outdent its block pkg/middleware/middleware_test.go:213:12: if block ends with a return statement, so drop this else and outdent its block pkg/plugins/dashboard_importer.go:153:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/plugins/dashboards_updater.go:39:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/plugins/dashboards_updater.go:121:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/plugins/plugins.go:210:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/plugins/plugins.go:235:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/eval_context.go:111:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/notifier.go:92:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/notifier.go:98:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/notifier.go:122:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/rule.go:108:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/rule.go:118:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/rule.go:121:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/alerting/notifiers/telegram.go:94:10: if block ends with a return statement, so drop this else and outdent its block pkg/services/sqlstore/annotation.go:34:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/sqlstore/annotation.go:99:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/sqlstore/dashboard_test.go:107:13: if block ends with a return statement, so drop this else and outdent its block pkg/services/sqlstore/plugin_setting.go:78:10: if block ends with a return statement, so drop this else and outdent its block pkg/services/sqlstore/preferences.go:91:10: if block ends with a return statement, so drop this else and outdent its block pkg/services/sqlstore/user.go:50:10: if block ends with a return statement, so drop this else and outdent its block pkg/services/sqlstore/migrator/migrator.go:106:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/services/sqlstore/migrator/postgres_dialect.go:48:10: if block ends with a return statement, so drop this else and outdent its block pkg/tsdb/time_range.go:59:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/tsdb/time_range.go:67:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) pkg/tsdb/cloudwatch/metric_find_query.go:225:9: if block ends with a return statement, so drop this else and outdent its block pkg/util/filepath.go:68:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
238 lines
6.4 KiB
Go
238 lines
6.4 KiB
Go
package notifiers
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"os"
|
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
"github.com/grafana/grafana/pkg/log"
|
|
m "github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/services/alerting"
|
|
)
|
|
|
|
const (
|
|
captionLengthLimit = 200
|
|
)
|
|
|
|
var (
|
|
telegramApiUrl = "https://api.telegram.org/bot%s/%s"
|
|
)
|
|
|
|
func init() {
|
|
alerting.RegisterNotifier(&alerting.NotifierPlugin{
|
|
Type: "telegram",
|
|
Name: "Telegram",
|
|
Description: "Sends notifications to Telegram",
|
|
Factory: NewTelegramNotifier,
|
|
OptionsTemplate: `
|
|
<h3 class="page-heading">Telegram API settings</h3>
|
|
<div class="gf-form">
|
|
<span class="gf-form-label width-9">BOT API Token</span>
|
|
<input type="text" required
|
|
class="gf-form-input"
|
|
ng-model="ctrl.model.settings.bottoken"
|
|
placeholder="Telegram BOT API Token"></input>
|
|
</div>
|
|
<div class="gf-form">
|
|
<span class="gf-form-label width-9">Chat ID</span>
|
|
<input type="text" required
|
|
class="gf-form-input"
|
|
ng-model="ctrl.model.settings.chatid"
|
|
data-placement="right">
|
|
</input>
|
|
<info-popover mode="right-absolute">
|
|
Integer Telegram Chat Identifier
|
|
</info-popover>
|
|
</div>
|
|
`,
|
|
})
|
|
|
|
}
|
|
|
|
type TelegramNotifier struct {
|
|
NotifierBase
|
|
BotToken string
|
|
ChatID string
|
|
UploadImage bool
|
|
log log.Logger
|
|
}
|
|
|
|
func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
|
if model.Settings == nil {
|
|
return nil, alerting.ValidationError{Reason: "No Settings Supplied"}
|
|
}
|
|
|
|
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"}
|
|
}
|
|
|
|
if chatId == "" {
|
|
return nil, alerting.ValidationError{Reason: "Could not find Chat Id in settings"}
|
|
}
|
|
|
|
return &TelegramNotifier{
|
|
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) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *m.SendWebhookSync {
|
|
if sendImageInline {
|
|
cmd, err := this.buildMessageInlineImage(evalContext)
|
|
if err == nil {
|
|
return cmd
|
|
}
|
|
this.log.Error("Could not generate Telegram message with inline image.", "err", err)
|
|
}
|
|
|
|
return this.buildMessageLinkedImage(evalContext)
|
|
}
|
|
|
|
func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.EvalContext) *m.SendWebhookSync {
|
|
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)
|
|
}
|
|
|
|
metrics := generateMetricsMessage(evalContext)
|
|
if metrics != "" {
|
|
message = message + fmt.Sprintf("\n<i>Metrics:</i>%s", metrics)
|
|
}
|
|
|
|
cmd := this.generateTelegramCmd(message, "text", "sendMessage", func(w *multipart.Writer) {
|
|
fw, _ := w.CreateFormField("parse_mode")
|
|
fw.Write([]byte("html"))
|
|
})
|
|
return cmd
|
|
}
|
|
|
|
func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.EvalContext) (*m.SendWebhookSync, error) {
|
|
var imageFile *os.File
|
|
var err error
|
|
|
|
imageFile, err = os.Open(evalContext.ImageOnDiskPath)
|
|
defer imageFile.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ruleUrl, err := evalContext.GetRuleUrl()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
metrics := generateMetricsMessage(evalContext)
|
|
message := generateImageCaption(evalContext, ruleUrl, metrics)
|
|
|
|
cmd := this.generateTelegramCmd(message, "caption", "sendPhoto", func(w *multipart.Writer) {
|
|
fw, _ := w.CreateFormFile("photo", evalContext.ImageOnDiskPath)
|
|
io.Copy(fw, imageFile)
|
|
})
|
|
return cmd, nil
|
|
}
|
|
|
|
func (this *TelegramNotifier) generateTelegramCmd(message string, messageField string, apiAction string, extraConf func(writer *multipart.Writer)) *m.SendWebhookSync {
|
|
var body bytes.Buffer
|
|
w := multipart.NewWriter(&body)
|
|
|
|
fw, _ := w.CreateFormField("chat_id")
|
|
fw.Write([]byte(this.ChatID))
|
|
|
|
fw, _ = w.CreateFormField(messageField)
|
|
fw.Write([]byte(message))
|
|
|
|
extraConf(w)
|
|
|
|
w.Close()
|
|
|
|
this.log.Info("Sending telegram notification", "chat_id", this.ChatID, "bot_token", this.BotToken, "apiAction", apiAction)
|
|
url := fmt.Sprintf(telegramApiUrl, this.BotToken, apiAction)
|
|
|
|
cmd := &m.SendWebhookSync{
|
|
Url: url,
|
|
Body: body.String(),
|
|
HttpMethod: "POST",
|
|
HttpHeader: map[string]string{
|
|
"Content-Type": w.FormDataContentType(),
|
|
},
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
func generateMetricsMessage(evalContext *alerting.EvalContext) string {
|
|
metrics := ""
|
|
fieldLimitCount := 4
|
|
for index, evt := range evalContext.EvalMatches {
|
|
metrics += fmt.Sprintf("\n%s: %s", evt.Metric, evt.Value)
|
|
if index > fieldLimitCount {
|
|
break
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func generateImageCaption(evalContext *alerting.EvalContext, ruleUrl string, metrics string) string {
|
|
message := evalContext.GetNotificationTitle()
|
|
|
|
if len(evalContext.Rule.Message) > 0 {
|
|
message = fmt.Sprintf("%s\nMessage: %s", message, evalContext.Rule.Message)
|
|
}
|
|
|
|
if len(message) > captionLengthLimit {
|
|
message = message[0:captionLengthLimit]
|
|
|
|
}
|
|
|
|
if len(ruleUrl) > 0 {
|
|
urlLine := fmt.Sprintf("\nURL: %s", ruleUrl)
|
|
message = appendIfPossible(message, urlLine, captionLengthLimit)
|
|
}
|
|
|
|
if metrics != "" {
|
|
metricsLines := fmt.Sprintf("\n\nMetrics:%s", metrics)
|
|
message = appendIfPossible(message, metricsLines, captionLengthLimit)
|
|
}
|
|
|
|
return message
|
|
}
|
|
|
|
func appendIfPossible(message string, extra string, sizeLimit int) string {
|
|
if len(extra)+len(message) <= sizeLimit {
|
|
return message + extra
|
|
}
|
|
log.Debug("Line too long for image caption.", "value", extra)
|
|
return message
|
|
}
|
|
|
|
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
|
|
var cmd *m.SendWebhookSync
|
|
if evalContext.ImagePublicUrl == "" && this.UploadImage {
|
|
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)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|