Proton 主页

Sieve 筛选(高级自定义筛选)

阅读
11 分钟
类别
接收和阅读电子邮件

Proton Mail 为用户提供了多种自动筛选电子邮件的方式,可以为邮件分配标签或将邮件归入文件夹。通常有以下三种方法:

  1. 将发件人添加到黑名单和白名单 ,使他们的邮件始终或从不进入垃圾邮件文件夹;
  2. 通过 Proton Mail 的交互式界面创建自定义筛选;
  3. [最高级] 使用 Sieve 创建自定义筛选。

在这些方法中,使用 Sieve 创建自定义筛选的灵活性最高,但使用起来也更为复杂。因此,我们将 Sieve 视为面向有一定技术经验用户的高级功能。对于大多数用户而言,交互式界面足以构建您所需的自定义筛选。

目录

什么是 Sieve?

Sieve 是一种用于筛选电子邮件的编程语言。您可以通过编写简单的规则在 Sieve 中创建筛选。例如,“为来自小李的所有邮件添加绿色标签”。将这些规则组合起来,就能构建出复杂的筛选系统。

您可以从头编写 Sieve 规则,借鉴本文所列示例之类的现成规则,也可以借助软件来简化编写过程。

事实上,我们已经为您提供了一种使用交互式界面创建 Sieve 筛选的方法,该界面会根据您的输入生成 Sieve 筛选。学习 Sieve 的另一个好方法是先使用交互式界面创建筛选,再在 Sieve 中编辑筛选,因为交互式界面本身就使用了 Sieve 的一个子集。

例如,将以下使用交互式界面创建的筛选……


……与在 Sieve 编辑器中查看的同一筛选的代码进行对比:

本文将向您介绍 Sieve,并讲解如何编写自己的 Sieve 筛选。如果您需要更多关于使用 Sieve 的信息,网上有许多其他教程可以提供帮助。如果您对 Proton Mail 中的 Sieve 有疑问而本页面未能解答,可以随时通过这里联系支持团队。

入门

要开始创建 Sieve 筛选,请登录 mail.proton.me(新窗口),然后前往设置 → 所有设置 → Proton Mail → 筛选 → 添加 Sieve 筛选。

Sieve 脚本由一系列命令组成。在大多数脚本中,它以 require 命令开头。require 命令用于加载提供特定功能的扩展程序。例如,要分配标签或将邮件放入文件夹,我们需要使用 fileinto 命令。

要加载此命令,只需写入:

require "fileinto";

要加载多个扩展程序,可以使用列表:

require ["fileinto", "imap4flags"];

在这里,imap4flags 加载的扩展程序可用于将邮件标记为已读。在 require 命令之后,您通常需要对收到的邮件执行一些测试。这可以通过将 if 与其他命令(如 address 或 header)结合来实现。

最后,如果测试通过,您就可以对邮件应用操作。例如,假设我们想把来自特定发件人的所有电子邮件都放入同一个文件夹,并将收到的邮件标记为已读,则可以这样编写:

require ["fileinto", "imap4flags"];
# I don't really like Spott
if address :is "from" "Spott.Tenerman@northpark.example.com"
{ 
    addflag "\\Seen";
    fileinto "enemies";
}

请注意,# 符号表示注释,不会被解释为 Sieve 脚本的一部分。此外,您设置的文件夹或标签(此处为 enemies )必须存在于您的环境中:您可以在此了解如何创建文件夹和标签。输入此筛选后,请点击“保存”按钮。此后,您的筛选将对每一封收到的电子邮件运行。

当然,您可能想把来自多个发件人的电子邮件放入同一个文件夹。在这种情况下,您可以向 address 命令传递一个字符串列表(字符串(新窗口)是由一系列字符组成的,此处即为电子邮件地址):

require ["fileinto", "imap4flags"];
# I don't really like Spott and Kyyyhel
if address :is "from" ["Spott.Tenerman@northpark.example.com", "Kyhel.Broski@northpark.example.com"]
{ 
    addflag "\\Seen";
    fileinto "enemies";
}

请注意,凡是能向测试条件传递字符串的地方,通常也可以传递列表。此时,系统会尝试匹配列表中的任意字符串。但这不适用于 fileinto、addflag 等命令。

但 Sieve 的功能远不止整理邮箱。它还允许您拒绝电子邮件并附上回复消息:

require "reject";
# Reject mails that spell my name wrong
if header :contains "subject" "Kyhel"
{
    reject "My name is not Kyhel";
}

使用测试

在 Sieve 中组合测试

正如我们在入门部分,向您展示的那样,您可以使用 if 命令对收到的邮件执行测试,以确定某封电子邮件是否应受 require 命令的影响。

除了 if 命令之外,还有其他测试命令可让您以简单的方式构建脚本。

Else

else 命令允许您在 if 命令未执行其操作的情况下执行某些操作。示例如下:

require ["fileinto"];
# If the subject contains something incomprehensible, then put the mail into the kenny folder
if header :contains "subject" "mmph mmph"
{
    fileinto "Kenny";
} else { 
    fileinto "Understandable";
}

在这里,如果主题恰好是 mmph mmph,电子邮件将被移动到 Kenny 文件夹中。反之(即主题不是 mmph mmph 时),电子邮件将被移动到 Understandable 文件夹中。

Elsif

