[指南] 使用 neomutt 管理你的邮箱

[!NOTE]

  1. 这是一个指南而非教程, 重点带你理清它们之间的配置关系

围绕整个流程,简单画个了图,很好地概括了我的邮件管理流程。

                          Others
                            ||\
                            || Send
                    Receive ||
                           \||
     ------------------ Email Sever -----------------
     ||\                                           /|\
     ||                                             |
    Sync                                           Send
     ||                                             |
    \||                                             |
   mbsync -----> notmuch --tags--> neomutt -----> msmtp

IMAP client                                     SMTP client
  • isync (mbsync): 作为 IMAP 客户端, 支持双向同步
  • notmuch: 负责为邮件打标签
  • neomutt: 作为邮件客户端 (TUI)
  • msmtp: 作为 SMTP 客户端
  • alot: notmuch 的一个前端, 可以很方便看 taglist (TUI)
  • oama: OAuth 凭证管理器, 用于获取 OAUTH2 Token 和 授权访问邮件地址

Step 0: 获取 OAUTH2 令牌 (Token)

Gmail 和 Outlook 都要求使用 OAUTH2 认证。

这一步是所有步骤中最麻烦的一步,相信大部分人在这一步可能就止步于此了。

获取 OAUTH2 令牌前,你需要先获取 client_idclient_secret

  • 对于 Gmail, 你需要在 Google 的云平台注册一个 OAuth 应用程序,这样你才能获取 OAuth client_idclient_secret 密钥。
  • 对于 Outlook, 你有两种方式。
    1. 和 Gmail 一样, 只不过是在 Microsoft 的云平台 (Azure) 进行类似操作。(Azure 帐户必须至少是应用程序开发人员)
    2. 使用设备代码流(device code flow)授权方法, client_secret 可能不是必要的, 因此你可以使用开源邮件客户端例如 Thunderbird 或者 Evolutionclient_id

具体操作步骤可参考:

这里我以 Outlook 邮箱,并采用第二种方式举例
这里是一些开源的邮件客户端 client_id。

Application client_id Sources
Mozilla/Thunderbird 9e5f94bc-e8a4-4e73-b8be-63364c29d753 1
GNOME/Evolution 20460e5d-ce91-49af-a3a5-70b6be7486d1 1

首先安装 oama, 在 ~/.config/oama/config.yaml 添加下面的配置,然后执行 oama authorize microsoft <you@outlook.com> --device, 在浏览器页面完成认证。


## Possible options for keeping refresh and access tokens:
## GPG - in a gpg encrypted file $XDG_STATE_HOME/oama/<email-address>.oauth
##       (XDG_STATE_HOME defaults to ~/.local/state)
## GPG - in a gpg encrypted file ~/.local/state/oama/<email-address>.oauth
## KEYRING - in the keyring of a password manager with Secret Service API
##
## Choose exactly one.

encryption:
    tag: KEYRING

# encryption:
#   tag: GPG
#   contents: your-KEY-ID

services:
  microsoft:
    client_id: 20460e5d-ce91-49af-a3a5-70b6be7486d1 # Evolution

Step 1: 从邮件服务器拉取邮件

我们使用 isync 来从远程服务器双向同步邮件 (注意, 它的命令行名称是 mbsync)

这里需要配置三个部分

  1. IMAP Accounts
  2. IMAP Stores
  3. Channels and Groups
IMAPAccount <随便起一个名字>
Host outlook.office365.com
Port 993
User <你的Outlook邮箱地址>
AuthMechs XOAUTH2
PassCmd "oama access <你的Outlook邮箱地址>"
TLSType IMAPS
SystemCertificates yes
Timeout 10
  • IMAPStore: 远程 IMAP Store
  • MaildirStore: 本地 Maildir Store
  • Path: 设定 Maildir 的根目录, 之后拉取的邮箱都存储在这里。(注意: 最后这个 / 不能忽略)
  • Inbox: INBOX 邮箱的路径
IMAPStore outlook-remote
Account <指定你刚刚定义的 IMAPAccount>

