mirror of
https://github.com/hanzoai/calendar.git
synced 2026-09-24 22:42:24 +00:00
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main: - Rebrand Cal.com to Cal.diy across the entire codebase - Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions - Switch license from AGPL-3.0 to MIT - Remove docs/ directory (migrated to Nextra at cal.diy) - Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc. - Clean up .env.example for self-hosted Cal.diy - Update Docker image references to calcom/cal.diy - Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork - Add PR welcome bot for Cal.diy contributors - Fix API v2 breaking changes oasdiff ignore entries - Replace Blacksmith CI runners with default GitHub Actions 3893 files changed, 20789 insertions(+), 411020 deletions(-) Co-Authored-By: [email protected] <[email protected]> * refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * rip out org related comments in api v2 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
126 lines
3.7 KiB
TypeScript
126 lines
3.7 KiB
TypeScript
import type { Host } from "@calcom/features/bookings/lib/getHostsAndGuests";
|
|
import { AttendeeRepository } from "@calcom/features/bookings/repositories/AttendeeRepository";
|
|
import { prisma } from "@calcom/prisma";
|
|
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
|
import {
|
|
calculateMaxStartTime,
|
|
log,
|
|
prepareNoShowTrigger,
|
|
sendWebhookPayload,
|
|
} from "./common";
|
|
|
|
type UpdatedAttendee = {
|
|
id: number;
|
|
email: string;
|
|
name: string;
|
|
locale: string | null;
|
|
timeZone: string;
|
|
phoneNumber: string | null;
|
|
bookingId: number | null;
|
|
noShow: boolean | null;
|
|
};
|
|
|
|
const markGuestAsNoshowInBooking = async ({
|
|
bookingId,
|
|
hostsThatJoinedTheCall,
|
|
guestsThatDidntJoinTheCall,
|
|
}: {
|
|
bookingId: number;
|
|
hostsThatJoinedTheCall: Host[];
|
|
guestsThatDidntJoinTheCall?: { email: string; name: string }[];
|
|
}): Promise<{
|
|
updatedAttendees: UpdatedAttendee[] | null;
|
|
}> => {
|
|
const attendeeRepository = new AttendeeRepository(prisma);
|
|
|
|
try {
|
|
if (guestsThatDidntJoinTheCall && guestsThatDidntJoinTheCall.length > 0) {
|
|
const emailsToUpdate = guestsThatDidntJoinTheCall.map((g) => g.email);
|
|
await attendeeRepository.updateManyNoShowByBookingIdAndEmails({
|
|
where: { bookingId, emails: emailsToUpdate },
|
|
data: { noShow: true },
|
|
});
|
|
} else {
|
|
const hostsThatJoinedTheCallEmails = hostsThatJoinedTheCall.map((h) => h.email);
|
|
await attendeeRepository.updateManyNoShowByBookingIdExcludingEmails({
|
|
where: { bookingId, excludeEmails: hostsThatJoinedTheCallEmails },
|
|
data: { noShow: true },
|
|
});
|
|
}
|
|
|
|
const updatedAttendees = await attendeeRepository.findByBookingId(bookingId);
|
|
|
|
return { updatedAttendees };
|
|
} catch (err) {
|
|
log.error("Error marking guests as no show in booking", err);
|
|
return { updatedAttendees: null };
|
|
}
|
|
};
|
|
|
|
export async function triggerGuestNoShow(payload: string): Promise<void> {
|
|
const result = await prepareNoShowTrigger(payload);
|
|
if (!result) return;
|
|
const {
|
|
webhook,
|
|
booking,
|
|
hosts,
|
|
hostsThatJoinedTheCall,
|
|
didGuestJoinTheCall,
|
|
guestsThatDidntJoinTheCall,
|
|
originalRescheduledBooking,
|
|
participants,
|
|
} = result;
|
|
|
|
const hostEmails = new Set(hosts.map((h) => h.email));
|
|
|
|
const maxStartTime = calculateMaxStartTime(booking.startTime, webhook.time, webhook.timeUnit);
|
|
|
|
const requireEmailForGuests = booking.eventType?.calVideoSettings?.requireEmailForGuests ?? false;
|
|
|
|
if (requireEmailForGuests) {
|
|
if (guestsThatDidntJoinTheCall.length > 0) {
|
|
const { updatedAttendees } = await markGuestAsNoshowInBooking({
|
|
bookingId: booking.id,
|
|
hostsThatJoinedTheCall,
|
|
guestsThatDidntJoinTheCall,
|
|
});
|
|
|
|
const guests = updatedAttendees?.filter((a) => !hostEmails.has(a.email)) ?? [];
|
|
const bookingWithUpdatedData = updatedAttendees
|
|
? { ...booking, attendees: updatedAttendees, guests }
|
|
: booking;
|
|
|
|
await sendWebhookPayload(
|
|
webhook,
|
|
WebhookTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
|
bookingWithUpdatedData,
|
|
maxStartTime,
|
|
participants,
|
|
originalRescheduledBooking
|
|
);
|
|
}
|
|
} else {
|
|
if (!didGuestJoinTheCall) {
|
|
const { updatedAttendees } = await markGuestAsNoshowInBooking({
|
|
bookingId: booking.id,
|
|
hostsThatJoinedTheCall,
|
|
});
|
|
|
|
const guests = updatedAttendees?.filter((a) => !hostEmails.has(a.email)) ?? [];
|
|
|
|
const bookingWithUpdatedData = updatedAttendees
|
|
? { ...booking, attendees: updatedAttendees, guests }
|
|
: booking;
|
|
|
|
await sendWebhookPayload(
|
|
webhook,
|
|
WebhookTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
|
bookingWithUpdatedData,
|
|
maxStartTime,
|
|
participants,
|
|
originalRescheduledBooking
|
|
);
|
|
}
|
|
}
|
|
}
|