first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
import uniq from 'lodash/uniq';
|
||||
import { NodeOperationError, type IDataObject, type IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import type { IEmail } from '@utils/sendAndWait/interfaces';
|
||||
|
||||
import {
|
||||
encodeEmail,
|
||||
googleApiRequest,
|
||||
prepareEmailAttachments,
|
||||
prepareEmailBody,
|
||||
prepareEmailsInput,
|
||||
} from '../GenericFunctions';
|
||||
import type { GmailMessage, GmailMessageMetadata, GmailUserProfile } from '../types';
|
||||
|
||||
export async function replyToEmail(
|
||||
this: IExecuteFunctions,
|
||||
gmailId: string,
|
||||
options: IDataObject,
|
||||
itemIndex: number,
|
||||
nodeVersion: number,
|
||||
) {
|
||||
if (options.replyToSenderOnly && options.replyToRecipientsOnly) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Both "Reply to Sender Only" and "Reply to Recipient Only" cannot be enabled at the same time. Please select only one option.',
|
||||
{ itemIndex },
|
||||
);
|
||||
}
|
||||
|
||||
let qs: IDataObject = {};
|
||||
|
||||
let cc = '';
|
||||
let bcc = '';
|
||||
|
||||
if (options.ccList) {
|
||||
cc = prepareEmailsInput.call(this, options.ccList as string, 'CC', itemIndex);
|
||||
}
|
||||
|
||||
if (options.bccList) {
|
||||
bcc = prepareEmailsInput.call(this, options.bccList as string, 'BCC', itemIndex);
|
||||
}
|
||||
let attachments: IDataObject[] = [];
|
||||
if (options.attachmentsUi) {
|
||||
attachments = await prepareEmailAttachments.call(
|
||||
this,
|
||||
options.attachmentsUi as IDataObject,
|
||||
itemIndex,
|
||||
);
|
||||
if (attachments.length) {
|
||||
qs = {
|
||||
userId: 'me',
|
||||
uploadType: 'media',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `/gmail/v1/users/me/messages/${gmailId}`;
|
||||
|
||||
qs.format = 'metadata';
|
||||
const { payload, threadId } = (await googleApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
endpoint,
|
||||
{},
|
||||
qs,
|
||||
)) as GmailMessageMetadata;
|
||||
|
||||
const subject =
|
||||
payload.headers.filter(
|
||||
(data: { [key: string]: string }) => data.name.toLowerCase() === 'subject',
|
||||
)[0]?.value || '';
|
||||
|
||||
const messageIdGlobal =
|
||||
payload.headers.filter(
|
||||
(data: { [key: string]: string }) => data.name.toLowerCase() === 'message-id',
|
||||
)[0]?.value || '';
|
||||
|
||||
const { emailAddress } = (await googleApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
'/gmail/v1/users/me/profile',
|
||||
)) as GmailUserProfile;
|
||||
|
||||
const to: string[] = [];
|
||||
const replyToSenderOnly =
|
||||
options.replyToSenderOnly === undefined ? false : (options.replyToSenderOnly as boolean);
|
||||
const replyToRecipientsOnly =
|
||||
options.replyToRecipientsOnly === undefined
|
||||
? false
|
||||
: (options.replyToRecipientsOnly as boolean);
|
||||
|
||||
const prepareEmailString = (email: string) => {
|
||||
if (email.includes(emailAddress)) return;
|
||||
if (email.includes('<') && email.includes('>')) {
|
||||
to.push(email);
|
||||
} else {
|
||||
to.push(`<${email}>`);
|
||||
}
|
||||
};
|
||||
|
||||
let replyToHeaderName = 'from';
|
||||
if (nodeVersion >= 2.2 && payload.headers.some((h) => h.name.toLowerCase() === 'reply-to')) {
|
||||
replyToHeaderName = 'reply-to';
|
||||
}
|
||||
|
||||
for (const header of payload.headers) {
|
||||
const headerName = (header.name || '').toLowerCase();
|
||||
if (headerName === replyToHeaderName && !replyToRecipientsOnly) {
|
||||
const replyToEmail = header.value;
|
||||
if (replyToEmail.includes('<') && replyToEmail.includes('>')) {
|
||||
to.push(replyToEmail);
|
||||
} else {
|
||||
to.push(`<${replyToEmail}>`);
|
||||
}
|
||||
}
|
||||
|
||||
if (headerName === 'to' && !replyToSenderOnly) {
|
||||
const toEmails = header.value;
|
||||
toEmails.split(',').forEach(prepareEmailString);
|
||||
}
|
||||
}
|
||||
|
||||
let from = '';
|
||||
if (options.senderName) {
|
||||
from = `${options.senderName as string} <${emailAddress}>`;
|
||||
}
|
||||
|
||||
const toString = uniq(to).join(', ');
|
||||
|
||||
const email: IEmail = {
|
||||
from,
|
||||
to: toString,
|
||||
cc,
|
||||
bcc,
|
||||
subject,
|
||||
attachments,
|
||||
inReplyTo: messageIdGlobal,
|
||||
reference: messageIdGlobal,
|
||||
...prepareEmailBody.call(this, itemIndex),
|
||||
};
|
||||
|
||||
const body = {
|
||||
raw: await encodeEmail(email),
|
||||
threadId,
|
||||
};
|
||||
|
||||
return (await googleApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'/gmail/v1/users/me/messages/send',
|
||||
body,
|
||||
qs,
|
||||
)) as GmailMessage;
|
||||
}
|
||||
Reference in New Issue
Block a user