From ea3077ea2e905c2e9447e6caf6ec994cdb4eef2f Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 23 Mar 2023 18:18:33 +0800 Subject: [PATCH] Use receipient address in the envelope field --- packages/inbound-email-handler/src/index.ts | 32 ++++++++++++------- .../test/newsletter.test.ts | 23 ++++++++++++- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/packages/inbound-email-handler/src/index.ts b/packages/inbound-email-handler/src/index.ts index adb44f4eb..201f5a4fb 100644 --- a/packages/inbound-email-handler/src/index.ts +++ b/packages/inbound-email-handler/src/index.ts @@ -3,20 +3,20 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { PubSub } from '@google-cloud/pubsub' +import { handleNewsletter } from '@omnivore/content-handler' import * as Sentry from '@sentry/serverless' +import axios from 'axios' +import * as jwt from 'jsonwebtoken' import parseHeaders from 'parse-headers' import * as multipart from 'parse-multipart-data' +import { promisify } from 'util' import { handleConfirmation, isConfirmationEmail, parseUnsubscribe, } from './newsletter' -import { PubSub } from '@google-cloud/pubsub' import { handlePdfAttachment } from './pdf' -import { handleNewsletter } from '@omnivore/content-handler' -import axios from 'axios' -import { promisify } from 'util' -import * as jwt from 'jsonwebtoken' interface SaveReceivedEmailResponse { id: string @@ -68,6 +68,19 @@ const saveReceivedEmail = async ( return response.data as SaveReceivedEmailResponse } +export const parsedTo = (parsed: Record): string => { + // envelope to contains the real recipient email address + try { + const envelope = JSON.parse(parsed.envelope) as Record< + string, + string | string[] + > + return envelope.to.toString().split(',')[0] || parsed.to + } catch (err) { + return parsed.to + } +} + export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( async (req, res) => { try { @@ -98,17 +111,12 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( const subject = parsed['subject'] const html = parsed['html'] const text = parsed['text'] - - // headers added when forwarding email by some rules in Gmail - // e.g. 'X-Forwarded-To: recipient@omnivore.app' - const forwardedTo = headers['x-forwarded-to']?.toString().split(',')[0] + // if an email is forwarded to the inbox, the to is the forwarding email recipient + const to = parsedTo(parsed) // x-forwarded-for is a space separated list of email address // the first one is the forwarding email sender and the last one is the recipient // e.g. 'X-Forwarded-For: sender@omnivore.app recipient@omnivore.app' const forwardedFrom = headers['x-forwarded-for']?.toString().split(' ')[0] - - // if an email is forwarded to the inbox, the to is the forwarding email recipient - const to = forwardedTo || parsed['to'] const unSubHeader = headers['list-unsubscribe']?.toString() const { id: receivedEmailId } = await saveReceivedEmail(to, { diff --git a/packages/inbound-email-handler/test/newsletter.test.ts b/packages/inbound-email-handler/test/newsletter.test.ts index 598b0a50d..284e327b3 100644 --- a/packages/inbound-email-handler/test/newsletter.test.ts +++ b/packages/inbound-email-handler/test/newsletter.test.ts @@ -1,5 +1,6 @@ -import 'mocha' import { expect } from 'chai' +import 'mocha' +import { parsedTo } from '../src' import { getConfirmationCode, isConfirmationEmail, @@ -90,3 +91,23 @@ describe('Newsletter email test', () => { }) }) }) + +describe('parsedTo', () => { + it('returns envelope to if exists', () => { + const to = 'receipient@inbox.omnivore.app' + expect( + parsedTo({ + envelope: `{"to":["${to}"],"from":"sender@omnivore.app"}`, + }) + ).to.equal(to) + }) + + it('returns parsed to if envelope does not exists', () => { + const to = 'receipient@inbox.omnivore.app' + expect( + parsedTo({ + to, + }) + ).to.equal(to) + }) +})