import { sendAttendeeRequestEmailAndSMS, sendOrganizerRequestEmail } from "@calcom/emails/email-manager"; import { getWebhookProducer } from "@calcom/features/di/webhooks/containers/webhook"; import { CreditService } from "@calcom/features/ee/billing/credit-service"; import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType"; import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService"; import type { Workflow } from "@calcom/features/ee/workflows/lib/types"; import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import type { Prisma } from "@calcom/prisma/client"; import { WorkflowTriggerEvents } from "@calcom/prisma/enums"; import type { EventTypeMetadata } from "@calcom/prisma/zod-utils"; import type { CalendarEvent } from "@calcom/types/Calendar"; const log = logger.getSubLogger({ prefix: ["[handleBookingRequested] book:user"] }); /** * Supposed to do whatever is needed when a booking is requested. */ export async function handleBookingRequested(args: { evt: CalendarEvent; /** When booking is from a platform/OAuth client, pass so platform webhook subscribers are notified */ oAuthClientId?: string | null; booking: { smsReminderNumber: string | null; eventType: { workflows: { workflow: Workflow; }[]; owner: { hideBranding: boolean; } | null; team?: { parentId: number | null; } | null; currency: string; hosts?: { user: { email: string; destinationCalendar?: { primaryEmail: string | null; } | null; }; }[]; description: string | null; id: number; length: number; price: number; requiresConfirmation: boolean; title: string; teamId?: number | null; metadata: Prisma.JsonValue; } | null; eventTypeId: number | null; userId: number | null; id: number; }; }) { const { evt, booking, oAuthClientId } = args; log.debug("Emails: Sending booking requested emails"); await sendOrganizerRequestEmail({ ...evt }, booking?.eventType?.metadata as EventTypeMetadata); await sendAttendeeRequestEmailAndSMS( { ...evt }, evt.attendees[0], booking?.eventType?.metadata as EventTypeMetadata ); const orgId = await getOrgIdFromMemberOrTeamId({ memberId: booking.userId, teamId: booking.eventType?.teamId, }); try { if (!evt.uid) { log.error("Cannot queue BOOKING_REQUESTED webhook: missing booking uid"); } else { try { // Keep params in sync with RegularBookingService (non-payment path) so // subscriber filtering (userId, eventTypeId, teamId, orgId, oAuthClientId) is consistent. const webhookProducer = getWebhookProducer(); await webhookProducer.queueBookingRequestedWebhook({ bookingUid: evt.uid, userId: booking.userId ?? undefined, eventTypeId: booking.eventTypeId ?? undefined, teamId: booking.eventType?.teamId ?? undefined, orgId, oAuthClientId: oAuthClientId ?? undefined, }); } catch (error) { log.error("Error queueing BOOKING_REQUESTED webhook", safeStringify(error)); } } const workflows = await getAllWorkflowsFromEventType(booking.eventType, booking.userId); if (workflows.length > 0) { const creditService = new CreditService(); await WorkflowService.scheduleWorkflowsFilteredByTriggerEvent({ workflows, smsReminderNumber: booking.smsReminderNumber, hideBranding: evt.hideBranding, calendarEvent: { ...evt, bookerUrl: evt.bookerUrl as string, eventType: { slug: evt.type, hosts: booking.eventType?.hosts, schedulingType: evt.schedulingType, }, }, triggers: [WorkflowTriggerEvents.BOOKING_REQUESTED], creditCheckFn: creditService.hasAvailableCredits.bind(creditService), }); } } catch (error) { // Silently fail log.error("Error in handleBookingRequested", safeStringify(error)); } }