From c730eaa8607f435d2e7b34f8089ab4e5bcf3a518 Mon Sep 17 00:00:00 2001 From: shardingHe <125482062+shardingHe@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:04:02 +0800 Subject: [PATCH] Move feishucard (#1563) * Fix an exception situation where the prod and cate fields cannot be updated. * add feishucard.tpl * move feishucard to v6 --------- Co-authored-by: shardingHe --- alert/dispatch/dispatch.go | 13 ++-- alert/sender/feishucard.go | 151 ++++++++++++++++++++++++++++++++++++ alert/sender/sender.go | 2 + etc/template/feishucard.tpl | 17 ++++ models/notify_tpl.go | 2 +- models/user.go | 3 +- 6 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 alert/sender/feishucard.go create mode 100644 etc/template/feishucard.tpl diff --git a/alert/dispatch/dispatch.go b/alert/dispatch/dispatch.go index bce3c9ca..47e9fc06 100644 --- a/alert/dispatch/dispatch.go +++ b/alert/dispatch/dispatch.go @@ -83,12 +83,13 @@ func (e *Dispatch) relaodTpls() error { smtp := e.notifyConfigCache.GetSMTP() senders := map[string]sender.Sender{ - models.Email: sender.NewSender(models.Email, tmpTpls, smtp), - models.Dingtalk: sender.NewSender(models.Dingtalk, tmpTpls, smtp), - models.Wecom: sender.NewSender(models.Wecom, tmpTpls, smtp), - models.Feishu: sender.NewSender(models.Feishu, tmpTpls, smtp), - models.Mm: sender.NewSender(models.Mm, tmpTpls, smtp), - models.Telegram: sender.NewSender(models.Telegram, tmpTpls, smtp), + models.Email: sender.NewSender(models.Email, tmpTpls, smtp), + models.Dingtalk: sender.NewSender(models.Dingtalk, tmpTpls, smtp), + models.Wecom: sender.NewSender(models.Wecom, tmpTpls, smtp), + models.Feishu: sender.NewSender(models.Feishu, tmpTpls, smtp), + models.Mm: sender.NewSender(models.Mm, tmpTpls, smtp), + models.Telegram: sender.NewSender(models.Telegram, tmpTpls, smtp), + models.Feishucard: sender.NewSender(models.Feishucard, tmpTpls, smtp), } e.RwLock.RLock() diff --git a/alert/sender/feishucard.go b/alert/sender/feishucard.go new file mode 100644 index 00000000..bff511a5 --- /dev/null +++ b/alert/sender/feishucard.go @@ -0,0 +1,151 @@ +package sender + +import ( + "fmt" + "html/template" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" + + "github.com/toolkits/pkg/logger" +) + +type Conf struct { + WideScreenMode bool `json:"wide_screen_mode"` + EnableForward bool `json:"enable_forward"` +} + +type Te struct { + Content string `json:"content"` + Tag string `json:"tag"` +} + +type Element struct { + Tag string `json:"tag"` + Text Te `json:"text"` + Content string `json:"content"` + Elements []Element `json:"elements"` +} + +type Titles struct { + Content string `json:"content"` + Tag string `json:"tag"` +} + +type Headers struct { + Title Titles `json:"title"` + Template string `json:"template"` +} + +type Cards struct { + Config Conf `json:"config"` + Elements []Element `json:"elements"` + Header Headers `json:"header"` +} + +type feishuCard struct { + feishu + Card Cards `json:"card"` +} + +type FeishucardSender struct { + tpl *template.Template +} + +const ( + Recovered = "recovered" + Triggered = "triggered" +) + +var ( + //regRecovered ,regTriggered = regexp.MustCompile(Recovered) regexp.MustCompile(Triggered) + body = feishuCard{ + feishu: feishu{Msgtype: "interactive"}, + Card: Cards{ + Config: Conf{ + WideScreenMode: true, + EnableForward: true, + }, + Header: Headers{ + Title: Titles{ + //Content: SendTitle, + Tag: "plain_text", + }, + //Template: color, + }, + Elements: []Element{ + Element{ + Tag: "div", + Text: Te{ + //Content: message, + Tag: "lark_md", + }, + }, + { + Tag: "hr", + }, + { + Tag: "note", + Elements: []Element{ + { + //Content: SendTitle, + Tag: "lark_md", + }, + }, + }, + }, + }, + } +) + +func (fs *FeishucardSender) Send(ctx MessageContext) { + if len(ctx.Users) == 0 || ctx.Rule == nil || ctx.Event == nil { + return + } + urls, _ := fs.extract(ctx.Users) + message := BuildTplMessage(fs.tpl, ctx.Event) + color := "red" + lowerUnicode := strings.ToLower(message) + switch { + case strings.Count(lowerUnicode, Recovered) > 0 && strings.Count(lowerUnicode, Triggered) > 0: + //case len(regRecovered.FindAllStringIndex(lowerUnicode, -1))>0 && len(regTriggered.FindAllStringIndex(lowerUnicode, -1)) > 0: + color = "orange" + case strings.Count(lowerUnicode, Recovered) > 0: + color = "green" + default: + } + SendTitle := fmt.Sprintf("🔔 [告警提醒] - %s", ctx.Event.RuleName) + body.Card.Header.Title.Content = SendTitle + body.Card.Header.Template = color + body.Card.Elements[0].Text.Content = message + body.Card.Elements[2].Elements[0].Content = SendTitle + for _, url := range urls { + fs.doSend(url, body) + } +} + +func (fs *FeishucardSender) extract(users []*models.User) ([]string, []string) { + urls := make([]string, 0, len(users)) + ats := make([]string, 0) + for i := range users { + if token, has := users[i].ExtractToken(models.Feishucard); has { + url := token + if !strings.HasPrefix(token, "https://") { + url = "https://open.feishu.cn/open-apis/bot/v2/hook/" + strings.TrimSpace(token) + } + urls = append(urls, url) + } + } + return urls, ats +} + +func (fs *FeishucardSender) doSend(url string, body feishuCard) { + res, code, err := poster.PostJSON(url, time.Second*5, body, 3) + if err != nil { + logger.Errorf("feishucard_sender: result=fail url=%s code=%d error=%v response=%s", url, code, err, string(res)) + } else { + logger.Debugf("feishucard_sender: result=succ url=%s code=%d response=%s", url, code, string(res)) + } +} diff --git a/alert/sender/sender.go b/alert/sender/sender.go index e18b08a9..3a715a05 100644 --- a/alert/sender/sender.go +++ b/alert/sender/sender.go @@ -31,6 +31,8 @@ func NewSender(key string, tpls map[string]*template.Template, smtp aconf.SMTPCo return &WecomSender{tpl: tpls[models.Wecom]} case models.Feishu: return &FeishuSender{tpl: tpls[models.Feishu]} + case models.Feishucard: + return &FeishucardSender{tpl: tpls[models.Feishucard]} case models.Email: return &EmailSender{subjectTpl: tpls["mailsubject"], contentTpl: tpls[models.Email], smtp: smtp} case models.Mm: diff --git a/etc/template/feishucard.tpl b/etc/template/feishucard.tpl new file mode 100644 index 00000000..bc1ac1e7 --- /dev/null +++ b/etc/template/feishucard.tpl @@ -0,0 +1,17 @@ +{{ if .IsRecovered }} +{{- if ne .Cate "host"}} +**告警集群:** {{.Cluster}}{{end}} +**级别状态:** S{{.Severity}} Recovered +**告警名称:** {{.RuleName}} +**恢复时间:** {{timeformat .LastEvalTime}} +**告警描述:** **服务已恢复** +{{- else }} +{{- if ne .Cate "host"}} +**告警集群:** {{.Cluster}}{{end}} +**级别状态:** S{{.Severity}} Triggered +**告警名称:** {{.RuleName}} +**触发时间:** {{timeformat .TriggerTime}} +**发送时间:** {{timestamp}} +**触发时值:** {{.TriggerValue}} +{{if .RuleNote }}**告警描述:** **{{.RuleNote}}**{{end}} +{{- end -}} \ No newline at end of file diff --git a/models/notify_tpl.go b/models/notify_tpl.go index 48ae1c2a..3ccf4600 100644 --- a/models/notify_tpl.go +++ b/models/notify_tpl.go @@ -107,7 +107,7 @@ func InitNotifyConfig(c *ctx.Context, tplDir string) { if cval == "" { var notifyChannels []NotifyChannel - channels := []string{Dingtalk, Wecom, Feishu, Mm, Telegram, Email} + channels := []string{Dingtalk, Wecom, Feishu, Mm, Telegram, Email, Feishucard} for _, channel := range channels { notifyChannels = append(notifyChannels, NotifyChannel{Ident: channel, Name: channel, BuiltIn: true}) } diff --git a/models/user.go b/models/user.go index 6e109ca2..7178ec3e 100644 --- a/models/user.go +++ b/models/user.go @@ -23,6 +23,7 @@ const ( Dingtalk = "dingtalk" Wecom = "wecom" Feishu = "feishu" + Feishucard = "feishucard" Mm = "mm" Telegram = "telegram" Email = "email" @@ -607,7 +608,7 @@ func (u *User) ExtractToken(key string) (string, bool) { case Wecom: ret := gjson.GetBytes(bs, WecomKey) return ret.String(), ret.Exists() - case Feishu: + case Feishu, Feishucard: ret := gjson.GetBytes(bs, FeishuKey) return ret.String(), ret.Exists() case Mm: