572 lines
24 KiB
EmacsLisp
572 lines
24 KiB
EmacsLisp
;;; mu4e-config.el --- mu4e email configuration -*- lexical-binding: t; -*-
|
|
|
|
;; mu4e should already be loaded from .emacs before this file is loaded
|
|
;; If not loaded, try to load it
|
|
(add-to-list 'load-path "/opt/homebrew/Cellar/mu/1.12.12/share/emacs/site-lisp/mu/mu4e")
|
|
(require 'mu4e)
|
|
|
|
;; HTML rendering configuration for mu4e 1.12
|
|
|
|
;; Use shr as the default renderer
|
|
(setq mm-text-html-renderer 'shr)
|
|
|
|
;; Configure shr for better plain text display
|
|
;; (setq shr-use-colors nil) ; No colors
|
|
;; (setq shr-use-fonts nil) ; No variable fonts
|
|
;; (setq shr-width 80) ; 80 column width
|
|
(setq shr-bullet "• ") ; Nice bullet
|
|
|
|
;; Increase font size in SHR (HTML rendering)
|
|
(defun my-shr-rescale-font ()
|
|
"Increase font size in SHR rendered content."
|
|
(text-scale-set 1)) ; Increase by 1 step, adjust as needed (2, 3, etc.)
|
|
|
|
;; Apply font scaling to mu4e HTML viewing
|
|
(add-hook 'mu4e-view-mode-hook 'my-shr-rescale-font)
|
|
|
|
;; Create a custom command to view HTML with pandoc
|
|
(defun mu4e-view-html-with-pandoc ()
|
|
"View the HTML part of the current message using pandoc."
|
|
(interactive)
|
|
(let ((msg (mu4e-message-at-point)))
|
|
(unless msg
|
|
(mu4e-error "No message at point"))
|
|
(let* ((path (mu4e-message-field msg :path))
|
|
(pandoc-buffer "*mu4e-pandoc*")
|
|
(temp-dir (make-temp-file "mu4e-extract" t)))
|
|
(if path
|
|
(progn
|
|
(with-current-buffer (get-buffer-create pandoc-buffer)
|
|
(erase-buffer)
|
|
(insert "=== HTML Email Rendered with Pandoc ===\n\n")
|
|
;; Extract all parts
|
|
(call-process "mu" nil nil nil
|
|
"extract" "--save-all"
|
|
(format "--target-dir=%s" temp-dir) path)
|
|
;; Find HTML files and convert them
|
|
(let ((html-files (directory-files temp-dir t "\\.html?$")))
|
|
(if html-files
|
|
(dolist (html-file html-files)
|
|
(insert (shell-command-to-string
|
|
(format "pandoc -f html -t markdown --wrap=auto --columns=80 '%s' 2>/dev/null"
|
|
html-file))))
|
|
;; No HTML files found, try extracting from message directly
|
|
(let ((raw-msg (shell-command-to-string (format "cat '%s'" path))))
|
|
;; Look for HTML content between boundaries
|
|
(if (string-match "Content-Type: text/html" raw-msg)
|
|
(progn
|
|
(insert "Converting HTML content...\n\n")
|
|
(let ((temp-html (make-temp-file "mu4e-msg" nil ".html")))
|
|
(with-temp-file temp-html
|
|
(insert raw-msg))
|
|
(insert (shell-command-to-string
|
|
(format "cat '%s' | sed -n '/Content-Type: text\\/html/,/^--/p' | sed '1,/^$/d' | sed '/^--/,$d' | pandoc -f html -t markdown --wrap=auto --columns=80 2>/dev/null"
|
|
temp-html)))
|
|
(delete-file temp-html)))
|
|
(insert "No HTML content found in this message.\n")))))
|
|
;; Clean up temp directory
|
|
(delete-directory temp-dir t)
|
|
(goto-char (point-min))
|
|
(view-mode)
|
|
(display-buffer (current-buffer))))
|
|
(mu4e-warn "Cannot access message file")))))
|
|
|
|
;; Simpler approach: Add action to view HTML with pandoc
|
|
(add-to-list 'mu4e-view-actions
|
|
'("pandoc" . (lambda (msg)
|
|
(let* ((path (mu4e-message-field msg :path))
|
|
(pandoc-buffer "*mu4e-pandoc*"))
|
|
(when path
|
|
(with-current-buffer (get-buffer-create pandoc-buffer)
|
|
(erase-buffer)
|
|
(insert "=== HTML Email Rendered with Pandoc ===\n\n")
|
|
(let ((html-content
|
|
(shell-command-to-string
|
|
(format "mu view '%s' 2>/dev/null | awk '/text\\/html/{flag=1; next} flag && /^--/{exit} flag' | pandoc -f html -t markdown --wrap=auto --columns=80 2>/dev/null" path))))
|
|
(if (and html-content (> (length html-content) 0))
|
|
(insert html-content)
|
|
;; If no HTML part found, show message
|
|
(insert "No HTML content found in this message.\n\n")
|
|
(insert (shell-command-to-string
|
|
(format "mu view '%s'" path)))))
|
|
(goto-char (point-min))
|
|
(view-mode)
|
|
(display-buffer (current-buffer)))))))
|
|
t)
|
|
|
|
;; Add keybinding for pandoc view
|
|
(with-eval-after-load 'mu4e-view
|
|
(define-key mu4e-view-mode-map (kbd "H") 'mu4e-view-html-with-pandoc))
|
|
|
|
(message "mu4e: HTML rendering configured. Press 'H' in message view to render with pandoc.")
|
|
|
|
;; Basic mu4e settings
|
|
(setq mu4e-maildir "~/Maildir"
|
|
;; Use our processing script instead of plain mbsync
|
|
;; This will sync mail and fix List-Id headers
|
|
mu4e-get-mail-command (expand-file-name "process-mail.sh" user-emacs-directory)
|
|
mu4e-update-interval 300 ; Update every 5 minutes
|
|
mu4e-compose-signature-auto-include nil
|
|
mu4e-view-show-images t
|
|
mu4e-view-show-addresses t
|
|
mu4e-change-filenames-when-moving t ; Needed for mbsync
|
|
mu4e-index-cleanup t ; Clean up after moving
|
|
mu4e-index-lazy-check nil ; Don't be lazy about indexing
|
|
mu4e-hide-index-messages t) ; Hide indexing messages to avoid errors
|
|
|
|
;; Function to get current context's maildir prefix
|
|
(defun mu4e-current-context-maildir-prefix ()
|
|
"Get the maildir prefix for the current context."
|
|
(if mu4e-context-current
|
|
(let ((context-name (mu4e-context-name mu4e-context-current)))
|
|
(format "maildir:/%s/*" context-name))
|
|
""))
|
|
|
|
;; Bookmarks (shortcuts to common searches)
|
|
;; Use setq to define the complete list at once
|
|
(setq mu4e-bookmarks
|
|
'(;; Basic views - work in current context
|
|
(: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"
|
|
:key ?w)
|
|
|
|
;; Smart mailboxes - search across all contexts
|
|
(:name "📌 Important"
|
|
:query "(flag:flagged OR prio:high OR from:/boss|manager|ceo|director|important/ OR subject:/urgent|important|critical|asap/) AND NOT flag:trashed"
|
|
:key ?i)
|
|
|
|
(:name "📰 Newsletters"
|
|
:query "(from:/nytimes|newyorktimes|atlantic|politico/ OR from:nytimes.com OR from:theatlantic.com OR from:politico.com OR from:politico.eu) AND NOT flag:trashed AND NOT flag:flagged"
|
|
:key ?n)
|
|
|
|
(:name "🛍️ Purchases & Orders"
|
|
:query "(from:/amazon|ebay|paypal|stripe|shopify|order|store|shop|invoice|receipt/ OR subject:/order|invoice|receipt|purchase|payment|confirmation|shipping|delivery|tracking/) AND NOT flag:trashed"
|
|
:key ?p)
|
|
|
|
(:name "📎 Attachments"
|
|
:query "flag:attach AND NOT flag:trashed"
|
|
:key ?a)
|
|
|
|
(:name "🎫 Travel & Tickets"
|
|
:query "(from:/airline|hotel|booking|expedia|airbnb|uber|lyft|train|eventbrite|ticketmaster/ OR subject:/booking|reservation|ticket|flight|itinerary|confirmation/) AND NOT flag:trashed"
|
|
:key ?v)
|
|
|
|
(:name "💰 Finance & Banking"
|
|
:query "(from:/bank|credit|visa|mastercard|amex|insurance|tax|accountant/ OR subject:/statement|balance|transaction|payment/) AND NOT flag:trashed"
|
|
:key ?f)
|
|
|
|
(:name "👥 Social & Forums"
|
|
:query "(from:/facebook|twitter|linkedin|instagram|reddit|github|gitlab|discourse|forum/ OR subject:/commented|replied|mentioned|tagged|followed/) AND NOT flag:trashed"
|
|
:key ?s)
|
|
|
|
;; Mailing Lists (Personal context)
|
|
(:name "📋 All Mailing Lists"
|
|
:query "maildir:/Personal/Lists AND NOT flag:trashed"
|
|
:key ?L)
|
|
|
|
(:name "📋 C++ std-discussion"
|
|
:query "list:std-discussion.lists.isocpp.org AND NOT flag:trashed"
|
|
:key ?C)
|
|
|
|
(:name "📋 Qt Interest"
|
|
:query "list:interest.qt-project.org AND NOT flag:trashed"
|
|
:key ?q)
|
|
|
|
(:name "📋 Boost"
|
|
:query "list:boost.lists.boost.org AND NOT flag:trashed"
|
|
:key ?b)
|
|
|
|
(:name "📋 GCC"
|
|
:query "list:gcc.gcc.gnu.org AND NOT flag:trashed"
|
|
:key ?G)
|
|
|
|
(:name "📋 LKML"
|
|
:query "list:linux-kernel.vger.kernel.org AND NOT flag:trashed"
|
|
:key ?K)))
|
|
|
|
(setq mu4e-maildir-shortcuts
|
|
'(("/Personal/INBOX" . ?i)
|
|
("/Personal/Sent" . ?s)
|
|
("/Personal/Trash" . ?t)
|
|
("/Personal/Drafts" . ?d)
|
|
("/Personal/Spam " . ?S)
|
|
("/Personal/Archive" . ?a)
|
|
("/Personal/Lists" . ?l)
|
|
("/IONOS/INBOX" . ?I)
|
|
("/IONOS/Sent" . ?S)
|
|
("/IONOS/Trash" . ?T)
|
|
("/IONOS/Drafts" . ?D)
|
|
("/IONOS/Archive" . ?A)))
|
|
|
|
;; Custom function for fuzzy relative timestamps
|
|
(defun my-mu4e-format-date (date)
|
|
"Format DATE as a fuzzy relative time string."
|
|
(let* ((now (float-time))
|
|
(time (float-time date))
|
|
(diff (- now time))
|
|
(sec diff)
|
|
(min (/ diff 60))
|
|
(hour (/ diff 3600))
|
|
(day (/ diff 86400))
|
|
(week (/ diff 604800))
|
|
(month (/ diff 2592000))
|
|
(year (/ diff 31536000)))
|
|
(cond
|
|
((< sec 60) "just now")
|
|
((< min 2) "1 min ago")
|
|
((< min 60) (format "%d mins ago" (truncate min)))
|
|
((< hour 2) "1 hour ago")
|
|
((< hour 24) (format "%d hours ago" (truncate hour)))
|
|
((< day 2) "yesterday")
|
|
((< day 7) (format "%d days ago" (truncate day)))
|
|
((< week 2) "1 week ago")
|
|
((< week 4) (format "%d weeks ago" (truncate week)))
|
|
((< month 2) "1 month ago")
|
|
((< month 12) (format "%d months ago" (truncate month)))
|
|
((< year 2) "1 year ago")
|
|
(t (format "%d years ago" (truncate year))))))
|
|
|
|
;; Custom header field for fuzzy date
|
|
(add-to-list 'mu4e-header-info-custom
|
|
'(:fuzzy-date . (:name "Date"
|
|
:shortname "Date"
|
|
:function (lambda (msg)
|
|
(my-mu4e-format-date (mu4e-message-field msg :date))))))
|
|
|
|
;; UI Configuration - use fuzzy dates in headers
|
|
(setq mu4e-headers-fields
|
|
'((:fuzzy-date . 15) ; Fuzzy date with 15 char width
|
|
(:flags . 6)
|
|
(:from-or-to . 22)
|
|
(:subject)))
|
|
|
|
;; Make mu4e respect the current color theme
|
|
(setq mu4e-view-use-gnus t) ; Use Gnus article mode for better theme support
|
|
(setq shr-use-colors nil) ; Let the theme handle colors in HTML emails
|
|
|
|
;; Use full window for reading emails
|
|
(setq mu4e-split-view nil) ; Don't split, use full window for message view
|
|
|
|
;; Make headers/search view respect theme
|
|
(setq mu4e-headers-unread-face 'bold) ; Use theme's bold face instead of custom color
|
|
(setq mu4e-headers-highlight-face 'highlight) ; Use theme's highlight face
|
|
(setq mu4e-headers-flagged-face 'font-lock-warning-face) ; Use theme's warning face
|
|
|
|
;; Enable inline images
|
|
(setq mu4e-view-show-images t)
|
|
(when (fboundp 'imagemagick-register-types)
|
|
(imagemagick-register-types))
|
|
|
|
;; Colorize inline patches in emails
|
|
(require 'diff-mode)
|
|
|
|
(defun mu4e-colorize-patch ()
|
|
"Colorize patches in mu4e view buffers."
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
;; Look for patch sections (starting with diff, ---, or @@)
|
|
(while (re-search-forward "^\\(diff \\|--- \\|\\+\\+\\+ \\|@@ \\)" nil t)
|
|
(let ((patch-start (match-beginning 0)))
|
|
;; Find the end of the patch
|
|
(if (re-search-forward "^[^-+@ \t]" nil t)
|
|
(backward-char)
|
|
(goto-char (point-max)))
|
|
(let ((patch-end (point))
|
|
(inhibit-read-only t))
|
|
;; Apply diff-mode font-lock to the patch region
|
|
(add-text-properties patch-start patch-end
|
|
'(face nil)) ; Reset face first
|
|
(save-restriction
|
|
(narrow-to-region patch-start patch-end)
|
|
(diff-mode)
|
|
(font-lock-fontify-region patch-start patch-end)
|
|
(widen))))))
|
|
;; Also colorize simple diff lines
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "^\\(-.*\\)$" nil t)
|
|
(let ((inhibit-read-only t))
|
|
(add-face-text-property (match-beginning 1) (match-end 1)
|
|
'diff-removed)))
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "^\\(\\+.*\\)$" nil t)
|
|
(let ((inhibit-read-only t))
|
|
(add-face-text-property (match-beginning 1) (match-end 1)
|
|
'diff-added)))
|
|
(goto-char (point-min))
|
|
(while (re-search-forward "^\\(@@.*@@\\).*$" nil t)
|
|
(let ((inhibit-read-only t))
|
|
(add-face-text-property (match-beginning 1) (match-end 1)
|
|
'diff-hunk-header)))))
|
|
|
|
;; Hook to colorize patches when viewing messages
|
|
(add-hook 'mu4e-view-mode-hook 'mu4e-colorize-patch)
|
|
|
|
;; For Gnus article mode (when mu4e-view-use-gnus is t)
|
|
(with-eval-after-load 'gnus-art
|
|
(add-hook 'gnus-article-mode-hook 'mu4e-colorize-patch))
|
|
|
|
;; Use mu4e for composing email
|
|
(setq mail-user-agent 'mu4e-user-agent)
|
|
|
|
;; Keybindings
|
|
(global-set-key (kbd "C-x m") 'mu4e)
|
|
(global-set-key (kbd "C-x M") 'mu4e-compose-new)
|
|
|
|
;; Double-check pandoc is available and being used
|
|
(if (executable-find "pandoc")
|
|
(message "Pandoc found at: %s" (executable-find "pandoc"))
|
|
(message "WARNING: Pandoc not found!"))
|
|
|
|
;; Prefer plain text when available, but show HTML when it's the only option
|
|
(setq mu4e-view-prefer-html nil)
|
|
|
|
;; Actions
|
|
(add-to-list 'mu4e-view-actions
|
|
'("ViewInBrowser" . mu4e-action-view-in-browser) t)
|
|
|
|
;; Don't keep message buffers around
|
|
(setq message-kill-buffer-on-exit t)
|
|
|
|
;; For Proton Bridge: Comment out custom marks for now to avoid errors
|
|
;; The default behavior will be:
|
|
;; d - move to trash
|
|
;; D - delete permanently
|
|
;; r - refile/archive
|
|
|
|
;; If you want to customize deletion behavior, uncomment and adjust:
|
|
;; (with-eval-after-load 'mu4e
|
|
;; ;; Make 'd' archive instead of trash
|
|
;; (setf (alist-get 'trash mu4e-marks)
|
|
;; (list :char '("d" . "▼")
|
|
;; :prompt "archive"
|
|
;; :dyn-target (lambda (target msg)
|
|
;; (mu4e-get-refile-folder msg))
|
|
;; :action (lambda (docid msg target)
|
|
;; (mu4e--server-move docid
|
|
;; (mu4e--mark-check-target target)
|
|
;; "-N")))))
|
|
|
|
;; Test function to verify pandoc is working
|
|
(defun mu4e-test-pandoc ()
|
|
"Test if pandoc is being used for HTML rendering."
|
|
(interactive)
|
|
(let ((test-html "<html><body><h1>Test Header</h1><p>This is a <strong>test</strong> paragraph with <em>emphasis</em>.</p><ul><li>Item 1</li><li>Item 2</li></ul></body></html>")
|
|
(temp-file (make-temp-file "mu4e-pandoc-test" nil ".html")))
|
|
(with-temp-file temp-file
|
|
(insert test-html))
|
|
(message "Testing pandoc with command: %s" mu4e-html2text-command)
|
|
(message "Input HTML:\n%s" test-html)
|
|
(message "Pandoc output:\n%s"
|
|
(shell-command-to-string
|
|
(format "%s < %s" mu4e-html2text-command temp-file)))
|
|
(delete-file temp-file)))
|
|
|
|
;; Mailing lists configuration
|
|
(setq mu4e-mailing-lists
|
|
'((:list-id "linux-kernel.vger.linux.org" :name "linux-kernel")
|
|
(:list-id "std-discussion.lists.isocpp.org" :name "std-discussion")
|
|
(:list-id "gcc.gnu.gcc.org" :name "gnu-gcc")
|
|
(:list-id "interest.qt-project.org" :name "Qt-Interest")
|
|
(:list-id "boost.lists.boost.org" :name "Boost")
|
|
(:list-id "boost-announce.lists.boost.org" :name "Boost-Announce")
|
|
(:list-id "boost-interest.lists.boost.org" :name "Boost-Interest")))
|
|
|
|
;; Signature
|
|
(setq mu4e-compose-signature
|
|
"Jens")
|
|
|
|
;; SMTP Configuration for sending mail
|
|
(require 'smtpmail)
|
|
(setq message-send-mail-function 'smtpmail-send-it)
|
|
|
|
;; Default SMTP settings (will be overridden by context)
|
|
(setq smtpmail-stream-type 'starttls
|
|
smtpmail-smtp-service 587
|
|
smtpmail-debug-info t
|
|
smtpmail-debug-verb t
|
|
smtpmail-auth-credentials "~/.authinfo")
|
|
|
|
;; Configure context policy
|
|
(setq mu4e-context-policy 'pick-first
|
|
mu4e-compose-context-policy 'ask-if-none)
|
|
|
|
;; Function to set SMTP parameters based on From address
|
|
(defun my-mu4e-set-smtp-params ()
|
|
"Set SMTP parameters based on the From address."
|
|
(let ((from (message-field-value "From")))
|
|
(cond
|
|
;; Personal account - also handles alias addresses @luedicke.me and @luedicke.xyz
|
|
((or (string-match "@luedicke\\.me" from)
|
|
(string-match "@luedicke\\.xyz" from))
|
|
(message "Setting SMTP for Personal account (Proton Bridge)...")
|
|
(setq smtpmail-smtp-server "127.0.0.1"
|
|
smtpmail-smtp-service 1025
|
|
smtpmail-stream-type 'starttls
|
|
smtpmail-smtp-user "jens@luedicke.me" ; Always use main account for auth
|
|
smtpmail-auth-credentials "~/.authinfo"
|
|
smtpmail-smtp-timeout 30)) ; 30 second timeout
|
|
((string-match "jens@luedicke.cloud" from)
|
|
(message "Setting SMTP for IONOS account...")
|
|
(setq smtpmail-smtp-server "smtp.ionos.de"
|
|
smtpmail-smtp-service 587
|
|
smtpmail-stream-type 'starttls
|
|
smtpmail-smtp-user "jens@luedicke.cloud"
|
|
smtpmail-auth-credentials "~/.authinfo"
|
|
smtpmail-smtp-timeout 30)) ; 30 second timeout
|
|
(t
|
|
(error "Unknown sender address: %s" from)))))
|
|
|
|
;; Hook to set SMTP params before sending
|
|
(add-hook 'message-send-hook 'my-mu4e-set-smtp-params)
|
|
|
|
;; Alias email addresses configuration
|
|
;; Define your email aliases here
|
|
(setq my-email-aliases
|
|
'(("std-discussion@luedicke.xyz" . "std-discussion.lists.isocpp.org")
|
|
("gnu-gcc@luedicke.xyz" . "gcc.gcc.gnu.org")
|
|
("qt-interest@luedicke.xyz" . "interest.qt-project.org")
|
|
("boost@luedicke.xyz" . "boost.lists.boost.org")
|
|
("jens@luedicke.me" . "linux-kernel.vger.kernel.org")))
|
|
|
|
;; Variable to store the desired From address
|
|
(defvar my-mu4e-reply-address nil
|
|
"Stores the email address to use for replies to mailing lists.")
|
|
|
|
;; Function to determine the reply address based on recipient
|
|
(defun my-mu4e-set-from-address ()
|
|
"Set the From address based on the original recipient.
|
|
If the message was sent to one of our aliases (via mailing list),
|
|
use that alias as the From address."
|
|
(setq my-mu4e-reply-address nil) ; Reset first
|
|
(let ((msg mu4e-compose-parent-message))
|
|
(when msg
|
|
(let ((list-id (mu4e-message-field msg :list))
|
|
(to (mu4e-message-field msg :to))
|
|
(cc (mu4e-message-field msg :cc)))
|
|
;; Check if this message came from a mailing list we have an alias for
|
|
(dolist (alias-pair my-email-aliases)
|
|
(when (and list-id
|
|
(string-match-p (regexp-quote (cdr alias-pair)) list-id)
|
|
(not my-mu4e-reply-address))
|
|
;; Store the alias to use
|
|
(setq my-mu4e-reply-address (car alias-pair))
|
|
(message "Will use alias address: %s" my-mu4e-reply-address)))))))
|
|
|
|
;; Function to actually set the From header
|
|
(defun my-mu4e-compose-set-from ()
|
|
"Set the From address in the compose buffer."
|
|
(when my-mu4e-reply-address
|
|
;; For aliases, we need to keep the authenticated address in From
|
|
;; but add a Reply-To with the alias address
|
|
(save-excursion
|
|
;; Keep the main address in From (for SMTP authentication)
|
|
(message-remove-header "From")
|
|
(message-add-header (format "From: %s <%s>" user-full-name "jens@luedicke.me"))
|
|
|
|
;; Add Reply-To with the alias address so replies come back to the right address
|
|
(message-remove-header "Reply-To")
|
|
(message-add-header (format "Reply-To: %s <%s>" user-full-name my-mu4e-reply-address))
|
|
|
|
;; Optionally add a comment in the From field to show which list this is for
|
|
;; This helps you see which alias you're using
|
|
(goto-char (point-min))
|
|
(when (re-search-forward "^From: \\(.*\\) <\\(.*\\)>$" nil t)
|
|
(replace-match (format "From: %s (via %s) <%s>"
|
|
user-full-name
|
|
my-mu4e-reply-address
|
|
"jens@luedicke.me"))))
|
|
(message "Using Reply-To address: %s" my-mu4e-reply-address)))
|
|
|
|
;; Hook to set the From address when composing replies
|
|
(add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-from-address)
|
|
;; Run after a short delay to ensure context switching is complete
|
|
(add-hook 'mu4e-compose-mode-hook
|
|
(lambda ()
|
|
(run-at-time 0.1 nil 'my-mu4e-compose-set-from)))
|
|
|
|
;; Update contexts to include SMTP settings
|
|
(setq mu4e-contexts
|
|
`(,(make-mu4e-context
|
|
:name "Personal"
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/Personal" (mu4e-message-field msg :maildir))))
|
|
:vars '((user-mail-address . "jens@luedicke.me")
|
|
(user-full-name . "Jens Luedicke")
|
|
(mu4e-drafts-folder . "/Personal/Drafts")
|
|
(mu4e-sent-folder . "/Personal/Sent")
|
|
(mu4e-trash-folder . "/Personal/Trash")
|
|
(mu4e-refile-folder . "/Personal/Archive")))
|
|
,(make-mu4e-context
|
|
:name "IONOS"
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/IONOS" (mu4e-message-field msg :maildir))))
|
|
:vars '((user-mail-address . "jens@luedicke.cloud")
|
|
(user-full-name . "Jens Luedicke")
|
|
(mu4e-drafts-folder . "/IONOS/Drafts")
|
|
(mu4e-sent-folder . "/IONOS/Sent")
|
|
(mu4e-trash-folder . "/IONOS/Trash")
|
|
(mu4e-refile-folder . "/IONOS/Archive")))))
|
|
|
|
;; Optional: Auto-filing rules for incoming mail
|
|
;; Uncomment and customize these to automatically move messages to folders
|
|
;; (setq mu4e-headers-auto-update t) ; Auto-update headers buffer
|
|
|
|
;; Example auto-filing with mu4e-marks
|
|
;; This runs when indexing new mail
|
|
(defun my-mu4e-auto-file ()
|
|
"Auto-file certain messages to specific folders."
|
|
(when (mu4e-message-field mu4e-compose-parent-message :subject)
|
|
(let ((subject (mu4e-message-field mu4e-compose-parent-message :subject))
|
|
(from (mu4e-message-field mu4e-compose-parent-message :from)))
|
|
|
|
;; Auto-file newsletters
|
|
(when (or (string-match-p "newsletter\\|digest\\|weekly" subject)
|
|
(string-match-p "noreply\\|no-reply\\|newsletter" (car from)))
|
|
(mu4e-message-field mu4e-compose-parent-message :maildir "/Newsletters"))
|
|
|
|
;; Auto-file purchase receipts
|
|
(when (string-match-p "order\\|receipt\\|invoice\\|purchase" subject)
|
|
(mu4e-message-field mu4e-compose-parent-message :maildir "/Purchases")))))
|
|
|
|
;; Hook to run auto-filing after updating
|
|
;; (add-hook 'mu4e-index-updated-hook 'my-mu4e-auto-file)
|
|
|
|
;; Custom search query functions for advanced users
|
|
(defun mu4e-search-important ()
|
|
"Search for important messages."
|
|
(interactive)
|
|
(mu4e-search "(flag:flagged OR prio:high OR from:/boss|manager|ceo|director/) AND NOT flag:trashed"))
|
|
|
|
(defun mu4e-search-newsletters ()
|
|
"Search for newsletters."
|
|
(interactive)
|
|
(mu4e-search "(list:/.+/ OR from:/newsletter|news|digest/ OR body:/unsubscribe/) AND NOT flag:trashed"))
|
|
|
|
(defun mu4e-search-purchases ()
|
|
"Search for purchase-related emails."
|
|
(interactive)
|
|
(mu4e-search "(from:/amazon|ebay|paypal|order|shop/ OR subject:/order|invoice|receipt|purchase/) AND NOT flag:trashed"))
|
|
|
|
;; Customization tips:
|
|
;; 1. To add more bookmarks, add entries to mu4e-bookmarks above
|
|
;; 2. To customize search patterns, modify the :query strings
|
|
;; 3. To change keyboard shortcuts, modify the :key values
|
|
;; 4. To add sender-specific rules, add from:/sender@domain/ patterns
|
|
;; 5. To exclude certain messages, add AND NOT conditions
|
|
|
|
;; VIP sender list example (uncomment and customize):
|
|
;; (setq my-vip-senders '("boss@company.com" "important@client.com"))
|
|
;; Then use in queries: (member-if (lambda (vip) (string-match-p vip from)) my-vip-senders)
|
|
|
|
(provide 'mu4e-config)
|
|
;;; mu4e-config.el ends here
|