mu4e
まくら
元々は家でゲームがしたい(小学生のことTAITOのSpace Invaderが大ブームになった)という理由で始めたコンピューターとの付き合いが、いつの間にか人とのCommunicationの手段になったのは、明らかにパソコン通信のせいで、参加していたBBSのChat roomに入り浸るようになってからだったように思う。
Unixを触るようになり、UUCPで自宅に居ながらにして電子メールが使えるようになって以来、数年前までは間違いなくE-Mailこそが他者とのCommunicationの中心にあった。 したがって、筆者の場合、E-Mailの読み書きを簡単にできるようなApplicationが最も重要なツールであった時代が長い。
そのため、メールクライアントは色々使ってきた。それなりの期間(最低でも半年以上)利用したツールは
- rmail (Emacs)
- Gnus (Emacs)
- mew (Emacs) おそらく、これが一番長い
- Bekky (Windows)
- WinBiff (Windows)
- Apple Mail (macOS)
- Thunderbird (macOS/Windows/FreeBSD)
- Shylpheed (macOS)
と、幾つもあった。しかし、個人的な要件を満たすMail Readerはあまりなかった。動作するPlatformが限られていたり、表示が望み通りにならなかったり、EoLになったり…
というわけで、Sylpheedを卒業して次のMail Readerに移行することにした。
準備
今回の選択はmu4eである。 mu4eはmuコマンドを利用したメールの検索機能を利用して、Emacs上でMailの読み書きを行うためのツールである。
mu4eを利用してMailを処理するためにはいくつかの方法があるが、今回は以下の構成で行くことにした。
- IMAPを利用して手元にメールを同期するsoftware → mbsync(isync)
- smtpを利用してメールを送信するsoftware → msmtp
- Emacs → GNU Emacs 30
- メール処理 → mu, mu4e
mbsync
mbsyncは、RemoteのMDA(IMAP Server)からメールを手元に持ってくるツールである。 Mailboxの同期をEmacs上で行わないのでメールの取得とメールの処理を独立して行えるのがメリットである。 なお、mu4eはメールボックスがありさえすれば良いので、mbsyncである必要はない。imapsyncなどいくつかのツールがあるので、好みのものを選べば良いだろう。
今回は、複数のアカウントをまとめて管理する前提で設定を行う。 設定の詳細は、man mbsyncなどしてman pageを見ること。 ここでは、うちで動作した設定を改変して記述してある。 名前付け替えなどいくつかトリッキーなことをしているので、わからなければManualを読むべきである。
なお、本記事においては、動作確認なども兼ねているためpasswordをrawで記載しているが、これはGPGなどを利用して暗号化するべきである。 暗号化の方法などはGoogleで検索すれば大量に出てくるので、そちらを参照のこと。
# # .mbsyncrc - configuration for mbsync. # ##### General Configuration Create Both Expunge Both CopyArrivalDate yes Sync All SyncState * ##### sample@example.com (Dovecot) IMAPAccount sample_example Host dovecot.example.com Port 993 User sample@example.com Pass Ultra-Secret SSLType IMAPS ### IMAP Store configuration IMAPStore mls_seirios-remote Account mls_seirios ### Maildir Store configuration MaildirStore sample_example-local SubFolders Verbatim Path ~/Maildir/sample_example/ Inbox ~/Maildir/sample_example/Inbox ### Channel Configuration Channel sample_example-local Far :sample_example-remote: Near :sample_example-local: Patterns * ##### sample@icloud.com (Apple Mail) IMAPAccount sample_icloud Host imap.mail.me.com Port 993 User sample@icloud.com Pass Apple-Application-password-set-at-Apple SSLType IMAPS AuthMechs PLAIN ### IMAP Store configuration IMAPStore sample_icloud-remote Account sample_icloud ### Maildir Store configuration MaildirStore sample_icloud-local SubFolders Verbatim Path ~/Maildir/sample_icloud/ Inbox ~/Maildir/sample_icloud/Inbox ### Channel Configuration Channel sample_icloud-base Far :sample_icloud-remote: Near :sample_icloud-local: Patterns * !Sent !"Sent Messages" !Spam !"Junk" !Trash !"Deleted Messages" Channel sample_icloud-base-sent Far :sample_icloud-remote:"Sent Messages" Near :sample_icloud-local:Sent Channel sample_icloud-base-spam Far :sample_icloud-remote:"Junk" Near :sample_icloud-local:Spam Channel sample_icloud-base-trash Far :sample_icloud-remote:"Deleted Messages" Near :sample_icloud-local:Trash Group sample_icloud Channel sample_icloud-base Channel sample_icloud-sent Channel sample_icloud-spam Channel sample_icloud-trash ##### sample@gmail.com (Gmail) ##### もし、GmailのIMAP Folderが日本語だった場合、Gmailから表示を英語モードにしておくこと。日本語だとうまくいかないことがある IMAPAccount sample_gmail Host imap.gmail.com Port 993 User sample@gmail.com Pass Gmail-Application-password-set-at-Gmail SSLType IMAPS AuthMechs PLAIN ### IMAP Store configuration IMAPStore sample_gmail-remote Account sample_gmail ### Maildir Store configuration MaildirStore sample_gmail-local SubFolders Verbatim Path ~/Maildir/sample_gmail/ Inbox ~/Maildir/sample_gmail/Inbox ### Channel Configuration Channel sample_gmail-base Far :sample_gmail-remote: Near :sample_gmail-local: Patterns * !"[Gmail]*" !Sent !Spam !Trash Channel sample_gmail-sent Far :sample_gmail-remote:"[Gmail]/Sent Mail" Near :sample_gmail-local:Sent Channel sample_gmail-spam Far :sample_gmail-remote:"[Gmail]/Spam" Near :sample_gmail-local:Spam Channel sample_gmail-trash Far :sample_gmail-remote:"[Gmail]/Trash" Near :sample_gmail-local:Trash Group sample_gmail Channel sample_gmail-base Channel sample_gmail-sent Channel sample_gmail-spam Channel sample_gmail-trash ##### sheo0147@yahoo.co.jp (Yahoo! Japan) IMAPAccount sample_yahoo Host imap.mail.yahoo.co.jp Port 993 User sample@yahoo.co.jp Pass Ultra-Secret SSLType IMAPS ### IMAP Store configuration IMAPStore sample_yahoo-remote Account sample_yahoo ### Maildir Store configuration MaildirStore sample_yahoo-local SubFolders Verbatim Path ~/Maildir/sample_yahoo/ Inbox ~/Maildir/sample_yahoo/Inbox ### Channel Configuration Channel sample_yahoo-base Far :sample_yahoo-remote: Near :sample_yahoo-local: Channel sample_yahoo-spam Far :sample_yahoo-remote:"Bulk Mail" Near :sample_yahoo-local:Spam Group sheo0147_yahoo Channel sample_yahoo-base Channel sample_yahoo-spam
ここまで設定したら、Directoryを作成し、mbsync -aを実行する
$ mkdir ~/Maildir $ mkdir ~/Maildir/sample_example ~/Maildir/sample_icloud ~/Maildir/sample_gmail ~/Maildir/sample_yahoo $ mbsync -a
これで、手元にMailが来たはず
- yahooの設定の際にPatternsを削除しているのは意図的である。
- 原因はよくわかっていないが、Yahooの場合だけ、Patternsを登録しているとInboxの同期に失敗する
- とりあえずPatternsを設定しなければうまく行くという不思議なことが起きているので、現時点では外す
muの初期化
Mailを取得したら、muでメールのIndexを作成する。 muはXapianを利用している。Xapianは原則としてヨーロッパ系言語の検索が主眼なので、日本語検索は厳しいかと思っていたが、FLAG_NGRAMS=“1”を設定することでそれなりに対応できることがわかったので、以下を設定してmuでDBを初期化する
$ export XAPIAN_CJK_NGRAM="t" $ export FLAG_NGRAMS="t" $ mbsync -a $ mu init --maildir=~/Maildir \ --my-address=sample@example.com \ --my-address=sample@icloud.com \ --my-address=sample@gmail.com \ --my-address=sample@yahoo.co.jp $ mu index $ mu info store ....
msmtp
msmtpはSMTP Clientで、メールを送信する際に利用できる。
近年では、UCE/SPAMのような単なる迷惑メールだけでなく、Cyber攻撃の道具としてもメールが利用されているため、メールを送信するための制限が厳しくなっている。 電子メールの送信にあたっては、送信用のSMTPサーバー(MTA)がそのメールを送付する資格があるかどうかを確認(SPF, DKIM, DMARCを利用する)され、大量のメールを送信する(MailingListを運営している場合など)場合には、更なる検査(ARCを利用)をされる。 例えば sample@example.com がFromとなるメールを送信することができるMXを限定し、そこ以外から送られたものは迷惑メールもしくは攻撃メールの可能性が高いと判断する、などができるようになっている。
したがって、自分のように「複数のメールアカウントを持って」おり、「それぞれを必要に応じて使い分ける」ような使い方をしている場合、メールを送付する際にどのMTAを利用すれば良いかいちいち判断し、適切なMTAからメールを送る必要がある。これを行うためのツールがmsmtpである。
なお、mbsyncと同様、メール送信系をmsmtpにするべき強い理由はない。単にmsmtpの例が非常に多いから利用しているだけである。 また、本記事の設定は動作確認なども兼ねているためpasswordをrawで記載している。しかし、生パスワードを設定ファイルに記載することはSecurity上の重大なリスクになる可能性が高い。したがって、これはGPGなどを利用して暗号化するべきである。 暗号化の方法などはman msmtpするなり、検索するなりすれば大量に出てくるので、そちらを参照のこと。
以下設定。上記mbsyncと合わせてある。
# # .msmtprc - configuration for msmtp. # defaults logfile ~/Maildir/.log/msmtp.log ##### sample@example.com account sample_example auth on host smtp.example.com port 465 protocol smtp from sample@example.com user sample@example.com password Ultra_secret tls on tls_starttls off ##### sample@icloud.com # *** WARNING *** Must need STARTTLS. This site doesn't use SMTPs. account sample_icloud auth on host smtp.mail.me.com port 587 protocol smtp from sample@icloud.com user sample@icloud.com password Apple-Application-password-set-at-Apple tls on tls_starttls on ##### sample@gmail.com account sample_gmail auth on host smtp.gmail.com port 465 protocol smtp from sample@gmail.com user sample@gmail.com password Gmail-Application-password-set-at-Gmail tls on tls_starttls off ##### sample@yahoo.co.jp account sample_yahoo auth on host smtp.mail.yahoo.co.jp port 465 protocol smtp from sample@yahoo.co.jp user sample@yahoo.co.jp password Ultra-Secret tls on tls_starttls off account default : sample_example
mu4e
mu4eの設定は、以下の理由で巨大になっています。こんなに難しいことしなくてもいい気はするんだけど…
- MultiAccount対応
- Mailの取り扱いはAccountに関係なくシームレスに行える
- どこのINBOXに来たメールでも、別のサーバーにrefileできるようにしてある
- refile ruleを大きく書き換えて、正規表現も込みにしたruleをかけるようにした
- 送信サーバーは、メールのFromフィールドを見て自動で決定するようにした
まぁ、色々なところから色々設定持ってきたり、Perplexityのお世話になったりしてます。 難しいcodeは書いてないから、コメント見ながら読めばわかると思いたい。 というかわかるということにします。
あと、これだけでかいと leaf に書き換えるのは面倒くさい…
- mu4e-init.el
;;;; .emacs.d/mu4e-init.el ;;;; mu4e configuration ;;;; ;;;; last updated: 2024/08/26 ;;;; Author: HEO SeonMeyong <seirios@seirios.org> ;;; Require packages upfront (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e/") (require 'mu4e) ; mu4e ;; (require 'mu4e-speedbar) ; Speedbar (require 'smtpmail) ; Compose and send ;;; Debugging ;; (setq mu4e-mu-debug t) ; for mu debbuging ;; (setq mu4e-debug t) ; for mu4e debugging ;;; for mu4e general settings ;; external Binaries. (setq mu4e-get-mail-command (concat (executable-find "mbsync") " -a")) ; this command is called to sync imap servers (setq mu4e-mu-binary (executable-find "mu")) ; mu binary filename. Installed by HomeBrew. (setq sendmail-program (executable-find "msmtp")) ; send Email program ;; Directories. (setq mu4e-maildir "~/Maildir") ; this is the directory we created before. (setq mu4e-attachment-dir "~/Desktop") ; save attachment to desktop ;; Manipulate mails (setq mu4e-change-filenames-when-moving t) ; rename files when moving - needed for mbsync (setq mu4e-update-interval (* 10 60)) ; how often to call it in seconds ;; overall behavivior (setq mu4e-confirm-quit nil) ; don't have to confirm when quitting (setq mu4e-modeline-show-global t) ; mu4e shares information on the modeline (add-hook 'mu4e-headers-search-hook (lambda (query) (setq-local global-mode-string '(:eval (concat (mu4e-context-label) " " (propertize (mu4e~quote-for-modeline mu4e~headers-last-query) 'face 'mu4e-modeline-face 'help-echo (format "%s" mu4e~headers-last-query))))))) (setq mu4e-modeline-max-width 100) ; Change mu4e modeline to 100 chars (setq mode-name "mu4e-headers") ; Add mode name to modeline (add-hook 'mu4e-headers-search-hook (lambda (query) (setq mode-name "mu4e"))) (setq mu4e-split-view "vertical") ; Split-View. Using with display-buffer-alist (add-to-list 'display-buffer-alist ; for Body-view window `(,(regexp-quote mu4e-view-buffer-name) display-buffer-in-side-window (side . right) (window-width . 0.5))) (setq mu4e-hide-index-messages t) ; hide annoying "mu4e Retrieving mail..." msg in mini buffer (setq mu4e-context-policy 'pick-first) ; start with the first (default) context. (pick-first, ask, ask-if-none, nil) ;; headers-view (setq mu4e-headers-include-related nil) ; by default do not show related emails (setq mu4e-headers-results-limit 5000) ; Limit of message view search results. default is 500 (setq mu4e-headers-show-threads t) ; by default do not show threads (setq mu4e-headers-date-format "%Y-%m-%dT%H:%M:%S") ; Header's time format. (setq mu4e-headers-fields ; Show headers list -- field and width(nil means unlimited) '( (:date . 20) ; alternatively, use :human-date (:flags . 6) ; Mail flags (:from . 20) ; From field (:to . 20) ; To field (:subject . nil))) ; alternatively, use :thread-subject ;; Body-view ;; prefer text messages. (with-eval-after-load "mm-decode" (add-to-list 'mm-discouraged-alternatives "text/html") (add-to-list 'mm-discouraged-alternatives "text/richtext")) (setq mu4e-view-date-format "%Y-%m-%dT%H:%M:%S") (setq mu4e-view-fields '(:from ; From field :to ; To field :cc ; Cc field :subject ; Subject field :flags ; Mail flags :date ; Date field(maybe send date and time) :maildir ; maildir information :mailing-list ; Mailing list field :tags ; Tags :attachments ; Attachment information :signature ; Message signature :decryption ; Decryption :message-id ; Message-ID :path ; Mail stored directory :user-agent ; User agent information )) ;; Draft and Compose (setq mu4e-compose-context-policy 'ask-if-none) ; ask for context if no context matches.(pick-first, ask, ask-if-none, nil) (setq mu4e-compose-signature-auto-include nil) ; Signature auto include when composed (defvaralias 'mu4e-compose-signature 'message-signature) ; Use signature variables. (setq message-citation-line-format "%N @ %Y-%m-%d %H:%M:%S :\n") ; customize the reply-quote-string: (setq message-citation-line-function 'message-insert-formatted-citation-line) ; M-x find-function RET message-citation-line-format for docs ;; Sending mails (setq mail-user-agent 'mu4e-user-agent) ; Use mu4e for e-mail in emacs (setq send-mail-function 'sendmail-send-it message-send-mail-function 'sendmail-send-it) ; Reconfigure Email send function (setq message-kill-buffer-on-exit t) ; don't keep message compose buffers around after sending: (setq message-sendmail-envelope-from 'header) ; select the right sender email from the context. ;; Additional supports (setq mu4e-eldoc-support t) ; get info about the current header in the echo-area. (add-hook 'mu4e-compose-mode-hook 'company-mode) ; mu4e address completion (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) ; attach files to mu4e messages using dired. See info "13.9 Dired" ;;; Personal environment/Variables ;;; Contexts (setq mu4e-contexts (list ;; sample@example.com (make-mu4e-context :name "sample_example" :match-func (lambda (msg) (when msg (string-prefix-p "/sample_example" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "sample@example.com" ) (user-full-name . "dovecot sample") (mu4e-drafts-folder . "/sample_example/Drafts") (mu4e-sent-folder . "/sample_example/Sent") (mu4e-trash-folder . "/sample_example/Trash") )) ;; sample@icloud.com (make-mu4e-context :name "sample_icloud" :match-func (lambda (msg) (when msg (string-prefix-p "/sample_icloud" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "sample@icloud.com") (user-full-name . "iCloud example") (mu4e-drafts-folder . "/sample_icloud/Drafts") (mu4e-sent-folder . "/sample_icloud/Sent") (mu4e-trash-folder . "/sample_icloud/Trash") )) ;; sample@gmail.com (make-mu4e-context :name "sample_gmail" :enter-func (lambda () (mu4e-message "Enter sample@gmail.com context")) :leave-func (lambda () (mu4e-message "Leave sample@gmail.com context")) :match-func (lambda (msg) (when msg (string-prefix-p "/sample_gmail" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "sample@gmail.com") (user-full-name . "Gmail sample") (mu4e-drafts-folder . "/sample_gmail/Drafts") (mu4e-sent-folder . "/sample_gmail/Sent") (mu4e-trash-folder . "/sample_gmail/Trash") )) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Bookmarks (setq mu4e-bookmarks "") (setq mu4e-bookmarks '( ( :name "Unread messages" :query "flag:unread AND NOT flag:trashed" :favorite t :key ?u) ( :name "Inbox - sample@example" :query "maildir:/sample_example/Inbox" :favorite t :key ?1) ( :name "Inbox - sample.icloud" :query "maildir:/sample_icloud/Inbox" :favorite t :key ?2) ( :name "Inbox - sample.yahoo" :query "maildir:/sample_yahoo/Inbox" :favorite t :key ?3) ( :name "Inbox - sample.gmail" :query "maildir:/sample_gmail/Inbox" :favorite t :key ?4) ( :name "SPAM/UCE" :query "maildir:/sample_example/Spam OR maildir:/sample_icloud/Spam OR maildir:/sample_gmail/Spam OR maildir:/sample_yahoo/Spam" :favorite t :key ?s) ( :name "Trash" :query "maildir:/sample_example/Trash OR maildir:/sample_icloud/Trash OR maildir:/sample_gmail/Trash OR maildir:/sample_yahoo/Trash" :favorite t :key ?t) ( :name "Today's messages" :query "date:today..now" :favorite t :key ?T) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Maildir Shortcuts (setq mu4e-maildir-shortcuts ;; Set IMAP Sub-directories... '( (:maildir "/sample_icloud/Finamce" :name "Fin" :key ?f) (:maildir "/sample_icloud/Game" :name "Game" :key ?g) (:maildir "/sample_gmail/MyDiv" :name "MyDiv" :key ?d) (:maildir "/sample_gmail/Logs" :name "Logs" :key ?l) (:maildir "/sample_yahoo/Agent" :name "Agent" :key ?a) (:maildir "/sample_example/Family" :name "Fam" :key ?f) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Refile rules (makunbound 'my-mu4e-refile-rules) (defvar my-mu4e-refile-rules '( ;; === Financial ("/sample_icloud/Finamce" "from" "foobank\\.co\\.jp$") ("/sample_icloud/Finamce" "from" "barbank\\.co\\.jp$") ("/sample_icloud/Finamce" "from" "bazcard\\.co\\.jp$") ("/sample_icloud/Finamce" "from" "hogepoint\\.jp$") ;; === Game ("/sample_icloud/Game" "from" "ninnin\\.\\(com\\|net\\|co\\.jp\\)$") ("/sample_icloud/Game" "from" "FourToKnight@mail\\.epicgames\\.com$") ;; === MyDiv ("/sample_gmail/MyDiv" "from" "sample@gmail\\.com$") ;; === Log ("/sample_gmail/Logs" "to" "log@example\\.com$") ("/sample_gmail/Logs" "to" "log@gmail\\.com$") ("/sample_gmail/Logs" "from" "^root@") ("/sample_gmail/Logs" "subj" "^Cron") ("/sample_gmail/Logs" "subj" "run\\.output$") ;; === Agent ("/sample_yahoo/Agent" "to" "sample+agent@\\(yahoo\\.co\\.jp\\|example\\.com\\)") ;; === Family ("/sample_example/Family" "from" "wife@foo\\.jp$") ("/sample_example/Family" "from" "son@bar\\.ac\\.jp$") ) "List of (refile-folder field regex) triples for refiling. Field can be 'to', 'cc', 'bcc', 'rcpt', 'from', 'subject', 'any', 'msgid' or 'list' for mailing lists.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Personal functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Select SMTP account from from header at composed email (defun my-mu4e-set-msmtp-account () ; chose from account before sending (if (message-mail-p) (save-excursion (let* ((from (save-restriction (message-narrow-to-headers) (message-fetch-field "from"))) (account (cond ((string-match "sample@example.com" from) "sample_example") ((string-match "sample@icloud.com" from) "sample_icloud") ((string-match "sample@yahoo.co.jp" from) "sample_yahoo") ((string-match "sample@gmail.com" from) "sample_gmail") ))) (setq message-sendmail-extra-arguments (list '"-a" account))) ))) (add-hook 'message-send-mail-hook 'my-mu4e-set-msmtp-account) ;;; Add Cc and Bcc field when compose Email. (add-hook 'mu4e-compose-mode-hook ; mu4e cc & bcc (defun my-add-cc-and-bcc () "My Function to automatically add Cc & Bcc: headers. This is in the mu4e compose mode." (save-excursion (message-add-header (concat "Cc: " user-mail-address "\n"))) (save-excursion (message-add-header "Bcc:\n")))) ;;; Require confirmation before sending mail without subject. (defun confirm-empty-subject () "Require confirmation before sending without subject." (let ((sub (message-field-value "Subject"))) (or (and sub (not (string-match "\\`[ \t]*\\'" sub))) (yes-or-no-p "Really send without Subject? ") (keyboard-quit)))) (add-hook 'message-send-hook #'confirm-empty-subject) ;;; Header viewでmail Search時にMaxmumを拡張して全部を検索する (defvar my-mu4e-page 1 "Current page in mu4e search.") (defun my-mu4e-reset-page (&rest _r) "Reset ‘my-mu4e-page’ to 1." (setq my-mu4e-page 1)) ;; Need to reset the "standard searches" when invoking an interactive search (add-hook 'mu4e-search-bookmark-hook #'my-mu4e-reset-page) (advice-add 'mu4e-search-maildir :before #'my-mu4e-reset-page) (defun my-mu4e-next-messages-for-query () "Fetch the next number of messages for current mu4e query. Move to last message in current view so that newly fetched messages are visible." (interactive) (when (and (mu4e-current-buffer-type-p 'headers) (not mu4e-search-full)) (when-let ((query (mu4e-last-query))) (cl-incf my-mu4e-page) (let ((mu4e-search-results-limit (* my-mu4e-page -1)) (last-msg (save-excursion (goto-char (point-max)) (forward-line -1) (plist-get (mu4e-message-at-point) :message-id)))) (mu4e-search query nil nil t last-msg))))) (keymap-set mu4e-headers-mode-map "N" #'my-mu4e-next-messages-for-query) ;;; Refile (makunbound 'my-mu4e-refile-message) (defun my-mu4e-refile-message (msg) "Determine the refile folder for mu4e messages based on specified fields in MSG." (cl-loop for (folder field regex) in my-mu4e-refile-rules for addresses = (cond ((string= field "any") (append (mu4e-message-field msg :to) (mu4e-message-field msg :cc) (mu4e-message-field msg :bcc) (mu4e-message-field msg :from))) ((string= field "rcpt") (append (mu4e-message-field msg :to) (mu4e-message-field msg :cc) (mu4e-message-field msg :bcc))) ((string= field "subj") (list (mu4e-message-field msg :subject))) ((string= field "msgid") (list (mu4e-message-field msg :msgid))) ((string= field "list") (list (mu4e-message-field msg :list))) (t (mu4e-message-field msg (intern (concat ":" field))))) when (seq-some (lambda (addr) (when addr (string-match-p regex (downcase (if (listp addr) (or (plist-get addr :email) "") addr))))) addresses) return folder ; refile先を相対pathで返す finally return nil)) ; 条件にマッチしない場合はnilを返す ;; mu4e-refile-message関数を利用してrefileする (setq mu4e-refile-folder "") (setq mu4e-refile-folder (lambda (msg) (or (my-mu4e-refile-message msg) (mu4e-message-field msg :maildir)))) ; リファイル先が決定できない場合は現在のフォルダを返す (makunbound 'my-mu4e-auto-refile-process) (defun my-mu4e-auto-refile-process (msg) "Process a single message for auto-refiling." (let ((target-folder (funcall mu4e-refile-folder msg))) (when (and target-folder (not (string= target-folder (mu4e-message-field msg :maildir)))) (mu4e-mark-set 'refile target-folder)))) (makunbound 'my-mu4e-auto-refile) (defun my-mu4e-auto-refile () "Automatically mark messages for refiling in the current folder based on mu4e-refile-folder rules." (interactive) (let* ((current-folder (mu4e-message-field (mu4e-message-at-point) :maildir)) (query (concat "maildir:" current-folder " " "AND (flag:new OR flag:unread) " "AND NOT flag:trashed"))) (mu4e-headers-search query) (add-hook 'mu4e-headers-found-hook 'my-mu4e-auto-refile-hook))) (makunbound 'my-mu4e-auto-refile-hook) (defun my-mu4e-auto-refile-hook () "Hook to process messages after headers search." (remove-hook 'mu4e-headers-found-hook 'my-mu4e-auto-refile-hook) (goto-char (point-min)) (while (not (eobp)) (let ((msg (mu4e-message-at-point))) (my-mu4e-auto-refile-process msg)) (forward-line)) (mu4e-headers-next nil)) ; Move to the next unread message after marking (makunbound 'my-mu4e-headers-auto-refile) (defun my-mu4e-headers-auto-refile () "Run auto-refile marking in headers view." (interactive) (my-mu4e-auto-refile)) (keymap-set mu4e-headers-mode-map "e" #'my-mu4e-headers-auto-refile) ; Refile key bind ;; Local Variables: ;; coding: utf-8 ;; comment-column: 72 ;; version-control: t ;; kept-old-versions: 2 ;; kept-new-versions: 2 ;; End: