send-email-action.ts•6.21 kB
import { ApFile, createAction, Property } from '@activepieces/pieces-framework';
import mime from 'mime-types';
import MailComposer from 'nodemailer/lib/mail-composer';
import Mail, { Attachment } from 'nodemailer/lib/mailer';
import { gmailAuth } from '../../';
import { google } from 'googleapis';
import { OAuth2Client } from 'googleapis-common';
export const gmailSendEmailAction = createAction({
auth: gmailAuth,
name: 'send_email',
description: 'Send an email through a Gmail account',
displayName: 'Send Email',
props: {
receiver: Property.Array({
displayName: 'Receiver Email (To)',
description: undefined,
required: true,
}),
cc: Property.Array({
displayName: 'CC Email',
description: undefined,
required: false,
}),
bcc: Property.Array({
displayName: 'BCC Email',
description: undefined,
required: false,
}),
subject: Property.ShortText({
displayName: 'Subject',
description: undefined,
required: true,
}),
body_type: Property.StaticDropdown({
displayName: 'Body Type',
required: true,
defaultValue: 'plain_text',
options: {
disabled: false,
options: [
{
label: 'plain text',
value: 'plain_text',
},
{
label: 'html',
value: 'html',
},
],
},
}),
body: Property.ShortText({
displayName: 'Body',
description: 'Body for the email you want to send',
required: true,
}),
reply_to: Property.Array({
displayName: 'Reply-To Email',
description: 'Email address to set as the "Reply-To" header',
required: false,
}),
sender_name: Property.ShortText({
displayName: 'Sender Name',
required: false,
}),
from: Property.ShortText({
displayName: 'Sender Email',
description:
"The address must be listed in your GMail account's settings",
required: false,
}),
attachments: Property.Array({
displayName: 'Attachments',
required: false,
properties: {
file: Property.File({
displayName: 'File',
description: 'File to attach to the email you want to send.',
required: true,
}),
name: Property.ShortText({
displayName: 'Attachment Name',
description: 'In case you want to change the name of the attachment.',
required: false,
}),
},
}),
in_reply_to: Property.ShortText({
displayName: 'In reply to',
description: 'Reply to this Message-ID',
required: false,
}),
draft: Property.Checkbox({
displayName: 'Create draft',
description: 'Create draft without sending the actual email',
required: true,
defaultValue: false,
}),
},
async run(context) {
const authClient = new OAuth2Client();
authClient.setCredentials(context.auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
const subjectBase64 = Buffer.from(context.propsValue['subject']).toString(
'base64'
);
const attachments = context.propsValue.attachments as {
file: ApFile;
name: string | undefined;
}[];
const replyTo = context.propsValue['reply_to']?.filter(
(email) => email !== ''
);
const receiver = context.propsValue['receiver']?.filter(
(email) => email !== ''
);
const cc = context.propsValue['cc']?.filter((email) => email !== '');
const bcc = context.propsValue['bcc']?.filter((email) => email !== '');
const mailOptions: Mail.Options = {
to: receiver.join(', '), // Join all email addresses with a comma
cc: cc ? cc.join(', ') : undefined,
bcc: bcc ? bcc.join(', ') : undefined,
subject: `=?UTF-8?B?${subjectBase64}?=`,
replyTo: replyTo ? replyTo.join(', ') : '',
text:
context.propsValue.body_type === 'plain_text'
? context.propsValue['body']
: undefined,
html:
context.propsValue.body_type === 'html'
? context.propsValue['body']
: undefined,
attachments: [],
};
let threadId = undefined;
if (context.propsValue.in_reply_to) {
mailOptions.headers = [
{
key: 'References',
value: context.propsValue.in_reply_to,
},
{
key: 'In-Reply-To',
value: context.propsValue.in_reply_to,
},
];
const messages = await gmail.users.messages.list({
userId: 'me',
q: `Rfc822msgid:${context.propsValue.in_reply_to}`,
});
threadId = messages.data.messages?.[0].threadId;
}
const senderEmail =
context.propsValue.from ||
(await google.oauth2({ version: 'v2', auth: authClient }).userinfo.get())
.data.email;
if (senderEmail) {
mailOptions.from = context.propsValue.sender_name
? `${context.propsValue['sender_name']} <${senderEmail}>`
: senderEmail;
}
if (attachments && attachments.length > 0) {
const attachmentOption: Attachment[] = attachments.map(
({ file, name }) => {
const lookupResult = mime.lookup(
file.extension ? file.extension : ''
);
return {
filename: name ?? file.filename,
content: file?.base64,
contentType: lookupResult ? lookupResult : undefined,
encoding: 'base64',
};
}
);
mailOptions.attachments = attachmentOption;
}
const mail: any = new MailComposer(mailOptions).compile();
mail.keepBcc = true;
const mailBody = await mail.build();
const encodedPayload = Buffer.from(mailBody)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_');
if (context.propsValue.draft) {
return await gmail.users.drafts.create({
userId: 'me',
requestBody: { message: { threadId, raw: encodedPayload } },
});
} else {
return await gmail.users.messages.send({
userId: 'me',
requestBody: {
threadId,
raw: encodedPayload,
},
});
}
},
});