@@ -118,14 +118,14 @@ class ScheduledEventRecurrenceNWeekday:
118118 def __init__ (self , * , n : int , day : ScheduledEventRecurrenceWeekday | int ) -> None :
119119 self .n : int = n
120120 self .day : ScheduledEventRecurrenceWeekday = try_enum (
121- ScheduledEventRecurrenceWeekday , day
121+ ScheduledEventRecurrenceWeekday , int ( day )
122122 )
123123
124124 def __repr__ (self ) -> str :
125125 return f"<ScheduledEventRecurrenceNWeekday n={ self .n } day={ self .day } >"
126126
127127 def to_payload (self ) -> dict [str , int ]:
128- return {"n" : int (self .n ), "day" : int (self .day . value )}
128+ return {"n" : int (self .n ), "day" : int (self .day )}
129129
130130
131131class ScheduledEventRecurrenceRule :
@@ -174,38 +174,31 @@ def __init__(
174174 self ,
175175 * ,
176176 start : datetime .datetime ,
177- frequency : ScheduledEventRecurrenceFrequency ,
177+ frequency : ScheduledEventRecurrenceFrequency | int ,
178178 interval : int ,
179179 end : datetime .datetime | None = None ,
180180 by_weekday : list [ScheduledEventRecurrenceWeekday | int ] | None = None ,
181- by_n_weekday : list [ScheduledEventRecurrenceNWeekday | dict [str , int ]]
182- | None = None ,
181+ by_n_weekday : list [ScheduledEventRecurrenceNWeekday ] | None = None ,
183182 by_month : list [ScheduledEventRecurrenceMonth | int ] | None = None ,
184183 by_month_day : list [int ] | None = None ,
185184 by_year_day : list [int ] | None = None ,
186185 count : int | None = None ,
187186 ) -> None :
188187 self .start : datetime .datetime = start
189188 self .end : datetime .datetime | None = end
190- self .frequency : ScheduledEventRecurrenceFrequency = frequency
189+ self .frequency : ScheduledEventRecurrenceFrequency = try_enum (
190+ ScheduledEventRecurrenceFrequency , int (frequency )
191+ )
191192 self .interval : int = interval
192193 self .by_weekday : list [ScheduledEventRecurrenceWeekday ] | None = (
193- [try_enum (ScheduledEventRecurrenceWeekday , day ) for day in by_weekday ]
194- if by_weekday is not None
194+ [try_enum (ScheduledEventRecurrenceWeekday , int ( day ) ) for day in by_weekday ]
195+ if by_weekday
195196 else None
196197 )
197- if by_n_weekday is not None :
198- self .by_n_weekday = [
199- entry
200- if isinstance (entry , ScheduledEventRecurrenceNWeekday )
201- else ScheduledEventRecurrenceNWeekday (** entry )
202- for entry in by_n_weekday
203- ]
204- else :
205- self .by_n_weekday = None
198+ self .by_n_weekday : list [ScheduledEventRecurrenceNWeekday ] | None = by_n_weekday
206199 self .by_month : list [ScheduledEventRecurrenceMonth ] | None = (
207- [try_enum (ScheduledEventRecurrenceMonth , month ) for month in by_month ]
208- if by_month is not None
200+ [try_enum (ScheduledEventRecurrenceMonth , int ( month ) ) for month in by_month ]
201+ if by_month
209202 else None
210203 )
211204 self .by_month_day : list [int ] | None = by_month_day
@@ -224,7 +217,13 @@ def from_data(
224217 ) -> ScheduledEventRecurrenceRule :
225218 start = utils .parse_time (data ["start" ])
226219 end = utils .parse_time (data .get ("end" ))
227- by_weekday = data .get ("by_weekday" )
220+
221+ raw_by_weekday = data .get ("by_weekday" )
222+ by_weekday = (
223+ [try_enum (ScheduledEventRecurrenceWeekday , day ) for day in raw_by_weekday ]
224+ if raw_by_weekday
225+ else None
226+ )
228227
229228 raw_by_n_weekday = data .get ("by_n_weekday" )
230229 by_n_weekday = (
@@ -233,39 +232,60 @@ def from_data(
233232 else None
234233 )
235234
235+ raw_by_month = data .get ("by_month" )
236+ by_month = (
237+ [try_enum (ScheduledEventRecurrenceMonth , month ) for month in raw_by_month ]
238+ if raw_by_month
239+ else None
240+ )
241+
236242 return cls (
237243 start = start ,
238244 end = end ,
239- frequency = data ["frequency" ],
245+ frequency = try_enum ( ScheduledEventRecurrenceFrequency , data ["frequency" ]) ,
240246 interval = data ["interval" ],
241247 by_weekday = by_weekday ,
242248 by_n_weekday = by_n_weekday ,
243- by_month = data . get ( " by_month" ) ,
249+ by_month = by_month ,
244250 by_month_day = data .get ("by_month_day" ),
245251 by_year_day = data .get ("by_year_day" ),
246252 count = data .get ("count" ),
247253 )
248254
249255 def to_payload (self ) -> dict [str , Any ]:
256+ """Convert the recurrence rule to an API payload.
257+
258+ Raises
259+ ------
260+ ValidationError
261+ If the recurrence rule violates Discord's system limitations.
262+
263+ Returns
264+ -------
265+ dict[str, Any]
266+ The recurrence rule as a dictionary suitable for the Discord API.
267+ """
268+ self .validate ()
269+
250270 payload : dict [str , Any ] = {
251271 "start" : self .start .isoformat (),
252- "frequency" : int ( self .frequency .value ) ,
272+ "frequency" : self .frequency .value ,
253273 "interval" : int (self .interval ),
254274 }
255275
256276 if self .end is not None :
257277 payload ["end" ] = self .end .isoformat ()
258278
259279 if self .by_weekday is not None :
260- payload ["by_weekday" ] = [int (day . value ) for day in self .by_weekday ]
280+ payload ["by_weekday" ] = [int (day ) for day in self .by_weekday ]
261281
262282 if self .by_n_weekday is not None :
263283 payload ["by_n_weekday" ] = [
264284 entry .to_payload () for entry in self .by_n_weekday
265285 ]
266286
267287 if self .by_month is not None :
268- payload ["by_month" ] = [int (month . value ) for month in self .by_month ]
288+ payload ["by_month" ] = [int (month ) for month in self .by_month ]
269289
270290 if self .by_month_day is not None :
271291 payload ["by_month_day" ] = self .by_month_day
@@ -278,6 +298,116 @@ def to_payload(self) -> dict[str, Any]:
278298
279299 return payload
280300
301+ def validate (self ) -> None :
302+ """Validate the recurrence rule against Discord's system limitations.
303+
304+ Raises
305+ ------
306+ ValidationError
307+ If the recurrence rule violates any system limitations.
308+ """
309+ # Mutually exclusive combinations
310+ has_by_weekday = self .by_weekday is not None
311+ has_by_n_weekday = self .by_n_weekday is not None
312+ has_by_month = self .by_month is not None
313+ has_by_month_day = self .by_month_day is not None
314+
315+ if has_by_weekday and has_by_n_weekday :
316+ raise ValidationError ("by_weekday and by_n_weekday are mutually exclusive" )
317+
318+ if has_by_month and has_by_n_weekday :
319+ raise ValidationError ("by_month and by_n_weekday are mutually exclusive" )
320+
321+ if has_by_month != has_by_month_day :
322+ raise ValidationError (
323+ "by_month and by_month_day must both be provided together"
324+ )
325+
326+ # Daily frequency (0) constraints
327+ if self .frequency == ScheduledEventRecurrenceFrequency .yearly :
328+ if has_by_weekday :
329+ raise ValidationError ("by_weekday is not valid for yearly events" )
330+
331+ # Weekly frequency (2) constraints
332+ if self .frequency == ScheduledEventRecurrenceFrequency .weekly :
333+ if has_by_weekday :
334+ if len (self .by_weekday ) != 1 :
335+ raise ValidationError (
336+ "by_weekday must have exactly 1 day for weekly events"
337+ )
338+
339+ if has_by_n_weekday :
340+ raise ValidationError ("by_n_weekday is not valid for weekly events" )
341+
342+ if has_by_month or has_by_month_day :
343+ raise ValidationError (
344+ "by_month and by_month_day are not valid for weekly events"
345+ )
346+
347+ # interval can only be 2 (every-other week) or 1 (weekly)
348+ if self .interval not in (1 , 2 ):
349+ raise ValidationError ("interval for weekly events can only be 1 or 2" )
350+
351+ # Daily frequency (3) constraints
352+ if self .frequency == ScheduledEventRecurrenceFrequency .daily :
353+ if has_by_n_weekday :
354+ raise ValidationError ("by_n_weekday is not valid for daily events" )
355+
356+ if has_by_month or has_by_month_day :
357+ raise ValidationError (
358+ "by_month and by_month_day are not valid for daily events"
359+ )
360+
361+ if has_by_weekday :
362+ # Validate known sets of weekdays for daily events
363+ allowed_sets = [
364+ [0 , 1 , 2 , 3 , 4 ], # Monday - Friday
365+ [1 , 2 , 3 , 4 , 5 ], # Tuesday - Saturday
366+ [6 , 0 , 1 , 2 , 3 ], # Sunday - Thursday
367+ [4 , 5 ], # Friday & Saturday
368+ [5 , 6 ], # Saturday & Sunday
369+ [6 , 0 ], # Sunday & Monday
370+ ]
371+ weekday_values = [day .value for day in self .by_weekday ]
372+ weekday_values .sort ()
373+
374+ if weekday_values not in allowed_sets :
375+ raise ValidationError (
376+ "by_weekday for daily events must be one of the allowed sets: "
377+ "[0,1,2,3,4], [1,2,3,4,5], [6,0,1,2,3], [4,5], [5,6], [6,0]"
378+ )
379+
380+ # Monthly frequency (1) constraints
381+ if self .frequency == ScheduledEventRecurrenceFrequency .monthly :
382+ if has_by_n_weekday :
383+ if len (self .by_n_weekday ) != 1 :
384+ raise ValidationError (
385+ "by_n_weekday must have exactly 1 entry for monthly events"
386+ )
387+
388+ if has_by_weekday :
389+ raise ValidationError ("by_weekday is not valid for monthly events" )
390+
391+ if has_by_month or has_by_month_day :
392+ raise ValidationError (
393+ "by_month and by_month_day are not valid for monthly events"
394+ )
395+
396+ # Yearly frequency (0) constraints
397+ if self .frequency == ScheduledEventRecurrenceFrequency .yearly :
398+ if has_by_n_weekday :
399+ raise ValidationError ("by_n_weekday is not valid for yearly events" )
400+
401+ if not (has_by_month and has_by_month_day ):
402+ raise ValidationError (
403+ "by_month and by_month_day must both be provided for yearly events"
404+ )
405+
406+ if len (self .by_month ) != 1 or len (self .by_month_day ) != 1 :
407+ raise ValidationError (
408+ "by_month and by_month_day must each have exactly 1 entry for yearly events"
409+ )
410+
281411
282412class ScheduledEvent (Hashable ):
283413 """Represents a Discord Guild Scheduled Event.
@@ -554,13 +684,13 @@ async def edit(
554684 payload ["description" ] = description
555685
556686 if status is not MISSING :
557- payload ["status" ] = int (status . value )
687+ payload ["status" ] = int (status )
558688
559689 if entity_type is not MISSING :
560- payload ["entity_type" ] = int (entity_type . value )
690+ payload ["entity_type" ] = int (entity_type )
561691
562692 if privacy_level is not MISSING :
563- payload ["privacy_level" ] = int (privacy_level . value )
693+ payload ["privacy_level" ] = int (privacy_level )
564694
565695 if entity_metadata is not MISSING :
566696 if entity_metadata is None :
0 commit comments