Solist Work Blog

ソフトウェアエンジニア / IT navigator

大量のメールを処理できるNeomutt

仕事ツールの中でも意外と重要なメーラーについて考えます。 一般的にメールクライアントは機能で比べることが多いと思いますが、 私がメーラーを選ぶ上で重要視していることは速度です。 ほとんどのオープンソースはGitHubかメーリングリストを使って開発されているので参加すると大量のメールでGmailがあふれることになります。 いちいちリポジトリをブラウザで見に行っていられないのでたくさんWatchしておいて何か起きたらメールで知ることになります。 そうなると数万〜数十万のメールがGmailにはいっていてもIMAPで快適に扱えないとこの用途では使い物になりません。

なぜIMAPにこだわるかといえば、数万〜数十万オーダーのメールをPOP3でとってきてDropboxに置いておくと初回のファイル同期が遅くなりすぎるからです。Dropboxの同期が数時間かかると気軽にクリーンインストールできません。クリーンインストールは最高のセキュリティ対策なので式年遷宮1のように定期的に行います。したがって、要件はメールの既読管理をしたいだけであるので何も管理しなくていいIMAPが最適なのです。2 そしてこのような厳しい要求に耐えうるのはneomutt以外にはないでしょう。

Neomutt(mutt)

neomuttは現存するメーラーでおそらく世界最速であろうと思います。 Cで実装されているターミナルで使うCUIのメーラーです。

neomuttのインターフェース

ご覧のように、2018年にもなってCUIのメールクライアントを使うのは懐古趣味ではないかと言われそうではありますが、 neomuttの速さを見ていただければ納得してもらえるのではないかと思っています。 このデタラメな速さが数万〜数十万オーダーで快適に作業するためには必要です。 GitHubに投稿するときはメールにある該当issueやプルリクエストのリンクをクリックするとChromiumが開くようになっています。

neomuttを使っているところ

さて、あまりにも早すぎて何をやっているかわからないと思うので少し説明をします。 まず l を押して、見せても問題のないGitHubでの公開のやり取りのメールだけをフィルタした状態でgifアニメーションは始まります。 GitHubのやり取りは初期状態として未読状態にしてあります。 青色のメールは未読状態のメールであるということになります。 ざっとメールを下に確認していって一番上に戻ってきます。

次に未読メールが自分と関係ないとわかっている場合を想定して未読メールを既読状態にしていきます。 しかし、この操作を取り消したいのでいったん1のメールに戻り 既読状態にしたメールを未読状態に戻していきます。 このようなすさまじい速度での作業に対応できるメールクライアントはneomuttしかありません。 2018年にもなって使う価値があるCUIソフトウェアであることがわかっていただけたかと思います。 neomuttの設定は最後に書いておきます。詳しくは私のGitHubをどうぞ。

Sylpheed

Sylpheedのインターフェース

GUIのメーラーも使っています。 GUIのメールクライアントではSylpheedが一番軽快で速いのではないかと思います。 もちろん速さはneomuttには及びませんが、とても使いやすいツールです。 私は大量のメールが来るGmailはneomuttで処理して、solist.workドメインなどに来るメールをSylpheedで処理するようにしています。3 Sylpheedはsolist.workドメインのメールを扱うのでデータの保護を重要視していて、POP3で受信するとすぐにDropboxにデータがバックアップされるようになっています。 SylpheedはEmacsキーバインドでメールの作業が一通りできるのでオススメです。

~/.sylpheed-2.0/folderlist.xml

<?xml version="1.0" encoding="UTF-8"?>

<folderlist>
    <folder type="mh" name="メール箱" path="/home/masa/Dropbox/sylpheed/Mail">
        <folderitem type="outbox" name="sent" path="sent" threaded="1" mtime="0" new="0" unread="0" total="32" />
        <folderitem type="inbox" name="inbox" path="inbox" threaded="1" sort_key="date" sort_type="descending" mtime="1541688903" new="0" unread="0" total="246">
            <folderitem type="normal" name="support" path="inbox/support" threaded="1" mtime="0" new="0" unread="0" total="2" />
        </folderitem>
        <folderitem type="junk" name="junk" path="junk" threaded="1" mtime="0" new="0" unread="0" total="0" />
        <folderitem type="trash" name="trash" path="trash" threaded="1" mtime="0" new="0" unread="0" total="2" />
        <folderitem type="queue" name="queue" path="queue" threaded="1" mtime="0" new="0" unread="0" total="0" />
        <folderitem type="draft" name="draft" path="draft" threaded="1" mtime="0" new="0" unread="0" total="1" />
    </folder>
