2023-01-27 03:22:44 -08:00
import type { OptionsWithUri } from 'request' ;
2020-08-18 03:40:19 -07:00
2022-08-17 08:50:24 -07:00
import { simpleParser } from 'mailparser' ;
2020-08-18 03:40:19 -07:00
2023-01-27 03:22:44 -08:00
import type { IExecuteFunctions , IExecuteSingleFunctions , ILoadOptionsFunctions } from 'n8n-core' ;
2020-08-18 03:40:19 -07:00
2023-01-27 03:22:44 -08:00
import type {
2020-08-18 03:40:19 -07:00
IBinaryKeyData ,
2022-09-08 05:44:34 -07:00
ICredentialDataDecryptedObject ,
2020-08-18 03:40:19 -07:00
IDataObject ,
INodeExecutionData ,
2022-09-08 05:44:34 -07:00
IPollFunctions ,
2023-02-27 19:39:43 -08:00
JsonObject ,
2020-08-18 03:40:19 -07:00
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeApiError , NodeOperationError } from 'n8n-workflow' ;
2020-08-18 03:40:19 -07:00
2022-04-08 14:32:08 -07:00
import moment from 'moment-timezone' ;
2021-01-10 11:49:47 -08:00
2022-04-08 14:32:08 -07:00
import jwt from 'jsonwebtoken' ;
2021-01-10 11:49:47 -08:00
2022-09-08 05:44:34 -07:00
import { DateTime } from 'luxon' ;
2023-02-23 07:16:05 -08:00
import isEmpty from 'lodash.isempty' ;
2022-09-08 05:44:34 -07:00
export interface IEmail {
from ? : string ;
to? : string ;
cc? : string ;
bcc? : string ;
inReplyTo? : string ;
reference? : string ;
subject : string ;
body : string ;
htmlBody? : string ;
attachments? : IDataObject [ ] ;
}
export interface IAttachments {
type : string ;
name : string ;
content : string ;
2021-12-24 07:12:18 -08:00
}
2023-01-13 09:11:56 -08:00
import MailComposer from 'nodemailer/lib/mail-composer' ;
async function getAccessToken (
this : IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions ,
credentials : ICredentialDataDecryptedObject ,
) : Promise < IDataObject > {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/gmail.labels' ,
'https://www.googleapis.com/auth/gmail.addons.current.action.compose' ,
'https://www.googleapis.com/auth/gmail.addons.current.message.action' ,
'https://mail.google.com/' ,
'https://www.googleapis.com/auth/gmail.modify' ,
'https://www.googleapis.com/auth/gmail.compose' ,
] ;
const now = moment ( ) . unix ( ) ;
credentials . email = ( credentials . email as string ) . trim ( ) ;
const privateKey = ( credentials . privateKey as string ) . replace ( /\\n/g , '\n' ) . trim ( ) ;
const signature = jwt . sign (
{
iss : credentials.email ,
sub : credentials.delegatedEmail || credentials . email ,
scope : scopes.join ( ' ' ) ,
aud : 'https://oauth2.googleapis.com/token' ,
iat : now ,
exp : now + 3600 ,
} ,
privateKey ,
{
algorithm : 'RS256' ,
header : {
kid : privateKey ,
typ : 'JWT' ,
alg : 'RS256' ,
} ,
} ,
) ;
const options : OptionsWithUri = {
headers : {
'Content-Type' : 'application/x-www-form-urlencoded' ,
} ,
method : 'POST' ,
form : {
grant_type : 'urn:ietf:params:oauth:grant-type:jwt-bearer' ,
assertion : signature ,
} ,
uri : 'https://oauth2.googleapis.com/token' ,
json : true ,
} ;
return this . helpers . request ( options ) ;
}
2020-11-02 13:47:47 -08:00
2022-08-17 08:50:24 -07:00
export async function googleApiRequest (
2022-09-08 05:44:34 -07:00
this : IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions ,
2022-08-17 08:50:24 -07:00
method : string ,
endpoint : string ,
2022-09-08 05:44:34 -07:00
body : IDataObject = { } ,
2022-08-17 08:50:24 -07:00
qs : IDataObject = { } ,
uri? : string ,
option : IDataObject = { } ,
2022-09-08 05:44:34 -07:00
) {
2020-08-18 03:40:19 -07:00
let options : OptionsWithUri = {
headers : {
2022-08-17 08:50:24 -07:00
Accept : 'application/json' ,
2020-08-18 03:40:19 -07:00
'Content-Type' : 'application/json' ,
} ,
method ,
body ,
qs ,
2023-01-19 04:37:19 -08:00
uri : uri || ` https://www.googleapis.com ${ endpoint } ` ,
2022-01-27 12:25:32 -08:00
qsStringifyOptions : {
2021-03-02 03:47:53 -08:00
arrayFormat : 'repeat' ,
} ,
2020-08-18 03:40:19 -07:00
json : true ,
} ;
options = Object . assign ( { } , options , option ) ;
try {
if ( Object . keys ( body ) . length === 0 ) {
delete options . body ;
}
2022-09-08 05:44:34 -07:00
let credentialType = 'gmailOAuth2' ;
const authentication = this . getNodeParameter ( 'authentication' , 0 ) as string ;
if ( authentication === 'serviceAccount' ) {
2021-08-20 09:57:30 -07:00
const credentials = await this . getCredentials ( 'googleApi' ) ;
2022-09-08 05:44:34 -07:00
credentialType = 'googleApi' ;
2021-01-10 11:49:47 -08:00
2022-09-08 05:44:34 -07:00
const { access_token } = await getAccessToken . call ( this , credentials ) ;
2021-01-10 11:49:47 -08:00
2022-12-02 12:54:28 -08:00
( options . headers as IDataObject ) . Authorization = ` Bearer ${ access_token } ` ;
2021-01-10 11:49:47 -08:00
}
2022-09-08 05:44:34 -07:00
const response = await this . helpers . requestWithAuthentication . call (
this ,
credentialType ,
options ,
) ;
return response ;
2020-08-18 03:40:19 -07:00
} catch ( error ) {
2021-06-22 10:59:27 -07:00
if ( error . code === 'ERR_OSSL_PEM_NO_START_LINE' ) {
error . statusCode = '401' ;
}
2022-09-08 05:44:34 -07:00
if ( error . httpCode === '400' ) {
if ( error . cause && ( ( error . cause . message as string ) || '' ) . includes ( 'Invalid id value' ) ) {
const resource = this . getNodeParameter ( 'resource' , 0 ) as string ;
2022-12-02 12:54:28 -08:00
const errorOptions = {
2022-09-08 05:44:34 -07:00
message : ` Invalid ${ resource } ID ` ,
description : ` ${
resource . charAt ( 0 ) . toUpperCase ( ) + resource . slice ( 1 )
} IDs should look something like this : 182 b676d244938bd ` ,
} ;
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , errorOptions ) ;
2022-09-08 05:44:34 -07:00
}
}
if ( error . httpCode === '404' ) {
let resource = this . getNodeParameter ( 'resource' , 0 ) as string ;
if ( resource === 'label' ) {
resource = 'label ID' ;
}
2022-12-02 12:54:28 -08:00
const errorOptions = {
2022-09-08 05:44:34 -07:00
message : ` ${ resource . charAt ( 0 ) . toUpperCase ( ) + resource . slice ( 1 ) } not found ` ,
description : '' ,
} ;
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , errorOptions ) ;
2022-09-08 05:44:34 -07:00
}
if ( error . httpCode === '409' ) {
const resource = this . getNodeParameter ( 'resource' , 0 ) as string ;
if ( resource === 'label' ) {
2022-12-02 12:54:28 -08:00
const errorOptions = {
2022-12-29 03:20:43 -08:00
message : 'Label name exists already' ,
2022-09-08 05:44:34 -07:00
description : '' ,
} ;
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , errorOptions ) ;
2022-09-08 05:44:34 -07:00
}
}
if ( error . code === 'EAUTH' ) {
2022-12-02 12:54:28 -08:00
const errorOptions = {
2022-09-08 05:44:34 -07:00
message : error?.body?.error_description || 'Authorization error' ,
description : ( error as Error ) . message ,
} ;
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , errorOptions ) ;
2022-09-08 05:44:34 -07:00
}
if (
( ( error . message as string ) || '' ) . includes ( 'Bad request - please check your parameters' ) &&
error . description
) {
2022-12-02 12:54:28 -08:00
const errorOptions = {
2022-09-08 05:44:34 -07:00
message : error.description ,
2022-12-29 03:20:43 -08:00
description : '' ,
2022-09-08 05:44:34 -07:00
} ;
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , errorOptions ) ;
2022-09-08 05:44:34 -07:00
}
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject , {
2022-09-08 05:44:34 -07:00
message : error.message ,
description : error.description ,
} ) ;
2020-08-18 03:40:19 -07:00
}
}
2022-08-17 08:50:24 -07:00
export async function parseRawEmail (
2022-09-08 05:44:34 -07:00
this : IExecuteFunctions | IPollFunctions ,
2022-12-02 06:25:21 -08:00
2022-08-17 08:50:24 -07:00
messageData : any ,
dataPropertyNameDownload : string ,
) : Promise < INodeExecutionData > {
2023-02-27 19:39:43 -08:00
const messageEncoded = Buffer . from ( messageData . raw as string , 'base64' ) . toString ( 'utf8' ) ;
2022-12-05 06:12:26 -08:00
const responseData = await simpleParser ( messageEncoded ) ;
2020-08-18 03:40:19 -07:00
const headers : IDataObject = { } ;
for ( const header of responseData . headerLines ) {
headers [ header . key ] = header . line ;
}
const binaryData : IBinaryKeyData = { } ;
if ( responseData . attachments ) {
2022-09-08 05:44:34 -07:00
const downloadAttachments = this . getNodeParameter (
'options.downloadAttachments' ,
0 ,
false ,
) as boolean ;
if ( downloadAttachments ) {
for ( let i = 0 ; i < responseData . attachments . length ; i ++ ) {
const attachment = responseData . attachments [ i ] ;
binaryData [ ` ${ dataPropertyNameDownload } ${ i } ` ] = await this . helpers . prepareBinaryData (
attachment . content ,
attachment . filename ,
attachment . contentType ,
) ;
}
2020-08-18 03:40:19 -07:00
}
}
2020-10-15 01:41:47 -07:00
const mailBaseData : IDataObject = { } ;
2022-08-17 08:50:24 -07:00
const resolvedModeAddProperties = [ 'id' , 'threadId' , 'labelIds' , 'sizeEstimate' ] ;
2020-10-15 01:41:47 -07:00
for ( const key of resolvedModeAddProperties ) {
mailBaseData [ key ] = messageData [ key ] ;
}
2022-12-05 06:12:26 -08:00
const json = Object . assign ( { } , mailBaseData , responseData , {
headers ,
headerLines : undefined ,
attachments : undefined ,
} ) as IDataObject ;
2020-10-15 01:41:47 -07:00
2020-08-18 03:40:19 -07:00
return {
2022-12-05 06:12:26 -08:00
json ,
2020-08-18 03:40:19 -07:00
binary : Object.keys ( binaryData ) . length ? binaryData : undefined ,
} as INodeExecutionData ;
}
//------------------------------------------------------------------------------------------------------------------------------------------
// This function converts an email object into a MIME encoded email and then converts that string into base64 encoding
// for more info on MIME, https://docs.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2010/aa494197(v%3Dexchg.140)
//------------------------------------------------------------------------------------------------------------------------------------------
2020-11-02 13:47:47 -08:00
export async function encodeEmail ( email : IEmail ) {
2022-09-08 05:44:34 -07:00
// https://nodemailer.com/extras/mailcomposer/#e-mail-message-fields
2020-11-02 13:47:47 -08:00
const mailOptions = {
2021-07-30 07:26:57 -07:00
from : email . from ,
2020-11-02 13:47:47 -08:00
to : email.to ,
2021-01-07 02:58:28 -08:00
cc : email.cc ,
2020-11-02 13:47:47 -08:00
bcc : email.bcc ,
2022-09-08 05:44:34 -07:00
inReplyTo : email.inReplyTo ,
2020-11-02 13:47:47 -08:00
references : email.reference ,
subject : email.subject ,
text : email.body ,
2022-01-27 12:25:32 -08:00
keepBcc : true ,
2020-11-02 13:47:47 -08:00
} as IDataObject ;
2022-09-08 05:44:34 -07:00
2021-01-07 02:58:28 -08:00
if ( email . htmlBody ) {
mailOptions . html = email . htmlBody ;
}
2020-11-02 13:47:47 -08:00
2022-08-17 08:50:24 -07:00
if (
email . attachments !== undefined &&
Array . isArray ( email . attachments ) &&
email . attachments . length > 0
) {
2020-11-02 13:47:47 -08:00
const attachments = email . attachments . map ( ( attachment ) = > ( {
filename : attachment.name ,
content : attachment.content ,
contentType : attachment.type ,
2021-01-07 02:58:28 -08:00
encoding : 'base64' ,
2020-11-02 13:47:47 -08:00
} ) ) ;
mailOptions . attachments = attachments ;
}
2020-08-18 03:40:19 -07:00
2023-01-13 09:11:56 -08:00
const mail = new MailComposer ( mailOptions ) . compile ( ) ;
2020-11-02 23:17:54 -08:00
2022-01-27 12:25:32 -08:00
// by default the bcc headers are deleted when the mail is built.
2023-01-13 09:11:56 -08:00
// So add keepBcc flag to override such behaviour. Only works when
2022-01-27 12:25:32 -08:00
// the flag is set after the compilation.
2023-01-13 09:11:56 -08:00
// @ts-expect-error - https://nodemailer.com/extras/mailcomposer/#bcc
2022-01-27 12:25:32 -08:00
mail . keepBcc = true ;
2020-08-18 03:40:19 -07:00
2022-12-02 12:54:28 -08:00
const mailBody = await mail . build ( ) ;
2020-08-18 03:40:19 -07:00
2020-11-02 23:17:54 -08:00
return mailBody . toString ( 'base64' ) . replace ( /\+/g , '-' ) . replace ( /\//g , '_' ) ;
2020-08-18 03:40:19 -07:00
}
2022-08-17 08:50:24 -07:00
export async function googleApiRequestAllItems (
2022-09-08 05:44:34 -07:00
this : IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions ,
2022-08-17 08:50:24 -07:00
propertyName : string ,
method : string ,
endpoint : string ,
2022-12-02 06:25:21 -08:00
2022-08-17 08:50:24 -07:00
body : any = { } ,
query : IDataObject = { } ,
) : Promise < any > {
2020-08-18 03:40:19 -07:00
const returnData : IDataObject [ ] = [ ] ;
let responseData ;
query . maxResults = 100 ;
do {
2023-02-27 19:39:43 -08:00
responseData = await googleApiRequest . call ( this , method , endpoint , body as IDataObject , query ) ;
2022-12-02 12:54:28 -08:00
query . pageToken = responseData . nextPageToken ;
2023-02-27 19:39:43 -08:00
returnData . push . apply ( returnData , responseData [ propertyName ] as IDataObject [ ] ) ;
2022-12-02 12:54:28 -08:00
} while ( responseData . nextPageToken !== undefined && responseData . nextPageToken !== '' ) ;
2020-08-18 03:40:19 -07:00
return returnData ;
}
export function extractEmail ( s : string ) {
2022-09-08 05:44:34 -07:00
if ( s . includes ( '<' ) ) {
const data = s . split ( '<' ) [ 1 ] ;
return data . substring ( 0 , data . length - 1 ) ;
}
return s ;
2020-08-18 03:40:19 -07:00
}
2021-01-10 11:49:47 -08:00
2022-09-08 05:44:34 -07:00
export function prepareQuery (
this : IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions ,
fields : IDataObject ,
) {
const qs : IDataObject = { . . . fields } ;
if ( qs . labelIds ) {
if ( qs . labelIds === '' ) {
delete qs . labelIds ;
} else {
qs . labelIds = qs . labelIds as string [ ] ;
}
}
if ( qs . sender ) {
if ( qs . q ) {
qs . q += ` from: ${ qs . sender } ` ;
} else {
qs . q = ` from: ${ qs . sender } ` ;
}
delete qs . sender ;
}
if ( qs . readStatus && qs . readStatus !== 'both' ) {
if ( qs . q ) {
qs . q += ` is: ${ qs . readStatus } ` ;
} else {
qs . q = ` is: ${ qs . readStatus } ` ;
}
delete qs . readStatus ;
}
if ( qs . receivedAfter ) {
let timestamp = DateTime . fromISO ( qs . receivedAfter as string ) . toSeconds ( ) ;
const timestampLengthInMilliseconds1990 = 12 ;
2022-12-05 06:12:26 -08:00
if (
! timestamp &&
typeof qs . receivedAfter === 'number' &&
qs . receivedAfter . toString ( ) . length < timestampLengthInMilliseconds1990
) {
timestamp = qs . receivedAfter ;
}
2022-09-08 05:44:34 -07:00
if ( ! timestamp && ( qs . receivedAfter as string ) . length < timestampLengthInMilliseconds1990 ) {
timestamp = parseInt ( qs . receivedAfter as string , 10 ) ;
}
if ( ! timestamp ) {
timestamp = Math . floor (
DateTime . fromMillis ( parseInt ( qs . receivedAfter as string , 10 ) ) . toSeconds ( ) ,
) ;
}
if ( ! timestamp ) {
const description = ` ' ${ qs . receivedAfter } ' isn't a valid date and time. If you're using an expression, be sure to set an ISO date string or a timestamp. ` ;
2022-12-29 03:20:43 -08:00
throw new NodeOperationError ( this . getNode ( ) , "Invalid date/time in 'Received After' field" , {
2022-09-08 05:44:34 -07:00
description ,
} ) ;
}
if ( qs . q ) {
qs . q += ` after: ${ timestamp } ` ;
} else {
qs . q = ` after: ${ timestamp } ` ;
}
delete qs . receivedAfter ;
}
if ( qs . receivedBefore ) {
let timestamp = DateTime . fromISO ( qs . receivedBefore as string ) . toSeconds ( ) ;
const timestampLengthInMilliseconds1990 = 12 ;
if ( ! timestamp && ( qs . receivedBefore as string ) . length < timestampLengthInMilliseconds1990 ) {
timestamp = parseInt ( qs . receivedBefore as string , 10 ) ;
}
if ( ! timestamp ) {
timestamp = Math . floor (
DateTime . fromMillis ( parseInt ( qs . receivedBefore as string , 10 ) ) . toSeconds ( ) ,
) ;
}
if ( ! timestamp ) {
const description = ` ' ${ qs . receivedBefore } ' isn't a valid date and time. If you're using an expression, be sure to set an ISO date string or a timestamp. ` ;
2022-12-29 03:20:43 -08:00
throw new NodeOperationError ( this . getNode ( ) , "Invalid date/time in 'Received Before' field" , {
2022-09-08 05:44:34 -07:00
description ,
} ) ;
}
if ( qs . q ) {
qs . q += ` before: ${ timestamp } ` ;
} else {
qs . q = ` before: ${ timestamp } ` ;
}
delete qs . receivedBefore ;
}
return qs ;
}
export function prepareEmailsInput (
this : IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions ,
input : string ,
fieldName : string ,
itemIndex : number ,
) {
let emails = '' ;
input . split ( ',' ) . forEach ( ( entry ) = > {
const email = entry . trim ( ) ;
if ( email . indexOf ( '@' ) === - 1 ) {
const description = ` The email address ' ${ email } ' in the ' ${ fieldName } ' field isn't valid ` ;
2022-12-29 03:20:43 -08:00
throw new NodeOperationError ( this . getNode ( ) , 'Invalid email address' , {
2022-09-08 05:44:34 -07:00
description ,
itemIndex ,
} ) ;
}
if ( email . includes ( '<' ) && email . includes ( '>' ) ) {
emails += ` ${ email } , ` ;
} else {
emails += ` < ${ email } >, ` ;
}
} ) ;
return emails ;
}
export function prepareEmailBody (
this : IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions ,
itemIndex : number ,
) {
const emailType = this . getNodeParameter ( 'emailType' , itemIndex ) as string ;
let body = '' ;
let htmlBody = '' ;
if ( emailType === 'html' ) {
htmlBody = ( this . getNodeParameter ( 'message' , itemIndex , '' ) as string ) . trim ( ) ;
} else {
body = ( this . getNodeParameter ( 'message' , itemIndex , '' ) as string ) . trim ( ) ;
}
return { body , htmlBody } ;
}
export async function prepareEmailAttachments (
this : IExecuteFunctions ,
options : IDataObject ,
items : INodeExecutionData [ ] ,
itemIndex : number ,
) {
const attachmentsList : IDataObject [ ] = [ ] ;
2022-12-02 12:54:28 -08:00
const attachments = options . attachmentsBinary as IDataObject [ ] ;
2022-09-08 05:44:34 -07:00
if ( attachments && ! isEmpty ( attachments ) ) {
for ( const { property } of attachments ) {
for ( const name of ( property as string ) . split ( ',' ) ) {
if ( ! items [ itemIndex ] . binary || items [ itemIndex ] . binary ! [ name ] === undefined ) {
const description = ` This node has no input field called ' ${ name } ' ` ;
2022-12-29 03:20:43 -08:00
throw new NodeOperationError ( this . getNode ( ) , 'Attachment not found' , {
2022-09-08 05:44:34 -07:00
description ,
itemIndex ,
} ) ;
}
const binaryData = items [ itemIndex ] . binary ! [ name ] ;
const binaryDataBuffer = await this . helpers . getBinaryDataBuffer ( itemIndex , name ) ;
if ( ! items [ itemIndex ] . binary ! [ name ] || ! Buffer . isBuffer ( binaryDataBuffer ) ) {
const description = ` The input field ' ${ name } ' doesn't contain an attachment. Please make sure you specify a field containing binary data ` ;
2022-12-29 03:20:43 -08:00
throw new NodeOperationError ( this . getNode ( ) , 'Attachment not found' , {
2022-09-08 05:44:34 -07:00
description ,
itemIndex ,
} ) ;
}
attachmentsList . push ( {
2023-01-19 04:37:19 -08:00
name : binaryData.fileName || 'unknown' ,
2022-09-08 05:44:34 -07:00
content : binaryDataBuffer ,
type : binaryData . mimeType ,
} ) ;
}
}
}
return attachmentsList ;
}
export function unescapeSnippets ( items : INodeExecutionData [ ] ) {
const result = items . map ( ( item ) = > {
2022-12-02 12:54:28 -08:00
const snippet = item . json . snippet as string ;
2022-09-08 05:44:34 -07:00
if ( snippet ) {
2022-12-02 12:54:28 -08:00
item . json . snippet = snippet . replace ( /&|<|>|'|"/g , ( match ) = > {
switch ( match ) {
case '&' :
return '&' ;
case '<' :
return '<' ;
case '>' :
return '>' ;
case ''' :
return "'" ;
case '"' :
return '"' ;
default :
return match ;
}
} ) ;
2022-09-08 05:44:34 -07:00
}
return item ;
} ) ;
return result ;
}
export async function replayToEmail (
this : IExecuteFunctions ,
items : INodeExecutionData [ ] ,
gmailId : string ,
options : IDataObject ,
itemIndex : number ,
) {
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 ,
items ,
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 ) ;
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' ) ;
let to = '' ;
const replyToSenderOnly =
options . replyToSenderOnly === undefined ? false : ( options . replyToSenderOnly as boolean ) ;
2022-12-02 12:54:28 -08:00
const prepareEmailString = ( email : string ) = > {
2023-02-27 19:39:43 -08:00
if ( email . includes ( emailAddress as string ) ) return ;
2022-12-02 12:54:28 -08:00
if ( email . includes ( '<' ) && email . includes ( '>' ) ) {
to += ` ${ email } , ` ;
} else {
to += ` < ${ email } >, ` ;
}
} ;
2022-09-08 05:44:34 -07:00
for ( const header of payload . headers as IDataObject [ ] ) {
if ( ( ( header . name as string ) || '' ) . toLowerCase ( ) === 'from' ) {
const from = header . value as string ;
if ( from . includes ( '<' ) && from . includes ( '>' ) ) {
to += ` ${ from } , ` ;
} else {
to += ` < ${ from } >, ` ;
}
}
if ( ( ( header . name as string ) || '' ) . toLowerCase ( ) === 'to' && ! replyToSenderOnly ) {
const toEmails = header . value as string ;
2022-12-02 12:54:28 -08:00
toEmails . split ( ',' ) . forEach ( prepareEmailString ) ;
2022-09-08 05:44:34 -07:00
}
}
let from = '' ;
if ( options . senderName ) {
from = ` ${ options . senderName as string } < ${ emailAddress } > ` ;
}
const email : IEmail = {
from ,
to ,
cc ,
bcc ,
subject ,
attachments ,
inReplyTo : messageIdGlobal ,
reference : messageIdGlobal ,
. . . prepareEmailBody . call ( this , itemIndex ) ,
} ;
const body = {
raw : await encodeEmail ( email ) ,
threadId ,
} ;
2022-12-02 12:54:28 -08:00
return googleApiRequest . call ( this , 'POST' , '/gmail/v1/users/me/messages/send' , body , qs ) ;
2022-09-08 05:44:34 -07:00
}
export async function simplifyOutput (
this : IExecuteFunctions | IPollFunctions ,
data : IDataObject [ ] ,
) {
2022-12-29 03:20:43 -08:00
const labelsData = await googleApiRequest . call ( this , 'GET' , '/gmail/v1/users/me/labels' ) ;
2022-09-08 05:44:34 -07:00
const labels = ( ( labelsData . labels as IDataObject [ ] ) || [ ] ) . map ( ( { id , name } ) = > ( {
id ,
name ,
} ) ) ;
2022-12-02 12:54:28 -08:00
return ( data || [ ] ) . map ( ( item ) = > {
2022-09-08 05:44:34 -07:00
if ( item . labelIds ) {
item . labels = labels . filter ( ( label ) = >
( item . labelIds as string [ ] ) . includes ( label . id as string ) ,
) ;
delete item . labelIds ;
}
if ( item . payload && ( item . payload as IDataObject ) . headers ) {
const { headers } = item . payload as IDataObject ;
( ( headers as IDataObject [ ] ) || [ ] ) . forEach ( ( header ) = > {
item [ header . name as string ] = header . value ;
} ) ;
delete ( item . payload as IDataObject ) . headers ;
}
return item ;
} ) ;
}