# Where are we going to keep the mail associated with this account
MaildirStore outlook-local
SubFolders Verbatim
Path  ~/.local/share/maildir/outlook/
Inbox ~/.local/share/maildir/outlook/INBOX

之后利用 Channel 建立远程邮箱与本地邮箱的映射关系
你可以建立多个 Channel 和 Group 并自由组合它们
之后你可以选定其中一个 Channel 或 Group 来同步邮件, 例如 mbsync outlook-inbox 仅同步 INBOX 邮箱, mbsync outlook 同步多个邮箱。

Channel outlook-inbox
Far  :outlook-remote:INBOX
Near :outlook-local:INBOX

Channel outlook-sent
Far  :outlook-remote:"Sent"
Near :outlook-local:"Sent"

Channel outlook-drafts
Far  :outlook-remote:"Drafts"
Near :outlook-local:"Drafts"

Channel outlook-trash
Far  :outlook-remote:"Deleted"
Near :outlook-local:"Trash"

Channel outlook-archive
Far  :outlook-remote:"Archive"
Near :outlook-local:"Archive"

Channel outlook-notes
Far  :outlook-remote:"Notes"
Near :outlook-local:"Notes"

Channel outlook-spam
Far  :outlook-remote:"Junk"
Near :outlook-local:"Spam"



Group outlook
Channel outlook-inbox
Channel outlook-sent
Channel outlook-drafts
Channel outlook-trash
Channel outlook-notes
Channel outlook-archive
Channel outlook-spam

对应的映射关系如下,

IMAP mailboxes Local mailboxes
INBOX ~/.local/share/maildir/outlook/INBOX
Sent ~/.local/share/maildir/outlook/Sent
Drafts ~/.local/share/maildir/outlook/Drafts
Deleted ~/.local/share/maildir/outlook/Trash
... ...

Step 2: 为本地邮箱邮件打标签

这一步是为所有邮箱中的邮件进行打标签,之后在 neomuut 时我们可以通过标签筛选过滤以创建虚拟邮箱。

  • database.mail_root: 指向我们本地邮箱的根路径
  • database.path: 指定 xapian 路径, 这是 notmuch 标签数据库的位置
  • database.hook_dir: 指定 hook 路径, 默认是 database.path/notmuch/hooks, 我建议放在配置文件附近方便修改
  • new.tags: 当执行 notmuch new 后, 会为所有邮件打上的标签
  • search.exclude_tags: 当执行 notmuch search 时, 排除带有特定标签的邮箱。

[!NOTE]
注意:

  1. 这里不能使用 ~, 只能写成 /home/<User> 形式
[database]
mail_root=/home/<User>/.local/share/maildir/outlook
path=/home/<User>/.local/share/notmuch/outlook
hook_dir=/home/<User>/.config/notmuch/outlook/hooks

[user]
name=<用户名>
primary_email=<你的 Outlook 邮箱地址>

[new]
tags=inbox
ignore=/.*[.](json|lock|bak)$/

[search]
# https://notmuchmail.org/excluding/
exclude_tags=deleted;spam;

[maildir]
synchronize_flags=true

接下来在 hook_dir 目录下创建一个 post-new 钩子 (不要忘记给它可执行权限)
你可以在这里完成自动化打标签的操作, 这样每次 notmuch new 时自动完成打标签。
它支持以 from:xxx, to:xxx, folder:xxx, cc:xxx 等前缀作为过滤条件

[!NOTE]
注意:

  1. 使用 notmuch 相关命令时, 它会假设指向的是 default 目录下的配置, 所以你会看到我这里使用了一个 NOTMUCH 包装函数, 使用 --config 选项指定特定配置文件
  2. new.tags = inbox 会为所有邮件打上 inbox 标签, 可以通过 -inbox 移除它, 避免污染标签
  3. folder:xxx 是以 mail_root 为相对路径
  4. 可以使用 --batch 批量添加
#!/usr/bin/env bash