</folderlist>

folderlist.xmlの中の

path="/home/masa/Dropbox/sylpheed/Mail"

でDropbox内にメールデータを置くことができます。 これでSylpheedで受信したメールはすぐにDropboxに同期されるのでバックアップ不要となります。バックアップしなくていいクリーンインストールを気軽にできる環境になれるともう以前のような環境には戻れません。

Sylpheedの設定が終わったら${HOME}/.sylpheed-2.0をDropboxに同期しましょう。

Makefile

sylpheed: ## Init sylpheed
	sudo pacman -S sylpheed
	test -L ${HOME}/.sylpheed-2.0 || rm -rf ${HOME}/.sylpheed-2.0
	ln -vsfn ${HOME}/Dropbox/sylpheed/.sylpheed-2.0   ${HOME}/.sylpheed-2.0

allinstall: ttf-cica install init initdropbox pipinstall goinstall aur mozc neomutt docker mariadb redis rbenv rustinstall nodeinstall screenkey dnsmasq desktop chromium jekyll sxiv zeal zoom toggle sylpheed

このようなMakefileを用意すると次回からはmake sylpheedでこの処理が適応されます。 さらにmake allinstallするとmake sylpheedも実行されるのでクリーンインストール時はmake allinstallするだけでよいことになります。 詳しくはこちらの記事を参考にしてください。

黒背景について

普段使っている環境の背景が黒いのは格好いいからそうしているのではなく長時間見ていても目が悪くならないようにするためです。 Emacs Terminal Email Chromiumは基本的に黒背景で使用します。4 資料を作るとき人に見せる時だけChromiumは通常の状態に戻してよそ行きの状態にします。 これは目の負担はディスプレイの面積×明るさであると一般的に知られていて、 普段は黒背景で目の負担を下げたいからです。 ですから私は大きなディスプレイも使わないようにしています。 ソフトウェアエンジニアは長年液晶ディスプレイを見続けるわけですから目は大切にしなければいけません。

Chromiumの黒背景はStylusDark Readerを使って実現しています。 私がStylusで使っているスタイルはこちらからダウンロードできます。 Stylusのスタイルを適応するサイトではDark Readerをそのドメインだけオフにします。 基本的にはDark Readerで黒背景を実現し、Dark Readerで見にくいサイトだけStylusで対応するという感じです。私がStylusで黒背景を実現しているサイトはGoogle、Googleドライブ、Googleカレンダー、GitLab、Feedly、Trelloです。RedditTwitterやGmailのように夜間モードが実装されているサイトでは夜間モードを使用しています。Hacker Newsは見にくいのでEmacsのhackernews.elで見ています。RedditRTVで見るのがお勧めです。

誰だったかは忘れましたが昔ビリヤードのプレイヤーで視力を落とさないようにするために 新聞や雑誌をお付きの人に読ませているというエピソードがありました。 これは目をいたわる別の視点とアイデアを提供してくれます。 視力を落とさないようにするために人を使ってもよいし、 また、それは人である必要もないのです。 現代のソフトウェアエンジニア的にはコンピュータに読ませるかポットキャストをソースにするという方法がよいと思います。 人を雇うのはコストが高すぎると思うので。

ポットキャストのような音を情報源として利用すると、もう一つのメリットが得られます。 それは、存在しなかった時間を作りだせることです。 ポットキャストを聞きながらジョギングや散歩や通勤をすると今まで有効利用されていない 時間についでに情報をインプットすることが可能になります。5 最近有益なポットキャストが増えてきたのはこういう事情もあるのかなと思っています。 はしょって読まれるブログより、運動しながら聞かれるのだとしても早送りされることがないポットキャストのほうが メディアとしての価値がありそうな気がします。 まぁはしょって読めないような濃密な記事を書けばよいと思いますが…。

