From b5019cb42533813e52bab82c7c8dfd4acca8c643 Mon Sep 17 00:00:00 2001 From: Blagovest Petrov Date: Sun, 31 Aug 2025 15:55:00 +0300 Subject: [PATCH] refactor notificators and add matrix support --- README.md | 1 + config.ini-dist | 7 +++ emailsender/go.mod | 3 -- go.mod | 4 +- go.sum | 2 + main.go | 37 +++++++++++++- .../emailsender}/emailsender.go | 0 notificators/matrixsender/matrixsender.go | 48 +++++++++++++++++++ 8 files changed, 94 insertions(+), 8 deletions(-) delete mode 100644 emailsender/go.mod rename {emailsender => notificators/emailsender}/emailsender.go (100%) create mode 100644 notificators/matrixsender/matrixsender.go diff --git a/README.md b/README.md index ccd487b..f06aed0 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Resticara is a wrapper around [Restic](https://restic.net/), designed to simplif * Simplified Configuration: Uses config.ini for easy setup and configuration. * Syslog Integration: Logging to syslog is enabled by default for better traceability. * Email Notifications: Can be configured to send emails upon backup completion or failure. +* Matrix Notifications: Send backup status messages to a Matrix room. * Single Binary: Written in Go, Resticara is distributed as a single binary—making it extremely easy to deploy. * Systemd Timer Generation: Create and activate systemd timers with `resticara gentimer`. diff --git a/config.ini-dist b/config.ini-dist index 67acdd3..6fbbf08 100644 --- a/config.ini-dist +++ b/config.ini-dist @@ -12,6 +12,13 @@ enabled = false ;server = "mail.example.com" ;port = "587" +[matrix] +enabled = false +;server = "https://matrix.example.com" +;username = "@bot:example.com" +;pass = "#################" +;room_id = "!abcdefg:example.com" + [dir:website] bucket = b2:bucket:wpsites/ directory = /var/www diff --git a/emailsender/go.mod b/emailsender/go.mod deleted file mode 100644 index b93c382..0000000 --- a/emailsender/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module emailsender - -go 1.21 diff --git a/go.mod b/go.mod index f0c35c5..2f12378 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,8 @@ module resticara go 1.21 require ( - github.com/yourusername/resticara/emailsender v0.0.0-00010101000000-000000000000 + github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 gopkg.in/ini.v1 v1.67.0 ) require github.com/stretchr/testify v1.8.4 // indirect - -replace github.com/yourusername/resticara/emailsender => ./emailsender diff --git a/go.sum b/go.sum index 5566e31..debb50a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= +github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/main.go b/main.go index 2c859a5..918e4a7 100644 --- a/main.go +++ b/main.go @@ -30,8 +30,9 @@ import ( "text/template" "time" - "github.com/yourusername/resticara/emailsender" "gopkg.in/ini.v1" + "resticara/notificators/emailsender" + "resticara/notificators/matrixsender" ) type CommandInfo struct { @@ -59,6 +60,11 @@ type Config struct { HostID string RetentionPrune int SMTPEnabled bool + MatrixEnabled bool + MatrixServer string + MatrixUser string + MatrixPass string + MatrixRoomID string Commands map[string]map[string]string } @@ -86,13 +92,20 @@ func readConfig(file string) (Config, error) { config.To = cfg.Section("smtp").Key("to").String() config.SMTPServer = cfg.Section("smtp").Key("server").String() config.SMTPPort = cfg.Section("smtp").Key("port").String() + + config.MatrixEnabled = cfg.Section("matrix").Key("enabled").MustBool(false) + config.MatrixServer = cfg.Section("matrix").Key("server").String() + config.MatrixUser = cfg.Section("matrix").Key("username").String() + config.MatrixPass = cfg.Section("matrix").Key("pass").String() + config.MatrixRoomID = cfg.Section("matrix").Key("room_id").String() + config.HostID = cfg.Section("general").Key("hostID").String() config.RetentionPrune = cfg.Section("general").Key("retention_prune").MustInt(30) config.Commands = make(map[string]map[string]string) for _, section := range cfg.Sections() { - if section.Name() == "smtp" { + if section.Name() == "smtp" || section.Name() == "matrix" { continue } @@ -567,6 +580,26 @@ func main() { } else { fmt.Println("SMTP is disabled, not sending email.") } + + if config.MatrixEnabled { + matrixSender := matrixsender.GomatrixSender{} + message := mailData.StatusMessage + "---" + time.Now().Format(time.RFC1123) + "\n\n" + mailMessageBuffer.String() + matrixConfig := matrixsender.MatrixConfig{ + Homeserver: config.MatrixServer, + Username: config.MatrixUser, + Password: config.MatrixPass, + RoomID: config.MatrixRoomID, + Message: message, + } + + if err := matrixSender.Send(matrixConfig); err != nil { + fmt.Println(err) + } else { + fmt.Println("Matrix message sent!") + } + } else { + fmt.Println("Matrix is disabled, not sending message.") + } case "prune": if len(args) < 2 { fmt.Println("Usage: resticara prune ") diff --git a/emailsender/emailsender.go b/notificators/emailsender/emailsender.go similarity index 100% rename from emailsender/emailsender.go rename to notificators/emailsender/emailsender.go diff --git a/notificators/matrixsender/matrixsender.go b/notificators/matrixsender/matrixsender.go new file mode 100644 index 0000000..4b83f38 --- /dev/null +++ b/notificators/matrixsender/matrixsender.go @@ -0,0 +1,48 @@ +package matrixsender + +import ( + "fmt" + + "github.com/matrix-org/gomatrix" +) + +type MatrixConfig struct { + Homeserver string + Username string + Password string + RoomID string + Message string +} + +type MatrixSender interface { + Send(cfg MatrixConfig) error +} + +type GomatrixSender struct{} + +func (s GomatrixSender) Send(cfg MatrixConfig) error { + cli, err := gomatrix.NewClient(cfg.Homeserver, "", "") + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + resp, err := cli.Login(&gomatrix.ReqLogin{ + Type: "m.login.password", + User: cfg.Username, + Password: cfg.Password, + }) + if err != nil { + return fmt.Errorf("failed to login: %w", err) + } + + cli.SetCredentials(resp.UserID, resp.AccessToken) + + if _, err := cli.JoinRoom(cfg.RoomID, "", nil); err != nil { + // attempt to continue even if already joined + } + + if _, err := cli.SendText(cfg.RoomID, cfg.Message); err != nil { + return fmt.Errorf("failed to send message: %w", err) + } + return nil +}