diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventPrivacyLevel.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventPrivacyLevel.cs new file mode 100644 index 0000000000..e056f68acc --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventPrivacyLevel.cs @@ -0,0 +1,20 @@ +using System; + +namespace Discord; + +/// +/// Represents the privacy level of a guild scheduled event. +/// +public enum GuildScheduledEventPrivacyLevel +{ + /// + /// The scheduled event is public and available in discovery. + /// + [Obsolete("This event type isn't supported yet! check back later.", true)] + Public = 1, + + /// + /// The scheduled event is only accessible to guild members. + /// + Private = 2, +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRule.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRule.cs new file mode 100644 index 0000000000..362f84b864 --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRule.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; + +namespace Discord; + +public readonly struct GuildScheduledEventRecurrenceRule +{ + /// + /// Gets the starting time of the recurrence interval. + /// + public DateTimeOffset StartsAt { get; } + + /// + /// Gets the ending time of the recurrence interval. + /// + public DateTimeOffset? EndsAt { get; } + + /// + /// Gets how often the event occurs. + /// + public RecurrenceFrequency Frequency { get; } + + /// + /// Gets the spacing between the events, defined by . + /// + public int Interval { get; } + + /// + /// Gets the set of specific days within a week for the event to recur on. + /// + public IReadOnlyCollection ByWeekday { get; } + + /// + /// Gets the list of specific days within a specific week to recur on. + /// + public IReadOnlyCollection ByNWeekday { get; } + + /// + /// Gets the set of specific months to recur on. + /// + public IReadOnlyCollection ByMonth { get; } + + /// + /// Gets the set of specific dates within a month to recur on. + /// + public IReadOnlyCollection ByMonthDay { get; } + + /// + /// Gets the set of days within a year to recur on. (1-364) + /// + public IReadOnlyCollection ByYearDay { get; } + + /// + /// Gets the total amount of times that the event is allowed to recur before stopping. + /// + /// + /// if the event recurs endlessly. + /// + public int? Count { get; } + + internal GuildScheduledEventRecurrenceRule(DateTimeOffset startsAt, DateTimeOffset? endsAt, RecurrenceFrequency frequency, + int interval, IReadOnlyCollection byWeekday, IReadOnlyCollection byNWeekday, + IReadOnlyCollection byMonth, IReadOnlyCollection byMonthDay, IReadOnlyCollection byYearDay, int? count) + { + StartsAt = startsAt; + EndsAt = endsAt; + Frequency = frequency; + Interval = interval; + ByWeekday = byWeekday; + ByNWeekday = byNWeekday; + ByMonth = byMonth; + ByMonthDay = byMonthDay; + ByYearDay = byYearDay; + Count = count; + } +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRuleProperties.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRuleProperties.cs new file mode 100644 index 0000000000..2968e8ea7a --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventRecurrenceRuleProperties.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Discord; + +public class GuildScheduledEventRecurrenceRuleProperties +{ + /// + /// Gets or sets the starting time of the recurrence interval. + /// + public DateTimeOffset StartsAt { get; set; } + + /// + /// Gets or sets how often the event occurs. + /// + public RecurrenceFrequency Frequency { get; set; } + + /// + /// Gets or sets the spacing between the events, defined by . + /// + public int Interval { get; set; } + + /// + /// Gets or sets the set of specific days within a week for the event to recur on. + /// + public HashSet ByWeekday { get; set; } + + /// + /// Gets or sets the list of specific days within a specific week to recur on. + /// + public List ByNWeekday { get; set; } + + /// + /// Gets or sets the set of specific months to recur on. + /// + public HashSet ByMonth { get; set; } + + /// + /// Gets or sets the set of specific dates within a month to recur on. + /// + public HashSet ByMonthDay { get; set; } + + + public GuildScheduledEventRecurrenceRuleProperties() {} + + public GuildScheduledEventRecurrenceRuleProperties(DateTimeOffset startsAt, RecurrenceFrequency frequency, + int interval, HashSet byWeekday, IEnumerable byNWeekday, + HashSet byMonth, HashSet byMonthDay) + { + StartsAt = startsAt; + Frequency = frequency; + Interval = interval; + ByWeekday = byWeekday; + ByNWeekday = byNWeekday?.ToList(); + ByMonth = byMonth; + ByMonthDay = byMonthDay; + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventStatus.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventStatus.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventStatus.cs rename to src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventStatus.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventType.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventType.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventType.cs rename to src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventType.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventsProperties.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventsProperties.cs similarity index 89% rename from src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventsProperties.cs rename to src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventsProperties.cs index d3be8b784b..7a62cef26c 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventsProperties.cs +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/GuildScheduledEventsProperties.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Discord { @@ -59,5 +55,10 @@ public class GuildScheduledEventsProperties /// Gets or sets the banner image of the event. /// public Optional CoverImage { get; set; } + + /// + /// Gets or sets the definition for how often this event should recur. + /// + public Optional RecurrenceRule { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuildScheduledEvent.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/IGuildScheduledEvent.cs similarity index 97% rename from src/Discord.Net.Core/Entities/Guilds/IGuildScheduledEvent.cs rename to src/Discord.Net.Core/Entities/GuildScheduledEvents/IGuildScheduledEvent.cs index 5c937f7866..a345b124f2 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuildScheduledEvent.cs +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/IGuildScheduledEvent.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Discord @@ -90,6 +88,11 @@ public interface IGuildScheduledEvent : IEntity /// int? UserCount { get; } + /// + /// Gets the definition for how often this event should recur. if not set. + /// + GuildScheduledEventRecurrenceRule? RecurrenceRule { get; } + /// /// Gets this events banner image url. /// diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceFrequency.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceFrequency.cs new file mode 100644 index 0000000000..e33c912da0 --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceFrequency.cs @@ -0,0 +1,12 @@ +namespace Discord; + +public enum RecurrenceFrequency +{ + Yearly = 0, + + Monthly = 1, + + Weekly = 2, + + Daily = 3 +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekday.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekday.cs new file mode 100644 index 0000000000..9c449cbed3 --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekday.cs @@ -0,0 +1,20 @@ +namespace Discord; + +public readonly struct RecurrenceRuleByNWeekday +{ + /// + /// Gets the week to reoccur on. (from 1 to 5) + /// + public int Week { get; } + + /// + /// Gets the day within a week to reoccur on. + /// + public RecurrenceRuleWeekday Day { get; } + + internal RecurrenceRuleByNWeekday(int week, RecurrenceRuleWeekday day) + { + Week = week; + Day = day; + } +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekdayProperties.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekdayProperties.cs new file mode 100644 index 0000000000..d8d66c0b4b --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleByNWeekdayProperties.cs @@ -0,0 +1,22 @@ +namespace Discord; + +public class RecurrenceRuleByNWeekdayProperties +{ + /// + /// Gets or sets the week to reoccur on. (from 1 to 5) + /// + public int Week { get; set; } + + /// + /// Gets or sets the day within a week to reoccur on. + /// + public RecurrenceRuleWeekday Day { get; set; } + + public RecurrenceRuleByNWeekdayProperties() {} + + public RecurrenceRuleByNWeekdayProperties(int week, RecurrenceRuleWeekday day) + { + Week = week; + Day = day; + } +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleMonth.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleMonth.cs new file mode 100644 index 0000000000..257c7b96ec --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleMonth.cs @@ -0,0 +1,28 @@ +namespace Discord; + +public enum RecurrenceRuleMonth +{ + January = 1, + + February = 2, + + March = 3, + + April = 4, + + May = 5, + + June = 6, + + July = 7, + + August = 8, + + September = 9, + + October = 10, + + November = 11, + + December = 12 +} diff --git a/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleWeekday.cs b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleWeekday.cs new file mode 100644 index 0000000000..f0aa663fb0 --- /dev/null +++ b/src/Discord.Net.Core/Entities/GuildScheduledEvents/RecurrenceRuleWeekday.cs @@ -0,0 +1,18 @@ +namespace Discord; + +public enum RecurrenceRuleWeekday +{ + Monday = 0, + + Tuesday = 1, + + Wednesday = 2, + + Thursday = 3, + + Friday = 4, + + Saturday = 5, + + Sunday = 6 +} diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventPrivacyLevel.cs b/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventPrivacyLevel.cs deleted file mode 100644 index 87881104c8..0000000000 --- a/src/Discord.Net.Core/Entities/Guilds/GuildScheduledEventPrivacyLevel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Discord -{ - /// - /// Represents the privacy level of a guild scheduled event. - /// - public enum GuildScheduledEventPrivacyLevel - { - /// - /// The scheduled event is public and available in discovery. - /// - [Obsolete("This event type isn't supported yet! check back later.", true)] - Public = 1, - - /// - /// The scheduled event is only accessible to guild members. - /// - Private = 2, - } -} diff --git a/src/Discord.Net.Rest/API/Common/GuildScheduledEvent.cs b/src/Discord.Net.Rest/API/Common/GuildScheduledEvent.cs index bafff4c8dd..c5b79e1497 100644 --- a/src/Discord.Net.Rest/API/Common/GuildScheduledEvent.cs +++ b/src/Discord.Net.Rest/API/Common/GuildScheduledEvent.cs @@ -2,41 +2,57 @@ using System; -namespace Discord.API +namespace Discord.API; + +internal class GuildScheduledEvent { - internal class GuildScheduledEvent - { - [JsonProperty("id")] - public ulong Id { get; set; } - [JsonProperty("guild_id")] - public ulong GuildId { get; set; } - [JsonProperty("channel_id")] - public Optional ChannelId { get; set; } - [JsonProperty("creator_id")] - public Optional CreatorId { get; set; } - [JsonProperty("name")] - public string Name { get; set; } - [JsonProperty("description")] - public Optional Description { get; set; } - [JsonProperty("scheduled_start_time")] - public DateTimeOffset ScheduledStartTime { get; set; } - [JsonProperty("scheduled_end_time")] - public DateTimeOffset? ScheduledEndTime { get; set; } - [JsonProperty("privacy_level")] - public GuildScheduledEventPrivacyLevel PrivacyLevel { get; set; } - [JsonProperty("status")] - public GuildScheduledEventStatus Status { get; set; } - [JsonProperty("entity_type")] - public GuildScheduledEventType EntityType { get; set; } - [JsonProperty("entity_id")] - public ulong? EntityId { get; set; } - [JsonProperty("entity_metadata")] - public GuildScheduledEventEntityMetadata EntityMetadata { get; set; } - [JsonProperty("creator")] - public Optional Creator { get; set; } - [JsonProperty("user_count")] - public Optional UserCount { get; set; } - [JsonProperty("image")] - public string Image { get; set; } - } + [JsonProperty("id")] + public ulong Id { get; set; } + + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + + [JsonProperty("channel_id")] + public Optional ChannelId { get; set; } + + [JsonProperty("creator_id")] + public Optional CreatorId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public Optional Description { get; set; } + + [JsonProperty("scheduled_start_time")] + public DateTimeOffset ScheduledStartTime { get; set; } + + [JsonProperty("scheduled_end_time")] + public DateTimeOffset? ScheduledEndTime { get; set; } + + [JsonProperty("privacy_level")] + public GuildScheduledEventPrivacyLevel PrivacyLevel { get; set; } + + [JsonProperty("status")] + public GuildScheduledEventStatus Status { get; set; } + + [JsonProperty("entity_type")] + public GuildScheduledEventType EntityType { get; set; } + + [JsonProperty("entity_id")] + public ulong? EntityId { get; set; } + [JsonProperty("entity_metadata")] + public GuildScheduledEventEntityMetadata EntityMetadata { get; set; } + + [JsonProperty("creator")] + public Optional Creator { get; set; } + + [JsonProperty("user_count")] + public Optional UserCount { get; set; } + + [JsonProperty("image")] + public string Image { get; set; } + + [JsonProperty("recurrence_rule")] + public GuildScheduledEventRecurrenceRule RecurrenceRule { get; set; } } diff --git a/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRule.cs b/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRule.cs new file mode 100644 index 0000000000..1de91a0c80 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRule.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using System; + +namespace Discord.API; + +public class GuildScheduledEventRecurrenceRule +{ + [JsonProperty("start")] + public DateTimeOffset StartAt { get; set; } + + [JsonProperty("end")] + public DateTimeOffset? EndAt { get; set; } + + [JsonProperty("frequency")] + public RecurrenceFrequency Frequency { get; set; } + + [JsonProperty("interval")] + public int Interval { get; set; } + + [JsonProperty("by_weekday")] + public RecurrenceRuleWeekday[] ByWeekday { get; set; } + + [JsonProperty("by_n_weekday")] + public GuildScheduledEventRecurrenceRuleByNWeekday[] ByNWeekday { get; set; } + + [JsonProperty("by_month")] + public RecurrenceRuleMonth[] ByMonth { get; set; } + + [JsonProperty("by_month_day")] + public int[] ByMonthDay { get; set; } + + [JsonProperty("by_year_day")] + public int[] ByYearDay { get; set; } + + [JsonProperty("count")] + public int? Count { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRuleByNWeebkday.cs b/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRuleByNWeebkday.cs new file mode 100644 index 0000000000..4b0d44ae46 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/GuildScheduledEventRecurrenceRuleByNWeebkday.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API; + +public class GuildScheduledEventRecurrenceRuleByNWeekday +{ + [JsonProperty("n")] + public int WeekNumber { get; set; } + + [JsonProperty("day")] + public RecurrenceRuleWeekday Day { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Rest/CreateGuildScheduledEventParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildScheduledEventParams.cs index 2ccd06fe6b..25c7b66039 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateGuildScheduledEventParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildScheduledEventParams.cs @@ -1,31 +1,37 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace Discord.API.Rest +namespace Discord.API.Rest; + +internal class CreateGuildScheduledEventParams { - internal class CreateGuildScheduledEventParams - { - [JsonProperty("channel_id")] - public Optional ChannelId { get; set; } - [JsonProperty("entity_metadata")] - public Optional EntityMetadata { get; set; } - [JsonProperty("name")] - public string Name { get; set; } - [JsonProperty("privacy_level")] - public GuildScheduledEventPrivacyLevel PrivacyLevel { get; set; } - [JsonProperty("scheduled_start_time")] - public DateTimeOffset StartTime { get; set; } - [JsonProperty("scheduled_end_time")] - public Optional EndTime { get; set; } - [JsonProperty("description")] - public Optional Description { get; set; } - [JsonProperty("entity_type")] - public GuildScheduledEventType Type { get; set; } - [JsonProperty("image")] - public Optional Image { get; set; } - } + [JsonProperty("channel_id")] + public Optional ChannelId { get; set; } + + [JsonProperty("entity_metadata")] + public Optional EntityMetadata { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("privacy_level")] + public GuildScheduledEventPrivacyLevel PrivacyLevel { get; set; } + + [JsonProperty("scheduled_start_time")] + public DateTimeOffset StartTime { get; set; } + + [JsonProperty("scheduled_end_time")] + public Optional EndTime { get; set; } + + [JsonProperty("description")] + public Optional Description { get; set; } + + [JsonProperty("entity_type")] + public GuildScheduledEventType Type { get; set; } + + [JsonProperty("image")] + public Optional Image { get; set; } + + [JsonProperty("recurrence_rule")] + public Optional RecurrenceRule { get; set; } } diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildScheduledEventParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildScheduledEventParams.cs index 1179ddcbe7..e7fe002266 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyGuildScheduledEventParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildScheduledEventParams.cs @@ -1,33 +1,40 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace Discord.API.Rest +namespace Discord.API.Rest; + +internal class ModifyGuildScheduledEventParams { - internal class ModifyGuildScheduledEventParams - { - [JsonProperty("channel_id")] - public Optional ChannelId { get; set; } - [JsonProperty("entity_metadata")] - public Optional EntityMetadata { get; set; } - [JsonProperty("name")] - public Optional Name { get; set; } - [JsonProperty("privacy_level")] - public Optional PrivacyLevel { get; set; } - [JsonProperty("scheduled_start_time")] - public Optional StartTime { get; set; } - [JsonProperty("scheduled_end_time")] - public Optional EndTime { get; set; } - [JsonProperty("description")] - public Optional Description { get; set; } - [JsonProperty("entity_type")] - public Optional Type { get; set; } - [JsonProperty("status")] - public Optional Status { get; set; } - [JsonProperty("image")] - public Optional Image { get; set; } - } + [JsonProperty("channel_id")] + public Optional ChannelId { get; set; } + + [JsonProperty("entity_metadata")] + public Optional EntityMetadata { get; set; } + + [JsonProperty("name")] + public Optional Name { get; set; } + + [JsonProperty("privacy_level")] + public Optional PrivacyLevel { get; set; } + + [JsonProperty("scheduled_start_time")] + public Optional StartTime { get; set; } + + [JsonProperty("scheduled_end_time")] + public Optional EndTime { get; set; } + + [JsonProperty("description")] + public Optional Description { get; set; } + + [JsonProperty("entity_type")] + public Optional Type { get; set; } + + [JsonProperty("status")] + public Optional Status { get; set; } + + [JsonProperty("image")] + public Optional Image { get; set; } + + [JsonProperty("recurrence_rule")] + public Optional RecurrenceRule { get; set; } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index ac9dce1207..d1ba05e4af 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -1273,6 +1273,55 @@ public static IAsyncEnumerable> GetEventUsersAsync } } + if (args.RecurrenceRule is { IsSpecified: true, Value: not null }) + { + var rule = args.RecurrenceRule.Value; + + var hasByWeekDay = rule.ByWeekday?.Any() ?? false; + var hasByNWeekDay = rule.ByNWeekday?.Any() ?? false; + var hasByMonth = (rule.ByMonthDay?.Any() ?? false) || (rule.ByMonth?.Any() ?? false); + + if (hasByWeekDay && hasByNWeekDay || + hasByWeekDay && hasByMonth || + hasByNWeekDay && hasByMonth) + { + throw new ArgumentException($"A recurrence rule can have one of ('{nameof(rule.ByWeekday)}', '{nameof(rule.ByNWeekday)}', '{nameof(rule.ByMonth)}' + '{nameof(rule.ByMonthDay)}'), but not a combination of them."); + } + + if (hasByWeekDay) + { + if (rule.Frequency is not RecurrenceFrequency.Daily and not RecurrenceFrequency.Weekly) + throw new ArgumentException($"A {nameof(rule.ByWeekday)} rule can only be used with {nameof(rule.Frequency)} of 'Daily' or 'Weekly'."); + + if (rule.Frequency is RecurrenceFrequency.Weekly) + { + if (rule.ByWeekday.Count != 1) + throw new ArgumentException("A 'Weekly' recurrence rule must have a single weekday selected."); + + if (rule.Interval == 1) + throw new ArgumentException($"{nameof(rule.Interval)} can only be set to a value other than '1' when {nameof(rule.Frequency)} is set to 'Weekly'"); + } + } + + if (hasByNWeekDay) + { + if (rule.Frequency is not RecurrenceFrequency.Monthly) + throw new ArgumentException($"A {rule.ByNWeekday} rule must have {nameof(rule.Frequency)} set to 'Monthly'."); + + if (rule.ByNWeekday.Count != 1) + throw new ArgumentException($"A {rule.ByNWeekday} must have exactly one day selected."); + } + + if (hasByMonth) + { + if (rule.Frequency is not RecurrenceFrequency.Yearly) + throw new ArgumentException($"A {rule.ByMonth} rule must have {nameof(rule.Frequency)} set to 'Yearly'."); + + if (rule.ByMonth?.Count is not 1 || rule.ByMonthDay?.Count is not 1) + throw new ArgumentException($"A {rule.ByMonth} rule must have exactly 1 day and 1 month selected."); + } + } + var apiArgs = new ModifyGuildScheduledEventParams() { ChannelId = args.ChannelId, @@ -1284,10 +1333,11 @@ public static IAsyncEnumerable> GetEventUsersAsync Status = args.Status, Type = args.Type, Image = args.CoverImage.IsSpecified - ? args.CoverImage.Value.HasValue - ? args.CoverImage.Value.Value.ToModel() - : null - : Optional.Unspecified + ? args.CoverImage.Value?.ToModel() + : Optional.Unspecified, + RecurrenceRule = args.RecurrenceRule.IsSpecified + ? args.RecurrenceRule.Value?.ToModel() + : Optional.Unspecified }; if (args.Location.IsSpecified) @@ -1328,7 +1378,8 @@ public static async Task CreateGuildEventAsync(BaseDiscordClient ulong? channelId = null, string location = null, Image? bannerImage = null, - RequestOptions options = null) + RequestOptions options = null, + GuildScheduledEventRecurrenceRuleProperties recurrenceRule = null) { if (location != null) { @@ -1353,6 +1404,53 @@ public static async Task CreateGuildEventAsync(BaseDiscordClient if (endTime != null && endTime <= startTime) throw new ArgumentOutOfRangeException(nameof(endTime), $"{nameof(endTime)} cannot be before the start time"); + if (recurrenceRule is not null) + { + var hasByWeekDay = recurrenceRule.ByWeekday?.Any() ?? false; + var hasByNWeekDay = recurrenceRule.ByNWeekday?.Any() ?? false; + var hasByMonth = (recurrenceRule.ByMonthDay?.Any() ?? false) || (recurrenceRule.ByMonth?.Any() ?? false); + + if (hasByWeekDay && hasByNWeekDay || + hasByWeekDay && hasByMonth || + hasByNWeekDay && hasByMonth) + { + throw new ArgumentException($"A recurrence rule can have one of ('{nameof(recurrenceRule.ByWeekday)}', '{nameof(recurrenceRule.ByNWeekday)}', '{nameof(recurrenceRule.ByMonth)}' + '{nameof(recurrenceRule.ByMonthDay)}'), but not a combination of them."); + } + + if (hasByWeekDay) + { + if (recurrenceRule.Frequency is not RecurrenceFrequency.Daily and not RecurrenceFrequency.Weekly) + throw new ArgumentException($"A {nameof(recurrenceRule.ByWeekday)} rule can only be used with {nameof(recurrenceRule.Frequency)} of 'Daily' or 'Weekly'."); + + if (recurrenceRule.Frequency is RecurrenceFrequency.Weekly) + { + if (recurrenceRule.ByWeekday.Count != 1) + throw new ArgumentException("A 'Weekly' recurrence rule must have a single weekday selected."); + + if (recurrenceRule.Interval == 1) + throw new ArgumentException($"{nameof(recurrenceRule.Interval)} can only be set to a value other than '1' when {nameof(recurrenceRule.Frequency)} is set to 'Weekly'"); + } + } + + if (hasByNWeekDay) + { + if (recurrenceRule.Frequency is not RecurrenceFrequency.Monthly) + throw new ArgumentException($"A {recurrenceRule.ByNWeekday} rule must have {nameof(recurrenceRule.Frequency)} set to 'Monthly'."); + + if (recurrenceRule.ByNWeekday.Count != 1) + throw new ArgumentException($"A {recurrenceRule.ByNWeekday} must have exactly one day selected."); + } + + if (hasByMonth) + { + if (recurrenceRule.Frequency is not RecurrenceFrequency.Yearly) + throw new ArgumentException($"A {recurrenceRule.ByMonth} rule must have {nameof(recurrenceRule.Frequency)} set to 'Yearly'."); + + if (recurrenceRule.ByMonth?.Count is not 1 || recurrenceRule.ByMonthDay?.Count is not 1) + throw new ArgumentException($"A {recurrenceRule.ByMonth} rule must have exactly 1 day and 1 month selected."); + } + } + var apiArgs = new CreateGuildScheduledEventParams() { @@ -1363,7 +1461,8 @@ public static async Task CreateGuildEventAsync(BaseDiscordClient PrivacyLevel = privacyLevel, StartTime = startTime, Type = type, - Image = bannerImage.HasValue ? bannerImage.Value.ToModel() : Optional.Unspecified + Image = bannerImage?.ToModel() ?? Optional.Unspecified, + RecurrenceRule = recurrenceRule?.ToModel() ?? Optional.Unspecified }; if (location != null) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 5911c14df0..d363cd114b 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -1277,6 +1277,7 @@ public Task> GetEventsAsync(RequestOptions o /// The location of the event; links are supported /// The optional banner image for the event. /// The options to be used when sending the request. + /// The definition for how often this event should recur. /// /// A task that represents the asynchronous create operation. /// @@ -1290,8 +1291,9 @@ public Task CreateEventAsync( ulong? channelId = null, string location = null, Image? coverImage = null, - RequestOptions options = null) - => GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options); + RequestOptions options = null, + GuildScheduledEventRecurrenceRuleProperties recurrenceRule = null) + => GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options, recurrenceRule); #endregion diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildEvent.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildEvent.cs index 597919846a..7df9301833 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildEvent.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildEvent.cs @@ -58,6 +58,9 @@ public class RestGuildEvent : RestEntity, IGuildScheduledEvent /// public int? UserCount { get; private set; } + /// + public GuildScheduledEventRecurrenceRule? RecurrenceRule { get; private set; } + internal RestGuildEvent(BaseDiscordClient client, IGuild guild, ulong id) : base(client, id) { @@ -106,6 +109,8 @@ internal void Update(Model model) UserCount = model.UserCount.ToNullable(); CoverImageId = model.Image; GuildId = model.GuildId; + + RecurrenceRule = model.RecurrenceRule?.ToEntity(); } /// diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs index 240cf71b92..70440ba439 100644 --- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs +++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs @@ -231,5 +231,34 @@ public static API.Message ToMessage(this API.InteractionResponse model, IDiscord Id = interaction.Id, }; } + + public static GuildScheduledEventRecurrenceRule ToEntity(this API.GuildScheduledEventRecurrenceRule rule) + => new( + rule.StartAt, + rule.EndAt, + rule.Frequency, + rule.Interval, + rule.ByWeekday?.ToReadOnlyCollection() ?? ImmutableArray.Empty, + rule.ByNWeekday?.Select(x => new RecurrenceRuleByNWeekday(x.WeekNumber, x.Day)).ToImmutableArray() ?? ImmutableArray.Empty, + rule.ByMonth?.ToReadOnlyCollection() ?? ImmutableArray.Empty, + rule.ByMonthDay?.ToReadOnlyCollection() ?? ImmutableArray.Empty, + rule.ByYearDay?.ToReadOnlyCollection() ?? ImmutableArray.Empty, + rule.Count); + + public static API.GuildScheduledEventRecurrenceRule ToModel(this GuildScheduledEventRecurrenceRuleProperties rule) + => new() + { + Frequency = rule.Frequency, + ByMonthDay = rule.ByMonthDay?.ToArray(), + ByNWeekday = rule.ByNWeekday?.Select(x => new API.GuildScheduledEventRecurrenceRuleByNWeekday + { + Day = x.Day, + WeekNumber = x.Week + }).ToArray(), + ByWeekday = rule.ByWeekday?.ToArray(), + ByMonth = rule.ByMonth?.ToArray(), + Interval = rule.Interval, + StartAt = rule.StartsAt + }; } } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index ae434bbe44..292bd8b38d 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -1417,6 +1417,7 @@ public Task> GetEventsAsync(RequestOptions o /// The location of the event; links are supported /// The optional banner image for the event. /// The options to be used when sending the request. + /// The definition for how often this event should recur. /// /// A task that represents the asynchronous create operation. /// @@ -1430,7 +1431,8 @@ public Task CreateEventAsync( ulong? channelId = null, string location = null, Image? coverImage = null, - RequestOptions options = null) + RequestOptions options = null, + GuildScheduledEventRecurrenceRuleProperties recurrenceRule = null) { // requirements taken from https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-permissions-requirements switch (type) @@ -1446,7 +1448,7 @@ public Task CreateEventAsync( break; } - return GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options); + return GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options, recurrenceRule); } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildEvent.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildEvent.cs index 07ec839c3f..f92ff5fb40 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildEvent.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildEvent.cs @@ -65,6 +65,9 @@ public class SocketGuildEvent : SocketEntity, IGuildScheduledEvent /// public int? UserCount { get; private set; } + /// + public GuildScheduledEventRecurrenceRule? RecurrenceRule { get; private set; } + internal SocketGuildEvent(DiscordSocketClient client, SocketGuild guild, ulong id) : base(client, id) { @@ -117,6 +120,8 @@ internal void Update(Model model) UserCount = model.UserCount.ToNullable(); CoverImageId = model.Image; GuildId = model.GuildId; + + RecurrenceRule = model.RecurrenceRule?.ToEntity(); } ///