NOTMUCH() {
    notmuch --config="$HOME/.config/notmuch/outlook/config" "$@"
}

## Apply tags
NOTMUCH tag --batch <"$HOME/email/taglist/tags.outlook"

NOTMUCH tag -inbox +sent -- folder:"Sent"
NOTMUCH tag -inbox +drafts -- folder:"Drafts"
NOTMUCH tag -inbox +deleted -- folder:"Trash"
NOTMUCH tag -inbox +junk -- folder:"Spam"

这里的 tags.outlook 是我自己维护的一个标签过滤列表
例如, 这里的第一条为来自 noreply@github.com 或者 no-reply@github.com 的邮件打上 noreply, github 标签

+noreply +github   -- from:noreply@github.com or from:no-reply@github.com
+noreply +codeberg -- from:noreply@codeberg.org
+noreply +google   -- from:noreply@google.com or from:no-reply@accounts.google.com
+noreply +firefox  -- from:accounts@firefox.com
+noreply +steam    -- from:noreply@steampowered.com

+noreply +forum +kde -- from:noreply@discuss.kde.org
+noreply +forum +emacscn -- from:noreply@emacs-china.org
+noreply +forum +archcn  -- from:noreply@archlinuxcn.org
+noreply +forum +debiancn -- from:debiancn@outlook.com
+noreply +forum +susecn -- from:noreply@suse.org.cn
+noreply +forum -- from:forum@cachyos.org

你可以使用 alot 查看标签列表,例如下图这样

Step 3: 使用 neomutt 显示邮箱邮件

neomutt 是 mutt 的一个分支,相较于 mutt 加了许多补丁,并且和上述提到的工具集成良好。
neomutt 涉及的内容比较多,由于篇幅原因,这里我重点说明与账号邮箱相关的配置, 它与前两步的配置相关。

# 这里的邮箱类型请选择 Maildir
set mbox_type = "Maildir"

set realname = "<用户名, 发邮件时会显示>"
set from     = "<你的 Outlook 邮箱地址>"

与 mbsync 配置相关的部分

## folder 指向你邮箱的根目录
set folder = "~/.local/share/maildir/outlook"

## 这些变量请与你邮箱名保持一致, `+` 表示如果不存在会自动创建, 路径相对于你的${folder}
set spoolfile = "+INBOX"
set record    = "+Sent"
set postponed = "+Drafts"
set trash     = "+Trash"

与 notmuch 配置相关的部分

## 与 notmuch 配置相关, 与你的 notmuch 配置保持一致
set nm_default_url = "notmuch://$HOME/.local/share/notmuch/outlook" # 与 database.path 保持一致
set nm_config_profile = "outlook" # notmuch 配置的目录名, 默认是 `default`

# 这个不必一致,它相当是为每个 notmuch://?query=tag:xx 后面默认加了一个 and not tag:deleted and not tag:junk
set nm_exclude_tags = "deleted, junk"

## 创建的虚拟邮箱
## 这里就用到了 notmuch 过滤, 这里仅是我的一个参考, 具体看你自己的过滤规则和喜好任意排布
named-mailboxes " INBOX"               "notmuch://?query=tag:inbox"
named-mailboxes " Unread"              "notmuch://?query=tag:unread"
named-mailboxes "> Sent"                "notmuch://?query=tag:sent"
named-mailboxes "> Drafts"              "notmuch://?query=tag:drafts"
named-mailboxes "󰊌 Forums"              "notmuch://?query=tag:forum"
named-mailboxes "     KDE"             "notmuch://?query=tag:forum and tag:kde"
named-mailboxes "    󰣇 Arch (CN)"       "notmuch://?query=tag:forum and tag:archcn"
named-mailboxes "     Debian (CN)"     "notmuch://?query=tag:forum and tag:debiancn"
named-mailboxes "     OpenSuse (CN)"   "notmuch://?query=tag:forum and tag:susecn"
named-mailboxes "     Emacs (CN)"      "notmuch://?query=tag:forum and tag:emacscn"
named-mailboxes " Github"              "notmuch://?query=tag:github"
named-mailboxes "    > noreply"         "notmuch://?query=tag:noreply and tag:github"
named-mailboxes " Codeberg"            "notmuch://?query=tag:codeberg"
named-mailboxes "    > noreply"         "notmuch://?query=tag:noreply and tag:codeberg"
named-mailboxes " Google"              "notmuch://?query=tag:google"
named-mailboxes "    > noreply"         "notmuch://?query=tag:noreply and tag:google"
named-mailboxes "󰈹 Firefox"             "notmuch://?query=tag:firefox"
named-mailboxes "    > noreply"         "notmuch://?query=tag:noreply and tag:firefox"
named-mailboxes " Steam"               "notmuch://?query=tag:steam"
named-mailboxes "    > noreply"         "notmuch://?query=tag:noreply and tag:steam"
named-mailboxes " Spam"                "notmuch://?query=tag:junk"
named-mailboxes " Trash"               "=Trash"

