Getting the body from a email-worker.

Hello there! I am in the process of transitioning to Cloudflare. Upon discovering that Cloudflare Workers support the ability to send emails to channels like Slack, I'm eager to implement this feature. I aim to send the body of the email to an external API. Is there a method to achieve this, and is there any pertinent information within the 'message' variable that I might have overlooked? Thanks in advance!
14 Replies
Jaren
Jaren•11mo ago
You should be able to use the standard fetch api to send it to an api
Sven
SvenOP•11mo ago
I meant the body of the recieved email :)
Jaren
Jaren•11mo ago
ah, misread. you would need to read the stream in the raw property of the message
Sven
SvenOP•11mo ago
Yea, only problem is I dont know how stream's work... I have read the docs but couldn't figure it out ;(
export default {
async email(message, env, ctx) {
switch (message.to) {
case "EMAIL_CASE":
await fetch("API_ENDPOINT", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"icon": "📨",
"title": message.headers.get('subject'),
"body": readable.getReader({ mode: 'byob' }),
"source": message.from,
}),
});
break;
default:
message.reject("Unknown address");
}
}
}
export default {
async email(message, env, ctx) {
switch (message.to) {
case "EMAIL_CASE":
await fetch("API_ENDPOINT", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"icon": "📨",
"title": message.headers.get('subject'),
"body": readable.getReader({ mode: 'byob' }),
"source": message.from,
}),
});
break;
default:
message.reject("Unknown address");
}
}
}
Chaika
Chaika•11mo ago
can't stringify a stream would have to base64 it or escape it what I've done in the past is just passed along the metadata as headers
Jaren
Jaren•11mo ago
What's stopping you from using it in a TextDecoder?
Chaika
Chaika•11mo ago
It's an email, if his goal is to send it to slack or something he's really going to want to decode it first or it'd be a lot of useless junk
Jaren
Jaren•11mo ago
couldn't you do this?
const textDecoder = new TextDecoder();
let result = "";
for await (let chunk of message.raw) {
result += textDecoder.decode(chunk, { stream: true });
}
const textDecoder = new TextDecoder();
let result = "";
for await (let chunk of message.raw) {
result += textDecoder.decode(chunk, { stream: true });
}
Chaika
Chaika•11mo ago
sorry I only read the original post after, but yea if the intent is to send it to a webhook, use an email parser first to get the contents: here's an example to Discord: https://github.com/Tyler-OBrien/cloudflare-worker-emails-to-discord/blob/master/index.js a raw email looks something like this:
MIME-Version: 1.0
References: <CABxEEohuqZBoVpsyY4pOFMYixhU2bzfxgs9tRLbUoV2NJMqCJw@mail.gmail.com>
<CAL5Lp9Xyo0mEQ6-c1yAQ+SuKXrT4Xu5y-7BnvnGS4RMjZOBJ=g@mail.gmail.com>
In-Reply-To: <CAL5Lp9Xyo0mEQ6-c1yAQ+SuKXrT4Xu5y-7BnvnGS4RMjZOBJ=g@mail.gmail.com>
From: Chris <[email protected]>
Date: Wed, 9 Jan 2019 08:36:15 -0800
Message-ID: <CABxEEoizOPyCLkq4+FBGNaw7KC2TJDfTZF5dp8xD9aFjDQoL+Q@mail.gmail.com>
Subject: Re: food for thought
Content-Type: multipart/related; boundary="000000000000382db9057f0910d6"

--000000000000382db9057f0910d6
Content-Type: multipart/alternative; boundary="000000000000382db0057f0910d5"

--000000000000382db0057f0910d5
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Ok. Just a thought. Got it.

--000000000000382db0057f0910d5
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div><div dir=3D"auto">Ok.=C2=A0 Just a thought.=C2=A0 Got it. =C2=A0</div>=
</div><div><br><div class=3D"gmail_quote"><div dir=3D"ltr">On Wed, Jan 9, 2=
MIME-Version: 1.0
References: <CABxEEohuqZBoVpsyY4pOFMYixhU2bzfxgs9tRLbUoV2NJMqCJw@mail.gmail.com>
<CAL5Lp9Xyo0mEQ6-c1yAQ+SuKXrT4Xu5y-7BnvnGS4RMjZOBJ=g@mail.gmail.com>
In-Reply-To: <CAL5Lp9Xyo0mEQ6-c1yAQ+SuKXrT4Xu5y-7BnvnGS4RMjZOBJ=g@mail.gmail.com>
From: Chris <[email protected]>
Date: Wed, 9 Jan 2019 08:36:15 -0800
Message-ID: <CABxEEoizOPyCLkq4+FBGNaw7KC2TJDfTZF5dp8xD9aFjDQoL+Q@mail.gmail.com>
Subject: Re: food for thought
Content-Type: multipart/related; boundary="000000000000382db9057f0910d6"

--000000000000382db9057f0910d6
Content-Type: multipart/alternative; boundary="000000000000382db0057f0910d5"

--000000000000382db0057f0910d5
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Ok. Just a thought. Got it.

--000000000000382db0057f0910d5
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div><div dir=3D"auto">Ok.=C2=A0 Just a thought.=C2=A0 Got it. =C2=A0</div>=
</div><div><br><div class=3D"gmail_quote"><div dir=3D"ltr">On Wed, Jan 9, 2=
and that's a simple case. You'd want to run it through an email parser first to get the text/plain body, or the cleaned up text/html body if there is no text/plain
Sven
SvenOP•11mo ago
Thanks! (both of you)
Chaika
Chaika•11mo ago
no problem! Yea you should be able to modify that repo to your needs, or you could just steal the parsing logic. It uses postal-mime and html-to-text, and the core of getting a useful contents to display is just:
let rawEmail = new Response(message.raw);
let arrayBuffer = await rawEmail.arrayBuffer();
const parser = new PostalMime.default();
const email = await parser.parse(arrayBuffer);
let emailText = email.text;
if (!emailText) {
// If there is no text, try to get the text from the html
emailText = convert(email.html);
}
let rawEmail = new Response(message.raw);
let arrayBuffer = await rawEmail.arrayBuffer();
const parser = new PostalMime.default();
const email = await parser.parse(arrayBuffer);
let emailText = email.text;
if (!emailText) {
// If there is no text, try to get the text from the html
emailText = convert(email.html);
}
Sven
SvenOP•11mo ago
That code will help me indeed with getting it to work faster :) Ty!
Andris Reinman
Andris Reinman•11mo ago
The postal-mime module API has changed, and it now includes built-in support for Cloudflare Email Workers. You can read about using it to parse emails from here: https://docs.emailengine.app/how-to-parse-emails-with-cloudflare-email-workers/
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
const parser = new PostalMime();
const email = await parser.parse(message.raw);
// ... do something with the parsed email
}
};
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
const parser = new PostalMime();
const email = await parser.parse(message.raw);
// ... do something with the parsed email
}
};
Chaika
Chaika•11mo ago
ah that's really cool, you added that in today! Helps remove some of that boilerplate
Want results from more Discord servers?
Add your server