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)
|
||||
unsubscribed Int @default(0)
|
||||
bounced Int @default(0)
|
||||
hardBounced Int @default(0)
|
||||
complained Int @default(0)
|
||||
status CampaignStatus @default(DRAFT)
|
||||
createdAt DateTime @default(now())
|
||||
@@ -356,18 +357,19 @@ enum EmailUsageType {
|
||||
}
|
||||
|
||||
model DailyEmailUsage {
|
||||
teamId Int
|
||||
date String
|
||||
type EmailUsageType
|
||||
domainId Int
|
||||
sent Int @default(0)
|
||||
delivered Int @default(0)
|
||||
opened Int @default(0)
|
||||
clicked Int @default(0)
|
||||
bounced Int @default(0)
|
||||
complained Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
teamId Int
|
||||
date String
|
||||
type EmailUsageType
|
||||
domainId Int
|
||||
sent Int @default(0)
|
||||
delivered Int @default(0)
|
||||
opened Int @default(0)
|
||||
clicked Int @default(0)
|
||||
bounced Int @default(0)
|
||||
complained Int @default(0)
|
||||
hardBounced Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
|
||||
|
@@ -334,7 +334,8 @@ export async function sendCampaignEmail(
|
||||
|
||||
export async function updateCampaignAnalytics(
|
||||
campaignId: string,
|
||||
emailStatus: EmailStatus
|
||||
emailStatus: EmailStatus,
|
||||
hardBounce: boolean = false
|
||||
) {
|
||||
const campaign = await db.campaign.findUnique({
|
||||
where: { id: campaignId },
|
||||
@@ -361,6 +362,9 @@ export async function updateCampaignAnalytics(
|
||||
break;
|
||||
case EmailStatus.BOUNCED:
|
||||
updateData.bounced = { increment: 1 };
|
||||
if (hardBounce) {
|
||||
updateData.hardBounced = { increment: 1 };
|
||||
}
|
||||
break;
|
||||
case EmailStatus.COMPLAINED:
|
||||
updateData.complained = { increment: 1 };
|
||||
|
@@ -1,5 +1,10 @@
|
||||
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 {
|
||||
unsubscribeContact,
|
||||
@@ -57,6 +62,12 @@ export async function parseSesHook(data: SesEvent) {
|
||||
// Update daily email usage statistics
|
||||
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 (
|
||||
[
|
||||
"DELIVERED",
|
||||
@@ -89,11 +100,13 @@ export async function parseSesHook(data: SesEvent) {
|
||||
bounced: updateField === "bounced" ? 1 : 0,
|
||||
complained: updateField === "complained" ? 1 : 0,
|
||||
sent: updateField === "sent" ? 1 : 0,
|
||||
hardBounced: isHardBounced ? 1 : 0,
|
||||
},
|
||||
update: {
|
||||
[updateField]: {
|
||||
increment: 1,
|
||||
},
|
||||
...(isHardBounced ? { hardBounced: { increment: 1 } } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -108,6 +121,7 @@ export async function parseSesHook(data: SesEvent) {
|
||||
campaignId: email.campaignId,
|
||||
teamId: email.teamId,
|
||||
event: mailStatus,
|
||||
mailData: data,
|
||||
});
|
||||
|
||||
const mailEvent = await db.emailEvent.findFirst({
|
||||
@@ -118,7 +132,11 @@ export async function parseSesHook(data: SesEvent) {
|
||||
});
|
||||
|
||||
if (!mailEvent) {
|
||||
await updateCampaignAnalytics(email.campaignId, mailStatus);
|
||||
await updateCampaignAnalytics(
|
||||
email.campaignId,
|
||||
mailStatus,
|
||||
isHardBounced
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,13 +157,23 @@ async function checkUnsubscribe({
|
||||
campaignId,
|
||||
teamId,
|
||||
event,
|
||||
mailData,
|
||||
}: {
|
||||
contactId: string;
|
||||
campaignId: string;
|
||||
teamId: number;
|
||||
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({
|
||||
where: {
|
||||
id: contactId,
|
||||
|
Reference in New Issue
Block a user