diff --git a/NetCord/Application.cs b/NetCord/Application.cs index 20fea1dc4..56b26660c 100644 --- a/NetCord/Application.cs +++ b/NetCord/Application.cs @@ -63,4 +63,14 @@ public Application(JsonModels.JsonApplication jsonModel, RestClient client) : ba if (integrationTypesConfiguration is not null) IntegrationTypesConfiguration = integrationTypesConfiguration.ToDictionary(i => i.Key, i => new ApplicationIntegrationTypeConfiguration(i.Value)); } + + public ImageUrl? GetIconUrl(ImageFormat format) => IconHash is string hash ? ImageUrl.ApplicationIcon(Id, hash, format) : null; + + public ImageUrl? GetCoverUrl(ImageFormat format) => CoverImageHash is string hash ? ImageUrl.ApplicationCover(Id, hash, format) : null; + + public ImageUrl? GetAssetUrl(ulong assetId, ImageFormat format) => ImageUrl.ApplicationAsset(Id, assetId, format); + + public ImageUrl? GetAchievementIconUrl(ulong achievementId, string iconHash, ImageFormat format) => ImageUrl.AchievementIcon(Id, achievementId, iconHash, format); + + public ImageUrl? GetStorePageAssetUrl(ulong assetId, ImageFormat format) => ImageUrl.StorePageAsset(Id, assetId, format); } diff --git a/NetCord/CustomEmoji.cs b/NetCord/CustomEmoji.cs index 6bd4c6202..83b2ddc7e 100644 --- a/NetCord/CustomEmoji.cs +++ b/NetCord/CustomEmoji.cs @@ -26,6 +26,8 @@ public CustomEmoji(JsonEmoji jsonModel, RestClient client) : base(jsonModel) public bool? Available => _jsonModel.Available; + public ImageUrl GetImageUrl(ImageFormat format) => ImageUrl.CustomEmoji(Id, format); + public override string ToString() => Animated ? $"" : $"<:{Name}:{Id}>"; public string ToString(string? format, IFormatProvider? formatProvider) => ToString(); diff --git a/NetCord/GuildScheduledEvent.cs b/NetCord/GuildScheduledEvent.cs index 9f4def244..c63ee7158 100644 --- a/NetCord/GuildScheduledEvent.cs +++ b/NetCord/GuildScheduledEvent.cs @@ -37,6 +37,10 @@ public partial class GuildScheduledEvent : ClientEntity, IJsonModel _jsonModel.UserCount; + public string? CoverImageHash => _jsonModel.CoverImageHash; + + public GuildScheduledEventRecurrenceRule? RecurrenceRule { get; } + public GuildScheduledEvent(JsonModels.JsonGuildScheduledEvent jsonModel, RestClient client) : base(client) { _jsonModel = jsonModel; @@ -44,5 +48,13 @@ public GuildScheduledEvent(JsonModels.JsonGuildScheduledEvent jsonModel, RestCli var creator = _jsonModel.Creator; if (creator is not null) Creator = new(creator, client); + + var recurrenceRule = _jsonModel.RecurrenceRule; + if (recurrenceRule is not null) + RecurrenceRule = new(recurrenceRule); } + + public bool HasCoverImage => CoverImageHash is not null; + + public ImageUrl? GetCoverImageUrl(ImageFormat format) => _jsonModel.CoverImageHash is string hash ? ImageUrl.GuildScheduledEventCover(Id, hash, format) : null; } diff --git a/NetCord/GuildScheduledEventRecurrenceRule.cs b/NetCord/GuildScheduledEventRecurrenceRule.cs new file mode 100644 index 000000000..bd1e6a5f7 --- /dev/null +++ b/NetCord/GuildScheduledEventRecurrenceRule.cs @@ -0,0 +1,28 @@ +using NetCord.JsonModels; + +namespace NetCord; + +public class GuildScheduledEventRecurrenceRule(JsonGuildScheduledEventRecurrenceRule jsonModel) : IJsonModel +{ + JsonGuildScheduledEventRecurrenceRule IJsonModel.JsonModel => jsonModel; + + public DateTimeOffset? StartAt => jsonModel.StartAt; + + public DateTimeOffset? EndAt => jsonModel.EndAt; + + public GuildScheduledEventRecurrenceRuleFrequency Frequency => jsonModel.Frequency; + + public int Interval => jsonModel.Interval; + + public GuildScheduledEventRecurrenceRuleWeekday? ByWeekday => jsonModel.ByWeekday; + + public GuildScheduledEventRecurrenceRuleNWeekday ByNWeekday { get; } = new(jsonModel.ByNWeekday); + + public GuildScheduledEventRecurrenceRuleMonth? ByMonth => jsonModel.ByMonth; + + public IReadOnlyList? ByMonthDay => jsonModel.ByMonthDay; + + public IReadOnlyList? ByYearDay => jsonModel.ByYearDay; + + public int? Count => jsonModel.Count; +} diff --git a/NetCord/GuildScheduledEventRecurrenceRuleFrequency.cs b/NetCord/GuildScheduledEventRecurrenceRuleFrequency.cs new file mode 100644 index 000000000..622f04ce3 --- /dev/null +++ b/NetCord/GuildScheduledEventRecurrenceRuleFrequency.cs @@ -0,0 +1,9 @@ +namespace NetCord; + +public enum GuildScheduledEventRecurrenceRuleFrequency : byte +{ + Yearly = 0, + Monthly = 1, + Weekly = 2, + Daily = 3, +} diff --git a/NetCord/GuildScheduledEventRecurrenceRuleMonth.cs b/NetCord/GuildScheduledEventRecurrenceRuleMonth.cs new file mode 100644 index 000000000..a7e28148f --- /dev/null +++ b/NetCord/GuildScheduledEventRecurrenceRuleMonth.cs @@ -0,0 +1,17 @@ +namespace NetCord; + +public enum GuildScheduledEventRecurrenceRuleMonth +{ + 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/NetCord/GuildScheduledEventRecurrenceRuleNWeekday.cs b/NetCord/GuildScheduledEventRecurrenceRuleNWeekday.cs new file mode 100644 index 000000000..1f46b515b --- /dev/null +++ b/NetCord/GuildScheduledEventRecurrenceRuleNWeekday.cs @@ -0,0 +1,12 @@ +using NetCord.JsonModels; + +namespace NetCord; + +public class GuildScheduledEventRecurrenceRuleNWeekday(JsonGuildScheduledEventRecurrenceRuleNWeekday jsonModel) : IJsonModel +{ + JsonGuildScheduledEventRecurrenceRuleNWeekday IJsonModel.JsonModel => jsonModel; + + public int N => jsonModel.N; + + public GuildScheduledEventRecurrenceRuleWeekday Day => jsonModel.Day; +} diff --git a/NetCord/GuildScheduledEventRecurrenceRuleWeekday.cs b/NetCord/GuildScheduledEventRecurrenceRuleWeekday.cs new file mode 100644 index 000000000..d3f1e12c5 --- /dev/null +++ b/NetCord/GuildScheduledEventRecurrenceRuleWeekday.cs @@ -0,0 +1,12 @@ +namespace NetCord; + +public enum GuildScheduledEventRecurrenceRuleWeekday : byte +{ + Monday = 0, + Tuesday = 1, + Wednesday = 2, + Thursday = 3, + Friday = 4, + Saturday = 5, + Sunday = 6, +} diff --git a/NetCord/GuildUser.cs b/NetCord/GuildUser.cs index 8e4f6ba80..082c9ffaf 100644 --- a/NetCord/GuildUser.cs +++ b/NetCord/GuildUser.cs @@ -11,20 +11,21 @@ public partial class GuildUser(JsonGuildUser jsonModel, ulong guildId, RestClien /// /// The ID of the guild the object belongs to. /// - public ulong GuildId { get; } = guildId; + public ulong GuildId => guildId; /// /// Gets the of the user's guild avatar. /// /// The format of the returned . Defaults to (or for animated avatars). /// An pointing to the user's guild avatar. If the user does not have one set, returns . - public ImageUrl? GetGuildAvatarUrl(ImageFormat? format = null) => GuildAvatarHash is string hash ? ImageUrl.GuildUserAvatar(GuildId, Id, hash, format) : null; + public ImageUrl? GetGuildAvatarUrl(ImageFormat? format = null) => GuildAvatarHash is string hash ? ImageUrl.GuildUserAvatar(guildId, Id, hash, format) : null; /// - /// Gets the of the user's guild avatar decoration. + /// Gets the of the user's guild banner. /// - /// An pointing to the user's guild avatar decoration. If the user does not have one set, returns . - public ImageUrl? GetGuildAvatarDecorationUrl() => GuildAvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null; + /// The format of the returned . Defaults to (or for animated banners). + /// An pointing to the user's guild banner. If the user does not have one set, returns . + public ImageUrl? GetGuildBannerUrl(ImageFormat? format = null) => GuildBannerHash is string hash ? ImageUrl.GuildUserBanner(guildId, Id, hash, format) : null; /// /// Applies a timeout to the for a specified . @@ -41,7 +42,7 @@ public partial class GuildUser(JsonGuildUser jsonModel, ulong guildId, RestClien /// public async Task GetInfoAsync(RestRequestProperties? properties = null) { - await foreach (var info in _client.SearchGuildUsersAsync(GuildId, new() { Limit = 1, AndQuery = [new UserIdsGuildUsersSearchQuery([Id])] }, properties).ConfigureAwait(false)) + await foreach (var info in _client.SearchGuildUsersAsync(guildId, new() { Limit = 1, AndQuery = [new UserIdsGuildUsersSearchQuery([Id])] }, properties).ConfigureAwait(false)) return info; throw new EntityNotFoundException("The user was not found."); diff --git a/NetCord/ImageFormat.cs b/NetCord/ImageFormat.cs index 8558066f3..0230ccaf6 100644 --- a/NetCord/ImageFormat.cs +++ b/NetCord/ImageFormat.cs @@ -1,6 +1,6 @@ namespace NetCord; -public enum ImageFormat +public enum ImageFormat : byte { Jpeg, Png, diff --git a/NetCord/ImageUrl.cs b/NetCord/ImageUrl.cs index 77f0542a0..65593d17e 100644 --- a/NetCord/ImageUrl.cs +++ b/NetCord/ImageUrl.cs @@ -66,7 +66,8 @@ public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan private protected virtual bool IsSizeValid(ReadOnlySpan format) { - for (int i = 0; i < format.Length; i++) + int length = format.Length; + for (int i = 0; i < length; i++) { if (!char.IsAsciiDigit(format[i])) return false; @@ -202,14 +203,14 @@ public static ImageUrl Sticker(ulong stickerId, ImageFormat format) return new($"/stickers/{stickerId}", GetFormat(format), Discord.MediaUrl); } - public static ImageUrl RoleIcon(ulong roleId, ImageFormat format) + public static ImageUrl RoleIcon(ulong roleId, string iconHash, ImageFormat format) { - return new($"/role-icons/{roleId}/role_icon", GetFormat(format)); + return new($"/role-icons/{roleId}/{iconHash}", GetFormat(format)); } - public static ImageUrl GuildScheduledEventCover(ulong scheduledEventId, string coverHash, ImageFormat format) + public static ImageUrl GuildScheduledEventCover(ulong scheduledEventId, string coverImageHash, ImageFormat format) { - return new($"/guild-events/{scheduledEventId}/{coverHash}", GetFormat(format)); + return new($"/guild-events/{scheduledEventId}/{coverImageHash}", GetFormat(format)); } public static ImageUrl GuildUserBanner(ulong guildId, ulong userId, string bannerHash, ImageFormat? format) diff --git a/NetCord/JsonModels/JsonGuildScheduledEvent.cs b/NetCord/JsonModels/JsonGuildScheduledEvent.cs index cac467595..5a90cc1e4 100644 --- a/NetCord/JsonModels/JsonGuildScheduledEvent.cs +++ b/NetCord/JsonModels/JsonGuildScheduledEvent.cs @@ -45,4 +45,10 @@ public class JsonGuildScheduledEvent : JsonEntity [JsonPropertyName("user_count")] public int? UserCount { get; set; } + + [JsonPropertyName("image")] + public string? CoverImageHash { get; set; } + + [JsonPropertyName("recurrence_rule")] + public JsonGuildScheduledEventRecurrenceRule? RecurrenceRule { get; set; } } diff --git a/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRule.cs b/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRule.cs new file mode 100644 index 000000000..26f373b86 --- /dev/null +++ b/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRule.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonGuildScheduledEventRecurrenceRule +{ + [JsonPropertyName("start")] + public DateTimeOffset? StartAt { get; set; } + + [JsonPropertyName("end")] + public DateTimeOffset? EndAt { get; set; } + + [JsonPropertyName("frequency")] + public GuildScheduledEventRecurrenceRuleFrequency Frequency { get; set; } + + [JsonPropertyName("interval")] + public int Interval { get; set; } + + [JsonPropertyName("by_weekday")] + public GuildScheduledEventRecurrenceRuleWeekday? ByWeekday { get; set; } + + [JsonPropertyName("by_n_weekday")] + public JsonGuildScheduledEventRecurrenceRuleNWeekday ByNWeekday { get; set; } + + [JsonPropertyName("by_month")] + public GuildScheduledEventRecurrenceRuleMonth? ByMonth { get; set; } + + [JsonPropertyName("by_month_day")] + public int[]? ByMonthDay { get; set; } + + [JsonPropertyName("by_year_day")] + public int[]? ByYearDay { get; set; } + + [JsonPropertyName("count")] + public int? Count { get; } +} diff --git a/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRuleNWeekday.cs b/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRuleNWeekday.cs new file mode 100644 index 000000000..74f33d047 --- /dev/null +++ b/NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRuleNWeekday.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonGuildScheduledEventRecurrenceRuleNWeekday +{ + [JsonPropertyName("n")] + public int N { get; set; } + + [JsonPropertyName("day")] + public GuildScheduledEventRecurrenceRuleWeekday Day { get; set; } +} diff --git a/NetCord/JsonModels/JsonGuildUser.cs b/NetCord/JsonModels/JsonGuildUser.cs index ed1aa9809..485492dcb 100644 --- a/NetCord/JsonModels/JsonGuildUser.cs +++ b/NetCord/JsonModels/JsonGuildUser.cs @@ -13,6 +13,9 @@ public class JsonGuildUser [JsonPropertyName("avatar")] public string? GuildAvatarHash { get; set; } + [JsonPropertyName("banner")] + public string? GuildBannerHash { get; set; } + [JsonPropertyName("roles")] public ulong[] RoleIds { get; set; } diff --git a/NetCord/PartialGuildUser.cs b/NetCord/PartialGuildUser.cs index f9ba0aad3..a62dd04cd 100644 --- a/NetCord/PartialGuildUser.cs +++ b/NetCord/PartialGuildUser.cs @@ -30,6 +30,11 @@ public PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : base(jsonM /// public string? GuildAvatarHash => _jsonModel.GuildAvatarHash; + /// + /// The user's guild banner hash. + /// + public string? GuildBannerHash => _jsonModel.GuildBannerHash; + /// /// A list of IDs representing the user's current roles. /// @@ -85,8 +90,19 @@ public PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : base(jsonM /// public bool HasGuildAvatar => GuildAvatarHash is not null; + /// + /// Whether the user has a guild banner set. + /// + public bool HasGuildBanner => GuildBannerHash is not null; + /// /// Whether the user has a set avatar decoration. /// public bool HasGuildAvatarDecoration => GuildAvatarDecorationData is not null; + + /// + /// Gets the of the user's guild avatar decoration. + /// + /// An pointing to the user's guild avatar decoration. If the user does not have one set, returns . + public ImageUrl? GetGuildAvatarDecorationUrl() => GuildAvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null; } diff --git a/NetCord/PartialGuildUserExtensions.cs b/NetCord/PartialGuildUserExtensions.cs index 609f8edb3..98364dd4c 100644 --- a/NetCord/PartialGuildUserExtensions.cs +++ b/NetCord/PartialGuildUserExtensions.cs @@ -29,7 +29,8 @@ public static Permissions GetPermissions(this PartialGuildUser user, RestGuild g if (user.Id == guild.OwnerId) return (Permissions)ulong.MaxValue; - var permissions = guild.EveryoneRole.Permissions; + if (guild.EveryoneRole is not { Permissions: var permissions }) + throw new InvalidOperationException("Cannot calculate permissions based on a partial guild."); foreach (var role in user.GetRoles(guild)) permissions |= role.Permissions; diff --git a/NetCord/Rest/RestGuild.cs b/NetCord/Rest/RestGuild.cs index c7f69fc28..3650301ee 100644 --- a/NetCord/Rest/RestGuild.cs +++ b/NetCord/Rest/RestGuild.cs @@ -104,9 +104,9 @@ int GetHighestRolePosition(PartialGuildUser user) /// /// Gets the of the 's splash. /// - /// The format of the returned . Defaults to . + /// The format of the returned . /// An pointing to the guild's splash. If the guild does not have one set, returns . - public ImageUrl? GetSplashUrl(ImageFormat format = ImageFormat.Png) => SplashHash is string hash ? ImageUrl.GuildSplash(Id, hash, format) : null; + public ImageUrl? GetSplashUrl(ImageFormat format) => SplashHash is string hash ? ImageUrl.GuildSplash(Id, hash, format) : null; /// /// Whether the has a set discovery splash. @@ -121,9 +121,9 @@ int GetHighestRolePosition(PartialGuildUser user) /// /// Gets the of the 's discovery splash. /// - /// The format of the returned . Defaults to . + /// The format of the returned . /// An pointing to the guild's discovery splash. If the guild does not have one set, returns . - public ImageUrl? GetDiscoverySplashUrl(ImageFormat format = ImageFormat.Png) => DiscoverySplashHash is string hash ? ImageUrl.GuildDiscoverySplash(Id, hash, format) : null; + public ImageUrl? GetDiscoverySplashUrl(ImageFormat format) => DiscoverySplashHash is string hash ? ImageUrl.GuildDiscoverySplash(Id, hash, format) : null; /// /// if the user is the owner of the . @@ -329,5 +329,8 @@ int GetHighestRolePosition(PartialGuildUser user) /// /// The guild's base role, applied to all users. /// - public Role EveryoneRole => Roles[Id]; + /// The guild's base role, applied to all users or if the guild is partial. + public Role? EveryoneRole => Roles.GetValueOrDefault(Id); + + public ImageUrl GetWidgetUrl(GuildWidgetStyle? style = null, string? hostname = null, ApiVersion? version = null) => ImageUrl.GuildWidget(Id, style, hostname, version); } diff --git a/NetCord/Role.cs b/NetCord/Role.cs index e339ea146..51aa4b32c 100644 --- a/NetCord/Role.cs +++ b/NetCord/Role.cs @@ -47,6 +47,8 @@ public Role(JsonRole jsonModel, ulong guildId, RestClient client) : base(client) GuildId = guildId; } + public ImageUrl? GetIconUrl(ImageFormat format) => IconHash is string hash ? ImageUrl.RoleIcon(Id, hash, format) : null; + public override string ToString() => $"<@&{Id}>"; public override bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) => Mention.TryFormatRole(destination, out charsWritten, Id); diff --git a/NetCord/Sticker.cs b/NetCord/Sticker.cs index 9d3e3df91..0d7850452 100644 --- a/NetCord/Sticker.cs +++ b/NetCord/Sticker.cs @@ -20,4 +20,6 @@ private protected Sticker(JsonModels.JsonSticker jsonModel) _jsonModel = jsonModel; Tags = _jsonModel.Tags.Split(','); } + + public ImageUrl GetImageUrl(ImageFormat format) => ImageUrl.Sticker(Id, format); } diff --git a/NetCord/Team.cs b/NetCord/Team.cs index b7dcdc79e..768e93b97 100644 --- a/NetCord/Team.cs +++ b/NetCord/Team.cs @@ -15,4 +15,6 @@ public class Team(JsonModels.JsonTeam jsonModel, RestClient client) : Entity, IJ public string Name => jsonModel.Name; public ulong OwnerId => jsonModel.OwnerId; + + public ImageUrl? GetIconUrl(ImageFormat format) => IconHash is string hash ? ImageUrl.TeamIcon(Id, hash, format) : null; }