From c832ea4645c4770ee641c062d8cb3fdc0592b87d Mon Sep 17 00:00:00 2001 From: Bastien Riviere Date: Sun, 3 Sep 2023 16:46:02 +0200 Subject: [PATCH] feat: add discord_embed handler --- bridge/discord.go | 74 +++++++++++++++++++++++++++++++++++++++++++++ config.example.scfg | 9 ++++++ config/config.go | 5 +++ k8s/bridge.yaml | 5 +++ main.go | 2 ++ 5 files changed, 95 insertions(+) create mode 100644 bridge/discord.go diff --git a/bridge/discord.go b/bridge/discord.go new file mode 100644 index 0000000..f2d423f --- /dev/null +++ b/bridge/discord.go @@ -0,0 +1,74 @@ +package bridge + +import ( + "encoding/json" + "log/slog" + "net/http" + "strings" +) + +type DiscordMessage struct { + Content string `json:"content"` + Embeds []struct { + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + Footer struct { + Text string `json:"text"` + } `json:"footer"` + Author struct { + Name string `json:"name"` + URL string `json:"url"` + } `json:"author"` + } `json:"embeds"` +} + +type DiscordEmbedHandler struct{} + +func NewDiscordEmbedHandler() DiscordEmbedHandler { + return DiscordEmbedHandler{} +} + +func (d DiscordEmbedHandler) ProduceNotifications(r *http.Request) ([]Notification, error) { + l := slog.With(slog.String("handler", "discord_embed")) + + dec := json.NewDecoder(r.Body) + defer r.Body.Close() + + var not DiscordMessage + if err := dec.Decode(¬); err != nil { + l.Error("invalid message format", "error", err) + return nil, err + } + + notifications := make([]Notification, len(not.Embeds)) + for i, embed := range not.Embeds { + not := notifications[i] + not.Title = embed.Title + not.IsMarkdown = true + if embed.URL != "" { + not.Actions = []NotificationAction{NewViewAction("Open in Browser", embed.URL)} + } + + var body strings.Builder + body.WriteString(embed.Description) + + if embed.Author.Name != "" { + body.WriteString("\n\n**Author**\n") + body.WriteString(embed.Author.Name) + if embed.Author.URL != "" { + body.WriteString(" (" + embed.Author.URL + ")") + } + } + + if embed.Footer.Text != "" { + body.WriteString("\n\n" + embed.Footer.Text) + } + + not.Body = body.String() + + notifications[i] = not + } + + return notifications, nil +} diff --git a/config.example.scfg b/config.example.scfg index 9437d2a..78dff6f 100644 --- a/config.example.scfg +++ b/config.example.scfg @@ -23,3 +23,12 @@ handler "/flux" { # type "alertmanager" # topic "/infra" # } + +# Handle discord type messages. This is meant for +# webhook that doesn't support generic one's. +# Instead, we convert discord messages to ntfy message. +# See: https://discord.com/developers/docs/resources/channel#message-object +handler "/discord-like" { + type "discord_embed" # handle message with `embeds` content + topic "discord-like" +} diff --git a/config/config.go b/config/config.go index 2b832f4..a02188c 100644 --- a/config/config.go +++ b/config/config.go @@ -11,12 +11,15 @@ type HandlerType int const ( HandlerFlux HandlerType = iota + 1 + HandlerDiscordEmbed ) func (h HandlerType) String() string { switch h { case HandlerFlux: return "flux" + case HandlerDiscordEmbed: + return "discord_embed" } panic("unreachable") } @@ -148,6 +151,8 @@ func readHandlerType(d *scfg.Directive) (HandlerType, error) { switch ty { case "flux": return HandlerFlux, nil + case "discord_embed": + return HandlerDiscordEmbed, nil default: return 0, fmt.Errorf("invalid handler type %q", ty) } diff --git a/k8s/bridge.yaml b/k8s/bridge.yaml index 5f393b7..490c21b 100644 --- a/k8s/bridge.yaml +++ b/k8s/bridge.yaml @@ -17,6 +17,11 @@ data: type "flux" topic "flux" } + + handler "/forgejo" { + type "discord_embed" + topic "forgejo" + } --- apiVersion: apps/v1 kind: Deployment diff --git a/main.go b/main.go index 8ea1926..252371e 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,8 @@ func main() { switch handler.Type { case config.HandlerFlux: h = bridge.NewFluxHandler() + case config.HandlerDiscordEmbed: + h = bridge.NewDiscordEmbedHandler() } slog.Debug("Registering bridge", "route", route, "handler", handler.Type)