trmnlc/src/sources/ics.ts

72 lines
1.8 KiB
TypeScript
Raw Normal View History

import * as ical from 'node-ical';
import type { AgendaEvent } from '../todayview';
export async function fetchICSAgenda(
urls: string[]
): Promise<AgendaEvent[]> {
const now = new Date();
const startOfDay = new Date(now);
startOfDay.setHours(0, 0, 0, 0);
const endOfDay = new Date(now);
endOfDay.setHours(23, 59, 59, 999);
const allEvents = (
await Promise.all(urls.map((url) => fetchOne(url, startOfDay, endOfDay)))
).flat();
allEvents.sort((a, b) => a.sortKey - b.sortKey);
return allEvents.map(({ sortKey, ...event }) => event);
}
interface RawAgendaEvent extends AgendaEvent {
sortKey: number;
}
async function fetchOne(
url: string,
start: Date,
end: Date
): Promise<RawAgendaEvent[]> {
const resp = await fetch(url, {
headers: [['User-Agent', 'private TRMNL fetcher']],
});
const data = await resp.text();
const parsed = Object.values(await ical.async.parseICS(data)).filter(
(a) => a?.type === 'VEVENT'
);
const events: RawAgendaEvent[] = [];
for (const event of parsed) {
for (const instance of ical.expandRecurringEvent(event, {
from: start,
to: end,
excludeExdates: true,
expandOngoing: true,
includeOverrides: true,
})) {
if (instance.isFullDay) continue;
const startTime = formatTime(instance.start);
const endTime = formatTime(instance.end);
const location = (instance.event as any).location ?? '';
events.push({
start: startTime,
end: endTime,
title: (instance.summary as string) ?? '',
where: location,
kind: '',
sortKey: instance.start.valueOf(),
});
}
}
return events;
}
function formatTime(d: Date): string {
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}