More mail stuff
This commit is contained in:
parent
b613c5b5c5
commit
06234cfeb9
@ -1,82 +1,102 @@
|
|||||||
#!/usr/bin/env hy
|
#!/usr/bin/env hy
|
||||||
|
|
||||||
(import subprocess [run PIPE DEVNULL])
|
(import email.parser :as parser)
|
||||||
|
(import subprocess [run PIPE])
|
||||||
(import threading [Thread])
|
(import threading [Thread])
|
||||||
(import enum [StrEnum])
|
|
||||||
(import os [path])
|
|
||||||
(import re)
|
|
||||||
(import json)
|
|
||||||
(import shutil)
|
(import shutil)
|
||||||
|
(import os)
|
||||||
|
(import sys)
|
||||||
|
|
||||||
(setv global-did-delete False)
|
(defclass MailInbox []
|
||||||
|
(setv maildir-path None)
|
||||||
|
(defn __init__ [self maildir-path]
|
||||||
|
(setv self.maildir-path maildir-path))
|
||||||
|
(defn get-new-messages [self folder]
|
||||||
|
(let [target-dir (+ self.maildir-path "/" folder "/new")]
|
||||||
|
(lfor file (os.listdir target-dir)
|
||||||
|
(MailMessage.from-file self (+ target-dir "/" file))))))
|
||||||
|
|
||||||
(defclass OutputType [StrEnum]
|
(defclass MailMessage []
|
||||||
(setv LINES "text"
|
(setv inbox None
|
||||||
JSON "json"))
|
file None
|
||||||
|
folder None
|
||||||
(defn notmuch [#* args [output None]]
|
|
||||||
(let [result (run ["notmuch"
|
|
||||||
#* args]
|
|
||||||
:stdout (if (is-not output None)
|
|
||||||
PIPE
|
|
||||||
DEVNULL)
|
|
||||||
:text True)]
|
|
||||||
(match output
|
|
||||||
OutputType.LINES (str.splitlines result.stdout)
|
|
||||||
OutputType.JSON (json.loads result.stdout))))
|
|
||||||
|
|
||||||
(defclass Message []
|
|
||||||
(setv uid None
|
|
||||||
mail-root None
|
|
||||||
sender None
|
sender None
|
||||||
subject None
|
subject None
|
||||||
|
flags None
|
||||||
|
read? False
|
||||||
attachment? False
|
attachment? False
|
||||||
notified? False
|
new? False)
|
||||||
read? False)
|
(defn __init__ [self inbox path sender subject attachment?]
|
||||||
(defn __init__ [self uid mail-root sender subject
|
(let [dir-path (os.path.dirname path)]
|
||||||
attachment? notified? read?]
|
(setv self.inbox inbox
|
||||||
(setv self.uid uid
|
self.file (os.path.basename path)
|
||||||
self.mail-root mail-root
|
self.folder (os.path.relpath (os.path.dirname dir-path)
|
||||||
self.sender sender
|
inbox.maildir-path)
|
||||||
self.subject subject
|
self.sender sender
|
||||||
self.attachment? attachment?
|
self.subject subject
|
||||||
self.notified? notified?
|
self.flags (MailMessage.-get-path-flags self.file)
|
||||||
self.notified? read?))
|
self.read? (in "S" self.flags)
|
||||||
(defn tag [self #* tags]
|
self.attachment? attachment?
|
||||||
(notmuch "tag" #* tags (+ "id:" self.uid)))
|
self.new? (= (os.path.basename dir-path) "new"))))
|
||||||
(defn move [self dest]
|
(defn get-dir-path [self]
|
||||||
(let [old-path (self.get-filename)
|
(+ self.inbox.maildir-path "/"
|
||||||
name (re.sub "U=[0-9]+:[0-9]+,([DFPRS]*$)"
|
self.folder "/"
|
||||||
r"\1" (path.basename old-path))
|
(if self.new? "new" "cur")))
|
||||||
new-path (+ self.mail-root "/" dest "/cur/" name)]
|
(defn move [self new-folder]
|
||||||
(shutil.move old-path new-path)))
|
(let [clean-new-folder (MailMessage.-clean-folder new-folder)]
|
||||||
(defn get-filename [self]
|
(when (!= self.folder clean-new-folder)
|
||||||
(get (notmuch "search" "--output=files" (+ "id:" (str self.uid))
|
(shutil.move (+ (self.get-dir-path) "/" self.file)
|
||||||
:output OutputType.LINES) 0))
|
(+ self.inbox.maildir-path "/"
|
||||||
(defn from-json [root mail-root]
|
clean-new-folder "/"
|
||||||
(let [uid (get root "id")
|
(if self.new? "new" "cur") "/"
|
||||||
headers (get root "headers")
|
self.file))
|
||||||
sender (parse-from-address (get headers "From"))
|
(setv self.folder clean-new-folder))))
|
||||||
subject (get headers "Subject")
|
(defn process [self]
|
||||||
tags (get root "tags")
|
(when self.new?
|
||||||
attachment? (in "attachment" tags)
|
(shutil.move (+ (self.get-dir-path) "/" self.file)
|
||||||
notified? (not-in "notnotified" tags)
|
(+ self.inbox.maildir-path "/"
|
||||||
read? (not-in "unread" tags)]
|
self.folder
|
||||||
(Message uid mail-root sender subject attachment? notified? read?)))
|
"/cur/"
|
||||||
(defn __str__ [self]
|
self.file))
|
||||||
(+ "Message From \"" self.sender "\": " self.subject
|
(setv self.new? False)))
|
||||||
" ("
|
(defn mark-read [self]
|
||||||
(if self.attachment? "attachment " "")
|
(when (not self.read?)
|
||||||
(if self.notified? "" "un") "notified "
|
(self.flags.add "S")
|
||||||
(if self.read? "" "un") "read"
|
(let [base-name (get self.file (slice (+ (self.file.rindex ",") 1)))
|
||||||
")")))
|
new-name (+ base-name (str.join "" self.flags))
|
||||||
|
dir-path (self.get-dir-path)]
|
||||||
(defn parse-from-address [header]
|
(shutil.move (+ dir-path "/" self.file) (+ dir-path "/" new-name))
|
||||||
(try
|
(setv self.file new-name
|
||||||
(let [index (str.index header "<")]
|
self.read? True))))
|
||||||
(get header (slice 1 (- index 2))))
|
(defn -parse-from-address [header]
|
||||||
(except [ValueError]
|
(try
|
||||||
header)))
|
(let [index (str.index header "<")]
|
||||||
|
(get header (slice 1 (- index 2))))
|
||||||
|
(except [ValueError]
|
||||||
|
header)))
|
||||||
|
(defn -clean-folder [folder]
|
||||||
|
(when (str.startswith folder "/")
|
||||||
|
(setv folder (get folder (slice 1))))
|
||||||
|
(when (str.endswith folder "/")
|
||||||
|
(setv folder (get folder (slice None -1))))
|
||||||
|
folder)
|
||||||
|
(defn -get-path-flags [path]
|
||||||
|
(set (get path (slice (+ (path.rindex ",") 1) None))))
|
||||||
|
(defn -message-has-attachment [mail-obj]
|
||||||
|
(when (mail-obj.is_multipart)
|
||||||
|
(for [part (mail-obj.walk)]
|
||||||
|
(when (str.startswith (part.get "Content-Disposition") "attachment")
|
||||||
|
(return True))))
|
||||||
|
False)
|
||||||
|
(defn from-file [inbox path]
|
||||||
|
(with [file-obj (open path "r")]
|
||||||
|
(let [parse (parser.Parser)
|
||||||
|
mail-obj (parse.parse file-obj :headersonly True)]
|
||||||
|
(MailMessage inbox
|
||||||
|
path
|
||||||
|
(MailMessage.-parse-from-address (mail-obj.get "From"))
|
||||||
|
(mail-obj.get "Subject")
|
||||||
|
(MailMessage.-message-has-attachment mail-obj))))))
|
||||||
|
|
||||||
(defn notify-send [title desc [time 0] [actions []]]
|
(defn notify-send [title desc [time 0] [actions []]]
|
||||||
(let [cmd ["notify-send" title desc "-t" (str time)]]
|
(let [cmd ["notify-send" title desc "-t" (str time)]]
|
||||||
@ -89,32 +109,29 @@
|
|||||||
(except [ValueError]
|
(except [ValueError]
|
||||||
None)))))
|
None)))))
|
||||||
|
|
||||||
(defn notify-message [msg]
|
(defn handle-message [msg]
|
||||||
(msg.tag "-notnotified")
|
(msg.process)
|
||||||
(let [result (notify-send (+ (if msg.attachment? " " "")
|
(when (not msg.read?)
|
||||||
"New mail from " msg.sender)
|
(match (notify-send (+ (if msg.attachment? " " "")
|
||||||
msg.subject
|
"New mail from " msg.sender)
|
||||||
:time 10000
|
msg.subject
|
||||||
:actions ["Mark Read" "Delete"])]
|
:time 10000
|
||||||
(match result
|
:actions ["Mark Read" "Delete"])
|
||||||
0 (msg.tag "-unread")
|
0 (msg.mark-read)
|
||||||
1 (do
|
1 (do
|
||||||
(setv global-did-delete True)
|
(msg.mark-read)
|
||||||
(msg.tag "-unread" "+deleted")
|
|
||||||
(msg.move "Trash")))))
|
(msg.move "Trash")))))
|
||||||
|
|
||||||
(let [mail-root (get (notmuch "config" "get" "database.mail_root"
|
(when (< (len sys.argv) 2)
|
||||||
:output OutputType.LINES) 0)
|
(print "usage: notify-mail.hy <maildir>" :file sys.stderr)
|
||||||
json-root (notmuch "show" "--format=json" "--body=false"
|
(sys.exit 1))
|
||||||
"tag:notnotified" "and"
|
|
||||||
"tag:unread" "and"
|
|
||||||
"folder:Inbox"
|
|
||||||
:output OutputType.JSON)]
|
|
||||||
(for [msg-json json-root]
|
|
||||||
(let [msg (Message.from-json (. msg-json [0] [0]) mail-root)]
|
|
||||||
(Thread.start (Thread :target notify-message
|
|
||||||
:args #(msg))))))
|
|
||||||
|
|
||||||
(notmuch "tag" "-notnotified" "*")
|
(when (= (get sys.argv 1) "-h")
|
||||||
(when global-did-delete
|
(print "usage: notify-mail.hy <maildir>")
|
||||||
(notmuch "new" "--no-hooks"))
|
(sys.exit 0))
|
||||||
|
|
||||||
|
(let [mail-inbox (MailInbox (get sys.argv 1))
|
||||||
|
new-msgs (mail-inbox.get-new-messages "Inbox")]
|
||||||
|
(for [msg new-msgs]
|
||||||
|
(Thread.start (Thread :target handle-message
|
||||||
|
:args #(msg)))))
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/env zsh
|
|
||||||
|
|
||||||
notmuch tag -deleted not folder:Trash
|
|
||||||
notmuch tag +deleted folder:Trash
|
|
||||||
|
|
||||||
notmuch tag -spam not folder:Spam
|
|
||||||
notmuch tag +spam folder:Spam
|
|
||||||
|
|
||||||
notmuch tag -starred not folder:Starred
|
|
||||||
|
|
||||||
hy "${HOME}/scripts/mail/notify-mail.hy"
|
|
@ -1,11 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Sync mail with mbsync and notmuch
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=-mbsync -a ; notmuch new
|
|
||||||
RuntimeMaxSec=3m
|
|
||||||
Restart=on-failure
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
11
systemd/sync-mail@.service
Normal file
11
systemd/sync-mail@.service
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Sync mail with mbsync and send notifications
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=-mbsync -a ; %h/scripts/mail/notify-mail.hy %h/.mail/%i
|
||||||
|
RuntimeMaxSec=3m
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
@ -1,5 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Sync mail every minute with mbsync and notmuch
|
Description=Sync mail every minute with mbsync and send notifications
|
||||||
|
|
||||||
[Timer]
|
[Timer]
|
||||||
OnCalendar=*-*-* *:*:00
|
OnCalendar=*-*-* *:*:00
|
Loading…
Reference in New Issue
Block a user