From 128fdc5f85d6b717310765f9f819d1679b5e161c Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sun, 25 Aug 2024 21:48:47 +0200 Subject: [PATCH] Implement folder mail notifier --- .gitignore | 2 + go.mod | 13 +++++++ go.sum | 8 ++++ main.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ watcher.go | 59 ++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 watcher.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba49162 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +test +main.log diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..316b29c --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module folder-mail-notifier + +go 1.23.0 + +require ( + github.com/joho/godotenv v1.5.1 + gopkg.in/fsnotify.v1 v1.4.7 +) + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + golang.org/x/sys v0.24.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..71fc8e6 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/main.go b/main.go new file mode 100644 index 0000000..03b9983 --- /dev/null +++ b/main.go @@ -0,0 +1,111 @@ +package main + +import ( + _ "embed" + "fmt" + "io" + "log" + "net/smtp" + "os" + "strings" + "time" + + "github.com/joho/godotenv" + "gopkg.in/fsnotify.v1" +) + +var Error *log.Logger +var Warning *log.Logger + +func init() { + log.SetFlags(log.Lmicroseconds | log.Lshortfile) + logFile, err := os.Create("main.log") + if err != nil { + log.Printf("Error creating log file: %v", err) + os.Exit(1) + } + logger := io.MultiWriter(os.Stdout, logFile) + log.SetOutput(logger) + + Error = log.New(io.MultiWriter(logFile, os.Stderr, os.Stdout), + fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) + Warning = log.New(io.MultiWriter(logFile, os.Stdout), + fmt.Sprintf("%sWarning:%s ", "\033[0;93m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) +} + +//go:embed .env +var env string +var envvar map[string]string +var queued bool +var changes = []*fsnotify.Event{} + +func main() { + var err error + envvar, err = godotenv.Parse(strings.NewReader(env)) + if err != nil { + Error.Fatalf("Error parsing .env file: %v", err) + return + } + + root, ok := envvar["ROOT"] + if !ok { + Error.Fatalf("Error parsing ROOT variable: %v", err) + return + } + + WatchRecursively(root, func(event fsnotify.Event) { + if !queued { + // time.AfterFunc(5*time.Minute, func() { doNotify() }) + delay := 5*time.Second + log.Printf("Mailing changes in %v seconds", delay.Seconds()) + time.AfterFunc(delay, func() { doNotify() }) + queued = true + } + changes = append(changes, &event) + }) +} + +func doNotify() error { + defer func() { queued = false }() + + smtpHost, ok := envvar["SMTP_HOST"] + if !ok { + return fmt.Errorf("error getting env var SMTP_HOST") + } + smtpPort, ok := envvar["SMTP_PORT"] + if !ok { + return fmt.Errorf("error getting env var SMTP_PORT") + } + smtpUser, ok := envvar["SMTP_USER"] + if !ok { + return fmt.Errorf("error getting env var SMTP_USER") + } + smtpPass, ok := envvar["SMTP_PASSWORD"] + if !ok { + return fmt.Errorf("error getting env var SMTP_PASSWORD") + } + log.Printf("Mailing %v changes", len(changes)) + strchanges := make([]string, 0, len(changes)) + for _, change := range changes { + strchanges = append(strchanges, change.String()) + } + + from := "admin@quack-lab.dev" + to := []string{"david.majdandzic@hotmail.com"} + subject := "New files:" + body := strings.Join(strchanges, "\n") + + msg := []byte(fmt.Sprintf("Subject: %s\nFrom: %s\nTo: %s\n\n%s", subject, from, strings.Join(to, ","), body)) + + auth := smtp.PlainAuth("", smtpUser, smtpPass, smtpHost) + err := smtp.SendMail(smtpHost+":"+smtpPort, auth, from, to, msg) + if err != nil { + return err + } + log.Println("Sent mail") + changes = []*fsnotify.Event{} + + return nil +} diff --git a/watcher.go b/watcher.go new file mode 100644 index 0000000..0ab34b3 --- /dev/null +++ b/watcher.go @@ -0,0 +1,59 @@ +package main + +import ( + "os" + "path/filepath" + + "gopkg.in/fsnotify.v1" +) + +func WatchRecursively(root string, changeHandler func(fsnotify.Event)) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + defer watcher.Close() + + done := make(chan bool) + + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + changeHandler(event) + case err, ok := <-watcher.Errors: + if !ok { + return + } + Error.Printf("error with watcher: %v", err) + } + } + }() + + dirs := 0 + err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Name() == ".git" { + return filepath.SkipDir + } + if info.IsDir() { + err = watcher.Add(path) + dirs++ + if err != nil { + return err + } + } + return nil + }) + if err != nil { + return err + } + + <-done + return nil +}