元々は家でゲームがしたい(小学生のことTAITOのSpace Invaderが大ブームになった)という理由で始めたコンピューターとの付き合いが、いつの間にか人とのCommunicationの手段になったのは、明らかにパソコン通信のせいで、参加していたBBSのChat roomに入り浸るようになってからだったように思う。
Unixを触るようになり、UUCPで自宅に居ながらにして電子メールが使えるようになって以来、数年前までは間違いなくE-Mailこそが他者とのCommunicationの中心にあった。 したがって、筆者の場合、E-Mailの読み書きを簡単にできるようなApplicationが最も重要なツールであった時代が長い。
そのため、メールクライアントは色々使ってきた。それなりの期間(最低でも半年以上)利用したツールは
と、幾つもあった。しかし、個人的な要件を満たすMail Readerはあまりなかった。動作するPlatformが限られていたり、表示が望み通りにならなかったり、EoLになったり…
というわけで、Sylpheedを卒業して次のMail Readerに移行することにした。
今回の選択はmu4eである。 mu4eはmuコマンドを利用したメールの検索機能を利用して、Emacs上でMailの読み書きを行うためのツールである。
mu4eを利用してMailを処理するためにはいくつかの方法があるが、今回は以下の構成で行くことにした。
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が来たはず
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は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の設定は、以下の理由で巨大になっています。こんなに難しいことしなくてもいい気はするんだけど…
まぁ、色々なところから色々設定持ってきたり、Perplexityのお世話になったりしてます。 難しいcodeは書いてないから、コメント見ながら読めばわかると思いたい。 というかわかるということにします。
あと、これだけでかいと leaf に書き換えるのは面倒くさい…
;;;; .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: