#!/usr/bin/env hy (import subprocess [run PIPE DEVNULL]) (import threading [Thread]) (import enum [StrEnum]) (import os [path]) (import json) (import shutil) (defclass OutputType [StrEnum] (setv LINES "text" JSON "json")) (defn notmuch [#* args [output OutputType.LINES]] (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 (let [json-root (json.loads result.stdout)] (if (!= (len json-root) 0) (. json-root [0] [0] [(slice None -1)]) []))))) (defclass Message [] (setv uid None mail-root None sender None subject None file None attachment? False notified? False read? False) (defn __init__ [self uid mail-root sender subject file attachment? notified? read?] (setv self.uid uid self.mail-root mail-root self.sender sender self.subject subject self.file file self.attachment? attachment? self.notified? notified? self.notified? read?)) (defn tag [self #* tags] (when (is self.uid None) (raise (ValueError "uid is None"))) (notmuch "tag" #* tags (+ "id:" self.uid))) (defn move [self dest] (let [name (path.basename self.file) new_path (+ mail-root "/" dest "/cur/" name)] (shutil.move msg.file new_path))) (defn from-json [root mail-root] (let [uid (get root "id") headers (get root "headers") sender (parse-from-address (get headers "From")) subject (get headers "Subject") file (. root ["filename"] [0]) tags (get root "tags") attachment? (in "attachment" tags) notified? (not-in "notnotified" tags) read? (not-in "unread" tags)] (Message uid mail-root sender subject file attachment? notified? read?))) (defn __str__ [self] (+ "Message From \"" self.sender "\": " self.subject " (" (if self.attachment? "attachment " "") (if self.notified? "" "un") "notified " (if self.read? "" "un") "read" ")"))) (defn parse-from-address [header] (try (let [index (str.index header "<")] (get header (slice 1 (- index 2)))) (except [ValueError] header))) (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 notify-message [msg] (let [result (notify-send (+ (if msg.attachment? "󰈙 " "") "New mail from " msg.sender) msg.subject :time 10000 :actions ["Mark Read" "Delete"])] (match result 0 (msg.tag "-unread") 1 (do (msg.tag "-unread") (msg.move "Trash"))))) (let [mail-root (get (notmuch "config" "get" "database.mail_root") 0) json-root (notmuch "show" "--format=json" "--body=false" "tag:notnotified" "and" "tag:unread" "and" "folder:Inbox" :output OutputType.JSON)] (for [msg-json json-root] (let [msg (Message.from-json msg-json mail-root)] (Thread.start (Thread :target notify-message :args #(msg)))))) (notmuch "tag" "-notnotified" "*")