Compare commits

..

21 Commits

Author SHA1 Message Date
3e3de4724f Fix url in Makefile
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2024-07-27 11:10:10 +01:00
1d26f341c7 Strip newlines from Slack payload
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2024-07-27 11:09:40 +01:00
801574a245 Print out slack payload on error
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2024-07-27 10:47:14 +01:00
42f9b8da76 Added Slack webhook handling
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2024-07-27 10:24:17 +01:00
4b25a2722e Get room from override
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2023-08-12 13:01:39 +01:00
a9f79c3c27 Added debug logging
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2023-08-12 12:59:11 +01:00
a6ae1fe7b2 Added support for querystring overriding
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2023-08-12 12:14:00 +01:00
5feb19868f Updated image in makefile
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2023-08-12 12:05:23 +01:00
9bddd75bf1 Add support for general markdown messages
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2023-04-25 21:08:12 +01:00
88cbbd17fc Switched back to formatted message
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 09:26:47 +00:00
b17a352d20 Change room again
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 08:54:02 +00:00
24564bfc4e Switched to sending standard text
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 08:45:16 +00:00
8532a10491 Switched to default room
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 08:37:02 +00:00
8cb15b4cf2 Added logging
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 08:21:45 +00:00
41008e4dee Added nexmo SMS parsing
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2022-02-28 08:07:59 +00:00
6c345c8aff Updated alert manager handling
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-24 14:15:39 +00:00
caea01d66a Fix release task
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-20 12:01:08 +00:00
2171865c10 Add support for setting room on webhook
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-19 20:56:31 +00:00
0be6cc8d88 Render formatting
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-05 17:59:18 +00:00
eaba72fb23 Render template
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-05 15:55:16 +00:00
2a79d08e71 Correctly get room ID
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2021-12-05 15:30:58 +00:00
9 changed files with 220 additions and 53 deletions

View File

@@ -1,6 +1,6 @@
.DEFAULT_GOAL := default
IMAGE ?= docker.cluster.fun/averagemarcus/tank:latest
IMAGE ?= rg.fr-par.scw.cloud/averagemarcus/tank:latest
.PHONY: test # Run all tests, linting and format checks
test: lint check-format run-tests
@@ -47,7 +47,7 @@ ci:
.PHONY: release # Release the latest version of the application
release:
@kubectl --namespace opengraph set image deployment opengraph web=docker.cluster.fun/averagemarcus/tank:$(SHA)
@kubectl --namespace tank set image deployment tank web=rg.fr-par.scw.cloud/averagemarcus/tank:$(SHA)
.PHONY: help # Show this list of commands
help:

View File

@@ -20,7 +20,8 @@ curl -X "POST" "http://localhost:3000/" \
* Send notice messages to a room
* Integrations:
* Alertmanager - Use the `/alertmanager` endpoint
* Slack - Use the `/slack` endpoint
* Nexmo SMS - Use the `/nexmo/sms` endpoint
## Building from source

View File

