random-scripts/mail/notify-mail.hy

163 lines
5.6 KiB
Hy
Executable File

#!/usr/bin/env hy
(import email.parser :as parser)
(import subprocess [run CompletedProcess PIPE])
(import threading [Thread])
(import shutil)
(import os)
(import sys)
(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)))))
(defn find-by-uid [self uid [exclude-msgs None]]
(let [files (str.splitlines
(. (run ["rg" "-Fl" uid self.maildir-path]
:stdout PIPE :text True) stdout))
output #{}]
(when (is-not exclude-msgs None)
(for [excluded exclude-msgs]
(files.remove (os.path.abspath (excluded.get-full-path)))))
(for [file files]
(let [msg (MailMessage.from-file self file)]
(when (= msg.uid uid)
(output.add msg))))
output))
(defn mark-all-read [self uid [exclude-msgs None]]
(let [msgs (self.find-by-uid uid exclude-msgs)]
(for [msg msgs]
(msg.mark-read)))))
(defclass MailMessage []
(setv inbox None
uid None
file None
folder None
sender None
subject None
flags None
read? False
attachment? False
new? False)
(defn __init__ [self inbox uid path sender subject attachment?]
(let [dir-path (os.path.dirname path)]
(setv self.inbox inbox
self.uid uid
self.file (os.path.basename path)
self.folder (os.path.relpath (os.path.dirname dir-path)
inbox.maildir-path)
self.sender sender
self.subject subject
self.flags (MailMessage.-get-path-flags self.file)
self.read? (in "S" self.flags)
self.attachment? attachment?
self.new? (= (os.path.basename dir-path) "new"))))
(defn get-dir-path [self]
(+ self.inbox.maildir-path "/"
self.folder "/"
(if self.new? "new" "cur")))
(defn get-full-path [self]
(+ (self.get-dir-path) "/" self.file))
(defn move [self new-folder]
(let [clean-new-folder (MailMessage.-clean-folder new-folder)]
(when (!= self.folder clean-new-folder)
(shutil.move (self.get-full-path)
(+ self.inbox.maildir-path "/"
clean-new-folder "/"
(if self.new? "new" "cur") "/"
self.file))
(setv self.folder clean-new-folder))))
(defn process [self]
(when self.new?
(shutil.move (self.get-full-path)
(+ self.inbox.maildir-path "/"
self.folder
"/cur/"
self.file))
(setv self.new? False)))
(defn mark-read [self]
(when (not self.read?)
(self.flags.add "S")
(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)]
(shutil.move (+ dir-path "/" self.file) (+ dir-path "/" new-name))
(setv self.file new-name
self.read? True))))
(defn -parse-from-address [header]
(try
(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
(mail-obj.get "Message-Id")
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 []]]
(let [cmd ["notify-send" title desc "-t" (str time)]]
(for [action actions]
(cmd.append "-A")
(cmd.append action))
(let [result (run cmd :stdout PIPE :text True)]
(try
(int result.stdout)
(except [ValueError]
None)))))
(defn handle-message [msg]
(msg.process)
(when (not msg.read?)
(match (notify-send (+ (if msg.attachment? "󰈙 " "")
"New mail from " msg.sender)
msg.subject
:time 10000
:actions ["Mark Read" "Delete"])
0 (do
(msg.mark-read)
(msg.inbox.mark-all-read msg.uid :exclude-msgs [msg]))
1 (do
(msg.mark-read)
(msg.inbox.mark-all-read msg.uid :exclude-msgs [msg])
(msg.move "Trash")))))
(when (< (len sys.argv) 2)
(print "usage: notify-mail.hy <maildir>" :file sys.stderr)
(sys.exit 1))
(when (= (get sys.argv 1) "-h")
(print "usage: notify-mail.hy <maildir>")
(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)))))