import { WorkflowTriggerEvents, TimeUnit, WorkflowTemplates, WorkflowActions, WorkflowMethods, } from "@prisma/client"; import dayjs from "@calcom/dayjs"; import prisma from "@calcom/prisma"; import { Prisma } from "@calcom/prisma/client"; import * as twilio from "./smsProviders/twilioProvider"; import customTemplate, { VariablesType } from "./templates/customTemplate"; import smsReminderTemplate from "./templates/smsReminderTemplate"; export enum timeUnitLowerCase { DAY = "day", MINUTE = "minute", YEAR = "year", } export type BookingInfo = { uid?: string | null; attendees: { name: string; email: string; timeZone: string }[]; organizer: { language: { locale: string }; name: string; email: string; timeZone: string; }; startTime: string; endTime: string; title: string; location?: string | null; additionalNotes?: string | null; customInputs?: Prisma.JsonValue; }; export const scheduleSMSReminder = async ( evt: BookingInfo, reminderPhone: string | null, triggerEvent: WorkflowTriggerEvents, action: WorkflowActions, timeSpan: { time: number | null; timeUnit: TimeUnit | null; }, message: string, workflowStepId: number, template: WorkflowTemplates, sender: string ) => { const { startTime, endTime } = evt; const uid = evt.uid as string; const currentDate = dayjs(); const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase; let scheduledDate = null; if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT) { scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null; } else if (triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) { scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null; } const name = action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].name : ""; const attendeeName = action === WorkflowActions.SMS_ATTENDEE ? evt.organizer.name : evt.attendees[0].name; const timeZone = action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone; switch (template) { case WorkflowTemplates.REMINDER: message = smsReminderTemplate(evt.startTime, evt.title, timeZone, attendeeName, name) || message; break; case WorkflowTemplates.CUSTOM: const variables: VariablesType = { eventName: evt.title, organizerName: evt.organizer.name, attendeeName: evt.attendees[0].name, attendeeEmail: evt.attendees[0].email, eventDate: dayjs(evt.startTime).tz(timeZone), eventTime: dayjs(evt.startTime).tz(timeZone), timeZone: timeZone, location: evt.location, additionalNotes: evt.additionalNotes, customInputs: evt.customInputs, }; const customMessage = await customTemplate(message, variables, evt.organizer.language.locale); message = customMessage.text; break; } if (message.length > 0 && reminderPhone) { //send SMS when event is booked/cancelled/rescheduled if ( triggerEvent === WorkflowTriggerEvents.NEW_EVENT || triggerEvent === WorkflowTriggerEvents.EVENT_CANCELLED || triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT ) { try { await twilio.sendSMS(reminderPhone, message, sender); } catch (error) { console.log(`Error sending SMS with error ${error}`); } } else if ( (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT || triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) && scheduledDate ) { // Can only schedule at least 60 minutes in advance and at most 7 days in advance if ( currentDate.isBefore(scheduledDate.subtract(1, "hour")) && !scheduledDate.isAfter(currentDate.add(7, "day")) ) { try { const scheduledSMS = await twilio.scheduleSMS( reminderPhone, message, scheduledDate.toDate(), sender ); await prisma.workflowReminder.create({ data: { bookingUid: uid, workflowStepId: workflowStepId, method: WorkflowMethods.SMS, scheduledDate: scheduledDate.toDate(), scheduled: true, referenceId: scheduledSMS.sid, }, }); } catch (error) { console.log(`Error scheduling SMS with error ${error}`); } } else if (scheduledDate.isAfter(currentDate.add(7, "day"))) { // Write to DB and send to CRON if scheduled reminder date is past 7 days await prisma.workflowReminder.create({ data: { bookingUid: uid, workflowStepId: workflowStepId, method: WorkflowMethods.SMS, scheduledDate: scheduledDate.toDate(), scheduled: false, }, }); } } } }; export const deleteScheduledSMSReminder = async (referenceId: string) => { try { await twilio.cancelSMS(referenceId); } catch (error) { console.log(`Error canceling reminder with error ${error}`); } };