Initial version

This commit is contained in:
Torjus Håkestad 2024-12-03 00:47:40 +01:00
parent 328790b281
commit 686a322f00
Signed by: torjus
SSH Key Fingerprint: SHA256:KjAds8wHfD2mBYK2H815s/+ABcSdcIHUndwHEdSxml4
6 changed files with 224 additions and 3 deletions

76
bus/bus.go Normal file
View File

@ -0,0 +1,76 @@
package bus
import (
"time"
"github.com/godbus/dbus/v5"
)
type NotifyBus struct {
conn *dbus.Conn
}
type BusNotification struct {
ID uint32
Summary string
Body string
Timeout time.Duration
}
type NotifyServerInfo struct {
Name string
Vendor string
Version string
SpecVersion string
}
func NewNotifyBus() (*NotifyBus, error) {
conn, err := dbus.ConnectSessionBus()
if err != nil {
return nil, err
}
return &NotifyBus{conn: conn}, nil
}
func (n *NotifyBus) Close() {
n.conn.Close()
}
func (n *NotifyBus) ServerInfo() (*NotifyServerInfo, error) {
obj := n.conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
call := obj.Call(
"org.freedesktop.Notifications.GetServerInformation", // Method
0, // Flags
)
if call.Err != nil {
return nil, call.Err
}
srvInfo := &NotifyServerInfo{}
call.Store(&srvInfo.Name, &srvInfo.Vendor, &srvInfo.Version, &srvInfo.SpecVersion)
return srvInfo, nil
}
func (n *NotifyBus) Notify(notification BusNotification) (uint32, error) {
obj := n.conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
var ret uint32
call := obj.Call(
"org.freedesktop.Notifications.Notify", // Method
0, // Flags
"alerttonotify", // App name
notification.ID, // Notification ID
"", // Icon
notification.Summary, // Summary
notification.Body, // Body
[]string{}, // Actions
map[string]dbus.Variant{}, // Hints
int32(notification.Timeout.Milliseconds()), // Timeout
)
if call.Err != nil {
return ret, call.Err
}
call.Store(&ret)
return ret, nil
}

View File

@ -48,7 +48,7 @@
version = version; version = version;
pname = "alerttonotify"; pname = "alerttonotify";
src = src; src = src;
vendorHash = pkgs.lib.fakeHash; vendorHash = "sha256-1ejnJykXY+j/xddFupLII2SXGsbxBSUaAe20YpcMzgQ=";
}; };
} }
); );

2
go.mod
View File

@ -1,3 +1,5 @@
module git.t-juice.club/torjus/alerttonotify module git.t-juice.club/torjus/alerttonotify
go 1.23.3 go 1.23.3
require github.com/godbus/dbus/v5 v5.1.0 // indirect

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=

54
main.go
View File

@ -1,9 +1,59 @@
package main package main
import "fmt" import (
"context"
"log/slog"
"net/http"
"os"
"os/signal"
"git.t-juice.club/torjus/alerttonotify/bus"
"git.t-juice.club/torjus/alerttonotify/server"
)
const Version = "v0.1.0" const Version = "v0.1.0"
func main() { func main() {
fmt.Println("Hello!") // Setup logging
logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
logger.Info("Starting alerttonotify", "version", Version)
// Setup dbus connection
nbus, err := bus.NewNotifyBus()
if err != nil {
logger.Error("Failed to create notify bus", "error", err)
os.Exit(1)
}
defer nbus.Close()
// Verify connection and server
info, err := nbus.ServerInfo()
if err != nil {
logger.Error("Failed to get notification server info", "error", err)
os.Exit(1)
return
}
logger.Info("Connected to notification daemon", "server", info.Name, "version", info.Version)
shutdownCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
// Setup http server
srv := server.NewServer(nbus, logger)
srv.Addr = ":5001"
// Listen for shutdown signal
go func() {
<-shutdownCtx.Done()
srv.Shutdown(context.Background())
}()
logger.Info("Starting http server", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error("Failed to start http server", "error", err)
os.Exit(1)
}
logger.Info("Shutting down")
} }

91
server/server.go Normal file
View File

@ -0,0 +1,91 @@
package server
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"strings"
"time"
"git.t-juice.club/torjus/alerttonotify/bus"
)
type Server struct {
logger *slog.Logger
nbus *bus.NotifyBus
http.Server
}
type Alert struct {
Status string `json:"status"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
GeneratorURL string `json:"generatorURL"`
Fingerprint string `json:"fingerprint"`
}
type AlertMessage struct {
Version string `json:"version"`
GroupKey string `json:"groupKey"`
TruncatedAlerts int `json:"truncatedAlerts"`
Status string `json:"status"`
Receiver string `json:"receiver"`
GroupLabels map[string]string `json:"groupLabels"`
CommonLabels map[string]string `json:"commonLabels"`
CommonAnnotations map[string]string `json:"commonAnnotations"`
ExternalURL string `json:"externalURL"`
Alerts []Alert `json:"alerts"`
}
func NewServer(nbus *bus.NotifyBus, logger *slog.Logger) *Server {
srv := &Server{
nbus: nbus,
logger: logger,
}
mux := http.NewServeMux()
mux.HandleFunc("GET /", srv.handleIndex)
mux.HandleFunc("POST /alert", srv.handleAlert)
srv.Handler = mux
return srv
}
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
s.logger.Info("index page")
w.Write([]byte("Hello!"))
}
func (s *Server) handleAlert(w http.ResponseWriter, r *http.Request) {
s.logger.Debug("Got new alert", "remoteAddr", r.RemoteAddr)
decoder := json.NewDecoder(r.Body)
var alertMessage AlertMessage
if err := decoder.Decode(&alertMessage); err != nil {
s.logger.Error("Failed to decode alert message", "error", err)
w.WriteHeader(http.StatusBadRequest)
return
}
s.logger.Debug("Decoded alert message", "alert", alertMessage)
s.logger.Info("Incoming message", "status", alertMessage.Status)
var sb strings.Builder
for _, alert := range alertMessage.Alerts {
s.logger.Debug("Incoming alert", "status", alert.Status, "fingerprint", alert.Fingerprint)
if summary, ok := alert.Annotations["summary"]; ok {
sb.WriteString(fmt.Sprintf("[%s] %s\n", strings.ToUpper(alert.Status), summary))
} else {
sb.WriteString(fmt.Sprintf("[%s]: %s\n", strings.ToUpper(alert.Status), alert.Fingerprint))
}
}
notification := bus.BusNotification{
Summary: fmt.Sprintf("%d alerts %s", len(alertMessage.Alerts), alertMessage.Status),
Body: sb.String(),
}
s.logger.Debug("Sending notification", "notification", notification)
s.nbus.Notify(notification)
}