@@ -1,11 +1,16 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
tTemplate "text/template"
"github.com/gofiber/fiber/v2"
"github.com/prometheus/alertmanager/template"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
)
@@ -17,21 +22,42 @@ func HandleAlertmanagerPayloadPost(c *fiber.Ctx) error {
}
fmt.Println("Got alertmanager payload")
fmt.Printf("%d alerts recieved\n", len(payload.Alerts))
for _, alert := range payload.Alerts {
s, _ := json.MarshalIndent(alert, "", "\t")
fmt.Println(string(s))
message := ""
var rendered bytes.Buffer
at, _ := tTemplate.New("AlertMessage").Parse(alert.Annotations["description"])
at.Execute(&rendered, alert)
if alert.Status == "firing" {
switch alert.Labels["severity"] {
case "warning":
message = fmt.Sprintf("⚠️ %s", alert.Annotations["description"])
message = fmt.Sprintf("⚠️ %s", rendered.String())
case "notify":
message = fmt.Sprintf("@room - %s", alert.Annotations["description"])
message = fmt.Sprintf("@room - %s", rendered.String())
}
} else {
message = fmt.Sprintf("☑️ %s", alert.Annotations["description"])
message = fmt.Sprintf(" %s", rendered.String())
}
_, err := matrixClient.SendText(id.RoomID(*defaultRoom), message)
if serviceURL, ok := alert.Annotations["service_url"]; ok {
message += "<br>Service URL: " + serviceURL
}
if alert.GeneratorURL != "" {
message += "<br>URL: " + alert.GeneratorURL
}
_, err := matrixClient.SendMessageEvent(
id.RoomID(getRoom(c.Query("room", *defaultRoom))),
event.EventMessage,
format.RenderMarkdown(message, true, true),
)
if err != nil {
fmt.Println("Failed sending to Matrix", err)
if httpErr, ok := err.(mautrix.HTTPError); ok {

1
go.mod
View File

@@ -5,5 +5,6 @@ go 1.16
require (
github.com/gofiber/fiber/v2 v2.14.0
github.com/prometheus/alertmanager v0.23.0
github.com/slack-go/slack v0.13.1
maunium.net/go/mautrix v0.9.14
)

9
go.sum
View File

@@ -197,6 +197,8 @@ github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3yg
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
@@ -265,8 +267,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -283,6 +286,7 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -411,6 +415,7 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@@ -424,6 +429,8 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/slack-go/slack v0.13.1 h1:6UkM3U1OnbhPsYeb1IMkQ6HSNOSikWluwOncJt4Tz/o=
github.com/slack-go/slack v0.13.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=

75
main.go
View File

@@ -1,6 +1,7 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
@@ -8,6 +9,8 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
)
@@ -55,9 +58,55 @@ func main() {
app.Use(logger.New())
app.Post("/", HandlePayloadPost)
app.Post("/alertmanager", HandleAlertmanagerPayloadPost)
app.Post("/slack", HandleSlackPayloadPost)
app.Post("/nexmo/sms", HandleNexmoSMSPost)
app.Listen(fmt.Sprintf(":%s", port))
}
func getRoom(roomID string) string {
room := parseRoom(roomID)
if strings.HasPrefix(room, "#") || !strings.HasPrefix("!", room) {
if !strings.HasPrefix(room, "#") {
room = fmt.Sprintf("#%s", room)
}
resp, err := matrixClient.ResolveAlias(id.RoomAlias(room))
if err == nil {
room = resp.RoomID.String()
} else {
room = strings.Replace(room, "#", "!", 1)
}
}
return room
}
func parseRoom(room string) string {
prefix := ""
local := ""
domain := ""
parts := strings.Split(room, ":")
if len(parts) == 2 {
domain = parts[1]
} else {
domain = matrixClient.HomeserverURL.Host
}
if strings.HasPrefix(parts[0], "!") {
prefix = "!"
parts[0] = strings.TrimPrefix(parts[0], "!")
}
if strings.HasPrefix(parts[0], "#") {
prefix = "#"
parts[0] = strings.TrimPrefix(parts[0], "#")
}
local = parts[0]
return fmt.Sprintf("%s%s:%s", prefix, local, domain)
}
func HandlePayloadPost(c *fiber.Ctx) error {
payload := Payload{}
@@ -69,6 +118,20 @@ func HandlePayloadPost(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
}
overrideRoom := c.Query("roomID")
if overrideRoom != "" {
fmt.Printf("Override room from query string: %s\n", overrideRoom)
payload.RoomID = getRoom(overrideRoom)
}
overrideType := c.Query("type")
if overrideType != "" {
fmt.Printf("Override type from query string: %s\n", overrideType)
payload.Type = PayloadType(overrideType)
}
s, _ := json.MarshalIndent(payload, "", "\t")
fmt.Println(string(s))
switch payload.Type {
case PayloadTypeText:
_, err := matrixClient.SendText(id.RoomID(payload.RoomID), payload.Message)
@@ -78,6 +141,18 @@ func HandlePayloadPost(c *fiber.Ctx) error {
}
return fiber.ErrInternalServerError
}
case PayloadTypeMarkdown:
_, err := matrixClient.SendMessageEvent(
id.RoomID(payload.RoomID),
event.EventMessage,
format.RenderMarkdown(payload.Message, true, true),
)
if err != nil {
if httpErr, ok := err.(mautrix.HTTPError); ok {
return c.Status(httpErr.Response.StatusCode).SendString(httpErr.RespError.Err)
}
return fiber.ErrInternalServerError
}
case PayloadTypeNotice:
_, err := matrixClient.SendNotice(id.RoomID(payload.RoomID), payload.Message)
if err != nil {

48
nexmo.go Normal file
View File

@@ -0,0 +1,48 @@
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
)
type NexmoSMS struct {
ID string `json:"messageId"`
From string `json:"msisdn"`
Text string `json:"text"`
Type string `json:"type"`
}
func HandleNexmoSMSPost(c *fiber.Ctx) error {
payload := NexmoSMS{}
if err := c.BodyParser(&payload); err != nil {
fmt.Println("Failed to parse payload", err)
return err
}
msg := fmt.Sprintf(`
New Message from: %s
> %s`, payload.From, payload.Text)
fmt.Println(msg)
_, err := matrixClient.SendMessageEvent(
id.RoomID(getRoom("pCzzyaOZfCrorkdgOR:matrix.cluster.fun")),
event.EventMessage,
format.RenderMarkdown(msg, true, true),
)
if err != nil {
fmt.Println("Failed sending to Matrix", err)
if httpErr, ok := err.(mautrix.HTTPError); ok {
return c.Status(httpErr.Response.StatusCode).SendString(httpErr.RespError.Err)
}
return fiber.ErrInternalServerError
}
return c.SendStatus(fiber.StatusOK)
}

View File

@@ -2,17 +2,14 @@ package main
import (
"errors"
"fmt"
"strings"
"maunium.net/go/mautrix/id"
)
type PayloadType string
const (
PayloadTypeText PayloadType = "text"
PayloadTypeNotice PayloadType = "notice"
PayloadTypeText PayloadType = "text"
PayloadTypeMarkdown PayloadType = "markdown"
PayloadTypeNotice PayloadType = "notice"
)
type Payload struct {
@@ -38,45 +35,7 @@ func (p *Payload) Validate(defaultRoom *string) error {
return errors.New("'roomID' is required")
}
room := parseRoom(p.RoomID)
if strings.HasPrefix(room, "#") || !strings.HasPrefix("!", room) {
if !strings.HasPrefix(room, "#") {
room = fmt.Sprintf("#%s", room)
}
resp, err := matrixClient.ResolveAlias(id.RoomAlias(room))
if err == nil {
p.RoomID = resp.RoomID.String()
} else {
p.RoomID = strings.Replace(room, "#", "!", 1)
}
}
p.RoomID = getRoom(p.RoomID)
return nil
}
func parseRoom(room string) string {
prefix := ""
local := ""
domain := ""
parts := strings.Split(room, ":")
if len(parts) == 2 {
domain = parts[1]
} else {
domain = matrixClient.HomeserverURL.Host
}
if strings.HasPrefix(parts[0], "!") {
prefix = "!"
parts[0] = strings.TrimPrefix(parts[0], "!")
}
if strings.HasPrefix(parts[0], "#") {
prefix = "#"
parts[0] = strings.TrimPrefix(parts[0], "#")
}
local = parts[0]
return fmt.Sprintf("%s%s:%s", prefix, local, domain)
}

50
slack.go Normal file
View File

@@ -0,0 +1,50 @@
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/slack-go/slack"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
)
func HandleSlackPayloadPost(c *fiber.Ctx) error {
body := string(c.Body())
body = strings.ReplaceAll(body, "\n", "\\n")
payload := slack.WebhookMessage{}
if err := json.Unmarshal([]byte(body), &payload); err != nil {
fmt.Println("Failed to parse payload", string(c.Body()), err)
return err
}
fmt.Println("Got slack payload")
s, _ := json.MarshalIndent(payload, "", "\t")
fmt.Println(string(s))
room := payload.Channel
if room == "" {
room = c.Query("room", *defaultRoom)
}
_, err := matrixClient.SendMessageEvent(
id.RoomID(getRoom(room)),
event.EventMessage,
format.RenderMarkdown(payload.Text, true, true),
)
if err != nil {
fmt.Println("Failed sending to Matrix", err)
if httpErr, ok := err.(mautrix.HTTPError); ok {
return c.Status(httpErr.Response.StatusCode).SendString(httpErr.RespError.Err)
}
return fiber.ErrInternalServerError
}
return c.SendStatus(fiber.StatusOK)
}