Initial Commit
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import type { Contact, Event, Project } from "@prisma/client";
|
||||
import dayjs from "dayjs";
|
||||
import { prisma } from "../database/prisma";
|
||||
import { ContactService } from "./ContactService";
|
||||
import { EmailService } from "./EmailService";
|
||||
import { Keys } from "./keys";
|
||||
import { wrapRedis } from "./redis";
|
||||
|
||||
export class ActionService {
|
||||
/**
|
||||
* Gets an action by its ID
|
||||
* @param id
|
||||
*/
|
||||
public static id(id: string) {
|
||||
return wrapRedis(Keys.Action.id(id), async () => {
|
||||
return prisma.action.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
events: true,
|
||||
notevents: true,
|
||||
triggers: true,
|
||||
emails: true,
|
||||
template: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all actions that share an event with the action with the given ID
|
||||
* @param id
|
||||
*/
|
||||
public static related(id: string) {
|
||||
return wrapRedis(Keys.Action.related(id), async () => {
|
||||
const action = await ActionService.id(id);
|
||||
|
||||
if (!action) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return prisma.action.findMany({
|
||||
where: {
|
||||
events: { some: { id: { in: action.events.map((e) => e.id) } } },
|
||||
id: { not: action.id },
|
||||
},
|
||||
include: { events: true },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all actions that have an event as a trigger
|
||||
* @param eventId
|
||||
*/
|
||||
public static event(eventId: string) {
|
||||
return wrapRedis(Keys.Action.event(eventId), async () => {
|
||||
return prisma.event
|
||||
.findUniqueOrThrow({ where: { id: eventId } })
|
||||
.actions({
|
||||
include: { events: true, template: true, notevents: true },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a contact and an event and triggers all required actions
|
||||
* @param contact
|
||||
* @param event
|
||||
* @param project
|
||||
*/
|
||||
public static async trigger({
|
||||
event,
|
||||
contact,
|
||||
project,
|
||||
}: { event: Event; contact: Contact; project: Project }) {
|
||||
const actions = await ActionService.event(event.id);
|
||||
|
||||
const triggers = await ContactService.triggers(contact.id);
|
||||
|
||||
for (const action of actions) {
|
||||
const hasTriggeredAction = !!triggers.find(
|
||||
(t) => t.actionId === action.id,
|
||||
);
|
||||
|
||||
if (action.runOnce && hasTriggeredAction) {
|
||||
// User has already triggered this run once action
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
action.notevents.length > 0 &&
|
||||
action.notevents.some((e) => triggers.some((t) => t.eventId === e.id))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let triggeredEvents = triggers.filter((t) => t.eventId === event.id);
|
||||
|
||||
if (hasTriggeredAction) {
|
||||
const lastActionTrigger = triggers.filter(
|
||||
(t) => t.contactId === contact.id && t.actionId === action.id,
|
||||
)[0];
|
||||
|
||||
triggeredEvents = triggeredEvents.filter(
|
||||
(e) => e.createdAt > lastActionTrigger.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedTriggers = [
|
||||
...new Set(triggeredEvents.map((t) => t.eventId)),
|
||||
];
|
||||
const requiredTriggers = action.events.map((e) => e.id);
|
||||
|
||||
if (
|
||||
updatedTriggers.sort().join(",") !== requiredTriggers.sort().join(",")
|
||||
) {
|
||||
// Not all required events have been triggered
|
||||
continue;
|
||||
}
|
||||
|
||||
await prisma.trigger.create({
|
||||
data: { actionId: action.id, contactId: contact.id },
|
||||
});
|
||||
|
||||
if (!contact.subscribed && action.template.type === "MARKETING") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.delay === 0) {
|
||||
const { subject, body } = EmailService.format({
|
||||
subject: action.template.subject,
|
||||
body: action.template.body,
|
||||
data: {
|
||||
plunk_id: contact.id,
|
||||
plunk_email: contact.email,
|
||||
...JSON.parse(contact.data ?? "{}"),
|
||||
},
|
||||
});
|
||||
|
||||
const { messageId } = await EmailService.send({
|
||||
from: {
|
||||
name: project.from ?? project.name,
|
||||
email:
|
||||
project.verified && project.email
|
||||
? project.email
|
||||
: "[email protected]",
|
||||
},
|
||||
to: [contact.email],
|
||||
content: {
|
||||
subject,
|
||||
html: EmailService.compile({
|
||||
content: body,
|
||||
footer: {
|
||||
unsubscribe: action.template.type === "MARKETING",
|
||||
},
|
||||
contact: {
|
||||
id: contact.id,
|
||||
},
|
||||
project: {
|
||||
name: project.name,
|
||||
},
|
||||
isHtml: action.template.style === "HTML",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.email.create({
|
||||
data: {
|
||||
messageId,
|
||||
actionId: action.id,
|
||||
contactId: contact.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await prisma.task.create({
|
||||
data: {
|
||||
actionId: action.id,
|
||||
contactId: contact.id,
|
||||
runBy: dayjs().add(action.delay, "minutes").toDate(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user