From 48bb2f4113a6a304da995175fa4ebd2c46adfa20 Mon Sep 17 00:00:00 2001 From: Matthew Krafczyk Date: Tue, 27 Jan 2015 18:56:54 +0100 Subject: [PATCH] Added the newmail_hook When new mail arrives, this hook is triggered, allowing the user to play a sound, or launch a popup. Signed-off-by: Matthew Krafczyk --- offlineimap.conf | 13 +++++++++++++ offlineimap/folder/Base.py | 16 ++++++++++++++++ offlineimap/repository/Base.py | 1 + offlineimap/repository/IMAP.py | 5 +++++ 4 files changed, 35 insertions(+) diff --git a/offlineimap.conf b/offlineimap.conf index 69ede92..6b61e64 100644 --- a/offlineimap.conf +++ b/offlineimap.conf @@ -295,6 +295,19 @@ remoterepository = RemoteExample #postsynchook = notifysync.sh +# This option stands in the [Account Test] section. +# +# You can specify a newmail hook to execute an external command upon receipt +# of new mail in the INBOX. +# +# This example plays a sound file of your chosing when new mail arrives. +# +# This feature is experimental. +# +#newmail_hook = lambda: os.sytem("cvlc --play-and-exit /path/to/sound/file.mp3" + +# " > /dev/null 2>&1") + + # This option stands in the [Account Test] section. # # OfflineImap caches the state of the synchronisation to e.g. be able to diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 7957b83..d81f3ef 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -40,6 +40,11 @@ class BaseFolder(object): # Top level dir name is always '' self.root = None self.name = name if not name == self.getsep() else '' + self.newmail_hook = None + # Only set the newmail_hook if the IMAP folder is named 'INBOX' + if self.name == 'INBOX': + self.newmail_hook = repository.newmail_hook + self.have_newmail = False self.repository = repository self.visiblename = repository.nametrans(name) # In case the visiblename becomes '.' or '/' (top-level) we use @@ -781,6 +786,9 @@ class BaseFolder(object): # Got new UID, change the local uid. # Save uploaded status in the statusfolder statusfolder.savemessage(new_uid, message, flags, rtime) + # Check whether the mail has been seen + if 'S' not in flags: + self.have_newmail = True elif new_uid == 0: # Message was stored to dstfolder, but we can't find it's UID # This means we can't link current message to the one created @@ -817,6 +825,9 @@ class BaseFolder(object): This function checks and protects us from action in dryrun mode.""" + # We have no new mail yet + self.have_newmail = False + threads = [] copylist = filter(lambda uid: not statusfolder.uidexists(uid), @@ -854,6 +865,11 @@ class BaseFolder(object): for thread in threads: thread.join() + # Execute new mail hook if we have new mail + if self.have_newmail: + if self.newmail_hook != None: + self.newmail_hook(); + def __syncmessagesto_delete(self, dstfolder, statusfolder): """Pass 2: Remove locally deleted messages on dst. diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py index 0cf44f8..adae16d 100644 --- a/offlineimap/repository/Base.py +++ b/offlineimap/repository/Base.py @@ -48,6 +48,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object): self.folderfilter = lambda foldername: 1 self.folderincludes = [] self.foldersort = None + self.newmail_hook = None if self.config.has_option(self.getsection(), 'nametrans'): self.nametrans = self.localeval.eval( self.getconf('nametrans'), {'re': re}) diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index 68c8e33..6f8d829 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -36,6 +36,11 @@ class IMAPRepository(BaseRepository): self._host = None self.imapserver = imapserver.IMAPServer(self) self.folders = None + # Only set the newmail_hook in an IMAP repository. + if self.config.has_option(self.getsection(), 'newmail_hook'): + self.newmail_hook = self.localeval.eval( + self.getconf('newmail_hook')) + if self.getconf('sep', None): self.ui.info("The 'sep' setting is being ignored for IMAP " "repository '%s' (it's autodetected)"% self)