Skip to content

Commit

Permalink
hooks: add mail-added hook
Browse files Browse the repository at this point in the history
The mail-added hook runs whenever a message is added to a folder.
Note that the hook does not run when a new message is received (the
mail-received hook already covers that) but instead runs whenever aerc
itself adds a message to a folder, e.g. when moving or copying a
message.

Changelog-added: `mail-added` hook that triggers when a message is added
 to a folder.
References: https://todo.sr.ht/~rjarry/aerc/136
Signed-off-by: Jason Cox <[email protected]>
Acked-by: Robin Jarry <[email protected]>
  • Loading branch information
jasonccox authored and rjarry committed Nov 2, 2023
1 parent 31b50f2 commit faa879f
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 60 deletions.
9 changes: 9 additions & 0 deletions app/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,15 @@ func (acct *AccountView) newStore(name string) *lib.MessageStore {
msg := fmt.Sprintf("mail-deleted hook: %s", err)
PushError(msg)
}
}, func(dest string) {
err := hooks.RunHook(&hooks.MailAdded{
Account: acct.Name(),
Folder: dest,
})
if err != nil {
msg := fmt.Sprintf("mail-added hook: %s", err)
PushError(msg)
}
},
acct.updateSplitView,
acct.dirlist.UiConfig(name).ThreadContext,
Expand Down
44 changes: 22 additions & 22 deletions commands/account/import-mbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ func (i ImportMbox) Execute(args []string) error {
app.PushError(err.Error())
return
}
worker := acct.Worker()

var appended uint32
for i, m := range messages {
Expand All @@ -79,27 +78,28 @@ func (i ImportMbox) Execute(args []string) error {
break
}
nbytes, _ := io.Copy(&buf, r)
worker.PostAction(&types.AppendMessage{
Destination: folder,
Flags: models.SeenFlag,
Date: time.Now(),
Reader: &buf,
Length: int(nbytes),
}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Unsupported:
errMsg := fmt.Sprintf("%s: AppendMessage is unsupported", args[0])
log.Errorf(errMsg)
app.PushError(errMsg)
return
case *types.Error:
log.Errorf("AppendMessage failed: %v", msg.Error)
done <- false
case *types.Done:
atomic.AddUint32(&appended, 1)
done <- true
}
})
store.Append(
folder,
models.SeenFlag,
time.Now(),
&buf,
int(nbytes),
func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Unsupported:
errMsg := fmt.Sprintf("%s: AppendMessage is unsupported", args[0])
log.Errorf(errMsg)
app.PushError(errMsg)
return
case *types.Error:
log.Errorf("AppendMessage failed: %v", msg.Error)
done <- false
case *types.Done:
atomic.AddUint32(&appended, 1)
done <- true
}
},
)

select {
case ok := <-done:
Expand Down
37 changes: 21 additions & 16 deletions commands/compose/postpone.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func (p Postpone) Execute(args []string) error {
if acct == nil {
return errors.New("No account selected")
}
store := acct.Store()
if store == nil {
return errors.New("No message store selected")
}
tab := app.SelectedTab()
if tab == nil {
return errors.New("No tab selected")
Expand Down Expand Up @@ -98,22 +102,23 @@ func (p Postpone) Execute(args []string) error {
handleErr(errors.Wrap(err, "WriteMessage"))
return
}
worker.PostAction(&types.AppendMessage{
Destination: targetFolder,
Flags: models.SeenFlag,
Date: time.Now(),
Reader: buf,
Length: buf.Len(),
}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
app.PushStatus("Message postponed.", 10*time.Second)
composer.SetPostponed()
composer.Close()
case *types.Error:
handleErr(msg.Error)
}
})
store.Append(
targetFolder,
models.SeenFlag,
time.Now(),
buf,
buf.Len(),
func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
app.PushStatus("Message postponed.", 10*time.Second)
composer.SetPostponed()
composer.Close()
case *types.Error:
handleErr(msg.Error)
}
},
)
}()

if !alreadyCreated {
Expand Down
48 changes: 28 additions & 20 deletions commands/compose/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,7 @@ func send(composer *app.Composer, ctx sendCtx,
}
if ctx.copyto != "" && ctx.scheme != "jmap" {
app.PushStatus("Copying to "+ctx.copyto, 10*time.Second)
errch := copyToSent(composer.Worker(), ctx.copyto,
copyBuf.Len(), &copyBuf)
errch := copyToSent(ctx.copyto, copyBuf.Len(), &copyBuf)
err = <-errch
if err != nil {
errmsg := fmt.Sprintf(
Expand Down Expand Up @@ -558,23 +557,32 @@ func newJmapSender(
return writer, err
}

func copyToSent(worker *types.Worker, dest string,
n int, msg io.Reader,
) <-chan error {
errCh := make(chan error)
worker.PostAction(&types.AppendMessage{
Destination: dest,
Flags: models.SeenFlag,
Date: time.Now(),
Reader: msg,
Length: n,
}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
errCh <- nil
case *types.Error:
errCh <- msg.Error
}
})
func copyToSent(dest string, n int, msg io.Reader) <-chan error {
errCh := make(chan error, 1)
acct := app.SelectedAccount()
if acct == nil {
errCh <- errors.New("No account selected")
return errCh
}
store := acct.Store()
if store == nil {
errCh <- errors.New("No message store selected")
return errCh
}
store.Append(
dest,
models.SeenFlag,
time.Now(),
msg,
n,
func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
errCh <- nil
case *types.Error:
errCh <- msg.Error
}
},
)
return errCh
}
4 changes: 4 additions & 0 deletions config/aerc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,10 @@ message/rfc822=colorize
# Executed when mail is deleted from a folder
#mail-deleted=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &

#
# Executed when aerc adds mail to a folder
#mail-added=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &

#
# Executed when aerc starts
#aerc-startup=aerc :terminal calcurse && aerc :next-tab
Expand Down
1 change: 1 addition & 0 deletions config/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type HooksConfig struct {
AercShutdown string `ini:"aerc-shutdown"`
MailReceived string `ini:"mail-received"`
MailDeleted string `ini:"mail-deleted"`
MailAdded string `ini:"mail-added"`
}

var Hooks HooksConfig
Expand Down
15 changes: 15 additions & 0 deletions doc/aerc-config.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,21 @@ They are configured in the *[hooks]* section of aerc.conf.

*mail-deleted* = _mbsync "$AERC_ACCOUNT:$AERC_FOLDER"_

*mail-added* = _<command>_
Executed when a message is added to a folder. Note that this hook is not
triggered when a new message is received (use *mail-received* for that) but
rather is only triggered when aerc itself adds a message to a folder, e.g.
when moving or copying a message.

Variables:

- *AERC_ACCOUNT*
- *AERC_FOLDER*

Example:

*mail-added* = _mbsync "$AERC_ACCOUNT:$AERC_FOLDER"_

*aerc-shutdown* = _<command>_
Executed when aerc shuts down. Aerc will wait for the command to finish
before exiting.
Expand Down
23 changes: 23 additions & 0 deletions lib/hooks/mail-added.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package hooks

import (
"fmt"

"git.sr.ht/~rjarry/aerc/config"
)

type MailAdded struct {
Account string
Folder string
}

func (m *MailAdded) Cmd() string {
return config.Hooks.MailAdded
}

func (m *MailAdded) Env() []string {
return []string{
fmt.Sprintf("AERC_ACCOUNT=%s", m.Account),
fmt.Sprintf("AERC_FOLDER=%s", m.Folder),
}
}
30 changes: 28 additions & 2 deletions lib/msgstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type MessageStore struct {
triggerNewEmail func(*models.MessageInfo)
triggerDirectoryChange func()
triggerMailDeleted func()
triggerMailAdded func(string)

threadBuilderDebounce *time.Timer
threadBuilderDelay time.Duration
Expand All @@ -89,7 +90,8 @@ func NewMessageStore(worker *types.Worker,
reverseOrder bool, reverseThreadOrder bool, sortThreadSiblings bool,
triggerNewEmail func(*models.MessageInfo),
triggerDirectoryChange func(), triggerMailDeleted func(),
onSelect func(*models.MessageInfo), threadContext bool,
triggerMailAdded func(string), onSelect func(*models.MessageInfo),
threadContext bool,
) *MessageStore {
if !worker.Backend.Capabilities().Thread {
clientThreads = true
Expand Down Expand Up @@ -124,6 +126,7 @@ func NewMessageStore(worker *types.Worker,
triggerNewEmail: triggerNewEmail,
triggerDirectoryChange: triggerDirectoryChange,
triggerMailDeleted: triggerMailDeleted,
triggerMailAdded: triggerMailAdded,

threadBuilderDelay: clientThreadsDelay,

Expand Down Expand Up @@ -608,7 +611,12 @@ func (store *MessageStore) Copy(uids []uint32, dest string, createDest bool,
store.worker.PostAction(&types.CopyMessages{
Destination: dest,
Uids: uids,
}, cb)
}, func(msg types.WorkerMessage) {
if _, ok := msg.(*types.Done); ok {
store.triggerMailAdded(dest)
}
cb(msg)
})
}

func (store *MessageStore) Move(uids []uint32, dest string, createDest bool,
Expand All @@ -635,11 +643,29 @@ func (store *MessageStore) Move(uids []uint32, dest string, createDest bool,
cb(msg)
case *types.Done:
store.triggerMailDeleted()
store.triggerMailAdded(dest)
cb(msg)
}
})
}

func (store *MessageStore) Append(dest string, flags models.Flags, date time.Time,
reader io.Reader, length int, cb func(msg types.WorkerMessage),
) {
store.worker.PostAction(&types.AppendMessage{
Destination: dest,
Flags: flags,
Date: date,
Reader: reader,
Length: length,
}, func(msg types.WorkerMessage) {
if _, ok := msg.(*types.Done); ok {
store.triggerMailAdded(dest)
}
cb(msg)
})
}

func (store *MessageStore) Flag(uids []uint32, flags models.Flags,
enable bool, cb func(msg types.WorkerMessage),
) {
Expand Down

0 comments on commit faa879f

Please sign in to comment.