2021-05-19 13:20:52 -05:00
|
|
|
package channels
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-03-14 18:27:10 -05:00
|
|
|
"errors"
|
2021-05-19 13:20:52 -05:00
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
|
2021-10-07 09:33:50 -05:00
|
|
|
"github.com/prometheus/alertmanager/template"
|
|
|
|
"github.com/prometheus/alertmanager/types"
|
|
|
|
"github.com/prometheus/common/model"
|
2022-06-07 12:54:23 -05:00
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-06-20 09:45:35 -05:00
|
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
2022-06-07 12:54:23 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/notifications"
|
2021-05-19 13:20:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ThreemaGwBaseURL = "https://msgapi.threema.ch/send_simple"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ThreemaNotifier is responsible for sending
|
|
|
|
// alert notifications to Threema.
|
|
|
|
type ThreemaNotifier struct {
|
2021-10-22 04:11:06 -05:00
|
|
|
*Base
|
2022-09-27 15:58:18 -05:00
|
|
|
log log.Logger
|
|
|
|
images ImageStore
|
|
|
|
ns notifications.WebhookSender
|
|
|
|
tmpl *template.Template
|
|
|
|
settings threemaSettings
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:58:18 -05:00
|
|
|
type threemaSettings struct {
|
|
|
|
GatewayID string `json:"gateway_id,omitempty" yaml:"gateway_id,omitempty"`
|
|
|
|
RecipientID string `json:"recipient_id,omitempty" yaml:"recipient_id,omitempty"`
|
|
|
|
APISecret string `json:"api_secret,omitempty" yaml:"api_secret,omitempty"`
|
2022-10-27 09:11:38 -05:00
|
|
|
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
|
|
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
2022-03-14 18:27:10 -05:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:58:18 -05:00
|
|
|
func buildThreemaSettings(fc FactoryConfig) (threemaSettings, error) {
|
|
|
|
settings := threemaSettings{}
|
|
|
|
err := fc.Config.unmarshalSettings(&settings)
|
2022-03-14 18:27:10 -05:00
|
|
|
if err != nil {
|
2022-09-27 15:58:18 -05:00
|
|
|
return settings, fmt.Errorf("failed to unmarshal settings: %w", err)
|
2021-11-22 05:56:18 -06:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
// GatewayID validaiton
|
|
|
|
if settings.GatewayID == "" {
|
|
|
|
return settings, errors.New("could not find Threema Gateway ID in settings")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
if !strings.HasPrefix(settings.GatewayID, "*") {
|
|
|
|
return settings, errors.New("invalid Threema Gateway ID: Must start with a *")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
if len(settings.GatewayID) != 8 {
|
|
|
|
return settings, errors.New("invalid Threema Gateway ID: Must be 8 characters long")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
|
|
|
|
// RecipientID validation
|
|
|
|
if settings.RecipientID == "" {
|
|
|
|
return settings, errors.New("could not find Threema Recipient ID in settings")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
if len(settings.RecipientID) != 8 {
|
|
|
|
return settings, errors.New("invalid Threema Recipient ID: Must be 8 characters long")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-09-27 15:58:18 -05:00
|
|
|
settings.APISecret = fc.DecryptFunc(context.Background(), fc.Config.SecureSettings, "api_secret", settings.APISecret)
|
|
|
|
if settings.APISecret == "" {
|
|
|
|
return settings, errors.New("could not find Threema API secret in settings")
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
2022-10-27 09:11:38 -05:00
|
|
|
|
|
|
|
if settings.Description == "" {
|
|
|
|
settings.Description = DefaultMessageEmbed
|
|
|
|
}
|
|
|
|
if settings.Title == "" {
|
|
|
|
settings.Title = DefaultMessageTitleEmbed
|
|
|
|
}
|
|
|
|
|
2022-09-27 15:58:18 -05:00
|
|
|
return settings, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ThreemaFactory(fc FactoryConfig) (NotificationChannel, error) {
|
|
|
|
notifier, err := NewThreemaNotifier(fc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, receiverInitError{
|
|
|
|
Reason: err.Error(),
|
|
|
|
Cfg: *fc.Config,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return notifier, nil
|
2022-03-14 18:27:10 -05:00
|
|
|
}
|
2021-05-19 13:20:52 -05:00
|
|
|
|
2022-09-27 15:58:18 -05:00
|
|
|
func NewThreemaNotifier(fc FactoryConfig) (*ThreemaNotifier, error) {
|
|
|
|
settings, err := buildThreemaSettings(fc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-05-19 13:20:52 -05:00
|
|
|
return &ThreemaNotifier{
|
2021-10-22 04:11:06 -05:00
|
|
|
Base: NewBase(&models.AlertNotification{
|
2022-09-27 15:58:18 -05:00
|
|
|
Uid: fc.Config.UID,
|
|
|
|
Name: fc.Config.Name,
|
|
|
|
Type: fc.Config.Type,
|
|
|
|
DisableResolveMessage: fc.Config.DisableResolveMessage,
|
|
|
|
Settings: fc.Config.Settings,
|
2021-05-19 13:20:52 -05:00
|
|
|
}),
|
2022-09-27 15:58:18 -05:00
|
|
|
log: log.New("alerting.notifier.threema"),
|
|
|
|
images: fc.ImageStore,
|
|
|
|
ns: fc.NotificationService,
|
|
|
|
tmpl: fc.Template,
|
|
|
|
settings: settings,
|
|
|
|
}, nil
|
2021-05-19 13:20:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Notify send an alert notification to Threema
|
|
|
|
func (tn *ThreemaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
|
2022-09-27 15:58:18 -05:00
|
|
|
tn.log.Debug("sending threema alert notification", "from", tn.settings.GatewayID, "to", tn.settings.RecipientID)
|
2021-05-19 13:20:52 -05:00
|
|
|
|
|
|
|
// Set up basic API request data
|
|
|
|
data := url.Values{}
|
2022-09-27 15:58:18 -05:00
|
|
|
data.Set("from", tn.settings.GatewayID)
|
|
|
|
data.Set("to", tn.settings.RecipientID)
|
|
|
|
data.Set("secret", tn.settings.APISecret)
|
2022-10-27 09:11:38 -05:00
|
|
|
data.Set("text", tn.buildMessage(ctx, as...))
|
2021-05-19 13:20:52 -05:00
|
|
|
|
|
|
|
cmd := &models.SendWebhookSync{
|
|
|
|
Url: ThreemaGwBaseURL,
|
|
|
|
Body: data.Encode(),
|
|
|
|
HttpMethod: "POST",
|
|
|
|
HttpHeader: map[string]string{
|
|
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
|
|
},
|
|
|
|
}
|
2022-01-26 09:42:40 -06:00
|
|
|
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
2022-10-19 16:36:54 -05:00
|
|
|
tn.log.Error("Failed to send threema notification", "error", err, "webhook", tn.Name)
|
2021-05-19 13:20:52 -05:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tn *ThreemaNotifier) SendResolved() bool {
|
|
|
|
return !tn.GetDisableResolveMessage()
|
|
|
|
}
|
2022-10-27 09:11:38 -05:00
|
|
|
|
|
|
|
func (tn *ThreemaNotifier) buildMessage(ctx context.Context, as ...*types.Alert) string {
|
|
|
|
var tmplErr error
|
|
|
|
tmpl, _ := TmplText(ctx, tn.tmpl, as, tn.log, &tmplErr)
|
|
|
|
|
|
|
|
message := fmt.Sprintf("%s%s\n\n*Message:*\n%s\n*URL:* %s\n",
|
|
|
|
selectEmoji(as...),
|
|
|
|
tmpl(tn.settings.Title),
|
|
|
|
tmpl(tn.settings.Description),
|
|
|
|
path.Join(tn.tmpl.ExternalURL.String(), "/alerting/list"),
|
|
|
|
)
|
|
|
|
|
|
|
|
if tmplErr != nil {
|
|
|
|
tn.log.Warn("failed to template Threema message", "error", tmplErr.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = withStoredImages(ctx, tn.log, tn.images,
|
|
|
|
func(_ int, image ngmodels.Image) error {
|
|
|
|
if image.URL != "" {
|
|
|
|
message += fmt.Sprintf("*Image:* %s\n", image.URL)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}, as...)
|
|
|
|
|
|
|
|
return message
|
|
|
|
}
|
|
|
|
|
|
|
|
func selectEmoji(as ...*types.Alert) string {
|
|
|
|
if types.Alerts(as...).Status() == model.AlertResolved {
|
|
|
|
return "\u2705 " // Check Mark Button
|
|
|
|
}
|
|
|
|
return "\u26A0\uFE0F " // Warning sign
|
|
|
|
}
|