neomuttの設定

Gmail側でIMAPの設定はすませてある前提です。

.muttrc

set send_charset="iso-2022-jp:utf-8"

source ~/Dropbox/mutt/password.rc

set folder = "imaps://imap.gmail.com:993"
set spoolfile = "+INBOX"
set imap_check_subscribed
set hostname = gmail.com
set mail_check = 120
set timeout = 300
set imap_keepalive = 300
set postponed = "+[GMail]/Drafts"
set record = "+[GMail]/Sent Mail"
set header_cache=~/.mutt/cache/headers
set message_cachedir=~/.mutt/cache/bodies
set certificate_file=~/.mutt/certificates
set signature =~/.mutt/signature
set move = no
set include
set sort = 'threads'
set sort_aux = 'reverse-last-date-received'
set auto_tag = yes
ignore "Authentication-Results:"
ignore "DomainKey-Signature:"
ignore "DKIM-Signature:"
hdr_order Date From To Cc
set pager_index_lines=8
bind editor ^T complete
bind editor <space> noop

# mailcap
alternative_order text/plain text/html *
set mailcap_path = ~/.mutt/mailcap
auto_view text/html

# emacs
#set editor="emacsclient -a \"\" -t"
# vim
set editor="vim"

# notify-send
set new_mail_command="notify-send --icon=mail-message-new 'New Email' '%n new messages, %u unread.' &"

# alias
set alias_file = "~/.mutt/aliases"
set sort_alias = alias
set reverse_alias = yes
source $alias_file

## GooBook
set query_command="goobook query '%s'"
macro index,pager a "<pipe-message>goobook add<return>" "add sender to google contacts"
bind editor <Tab> complete-query

# pager
set pager_context = 3
unset markers

# make sure that you are using mutt linked against slang, not ncurses, or
# suffer the consequences of weird color issues. use "mutt -v" to check this.

# custom body highlights -----------------------------------------------
# highlight my name and other personally relevant strings
#color body          color136        color234        "(ethan|schoonover)"
# custom index highlights ----------------------------------------------
# messages which mention my name in the body
#color index         color136        color234        "~b \"phil(_g|\!| gregory| gold)|pgregory\" !~N !~T !~F !~p !~P"
#color index         J_cream         color230        "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~N !~T !~F !~p !~P"
#color index         color136        color37         "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~T !~F !~p !~P"
#color index         color136        J_magent        "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~F !~p !~P"
## messages which are in reference to my mails
#color index         J_magent        color234        "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" !~N !~T !~F !~p !~P"
#color index         J_magent        color230        "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~N !~T !~F !~p !~P"
#color index         J_magent        color37         "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~T !~F !~p !~P"
#color index         J_magent        color160        "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~F !~p !~P"

# for background in 16 color terminal, valid background colors include:
# base03, bg, black, any of the non brights

# basic colors ---------------------------------------------------------
color normal        color241        color234        
color error         color160        color234        
color tilde         color235        color234        
color message       color37         color234        
color markers       color160        color254        
color attachment    color254        color234        
color search        color61         color234        
#color status        J_black         J_status        
color status        color241        color235        
color indicator     color255        color13        
color tree          color13        color255                                    # arrow in thread

# basic monocolor screen
mono  bold          bold
mono  underline     underline
mono  indicator     reverse
mono  error         bold

# index ----------------------------------------------------------------

#color index         color160        color234        "~D(!~p|~p)"               # deleted
#color index         color235        color234        ~F                         # flagged
#color index         color166        color234        ~=                         # duplicate messages
#color index         color240        color234        "~A!~N!~T!~p!~Q!~F!~D!~P"  # the rest
#color index         J_base          color234        "~A~N!~T!~p!~Q!~F!~D"      # the rest, new
color index         color160        color234        "~A"                        # all messages
color index         color166        color234        "~E"                        # expired messages
color index         color33         color234        "~N"                        # new messages
color index         color33         color234        "~O"                        # old messages
color index         color61         color234        "~Q"                        # messages that have been replied to
color index         color240        color234        "~R"                        # read messages
color index         color33         color234        "~U"                        # unread messages
color index         color33         color234        "~U~$"                      # unread, unreferenced messages
color index         color241        color234        "~v"                        # messages part of a collapsed thread
color index         color241        color234        "~P"                        # messages from me
color index         color37         color234        "~p!~F"                     # messages to me
color index         color37         color234        "~N~p!~F"                   # new messages to me
color index         color37         color234        "~U~p!~F"                   # unread messages to me
color index         color240        color234        "~R~p!~F"                   # messages to me
color index         color160        color234        "~F"                        # flagged messages
color index         color160        color234        "~F~p"                      # flagged messages to me
color index         color160        color234        "~N~F"                      # new flagged messages
color index         color160        color234        "~N~F~p"                    # new flagged messages to me
color index         color160        color234        "~U~F~p"                    # new flagged messages to me
color index         color235        color160        "~D"                        # deleted messages
color index         color245        color234        "~v~(!~N)"                  # collapsed thread with no unread
color index         color136        color234        "~v~(~N)"                   # collapsed thread with some unread
color index         color64         color234        "~N~v~(~N)"                 # collapsed thread with unread parent
# statusbg used to indicated flagged when foreground color shows other status
# for collapsed thread
color index         color160        color235        "~v~(~F)!~N"                # collapsed thread with flagged, no unread
color index         color136        color235        "~v~(~F~N)"                 # collapsed thread with some unread & flagged
color index         color64         color235        "~N~v~(~F~N)"               # collapsed thread with unread parent & flagged
color index         color64         color235        "~N~v~(~F)"                 # collapsed thread with unread parent, no unread inside, but some flagged
color index         color37         color235        "~v~(~p)"                   # collapsed thread with unread parent, no unread inside, some to me directly
color index         color136        color160        "~v~(~D)"                   # thread with deleted (doesn't differentiate between all or partial)
#color index         color136        color234        "~(~N)"                    # messages in threads with some unread
#color index         color64         color234        "~S"                       # superseded messages
#color index         color160        color234        "~T"                       # tagged messages
#color index         color166        color160        "~="                       # duplicated messages

# message headers ------------------------------------------------------

#color header        color240        color234        "^"
color hdrdefault    color240        color234        
color header        color241        color234        "^(From)"
color header        color33         color234        "^(Subject)"

# body -----------------------------------------------------------------

color quoted        color33         color234        
color quoted1       color37         color234        
color quoted2       color136        color234        
color quoted3       color160        color234        
color quoted4       color166        color234        

color signature     color240        color234        
color bold          color235        color234        
color underline     color235        color234        
color normal        color255        color234        
#
color body          color245        color234        "[;:][-o][)/(|]"    # emoticons
color body          color245        color234        "[;:][)(|]"         # emoticons
color body          color245        color234        "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\
                                                     |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\
                                                     |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?"
color body          color245        color234        "[ ][*][^*]*[*][ ]?" # more emoticon?
color body          color245        color234        "[ ]?[*][^*]*[*][ ]" # more emoticon?

## pgp

color body          color160        color234        "(BAD signature)"
color body          color37         color234        "(Good signature)"
color body          color234        color234        "^gpg: Good signature .*"
color body          color241        color234        "^gpg: "
color body          color241        color160        "^gpg: BAD signature from.*"
mono  body          bold                            "^gpg: Good signature"
mono  body          bold                            "^gpg: BAD signature from.*"

# yes, an insance URL regex
color body          color160        color234        "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
# and a heavy handed email regex
#color body          J_magent        color234        "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])"

# Various smilies and the like
#color body          color230        color234        "<[Gg]>"                            # <g>
#color body          color230        color234        "<[Bb][Gg]>"                        # <bg>
#color body          color136        color234        " [;:]-*[})>{(<|]"                  # :-) etc...
# *bold*
#color body          color33         color234        "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)"
#mono  body          bold                            "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)"
# _underline_
#color body          color33         color234        "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)"
#mono  body          underline                       "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)"
# /italic/  (Sometimes gets directory names)
#color body         color33         color234        "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)"
#mono body          underline                       "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)"