elsif 是 else if 的缩写。与 else 命令一样,它会在 if 条件不成立时执行,但前提是 elsif 条件成立。如果 elsif 条件不成立,则会执行下一个 elsif 或 else 代码块。

require ["fileinto", "imap4flags"];
# If the subject contains something incomprehensible, then put the mail into the kenny folder
if header :contains "subject" "mmph mmph"
{
    fileinto "Kenny";
# Kyhel sends me only speeches
} elsif address :is "from" "Kyhel.Broski@northpark.example.com" { 
    fileinto "Speeches";
} else { 
# otherwise the mail is important, so add a star.
    addflag "\\Flagged";
}

请注意,在一次运行中,if、elsif 或 else 代码块中只有一个会被执行。

Anyof

有时,您需要在多个测试中任一项成功时执行命令。为此,可以使用 anyof 命令。具体写法是在 anyof 后面跟一个左括号”(“、想要测试的语句(以逗号分隔),最后再加一个右括号”)”。

require ["fileinto", "imap4flags"];
# Kenny either sends from kenny@northpark.example.com or puts "mmph mmph" in the subject.
   if anyof(address :is "from" "kenny@northpark.example.com", header :contains "subject" "mmph mmph")
{
    fileinto "Kenny";
}

此脚本会将来自 kenny@northpark.example.com 或主题行中包含 mmph mmph 的消息放入 Kenny. 文件夹。

Allof

anyof 也有对应的命令:allof。它允许您仅在所有给定条件都匹配时才执行命令:

require ["fileinto", "imap4flags"];
# Kenny always sends me mails with mmph mmph
if allof(address :is "from" "Kenny@northpark.example.com", header :contains "subject" "mmph mmph")
{
    fileinto "Kenny";
}


此脚本会将来自 kenny@northpark.example.com 且主题行中包含 mmph mmph 的消息放入 Kenny 文件夹。

不要

最后,有时需要在某个条件不匹配时应用命令。只需在语句前面添加 not 即可实现:

require ["fileinto", "imap4flags"];
# If a subject line does not contain real guitar put it into the young people folder
if not header :contains "subject" "real guitar"
{
    fileinto "Young people";
}

这等同于:

require ["fileinto", "imap4flags"];
# The else part will be evaluated if the condition is not true
if header :contains "subject" "real guitar"
{
    # do nothing
} else {
    fileinto "Young people";
}

这会将主题行中不包含 real guitar 的所有电子邮件放入 Young people 文件夹。

当然,也可以将所有这些测试组合在一起使用。例如,如果一封电子邮件既不来自 Kenny,主题中也不包含 mmph mmph,您可能想为其添加星标:

require ["fileinto", "imap4flags"];
if not anyof (    header :contains "subject" "mmph mmph", 
    address :is "from" "Kyhel.Broski@northpark.example.com" ) { 
    addflag "\\Flagged";
}

对邮件标题进行测试

使用 address 命令,您可以对地址标题进行测试,例如 from、to 和 sender 标题。您可以使用以下任一标志来提取电子邮件地址的不同部分:

  • :localpart — @ 符号之前的部分
  • :domain — @ 符号之后的部分
  • :all — 完整地址

以下代码片段说明了此命令的用法:

require ["fileinto", "imap4flags"];
# Northpark people are made of paper, springfield are mostly yellow
if address :domain "from" "northpark.example.com"
{
    fileinto "PaperPeople";
} elsif address :domain "from" "springfield.example.com"{
    fileinto "YellowPeople";
}
if address :localpart "from" "chef"
{
    addflag "\\Flagged";
}

总而言之,此脚本会将从 northpark.example.com 域发送的所有内容放入 PaperPeople,并将从 springfield.example.com 域发送的内容放入 YellowPeople。

此外,无论电子邮件从哪个域名发送,只要 @ 之前的部分等于 chef(例如 chef@example.com),该消息就会被标记。另一个有趣的用法是将发送到某个地址的所有内容自动存入一个文件夹:

require ["fileinto", "imap4flags"];
# Put support mail in a separate organized folder
if address :localpart "to" "support"
{
    fileinto "Support";
}

高级

envelope 命令允许您执行更多测试。一般来说,address 测试只能从标题中提取值。但是,在 SMTP 会话中,可以指定与标题中不同的 from 地址。

在 Proton Mail 的标题界面中,envelope-from 等于 return-path 标题,envelope-to 等于 x-original-to 标题。

使用 envelope 命令,您可以从信封中获取实际的 to: 和 from: 地址。请注意,除这两个字段之外不存在其他字段,因此此命令中没有 sender。另外请注意,envelope 属于一个扩展程序,因此需要先 require 该扩展程序。

require ["fileinto", "imap4flags", "envelope"];
# Northpark people are made of paper, springfield are mostly purple
if envelope :domain "from" "northpark.example.com"
{
    fileinto "PaperPeople";
} elsif envelope :domain "from" "springfield.example.com"{
    fileinto "PurplePeople";
}
if envelope :localpart "from" "chef"
{
    addflag "\\Flagged";
}

使用比较器评估两个值

当测试需要评估两个值时,可以通过名为比较器(comparator)的不同标志来指定比较方式。前面我们已经用过两个比较器::is 和 :contains,前者检查给定字符串是否与特定值完全相等,后者检查特定值是否包含给定字符串,如下面的测试所示。

require ["fileinto", "imap4flags"];
if not anyof (
    header :contains "subject" "mmph mmph", # the subject contains mmph mmph 
    address :is "from" "Kyhel.Broski@northpark.example.com" # the recipient is exactly Kyhel.Broski@northpark.example.com
) { 
    addflag "\\Flagged";
}

比较器 :matches 还可用于定义更具体的格式。与比较器 :is 一样,它会从开头到结尾对两个值进行比较。但在 :matches 的情况下,您定义的值可以包含 ? 和 *。问号将匹配一个字符,星号(称为通配符)将匹配零个或多个字符。

使用这种格式,您可以创建如下测试:

require ["fileinto", "imap4flags"];
if header :matches "subject" "mmph*" { 
    addflag "\\Flagged";
}

在此示例中,如果消息的主题以 mmph 开头,并且后面跟有一个或多个字符,则测试将成功。换句话说,当主题以”mmph“开头时,该电子邮件就会被标记。”mmph mmph“和”mmph mmph Hello“都会匹配;”Hello mmph“和”Springfield sales“则不会。

星号还可以用在值的中间,并且可以多次使用,如下所示:

require ["fileinto", "imap4flags"];
if header :matches "subject" "mmph *mmph *mmph" { 
    addflag "\\Flagged";
}

如果主题以”mmph “(含一个空格)开头、包含另一个”mmph “(含一个空格),并以”mmph“(不含空格)结尾,则此测试将匹配。匹配的主题包括”mmph mmph mmph“、”mmph mmph mmphmmph“或”mmph is mmph and mmph“。

这个 :matches 比较器在比较地址时非常有用。如您所知,Proton Mail 有多个域名:protonmail.com 和 proton.me。如果您想检查消息是否来自 Proton Mail 用户,可以使用此脚本:

require ["fileinto", "imap4flags"];
# Put support mail in a separate organized folder
if address :domain :matches "from" "protonmail.*"
{
    fileinto "Internal";
}

如果出于某种原因,您需要精确匹配字符 * 或 ?,那么您可以通过在其前面添加 \\ 来引用(转义)它们:\\* 将匹配星号,\\? 将匹配问号。

请注意,我们还支持 regex 扩展程序,它同样定义了比较器 :regex。此比较器是 :matches 的更精确版本,但非常复杂。欲了解更多信息,请阅读官方文档(新窗口)。

在某些情况下(您将在本文后续部分看到),您可能需要比较数值。relational 包正是为此用途而设计的。此包定义了比较器 :value,其后需要跟比较类型。

它允许您检查某个值是否大于(比较类型为 “gt”)、大于等于(”ge”)、等于(”eq”)、小于等于(”le”)或小于(”lt”)给定值。

在比较数值时,comparator-i;ascii-numeric 包也非常有用。它会告诉脚本解释器,字符串的内容是数字,而不是常规字符串。可以通过在测试中添加 :comparator “i;ascii-numeric” 来使用它。

利用以上所有信息,我们可以构建如下脚本:

require ["fileinto", "relational", "comparator-i;ascii-numeric"];  
if header :value "ge" :comparator "i;ascii-numeric" "subject" "2"   
{    
 fileinto "Dummy example";
}

如果主题大于或等于 2,此测试会将消息移动到 Dummy example 文件夹。如果主题不是数字而是文本,则测试将失败。因此,这个示例看起来很简单,但它旨在与其他扩展程序(例如 date,我们将在本文后面介绍)结合使用。

在 Sieve 中使用上下文进行比较

您还可以使用扩展程序访问与您的帐户以及 Proton Mail 整体上下文相关的信息。例如,您可以检查发件人地址是否在联系人名单中。

访问您的联系人名单

您可以使用 extlists 扩展程序访问您的联系人名单。结合标题测试,即可检查某位联系人是否在您的联系人名单中。

require ["fileinto", "extlists"];  
# Checks that the sender is in your personal address book
if header :list "from" ":addrbook:personal?label=Family"   
{    
 fileinto "Known"; 
}

如果发件人包含在列表 :addrbook:personal?label=Family 中,此测试将为消息添加 Known 标签。该列表可以分为两部分。第一,:addrbook:personal 表示该地址在您的个人通讯录中。第二,label=Family 进一步缩小列表范围,指定联系人除了在您的通讯录中之外,还必须属于联系人群组 Family。如有需要,您可以将此标签更改为其他联系人群组。您也可以根据需要使用 :addrbook:personal?label=Work,此时只有当发件人在您的通讯录中且属于联系人群组 Work 时,测试才会成功。

更一般地说,列表遵循 Tag URI Scheme(新窗口),您可以添加额外参数来缩小筛选范围。然后即可按如下方式使用此列表:

require ["fileinto", "extlists"];  
# replace :your:list:here by the list you want to use
if header :list "from" ":your:list:here"
{    
 # some actions... 
}

我们提供了四个不同的列表:

  • :addrbook:personal¹ 用于检查某个地址是否在您的联系人名单中。该列表接受参数 label,用于匹配特定的联系人群组。此参数有四种变体:
    • :addrbook:personal?label=something 将匹配属于联系人群组 “something” 的联系人;
    • :addrbook:personal?label.starts-with=something 将匹配至少属于一个以 “something” 开头的群组的联系人;
    • :addrbook:personal?label.ends-with=something 将匹配至少属于一个以 “something” 结尾的群组的联系人;
    • :addrbook:personal?label.contains=something 将匹配至少属于一个包含 “something” 的群组的联系人。

您还可以访问与匹配的电子邮件相关的加密信息:

  • :addrbook:personal?keypinning=true 将匹配拥有已信任的密钥的联系人。将 true 改为 false,则会匹配没有已信任的密钥的联系人;
  • :addrbook:personal?encryption=true 将匹配已启用加密的联系人。将 true 改为 false,则会匹配未设置加密或已停用加密的联系人;
  • :addrbook:personal?signing=true 将匹配已启用签名的联系人。将 true 改为 false,即可匹配已禁用签名的联系人。
  • :addrbook:myself¹ 匹配您自己拥有的所有地址;
  • :addrbook:organization 匹配您所在组织中任何成员拥有的所有地址;
  • :incomingdefaults:inbox 检查该地址是否在您的白名单中;
  • :incomingdefaults:spam 检查该地址是否在您的黑名单中。

结合变量扩展程序(将在下一段中介绍),匹配变量将会发生改变。匹配变量 ${0} 将始终包含指定列表中的最后一个电子邮件地址。如果列表标记了备注 1,则匹配变量 ${1} 将包含显示名称。

例如,借助下面的列表,您可以创建一个筛选器,删除来自家人或白名单联系人以外任何人的所有电子邮件:

require "extlists";  
# checks that the sender is not in the contact group Family, whilelisted or yourself
if not anyof(
    header :list "from" ":addrbook:personal?label=Family", 
    header :list "from" ":incomingdefaults:inbox",
    header :list "from" ":addrbook:myself"
) {    
  discard; # permanently delete the email
}

如果满足该条件,就会执行 discard 操作。此操作会立刻且永久地删除该电子邮件。您也可以不使用 discard,而是通过 fileinto 操作将邮件移动到废件箱:fileinto “trash”;。

创建变量

管理上下文时,另一个实用工具是定义变量。变量是一种临时存储位置,您可以在其中放入文本并为其命名。之后,您就可以通过名称调用并重复使用这些内容。例如,您可以编写如下脚本:

require ["reject", "variables"];
# First check who is the sender
if allof(
    address :is "from" "Kenny@northpark.example.com", 
    header :contains "subject" "mmph mmph"
) {
    # It's from Kenny!
    # Create the variable message containing 'mmph mmph'
    set "message" "mmph mmph";
} else {
    # Create the variable message containing 'Sorry, I don't want emails today!'
    set "message" "Sorry, I don't want emails today!";
}
# Then, reject the message
reject "${message}";

这个脚本的目标是用自定义消息拒绝电子邮件。如果原始邮件来自 kenny 且主题为“mmph mmph”,则会创建变量“message”,其内容为 mmph mmph。否则,消息将被填充为“Sorry, I don’t want emails today!”。最后一步,我们在 reject 命令中重复使用此变量。

Sieve 本身并不定义变量,变量是在 variables 扩展程序中定义的,必须在脚本开头引用该扩展程序。定义变量有两种方法。

  • 显式创建变量:

操作 set 用于创建变量。

require "variables";
# Create a variable called "labelname" and containing the string "Work".
set "labelname" "Work";

第一个字符串是变量的名称,第二个是变量的值。因此,上面的示例创建了一个名为 labelname 、值为 Work 的变量。

变量定义后,您可以在字符串中添加以下格式来调用它:${name},其中 name 是变量的名称。执行时,它会被替换为变量的值。(如果未定义该变量,则会被替换为空字符串。)因此,在以下脚本中:

require "variables";
require "fileinto";
# Create a variable called "labelname" and containing the string "Work".
set "labelname" "Work";
# Move the email in "${foldername}/${labelname}" which becomes after variable resolution "/Work";
fileinto "${foldername}/${labelname}";

您的电子邮件将被加上 /Work 标签。由于我们已经定义了变量 labelname,因此它会被替换为 Work。但是,foldername 尚未定义,所以会被直接移除。

  • 隐式为变量赋值:

变量最有意思的用例是配合 :matches 测试使用。下面举个例子,假设发件人的电子邮件地址为 test@proton.me:

require "variables";
require "fileinto";
# do a matches test 
if header :matches "from" "*@*" {
    # The first * matches "test", the second "protonmail".  
    # Thus, the first matching variable contains "test"
    fileinto "${1}";
}

与 variables 扩展程序一起使用时,match 操作的结果将存储在变量中。系统会设置多个以数字命名的变量。首先,变量 0 将包含完整匹配结果(在此示例中即完整地址:test@proton.me)。然后,第一个匹配群组将赋值给变量 1(在此示例中为 test),第二个匹配群组将赋值给第二个变量(proton.me),依此类推,直到最后一个匹配群组。

在此示例中,将执行 fileinto 命令。位置被定义为 ${1},它会被识别为变量,因此如上文所述被替换为其值:test。

请注意,也可以在 :regex 测试中使用正则表达式匹配群组来为变量赋值。

转换变量

变量转换是一个很实用的功能。为此,set 关键字可以与多个标志组合使用,以便在将值赋给变量之前对其进行更改。

  • :lower 会将变量值改为小写;
  • :upper 会将变量值改为大写;
  • :lowerfirst 会将变量值的第一个字母改为小写;
  • :upperfirst 会将变量值的第一个字母改为大写;
  • :quotewildcard 会为字符串中包含的任何通配符加上引号,使其可以在 match 语句中按字面意义使用;
  • :length 会返回值的长度。

请注意,您还可以在另一个 set 操作中重复使用已定义的变量。我们来改进一下前面的示例:

require "variables";
require "fileinto";
# Set labelname to WORK, and modify it to lowercase with the first letter in upper case 
set :lower :upperfirst "labelname" "WORK";
set :lower :upperfirst "foldername" "geneva";
# Create a variable that is a combination of foldername and labelname
set "location" "${foldername}/${labelname}";
fileinto "${location}";

这个筛选器将逐步创建三个变量。第一条命令 set 会创建变量 labelname,其内容为 Work。这是因为原始值 WORK 先后经过 :lower 和 :upperfirst 两个标志处理,先变为 work,再变为 Work。

第二条 set 命令将创建第二个变量 foldername,其值为字符串 Geneva。这里同样使用了 :lower 和 :upperfirst 标志。

最后,创建变量 location。值“${foldername}/${labelname}”包含两个变量:foldername 和 labelname。因此,Sieve 解释器会将这些变量替换为它们的值:Work 和 Geneva。

最后,变量 location 被用作 fileinto 命令的参数。这样,电子邮件将被移动到以变量 location 的值命名的文件夹中:Work/Geneva。

比较器也可以应用于匹配变量:

require "variables";
require "fileinto";
if address :all :matches "from" "*@*" {
    set :lower :upperfirst "fileintovar" "${1}";
    fileinto "${fileintovar}";
}

在这里,如果发件人是 test@proton.me,该电子邮件将被加上 Test 标签。

更改变量值的另一种方法是使用 vnd.proton.eval 扩展程序。该扩展程序定义了新标志 :eval,让您可以进行一些简单的计算:

require "variables";
require "fileinto";
require "vnd.proton.eval";
# do a match test on the sender address
if header :matches "from" "*" {
    # create a variable called length, containing the length of the first     
    # matching variable
    set :length "length" "${1}"; 
    # Create a variable called fileintovar containing the result of the expression written below
    set :eval "fileintovar" "${length} * 25 - 1 / 8+3";
    fileinto "${fileintovar}";
}

在此示例中,仍然假设电子邮件来自 test@proton.me,该邮件将被标记为值 478。这是因为第一个匹配变量的长度为 19,而 19 * 25 – 1 / 8 + 3 的结果向上取整后为 478。

乍看之下这可能没什么用处,但与其他操作结合使用时,该扩展程序就能发挥更大的作用。

在 Sieve 中与其他字段进行比较

也可以针对特定邮件头进行匹配。例如,要将发送到邮件列表的所有消息(这些通常是营销消息或新闻通讯)归类存放,可以这样编写:

require "fileinto";
# Filter all lists into the same folder
if exists "list-unsubscribe"
{
    fileinto "advertisements";
}

要将社交媒体邮件归入各自的文件夹,可以这样编写:

require "fileinto";
# Filter all lists into the same folder
if anyof(exists "x-facebook", exists "x-linkedin-id") {
    fileinto "social";
} elsif exists "list-unsubscribe"
{
    fileinto "advertisements";
}

请注意,这里使用的是:

anyof(exists "x-facebook", exists "x-linkedin-id")

而不是:

exists ["x-facebook", "x-linkedin-id"]

因为后者会检查 x-facebook 和 x-linkedin-id 是否都已设置。

要查看邮件头实际包含的值,可以使用 header 测试:

require "fileinto";
# Put all mails that have been sent without TLS/SSL into the same folder
if header :is "x-pm-transfer-encryption" "none" {
    fileinto "unencrypted";
}

除了进行精确匹配的 :is 操作之外,header 命令还支持使用通配符进行匹配的 :matches(例如“*@*.com”会匹配任何以 .com 结尾的电子邮件地址),以及检查邮件头是否包含给定字符串的 :contains 命令。

设计 Sieve 筛选器时,检索电子邮件的邮件头可能很有用。要在 Proton Mail 网页界面中执行此操作,请前往更多 (∨) → 查看邮件头信息。届时会打开一个新窗口,其中包含该电子邮件的所有邮件头。

在 Sieve 中根据消息大小进行筛选

可以对大型电子邮件和小型电子邮件区别处理。例如,您可能希望标记大型电子邮件,以便将其删除,从而节省邮箱空间。实现方式如下:

require ["imap4flags"];
# Flag emails that probably have large attachments (> 2 MiB)
if size :over 2M # you can also use 2097152 if you want, they are synonymous
{
    addflag "\\Flagged";
}
# Automatically mark as read really small messages. They can't have much content anyway...
if size :under 1000
{
    addflag "\\Seen";
}

如您所见,如果要指定较大的大小,可以使用单位,只需在数字后面直接加上对应单位的字母即可。共有三种单位:K 表示 千比字节(即 1024 字节),M 表示 兆比字节(即 1 048 576 字节),G 表示 吉比字节 (即 1 073 741 824 字节)。

请注意,这里的“over”表示大于,“under”表示小于。也就是说,如果一封消息的大小恰好为 1000 字节,则

size :under 1000

和

size :over 1000

均不匹配。

请注意,Sieve 筛选器无法访问邮件的实际内容,只能显示加密后的大小。

对消息执行高级操作

在 Sieve 中,您可以对消息执行不同的操作。我们已在前面几节介绍了 fileinto、addflag、discard 和 reject 操作。本节将介绍更多高级操作。

假期消息和日期测试

您可以实现自动回复功能,方法是在 Sieve 中使用 vacation 命令。实际上,设置中的自动回复功能本身就依赖 Sieve 来实现。但在脚本中使用 vacation 选项,可以有更多自定义自动回复的方式。例如,您可以根据条件定制特定消息。注意:与设置中的自动回复功能一样,vacation 操作仅适用于付费套餐。

vacation 命令会向任何尝试联系您的人发送假期自动回复。它通常与 currentdate 或 date 测试配合使用,以便在特定时间段内发送回复。

假设我要在科罗拉多时间 2017 年 7 月 14 日至 2017 年 8 月 14 日期间休假。我可以使用以下 Sieve 代码来设置自动回复器:

require ["date", "vacation", "relational"];
if allof(currentdate :zone "US/Mountain" :value "ge" "date" "2017-07-14",
 currentdate :zone "US/Mountain" :value "le" "date" "2017-08-14")
{
    vacation "Queue you guys, I'm going on vacation.";
}

vacation 命令的可选参数包括 :handle 和 :days。默认情况下,Sieve 在特定天数(称为超时时间)内不会多次回复同一发件人。这可以避免无意中发送大量自动邮件。

要控制超时时间,可以使用 :days 参数。:days 参数用于指定保留地址且不回复这些地址的时间段,并且始终以天为单位指定。有时,Sieve 脚本中会有多个 vacation 命令,而您需要确保每条命令(在规则匹配时)至少发送一次回复。在这种情况下,可以使用 :handle 选项。:handle 的参数是一个字符串,用于标识所发送回复的类型。

为了说明其工作原理,假设您是一名教师。教师经常通过电子邮件收到作业。将这些作业归类到单独的文件夹中会很有用。此外,还可以拒绝任何超过截止日期提交的作业。

在这种情况下,您可能希望使用 handle 来确保在需要时发送回复:

require ["date", "vacation", "reject", "fileinto", "relational"];
if header :contains "subject" "Homework assignment 1"
{
    # remind people not to forget the attachments
    if size :under 5000
    {
        vacation :handle "Homework assignment 1 - missing attachment" "Your message size is really low. Please make sure you didn't forget to add the homework as an attachment.";
    }
   
    # check if the student made the deadline
    if  currentdate :zone "US/Mountain" :value "le" "date" "2017-06-12"
    {
        fileinto "Homework Assignment 1";
    } else {
        reject "Too late, you missed the deadline.";
    }
}
if header :contains "subject" "Homework assignment 2"
{
    # remind people not to forget the attachments
    if size :under 5000
    {
        vacation :handle "Homework assignment 2 - missing attachment" "Your message size is really low. Please make sure you didn't forget to add the homework as an attachment.";
    }
   
    # check if the student made the deadline
    if  currentdate :zone "US/Mountain" :value "le" "date" "2017-06-12"
    {
        fileinto "Homework Assignment 2";
    } else {
        reject "Too late, you missed the deadline.";
    }
}

以下是假期回复允许使用的全部参数,按应传递的顺序列出:

  • :days 是自动回复器发送一条假期消息后,不应再向同一发件人发送回复的天数
  • :subject 是自动回复器回复发件人时使用的前缀(默认为 auto)。例如,主题为“作业 2”的邮件在传递了 :subject “late” 的情况下,将收到主题为“late: 作业 2”的回复。
  • :mime 表示回复的第一行使用特定格式。这样可以以 html 消息格式发送回复。例如,要编写 html 回复,请传递 :mime 参数,并在回复的第一行写入 Content-Type : text/html。
  • :handle 是一个字符串,用于标识所发送回复的类型。

请注意,:zone 是 currentdate 中的可选参数,如果未设置,将使用服务器当地时间(对于 Proton Mail,即瑞士日内瓦时间)。

zone 参数接受时区偏移量(即“+0100”这样的字符串,表示 UTC+1),也接受 ICANN 数据库中的实际时区(参见 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones(新窗口))。后一种选项通常更有用(虽然不是标准的 Sieve),因为它还包含各时区的夏令时信息。

您可以使用常规的 :is/ :contains/ :matches 参数来比较日期。例如,以下 Sieve 命令匹配 2017 年 7 月的任何日期:

currentdate :zone "US/Mountain" :matches "date" "2017-07-??"

但在大多数情况下,:value 参数要实用得多。

需要传递的最后一个参数是格式。格式还决定了要比较日期的哪一部分。在前面的示例中,我们使用了日期格式。所有支持的格式如下:

  • year,年份,格式为“0000”至“9999”
  • month,月份,编码为“01”至“12”
  • day,日,编码为“01”至“31”
  • date,编码为 yyyy-mm-dd
  • hour,小时,编码为“00”至“23”
  • minute,分钟,编码为“00”至“59”
  • second,秒,编码为“00”至“60”(60 是闰秒(新窗口),只有在科学家认为有必要时才会在 23:59:60 出现)
  • time,时间,格式为 hh:mm:ss
  • iso8601,符合 ISO8601 标准的日期和时间,例如:2005-08-15T15:52:01+00:00
  • std11,符合 RFC2822 标准的日期和时间,例如:Mon, 15 Aug 2005 15:52:01 +0000
  • zone,时区偏移量,格式为 +/-zzzz,例如 +0000 或 -1200
  • julian,自 UTC 时间 1858 年 11 月 17 日以来的天数
  • weekday,星期几,从周日(0)到周六(6)

最后,也可以从邮件标题中获取日期,而不使用 currentdate。此 date 命令的工作方式与 currentdate 相同,只是需要在格式字符串之前多指定一个字符串:标题名称。例如:

date :zone "US/Mountain" :matches "received" "date" "2017-07-??"

请注意,标题不一定准确:发件人可以随意更改标题,因此并不可靠。

注意事项:

  1. 该自动回复器不会回复自动生成的消息,例如邮件列表、由其他自动回复器发送的电子邮件,或来自 noreply 地址的消息。
  2. 自动回复器不会向同一个电子邮件地址多次发送消息,除非 vacation 命令使用了不同的 :handle。

管理过期时间

Proton Mail 的一项独特功能是可以为已发送的邮件设置过期时间,到期后消息将从收件人的邮箱中删除。

您也可以通过添加过期时间,利用此功能来管理收到的消息:

require "vnd.proton.expire"; 
# permanently delete all incoming and outgoing emails after 10 days
expire "day" "10";

上面的脚本将在 10 天后删除所有收到的电子邮件。这有点极端,因为它适用于全部电子邮件。更合理的做法是使用条件,让脚本只应用于特定的一组电子邮件。例如,可以让不在您联系人中的人发来的消息过期:

require ["extlists", "vnd.proton.expire"];
# permanently delete after 10 days any email not from me or from someone in my address book.
if not anyof(
    header :list "from" ":addrbook:personal",
    header :list "from" ":addrbook:myself"
) {
 expire "day" "10";
}

注意:如果指定的过期时间超过 730 天,将自动限制为 730 天。

Sieve 的限制

有几点 Sieve 限制需要注意。

  • 已发送邮件无法手动或通过筛选器移动到收件箱/草稿箱。此类邮件将始终保留在已发送文件夹中,并保留用户或筛选器应用的文件夹/标签。
  • 接收的邮件无法手动或通过筛选器移动到草稿箱/已发送。
  • 草稿无法手动或通过筛选器移动到收件箱/已发送。
  • 对于垃圾邮件中的邮件(或处理期间被归入垃圾邮件的邮件),我们不会发送自动回复/Vacation 消息。如果您需要此类功能,可以考虑添加一个自定义文件夹(例如 mySpam),并停用其通知。
  • 自动回复/Vacation 消息的限制

关于正则表达式支持的说明

Proton 对 Sieve 的实现遵循 Sieve 邮件筛选标准,该标准仅包含最基本的正则表达式支持。一些常用的正则表达式简写语法在 Sieve 中不受支持,例如 \b(单词边界)、\w(单词字符)、\W(非单词字符)和 \d(数字)。因此,在筛选器中使用这些简写字符可能会导致筛选器静默失效。此限制源于 Sieve 标准本身,该标准优先考虑简洁性和跨平台兼容性。有关完整详细信息,请参阅 Sieve 正则表达式草案 RFC。

支持的操作和测试列表

软件包

Sieve 支持以下扩展程序。欲了解更多信息,请参阅官方文档。

Date

Envelope

FileInto

  • 用法:fileinto
  • 描述:为邮件应用标签,或将邮件移动到文件夹。
  • 文档: https://tools.ietf.org/html/rfc5228#section-4.1(新窗口)
  • 实现: 斜杠表示文件夹的完整路径;如果标签名称中包含斜杠,则必须对斜杠进行转义。支持以下选项:
    • “Work“:这是名为“Work”的标签或文件夹
    • “Work/Project1“:这是文件夹“Work”中名为“Project1”的子文件夹
    • “Work/Project1/Docs“:这是文件夹“Work”中子文件夹“Project1”下名为“Docs”的子文件夹
    • “Work/Misc\\/Others“:这是文件夹“Work”中名为“Misc/Others”的子文件夹
  • 另请参阅: 入门指南

Imap4flags

Reject

Vacation

Variables

关系

  • 用法:relational
  • 描述:提供关系匹配运算符。
  • 文档: https://tools.ietf.org/html/rfc5231

正则表达式

  • 用法:regex
  • 描述:提供正则表达式匹配运算符。
  • 文档: https://tools.ietf.org/id/draft-ietf-sieve-regex-01.html

ASCII 数字比较器

外部存储列表

  • 用法:extlists
  • 描述:提供对联系人列表的访问。
  • 文档: https://tools.ietf.org/html/rfc6134(新窗口)
  • 实现: 支持以下列表
    • addrbook:personal:个人联系人列表。支持以下查询:
      • label[.starts-with / .ends-with / .contains]=<group: string>:针对联系人群组执行的操作;
      • keypinning=<value: true / false>:定义联系人的公钥;
      • encryption=<value: true / false>:联系人的默认加密方式;
      • signing=<value: true / false>  联系人的默认签名。
    • :addrbook:myself 属于当前用户的地址;
    • :addrbook:organization 属于当前组织成员的地址;
    • :incomingdefaults:inbox 白名单
    • :incomingdefaults:spam 黑名单
  • 参见: 访问您的联系人列表

Eval

  • 用法:vnd.proton.eval
  • 描述:对以字符串形式给出的简单算术函数求值。
  • 参见: 转换变量

Include

过期

  • 用法:vnd.proton.expire
  • 描述:管理邮件过期时间。
  • 参见: 管理过期时间

测试

Currentdate

  • 用法:
currentdate [":zone" <time-zone: string>] [COMPARATOR] [MATCH-TYPE] <date-part: string> <key-list: string-list>

