feat: mandatory email reminder for attendees with @gmail.com (#12747)
Co-authored-by: Chiranjeev Vishnoi <somu209e@gmail.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
parent
6b26dbc6da
commit
3164cd4ae7
|
@ -2,6 +2,7 @@ import type { Prisma, Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@pri
|
||||||
|
|
||||||
import type { EventManagerUser } from "@calcom/core/EventManager";
|
import type { EventManagerUser } from "@calcom/core/EventManager";
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
|
import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/scheduleMandatoryReminder";
|
||||||
import { sendScheduledEmails } from "@calcom/emails";
|
import { sendScheduledEmails } from "@calcom/emails";
|
||||||
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
|
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
|
||||||
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
||||||
|
@ -256,27 +257,27 @@ export async function handleConfirmation(args: {
|
||||||
//Workflows - set reminders for confirmed events
|
//Workflows - set reminders for confirmed events
|
||||||
try {
|
try {
|
||||||
for (let index = 0; index < updatedBookings.length; index++) {
|
for (let index = 0; index < updatedBookings.length; index++) {
|
||||||
if (updatedBookings[index].eventType?.workflows) {
|
const eventTypeSlug = updatedBookings[index].eventType?.slug || "";
|
||||||
const evtOfBooking = evt;
|
const evtOfBooking = { ...evt, metadata: { videoCallUrl }, eventType: { slug: eventTypeSlug } };
|
||||||
evtOfBooking.startTime = updatedBookings[index].startTime.toISOString();
|
evtOfBooking.startTime = updatedBookings[index].startTime.toISOString();
|
||||||
evtOfBooking.endTime = updatedBookings[index].endTime.toISOString();
|
evtOfBooking.endTime = updatedBookings[index].endTime.toISOString();
|
||||||
evtOfBooking.uid = updatedBookings[index].uid;
|
evtOfBooking.uid = updatedBookings[index].uid;
|
||||||
const eventTypeSlug = updatedBookings[index].eventType?.slug || "";
|
const isFirstBooking = index === 0;
|
||||||
|
await scheduleMandatoryReminder(
|
||||||
const isFirstBooking = index === 0;
|
evtOfBooking,
|
||||||
|
updatedBookings[index]?.eventType?.workflows || [],
|
||||||
await scheduleWorkflowReminders({
|
false,
|
||||||
workflows: updatedBookings[index]?.eventType?.workflows || [],
|
!!updatedBookings[index].eventType?.owner?.hideBranding,
|
||||||
smsReminderNumber: updatedBookings[index].smsReminderNumber,
|
evt.attendeeSeatId
|
||||||
calendarEvent: {
|
);
|
||||||
...evtOfBooking,
|
await scheduleWorkflowReminders({
|
||||||
...{ metadata: { videoCallUrl }, eventType: { slug: eventTypeSlug } },
|
workflows: updatedBookings[index]?.eventType?.workflows || [],
|
||||||
},
|
smsReminderNumber: updatedBookings[index].smsReminderNumber,
|
||||||
isFirstRecurringEvent: isFirstBooking,
|
calendarEvent: evtOfBooking,
|
||||||
hideBranding: !!updatedBookings[index].eventType?.owner?.hideBranding,
|
isFirstRecurringEvent: isFirstBooking,
|
||||||
eventTypeRequiresConfirmation: true,
|
hideBranding: !!updatedBookings[index].eventType?.owner?.hideBranding,
|
||||||
});
|
eventTypeRequiresConfirmation: true,
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Silently fail
|
// Silently fail
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { getEventName } from "@calcom/core/event";
|
||||||
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
||||||
import { deleteMeeting } from "@calcom/core/videoClient";
|
import { deleteMeeting } from "@calcom/core/videoClient";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/scheduleMandatoryReminder";
|
||||||
import {
|
import {
|
||||||
sendAttendeeRequestEmail,
|
sendAttendeeRequestEmail,
|
||||||
sendOrganizerRequestEmail,
|
sendOrganizerRequestEmail,
|
||||||
|
@ -2718,15 +2719,21 @@ async function handler(
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadataFromEvent = videoCallUrl ? { videoCallUrl } : undefined;
|
const metadataFromEvent = videoCallUrl ? { videoCallUrl } : undefined;
|
||||||
|
const evtWithMetadata = { ...evt, metadata: metadataFromEvent, eventType: { slug: eventType.slug } };
|
||||||
|
|
||||||
|
await scheduleMandatoryReminder(
|
||||||
|
evtWithMetadata,
|
||||||
|
eventType.workflows || [],
|
||||||
|
!isConfirmedByDefault,
|
||||||
|
!!eventType.owner?.hideBranding,
|
||||||
|
evt.attendeeSeatId
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await scheduleWorkflowReminders({
|
await scheduleWorkflowReminders({
|
||||||
workflows: eventType.workflows,
|
workflows: eventType.workflows,
|
||||||
smsReminderNumber: smsReminderNumber || null,
|
smsReminderNumber: smsReminderNumber || null,
|
||||||
calendarEvent: {
|
calendarEvent: evtWithMetadata,
|
||||||
...evt,
|
|
||||||
...{ metadata: metadataFromEvent, eventType: { slug: eventType.slug } },
|
|
||||||
},
|
|
||||||
isNotConfirmed: !isConfirmedByDefault,
|
isNotConfirmed: !isConfirmedByDefault,
|
||||||
isRescheduleEvent: !!rescheduleUid,
|
isRescheduleEvent: !!rescheduleUid,
|
||||||
isFirstRecurringEvent: true,
|
isFirstRecurringEvent: true,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
||||||
|
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { defaultHandler } from "@calcom/lib/server";
|
import { defaultHandler } from "@calcom/lib/server";
|
||||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
|
@ -121,100 +122,185 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const reminder of unscheduledReminders) {
|
for (const reminder of unscheduledReminders) {
|
||||||
if (!reminder.workflowStep || !reminder.booking) {
|
if (!reminder.booking) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
if (!reminder.isMandatoryReminder && reminder.workflowStep) {
|
||||||
let sendTo;
|
try {
|
||||||
|
let sendTo;
|
||||||
|
|
||||||
switch (reminder.workflowStep.action) {
|
switch (reminder.workflowStep.action) {
|
||||||
case WorkflowActions.EMAIL_HOST:
|
case WorkflowActions.EMAIL_HOST:
|
||||||
sendTo = reminder.booking.user?.email;
|
sendTo = reminder.booking.user?.email;
|
||||||
break;
|
break;
|
||||||
case WorkflowActions.EMAIL_ATTENDEE:
|
case WorkflowActions.EMAIL_ATTENDEE:
|
||||||
sendTo = reminder.booking.attendees[0].email;
|
sendTo = reminder.booking.attendees[0].email;
|
||||||
break;
|
break;
|
||||||
case WorkflowActions.EMAIL_ADDRESS:
|
case WorkflowActions.EMAIL_ADDRESS:
|
||||||
sendTo = reminder.workflowStep.sendTo;
|
sendTo = reminder.workflowStep.sendTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name =
|
const name =
|
||||||
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
||||||
? reminder.booking.attendees[0].name
|
? reminder.booking.attendees[0].name
|
||||||
: reminder.booking.user?.name;
|
: reminder.booking.user?.name;
|
||||||
|
|
||||||
const attendeeName =
|
const attendeeName =
|
||||||
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
||||||
? reminder.booking.user?.name
|
? reminder.booking.user?.name
|
||||||
: reminder.booking.attendees[0].name;
|
: reminder.booking.attendees[0].name;
|
||||||
|
|
||||||
const timeZone =
|
const timeZone =
|
||||||
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
|
||||||
? reminder.booking.attendees[0].timeZone
|
? reminder.booking.attendees[0].timeZone
|
||||||
: reminder.booking.user?.timeZone;
|
: reminder.booking.user?.timeZone;
|
||||||
|
|
||||||
const locale =
|
const locale =
|
||||||
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE ||
|
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE ||
|
||||||
reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE
|
reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE
|
||||||
? reminder.booking.attendees[0].locale
|
? reminder.booking.attendees[0].locale
|
||||||
: reminder.booking.user?.locale;
|
: reminder.booking.user?.locale;
|
||||||
|
|
||||||
let emailContent = {
|
let emailContent = {
|
||||||
emailSubject: reminder.workflowStep.emailSubject || "",
|
emailSubject: reminder.workflowStep.emailSubject || "",
|
||||||
emailBody: `<body style="white-space: pre-wrap;">${reminder.workflowStep.reminderBody || ""}</body>`,
|
emailBody: `<body style="white-space: pre-wrap;">${
|
||||||
};
|
reminder.workflowStep.reminderBody || ""
|
||||||
|
}</body>`,
|
||||||
let emailBodyEmpty = false;
|
|
||||||
|
|
||||||
if (reminder.workflowStep.reminderBody) {
|
|
||||||
const { responses } = getCalEventResponses({
|
|
||||||
bookingFields: reminder.booking.eventType?.bookingFields ?? null,
|
|
||||||
booking: reminder.booking,
|
|
||||||
});
|
|
||||||
|
|
||||||
const variables: VariablesType = {
|
|
||||||
eventName: reminder.booking.eventType?.title || "",
|
|
||||||
organizerName: reminder.booking.user?.name || "",
|
|
||||||
attendeeName: reminder.booking.attendees[0].name,
|
|
||||||
attendeeEmail: reminder.booking.attendees[0].email,
|
|
||||||
eventDate: dayjs(reminder.booking.startTime).tz(timeZone),
|
|
||||||
eventEndTime: dayjs(reminder.booking?.endTime).tz(timeZone),
|
|
||||||
timeZone: timeZone,
|
|
||||||
location: reminder.booking.location || "",
|
|
||||||
additionalNotes: reminder.booking.description,
|
|
||||||
responses: responses,
|
|
||||||
meetingUrl: bookingMetadataSchema.parse(reminder.booking.metadata || {})?.videoCallUrl,
|
|
||||||
cancelLink: `/booking/${reminder.booking.uid}?cancel=true`,
|
|
||||||
rescheduleLink: `/${reminder.booking.user?.username}/${reminder.booking.eventType?.slug}?rescheduleUid=${reminder.booking.uid}`,
|
|
||||||
};
|
};
|
||||||
const emailLocale = locale || "en";
|
|
||||||
const emailSubject = customTemplate(
|
|
||||||
reminder.workflowStep.emailSubject || "",
|
|
||||||
variables,
|
|
||||||
emailLocale,
|
|
||||||
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
|
||||||
!!reminder.booking.user?.hideBranding
|
|
||||||
).text;
|
|
||||||
emailContent.emailSubject = emailSubject;
|
|
||||||
emailContent.emailBody = customTemplate(
|
|
||||||
reminder.workflowStep.reminderBody || "",
|
|
||||||
variables,
|
|
||||||
emailLocale,
|
|
||||||
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
|
||||||
!!reminder.booking.user?.hideBranding
|
|
||||||
).html;
|
|
||||||
|
|
||||||
emailBodyEmpty =
|
let emailBodyEmpty = false;
|
||||||
customTemplate(
|
|
||||||
|
if (reminder.workflowStep.reminderBody) {
|
||||||
|
const { responses } = getCalEventResponses({
|
||||||
|
bookingFields: reminder.booking.eventType?.bookingFields ?? null,
|
||||||
|
booking: reminder.booking,
|
||||||
|
});
|
||||||
|
|
||||||
|
const variables: VariablesType = {
|
||||||
|
eventName: reminder.booking.eventType?.title || "",
|
||||||
|
organizerName: reminder.booking.user?.name || "",
|
||||||
|
attendeeName: reminder.booking.attendees[0].name,
|
||||||
|
attendeeEmail: reminder.booking.attendees[0].email,
|
||||||
|
eventDate: dayjs(reminder.booking.startTime).tz(timeZone),
|
||||||
|
eventEndTime: dayjs(reminder.booking?.endTime).tz(timeZone),
|
||||||
|
timeZone: timeZone,
|
||||||
|
location: reminder.booking.location || "",
|
||||||
|
additionalNotes: reminder.booking.description,
|
||||||
|
responses: responses,
|
||||||
|
meetingUrl: bookingMetadataSchema.parse(reminder.booking.metadata || {})?.videoCallUrl,
|
||||||
|
cancelLink: `/booking/${reminder.booking.uid}?cancel=true`,
|
||||||
|
rescheduleLink: `/${reminder.booking.user?.username}/${reminder.booking.eventType?.slug}?rescheduleUid=${reminder.booking.uid}`,
|
||||||
|
};
|
||||||
|
const emailLocale = locale || "en";
|
||||||
|
const emailSubject = customTemplate(
|
||||||
|
reminder.workflowStep.emailSubject || "",
|
||||||
|
variables,
|
||||||
|
emailLocale,
|
||||||
|
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
||||||
|
!!reminder.booking.user?.hideBranding
|
||||||
|
).text;
|
||||||
|
emailContent.emailSubject = emailSubject;
|
||||||
|
emailContent.emailBody = customTemplate(
|
||||||
reminder.workflowStep.reminderBody || "",
|
reminder.workflowStep.reminderBody || "",
|
||||||
variables,
|
variables,
|
||||||
emailLocale,
|
emailLocale,
|
||||||
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat)
|
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
||||||
).text.length === 0;
|
!!reminder.booking.user?.hideBranding
|
||||||
} else if (reminder.workflowStep.template === WorkflowTemplates.REMINDER) {
|
).html;
|
||||||
|
|
||||||
|
emailBodyEmpty =
|
||||||
|
customTemplate(
|
||||||
|
reminder.workflowStep.reminderBody || "",
|
||||||
|
variables,
|
||||||
|
emailLocale,
|
||||||
|
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat)
|
||||||
|
).text.length === 0;
|
||||||
|
} else if (reminder.workflowStep.template === WorkflowTemplates.REMINDER) {
|
||||||
|
emailContent = emailReminderTemplate(
|
||||||
|
false,
|
||||||
|
reminder.workflowStep.action,
|
||||||
|
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
||||||
|
reminder.booking.startTime.toISOString() || "",
|
||||||
|
reminder.booking.endTime.toISOString() || "",
|
||||||
|
reminder.booking.eventType?.title || "",
|
||||||
|
timeZone || "",
|
||||||
|
attendeeName || "",
|
||||||
|
name || "",
|
||||||
|
!!reminder.booking.user?.hideBranding
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailContent.emailSubject.length > 0 && !emailBodyEmpty && sendTo) {
|
||||||
|
const batchIdResponse = await client.request({
|
||||||
|
url: "/v3/mail/batch",
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const batchId = batchIdResponse[1].batch_id;
|
||||||
|
|
||||||
|
if (reminder.workflowStep.action !== WorkflowActions.EMAIL_ADDRESS) {
|
||||||
|
sendEmailPromises.push(
|
||||||
|
sgMail.send({
|
||||||
|
to: sendTo,
|
||||||
|
from: {
|
||||||
|
email: senderEmail,
|
||||||
|
name: reminder.workflowStep.sender || SENDER_NAME,
|
||||||
|
},
|
||||||
|
subject: emailContent.emailSubject,
|
||||||
|
html: emailContent.emailBody,
|
||||||
|
batchId: batchId,
|
||||||
|
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||||
|
replyTo: reminder.booking.user?.email || senderEmail,
|
||||||
|
mailSettings: {
|
||||||
|
sandboxMode: {
|
||||||
|
enable: sandboxMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attachments: reminder.workflowStep.includeCalendarEvent
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
content: Buffer.from(getiCalEventAsString(reminder.booking) || "").toString("base64"),
|
||||||
|
filename: "event.ics",
|
||||||
|
type: "text/calendar; method=REQUEST",
|
||||||
|
disposition: "attachment",
|
||||||
|
contentId: uuidv4(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.workflowReminder.update({
|
||||||
|
where: {
|
||||||
|
id: reminder.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
scheduled: true,
|
||||||
|
referenceId: batchId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error scheduling Email with error ${error}`);
|
||||||
|
}
|
||||||
|
} else if (reminder.isMandatoryReminder) {
|
||||||
|
try {
|
||||||
|
const sendTo = reminder.booking.attendees[0].email;
|
||||||
|
const name = reminder.booking.attendees[0].name;
|
||||||
|
const attendeeName = reminder.booking.user?.name;
|
||||||
|
const timeZone = reminder.booking.attendees[0].timeZone;
|
||||||
|
|
||||||
|
let emailContent = {
|
||||||
|
emailSubject: "",
|
||||||
|
emailBody: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailBodyEmpty = false;
|
||||||
|
|
||||||
emailContent = emailReminderTemplate(
|
emailContent = emailReminderTemplate(
|
||||||
false,
|
false,
|
||||||
reminder.workflowStep.action,
|
WorkflowActions.EMAIL_ATTENDEE,
|
||||||
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat),
|
||||||
reminder.booking.startTime.toISOString() || "",
|
reminder.booking.startTime.toISOString() || "",
|
||||||
reminder.booking.endTime.toISOString() || "",
|
reminder.booking.endTime.toISOString() || "",
|
||||||
|
@ -224,23 +310,20 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
name || "",
|
name || "",
|
||||||
!!reminder.booking.user?.hideBranding
|
!!reminder.booking.user?.hideBranding
|
||||||
);
|
);
|
||||||
}
|
if (emailContent.emailSubject.length > 0 && !emailBodyEmpty && sendTo) {
|
||||||
|
const batchIdResponse = await client.request({
|
||||||
|
url: "/v3/mail/batch",
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
if (emailContent.emailSubject.length > 0 && !emailBodyEmpty && sendTo) {
|
const batchId = batchIdResponse[1].batch_id;
|
||||||
const batchIdResponse = await client.request({
|
|
||||||
url: "/v3/mail/batch",
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
|
|
||||||
const batchId = batchIdResponse[1].batch_id;
|
|
||||||
|
|
||||||
if (reminder.workflowStep.action !== WorkflowActions.EMAIL_ADDRESS) {
|
|
||||||
sendEmailPromises.push(
|
sendEmailPromises.push(
|
||||||
sgMail.send({
|
sgMail.send({
|
||||||
to: sendTo,
|
to: sendTo,
|
||||||
from: {
|
from: {
|
||||||
email: senderEmail,
|
email: senderEmail,
|
||||||
name: reminder.workflowStep.sender || "Cal.com",
|
name: reminder.workflowStep?.sender || SENDER_NAME,
|
||||||
},
|
},
|
||||||
subject: emailContent.emailSubject,
|
subject: emailContent.emailSubject,
|
||||||
html: emailContent.emailBody,
|
html: emailContent.emailBody,
|
||||||
|
@ -252,33 +335,23 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
enable: sandboxMode,
|
enable: sandboxMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
attachments: reminder.workflowStep.includeCalendarEvent
|
attachments: undefined,
|
||||||
? [
|
|
||||||
{
|
|
||||||
content: Buffer.from(getiCalEventAsString(reminder.booking) || "").toString("base64"),
|
|
||||||
filename: "event.ics",
|
|
||||||
type: "text/calendar; method=REQUEST",
|
|
||||||
disposition: "attachment",
|
|
||||||
contentId: uuidv4(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: undefined,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.workflowReminder.update({
|
await prisma.workflowReminder.update({
|
||||||
where: {
|
where: {
|
||||||
id: reminder.id,
|
id: reminder.id,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
scheduled: true,
|
scheduled: true,
|
||||||
referenceId: batchId,
|
referenceId: batchId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error scheduling Email with error ${error}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error scheduling Email with error ${error}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,10 @@ type PartialBooking =
|
||||||
> & { eventType: Partial<EventType> | null } & { user: Partial<User> | null })
|
> & { eventType: Partial<EventType> | null } & { user: Partial<User> | null })
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
export type PartialWorkflowReminder = Pick<WorkflowReminder, "id" | "scheduledDate"> & {
|
export type PartialWorkflowReminder = Pick<
|
||||||
|
WorkflowReminder,
|
||||||
|
"id" | "isMandatoryReminder" | "scheduledDate"
|
||||||
|
> & {
|
||||||
booking: PartialBooking | null;
|
booking: PartialBooking | null;
|
||||||
} & { workflowStep: PartialWorkflowStep };
|
} & { workflowStep: PartialWorkflowStep };
|
||||||
|
|
||||||
|
@ -113,6 +116,7 @@ export async function getAllUnscheduledReminders(): Promise<PartialWorkflowRemin
|
||||||
const select: Prisma.WorkflowReminderSelect = {
|
const select: Prisma.WorkflowReminderSelect = {
|
||||||
id: true,
|
id: true,
|
||||||
scheduledDate: true,
|
scheduledDate: true,
|
||||||
|
isMandatoryReminder: true,
|
||||||
workflowStep: {
|
workflowStep: {
|
||||||
select: {
|
select: {
|
||||||
action: true,
|
action: true,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
|
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
|
||||||
|
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import type { TimeUnit } from "@calcom/prisma/enums";
|
import type { TimeUnit } from "@calcom/prisma/enums";
|
||||||
|
@ -94,24 +95,46 @@ type ScheduleEmailReminderAction = Extract<
|
||||||
"EMAIL_HOST" | "EMAIL_ATTENDEE" | "EMAIL_ADDRESS"
|
"EMAIL_HOST" | "EMAIL_ATTENDEE" | "EMAIL_ADDRESS"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const scheduleEmailReminder = async (
|
export interface ScheduleReminderArgs {
|
||||||
evt: BookingInfo,
|
evt: BookingInfo;
|
||||||
triggerEvent: WorkflowTriggerEvents,
|
triggerEvent: WorkflowTriggerEvents;
|
||||||
action: ScheduleEmailReminderAction,
|
|
||||||
timeSpan: {
|
timeSpan: {
|
||||||
time: number | null;
|
time: number | null;
|
||||||
timeUnit: TimeUnit | null;
|
timeUnit: TimeUnit | null;
|
||||||
},
|
};
|
||||||
sendTo: MailData["to"],
|
template: WorkflowTemplates;
|
||||||
emailSubject: string,
|
sender?: string | null;
|
||||||
emailBody: string,
|
workflowStepId?: number;
|
||||||
workflowStepId: number,
|
seatReferenceUid?: string;
|
||||||
template: WorkflowTemplates,
|
}
|
||||||
sender: string,
|
|
||||||
hideBranding?: boolean,
|
interface scheduleEmailReminderArgs extends ScheduleReminderArgs {
|
||||||
seatReferenceUid?: string,
|
sendTo: MailData["to"];
|
||||||
includeCalendarEvent?: boolean
|
action: ScheduleEmailReminderAction;
|
||||||
) => {
|
emailSubject?: string;
|
||||||
|
emailBody?: string;
|
||||||
|
hideBranding?: boolean;
|
||||||
|
includeCalendarEvent?: boolean;
|
||||||
|
isMandatoryReminder?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) => {
|
||||||
|
const {
|
||||||
|
evt,
|
||||||
|
triggerEvent,
|
||||||
|
timeSpan,
|
||||||
|
template,
|
||||||
|
sender,
|
||||||
|
workflowStepId,
|
||||||
|
seatReferenceUid,
|
||||||
|
sendTo,
|
||||||
|
emailSubject = "",
|
||||||
|
emailBody = "",
|
||||||
|
hideBranding,
|
||||||
|
includeCalendarEvent,
|
||||||
|
isMandatoryReminder,
|
||||||
|
action,
|
||||||
|
} = args;
|
||||||
if (action === WorkflowActions.EMAIL_ADDRESS) return;
|
if (action === WorkflowActions.EMAIL_ADDRESS) return;
|
||||||
const { startTime, endTime } = evt;
|
const { startTime, endTime } = evt;
|
||||||
const uid = evt.uid as string;
|
const uid = evt.uid as string;
|
||||||
|
@ -251,7 +274,7 @@ export const scheduleEmailReminder = async (
|
||||||
to: data.to,
|
to: data.to,
|
||||||
from: {
|
from: {
|
||||||
email: senderEmail,
|
email: senderEmail,
|
||||||
name: sender,
|
name: sender || SENDER_NAME,
|
||||||
},
|
},
|
||||||
subject: emailContent.emailSubject,
|
subject: emailContent.emailSubject,
|
||||||
html: emailContent.emailBody,
|
html: emailContent.emailBody,
|
||||||
|
@ -289,7 +312,7 @@ export const scheduleEmailReminder = async (
|
||||||
// TODO: Maybe don't await for this?
|
// TODO: Maybe don't await for this?
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error sending Email");
|
log.error("Error sending Email");
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
|
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
|
||||||
|
@ -311,32 +334,59 @@ export const scheduleEmailReminder = async (
|
||||||
},
|
},
|
||||||
triggerEvent
|
triggerEvent
|
||||||
);
|
);
|
||||||
|
if (!isMandatoryReminder) {
|
||||||
|
await prisma.workflowReminder.create({
|
||||||
|
data: {
|
||||||
|
bookingUid: uid,
|
||||||
|
workflowStepId: workflowStepId,
|
||||||
|
method: WorkflowMethods.EMAIL,
|
||||||
|
scheduledDate: scheduledDate.toDate(),
|
||||||
|
scheduled: true,
|
||||||
|
referenceId: batchId,
|
||||||
|
seatReferenceId: seatReferenceUid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await prisma.workflowReminder.create({
|
||||||
|
data: {
|
||||||
|
bookingUid: uid,
|
||||||
|
method: WorkflowMethods.EMAIL,
|
||||||
|
scheduledDate: scheduledDate.toDate(),
|
||||||
|
scheduled: true,
|
||||||
|
referenceId: batchId,
|
||||||
|
seatReferenceId: seatReferenceUid,
|
||||||
|
isMandatoryReminder: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error scheduling email with error ${error}`);
|
||||||
|
}
|
||||||
|
} else if (scheduledDate.isAfter(currentDate.add(72, "hour"))) {
|
||||||
|
// Write to DB and send to CRON if scheduled reminder date is past 72 hours
|
||||||
|
if (!isMandatoryReminder) {
|
||||||
await prisma.workflowReminder.create({
|
await prisma.workflowReminder.create({
|
||||||
data: {
|
data: {
|
||||||
bookingUid: uid,
|
bookingUid: uid,
|
||||||
workflowStepId: workflowStepId,
|
workflowStepId: workflowStepId,
|
||||||
method: WorkflowMethods.EMAIL,
|
method: WorkflowMethods.EMAIL,
|
||||||
scheduledDate: scheduledDate.toDate(),
|
scheduledDate: scheduledDate.toDate(),
|
||||||
scheduled: true,
|
scheduled: false,
|
||||||
referenceId: batchId,
|
|
||||||
seatReferenceId: seatReferenceUid,
|
seatReferenceId: seatReferenceUid,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} else {
|
||||||
console.log(`Error scheduling email with error ${error}`);
|
await prisma.workflowReminder.create({
|
||||||
|
data: {
|
||||||
|
bookingUid: uid,
|
||||||
|
method: WorkflowMethods.EMAIL,
|
||||||
|
scheduledDate: scheduledDate.toDate(),
|
||||||
|
scheduled: false,
|
||||||
|
seatReferenceId: seatReferenceUid,
|
||||||
|
isMandatoryReminder: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (scheduledDate.isAfter(currentDate.add(72, "hour"))) {
|
|
||||||
// Write to DB and send to CRON if scheduled reminder date is past 72 hours
|
|
||||||
await prisma.workflowReminder.create({
|
|
||||||
data: {
|
|
||||||
bookingUid: uid,
|
|
||||||
workflowStepId: workflowStepId,
|
|
||||||
method: WorkflowMethods.EMAIL,
|
|
||||||
scheduledDate: scheduledDate.toDate(),
|
|
||||||
scheduled: false,
|
|
||||||
seatReferenceId: seatReferenceUid,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -362,6 +412,6 @@ export const deleteScheduledEmailReminder = async (reminderId: number, reference
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Error canceling reminder with error ${error}`);
|
log.error(`Error canceling reminder with error ${error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import type { Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@prisma/client";
|
import type { Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@prisma/client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
isSMSAction,
|
||||||
isTextMessageToAttendeeAction,
|
isTextMessageToAttendeeAction,
|
||||||
isWhatsappAction,
|
isWhatsappAction,
|
||||||
} from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
|
} from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
|
||||||
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
|
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||||
import { WorkflowActions, WorkflowMethods, WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
import { WorkflowActions, WorkflowMethods, WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import { deleteScheduledEmailReminder, scheduleEmailReminder } from "./emailReminderManager";
|
import { deleteScheduledEmailReminder, scheduleEmailReminder } from "./emailReminderManager";
|
||||||
|
import type { ScheduleTextReminderAction } from "./smsReminderManager";
|
||||||
import { deleteScheduledSMSReminder, scheduleSMSReminder } from "./smsReminderManager";
|
import { deleteScheduledSMSReminder, scheduleSMSReminder } from "./smsReminderManager";
|
||||||
import { deleteScheduledWhatsappReminder, scheduleWhatsappReminder } from "./whatsappReminderManager";
|
import { deleteScheduledWhatsappReminder, scheduleWhatsappReminder } from "./whatsappReminderManager";
|
||||||
|
|
||||||
|
@ -51,26 +53,26 @@ const processWorkflowStep = async (
|
||||||
) => {
|
) => {
|
||||||
if (isTextMessageToAttendeeAction(step.action) && !eventTypeRequiresConfirmation) return;
|
if (isTextMessageToAttendeeAction(step.action) && !eventTypeRequiresConfirmation) return;
|
||||||
|
|
||||||
if (step.action === WorkflowActions.SMS_ATTENDEE || step.action === WorkflowActions.SMS_NUMBER) {
|
if (isSMSAction(step.action)) {
|
||||||
const sendTo = step.action === WorkflowActions.SMS_ATTENDEE ? smsReminderNumber : step.sendTo;
|
const sendTo = step.action === WorkflowActions.SMS_ATTENDEE ? smsReminderNumber : step.sendTo;
|
||||||
await scheduleSMSReminder(
|
await scheduleSMSReminder({
|
||||||
evt,
|
evt,
|
||||||
sendTo,
|
reminderPhone: sendTo,
|
||||||
workflow.trigger,
|
triggerEvent: workflow.trigger,
|
||||||
step.action,
|
action: step.action as ScheduleTextReminderAction,
|
||||||
{
|
timeSpan: {
|
||||||
time: workflow.time,
|
time: workflow.time,
|
||||||
timeUnit: workflow.timeUnit,
|
timeUnit: workflow.timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id,
|
workflowStepId: step.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
step.sender || SENDER_ID,
|
sender: step.sender,
|
||||||
workflow.userId,
|
userId: workflow.userId,
|
||||||
workflow.teamId,
|
teamId: workflow.teamId,
|
||||||
step.numberVerificationPending,
|
isVerificationPending: step.numberVerificationPending,
|
||||||
seatReferenceUid
|
seatReferenceUid,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.EMAIL_ATTENDEE || step.action === WorkflowActions.EMAIL_HOST) {
|
} else if (step.action === WorkflowActions.EMAIL_ATTENDEE || step.action === WorkflowActions.EMAIL_HOST) {
|
||||||
let sendTo: string[] = [];
|
let sendTo: string[] = [];
|
||||||
|
|
||||||
|
@ -88,43 +90,43 @@ const processWorkflowStep = async (
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await scheduleEmailReminder(
|
await scheduleEmailReminder({
|
||||||
evt,
|
evt,
|
||||||
workflow.trigger,
|
triggerEvent: workflow.trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time: workflow.time,
|
time: workflow.time,
|
||||||
timeUnit: workflow.timeUnit,
|
timeUnit: workflow.timeUnit,
|
||||||
},
|
},
|
||||||
sendTo,
|
sendTo,
|
||||||
step.emailSubject || "",
|
emailSubject: step.emailSubject || "",
|
||||||
step.reminderBody || "",
|
emailBody: step.reminderBody || "",
|
||||||
step.id,
|
template: step.template,
|
||||||
step.template,
|
sender: step.sender || SENDER_NAME,
|
||||||
step.sender || SENDER_NAME,
|
workflowStepId: step.id,
|
||||||
hideBranding,
|
hideBranding,
|
||||||
seatReferenceUid,
|
seatReferenceUid,
|
||||||
step.includeCalendarEvent
|
includeCalendarEvent: step.includeCalendarEvent,
|
||||||
);
|
});
|
||||||
} else if (isWhatsappAction(step.action)) {
|
} else if (isWhatsappAction(step.action)) {
|
||||||
const sendTo = step.action === WorkflowActions.WHATSAPP_ATTENDEE ? smsReminderNumber : step.sendTo;
|
const sendTo = step.action === WorkflowActions.WHATSAPP_ATTENDEE ? smsReminderNumber : step.sendTo;
|
||||||
await scheduleWhatsappReminder(
|
await scheduleWhatsappReminder({
|
||||||
evt,
|
evt,
|
||||||
sendTo,
|
reminderPhone: sendTo,
|
||||||
workflow.trigger,
|
triggerEvent: workflow.trigger,
|
||||||
step.action,
|
action: step.action as ScheduleTextReminderAction,
|
||||||
{
|
timeSpan: {
|
||||||
time: workflow.time,
|
time: workflow.time,
|
||||||
timeUnit: workflow.timeUnit,
|
timeUnit: workflow.timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id,
|
workflowStepId: step.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
workflow.userId,
|
userId: workflow.userId,
|
||||||
workflow.teamId,
|
teamId: workflow.teamId,
|
||||||
step.numberVerificationPending,
|
isVerificationPending: step.numberVerificationPending,
|
||||||
seatReferenceUid
|
seatReferenceUid,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,7 +166,6 @@ export const scheduleWorkflowReminders = async (args: ScheduleWorkflowRemindersA
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const step of workflow.steps) {
|
for (const step of workflow.steps) {
|
||||||
await processWorkflowStep(workflow, step, {
|
await processWorkflowStep(workflow, step, {
|
||||||
calendarEvent: evt,
|
calendarEvent: evt,
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import type { Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@prisma/client";
|
||||||
|
|
||||||
|
import type { getEventTypesFromDB } from "@calcom/features/bookings/lib/handleNewBooking";
|
||||||
|
import { scheduleEmailReminder } from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager";
|
||||||
|
import type { BookingInfo } from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
|
||||||
|
import type { getDefaultEvent } from "@calcom/lib/defaultEvents";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import { WorkflowTriggerEvents, TimeUnit, WorkflowActions, WorkflowTemplates } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
|
const log = logger.getSubLogger({ prefix: ["[scheduleMandatoryReminder]"] });
|
||||||
|
|
||||||
|
export type NewBookingEventType =
|
||||||
|
| Awaited<ReturnType<typeof getDefaultEvent>>
|
||||||
|
| Awaited<ReturnType<typeof getEventTypesFromDB>>;
|
||||||
|
|
||||||
|
export async function scheduleMandatoryReminder(
|
||||||
|
evt: BookingInfo,
|
||||||
|
workflows: (WorkflowsOnEventTypes & {
|
||||||
|
workflow: Workflow & {
|
||||||
|
steps: WorkflowStep[];
|
||||||
|
};
|
||||||
|
})[],
|
||||||
|
requiresConfirmation: boolean,
|
||||||
|
hideBranding: boolean,
|
||||||
|
seatReferenceUid: string | undefined
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const hasExistingWorkflow = workflows.some((workflow) => {
|
||||||
|
return (
|
||||||
|
workflow.workflow?.trigger === WorkflowTriggerEvents.BEFORE_EVENT &&
|
||||||
|
((workflow.workflow.time !== null &&
|
||||||
|
workflow.workflow.time <= 12 &&
|
||||||
|
workflow.workflow?.timeUnit === TimeUnit.HOUR) ||
|
||||||
|
(workflow.workflow.time !== null &&
|
||||||
|
workflow.workflow.time <= 720 &&
|
||||||
|
workflow.workflow?.timeUnit === TimeUnit.MINUTE)) &&
|
||||||
|
workflow.workflow?.steps.some((step) => step?.action === WorkflowActions.EMAIL_ATTENDEE)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!hasExistingWorkflow &&
|
||||||
|
evt.attendees.some((attendee) => attendee.email.includes("@gmail.com")) &&
|
||||||
|
!requiresConfirmation
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const filteredAttendees =
|
||||||
|
evt.attendees?.filter((attendee) => attendee.email.includes("@gmail.com")) || [];
|
||||||
|
|
||||||
|
await scheduleEmailReminder({
|
||||||
|
evt,
|
||||||
|
triggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
|
||||||
|
action: WorkflowActions.EMAIL_ATTENDEE,
|
||||||
|
timeSpan: {
|
||||||
|
time: 1,
|
||||||
|
timeUnit: TimeUnit.HOUR,
|
||||||
|
},
|
||||||
|
sendTo: filteredAttendees,
|
||||||
|
template: WorkflowTemplates.REMINDER,
|
||||||
|
hideBranding,
|
||||||
|
seatReferenceUid,
|
||||||
|
includeCalendarEvent: false,
|
||||||
|
isMandatoryReminder: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import { SENDER_ID } from "@calcom/lib/constants";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import type { TimeFormat } from "@calcom/lib/timeFormat";
|
import type { TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import type { Prisma } from "@calcom/prisma/client";
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
import type { TimeUnit } from "@calcom/prisma/enums";
|
|
||||||
import { WorkflowTemplates, WorkflowActions, WorkflowMethods } from "@calcom/prisma/enums";
|
import { WorkflowTemplates, WorkflowActions, WorkflowMethods } from "@calcom/prisma/enums";
|
||||||
import { WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
import { WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||||
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import type { CalEventResponses, RecurringEvent } from "@calcom/types/Calendar";
|
import type { CalEventResponses, RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import { getSenderId } from "../alphanumericSenderIdSupport";
|
import { getSenderId } from "../alphanumericSenderIdSupport";
|
||||||
|
import type { ScheduleReminderArgs } from "./emailReminderManager";
|
||||||
import * as twilio from "./smsProviders/twilioProvider";
|
import * as twilio from "./smsProviders/twilioProvider";
|
||||||
import type { VariablesType } from "./templates/customTemplate";
|
import type { VariablesType } from "./templates/customTemplate";
|
||||||
import customTemplate from "./templates/customTemplate";
|
import customTemplate from "./templates/customTemplate";
|
||||||
|
@ -55,33 +56,42 @@ export type BookingInfo = {
|
||||||
metadata?: Prisma.JsonValue;
|
metadata?: Prisma.JsonValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ScheduleSMSReminderAction = Extract<WorkflowActions, "SMS_ATTENDEE" | "SMS_NUMBER">;
|
export type ScheduleTextReminderAction = Extract<
|
||||||
|
WorkflowActions,
|
||||||
|
"SMS_ATTENDEE" | "SMS_NUMBER" | "WHATSAPP_ATTENDEE" | "WHATSAPP_NUMBER"
|
||||||
|
>;
|
||||||
|
export interface ScheduleTextReminderArgs extends ScheduleReminderArgs {
|
||||||
|
reminderPhone: string | null;
|
||||||
|
message: string;
|
||||||
|
action: ScheduleTextReminderAction;
|
||||||
|
userId?: number | null;
|
||||||
|
teamId?: number | null;
|
||||||
|
isVerificationPending?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const scheduleSMSReminder = async (
|
export const scheduleSMSReminder = async (args: ScheduleTextReminderArgs) => {
|
||||||
evt: BookingInfo,
|
const {
|
||||||
reminderPhone: string | null,
|
evt,
|
||||||
triggerEvent: WorkflowTriggerEvents,
|
reminderPhone,
|
||||||
action: ScheduleSMSReminderAction,
|
triggerEvent,
|
||||||
timeSpan: {
|
action,
|
||||||
time: number | null;
|
timeSpan,
|
||||||
timeUnit: TimeUnit | null;
|
message = "",
|
||||||
},
|
workflowStepId,
|
||||||
message: string,
|
template,
|
||||||
workflowStepId: number,
|
sender,
|
||||||
template: WorkflowTemplates,
|
userId,
|
||||||
sender: string,
|
teamId,
|
||||||
userId?: number | null,
|
isVerificationPending = false,
|
||||||
teamId?: number | null,
|
seatReferenceUid,
|
||||||
isVerificationPending = false,
|
} = args;
|
||||||
seatReferenceUid?: string
|
|
||||||
) => {
|
|
||||||
const { startTime, endTime } = evt;
|
const { startTime, endTime } = evt;
|
||||||
const uid = evt.uid as string;
|
const uid = evt.uid as string;
|
||||||
const currentDate = dayjs();
|
const currentDate = dayjs();
|
||||||
const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
|
const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
|
||||||
let scheduledDate = null;
|
let scheduledDate = null;
|
||||||
|
|
||||||
const senderID = getSenderId(reminderPhone, sender);
|
const senderID = getSenderId(reminderPhone, sender || SENDER_ID);
|
||||||
|
|
||||||
//SMS_ATTENDEE action does not need to be verified
|
//SMS_ATTENDEE action does not need to be verified
|
||||||
//isVerificationPending is from all already existing workflows (once they edit their workflow, they will also have to verify the number)
|
//isVerificationPending is from all already existing workflows (once they edit their workflow, they will also have to verify the number)
|
||||||
|
@ -126,7 +136,9 @@ export const scheduleSMSReminder = async (
|
||||||
? attendeeToBeUsedInSMS.language?.locale
|
? attendeeToBeUsedInSMS.language?.locale
|
||||||
: evt.organizer.language.locale;
|
: evt.organizer.language.locale;
|
||||||
|
|
||||||
if (message) {
|
let smsMessage = message;
|
||||||
|
|
||||||
|
if (smsMessage) {
|
||||||
const variables: VariablesType = {
|
const variables: VariablesType = {
|
||||||
eventName: evt.title,
|
eventName: evt.title,
|
||||||
organizerName: evt.organizer.name,
|
organizerName: evt.organizer.name,
|
||||||
|
@ -144,10 +156,10 @@ export const scheduleSMSReminder = async (
|
||||||
cancelLink: `/booking/${evt.uid}?cancel=true`,
|
cancelLink: `/booking/${evt.uid}?cancel=true`,
|
||||||
rescheduleLink: `/${evt.organizer.username}/${evt.eventType.slug}?rescheduleUid=${evt.uid}`,
|
rescheduleLink: `/${evt.organizer.username}/${evt.eventType.slug}?rescheduleUid=${evt.uid}`,
|
||||||
};
|
};
|
||||||
const customMessage = customTemplate(message, variables, locale, evt.organizer.timeFormat);
|
const customMessage = customTemplate(smsMessage, variables, locale, evt.organizer.timeFormat);
|
||||||
message = customMessage.text;
|
smsMessage = customMessage.text;
|
||||||
} else if (template === WorkflowTemplates.REMINDER) {
|
} else if (template === WorkflowTemplates.REMINDER) {
|
||||||
message =
|
smsMessage =
|
||||||
smsReminderTemplate(
|
smsReminderTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -161,9 +173,9 @@ export const scheduleSMSReminder = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows debugging generated email content without waiting for sendgrid to send emails
|
// Allows debugging generated email content without waiting for sendgrid to send emails
|
||||||
log.debug(`Sending sms for trigger ${triggerEvent}`, message);
|
log.debug(`Sending sms for trigger ${triggerEvent}`, smsMessage);
|
||||||
|
|
||||||
if (message.length > 0 && reminderPhone && isNumberVerified) {
|
if (smsMessage.length > 0 && reminderPhone && isNumberVerified) {
|
||||||
//send SMS when event is booked/cancelled/rescheduled
|
//send SMS when event is booked/cancelled/rescheduled
|
||||||
if (
|
if (
|
||||||
triggerEvent === WorkflowTriggerEvents.NEW_EVENT ||
|
triggerEvent === WorkflowTriggerEvents.NEW_EVENT ||
|
||||||
|
@ -171,9 +183,9 @@ export const scheduleSMSReminder = async (
|
||||||
triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT
|
triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await twilio.sendSMS(reminderPhone, message, senderID);
|
await twilio.sendSMS(reminderPhone, smsMessage, senderID);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Error sending SMS with error ${error}`);
|
log.error(`Error sending SMS with error ${error}`);
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
|
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
|
||||||
|
@ -188,7 +200,7 @@ export const scheduleSMSReminder = async (
|
||||||
try {
|
try {
|
||||||
const scheduledSMS = await twilio.scheduleSMS(
|
const scheduledSMS = await twilio.scheduleSMS(
|
||||||
reminderPhone,
|
reminderPhone,
|
||||||
message,
|
smsMessage,
|
||||||
scheduledDate.toDate(),
|
scheduledDate.toDate(),
|
||||||
senderID
|
senderID
|
||||||
);
|
);
|
||||||
|
@ -205,7 +217,7 @@ export const scheduleSMSReminder = async (
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Error scheduling SMS with error ${error}`);
|
log.error(`Error scheduling SMS with error ${error}`);
|
||||||
}
|
}
|
||||||
} else if (scheduledDate.isAfter(currentDate.add(7, "day"))) {
|
} else if (scheduledDate.isAfter(currentDate.add(7, "day"))) {
|
||||||
// Write to DB and send to CRON if scheduled reminder date is past 7 days
|
// Write to DB and send to CRON if scheduled reminder date is past 7 days
|
||||||
|
@ -236,6 +248,6 @@ export const deleteScheduledSMSReminder = async (reminderId: number, referenceId
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Error canceling reminder with error ${error}`);
|
log.error(`Error canceling reminder with error ${error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import type { TimeUnit } from "@prisma/client";
|
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
@ -11,7 +9,7 @@ import {
|
||||||
} from "@calcom/prisma/enums";
|
} from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import * as twilio from "./smsProviders/twilioProvider";
|
import * as twilio from "./smsProviders/twilioProvider";
|
||||||
import type { BookingInfo, timeUnitLowerCase } from "./smsReminderManager";
|
import type { ScheduleTextReminderArgs, timeUnitLowerCase } from "./smsReminderManager";
|
||||||
import { deleteScheduledSMSReminder } from "./smsReminderManager";
|
import { deleteScheduledSMSReminder } from "./smsReminderManager";
|
||||||
import {
|
import {
|
||||||
whatsappEventCancelledTemplate,
|
whatsappEventCancelledTemplate,
|
||||||
|
@ -22,23 +20,21 @@ import {
|
||||||
|
|
||||||
const log = logger.getSubLogger({ prefix: ["[whatsappReminderManager]"] });
|
const log = logger.getSubLogger({ prefix: ["[whatsappReminderManager]"] });
|
||||||
|
|
||||||
export const scheduleWhatsappReminder = async (
|
export const scheduleWhatsappReminder = async (args: ScheduleTextReminderArgs) => {
|
||||||
evt: BookingInfo,
|
const {
|
||||||
reminderPhone: string | null,
|
evt,
|
||||||
triggerEvent: WorkflowTriggerEvents,
|
reminderPhone,
|
||||||
action: WorkflowActions,
|
triggerEvent,
|
||||||
timeSpan: {
|
action,
|
||||||
time: number | null;
|
timeSpan,
|
||||||
timeUnit: TimeUnit | null;
|
message = "",
|
||||||
},
|
workflowStepId,
|
||||||
message: string,
|
template,
|
||||||
workflowStepId: number,
|
userId,
|
||||||
template: WorkflowTemplates,
|
teamId,
|
||||||
userId?: number | null,
|
isVerificationPending = false,
|
||||||
teamId?: number | null,
|
seatReferenceUid,
|
||||||
isVerificationPending = false,
|
} = args;
|
||||||
seatReferenceUid?: string
|
|
||||||
) => {
|
|
||||||
const { startTime, endTime } = evt;
|
const { startTime, endTime } = evt;
|
||||||
const uid = evt.uid as string;
|
const uid = evt.uid as string;
|
||||||
const currentDate = dayjs();
|
const currentDate = dayjs();
|
||||||
|
@ -72,9 +68,11 @@ export const scheduleWhatsappReminder = async (
|
||||||
const timeZone =
|
const timeZone =
|
||||||
action === WorkflowActions.WHATSAPP_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone;
|
action === WorkflowActions.WHATSAPP_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone;
|
||||||
|
|
||||||
|
let textMessage = message;
|
||||||
|
|
||||||
switch (template) {
|
switch (template) {
|
||||||
case WorkflowTemplates.REMINDER:
|
case WorkflowTemplates.REMINDER:
|
||||||
message =
|
textMessage =
|
||||||
whatsappReminderTemplate(
|
whatsappReminderTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -87,7 +85,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
) || message;
|
) || message;
|
||||||
break;
|
break;
|
||||||
case WorkflowTemplates.CANCELLED:
|
case WorkflowTemplates.CANCELLED:
|
||||||
message =
|
textMessage =
|
||||||
whatsappEventCancelledTemplate(
|
whatsappEventCancelledTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -100,7 +98,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
) || message;
|
) || message;
|
||||||
break;
|
break;
|
||||||
case WorkflowTemplates.RESCHEDULED:
|
case WorkflowTemplates.RESCHEDULED:
|
||||||
message =
|
textMessage =
|
||||||
whatsappEventRescheduledTemplate(
|
whatsappEventRescheduledTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -113,7 +111,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
) || message;
|
) || message;
|
||||||
break;
|
break;
|
||||||
case WorkflowTemplates.COMPLETED:
|
case WorkflowTemplates.COMPLETED:
|
||||||
message =
|
textMessage =
|
||||||
whatsappEventCompletedTemplate(
|
whatsappEventCompletedTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -126,7 +124,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
) || message;
|
) || message;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
message =
|
textMessage =
|
||||||
whatsappReminderTemplate(
|
whatsappReminderTemplate(
|
||||||
false,
|
false,
|
||||||
action,
|
action,
|
||||||
|
@ -140,8 +138,8 @@ export const scheduleWhatsappReminder = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows debugging generated whatsapp content without waiting for twilio to send whatsapp messages
|
// Allows debugging generated whatsapp content without waiting for twilio to send whatsapp messages
|
||||||
log.debug(`Sending Whatsapp for trigger ${triggerEvent}`, message);
|
log.debug(`Sending Whatsapp for trigger ${triggerEvent}`, textMessage);
|
||||||
if (message.length > 0 && reminderPhone && isNumberVerified) {
|
if (textMessage.length > 0 && reminderPhone && isNumberVerified) {
|
||||||
//send WHATSAPP when event is booked/cancelled/rescheduled
|
//send WHATSAPP when event is booked/cancelled/rescheduled
|
||||||
if (
|
if (
|
||||||
triggerEvent === WorkflowTriggerEvents.NEW_EVENT ||
|
triggerEvent === WorkflowTriggerEvents.NEW_EVENT ||
|
||||||
|
@ -149,7 +147,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT
|
triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await twilio.sendSMS(reminderPhone, message, "", true);
|
await twilio.sendSMS(reminderPhone, textMessage, "", true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Error sending WHATSAPP with error ${error}`);
|
console.log(`Error sending WHATSAPP with error ${error}`);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +164,7 @@ export const scheduleWhatsappReminder = async (
|
||||||
try {
|
try {
|
||||||
const scheduledWHATSAPP = await twilio.scheduleSMS(
|
const scheduledWHATSAPP = await twilio.scheduleSMS(
|
||||||
reminderPhone,
|
reminderPhone,
|
||||||
message,
|
textMessage,
|
||||||
scheduledDate.toDate(),
|
scheduledDate.toDate(),
|
||||||
"",
|
"",
|
||||||
true
|
true
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "WorkflowReminder" ADD COLUMN "isMandatoryReminder" BOOLEAN DEFAULT false;
|
|
@ -823,17 +823,18 @@ enum TimeUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
model WorkflowReminder {
|
model WorkflowReminder {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
bookingUid String?
|
bookingUid String?
|
||||||
booking Booking? @relation(fields: [bookingUid], references: [uid])
|
booking Booking? @relation(fields: [bookingUid], references: [uid])
|
||||||
method WorkflowMethods
|
method WorkflowMethods
|
||||||
scheduledDate DateTime
|
scheduledDate DateTime
|
||||||
referenceId String? @unique
|
referenceId String? @unique
|
||||||
scheduled Boolean
|
scheduled Boolean
|
||||||
workflowStepId Int?
|
workflowStepId Int?
|
||||||
workflowStep WorkflowStep? @relation(fields: [workflowStepId], references: [id])
|
workflowStep WorkflowStep? @relation(fields: [workflowStepId], references: [id])
|
||||||
cancelled Boolean?
|
cancelled Boolean?
|
||||||
seatReferenceId String?
|
seatReferenceId String?
|
||||||
|
isMandatoryReminder Boolean? @default(false)
|
||||||
|
|
||||||
@@index([bookingUid])
|
@@index([bookingUid])
|
||||||
@@index([workflowStepId])
|
@@index([workflowStepId])
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
deleteScheduledWhatsappReminder,
|
deleteScheduledWhatsappReminder,
|
||||||
scheduleWhatsappReminder,
|
scheduleWhatsappReminder,
|
||||||
} from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
|
} from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
|
||||||
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
|
|
||||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
import { BookingStatus } from "@calcom/prisma/client";
|
import { BookingStatus } from "@calcom/prisma/client";
|
||||||
|
@ -197,54 +196,54 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await scheduleEmailReminder(
|
await scheduleEmailReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
eventTypeWorkflow.trigger,
|
triggerEvent: eventTypeWorkflow.trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time: eventTypeWorkflow.time,
|
time: eventTypeWorkflow.time,
|
||||||
timeUnit: eventTypeWorkflow.timeUnit,
|
timeUnit: eventTypeWorkflow.timeUnit,
|
||||||
},
|
},
|
||||||
sendTo,
|
sendTo,
|
||||||
step.emailSubject || "",
|
emailSubject: step.emailSubject || "",
|
||||||
step.reminderBody || "",
|
emailBody: step.reminderBody || "",
|
||||||
step.id,
|
template: step.template,
|
||||||
step.template,
|
sender: step.sender,
|
||||||
step.sender || SENDER_NAME
|
workflowStepId: step.id,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.SMS_NUMBER && step.sendTo) {
|
} else if (step.action === WorkflowActions.SMS_NUMBER && step.sendTo) {
|
||||||
await scheduleSMSReminder(
|
await scheduleSMSReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo,
|
reminderPhone: step.sendTo,
|
||||||
eventTypeWorkflow.trigger,
|
triggerEvent: eventTypeWorkflow.trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time: eventTypeWorkflow.time,
|
time: eventTypeWorkflow.time,
|
||||||
timeUnit: eventTypeWorkflow.timeUnit,
|
timeUnit: eventTypeWorkflow.timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id,
|
workflowStepId: step.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
step.sender || SENDER_ID,
|
sender: step.sender,
|
||||||
booking.userId,
|
userId: booking.userId,
|
||||||
eventTypeWorkflow.teamId
|
teamId: eventTypeWorkflow.teamId,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER && step.sendTo) {
|
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER && step.sendTo) {
|
||||||
await scheduleWhatsappReminder(
|
await scheduleWhatsappReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo,
|
reminderPhone: step.sendTo,
|
||||||
eventTypeWorkflow.trigger,
|
triggerEvent: eventTypeWorkflow.trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time: eventTypeWorkflow.time,
|
time: eventTypeWorkflow.time,
|
||||||
timeUnit: eventTypeWorkflow.timeUnit,
|
timeUnit: eventTypeWorkflow.timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id,
|
workflowStepId: step.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
booking.userId,
|
userId: booking.userId,
|
||||||
eventTypeWorkflow.teamId
|
teamId: eventTypeWorkflow.teamId,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
deleteScheduledWhatsappReminder,
|
deleteScheduledWhatsappReminder,
|
||||||
scheduleWhatsappReminder,
|
scheduleWhatsappReminder,
|
||||||
} from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
|
} from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
|
||||||
import { IS_SELF_HOSTED, SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
|
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
|
@ -315,54 +315,54 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||||
sendTo = step.sendTo || "";*/
|
sendTo = step.sendTo || "";*/
|
||||||
}
|
}
|
||||||
|
|
||||||
await scheduleEmailReminder(
|
await scheduleEmailReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
sendTo,
|
sendTo,
|
||||||
step.emailSubject || "",
|
emailSubject: step.emailSubject || "",
|
||||||
step.reminderBody || "",
|
emailBody: step.reminderBody || "",
|
||||||
step.id,
|
template: step.template,
|
||||||
step.template,
|
sender: step.senderName,
|
||||||
step.senderName || SENDER_NAME
|
workflowStepId: step.id,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.SMS_NUMBER) {
|
} else if (step.action === WorkflowActions.SMS_NUMBER) {
|
||||||
await scheduleSMSReminder(
|
await scheduleSMSReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo || "",
|
reminderPhone: step.sendTo || "",
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id,
|
workflowStepId: step.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
step.sender || SENDER_ID,
|
sender: step.sender,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER) {
|
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER) {
|
||||||
await scheduleWhatsappReminder(
|
await scheduleWhatsappReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo || "",
|
reminderPhone: step.sendTo || "",
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
step.id || 0,
|
workflowStepId: step.id || 0,
|
||||||
step.template,
|
template: step.template,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Promise.all(promiseScheduleReminders);
|
await Promise.all(promiseScheduleReminders);
|
||||||
|
@ -552,54 +552,54 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||||
sendTo = newStep.sendTo || "";*/
|
sendTo = newStep.sendTo || "";*/
|
||||||
}
|
}
|
||||||
|
|
||||||
await scheduleEmailReminder(
|
await scheduleEmailReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
newStep.action,
|
action: newStep.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
sendTo,
|
sendTo,
|
||||||
newStep.emailSubject || "",
|
emailSubject: newStep.emailSubject || "",
|
||||||
newStep.reminderBody || "",
|
emailBody: newStep.reminderBody || "",
|
||||||
newStep.id,
|
template: newStep.template,
|
||||||
newStep.template,
|
sender: newStep.senderName,
|
||||||
newStep.senderName || SENDER_NAME
|
workflowStepId: newStep.id,
|
||||||
);
|
});
|
||||||
} else if (newStep.action === WorkflowActions.SMS_NUMBER) {
|
} else if (newStep.action === WorkflowActions.SMS_NUMBER) {
|
||||||
await scheduleSMSReminder(
|
await scheduleSMSReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
newStep.sendTo || "",
|
reminderPhone: newStep.sendTo || "",
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
newStep.action,
|
action: newStep.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
newStep.reminderBody || "",
|
message: newStep.reminderBody || "",
|
||||||
newStep.id || 0,
|
workflowStepId: newStep.id || 0,
|
||||||
newStep.template,
|
template: newStep.template,
|
||||||
newStep.sender || SENDER_ID,
|
sender: newStep.sender,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
} else if (newStep.action === WorkflowActions.WHATSAPP_NUMBER) {
|
} else if (newStep.action === WorkflowActions.WHATSAPP_NUMBER) {
|
||||||
await scheduleWhatsappReminder(
|
await scheduleWhatsappReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
newStep.sendTo || "",
|
reminderPhone: newStep.sendTo || "",
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
newStep.action,
|
action: newStep.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
newStep.reminderBody || "",
|
message: newStep.reminderBody || "",
|
||||||
newStep.id || 0,
|
workflowStepId: newStep.id || 0,
|
||||||
newStep.template,
|
template: newStep.template,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Promise.all(promiseScheduleReminders);
|
await Promise.all(promiseScheduleReminders);
|
||||||
|
@ -703,54 +703,54 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||||
sendTo = step.sendTo || "";*/
|
sendTo = step.sendTo || "";*/
|
||||||
}
|
}
|
||||||
|
|
||||||
await scheduleEmailReminder(
|
await scheduleEmailReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
sendTo,
|
sendTo,
|
||||||
step.emailSubject || "",
|
emailSubject: step.emailSubject || "",
|
||||||
step.reminderBody || "",
|
emailBody: step.reminderBody || "",
|
||||||
createdStep.id,
|
template: step.template,
|
||||||
step.template,
|
sender: step.senderName,
|
||||||
step.senderName || SENDER_NAME
|
workflowStepId: createdStep.id,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.SMS_NUMBER && step.sendTo) {
|
} else if (step.action === WorkflowActions.SMS_NUMBER && step.sendTo) {
|
||||||
await scheduleSMSReminder(
|
await scheduleSMSReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo,
|
reminderPhone: step.sendTo,
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
createdStep.id,
|
workflowStepId: createdStep.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
step.sender || SENDER_ID,
|
sender: step.sender,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER && step.sendTo) {
|
} else if (step.action === WorkflowActions.WHATSAPP_NUMBER && step.sendTo) {
|
||||||
await scheduleWhatsappReminder(
|
await scheduleWhatsappReminder({
|
||||||
bookingInfo,
|
evt: bookingInfo,
|
||||||
step.sendTo,
|
reminderPhone: step.sendTo,
|
||||||
trigger,
|
triggerEvent: trigger,
|
||||||
step.action,
|
action: step.action,
|
||||||
{
|
timeSpan: {
|
||||||
time,
|
time,
|
||||||
timeUnit,
|
timeUnit,
|
||||||
},
|
},
|
||||||
step.reminderBody || "",
|
message: step.reminderBody || "",
|
||||||
createdStep.id,
|
workflowStepId: createdStep.id,
|
||||||
step.template,
|
template: step.template,
|
||||||
user.id,
|
userId: user.id,
|
||||||
userWorkflow.teamId
|
teamId: userWorkflow.teamId,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user