function safe(read, fallback) { try { const value = read(); return value === null || value === undefined ? fallback : value; } catch (_) { return fallback; } } function participantText(participant) { const name = String(safe(() => participant.displayName(), "")).trim(); const email = String(safe(() => participant.email(), "")).trim(); if (!name) { return email; } if (!email || name.toLowerCase() === email.toLowerCase()) { return name; } return `${name} <${email}>`; } function run(argv) { if (argv.length !== 2) { throw new Error("Usage: calendar-events.js "); } const from = new Date(argv[0]); const to = new Date(argv[1]); const calendar = Application("Calendar"); const result = []; for (const sourceCalendar of calendar.calendars()) { const events = sourceCalendar.events.whose({ _and: [ { startDate: { _lessThan: to } }, { endDate: { _greaterThan: from } } ] })(); for (const event of events) { const start = event.startDate(); const end = event.endDate(); const attendees = safe(() => event.attendees(), []) .map(participantText) .filter(value => value.length > 0); const organizer = String(safe(() => event.organizer(), "")).trim(); if (organizer && !attendees.includes(organizer)) { attendees.unshift(organizer); } result.push({ id: String(safe(() => event.uid(), `${start.toISOString()}|${event.summary()}`)), subject: String(safe(() => event.summary(), "")), start: start.toISOString(), end: end.toISOString(), location: String(safe(() => event.location(), "")), notes: String(safe(() => event.description(), "")), url: String(safe(() => event.url(), "")), attendees: attendees, isCanceled: String(safe(() => event.status(), "")) .toLowerCase() .includes("cancel") }); } } result.sort((left, right) => left.start.localeCompare(right.start)); return JSON.stringify(result); }