@@ -229,6 +229,7 @@ def _build_ical(
229229 location : str = "" ,
230230 status : str = "CONFIRMED" ,
231231 categories : list [str ] | None = None ,
232+ rrule : str = "" ,
232233) -> str :
233234 """Build a minimal iCalendar VEVENT string."""
234235 cal = ICal ()
@@ -248,10 +249,29 @@ def _build_ical(
248249 event .add ("status" , status )
249250 if categories :
250251 event .add ("categories" , categories )
252+ if rrule :
253+ event .add ("rrule" , _parse_rrule (rrule ))
251254 cal .add_component (event )
252255 return cal .to_ical ().decode ()
253256
254257
258+ def _parse_rrule (rrule_str : str ) -> dict [str , list [Any ]]:
259+ """Parse an RRULE string like 'FREQ=WEEKLY;COUNT=4;BYDAY=MO,WE' into a dict."""
260+ result : dict [str , list [Any ]] = {}
261+ for part in rrule_str .split (";" ):
262+ if "=" not in part :
263+ continue
264+ key , val = part .split ("=" , 1 )
265+ key = key .strip ()
266+ if key == "UNTIL" :
267+ result [key ] = [datetime .fromisoformat (val .strip ())]
268+ elif key in {"COUNT" , "INTERVAL" }:
269+ result [key ] = [int (val .strip ())]
270+ else :
271+ result [key ] = [v .strip () for v in val .split ("," )]
272+ return result
273+
274+
255275def _validate_status (status : str ) -> str :
256276 valid = {"CONFIRMED" , "TENTATIVE" , "CANCELLED" }
257277 upper = status .upper ()
@@ -424,6 +444,7 @@ async def create_event(
424444 location : str = "" ,
425445 status : str = "CONFIRMED" ,
426446 categories : str = "" ,
447+ rrule : str = "" ,
427448 ) -> str :
428449 """Create a new calendar event.
429450
@@ -440,6 +461,9 @@ async def create_event(
440461 location: Optional event location.
441462 status: Event status: "CONFIRMED" (default), "TENTATIVE", or "CANCELLED".
442463 categories: Optional comma-separated category names (e.g. "Work,Meeting").
464+ rrule: Optional recurrence rule in iCalendar RRULE format.
465+ Examples: "FREQ=DAILY;COUNT=5", "FREQ=WEEKLY;BYDAY=MO,WE,FR",
466+ "FREQ=MONTHLY;BYMONTHDAY=15;UNTIL=20261231T235959Z".
443467
444468 Returns:
445469 JSON object with the created event's uid and summary.
@@ -455,7 +479,7 @@ async def create_event(
455479 dtend = dtstart + timedelta (hours = 1 )
456480
457481 uid = str (uuid .uuid4 ())
458- ical_data = _build_ical (uid , summary , dtstart , dtend , description , location , status_upper , cat_list )
482+ ical_data = _build_ical (uid , summary , dtstart , dtend , description , location , status_upper , cat_list , rrule )
459483 client = get_client ()
460484 user = get_config ().user
461485 path = _caldav_path (user , calendar_id , f"{ uid } .ics" )
0 commit comments