転載・引用について

ユーザ用ツール

サイト用ツール


userapps:emacs:mu4e

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:
このウェブサイトはクッキーを使用しています。 Webサイトを使用することで、あなたはあなたのコンピュータにクッキーを保存することに同意します。 また、あなたはあなたが私たちのプライバシーポリシーを読んで理解したことを認めます。 同意しない場合はウェブサイトを離れてください。クッキーに関する詳細情報
userapps/emacs/mu4e.txt · 最終更新: 2024/08/27 02:20 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki