Email Workers とは
具体的な機能紹介は、上記のブログに書かれていますが、Email Routing のサービスとともに利用し、受信したメールを EmailEvent として Workers ロジックを経由して転送させることができます。
こちらを見ると、2022 年 3 月 3 日時点では Private Beta となっています。
前提条件
まずは Email Routing を構成してある必要があります。
以下のドキュメントに沿って、ドメインを登録し、Email Routing を有効化しておきます。
Email Workers の構成
Email Workers を作成します。
スターターテンプレートがあるので、Allowlist senders で進めます。
Email Workers が作成できたら、コードを編集します。
以下が今回使用するコードです。
-
テンプレートデフォルトの
message.headers.get("from")
はFirst Last <email@address>
となるため、if 条件分岐にはmessage.from
を使う -
allowList
やmessage.forward
は環境に応じて変更する-
message.forward
先は Verified な宛先のみ対象にできる
(迷惑メールの温床とならないためにも当然ですね)- Forward this email message to a verified destination address of the account. If you want, you can add extra headers to the email message. Only
X-*
headers are allowed. - When the promise resolves, the message is confirmed to be forwarded to a verified destination address.
- Forward this email message to a verified destination address of the account. If you want, you can add extra headers to the email message. Only
-
-
EmailEvent に含まれる情報をログに表示する
export default {
async email(message, env, ctx) {
const allowList = ["abc@gmail.com"];
console.log(`message from: ${message.from}`)
console.log(`message to: ${message.to}`)
console.log(`message subject: ${message.headers.get('subject')}`)
console.log(`message date: ${message.headers.get('date')}`)
console.log(`message message-id: ${message.headers.get('message-id')}`)
console.log(`message received: ${message.headers.get('received')}`)
console.log(`message headers: ${JSON.stringify([...message.headers])}`)
const { value: messageRaw } = await message.raw.getReader().read()
const messageRawJSON = new TextDecoder().decode(messageRaw)
console.log(`message raw: ${messageRawJSON}`)
console.log(`message rawSize: ${message.rawSize}`)
if (allowList.indexOf(message.from) == -1) {
message.setReject("Address not allowed");
} else {
await message.forward("test@corp.com");
}
}
}
/*
interface EmailMessage<Body = unknown> {
readonly from: string;
readonly to: string;
readonly headers: Headers;
readonly raw: ReadableStream;
readonly rawSize: number;
setReject(reason: String): void;
forward(rcptTo: string, headers?: Headers): Promise<void>;
}
*/
Route の構成
作成した Email Worker を Email Routing の受信アドレスの Action > Send to a Worker で指定します。
Email Workers を経由させたログ
Email Routing の受信アドレスにメールを送れば、allowlist
の場合、 message.forward
先に送られることが確認できます。
{
"outcome": "ok",
"scriptName": "allowlist-email-worker",
"exceptions": [],
"logs": [
{
"message": [
"message from: abc@gmail.com"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message to: destination@your-email-routing-domain.com"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message subject: hello"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message date: Fri, 3 Mar 2023 01:38:41 +0900"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message message-id: <xxx@mail.gmail.com>"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message received: by mail-xxx.google.com with SMTP id xxx3.0 for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message headers: [
[\"content-type\",\"multipart/alternative; boundary=\\\"xxx\\\"\"],
[\"date\",\"Fri, 3 Mar 2023 01:38:41 +0900\"],[\"dkim-signature\",\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=xx; b=xx\"],
[\"from\",\"First Last <abc@gmail.com>\"],
[\"message-id\",\"<xxx@mail.gmail.com>\"],
[\"mime-version\",\"1.0\"],
[\"received\",\"by mail-xxx.google.com with SMTP id xxx3.0 for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)\"],
[\"subject\",\"hello\"],
[\"to\",\"destination@your-email-routing-domain.com\"],
[\"x-gm-message-state\",\"xxx\"],
[\"x-google-dkim-signature\",\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=xx; b=xx\"],
[\"x-google-smtp-source\",\"xxx\"],
[\"x-received\",\"by xxx with SMTP id xxx.3.1677775132715; Thu, 02 Mar 2023 08:38:52 -0800 (PST)\"]]"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message raw: Received: from mail-xx.google.com (2607:f8b0:4864:20::1133)\r\n
by email.cloudflare.net (unknown) id LJvo9DIQ3xpI\r\n
for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 16:38:53 +0000\r\n
DKIM-Signature: v=1; a=rsa-sha256; d=email.cloudflare.net; s=2022; c=relaxed/relaxed; bh=xx; h=from:subject:date:to; t=1677775133; b=xx;\r\n
Received: by mail-xx.google.com with SMTP id xx3.0\r\n
for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)\r\n
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n
d=gmail.com; s=20210112;\r\n
h=to:subject:message-id:date:from:mime-version:from:to:cc:subject\r\n
:date:message-id:reply-to;\r\n
bh=xx=;\r\n
b=xx==\r\n
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n
d=1e100.net; s=20210112;\r\n
h=to:subject:message-id:date:from:mime-version:x-gm-message-state\r\n
:from:to:cc:subject:date:message-id:reply-to;\r\n
bh=xx\r\n
b=xx\r\n
X-Gm-Message-State: xx\r\n
X-Google-Smtp-Source: xx\r\n
X-Received: by xx with SMTP id\r\n
xx.3.1677775132715; Thu, 02\r\n
Mar 2023 08:38:52 -0800 (PST)\r\n
MIME-Version: 1.0\r\n
From: First Last <abc@gmail.com>\r\n
Date: Fri, 3 Mar 2023 01:38:41 +0900\r\n
Message-ID: <xxx@mail.gmail.com>\r\n
Subject: hello\r\n
To: destination@your-email-routing-domain.com\r\n
Content-Type: multipart/alternative; boundary=\"xx\"\r\n
\r\n
--000000000000cc700b05f5ed7612\r\n
Content-Type: text/plain; charset=\"UTF-8\"\r\n
\r\n
hello world\r\n
\r\n
--000000000000cc700b05f5ed7612\r\n
Content-Type: text/html; charset=\"UTF-8\"\r\n
\r\n
<div dir=\"ltr\">hello world<br></div>\r\n
\r\n
--000000000000cc700b05f5ed7612--\r\n"
],
"level": "log",
"timestamp": 1677775133400
},
{
"message": [
"message rawSize: 3224"
],
"level": "log",
"timestamp": 1677775133400
}
],
"eventTimestamp": 1677775133398,
"event": {
"rawSize": 3224,
"rcptTo": "destination@your-email-routing-domain.com",
"mailFrom": "abc@gmail.com"
},
"id": 4
}
message.setReject("Address not allowed")
の場合
以下のようなエラーメールが送信者に返ってきます。
- Reject this email message by returning a permanent SMTP error back to the connecting client, including the given reason.
not verified なメールアドレスを転送先とした場合
message.forward
先は Verified な宛先のみ対象にでき、以下のエラーとなります。
{
"outcome": "exception",
"scriptName": "allowlist-email-worker",
"exceptions": [
{
"name": "Error",
"message": "destination address not verified",
"timestamp": 1677774635838
}
],
...
}
受信したサンプルメール
感想
- Allowlist, Blocklist, メール受信を Webhook で通知、くらいであれば簡単にできる
- X- メールヘッダの追加等も簡単にできる(
X-My-Email-Worker-Processed
) -
messageRaw
の扱いは、以下のコード例にあるパーサーを使うとより効率的
{
"outcome": "ok",
"scriptName": "email_worker_parser",
"exceptions": [],
"logs": [
{
"message": [
"Mail subject: ",
"Test"
],
"level": "log",
"timestamp": 1684431951396
},
{
"message": [
"HTML version of Email: ",
"<div dir=\"ltr\">Hello, World!<br></div>\n\n"
],
"level": "log",
"timestamp": 1684431951396
},
{
"message": [
"Text version of Email: ",
"Hello, World!\n\n"
],
"level": "log",
"timestamp": 1684431951396
},
{
"message": [
"No attachments"
],
"level": "log",
"timestamp": 1684431951396
}
],
"eventTimestamp": 1684431951384,
"event": {
"rawSize": 5405,
"rcptTo": "receiver@example.com",
"mailFrom": "sender@example.com"
},
"id": 0
}