First Commit

This commit is contained in:
Simon Rieger 2023-11-23 14:04:44 +01:00
commit a76f6bfb0c
13 changed files with 452 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/mail-reminder.iml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mail-reminder.iml" filepath="$PROJECT_DIR$/.idea/mail-reminder.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

30
Dockerfile Normal file
View file

@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1
# Build the application from source
FROM golang:1.21.4 AS build-stage
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-mail-reminder
# Run the tests in the container
FROM build-stage AS run-test-stage
RUN go test -v ./...
# Deploy the application binary into a lean image
FROM gcr.io/distroless/base-debian11 AS build-release-stage
WORKDIR /
COPY --from=build-stage /docker-mail-reminder /docker-mail-reminder
#EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/docker-mail-reminder"]

58
README.md Normal file
View file

@ -0,0 +1,58 @@
# Your Go Application
This is a simple Go application that does iCalendar files (.ics) analyzes and sends notifications by email based on the events in these iCalendar files.
## Prerequisites
Before you begin, ensure you have the following installed:
- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose](https://docs.docker.com/compose/install/)
## Getting Started
1. **Clone the repository:**
```bash
git clone <repository-url>
```
2. **Create an environment variables file:**
Create a file named `.env` in your project directory. Fill this file with the required environment variables:
```env
FROM_EMAIL=user@example.com
TO_EMAIL=recipient@example.com
EMAIL_PASSWORD=your_password
SMTP_HOST=smtp.example.com
SMTP_PORT=587
ICS_DIR=/path/to/ics/files
```
Customize the values according to your application.
3. **Build and run the Docker container:**
```bash
docker-compose up --build
```
This command uses Docker Compose to build and run the container, loading environment variables from the `.env` file.
4. **Access your application:**
Open your web browser and go to [http://localhost:8080](http://localhost:8080).
## Customizing the Docker Image
- If your application uses additional environment variables, add them to the `.env` file.
- Customize the Dockerfile or docker-compose.yml if needed.
## Contributing
If you'd like to contribute, please fork the repository and create a pull request. Feel free to open an issue if you encounter any problems or have suggestions.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

3
cron-job.sh Normal file
View file

@ -0,0 +1,3 @@
#!/bin/sh
echo "0 6 * * * /usr/local/bin/docker-compose -f /opt/containers/mail-reminder/docker-compose.yml up --build --exit-code-from go-app" > /etc/crontabs/root
crond -f -l 8

15
docker-compose.yml Normal file
View file

@ -0,0 +1,15 @@
version: '3'
services:
go-app:
build:
context: .
env_file: .env
volumes:
- /opt/containers/mailu/dav/collection-root:/dav:ro
cron:
image: alpine:latest
volumes:
- ./cron-job.sh:/etc/periodic/daily/cron-job
command: ["crond", "-f", "-d", "8"]

16
go.mod Normal file
View file

@ -0,0 +1,16 @@
module mail-reminder
go 1.21
require (
github.com/apognu/gocal v0.9.0
github.com/sirupsen/logrus v1.9.3
gopkg.in/mail.v2 v2.3.1
)
require (
github.com/ChannelMeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
)

27
go.sum Normal file
View file

@ -0,0 +1,27 @@
github.com/ChannelMeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 h1:N5Vqww5QISEHsWHOWDEx4PzdIay3Cg0Jp7zItq2ZAro=
github.com/ChannelMeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61/go.mod h1:GnKXcK+7DYNy/8w2Ex//Uql4IgfaU82Cd5rWKb7ah00=
github.com/apognu/gocal v0.9.0 h1:2lGdZprjYs9A6l1RTEmapmpE1PiDbXNX8bUVqZt3vm4=
github.com/apognu/gocal v0.9.0/go.mod h1:ZOJfNOqpz8aasi3uqzDu+eWTT6VuEa/TvQWiYYWlb80=
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 h1:o64h9XF42kVEUuhuer2ehqrlX8rZmvQSU0+Vpj1rF6Q=
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61/go.mod h1:Rp8e0DCtEKwXFOC6JPJQVTz8tuGoGvw6Xfexggh/ed0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

49
ics/subdir/test.ics Normal file
View file

@ -0,0 +1,49 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ical.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20230407T050750Z
TZURL:https://www.tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739536627-74911@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231123T120000
DTEND;TZID=Europe/Berlin:20231123T120000
SUMMARY:test1
DESCRIPTION:test1
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739551336-72964@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231123T120000
DTEND;TZID=Europe/Berlin:20231124T120000
SUMMARY:test2
DESCRIPTION:test2
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739565150-71554@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231124T120000
DTEND;TZID=Europe/Berlin:20231125T120000
SUMMARY:test3
DESCRIPTION:test3
END:VEVENT
END:VCALENDAR

49
ics/test.ics Normal file
View file

@ -0,0 +1,49 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ical.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20230407T050750Z
TZURL:https://www.tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739536627-74911@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231123T120000
DTEND;TZID=Europe/Berlin:20231123T120000
SUMMARY:test1
DESCRIPTION:test1
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739551336-72964@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231123T120000
DTEND;TZID=Europe/Berlin:20231124T120000
SUMMARY:test2
DESCRIPTION:test2
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20231123T113949Z
UID:1700739565150-71554@ical.marudot.com
DTSTART;TZID=Europe/Berlin:20231124T120000
DTEND;TZID=Europe/Berlin:20231125T120000
SUMMARY:test3
DESCRIPTION:test3
END:VEVENT
END:VCALENDAR

174
main.go Normal file
View file

@ -0,0 +1,174 @@
package main
import (
"crypto/tls"
"fmt"
"github.com/apognu/gocal"
log "github.com/sirupsen/logrus"
gomail "gopkg.in/mail.v2"
"os"
"strconv"
"strings"
"time"
)
var (
fromString string
toAddresses []string
passwordString string
hostString string
hostPortString int
)
func main() {
// Configure logrus
log.SetFormatter(&log.TextFormatter{})
log.SetOutput(os.Stdout)
log.SetLevel(log.InfoLevel)
log.Info("Hello World!")
// Initialisiere err mit einem Fehlerwert
var err error
// Read environment variables
fromString = os.Getenv("FROM_EMAIL")
toAddresses = strings.Split(os.Getenv("TO_EMAIL"), ",")
passwordString = os.Getenv("EMAIL_PASSWORD")
hostString = os.Getenv("SMTP_HOST")
hostPortString, err = strconv.Atoi(os.Getenv("SMTP_PORT"))
// Überprüfung auf Fehler
if err != nil {
fmt.Println("Fehler bei der Umwandlung. Verwende Standardwert 587.")
// Setze einen Standardwert, zum Beispiel 0
hostPortString = 587
}
// Get the current working directory
dir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
log.Infof("Current Working Directory is = %s", dir)
// Read and split multiple folders
folderList := os.Getenv("ICS_DIR")
if folderList == "" || fromString == "" || len(toAddresses) == 0 || passwordString == "" || hostString == "" {
log.Fatal("Es fehlen noch einige Parameter!!!\nICS_DIR, FROM_EMAIL, TO_EMAIL, EMAIL_PASSWORD, SMTP_HOST")
}
folders := strings.Split(folderList, ",")
for _, folder := range folders {
log.Infof("folder: %s from: %s to: %s password: %s host: %s", folder, fromString, toAddresses, passwordString, hostString)
listFilesForFolder(folder)
}
}
func listFilesForFolder(folder string) {
// Öffne das Verzeichnis
dir, err := os.Open(folder)
if err != nil {
log.Fatal("Fehler beim Öffnen des Ordners:", err)
return
}
defer dir.Close()
// Lies alle Dateien im Verzeichnis
dateien, err := dir.Readdir(0)
if err != nil {
log.Fatal("Fehler beim Lesen des Verzeichnisses:", err)
return
}
// Durchlaufe die Liste der Dateien und gebe ihre Namen aus
for _, datei := range dateien {
// Überprüfe, ob es sich um ein Verzeichnis handelt. Wenn ja, ignoriere es.
if datei.IsDir() {
continue
}
// Hier kannst du die Dateinamen ausgeben oder damit arbeiten.
log.Println(datei.Name())
getNotifications(folder + "/" + datei.Name())
}
}
func getNotifications(file string) {
f, err := os.Open(file)
if err != nil {
log.Fatal(err)
}
defer f.Close()
var tzMapping = map[string]string{
"My Super Zone": "Europe/Berlin",
}
gocal.SetTZMapper(func(s string) (*time.Location, error) {
if tzid, ok := tzMapping[s]; ok {
return time.LoadLocation(tzid)
}
return nil, fmt.Errorf("")
})
start, end := truncateToDay(time.Now()), truncateToDay(time.Now()).Add(24*60*time.Minute)
c := gocal.NewParser(f)
c.Start, c.End = &start, &end
c.Parse()
for _, e := range c.Events {
log.Infof("%s on %s", e.Summary, e.Start)
messageSubject := fmt.Sprintf("Es existiert für heute ein neuer Kalendereintrag Namens: %s", e.Summary)
log.Println(messageSubject)
messageText := fmt.Sprintf("Der Termin beginnt heute um: %s und endet um: %s.", e.Start, e.End)
if len(e.Location) != 0 {
messageText += fmt.Sprintf("\n\nEr findet in %s statt.", e.Location)
}
if len(e.Description) != 0 {
messageText += fmt.Sprintf("\n\nFolgende Notiz existiert in diesen Eintrag: \n%s", e.Description)
}
messageText += "\n\n This email is a service from mail-reminder Version 1.0 written in Golang. \n Delivered by Simon Rieger"
for _, toAddress := range toAddresses {
sendMail(messageSubject, messageText, toAddress)
}
}
}
func truncateToDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
func sendMail(messageSubject string, messageText string, toAddress string) {
m := gomail.NewMessage()
// Set E-Mail sender
m.SetHeader("From", fromString)
// Set E-Mail receivers
m.SetHeader("To", toAddress)
// Set E-Mail subject
m.SetHeader("Subject", messageSubject)
// Set E-Mail body. You can set plain text or html with text/html
m.SetBody("text/plain", messageText)
// Settings for SMTP server
d := gomail.NewDialer(hostString, hostPortString, fromString, passwordString)
// This is only needed when SSL/TLS certificate is not valid on server.
// In production this should be set to false.
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
// Now send E-Mail
if err := d.DialAndSend(m); err != nil {
log.Fatal(err)
panic(err)
}
log.Infof("Email Message Sent Successfully")
}