Date

  • 用法:
date [":zone" <time-zone: string> / ":originalzone"] [MATCH-TYPE] <header-name: string> <date-part: string> <key-list: string-list

HasFlag

  • 用法:
hasflag [MATCH-TYPE] [COMPARATOR] <list-of-flags: string-list>
  • 描述:测试某封邮件是否带有特定标志。
  • 软件包:imap4flags
  • 参阅:入门指南

Envelope

  • 用法:
envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] <envelope-part: string-list> <key-list: string-list>

Address·

  • 用法:
address [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] <header-list: string-list> <key-list: string-list>
  • 描述:测试解析为地址的指定标题是否与指定关键字匹配。
  • 软件包:<default>
  • 参阅:在 Sieve 中比较地址

Header

  • 用法:
header [COMPARATOR] [MATCH-TYPE] <header-names: string-list> <key-list: string-list>

HasExpiration

  • 用法:
hasexpiration
  • 描述:测试邮件是否设置了过期时间。
  • 软件包:vnd.proton.expire

Exists

  • 用法:
exists <header-names: string-list>

过期

  • 用法:
expiration :comparator "i;ascii-numeric" [MATCH-TYPE] <unit: "day" / "minute" / "second"> <key-list: string-list>
  • 描述:将邮件的过期时间与给定关键字进行比较。如果针对的是不会过期的邮件,该测试将失败。
  • 软件包:vnd.proton.expire

Size

  • 用法:
size <":over" / ":under"> <limit: number>

String

  • 用法:
string [MATCH-TYPE] [COMPARATOR] <source: string-list> <key-list: string-list>
  • 描述:评估源字符串是否与任一关键字匹配。
  • 软件包:variables

Anyof

  • 用法:
anyof <tests: test-list>
  • 描述:对所提供的测试执行逻辑或运算。
  • 软件包:<default>
  • 另请参阅: 入门指南

Allof

  • 用法:
allof <tests: test-list>
  • 描述:对所提供的测试执行逻辑与运算。
  • 软件包:<default>
  • 参阅:入门指南

Not

  • 用法:
not <test: test>
  • 描述:反转给定测试的结果。
  • 软件包:<default>
  • 参阅:入门指南

True

  • 用法:
true
  • 描述:始终匹配。
  • 软件包:<default>

False

  • 用法:
false
  • 描述:从不匹配。
  • 软件包:<default>

操作

Require

  • 用法:
require <packages: string-list>
  • 描述:载入指定的扩展程序,以便使用其方法或修改。
  • 软件包:<default>
  • 另请参阅: 入门指南

FileInto

  • 用法:
fileinto <folder: string>
  • 描述:将正在处理的邮件移动到特定文件夹。
  • 软件包:fileinto
  • 参阅:入门指南

Addflag

  • 用法:
addflag <list-of-flags: string-list>
  • 描述:为正在处理的邮件添加指定标志。
  • 软件包:imap4flags
  • 参阅:入门指南

Removeflag

  • 用法:
removeflag <list-of-flags: string-list>
  • 描述:从正在处理的邮件中移除指定标志。
  • 软件包:imap4flags

设置标志

  • 用法:
setflag <list-of-flags: string-list>
  • 描述:移除全部标志,并为正在处理的邮件设置指定的标志。
  • 软件包:imap4flags

停止

  • 用法:
stop
  • 描述:停止处理全部 Sieve 邮件筛选。后续的 Sieve 邮件筛选将不会运行。
  • 软件包:<default>

返回

  • 用法:
return
  • 描述:停止处理当前的 Sieve 邮件筛选。后续的 Sieve 邮件筛选仍会运行。
  • 软件包:include

设置

  • 用法:
set [MODIFIER] <name: string> <value: string>
  • 描述:创建新变量,将给定名称与给定值相关联。
  • 软件包:variables
  • 参见: 创建变量

丢弃

  • 用法:
discard
  • 描述:在此 Sieve 邮件筛选结束时丢弃邮件。丢弃之后,将不会再运行其他 Sieve 邮件筛选。
  • 软件包:<default>

保留

  • 用法:
keep
  • 描述:撤销上一个丢弃命令。只有位于同一 Sieve 邮件筛选中时才会生效。如果尚未执行过丢弃,则此命令不会产生任何效果。
  • 软件包:<default>

Reject

  • 用法:
reject <reason: string>
  • 描述:向发件人的 SMTP 地址发送一封包含原因的电子邮件,并立即丢弃此邮件。保留命令无法取消此操作。
  • 软件包:<default>
  • 参见:入门指南、度假邮件与日期测试

过期

  • 用法:
expire <unit: "day" / "minute" / "second"> <value: string>
  • 描述:在给定的时间后使邮件过期。
  • 软件包:vnd.proton.expire
  • 参见:管理过期

取消过期

  • 用法:
unexpire
  • 描述:移除邮件的过期时间。
  • 软件包:vnd.proton.expire

Vacation

  • 用法:
vacation [":days" number] [":subject" string] [":mime"] [":handle" string] <reason: string>
  • 描述:向发件人的 SMTP 地址发送自动回复,并以指定的原因作为正文。
  • 软件包:vacation(需要付费帐户)
  • 参见:度假邮件与日期测试

有效外部名单类型

  • 用法:
valid_ext_list <ext-list-names: string-list>
  • 描述:测试给定的全部外部名单类型是否均受支持且有效。
  • 软件包:extlists