Files
yourwillyourwish/lib/calendar.ts
2026-02-06 21:44:04 -06:00

176 lines
4.5 KiB
TypeScript

import { loadSystemConfig } from "./system-config";
export type CalendarEvent = {
summary: string;
description: string;
startTime: Date;
endTime: Date;
location?: string;
attendees?: string[];
};
/**
* Generate ICS file content for calendar invitation
*/
export function generateIcsFile(event: CalendarEvent): string {
const formatDate = (date: Date): string => {
return date
.toISOString()
.replace(/[-:]/g, "")
.replace(/\.\d{3}/, "");
};
const startDateStr = formatDate(event.startTime);
const endDateStr = formatDate(event.endTime);
const now = formatDate(new Date());
const uid = `${now}@estate-platform.com`;
// Escape special characters in text
const escape = (text: string) => {
return text
.replace(/\\/g, "\\\\")
.replace(/;/g, "\\;")
.replace(/,/g, "\\,")
.replace(/\n/g, "\\n");
};
let icsContent = [
"BEGIN:VCALENDAR",
"VERSION:2.0",
"PRODID:-//Estate Platform//Webinar Calendar//EN",
"CALSCALE:GREGORIAN",
"METHOD:REQUEST",
"BEGIN:VEVENT",
`UID:${uid}`,
`DTSTAMP:${now}`,
`DTSTART:${startDateStr}`,
`DTEND:${endDateStr}`,
`SUMMARY:${escape(event.summary)}`,
`DESCRIPTION:${escape(event.description)}`,
];
if (event.location) {
icsContent.push(`LOCATION:${escape(event.location)}`);
}
// Add organizer
icsContent.push("ORGANIZER;CN=Estate Platform:mailto:noreply@estate-platform.com");
// Add attendees
if (event.attendees && event.attendees.length > 0) {
event.attendees.forEach((email) => {
icsContent.push(
`ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:${email}`
);
});
}
icsContent.push(
"STATUS:CONFIRMED",
"SEQUENCE:0",
"BEGIN:VALARM",
"TRIGGER:-PT15M",
"ACTION:DISPLAY",
"DESCRIPTION:Reminder: Webinar starts in 15 minutes",
"END:VALARM",
"END:VEVENT",
"END:VCALENDAR",
);
return icsContent.join("\r\n");
}
/**
* Create a Google Calendar event (if service account is configured)
*/
export async function createGoogleCalendarEvent(
event: CalendarEvent
): Promise<{ success: boolean; eventId?: string; error?: string }> {
try {
const config = await loadSystemConfig();
const calendarConfig = config.googleCalendar;
if (!calendarConfig?.enabled) {
return {
success: false,
error: "Google Calendar is not enabled",
};
}
if (
!calendarConfig.serviceAccountEmail ||
!calendarConfig.serviceAccountKey ||
!calendarConfig.calendarId
) {
return {
success: false,
error: "Google Calendar credentials are not configured",
};
}
// In a real implementation, you would use the googleapis library
// to create the event using service account credentials
// For now, we'll just return a mock success
// TODO: Implement actual Google Calendar API integration
console.log("[CALENDAR] Would create event:", event);
return {
success: true,
eventId: `mock-event-${Date.now()}`,
};
} catch (error) {
console.error("[CALENDAR] Error creating event:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Failed to create calendar event",
};
}
}
/**
* Helper to calculate end time based on duration in minutes
*/
export function calculateEndTime(startTime: Date, durationMinutes: number): Date {
return new Date(startTime.getTime() + durationMinutes * 60 * 1000);
}
/**
* Create a calendar event for a webinar registration
*/
export async function createWebinarCalendarEvent(
webinar: {
title: string;
description: string;
startAt: Date;
duration: number;
meetingInfo: any;
},
attendeeEmail: string
): Promise<{ icsContent: string; googleEventId?: string }> {
const endTime = calculateEndTime(webinar.startAt, webinar.duration);
const meetingLink = webinar.meetingInfo?.meetingLink || "TBD";
const description = `${webinar.description}\n\nJoin Link: ${meetingLink}`;
const event: CalendarEvent = {
summary: webinar.title,
description,
startTime: webinar.startAt,
endTime,
location: meetingLink,
attendees: [attendeeEmail],
};
// Generate ICS file
const icsContent = generateIcsFile(event);
// Optionally create Google Calendar event
const googleResult = await createGoogleCalendarEvent(event);
return {
icsContent,
googleEventId: googleResult.success ? googleResult.eventId : undefined,
};
}