同步 Notmuch tag 与 IMAP 文件夹以保证多端同步性

Notmuch 是一个以标签 (tag) 为中心的邮件客户端,但其标签通常不会与 IMAP 文件夹同步。而且它本身无法在 maildir 文件夹(一个 folder 对应你邮件账户中的一个 mailbox)之间移动邮件。如果你只用 Notmuch 来读邮件,这种设置没什么问题。但是,如果你还在使用其他邮件客户端,比如在手机上,并且希望在所有设备上保持同步,情况就会变得比较复杂。

本节介绍一些 hack 的方案,以方便 maildir 文件夹之间移动邮件从而与 remote 邮箱同步更改,使得在不同设备上同时使用 Notmuch 和其他邮件客户端时不会出现不同步的问题。

在 Maildir 文件夹之间移动邮件的 shell 脚本

将以下文件放在你的 $PATH 下,并给它一个类似 movemail 的名字。

这个脚本改编自 Notmuch 邮件列表中的讨论

这个脚本接受两个参数,第一个是邮件的文件路径,第二个是 Maildir 文件夹。通常第一个参数会在 notmuch hook 中通过 stdin 传入,第二个则是目标 maildir 位置。

#!/usr/bin/env bash
# move mails stored in maildir format between maildir folders. Mainly used as a
# pre-hook script executed at `notmuch new` command. This script also assumes
# the maildir synchronizer is mbsync/isync.

# `mbsync` requires an unique "UID" identifier for the filename of each mail.
# so the HEADER U=xxx should be removed before moving mails.

# remove the paths (leading characters), only containing the filename
only_last_file_name=${1##*/} # double #s indicates matching chars as many as possible.

# check if file if exists
if [ ! -f "$1" ];
then
    echo "File $1 does not exist."
    exit 0;
fi

# don't print the output of grep, just check the condition
if echo $1 | grep ':2,[PRSTDF]\{1,6\}' > /dev/null;
then
    # print out messages of moving files
    echo "Moving $1 to $2/cur/$(echo $only_last_file_name | sed 's/,U=[0-9]\{1,\}//')"
    # move messages with flags to cur/ directory
    mv -f "$1" "$2/cur/$(echo $only_last_file_name | sed 's/,U=[0-9]\{1,\}//')"
else
    # print out messages of moving files
    echo "Moving $1 to $2/new/$(echo $only_last_file_name | sed 's/,U=[0-9]\{1,\}//')"
    # move messages with no flags to new/ directory
    mv -f "$1" "$2/new/$(echo $only_last_file_name | sed 's/,U=[0-9]\{1,\}//')"
fi

整合上述 movemail 脚本的 Notmuch 配置

要整合 movemail 脚本,需要配置两个 Notmuch hooks:

  1. pre-new Hook
    • notmuch new 命令之前执行
    • 管理 Maildir 文件夹之间的邮件迁移
  2. post-new Hook
    • notmuch new 命令之后执行
    • 更新 Notmuch 标签以保证对应新的邮件位置
    • 用对应的 Maildir 文件夹更新标签

Example pre-new script

#!/usr/bin/env bash

my_personal_gmail="$HOME/Maildir/my-personal-gmail"
my_personal_outlook="$HOME/Maildir/my-personal-outlook"

# action-delete
notmuch search --output=files --format=text0 "tag:action-delete and folder:/my-personal-gmail/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_gmail/[Gmail]/Trash"

notmuch search --output=files --format=text0 "tag:action-delete and folder:/my-personal-outlook/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_outlook/Deleted"

# action-archive
notmuch search --output=files --format=text0 "tag:action-archive and folder:/my-personal-gmail/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_gmail/Archive"

notmuch search --output=files --format=text0 "tag:action-archive and folder:/my-personal-outlook/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_outlook/Archive"

# action-spam
notmuch search --output=files --format=text0 "tag:action-spam and folder:/my-personal-gmail/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_gmail/[Gmail]/Spam"

notmuch search --output=files --format=text0 "tag:action-spam and folder:/my-personal-outlook/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_outlook/Junk"

# action-inbox
notmuch search --output=files --format=text0 "tag:action-inbox and folder:/my-personal-gmail/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_gmail/Inbox"

notmuch search --output=files --format=text0 "tag:action-inbox and folder:/my-personal-outlook/" \
    | xargs -r -0 -n1 -I{} movemail {} "$my_personal_outlook/Inbox"

Example post-new script

#!/usr/bin/env bash
# Synchronization between maildir folders and tag

notmuch tag +sent -- folder:"/Sent/"
notmuch tag -sent -- not folder:"/Sent/"

notmuch tag +deleted -- folder:"/Trash/" or folder:"/Deleted/"
notmuch tag -deleted -- not folder:"/Trash/" and not folder:"/Deleted/"

notmuch tag +drafts -- folder:"/Drafts/" or folder:"/drafts/"
notmuch tag -drafts -- not folder:"/Drafts/" and not folder:"/drafts/"

notmuch tag +archive -- folder:"/Archive/"
notmuch tag -archive -- not folder:"/Archive/"

notmuch tag +spam -- folder:"/Spam/" or folder:"/Junk/"
notmuch tag -spam -- not folder:"/Spam/" and not folder:"/Junk/"

notmuch tag +inbox -- folder:"/Inbox/"
notmuch tag -inbox -- not folder:"/Inbox/"

notmuch tag -action-delete -- tag:deleted
notmuch tag -action-archive -- tag:archive
notmuch tag -action-spam -- tag:spam
notmuch tag -action-inbox -- tag:inbox

解释

pre-new hook 执行以下任务:

  • 检查当前邮件的标签。如果邮件有 action-delete 标签,就将其移到相应邮件账户的垃圾箱文件夹。
  • action-archiveaction-spamaction-inbox 应用相同的逻辑,将邮件移动到各自的文件夹。

post-new hook 执行两个主要的标签管理任务:

  1. 基于位置更新标签

    • 为 sent folder 中的邮件分配 sent 标签
    • 从不再 sent folder 中的邮件删除 sent 标签
    • 对其他标签重复相同操作:archive、inbox 和 spam
  2. 清理基于操作的标签

    • 邮件移动后删除操作相关的标签(deleted、archive、spam、inbox)
    • 文件夹转换成功后清理标签
    • 防止对已处理的邮件重复操作

Emacs configuration

The following lines in my Emacs configuration reflect my tag setup:

(setq notmuch-tagging-keys '(("a" notmuch-archive-tags "Archive")
                             ("u" notmuch-show-mark-read-tags "Mark read")
                             ("f" ("+flagged") "Flag")
                             ("s" ("+action-spam") "Mark as spam")
                             ("d" ("+action-delete") "Delete"))
      notmuch-archive-tags '("+action-archive")
      notmuch-draft-tags '("+drafts")
      mg-notmuch-deleted-tags "action-delete")

当我运行 notmuch-tag-jump 命令时,我可以给邮件标记特定的操作:

  • action-delete 用于删除邮件
  • action-archive 用于归档邮件
  • action-spam 用于将邮件标记为垃圾邮件

同步邮件的完整命令

notmuch new && mbsync -a && notmuch new
  • 第一个 notmuch new 在 Maildir 文件夹之间移动邮件
  • 第二个 notmuch new 索引新收到的邮件并更新本地的 notmuch database
1 个赞