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; }