# Border lines.
#color body          color33         color234        "( *[-+=#*~_]){6,}"

#folder-hook .                  "color status        J_black         J_status        "
#folder-hook gmail/inbox        "color status        J_black         color136        "
#folder-hook gmail/important    "color status        J_black         color136        "

.mutt/mailcap

text/html; w3m -I %{charset} -T text/html; copiousoutput;
text/markdown; w3m -I %{charset} -T text/markdown; copiousoutput;
image/jpg; eog %s
image/jpeg; eog %s
image/png; eog %s
image/gif; eog %s
application/pdf; evince %s
application/x-pdf; evince %s
application/zip; file-roller %s
application/gzip; file-roller %s
application/x-rar-compressed; file-roller %s
application/x-msexcel; xdg-open %s
application/ms-exc; xdg-open %s
application/excel; xdg-open %s
application/msexcel; xdg-open %s
application/vnd.ms-exc; xdg-open %s
application/vnd.ms-excel; xdg-open %s
application/msword; xdg-open %s
application/vnd.openxmlformats-officedocument.wordprocessingml.document; xdg-open %s
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; xdg-open %s

パスワードはGitHubで共有しないようにしておきます。 もし間違ってGitHubにコミットしてしまったらすぐにパスワードは変更しましょう。

~/Dropbox/mutt/password.rc

set imap_user = "youraddress@gmail.com"
set imap_pass = "yourpassword"
set smtp_url  = "smtp://youraddress@smtp.gmail.com:587/"
set smtp_pass = "yourpassword"
set from      = "youraddress@gmail.com"
set realname  = "Your Name"

~/Dropbox/mutt/.goobookrc

# "#" or ";" at the start of a line makes it a comment.
[DEFAULT]
# The following are optional, defaults are shown

# This file is written by the oauth library, and should be kept secure,
# it's like a password to your google contacts.
;oauth_db_filename: ~/.goobook_auth.json

# The client secret file is not really secret.
;client_secret_filename: ~/.goobook_client_secret.json

;cache_filename: ~/.goobook_cache
;cache_expiry_hours: 24
;filter_groupless_contacts: yes

# New contacts will be added to this group in addition to "My Contacts"
# Note that the group has to already exist on google or an error will occur.
# One use for this is to add new contacts to an "Unsorted" group, which can
# be sorted easier than all of "My Contacts".
;default_group:

email: youraddress@gmail.com
password: yourpassword

~/Dropbox/mutt/signature

Your Name

~/Dropbox/mutt/aliases

alias piko Piko Taro <pikotaro@example.com>
alias kosaka Kosaka Daimaou <kosaka.daimaou@example.com>

こうしておくと新規メールを出すときに

To: piko

で Piko Taro pikotaro@example.com にメールすることができます。


  1. 20年に一度ではセキュリティ対策にはならないでしょう。
  2. オフラインのためにPOP3でローカルにメールデータを持つのはこのオーダーでは面倒です。Dropboxのサーバーはアメリカにあるので太平洋ケーブルを渡りますし初回の同期が遅いのは仕方ないでしょう。年に数日くらいある移動中のオフラインの時間は電子書籍を読むことにしているのでオフラインは諦めています。面倒かつリターンが少ないものは諦めるのも正しい戦略だと思います。最近はオフラインになる場所も少なくなってきているので実はそんなに困っていないような….
  3. メールをEmacsで書きたいときもあるのでMewも使っています。
  4. 実際のところほとんど全てのアプリケーションが黒背景です。私のつかっているGTKテーマはArc-Darkです。Arch Linuxならsudo pacman -S arc-gtk-theme だけでインストールできます。
  5. ヘッドホンで両耳を塞ぐと危ないので片耳だけで聞くのがオススメです

タグ一覧

お仕事の依頼などはこちらからどうぞ

お仕事の依頼はこちらからどうぞ