From 6df300b58ae49c3e2b1bb3ee497744093428da81 Mon Sep 17 00:00:00 2001 From: ndwarshuis Date: Thu, 14 Mar 2024 10:19:08 -0400 Subject: [PATCH] ENH make email config modular --- etc/conf.org | 372 +++++++++++++++++++++---------------------- local/lib/.gitignore | 1 + 2 files changed, 179 insertions(+), 194 deletions(-) create mode 100644 local/lib/.gitignore diff --git a/etc/conf.org b/etc/conf.org index e2639d0..5f2ffcd 100644 --- a/etc/conf.org +++ b/etc/conf.org @@ -3402,223 +3402,207 @@ Initialize by running =nd/mu-init=. #+BEGIN_SRC emacs-lisp (nd/require-bin "pandoc" :aur "pandoc-bin") -(nd/when-bin "mu" - (require 'mu4e) - (use-package password-store - :straight t) +(let ((acnts-path (f-join (nd/expand-lib-directory "mu4e") "accounts.el"))) + (when (f-exists-p acnts-path) + (nd/when-bin "mu" + ;; load mu itself + (require 'mu4e) + (require 'smtpmail) + ;; (require 'smtpmail-async) - (defun nd/mu4e-junk-folder (msg) - (->> (mu4e-context-determine msg nil) - (mu4e-context-vars) - (alist-get 'nd/mu4e-junk-folder))) + ;; + ;; apply common config shared b/t all accounts I use + ;; - (defun nd/mu4e-headers-mark-for-junk () - (interactive) - (mu4e-headers-mark-and-next 'junk)) + (defun nd/mu-init (maildir) + "Initialize the mu database using available contexts." + (->> mu4e-contexts + (--map (->> (mu4e-context-vars it) + (alist-get 'user-mail-address) + (format "--my-address=%s"))) + (s-join " ") + (format "mu init --maildir %s %s" maildir) + (shell-command-to-string))) - (defun nd/make-mu4e-context (name dir addr smtp-srv sent-behavior) - (let* ((trash (format "/%s/trash" dir)) - (drafts (format "/%s/drafts" dir)) - (sent (format "/%s/sent" dir)) - (archive (format "/%s/archive" dir)) - (inbox (format "/%s/inbox" dir)) - (junk (format "/%s/junk" dir)) - (shortcuts (--map (list :maildir (car it) :key (cdr it)) - `((,trash . ?t) - (,drafts . ?d) - (,sent . ?s) - (,archive . ?a) - (,inbox . ?i) - (,junk . ?j)))) + (defun nd/mu4e-junk-folder (msg) + "Hacky function to return junk folder from context. - (mf (lambda (d msg) - (-some--> msg - (mu4e-message-field it :maildir) - (string-prefix-p (concat "/" d) it))))) - (make-mu4e-context - :name name - :match-func (-partial mf dir) ; use lexical scope here - :vars `((mu4e-trash-folder . ,trash) - (mu4e-drafts-folder . ,drafts) - (mu4e-sent-folder . ,sent) - (mu4e-refile-folder . ,archive) - (nd/mu4e-junk-folder . ,junk) - (mu4e-sent-messages-behavior . ,sent-behavior) - (smtpmail-stream-type . starttls) - (smtpmail-smtp-server . ,smtp-srv) - (smtpmail-smtp-service . 587) - (smtpmail-smtp-user . ,addr) - (user-mail-address . ,addr) - (mu4e-maildir-shortcuts . ,shortcuts))))) + Must be bound to symbol `nd/mu4e-junk-folder`." + (->> (mu4e-context-determine msg nil) + (mu4e-context-vars) + (alist-get 'nd/mu4e-junk-folder))) - ;; display mu4e in same window - (add-to-list 'display-buffer-alist - `(,(regexp-quote mu4e-main-buffer-name) - display-buffer-same-window)) + (defun nd/mu4e-headers-mark-for-junk () + "Function to mark messages as junk." + (interactive) + (mu4e-headers-mark-and-next 'junk)) - (add-to-list - 'mu4e-marks - '(junk :char ("j" . "┻") - :prompt "junk" - :dyn-target - (lambda (target msg) (nd/mu4e-junk-folder msg)) - :action - (lambda - (docid msg target) - (mu4e--server-move docid - (mu4e--mark-check-target target) - "-N")))) - + (defun nd/make-mu4e-context (name dir addr smtp-srv smtp-tls sent-behavior) + (-let* ((trash (format "/%s/trash" dir)) + (drafts (format "/%s/drafts" dir)) + (sent (format "/%s/sent" dir)) + (archive (format "/%s/archive" dir)) + (inbox (format "/%s/inbox" dir)) + (junk (format "/%s/junk" dir)) + (shortcuts (--map (list :maildir (car it) :key (cdr it)) + `((,trash . ?t) + (,drafts . ?d) + (,sent . ?s) + (,archive . ?a) + (,inbox . ?i) + (,junk . ?j)))) + ;; either use TLS or no authentication for outgoing + ((smtp-proto smtp-port) (if smtp-tls + '(starttls 587) + '(nil 25))) + (mf (lambda (d msg) + (-some--> msg + (mu4e-message-field it :maildir) + (string-prefix-p (concat "/" d) it))))) + (make-mu4e-context + :name name + :match-func (-partial mf dir) ; use lexical scope here + :vars `((mu4e-trash-folder . ,trash) + (mu4e-drafts-folder . ,drafts) + (mu4e-sent-folder . ,sent) + (mu4e-refile-folder . ,archive) + (nd/mu4e-junk-folder . ,junk) + (mu4e-sent-messages-behavior . ,sent-behavior) + (smtpmail-stream-type . ,smtp-proto) + (smtpmail-smtp-server . ,smtp-srv) + (smtpmail-smtp-service . ,smtp-port) + (smtpmail-smtp-user . ,addr) + (user-mail-address . ,addr) + (mu4e-maildir-shortcuts . ,shortcuts))))) - (setq mail-user-agent 'mu4e-user-agent - message-kill-buffer-on-exit t + ;; display mu4e in same window + (add-to-list 'display-buffer-alist + `(,(regexp-quote mu4e-main-buffer-name) + display-buffer-same-window)) - ;; misc - mu4e-change-filenames-when-moving t - mu4e-confirm-quit nil - mu4e-compose-dont-reply-to-self t - mu4e-get-mail-command "mbsync -a && mu-index-emacs-maybe" - mu4e-use-fancy-chars t + ;; special mark for junk (which is different from trash) + ;; trash = delete later + ;; junk = spam, which I don't want to delete so I can train spam filters + ;; + ;; NOTE weird cross symbol picked because it looks like a certain digit + (add-to-list + 'mu4e-marks + '(junk :char ("j" . "┻") + :prompt "junk" + :dyn-target + (lambda (target msg) (nd/mu4e-junk-folder msg)) + :action + (lambda + (docid msg target) + (mu4e--server-move docid + (mu4e--mark-check-target target) + "-N")))) - ;; sub some fancy chars that don't have valid codes - mu4e-headers-trashed-mark '("T" . "Ω") - mu4e-headers-unread-mark '("U" . "✉") - mu4e-headers-personal-mark '("P" . "Ρ") - mu4e-headers-list-mark '("L" . "Λ") - mu4e-headers-attach-mark '("a" . "ɑ") - mu4e-headers-thread-root-prefix '("* " . "● ") - mu4e-headers-threaded-label '("T" . "Ψ") - mu4e-headers-related-label '("R" . "↔") + ;; buttload of common settings I like + (setq mail-user-agent 'mu4e-user-agent + message-kill-buffer-on-exit t - ;; directories - mu4e-attachment-dir "~/Downloads" - - ;; headers - mu4e-headers-show-target nil - mu4e-headers-fields '((:human-date . 11) - (:flags . 5) - (:from . 22) - (:thread-subject)) - mu4e-headers-date-format "%F" - mu4e-headers-time-format "%R" + ;; misc + mu4e-change-filenames-when-moving t + mu4e-confirm-quit nil + mu4e-compose-dont-reply-to-self t + mu4e-get-mail-command "mbsync -a && mu-index-emacs-maybe" + mu4e-use-fancy-chars t - ;; view - mu4e-view-show-images t - mu4e-view-show-addresses t - mu4e-view-prefer-html t + ;; sub some fancy chars that don't have valid codes + mu4e-headers-trashed-mark '("T" . "Ω") + mu4e-headers-unread-mark '("U" . "✉") + mu4e-headers-personal-mark '("P" . "Ρ") + mu4e-headers-list-mark '("L" . "Λ") + mu4e-headers-attach-mark '("a" . "ɑ") + mu4e-headers-thread-root-prefix '("* " . "● ") + mu4e-headers-threaded-label '("T" . "Ψ") + mu4e-headers-related-label '("R" . "↔") - ;; compose - mu4e-compose-signature-auto-include nil ;; sigs are annoying by default - mu4e-compose-signature "Thank you,\nNathan Dwarshuis" + ;; directories + mu4e-attachment-dir "~/Downloads" + + ;; headers + mu4e-headers-show-target nil + mu4e-headers-fields '((:human-date . 11) + (:flags . 5) + (:from . 22) + (:thread-subject)) + mu4e-headers-date-format "%F" + mu4e-headers-time-format "%R" - ;; aliases - mail-personal-alias-file (no-littering-expand-etc-file-name - "mailrc") + ;; view + mu4e-view-show-images t + mu4e-view-show-addresses t + mu4e-view-prefer-html t - ;; yanking (aka citing) - message-yank-prefix "" ;; the ">" characters are annoying - message-yank-cited-prefix "" - message-yank-empty-prefix "" + ;; compose + message-signature nil - ;; contexts (multiple inboxes) - mu4e-context-policy 'pick-first - mu4e-compose-context-policy 'ask-if-none - mu4e-contexts - (list - (nd/make-mu4e-context "personal" - "yavin4" - "ndwar@yavin4.ch" - "peart4prez.yavin4.ch" - 'sent) - (nd/make-mu4e-context "alpha" - "gmail" - "natedwarshuis@gmail.com" - "smtp.gmail.com" - 'delete))) - - ;; enable visual line mode and spell checking - (add-hook 'mu4e-compose-mode-hook 'turn-off-auto-fill) - (add-hook 'mu4e-compose-mode-hook 'visual-line-mode) - (add-hook 'mu4e-view-mode-hook 'turn-off-auto-fill) - (add-hook 'mu4e-view-mode-hook 'visual-line-mode) - (add-hook 'mu4e-compose-mode-hook (lambda () (flyspell-mode 1))) - - ;; Outlook doesn't know how to fold mu4e messages by default - ;; This is enabled by using 32 underscores followed by the addressing - ;; info of the previou message(s). - (require 'nnheader) ; necessary for the header macros below + ;; aliases + mail-personal-alias-file (no-littering-expand-etc-file-name + "mailrc") - (defun nd/message-insert-citation-header () - "Insert the header of the reply message." - (let* ((h message-reply-headers) - (sep "________________________________") - (from (concat "From: " (mail-header-from h))) - (date (concat "Sent: " (mail-header-date h))) - (to (concat "To: " user-full-name)) - (subj (concat "Subject: " (message-strip-subject-re (mail-header-subject h))))) - (insert (string-join `("" ,sep ,from ,date ,to ,subj "") "\n")))) - - (setq message-citation-line-function 'nd/message-insert-citation-header) + ;; yanking (aka citing) + message-yank-prefix "" ;; the ">" characters are annoying + message-yank-cited-prefix "" + message-yank-empty-prefix "" - ;; prevent html to text conversion from destroying links - (setq - mu4e-compose-pre-hook - (lambda () - (let* ((msg mu4e-compose-parent-message) - (html (and msg (plist-get msg :body-html))) - ;; oops, mu4e screwed up - (mu4e-html2text-command - (nd/if-bin "pandoc" - "pandoc -f html -t plain --reference-links" - 'mu4e-shr2text))) - (when (and html mu4e-view-prefer-html (member mu4e-compose-type '(reply forward))) - ;; hackity hack, since the normal mu4e-message-body-text function - ;; does not render the desired html, do it here and force the - ;; aforementioned function to only look at text by removing - ;; the html - (plist-put msg :body-txt (mu4e~html2text-shell msg mu4e-html2text-command)) - (plist-put msg :body-html nil))))) + ;; contexts (multiple inboxes) + mu4e-context-policy 'pick-first + mu4e-compose-context-policy 'ask-if-none) - (require 'smtpmail) - ;; (require 'smtpmail-async) - (setq send-mail-function 'smtpmail-send-it - smtpmail-debug-info nil - auth-source-debug nil - message-send-mail-function 'smtpmail-send-it) - (setq auth-sources '(password-store)) + ;; enable visual line mode and spell checking + (add-hook 'mu4e-compose-mode-hook 'turn-off-auto-fill) + (add-hook 'mu4e-compose-mode-hook 'visual-line-mode) + (add-hook 'mu4e-view-mode-hook 'turn-off-auto-fill) + (add-hook 'mu4e-view-mode-hook 'visual-line-mode) + (add-hook 'mu4e-compose-mode-hook (lambda () (flyspell-mode 1))) - (defun nd/mu-init () - "Initialize the mu database" - (->> mu4e-contexts - (--map (->> (mu4e-context-vars it) - (alist-get 'user-mail-address) - (format "--my-address=%s"))) - (s-join " ") - (format "mu init --maildir /mnt/data/Mail %s") - (shell-command-to-string))) + ;; Outlook doesn't know how to fold mu4e messages by default + ;; This is enabled by using 32 underscores followed by the addressing + ;; info of the previou message(s). + (require 'nnheader) ; necessary for the header macros below - (defun nd/lookup-oauth-secret (type user) - (->> (format "pass email/%s/%s" user type) - (shell-command-to-string) - (s-trim))) + (defun nd/message-insert-citation-header () + "Insert the header of the reply message." + (let* ((h message-reply-headers) + (sep "________________________________") + (from (concat "From: " (mail-header-from h))) + (date (concat "Sent: " (mail-header-date h))) + (to (concat "To: " user-full-name)) + (subj (concat "Subject: " (message-strip-subject-re (mail-header-subject h))))) + (insert (string-join `("" ,sep ,from ,date ,to ,subj "") "\n")))) - (defun nd/xoauth2-get-secrets (host user port) - (when (and (string= host "smtp.gmail.com") - (string= user "natedwarshuis@gmail.com") - (string= port "587")) - (list :token-url (nd/lookup-oauth-secret "token_url" user) - :client-id (nd/lookup-oauth-secret "client_id" user) - :client-secret (nd/lookup-oauth-secret "client_secret" user) - :refresh-token (nd/lookup-oauth-secret "refresh_token" user)))) + (setq message-citation-line-function 'nd/message-insert-citation-header) - (use-package auth-source-xoauth2 - :straight t - :after smtpmail - :config - (setq auth-source-xoauth2-creds #'nd/xoauth2-get-secrets) - (add-to-list 'smtpmail-auth-supported 'xoauth2) - (auth-source-xoauth2-enable))) + ;; prevent html to text conversion from destroying links + (setq + mu4e-compose-pre-hook + (lambda () + (let* ((msg mu4e-compose-parent-message) + (html (and msg (plist-get msg :body-html))) + ;; oops, mu4e screwed up + (mu4e-html2text-command + (nd/if-bin "pandoc" + "pandoc -f html -t plain --reference-links" + 'mu4e-shr2text))) + (when (and html mu4e-view-prefer-html (member mu4e-compose-type '(reply forward))) + ;; hackity hack, since the normal mu4e-message-body-text function + ;; does not render the desired html, do it here and force the + ;; aforementioned function to only look at text by removing + ;; the html + (plist-put msg :body-txt (mu4e~html2text-shell msg mu4e-html2text-command)) + (plist-put msg :body-html nil))))) + + (setq send-mail-function 'smtpmail-send-it + smtpmail-debug-info nil + auth-source-debug nil + message-send-mail-function 'smtpmail-send-it) + + ;; load instance-specific accounts + (load-file acnts-path)))) #+END_SRC ** shell #+begin_src emacs-lisp diff --git a/local/lib/.gitignore b/local/lib/.gitignore new file mode 100644 index 0000000..2cf33f2 --- /dev/null +++ b/local/lib/.gitignore @@ -0,0 +1 @@ +!mu4e \ No newline at end of file