;;; mail-settings.el --- Mail settings -*- lexical-binding: t -*- ;;; Commentary: ;; https://kkatsuyuki.github.io/notmuch-conf/ ;; https://opensource.com/article/20/1/organize-email-notmuch ;; https://notmuchmail.org/emacstips/ ;; https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-2-notmuch-and-emacs/ ;; `mm-text-html-renderer' ;; `mailcap-parse-mailcaps' in mailcap.el uses a given path or the ;; value of environment variable MAILCAPS if set or either ;; ("~/.mailcap", "/etc/mailcap", "/usr/etc/mailcap", ;; "/usr/local/etc/mailcap") for *nix or ("~/.mailcap", "~/mail.cap", ;; "~/etc/mail.cap") for `mailcap-poor-system-types'. parsed ;; mailcaps, see `mailcap-parsed-p', are in `mailcap-mime-data'. ;; `notmuch-show-view-part' `mm-inlined-types' ;; `notmuch-show-attachment-debug' ;;; Requirments: ;; (mu / mu4e) ;; mu4e-maildirs-extension https://melpa.org/#/mu4e-maildirs-extension https://github.com/agpchil/mu4e-maildirs-extension https://www.djcbsoftware.nl/code/mu/mu4e/ ;; (notmuch) ;;; Code: (use-package message :defer t :config (set-face-attribute 'message-header-name nil :foreground "#4f97d7") (set-face-attribute 'message-header-subject nil :foreground "#bc6ec5") (set-face-attribute 'message-header-to nil :foreground "#c56ec3") (set-face-attribute 'message-header-cc nil :foreground "#c56ec3") (set-face-attribute 'message-header-other nil :foreground "#ce537a") (set-face-attribute 'message-cited-text nil :foreground "#9f8766") (set-face-attribute 'message-mml nil :foreground "#86dc2f") ;; mime meta language ;; define in custom.el user-mail-address, user-full-name (setq message-directory "~/.local/share/mail/") ;; sent mails (setq message-auto-save-directory "~/.local/share/mail/drafts/") ;; drafts (setq message-kill-buffer-on-exit t) ;; after sending mail (setq mm-text-html-renderer 'shr)) (defgroup my-mu4e nil "My mu4e concept mapping" :prefix "my-mu4e-" :group 'my) (defcustom my-mu4e-sync 'test "One of `test', `offlineimap' and `mbsync'. see also `my-mu4e-offlineimap-command' and `my-mu4e-mbsync-command'. `mu4e-get-mail-command' and `mu4e-change-filenames-when-moving' will be set according to this variable." :group 'my-mu4e :type '(choice symbol (const :tag "test" test) (const :tag "offlineimap" offlineimap) (const :tag "mbsync" mbsync))) (defcustom my-mu4e-offlineimap-command "offlineimap" "Oflineimap command used for mu4e if set in `my-mu4e-sync'." :group 'my-mu4e :type '(choice string (const :tag "offlineimap" "offlineimap"))) (defcustom my-mu4e-mbsync-command "mbsync" "mbsync command used for mu4e if set in `my-mu4e-sync'." :group 'my-mu4e :type '(choice string (const :tag "mbsync" "mbsync"))) (defcustom my-mu4e-bookmarks '((:name "Unread messages" :query "flag:unread AND NOT flag:trashed" :key ?u) (:name "Today's messages" :query "date:today..now" :key ?t) (:name "Last 7 days" :query "date:7d..now" :hide-unread t :key ?w) (:name "Messages with images" :query "mime:image/*" :key ?p)) "Documention see `mu4e-bookmarks' in `mu4e-vars.el'. :query same as \"mu find xxx\"" :group 'my-mu4e :type '(repeat (plist))) (defcustom my-mu4e-attachment-dir (expand-file-name "~/") "Documention see `mu4e-attachment-dir' in `mu4e-vars.el'." :group 'my-mu4e :type '(choice directory (const :tag "~/" "~/") (const :tag "~/Downloads" "~/Downloads")) :safe 'stringp) (defcustom my-mu4e-contexts nil "Documention see `mu4e-contexts' in `mu4e-context.el'." :group 'my-mu4e :type '(list)) (defcustom my-mu4e-user-mailing-lists nil "Documention see `mu4e-user-mailing-lists' in `mu4e-lists.el'." :group 'my-mu4e :type '(repeat (cons string string))) ;; note `mu4e-headers-results-limit' ;; headers view ;; [/] `mu4e-headers-search-narrow' - filter messages in current search query (x AND y) ;; [\] `mu4e-headers-query-prev' - go back to previous search query (use-package mu4e :delight (mu4e-main-mode "Mum") ;; "\u01F15C\uFF4D" (mu4e-headers-mode "Muh") ;; "\u01515C\uFF48" will be overwitten with `mu4e~headers-search-execute' (mu4e-view-mode "Muv") ;; "\u01F15C\uFF56" (mu4e~update-mail-mode "Muu") ;; "\u01F15C\uFF55" :commands mu4e :config ;; allow to display emoji in the subject lines of the emails (delete 'mu4e-headers-mode emojify-inhibit-major-modes) ;; https://github.com/iqbalansari/emacs-emojify/issues/39 ;; set `mu4e-get-mail-command' (runs mu index afterwards, by default key U in main or C-c C-u else) ;; and `mu4e-change-filenames-when-moving' ;; no default cond for custom settings (cond ((eq my-mu4e-sync 'test) "reset to saved (`custom-set-variables') or standard (if not saved) value" (custom-reevaluate-setting 'mu4e-get-mail-command) ;; true for testing (custom-reevaluate-setting 'mu4e-change-filenames-when-moving)) ((eq my-mu4e-sync 'offlineimap) (setq mu4e-get-mail-command my-mu4e-offlineimap-command) (setq mu4e-change-filenames-when-moving nil)) ;; nil better for offlineimap ((eq my-mu4e-sync 'mbsync) (setq mu4e-get-mail-command my-mu4e-mbsync-command) (setq mu4e-change-filenames-when-moving t))) ;; t better for mbsync ;;; vars (setq mu4e-completing-read-function 'ivy-completing-read) (setq mu4e-confirm-quit nil) (setq mu4e-use-fancy-chars nil) ;; use unicodes for fancy tags etc. But this does not means emojis are activated. (setq mu4e-bookmarks my-mu4e-bookmarks) (setq mu4e-attachment-dir my-mu4e-attachment-dir) ;;; headers ;;(setq mu4e-headers-results-limit 500) ;; to ignore the limit for one query press a C-u before invoking the search. consider narrowing the search instead of increasing the limit number (setq mu4e-headers-fields '((:human-date . 12) (:flags . 6) (:mailing-list . 10) ;;(:from . 22) (:from-or-to . 22) ;; :from-or-to shows From: except when the e-mail was sent by the user (i.e., you) in that case it shows To: (prefixed by To, see also `mu4e-headers-from-or-to-prefix') (:subject))) ;;(setq mu4e-headers-from-or-to-prefix '("" . "To ")) ;; prefix "To " for sent messages ;;; message ;;(setq mu4e-html2text-command ...) ;;; view (setq mu4e-view-show-images t) (setq mu4e-view-show-addresses t) (add-to-list 'mu4e-headers-actions '("view in browser" . mu4e-action-view-in-browser)) (add-to-list 'mu4e-view-actions '("view in browser" . mu4e-action-view-in-browser)) ;;; actions (setq mu4e-action-tags-header "X-Keywords") ;; for e.g. gmail ;;; compose ;; use mu4e for e-mail in emacs (setq mail-user-agent 'mu4e-user-agent) ;; for completion for To, CC, BSS fields ;; (setq mu4e-sent-messages-behavior 'delete) ;; TODO: sent is automatic done via server? therefore delete? ;;; draft ;;(defalias 'mu4e-compose-signature 'message-signature) ;; point to `message-signature' (setq mu4e-compose-signature-auto-include nil) ;; see also `mu4e-compose-signature defined in contexts ;;; context ;; switch context with M-x mu4e-context-switch (force reload (if same context) with prefix-argument C-u) ;; see current context with M-x mu4e-context-current (setq mu4e-contexts my-mu4e-contexts) (defun my-mu4e-message-maildir-matches (msg rx) "helper to determine which account context to use based on the folder in maildir the email (eg. ~/.mail/work) is located in." (when rx (if (listp rx) ;; If rx is a list, try each one for a match (or (mu4e-message-maildir-matches msg (car rx)) (mu4e-message-maildir-matches msg (cdr rx))) ;; Not a list, check rx (string-match rx (mu4e-message-field msg :maildir))))) ;; overwite in custom.el (defun my-mu4e-refile-folder-work (msg) "Function for chosing the refile folder for the work account. MSG is a message p-list from mu4e." (cond ;; nil no refiling )) ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; guess or ask the correct context, e.g. (setq mu4e-context-policy 'pick-first) ;; start with the first (default) context; default is to ask-if-none (ask when there's no context yet, and none match) ;; (setq mu4e-compose-context-policy nil) ;; compose with the current context is no context matches; default is to ask ;;; lists ;; The shortname (friendly) should a at most 8 characters, camel-case, see also `mu4e-mailing-list-patterns' (setq mu4e-user-mailing-lists my-mu4e-user-mailing-lists) ;;; org ;;(setq mu4e-org-link-query-in-headers-mode nil) ;; point to message (default) rather then the query ) ;; TODO: removed ;; https://github.com/agpchil/mu4e-maildirs-extension ;; https://github.com/djcb/mu/pull/1586 ;; (use-package mu4e-maildirs-extension ;; :after mu4e ;; :config ;; (setq mu4e-maildirs-extension-title ;; (concat " Maildirs\t([" ;; (key-description mu4e-maildirs-extension-toggle-maildir-key) ;; "] fold, [C-u " ;; (key-description mu4e-maildirs-extension-toggle-maildir-key) ;; "] fold w/ children, [RET]/[mouse-2] jump to, [C-u RET] jump to unread only)\n")) ;; (setq mu4e-maildirs-extension-action-text ;; (concat "\t* [" ;; (key-description mu4e-maildirs-extension-action-key) ;; "]pdate index & cache\t([C-u " ;; (key-description mu4e-maildirs-extension-action-key) ;; "] clear cache, [C-u C-u " ;; (key-description mu4e-maildirs-extension-action-key) ;; "] clear and refresh)\n")) ;; (setq mu4e-maildirs-extension-maildir-default-prefix "└") ;;├> └> ;; (setq mu4e-maildirs-extension-fake-maildir-separator "\\.") ;; ;; ;; overwrite to remove double entries: ;; ;; /desy/* ;; ;; /desy/Drafts ;; ;; /desy/INBOX ;; ;; /desy/INBOX.event ;; ;; /desy/INBOX.Ing ;; ;; /desy/INBOX/Ing/* ;; ;; /desy/INBOX.Ing.CAD ;; ;; /desy/INBOX/Ing/CAD/* ;; ;; alteration: searching for "/.*/.*" pathes and do not append /* to them. ;; (defun mu4e-maildirs-extension-paths () ;; "Get maildirs paths." ;; (let ((paths (mu4e-maildirs-extension-get-relevant-maildirs)) ;; (paths-to-show nil)) ;; ;; (mapc #'(lambda (name) ;; (let ((parents (butlast (mu4e-maildirs-extension-parse name))) ;; (path nil)) ;; (mapc #'(lambda (parent-name) ;; (setq path (concat path "/" parent-name)) ;; (unless (or (member path paths-to-show) ;; (string-match-p "/.*/.*" path)) ;; (add-to-list 'paths-to-show (format "%s/*" path) t))) ;; parents)) ;; (add-to-list 'paths-to-show name t)) ;; paths) ;; paths-to-show)) ;; ;; (mu4e-maildirs-extension)) (defgroup my-notmuch nil "My notmuch concept mapping" :prefix "my-notmuch-" :group 'my) (defcustom my-notmuch-tag-formats '(("unread" (propertize tag 'face 'notmuch-tag-unread)) ("flagged" (propertize tag 'face 'notmuch-tag-flagged) (notmuch-tag-format-image-data tag (notmuch-tag-star-icon)))) "Documention see `notmuch-tag-formats' in `notmuch-tag.el'." :group 'my-notmuch :type 'notmuch-tag-format-type) (defcustom my-notmuch-saved-searches `((:name "inbox" :query "tag:inbox" :key ,(kbd "i")) (:name "unread" :query "tag:unread" :key ,(kbd "u")) (:name "flagged" :query "tag:flagged" :key ,(kbd "f")) (:name "sent" :query "tag:sent" :key ,(kbd "t")) (:name "drafts" :query "tag:draft" :key ,(kbd "d")) (:name "all mail" :query "*" :key ,(kbd "a"))) "Documention see `notmuch-saved-searches' in `notmuch-hello.el'." :group 'my-notmuch :get 'notmuch-hello--saved-searches-to-plist :type '(repeat notmuch-saved-search-plist) :tag "List of Saved Searches") (use-package notmuch :delight (notmuch-hello-mode "Nmh") ;; "\u01F15D\uFF48" (notmuch-message-mode "Nmm") ;; "\u01F15D\uFF4D" (notmuch-search-mode "Nms") ;; "\u01F15D\uFF53" (notmuch-show-mode "Nmv") ;; "\u01F15D\uFF57" (notmuch-tree-mode "Nmt") ;; "\u01F15D\uFF54" :commands notmuch :config (set-face-attribute 'notmuch-tag-flagged nil :foreground "#b1951d") (setq notmuch-tag-formats my-notmuch-tag-formats) (setq notmuch-search-oldest-first nil) ;; newest first (setq notmuch-saved-searches my-notmuch-saved-searches) (add-to-list 'notmuch-hello-sections 'my-notmuch-hello-insert-footer t) (defun my-notmuch-hello-insert-footer () "Insert the notmuch-hello footer." (widget-insert "\n") (let ((start (point))) ;; to center text (widget-insert "To download mails execute ") (if (executable-find "offlineimap") (widget-create 'link :notify (lambda (&rest ignore) (my-mail-exec-offlineimap)) :button-prefix "" :button-suffix "" :help-echo "my-mail-exec-offlineimap" "OfflineIMAP") (widget-create 'link :notify (lambda (&rest ignore) (message "offlineimap is not installed")) :button-prefix "" :button-suffix "" :button-face 'error :help-echo "offlineimap is not installed" "OfflineIMAP")) (widget-insert " (also using ") (if (executable-find "notmuch") (widget-insert "notmuch") (widget-insert (propertize "notmuch" 'face 'error))) (widget-insert " and ") (if (executable-find "afew") (widget-insert "afew") (widget-insert (propertize "afew" 'face 'error))) (widget-insert ")") (let ((fill-column (- (window-width) notmuch-hello-indent))) (center-region start (point))))) (defun my-mail-exec-offlineimap () "execute offlineimap" (interactive) (set-process-sentinel (start-process-shell-command "offlineimap" "*offlineimap*" "offlineimap -o") #'(lambda (process event) (notmuch-refresh-all-buffers) (let ((w (get-buffer-window "*offlineimap*"))) (when w (with-selected-window w (recenter (window-end))))))) (popwin:display-buffer "*offlineimap*")) ;; add a special buffer config (add-to-list 'popwin:special-display-config '("*offlineimap*" :dedicated t :position bottom :stick t :height 0.4 :noselect t)) ) (provide 'mail-settings) ;;; mail-settings.el ends here