only unsub contacts on permanent bounces (#156)
* only unsub on permanent counces * add hard bounce to email usage * add hard bounce for campaign * fix
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "DailyEmailUsage" ADD COLUMN "hardBounced" INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "EmailEvent" ALTER COLUMN "status" DROP DEFAULT;
|
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Campaign" ADD COLUMN "hardBounced" INTEGER NOT NULL DEFAULT 0;
|
@@ -325,6 +325,7 @@ model Campaign {
|
|||||||
clicked Int @default(0)
|
clicked Int @default(0)
|
||||||
unsubscribed Int @default(0)
|
unsubscribed Int @default(0)
|
||||||
bounced Int @default(0)
|
bounced Int @default(0)
|
||||||
|
hardBounced Int @default(0)
|
||||||
complained Int @default(0)
|
complained Int @default(0)
|
||||||
status CampaignStatus @default(DRAFT)
|
status CampaignStatus @default(DRAFT)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -366,6 +367,7 @@ model DailyEmailUsage {
|
|||||||
clicked Int @default(0)
|
clicked Int @default(0)
|
||||||
bounced Int @default(0)
|
bounced Int @default(0)
|
||||||
complained Int @default(0)
|
complained Int @default(0)
|
||||||
|
hardBounced Int @default(0)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@ -334,7 +334,8 @@ export async function sendCampaignEmail(
|
|||||||
|
|
||||||
export async function updateCampaignAnalytics(
|
export async function updateCampaignAnalytics(
|
||||||
campaignId: string,
|
campaignId: string,
|
||||||
emailStatus: EmailStatus
|
emailStatus: EmailStatus,
|
||||||
|
hardBounce: boolean = false
|
||||||
) {
|
) {
|
||||||
const campaign = await db.campaign.findUnique({
|
const campaign = await db.campaign.findUnique({
|
||||||
where: { id: campaignId },
|
where: { id: campaignId },
|
||||||
@@ -361,6 +362,9 @@ export async function updateCampaignAnalytics(
|
|||||||
break;
|
break;
|
||||||
case EmailStatus.BOUNCED:
|
case EmailStatus.BOUNCED:
|
||||||
updateData.bounced = { increment: 1 };
|
updateData.bounced = { increment: 1 };
|
||||||
|
if (hardBounce) {
|
||||||
|
updateData.hardBounced = { increment: 1 };
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EmailStatus.COMPLAINED:
|
case EmailStatus.COMPLAINED:
|
||||||
updateData.complained = { increment: 1 };
|
updateData.complained = { increment: 1 };
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
import { EmailStatus, Prisma, UnsubscribeReason } from "@prisma/client";
|
import { EmailStatus, Prisma, UnsubscribeReason } from "@prisma/client";
|
||||||
import { SesClick, SesEvent, SesEventDataKey } from "~/types/aws-types";
|
import {
|
||||||
|
SesBounce,
|
||||||
|
SesClick,
|
||||||
|
SesEvent,
|
||||||
|
SesEventDataKey,
|
||||||
|
} from "~/types/aws-types";
|
||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
import {
|
import {
|
||||||
unsubscribeContact,
|
unsubscribeContact,
|
||||||
@@ -57,6 +62,12 @@ export async function parseSesHook(data: SesEvent) {
|
|||||||
// Update daily email usage statistics
|
// Update daily email usage statistics
|
||||||
const today = new Date().toISOString().split("T")[0] as string; // Format: YYYY-MM-DD
|
const today = new Date().toISOString().split("T")[0] as string; // Format: YYYY-MM-DD
|
||||||
|
|
||||||
|
const isHardBounced =
|
||||||
|
mailStatus === EmailStatus.BOUNCED &&
|
||||||
|
(mailData as SesBounce).bounceType === "Permanent";
|
||||||
|
|
||||||
|
console.log("mailStatus", mailStatus, "isHardBounced", isHardBounced);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
"DELIVERED",
|
"DELIVERED",
|
||||||
@@ -89,11 +100,13 @@ export async function parseSesHook(data: SesEvent) {
|
|||||||
bounced: updateField === "bounced" ? 1 : 0,
|
bounced: updateField === "bounced" ? 1 : 0,
|
||||||
complained: updateField === "complained" ? 1 : 0,
|
complained: updateField === "complained" ? 1 : 0,
|
||||||
sent: updateField === "sent" ? 1 : 0,
|
sent: updateField === "sent" ? 1 : 0,
|
||||||
|
hardBounced: isHardBounced ? 1 : 0,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
[updateField]: {
|
[updateField]: {
|
||||||
increment: 1,
|
increment: 1,
|
||||||
},
|
},
|
||||||
|
...(isHardBounced ? { hardBounced: { increment: 1 } } : {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -108,6 +121,7 @@ export async function parseSesHook(data: SesEvent) {
|
|||||||
campaignId: email.campaignId,
|
campaignId: email.campaignId,
|
||||||
teamId: email.teamId,
|
teamId: email.teamId,
|
||||||
event: mailStatus,
|
event: mailStatus,
|
||||||
|
mailData: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mailEvent = await db.emailEvent.findFirst({
|
const mailEvent = await db.emailEvent.findFirst({
|
||||||
@@ -118,7 +132,11 @@ export async function parseSesHook(data: SesEvent) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!mailEvent) {
|
if (!mailEvent) {
|
||||||
await updateCampaignAnalytics(email.campaignId, mailStatus);
|
await updateCampaignAnalytics(
|
||||||
|
email.campaignId,
|
||||||
|
mailStatus,
|
||||||
|
isHardBounced
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,13 +157,23 @@ async function checkUnsubscribe({
|
|||||||
campaignId,
|
campaignId,
|
||||||
teamId,
|
teamId,
|
||||||
event,
|
event,
|
||||||
|
mailData,
|
||||||
}: {
|
}: {
|
||||||
contactId: string;
|
contactId: string;
|
||||||
campaignId: string;
|
campaignId: string;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
event: EmailStatus;
|
event: EmailStatus;
|
||||||
|
mailData: SesEvent;
|
||||||
}) {
|
}) {
|
||||||
if (event === EmailStatus.BOUNCED || event === EmailStatus.COMPLAINED) {
|
/**
|
||||||
|
* If the email is bounced and the bounce type is permanent, we need to unsubscribe the contact
|
||||||
|
* If the email is complained, we need to unsubscribe the contact
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
(event === EmailStatus.BOUNCED &&
|
||||||
|
mailData.bounce?.bounceType === "Permanent") ||
|
||||||
|
event === EmailStatus.COMPLAINED
|
||||||
|
) {
|
||||||
const contact = await db.contact.findUnique({
|
const contact = await db.contact.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: contactId,
|
id: contactId,
|
||||||
|
Reference in New Issue
Block a user