Skip to content

Commit ac886cd

Browse files
refactor: replace unmaintained 'ical-expander' with 'ical.js'
1 parent 88eb5f8 commit ac886cd

3 files changed

Lines changed: 256 additions & 261 deletions

File tree

node_helper.js

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const Log = require("logger");
22
const moment = require("moment-timezone");
3-
const IcalExpander = require("ical-expander");
3+
const ICAL = require("ical.js");
44

55
const NodeHelper = require("node_helper");
66

@@ -118,6 +118,101 @@ module.exports = NodeHelper.create({
118118
}
119119
},
120120

121+
/**
122+
* Parse iCal data and expand recurring events using ical.js
123+
* @param {string} iCalData - The iCal data string
124+
* @param {Date} startDate - Start date for event range
125+
* @param {Date} endDate - End date for event range
126+
* @param {number} maxIterations - Maximum iterations for recurring events
127+
* @returns {Object} Object containing events and occurrences arrays
128+
*/
129+
parseAndExpandEvents (iCalData, startDate, endDate, maxIterations = 1000) {
130+
let jcalData;
131+
try {
132+
jcalData = ICAL.parse(iCalData);
133+
} catch (error) {
134+
throw new Error(`Failed to parse iCal data: ${error.message}`);
135+
}
136+
137+
const comp = new ICAL.Component(jcalData);
138+
const vevents = comp.getAllSubcomponents("vevent");
139+
140+
const events = [];
141+
const occurrences = [];
142+
143+
for (const vevent of vevents) {
144+
const event = new ICAL.Event(vevent);
145+
146+
if (event.isRecurring()) {
147+
// Handle recurring events
148+
const iterator = event.iterator();
149+
let count = 0;
150+
let occurrence;
151+
152+
while ((occurrence = iterator.next()) && count < maxIterations) {
153+
const occurrenceStartDate = occurrence.toJSDate();
154+
const occurrenceEndDate = new Date(occurrenceStartDate.getTime() + event.duration.toSeconds() * 1000);
155+
156+
// Check if this occurrence falls within our date range
157+
if (occurrenceEndDate >= startDate && occurrenceStartDate <= endDate) {
158+
occurrences.push({
159+
startDate: {
160+
toJSDate: () => occurrenceStartDate
161+
},
162+
endDate: {
163+
toJSDate: () => occurrenceEndDate
164+
},
165+
item: {
166+
summary: event.summary,
167+
location: event.location,
168+
description: event.description,
169+
uid: event.uid,
170+
isRecurring: () => true,
171+
duration: event.duration
172+
},
173+
component: vevent
174+
});
175+
}
176+
177+
count += 1;
178+
179+
// Stop if we've gone past our end date
180+
if (occurrenceStartDate > endDate) {
181+
break;
182+
}
183+
}
184+
} else {
185+
// Handle single events
186+
const eventStartDate = event.startDate.toJSDate();
187+
const eventEndDate = event.endDate.toJSDate();
188+
189+
// Check if this event falls within our date range
190+
if (eventEndDate >= startDate && eventStartDate <= endDate) {
191+
events.push({
192+
startDate: {
193+
toJSDate: () => eventStartDate
194+
},
195+
endDate: {
196+
toJSDate: () => eventEndDate
197+
},
198+
summary: event.summary,
199+
location: event.location,
200+
description: event.description,
201+
uid: event.uid,
202+
isRecurring: () => false,
203+
duration: event.duration,
204+
component: vevent
205+
});
206+
}
207+
}
208+
}
209+
210+
return {
211+
events,
212+
occurrences
213+
};
214+
},
215+
121216
parser (calendar, iCalData = null, error = null) {
122217
if (error) {
123218
Log.error(`[CALEXT2] calendar:${calendar.name} >> Error: ${error.message || error}`);
@@ -127,22 +222,17 @@ module.exports = NodeHelper.create({
127222
Log.log(`[CALEXT2] calendar:${calendar.name} >> No data to fetch`);
128223
return;
129224
}
130-
let icalExpander;
131-
try {
132-
icalExpander = new IcalExpander({
133-
ics: iCalData,
134-
maxIterations: calendar.maxIterations
135-
});
136-
} catch (e) {
137-
Log.log(`[CALEXT2] calendar:${calendar.name} >> ${e.message}`);
138-
return;
139-
}
140225

141226
let events;
142227
try {
143-
events = icalExpander.between(
144-
moment().subtract(calendar.beforeDays, "days").startOf("day").toDate(),
145-
moment().add(calendar.afterDays, "days").endOf("day").toDate()
228+
const startDate = moment().subtract(calendar.beforeDays, "days").startOf("day").toDate();
229+
const endDate = moment().add(calendar.afterDays, "days").endOf("day").toDate();
230+
231+
events = this.parseAndExpandEvents(
232+
iCalData,
233+
startDate,
234+
endDate,
235+
calendar.maxIterations
146236
);
147237
} catch (e) {
148238
Log.log(`[CALEXT2] calendar:${calendar.name} >> ${e.message}`);

0 commit comments

Comments
 (0)