可以看到我的侧边栏和上面这个布局一致,可以通过快捷键进入不同的邮箱。

与 msmtp 配置相关的部分 (这一步暂时先跳过)

# 当你发送邮件时,它会调用执行的命令 (这里使用 msmtp)
# 这里 -a 是为了指向使用哪个账号发信, 之后会提到 msmtp 配置, 这里暂时不要管它
set sendmail = "/usr/bin/msmtp -a <你设置的 msmtp 账号>"

Step 4: msmtp 负责发送邮件

msmtp 作为 SMTP 客户端。

除了主机地址与端口不同,这里的许多配置与 isync 配置类似。

# Set default values for all following accounts.
defaults
auth on
tls  on
#tls_trust_file system
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        ~/.cache/msmtp/msmtp.log

## Microsoft Outlook
account         <msmtp 账号名>
from            <你的 Outlook 邮箱地址>
user            <你的 Outlook 邮箱地址>
host            smtp-mail.outlook.com
#host            smtp.office365.com
port            587
tls_starttls    on
#auth            oauthbearer
auth            xoauth2
passwordeval    oama access <你的 Outlook 邮箱地址>

技巧

最后,我为每个账号写了一个脚本, 用于自动同步邮件并打标签

#!/usr/bin/env bash

MAILDIR="$HOME/.local/share/maildir/outlook"
NOTMUCH_CONF="$HOME/.config/notmuch/outlook/config"
ISYNC_CONF="$HOME/.config/mbsync/config.outlook"

is_open_tui=0
is_only_push=0

NOTMUCH() {
    notmuch --config="$NOTMUCH_CONF" "$@"
}

MBSYNC() {
    mbsync --config "$ISYNC_CONF" "$@"
}

mkdir -p "$MAILDIR"

while [ -n "$1" ]; do
    case "$1" in
    -n | --now)
        MBSYNC outlook
        ;;
    --push)
        # only push
        MBSYNC --push outlook
        is_only_push=1
        ;;
    --pull)
        # only pull
        MBSYNC --pull outlook
        ;;
    --tui)
        is_open_tui=1
        ;;
    esac
    shift
done

if [ $is_only_push -ne 1 ]; then
    NOTMUCH new
fi

if [ $is_open_tui -eq 1 ]; then
    alot -n "$NOTMUCH_CONF"
fi

可以将它绑定到 neomutt 上的一个快捷键, 这样就可以在 neomutt 界面进行同步邮件了。

macro index S \
   "<shell-escape>~/.local/bin/email/sync-outlook -n<Enter>" \
   "Sync Outlook inbox in background"

结语

关于配置 neomutt/mutt 的帖子很少或是已经过时, 我花了大概一周时间才慢慢搞懂里面的许多内容, 更不提里面还有很多坑。

由于篇幅原因,我只挑了其中最重要的部分进行解释, 还有很多技巧和配置 (例如多账号、mailcap等)没有在这里展示。

完整的配置可以在我的 Codeberg 仓库找到。记得查看 devel 分支, main 分支可能不是最新。

2 Likes