Skip to content

Commit 93bad28

Browse files
feat: add SMS_ATTENDEE action to activateEventType.handler.ts (calcom#21201)
1 parent 47d9416 commit 93bad28

4 files changed

Lines changed: 119 additions & 29 deletions

File tree

packages/features/tasker/tasks/scanWorkflowBody.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ describe("scanWorkflowBody", () => {
216216
passthrough: true,
217217
}),
218218
});
219-
expect(prismaMock.workflowStep.update).not.toHaveBeenCalled();
219+
expect(prismaMock.workflowStep.update).toHaveBeenCalled();
220+
expect(scheduleWorkflowNotifications).toHaveBeenCalled();
221+
220222
expect(lockUser).not.toHaveBeenCalled();
221223
});
222224
});

packages/features/tasker/tasks/scanWorkflowBody.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ export const scanWorkflowBodySchema = z.object({
1717
const log = logger.getSubLogger({ prefix: ["[tasker] scanWorkflowBody"] });
1818

1919
export async function scanWorkflowBody(payload: string) {
20+
const { workflowStepIds, userId } = scanWorkflowBodySchema.parse(JSON.parse(payload));
21+
2022
if (!process.env.IFFY_API_KEY) {
2123
log.info("IFFY_API_KEY not set, skipping scan");
24+
await prisma.workflowStep.updateMany({
25+
where: {
26+
id: {
27+
in: workflowStepIds,
28+
},
29+
},
30+
data: {
31+
verifiedAt: new Date(),
32+
},
33+
});
2234
return;
2335
}
2436

25-
const { workflowStepIds, userId } = scanWorkflowBodySchema.parse(JSON.parse(payload));
26-
2737
const workflowSteps = await prisma.workflowStep.findMany({
2838
where: {
2939
id: {
@@ -91,29 +101,27 @@ export async function scanWorkflowBody(payload: string) {
91101
const isSpam = await iffyScanBody(workflowStep.reminderBody, workflowStep.id);
92102

93103
if (isSpam) {
94-
if (workflowStep.workflow.user?.whitelistWorkflows) {
95-
log.warn(
96-
`For whitelisted user, workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`
97-
);
104+
if (!workflowStep.workflow.user?.whitelistWorkflows) {
105+
// We won't delete the workflow step incase it is flagged as a false positive
106+
log.warn(`Workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`);
107+
await lockUser("userId", userId.toString(), LockReason.SPAM_WORKFLOW_BODY);
108+
109+
// Return early if spam is detected
98110
return;
99111
}
100-
101-
// We won't delete the workflow step incase it is flagged as a false positive
102-
log.warn(`Workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`);
103-
await lockUser("userId", userId.toString(), LockReason.SPAM_WORKFLOW_BODY);
104-
105-
// Return early if spam is detected
106-
return;
107-
} else {
108-
await prisma.workflowStep.update({
109-
where: {
110-
id: workflowStep.id,
111-
},
112-
data: {
113-
verifiedAt: new Date(),
114-
},
115-
});
112+
log.warn(
113+
`For whitelisted user, workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`
114+
);
116115
}
116+
117+
await prisma.workflowStep.update({
118+
where: {
119+
id: workflowStep.id,
120+
},
121+
data: {
122+
verifiedAt: new Date(),
123+
},
124+
});
117125
}
118126

119127
const workflow = await prisma.workflow.findFirst({

packages/trpc/server/routers/viewer/workflows/activateEventType.handler.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
139139

140140
let allEventTypes = [];
141141

142-
//get all event types of of team or user
142+
//get all event types of team or user
143143
if (eventTypeWorkflow.teamId) {
144144
allEventTypes = await prisma.eventType.findMany({
145145
where: {
@@ -193,7 +193,14 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
193193
// activate workflow and schedule reminders for existing bookings
194194
const bookingsForReminders = await prisma.booking.findMany({
195195
where: {
196-
eventTypeId: eventTypeId,
196+
OR: [
197+
{ eventTypeId },
198+
{
199+
eventType: {
200+
parentId: eventTypeId,
201+
},
202+
},
203+
],
197204
status: BookingStatus.ACCEPTED,
198205
startTime: {
199206
gte: new Date(),
@@ -352,6 +359,44 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
352359
teamId: eventTypeWorkflow.teamId,
353360
verifiedAt: step.verifiedAt,
354361
});
362+
} else if (booking.smsReminderNumber) {
363+
if (step.action === WorkflowActions.SMS_ATTENDEE) {
364+
await scheduleSMSReminder({
365+
evt: bookingInfo,
366+
reminderPhone: booking.smsReminderNumber,
367+
triggerEvent: eventTypeWorkflow.trigger,
368+
action: step.action,
369+
timeSpan: {
370+
time: eventTypeWorkflow.time,
371+
timeUnit: eventTypeWorkflow.timeUnit,
372+
},
373+
message: step.reminderBody || "",
374+
workflowStepId: step.id,
375+
template: step.template,
376+
sender: step.sender,
377+
userId: booking.userId,
378+
teamId: eventTypeWorkflow.teamId,
379+
verifiedAt: step.verifiedAt,
380+
});
381+
} else if (step.action === WorkflowActions.WHATSAPP_ATTENDEE) {
382+
await scheduleWhatsappReminder({
383+
evt: bookingInfo,
384+
reminderPhone: booking.smsReminderNumber,
385+
triggerEvent: eventTypeWorkflow.trigger,
386+
action: step.action,
387+
timeSpan: {
388+
time: eventTypeWorkflow.time,
389+
timeUnit: eventTypeWorkflow.timeUnit,
390+
},
391+
message: step.reminderBody || "",
392+
workflowStepId: step.id,
393+
template: step.template,
394+
sender: step.sender,
395+
userId: booking.userId,
396+
teamId: eventTypeWorkflow.teamId,
397+
verifiedAt: step.verifiedAt,
398+
});
399+
}
355400
}
356401
}
357402
}

packages/trpc/server/routers/viewer/workflows/util.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const bookingSelect = {
4444
title: true,
4545
uid: true,
4646
metadata: true,
47+
smsReminderNumber: true,
4748
attendees: {
4849
select: {
4950
name: true,
@@ -620,10 +621,6 @@ export async function scheduleBookingReminders(
620621

621622
//create reminders for all bookings for each workflow step
622623
const promiseSteps = workflowSteps.map(async (step) => {
623-
// we do not have attendees phone number (user is notified about that when setting this action)
624-
if (step.action == WorkflowActions.SMS_ATTENDEE || step.action == WorkflowActions.WHATSAPP_ATTENDEE)
625-
return;
626-
627624
const promiseScheduleReminders = bookings.map(async (booking) => {
628625
const defaultLocale = "en";
629626
const bookingInfo = {
@@ -742,6 +739,44 @@ export async function scheduleBookingReminders(
742739
teamId: teamId,
743740
verifiedAt: step?.verifiedAt ?? null,
744741
});
742+
} else if (booking.smsReminderNumber) {
743+
if (step.action === WorkflowActions.SMS_ATTENDEE) {
744+
await scheduleSMSReminder({
745+
evt: bookingInfo,
746+
reminderPhone: booking.smsReminderNumber,
747+
triggerEvent: trigger,
748+
action: step.action,
749+
timeSpan: {
750+
time,
751+
timeUnit,
752+
},
753+
message: step.reminderBody || "",
754+
workflowStepId: step.id,
755+
template: step.template,
756+
sender: step.sender,
757+
userId: userId,
758+
teamId: teamId,
759+
verifiedAt: step?.verifiedAt ?? null,
760+
});
761+
} else if (step.action === WorkflowActions.WHATSAPP_ATTENDEE) {
762+
await scheduleWhatsappReminder({
763+
evt: bookingInfo,
764+
reminderPhone: booking.smsReminderNumber,
765+
triggerEvent: trigger,
766+
action: step.action,
767+
timeSpan: {
768+
time,
769+
timeUnit,
770+
},
771+
message: step.reminderBody || "",
772+
workflowStepId: step.id,
773+
template: step.template,
774+
sender: step.sender,
775+
userId: userId,
776+
teamId: teamId,
777+
verifiedAt: step?.verifiedAt ?? null,
778+
});
779+
}
745780
}
746781
});
747782
await Promise.all(promiseScheduleReminders);

0 commit comments

Comments
 (0)