imap daemon
This commit is contained in:
parent
5636958dc9
commit
c031bef006
|
@ -0,0 +1,79 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"git.ewintr.nl/go-kit/log"
|
||||
"git.ewintr.nl/gte/internal/process"
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
"git.ewintr.nl/gte/pkg/msend"
|
||||
"git.ewintr.nl/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := log.New(os.Stdout)
|
||||
logger.Info("started")
|
||||
|
||||
msgStore := mstore.NewIMAP(&mstore.IMAPConfig{
|
||||
IMAPURL: os.Getenv("IMAP_URL"),
|
||||
IMAPUsername: os.Getenv("IMAP_USERNAME"),
|
||||
IMAPPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
})
|
||||
msgSender := msend.NewSSLSMTP(&msend.SSLSMTPConfig{
|
||||
URL: os.Getenv("SMTP_URL"),
|
||||
Username: os.Getenv("SMTP_USERNAME"),
|
||||
Password: os.Getenv("SMTP_PASSWORD"),
|
||||
From: os.Getenv("SMTP_FROM"),
|
||||
To: os.Getenv("SMTP_TO"),
|
||||
})
|
||||
repo := task.NewRepository(msgStore)
|
||||
disp := task.NewDispatcher(msgSender)
|
||||
|
||||
inboxProc := process.NewInbox(repo)
|
||||
recurProc := process.NewRecur(repo, disp, 6)
|
||||
|
||||
go Run(inboxProc, recurProc, logger)
|
||||
|
||||
done := make(chan os.Signal)
|
||||
signal.Notify(done, os.Interrupt)
|
||||
<-done
|
||||
logger.Info("stopped")
|
||||
}
|
||||
|
||||
func Run(inboxProc *process.Inbox, recurProc *process.Recur, logger log.Logger) {
|
||||
logger = logger.WithField("func", "run")
|
||||
inboxTicker := time.NewTicker(10 * time.Second)
|
||||
recurTicker := time.NewTicker(time.Hour)
|
||||
oldToday := task.Today
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-inboxTicker.C:
|
||||
result, err := inboxProc.Process()
|
||||
if err != nil {
|
||||
logger.WithErr(err).Error("failed processing inbox")
|
||||
|
||||
continue
|
||||
}
|
||||
logger.WithField("count", result.Count).Info("finished processing inbox")
|
||||
case <-recurTicker.C:
|
||||
year, month, day := time.Now().Date()
|
||||
newToday := task.NewDate(year, int(month), day)
|
||||
if oldToday.Equal(newToday) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
oldToday = newToday
|
||||
result, err := recurProc.Process()
|
||||
if err != nil {
|
||||
logger.WithErr(err).Error("failed generating recurring tasks")
|
||||
|
||||
continue
|
||||
}
|
||||
logger.WithField("count", result.Count).Info("finished generating recurring tasks")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.ewintr.nl/go-kit/log"
|
||||
"git.ewintr.nl/gte/internal/process"
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
"git.ewintr.nl/gte/pkg/msend"
|
||||
"git.ewintr.nl/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := &mstore.ImapConfiguration{
|
||||
ImapUrl: os.Getenv("IMAP_URL"),
|
||||
ImapUsername: os.Getenv("IMAP_USERNAME"),
|
||||
ImapPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
logger := log.New(os.Stdout).WithField("cmd", "generate-recurring")
|
||||
IMAPConfig := &mstore.IMAPConfig{
|
||||
IMAPURL: os.Getenv("IMAP_URL"),
|
||||
IMAPUsername: os.Getenv("IMAP_USERNAME"),
|
||||
IMAPPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
}
|
||||
if !config.Valid() {
|
||||
log.Fatal("please set IMAP_USER, IMAP_PASSWORD, etc environment variables")
|
||||
msgStore := mstore.NewIMAP(IMAPConfig)
|
||||
|
||||
SMTPConfig := &msend.SSLSMTPConfig{
|
||||
URL: os.Getenv("SMTP_URL"),
|
||||
Username: os.Getenv("SMTP_USERNAME"),
|
||||
Password: os.Getenv("SMTP_PASSWORD"),
|
||||
From: os.Getenv("SMTP_FROM"),
|
||||
To: os.Getenv("SMTP_TO"),
|
||||
}
|
||||
if !SMTPConfig.Valid() {
|
||||
logger.Error("please set SMTP_URL, SMTP_USERNAME, etc environment variables")
|
||||
os.Exit(1)
|
||||
}
|
||||
mailSend := msend.NewSSLSMTP(SMTPConfig)
|
||||
|
||||
daysAhead, err := strconv.Atoi(os.Getenv("GTE_DAYS_AHEAD"))
|
||||
if err != nil {
|
||||
daysAhead = 0
|
||||
}
|
||||
|
||||
mailStore, err := mstore.ImapConnect(config)
|
||||
taskRepo := task.NewRepository(msgStore)
|
||||
taskDisp := task.NewDispatcher(mailSend)
|
||||
|
||||
recur := process.NewRecur(taskRepo, taskDisp, daysAhead)
|
||||
result, err := recur.Process()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer mailStore.Disconnect()
|
||||
|
||||
taskRepo := task.NewRepository(mailStore)
|
||||
tasks, err := taskRepo.FindAll(task.FOLDER_RECURRING)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rDate := task.Today.AddDays(daysAhead)
|
||||
for _, t := range tasks {
|
||||
if t.RecursOn(rDate) {
|
||||
subject, body, err := t.CreateDueMessage(rDate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := mailStore.Add(task.FOLDER_PLANNED, subject, body); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.WithErr(err).Error("unable to process recurring")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger.WithField("count", result.Count).Info("finished generating recurring tasks")
|
||||
}
|
||||
|
|
|
@ -1,46 +1,28 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.ewintr.nl/go-kit/log"
|
||||
"git.ewintr.nl/gte/internal/process"
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
"git.ewintr.nl/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := &mstore.ImapConfiguration{
|
||||
ImapUrl: os.Getenv("IMAP_URL"),
|
||||
ImapUsername: os.Getenv("IMAP_USERNAME"),
|
||||
ImapPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
}
|
||||
if !config.Valid() {
|
||||
log.Fatal("please set IMAP_USER, IMAP_PASSWORD, etc environment variables")
|
||||
logger := log.New(os.Stdout).WithField("cmd", "process-inbox")
|
||||
config := &mstore.IMAPConfig{
|
||||
IMAPURL: os.Getenv("IMAP_URL"),
|
||||
IMAPUsername: os.Getenv("IMAP_USERNAME"),
|
||||
IMAPPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
}
|
||||
msgStore := mstore.NewIMAP(config)
|
||||
|
||||
mailStore, err := mstore.ImapConnect(config)
|
||||
inboxProcessor := process.NewInbox(task.NewRepository(msgStore))
|
||||
result, err := inboxProcessor.Process()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer mailStore.Disconnect()
|
||||
|
||||
taskRepo := task.NewRepository(mailStore)
|
||||
tasks, err := taskRepo.FindAll(task.FOLDER_INBOX)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var cleanupNeeded bool
|
||||
for _, t := range tasks {
|
||||
if t.Dirty {
|
||||
if err := taskRepo.Update(t); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cleanupNeeded = true
|
||||
}
|
||||
}
|
||||
if cleanupNeeded {
|
||||
if err := taskRepo.CleanUp(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logger.WithErr(err).Error("unable to process inbox")
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.WithField("count", result.Count).Info("finished processing inbox")
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module git.ewintr.nl/gte
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
git.ewintr.nl/go-kit v0.0.0-20210509123609-19e474005502
|
||||
git.ewintr.nl/go-kit v0.0.0-20210513091124-da7006c2c242
|
||||
github.com/emersion/go-imap v1.1.0
|
||||
github.com/emersion/go-message v0.14.1
|
||||
github.com/google/uuid v1.2.0
|
||||
|
|
12
go.sum
12
go.sum
|
@ -1,7 +1,9 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210509123609-19e474005502 h1:h3PYDz6uWAz2mBPOFShhkZv7SapfLWXcBDsmHqFCq5w=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210509123609-19e474005502/go.mod h1:eYANz1nepfc6lHxa9UcNZFvLaezBrihttH/Pdc5+0Vk=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210513084754-6c0524f3de86 h1:jVP4muIBqQ5poAuOlDgW/PhYscSnRHdqEqX1WfAK++A=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210513084754-6c0524f3de86/go.mod h1:eYANz1nepfc6lHxa9UcNZFvLaezBrihttH/Pdc5+0Vk=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210513091124-da7006c2c242 h1:9UIbgqTOIot2rAXqVWhd0Q9MyWru1kIzr52HPWpCCTM=
|
||||
git.ewintr.nl/go-kit v0.0.0-20210513091124-da7006c2c242/go.mod h1:eYANz1nepfc6lHxa9UcNZFvLaezBrihttH/Pdc5+0Vk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
|
@ -65,11 +67,14 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
|
@ -224,6 +229,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
|
@ -237,7 +243,6 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -310,6 +315,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInboxProcess = errors.New("could not process inbox")
|
||||
)
|
||||
|
||||
type Inbox struct {
|
||||
taskRepo *task.TaskRepo
|
||||
}
|
||||
|
||||
type InboxResult struct {
|
||||
Count int
|
||||
}
|
||||
|
||||
func NewInbox(repo *task.TaskRepo) *Inbox {
|
||||
return &Inbox{
|
||||
taskRepo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (inbox *Inbox) Process() (*InboxResult, error) {
|
||||
tasks, err := inbox.taskRepo.FindAll(task.FOLDER_INBOX)
|
||||
if err != nil {
|
||||
return &InboxResult{}, fmt.Errorf("%w: %v", ErrInboxProcess, err)
|
||||
}
|
||||
|
||||
var cleanupNeeded bool
|
||||
for _, t := range tasks {
|
||||
if t.Dirty {
|
||||
if err := inbox.taskRepo.Update(t); err != nil {
|
||||
return &InboxResult{}, fmt.Errorf("%w: %v", ErrInboxProcess, err)
|
||||
}
|
||||
cleanupNeeded = true
|
||||
}
|
||||
}
|
||||
if cleanupNeeded {
|
||||
if err := inbox.taskRepo.CleanUp(); err != nil {
|
||||
return &InboxResult{}, fmt.Errorf("%w: %v", ErrInboxProcess, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &InboxResult{
|
||||
Count: len(tasks),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package process_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.ewintr.nl/go-kit/test"
|
||||
"git.ewintr.nl/gte/internal/process"
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
"git.ewintr.nl/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func TestInboxProcess(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
messages map[string][]*mstore.Message
|
||||
expResult *process.InboxResult
|
||||
expMsgs map[string][]*mstore.Message
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
messages: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {},
|
||||
},
|
||||
expResult: &process.InboxResult{},
|
||||
expMsgs: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all flavors",
|
||||
messages: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {
|
||||
{
|
||||
Subject: "to new",
|
||||
},
|
||||
{
|
||||
Subject: "to recurring",
|
||||
Body: "recur: 2021-05-14, daily\nid: xxx-xxx\nversion: 1",
|
||||
},
|
||||
{
|
||||
Subject: "to planned",
|
||||
Body: "due: 2021-05-14\nid: xxx-xxx\nversion: 1",
|
||||
},
|
||||
{
|
||||
Subject: "to unplanned",
|
||||
Body: "id: xxx-xxx\nversion: 1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expResult: &process.InboxResult{
|
||||
Count: 4,
|
||||
},
|
||||
expMsgs: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {},
|
||||
task.FOLDER_NEW: {{Subject: "to new"}},
|
||||
task.FOLDER_RECURRING: {{Subject: "to recurring"}},
|
||||
task.FOLDER_PLANNED: {{Subject: "2021-05-14 (friday) - to planned"}},
|
||||
task.FOLDER_UNPLANNED: {{Subject: "to unplanned"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cleanup",
|
||||
messages: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {{
|
||||
Subject: "new version",
|
||||
Body: "id: xxx-xxx\nversion: 3",
|
||||
}},
|
||||
task.FOLDER_UNPLANNED: {{
|
||||
Subject: "old version",
|
||||
Body: "id: xxx-xxx\nversion: 3",
|
||||
}},
|
||||
},
|
||||
expResult: &process.InboxResult{
|
||||
Count: 1,
|
||||
},
|
||||
expMsgs: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {},
|
||||
task.FOLDER_UNPLANNED: {{Subject: "new version"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cleanup version conflict",
|
||||
messages: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {{
|
||||
Subject: "new version",
|
||||
Body: "id: xxx-xxx\nversion: 3",
|
||||
}},
|
||||
task.FOLDER_UNPLANNED: {{
|
||||
Subject: "not really old version",
|
||||
Body: "id: xxx-xxx\nversion: 5",
|
||||
}},
|
||||
},
|
||||
expResult: &process.InboxResult{
|
||||
Count: 1,
|
||||
},
|
||||
expMsgs: map[string][]*mstore.Message{
|
||||
task.FOLDER_INBOX: {},
|
||||
task.FOLDER_UNPLANNED: {{Subject: "not really old version"}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mstorer, err := mstore.NewMemory([]string{
|
||||
task.FOLDER_INBOX,
|
||||
task.FOLDER_NEW,
|
||||
task.FOLDER_RECURRING,
|
||||
task.FOLDER_PLANNED,
|
||||
task.FOLDER_UNPLANNED,
|
||||
})
|
||||
test.OK(t, err)
|
||||
for folder, messages := range tc.messages {
|
||||
for _, m := range messages {
|
||||
test.OK(t, mstorer.Add(folder, m.Subject, m.Body))
|
||||
}
|
||||
}
|
||||
|
||||
inboxProc := process.NewInbox(task.NewRepository(mstorer))
|
||||
actResult, err := inboxProc.Process()
|
||||
|
||||
test.OK(t, err)
|
||||
test.Equals(t, tc.expResult, actResult)
|
||||
for folder, expMessages := range tc.expMsgs {
|
||||
actMessages, err := mstorer.Messages(folder)
|
||||
test.OK(t, err)
|
||||
test.Equals(t, len(expMessages), len(actMessages))
|
||||
if len(expMessages) == 0 {
|
||||
|
||||
continue
|
||||
}
|
||||
test.Equals(t, expMessages[0].Subject, actMessages[0].Subject)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRecurProcess = errors.New("could not generate tasks from recurrer")
|
||||
)
|
||||
|
||||
type Recur struct {
|
||||
taskRepo *task.TaskRepo
|
||||
taskDispatcher *task.Dispatcher
|
||||
daysAhead int
|
||||
}
|
||||
|
||||
type RecurResult struct {
|
||||
Count int
|
||||
}
|
||||
|
||||
func NewRecur(repo *task.TaskRepo, disp *task.Dispatcher, daysAhead int) *Recur {
|
||||
return &Recur{
|
||||
taskRepo: repo,
|
||||
taskDispatcher: disp,
|
||||
daysAhead: daysAhead,
|
||||
}
|
||||
}
|
||||
|
||||
func (recur *Recur) Process() (*RecurResult, error) {
|
||||
tasks, err := recur.taskRepo.FindAll(task.FOLDER_RECURRING)
|
||||
if err != nil {
|
||||
return &RecurResult{}, fmt.Errorf("%w: %v", ErrRecurProcess, err)
|
||||
}
|
||||
|
||||
rDate := task.Today.AddDays(recur.daysAhead)
|
||||
var count int
|
||||
for _, t := range tasks {
|
||||
if t.RecursOn(rDate) {
|
||||
newTask, err := t.GenerateFromRecurrer(rDate)
|
||||
if err != nil {
|
||||
return &RecurResult{}, fmt.Errorf("%w: %v", ErrRecurProcess, err)
|
||||
}
|
||||
if err := recur.taskDispatcher.Dispatch(newTask); err != nil {
|
||||
return &RecurResult{}, fmt.Errorf("%w: %v", ErrRecurProcess, err)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return &RecurResult{
|
||||
Count: count,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package process_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.ewintr.nl/go-kit/test"
|
||||
"git.ewintr.nl/gte/internal/process"
|
||||
"git.ewintr.nl/gte/internal/task"
|
||||
"git.ewintr.nl/gte/pkg/msend"
|
||||
"git.ewintr.nl/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func TestRecurProcess(t *testing.T) {
|
||||
task.Today = task.NewDate(2021, 5, 14)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
recurMsgs []*mstore.Message
|
||||
expResult *process.RecurResult
|
||||
expMsgs []*msend.Message
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
expResult: &process.RecurResult{},
|
||||
expMsgs: []*msend.Message{},
|
||||
},
|
||||
{
|
||||
name: "one of two recurring",
|
||||
recurMsgs: []*mstore.Message{
|
||||
{
|
||||
Subject: "not recurring",
|
||||
Body: "recur: 2021-05-20, daily\nid: xxx-xxx\nversion: 1",
|
||||
},
|
||||
{
|
||||
Subject: "recurring",
|
||||
Body: "recur: 2021-05-10, daily\nid: xxx-xxx\nversion: 1",
|
||||
},
|
||||
},
|
||||
expResult: &process.RecurResult{
|
||||
Count: 1,
|
||||
},
|
||||
expMsgs: []*msend.Message{
|
||||
{Subject: "2021-05-15 (saturday) - recurring"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mstorer, err := mstore.NewMemory([]string{
|
||||
task.FOLDER_INBOX,
|
||||
task.FOLDER_NEW,
|
||||
task.FOLDER_RECURRING,
|
||||
task.FOLDER_PLANNED,
|
||||
task.FOLDER_UNPLANNED,
|
||||
})
|
||||
test.OK(t, err)
|
||||
for _, m := range tc.recurMsgs {
|
||||
test.OK(t, mstorer.Add(task.FOLDER_RECURRING, m.Subject, m.Body))
|
||||
}
|
||||
msender := msend.NewMemory()
|
||||
|
||||
recurProc := process.NewRecur(task.NewRepository(mstorer), task.NewDispatcher(msender), 1)
|
||||
actResult, err := recurProc.Process()
|
||||
test.OK(t, err)
|
||||
test.Equals(t, tc.expResult, actResult)
|
||||
for i, expMsg := range tc.expMsgs {
|
||||
test.Equals(t, expMsg.Subject, msender.Messages[i].Subject)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package task
|
||||
|
||||
import "git.ewintr.nl/gte/pkg/msend"
|
||||
|
||||
type Dispatcher struct {
|
||||
msender msend.MSender
|
||||
}
|
||||
|
||||
func NewDispatcher(msender msend.MSender) *Dispatcher {
|
||||
return &Dispatcher{
|
||||
msender: msender,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) Dispatch(t *Task) error {
|
||||
return d.msender.Send(&msend.Message{
|
||||
Subject: t.FormatSubject(),
|
||||
Body: t.FormatBody(),
|
||||
})
|
||||
}
|
|
@ -52,8 +52,8 @@ func (tr *TaskRepo) Update(t *Task) error {
|
|||
}
|
||||
|
||||
// add new
|
||||
if err := tr.mstore.Add(t.Folder, t.FormatSubject(), t.FormatBody()); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrMStoreError, err)
|
||||
if err := tr.Add(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove old
|
||||
|
@ -66,6 +66,18 @@ func (tr *TaskRepo) Update(t *Task) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tr *TaskRepo) Add(t *Task) error {
|
||||
if t == nil {
|
||||
return ErrInvalidTask
|
||||
}
|
||||
|
||||
if err := tr.mstore.Add(t.Folder, t.FormatSubject(), t.FormatBody()); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrMStoreError, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup removes older versions of tasks
|
||||
func (tr *TaskRepo) CleanUp() error {
|
||||
// loop through folders, get all task version info
|
||||
|
|
|
@ -261,20 +261,18 @@ func (t *Task) RecursOn(date Date) bool {
|
|||
return t.Recur.RecursOn(date)
|
||||
}
|
||||
|
||||
func (t *Task) CreateDueMessage(date Date) (string, string, error) {
|
||||
if !t.IsRecurrer() {
|
||||
return "", "", ErrTaskIsNotRecurring
|
||||
func (t *Task) GenerateFromRecurrer(date Date) (*Task, error) {
|
||||
if !t.IsRecurrer() || !t.RecursOn(date) {
|
||||
return &Task{}, ErrTaskIsNotRecurring
|
||||
}
|
||||
|
||||
tempTask := &Task{
|
||||
return &Task{
|
||||
Id: uuid.New().String(),
|
||||
Version: 1,
|
||||
Action: t.Action,
|
||||
Project: t.Project,
|
||||
Due: date,
|
||||
}
|
||||
|
||||
return tempTask.FormatSubject(), tempTask.FormatBody(), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func FieldFromBody(field, body string) (string, bool) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package msend
|
||||
|
||||
type Memory struct {
|
||||
Messages []*Message
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{
|
||||
Messages: []*Message{},
|
||||
}
|
||||
}
|
||||
|
||||
func (mem *Memory) Send(msg *Message) error {
|
||||
mem.Messages = append(mem.Messages, msg)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package msend_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.ewintr.nl/go-kit/test"
|
||||
"git.ewintr.nl/gte/pkg/msend"
|
||||
)
|
||||
|
||||
func TestMemorySend(t *testing.T) {
|
||||
mem := msend.NewMemory()
|
||||
test.Equals(t, []*msend.Message{}, mem.Messages)
|
||||
|
||||
msg1 := &msend.Message{Subject: "sub1", Body: "body1"}
|
||||
test.OK(t, mem.Send(msg1))
|
||||
test.Equals(t, []*msend.Message{msg1}, mem.Messages)
|
||||
|
||||
msg2 := &msend.Message{Subject: "sub2", Body: "body2"}
|
||||
test.OK(t, mem.Send(msg2))
|
||||
test.Equals(t, []*msend.Message{msg1, msg2}, mem.Messages)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package msend
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrSendFail = errors.New("could not send message")
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Subject string
|
||||
Body string
|
||||
}
|
||||
|
||||
type MSender interface {
|
||||
Send(msg *Message) error
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package msend
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSMTPInvalidConfig = errors.New("invalid smtp configuration")
|
||||
ErrSMTPConnectionFailed = errors.New("connection to smtp server failed")
|
||||
ErrSendMessageFailed = errors.New("could not send message")
|
||||
)
|
||||
|
||||
type SSLSMTPConfig struct {
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
func (ssc *SSLSMTPConfig) Valid() bool {
|
||||
if _, _, err := net.SplitHostPort(ssc.URL); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return ssc.Username != "" && ssc.Password != "" && ssc.To != "" && ssc.From != ""
|
||||
}
|
||||
|
||||
type SSLSMTP struct {
|
||||
config *SSLSMTPConfig
|
||||
client *smtp.Client
|
||||
connected bool
|
||||
}
|
||||
|
||||
func NewSSLSMTP(config *SSLSMTPConfig) *SSLSMTP {
|
||||
return &SSLSMTP{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSLSMTP) Connect() error {
|
||||
if !s.config.Valid() {
|
||||
return ErrSMTPInvalidConfig
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(s.config.URL)
|
||||
auth := smtp.PlainAuth("", s.config.Username, s.config.Password, host)
|
||||
conn, err := tls.Dial("tcp", s.config.URL, &tls.Config{ServerName: host})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSMTPConnectionFailed, err)
|
||||
}
|
||||
client, err := smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSMTPConnectionFailed, err)
|
||||
}
|
||||
if err := client.Auth(auth); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSMTPConnectionFailed, err)
|
||||
}
|
||||
s.client = client
|
||||
s.connected = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSLSMTP) Close() error {
|
||||
if !s.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.client.Quit(); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSMTPConnectionFailed, err)
|
||||
}
|
||||
s.connected = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSLSMTP) Send(msg *Message) error {
|
||||
if err := s.Connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
from := mail.Address{
|
||||
Name: "gte",
|
||||
Address: s.config.From,
|
||||
}
|
||||
to := mail.Address{
|
||||
Name: "todo",
|
||||
Address: s.config.To,
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["From"] = from.String()
|
||||
headers["To"] = to.String()
|
||||
headers["Subject"] = msg.Subject
|
||||
|
||||
message := ""
|
||||
for k, v := range headers {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += fmt.Sprintf("\r\n%s", msg.Body)
|
||||
|
||||
if err := s.client.Mail(s.config.From); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSendMessageFailed, err)
|
||||
}
|
||||
if err := s.client.Rcpt(s.config.To); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSendMessageFailed, err)
|
||||
}
|
||||
wc, err := s.client.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSendMessageFailed, err)
|
||||
}
|
||||
if _, err := wc.Write([]byte(message)); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSendMessageFailed, err)
|
||||
}
|
||||
if err := wc.Close(); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSendMessageFailed, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package mstore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -12,71 +13,97 @@ import (
|
|||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
type Body struct {
|
||||
var (
|
||||
ErrIMAPInvalidConfig = errors.New("invalid imap configuration")
|
||||
ErrIMAPConnFailure = errors.New("could not connect with imap")
|
||||
ErrIMAPNotConnected = errors.New("unable to perform, not connected to imap")
|
||||
ErrIMAPServerProblem = errors.New("imap server was unable to perform operation")
|
||||
)
|
||||
|
||||
type IMAPBody struct {
|
||||
reader io.Reader
|
||||
length int
|
||||
}
|
||||
|
||||
func NewBody(msg string) *Body {
|
||||
|
||||
return &Body{
|
||||
func NewIMAPBody(msg string) *IMAPBody {
|
||||
return &IMAPBody{
|
||||
reader: strings.NewReader(msg),
|
||||
length: len([]byte(msg)),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Body) Read(p []byte) (int, error) {
|
||||
func (b *IMAPBody) Read(p []byte) (int, error) {
|
||||
return b.reader.Read(p)
|
||||
}
|
||||
|
||||
func (b *Body) Len() int {
|
||||
func (b *IMAPBody) Len() int {
|
||||
return b.length
|
||||
}
|
||||
|
||||
type ImapConfiguration struct {
|
||||
ImapUrl string
|
||||
ImapUsername string
|
||||
ImapPassword string
|
||||
type IMAPConfig struct {
|
||||
IMAPURL string
|
||||
IMAPUsername string
|
||||
IMAPPassword string
|
||||
}
|
||||
|
||||
func (esc *ImapConfiguration) Valid() bool {
|
||||
if esc.ImapUrl == "" {
|
||||
func (esc *IMAPConfig) Valid() bool {
|
||||
if esc.IMAPURL == "" {
|
||||
return false
|
||||
}
|
||||
if esc.ImapUsername == "" || esc.ImapPassword == "" {
|
||||
if esc.IMAPUsername == "" || esc.IMAPPassword == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Imap struct {
|
||||
imap *client.Client
|
||||
type IMAP struct {
|
||||
config *IMAPConfig
|
||||
connected bool
|
||||
client *client.Client
|
||||
mboxStatus *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func ImapConnect(conf *ImapConfiguration) (*Imap, error) {
|
||||
imap, err := client.DialTLS(conf.ImapUrl, nil)
|
||||
func NewIMAP(config *IMAPConfig) *IMAP {
|
||||
return &IMAP{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (im *IMAP) Connect() error {
|
||||
if !im.config.Valid() {
|
||||
return ErrIMAPInvalidConfig
|
||||
}
|
||||
if im.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
cl, err := client.DialTLS(im.config.IMAPURL, nil)
|
||||
if err != nil {
|
||||
return &Imap{}, err
|
||||
return fmt.Errorf("%w: %v", ErrIMAPConnFailure, err)
|
||||
}
|
||||
if err := imap.Login(conf.ImapUsername, conf.ImapPassword); err != nil {
|
||||
return &Imap{}, err
|
||||
if err := cl.Login(im.config.IMAPUsername, im.config.IMAPPassword); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrIMAPConnFailure, err)
|
||||
}
|
||||
|
||||
return &Imap{
|
||||
imap: imap,
|
||||
}, nil
|
||||
im.client = cl
|
||||
im.connected = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *Imap) Disconnect() {
|
||||
es.imap.Logout()
|
||||
func (im *IMAP) Close() {
|
||||
im.client.Logout()
|
||||
im.connected = false
|
||||
}
|
||||
|
||||
func (es *Imap) Folders() ([]string, error) {
|
||||
func (im *IMAP) Folders() ([]string, error) {
|
||||
im.Connect()
|
||||
defer im.Close()
|
||||
|
||||
boxes, done := make(chan *imap.MailboxInfo), make(chan error)
|
||||
go func() {
|
||||
done <- es.imap.List("", "*", boxes)
|
||||
done <- im.client.List("", "*", boxes)
|
||||
}()
|
||||
|
||||
folders := []string{}
|
||||
|
@ -91,28 +118,35 @@ func (es *Imap) Folders() ([]string, error) {
|
|||
return folders, nil
|
||||
}
|
||||
|
||||
func (es *Imap) selectFolder(folder string) error {
|
||||
status, err := es.imap.Select(folder, false)
|
||||
if err != nil {
|
||||
return err
|
||||
func (im *IMAP) selectFolder(folder string) error {
|
||||
if !im.connected {
|
||||
return ErrIMAPNotConnected
|
||||
}
|
||||
|
||||
es.mboxStatus = status
|
||||
status, err := im.client.Select(folder, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w, %v", ErrIMAPServerProblem, err)
|
||||
}
|
||||
|
||||
im.mboxStatus = status
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *Imap) Messages(folder string) ([]*Message, error) {
|
||||
if err := es.selectFolder(folder); err != nil {
|
||||
func (im *IMAP) Messages(folder string) ([]*Message, error) {
|
||||
im.Connect()
|
||||
defer im.Close()
|
||||
|
||||
if err := im.selectFolder(folder); err != nil {
|
||||
return []*Message{}, err
|
||||
}
|
||||
|
||||
if es.mboxStatus.Messages == 0 {
|
||||
if im.mboxStatus.Messages == 0 {
|
||||
return []*Message{}, nil
|
||||
}
|
||||
|
||||
seqset := new(imap.SeqSet)
|
||||
seqset.AddRange(uint32(1), es.mboxStatus.Messages)
|
||||
seqset.AddRange(uint32(1), im.mboxStatus.Messages)
|
||||
|
||||
// Get the whole message body
|
||||
section := &imap.BodySectionName{}
|
||||
|
@ -120,7 +154,7 @@ func (es *Imap) Messages(folder string) ([]*Message, error) {
|
|||
|
||||
imsg, done := make(chan *imap.Message), make(chan error)
|
||||
go func() {
|
||||
done <- es.imap.Fetch(seqset, items, imsg)
|
||||
done <- im.client.Fetch(seqset, items, imsg)
|
||||
}()
|
||||
|
||||
messages := []*Message{}
|
||||
|
@ -169,30 +203,39 @@ func (es *Imap) Messages(folder string) ([]*Message, error) {
|
|||
}
|
||||
|
||||
if err := <-done; err != nil {
|
||||
return []*Message{}, err
|
||||
return []*Message{}, fmt.Errorf("%w: %v", ErrIMAPServerProblem, err)
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func (es *Imap) Add(folder, subject, body string) error {
|
||||
func (im *IMAP) Add(folder, subject, body string) error {
|
||||
im.Connect()
|
||||
defer im.Close()
|
||||
|
||||
msgStr := fmt.Sprintf(`From: todo <mstore@erikwinter.nl>
|
||||
Date: %s
|
||||
Subject: %s
|
||||
|
||||
%s`, time.Now().Format(time.RFC822Z), subject, body)
|
||||
|
||||
msg := NewBody(msgStr)
|
||||
msg := NewIMAPBody(msgStr)
|
||||
|
||||
return es.imap.Append(folder, nil, time.Time{}, imap.Literal(msg))
|
||||
if err := im.client.Append(folder, nil, time.Time{}, imap.Literal(msg)); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrIMAPServerProblem, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *Imap) Remove(msg *Message) error {
|
||||
func (im *IMAP) Remove(msg *Message) error {
|
||||
if msg == nil || !msg.Valid() {
|
||||
return ErrInvalidMessage
|
||||
}
|
||||
im.Connect()
|
||||
defer im.Close()
|
||||
|
||||
if err := es.selectFolder(msg.Folder); err != nil {
|
||||
if err := im.selectFolder(msg.Folder); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -200,14 +243,14 @@ func (es *Imap) Remove(msg *Message) error {
|
|||
seqset := new(imap.SeqSet)
|
||||
seqset.AddRange(msg.Uid, msg.Uid)
|
||||
storeItem := imap.FormatFlagsOp(imap.SetFlags, true)
|
||||
err := es.imap.UidStore(seqset, storeItem, imap.FormatStringList([]string{imap.DeletedFlag}), nil)
|
||||
err := im.client.UidStore(seqset, storeItem, imap.FormatStringList([]string{imap.DeletedFlag}), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%w: %v", ErrIMAPServerProblem, err)
|
||||
}
|
||||
|
||||
// expunge box
|
||||
if err := es.imap.Expunge(nil); err != nil {
|
||||
return err
|
||||
if err := im.client.Expunge(nil); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrIMAPServerProblem, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in New Issue