ff1f9273cb
and if it does not match the discord regex it prints a warning but lets things go ahead, this is handy for debugging and canary subdomains of discord. H/T to Tom Bowditch who discovered the latter part
158 lines
4.0 KiB
Go
158 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// Discord color values
|
|
const (
|
|
ColorRed = 10038562
|
|
ColorGreen = 3066993
|
|
ColorGrey = 9807270
|
|
)
|
|
|
|
type alertManAlert struct {
|
|
Annotations struct {
|
|
Description string `json:"description"`
|
|
Summary string `json:"summary"`
|
|
} `json:"annotations"`
|
|
EndsAt string `json:"endsAt"`
|
|
GeneratorURL string `json:"generatorURL"`
|
|
Labels map[string]string `json:"labels"`
|
|
StartsAt string `json:"startsAt"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type alertManOut struct {
|
|
Alerts []alertManAlert `json:"alerts"`
|
|
CommonAnnotations struct {
|
|
Summary string `json:"summary"`
|
|
} `json:"commonAnnotations"`
|
|
CommonLabels struct {
|
|
Alertname string `json:"alertname"`
|
|
} `json:"commonLabels"`
|
|
ExternalURL string `json:"externalURL"`
|
|
GroupKey string `json:"groupKey"`
|
|
GroupLabels struct {
|
|
Alertname string `json:"alertname"`
|
|
} `json:"groupLabels"`
|
|
Receiver string `json:"receiver"`
|
|
Status string `json:"status"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
type discordOut struct {
|
|
Content string `json:"content"`
|
|
Embeds []discordEmbed `json:"embeds"`
|
|
}
|
|
|
|
type discordEmbed struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Color int `json:"color"`
|
|
Fields []discordEmbedField `json:"fields"`
|
|
}
|
|
|
|
type discordEmbedField struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
const defaultListenAddress = "127.0.0.1:9094"
|
|
|
|
func main() {
|
|
envWhURL := os.Getenv("DISCORD_WEBHOOK")
|
|
whURL := flag.String("webhook.url", envWhURL, "Discord WebHook URL.")
|
|
|
|
envListenAddress := os.Getenv("LISTEN_ADDRESS")
|
|
listenAddress := flag.String("listen.address", envListenAddress, "Address:Port to listen on.")
|
|
|
|
flag.Parse()
|
|
|
|
if *whURL == "" {
|
|
log.Fatalf("Environment variable 'DISCORD_WEBHOOK' or CLI parameter 'webhook.url' not found.")
|
|
}
|
|
|
|
if *listenAddress == "" {
|
|
*listenAddress = defaultListenAddress
|
|
}
|
|
|
|
_, err := url.Parse(*whURL)
|
|
if err != nil {
|
|
log.Fatalf("The Discord WebHook URL doesn't seem to be a valid URL.")
|
|
}
|
|
|
|
re := regexp.MustCompile(`https://discord(?:app)?.com/api/webhooks/[0-9]{18}/[a-zA-Z0-9_-]+`)
|
|
if ok := re.Match([]byte(*whURL)); !ok {
|
|
log.Printf("The Discord WebHook URL doesn't seem to be valid.")
|
|
}
|
|
|
|
log.Printf("Listening on: %s", *listenAddress)
|
|
http.ListenAndServe(*listenAddress, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
b, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
amo := alertManOut{}
|
|
err = json.Unmarshal(b, &amo)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
groupedAlerts := make(map[string][]alertManAlert)
|
|
|
|
for _, alert := range amo.Alerts {
|
|
groupedAlerts[alert.Status] = append(groupedAlerts[alert.Status], alert)
|
|
}
|
|
|
|
for status, alerts := range groupedAlerts {
|
|
DO := discordOut{}
|
|
|
|
RichEmbed := discordEmbed{
|
|
Title: fmt.Sprintf("[%s:%d] %s", strings.ToUpper(status), len(alerts), amo.CommonLabels.Alertname),
|
|
Description: amo.CommonAnnotations.Summary,
|
|
Color: ColorGrey,
|
|
Fields: []discordEmbedField{},
|
|
}
|
|
|
|
if status == "firing" {
|
|
RichEmbed.Color = ColorRed
|
|
} else if status == "resolved" {
|
|
RichEmbed.Color = ColorGreen
|
|
}
|
|
|
|
if amo.CommonAnnotations.Summary != "" {
|
|
DO.Content = fmt.Sprintf(" === %s === \n", amo.CommonAnnotations.Summary)
|
|
}
|
|
|
|
for _, alert := range alerts {
|
|
realname := alert.Labels["instance"]
|
|
if strings.Contains(realname, "localhost") && alert.Labels["exported_instance"] != "" {
|
|
realname = alert.Labels["exported_instance"]
|
|
}
|
|
|
|
RichEmbed.Fields = append(RichEmbed.Fields, discordEmbedField{
|
|
Name: fmt.Sprintf("[%s]: %s on %s", strings.ToUpper(status), alert.Labels["alertname"], realname),
|
|
Value: alert.Annotations.Description,
|
|
})
|
|
}
|
|
|
|
DO.Embeds = []discordEmbed{RichEmbed}
|
|
|
|
DOD, _ := json.Marshal(DO)
|
|
http.Post(*whURL, "application/json", bytes.NewReader(DOD))
|
|
}
|
|
}))
|
|
}
|