|
|
|
|
@ -23,6 +23,7 @@ type GoogleChatNotifier struct { |
|
|
|
|
URL string |
|
|
|
|
log log.Logger |
|
|
|
|
ns notifications.WebhookSender |
|
|
|
|
images ImageStore |
|
|
|
|
tmpl *template.Template |
|
|
|
|
content string |
|
|
|
|
} |
|
|
|
|
@ -41,7 +42,7 @@ func GoogleChatFactory(fc FactoryConfig) (NotificationChannel, error) { |
|
|
|
|
Cfg: *fc.Config, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return NewGoogleChatNotifier(cfg, fc.NotificationService, fc.Template), nil |
|
|
|
|
return NewGoogleChatNotifier(cfg, fc.ImageStore, fc.NotificationService, fc.Template), nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewGoogleChatConfig(config *NotificationChannelConfig) (*GoogleChatConfig, error) { |
|
|
|
|
@ -56,7 +57,7 @@ func NewGoogleChatConfig(config *NotificationChannelConfig) (*GoogleChatConfig, |
|
|
|
|
}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewGoogleChatNotifier(config *GoogleChatConfig, ns notifications.WebhookSender, t *template.Template) *GoogleChatNotifier { |
|
|
|
|
func NewGoogleChatNotifier(config *GoogleChatConfig, images ImageStore, ns notifications.WebhookSender, t *template.Template) *GoogleChatNotifier { |
|
|
|
|
return &GoogleChatNotifier{ |
|
|
|
|
Base: NewBase(&models.AlertNotification{ |
|
|
|
|
Uid: config.UID, |
|
|
|
|
@ -69,6 +70,7 @@ func NewGoogleChatNotifier(config *GoogleChatConfig, ns notifications.WebhookSen |
|
|
|
|
URL: config.URL, |
|
|
|
|
log: log.New("alerting.notifier.googlechat"), |
|
|
|
|
ns: ns, |
|
|
|
|
images: images, |
|
|
|
|
tmpl: t, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -138,6 +140,9 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) ( |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
if screenshots := gcn.buildScreenshotCard(ctx, as); screenshots != nil { |
|
|
|
|
res.Cards = append(res.Cards, *screenshots) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if tmplErr != nil { |
|
|
|
|
gcn.log.Warn("failed to template GoogleChat message", "err", tmplErr.Error()) |
|
|
|
|
@ -176,6 +181,53 @@ func (gcn *GoogleChatNotifier) SendResolved() bool { |
|
|
|
|
return !gcn.GetDisableResolveMessage() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (gcn *GoogleChatNotifier) buildScreenshotCard(ctx context.Context, alerts []*types.Alert) *card { |
|
|
|
|
card := card{ |
|
|
|
|
Header: header{ |
|
|
|
|
Title: "Screenshots", |
|
|
|
|
}, |
|
|
|
|
Sections: []section{}, |
|
|
|
|
} |
|
|
|
|
for _, alert := range alerts { |
|
|
|
|
imgToken := getTokenFromAnnotations(alert.Annotations) |
|
|
|
|
if len(imgToken) == 0 { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
timeoutCtx, cancel := context.WithTimeout(ctx, ImageStoreTimeout) |
|
|
|
|
imgURL, err := gcn.images.GetURL(timeoutCtx, imgToken) |
|
|
|
|
cancel() |
|
|
|
|
if err != nil { |
|
|
|
|
if !errors.Is(err, ErrImagesUnavailable) { |
|
|
|
|
// Ignore errors. Don't log "ImageUnavailable", which means the storage doesn't exist.
|
|
|
|
|
gcn.log.Warn("failed to retrieve image url from store", "error", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(imgURL) > 0 { |
|
|
|
|
section := section{ |
|
|
|
|
Widgets: []widget{ |
|
|
|
|
textParagraphWidget{ |
|
|
|
|
Text: text{ |
|
|
|
|
Text: fmt.Sprintf("%s: %s", alert.Status(), alert.Name()), |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
imageWidget{ |
|
|
|
|
Image: imageData{ |
|
|
|
|
ImageURL: imgURL, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
card.Sections = append(card.Sections, section) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if len(card.Sections) == 0 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
return &card |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Structs used to build a custom Google Hangouts Chat message card.
|
|
|
|
|
// See: https://developers.google.com/hangouts/chat/reference/message-formats/cards
|
|
|
|
|
type outerStruct struct { |
|
|
|
|
@ -208,6 +260,14 @@ type textParagraphWidget struct { |
|
|
|
|
Text text `json:"textParagraph"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type imageWidget struct { |
|
|
|
|
Image imageData `json:"image"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type imageData struct { |
|
|
|
|
ImageURL string `json:"imageUrl"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type text struct { |
|
|
|
|
Text string `json:"text"` |
|
|
|
|
} |
|
|
|
|
|