Skip to content

Commit

Permalink
Add image URL methods (#42)
Browse files Browse the repository at this point in the history
* Add image URL methods and recurrence rule support

- Added methods to `Application`, `CustomEmoji`, `Role`, `Sticker`, and `Team` classes for retrieving image URLs.
- Enhanced `GuildScheduledEvent` and `JsonGuildScheduledEvent` with cover image hashes and recurrence rules.
- Modified `GuildUser` to use expression-bodied member for `GuildId`.
- Updated `ImageFormat` enum to inherit from `byte`.
- Improved `ImageUrl` class with new URL generation methods and optimized `IsSizeValid`.
- Added `GuildBannerHash` property to `JsonGuildUser`.
- Enhanced `PartialGuildUser` with properties for guild banners and avatar decorations.
- Updated `RestGuild` with widget URL generation and modified `EveryoneRole` property.
- Introduced new classes and enums for guild scheduled event recurrence rules.
- Added JSON model classes for recurrence rule functionality.

* Improve exception

* Fix xml docs
  • Loading branch information
KubaZ2 authored Sep 1, 2024
1 parent d2c2a92 commit 0178040
Showing 21 changed files with 205 additions and 18 deletions.
10 changes: 10 additions & 0 deletions NetCord/Application.cs
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 2 additions & 0 deletions NetCord/CustomEmoji.cs
Original file line number Diff line number Diff line change
@@ -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 ? $"<a:{Name}:{Id}>" : $"<:{Name}:{Id}>";

public string ToString(string? format, IFormatProvider? formatProvider) => ToString();
12 changes: 12 additions & 0 deletions NetCord/GuildScheduledEvent.cs
Original file line number Diff line number Diff line change
@@ -37,12 +37,24 @@ public partial class GuildScheduledEvent : ClientEntity, IJsonModel<JsonModels.J

public int? UserCount => _jsonModel.UserCount;

public string? CoverImageHash => _jsonModel.CoverImageHash;

public GuildScheduledEventRecurrenceRule? RecurrenceRule { get; }

public GuildScheduledEvent(JsonModels.JsonGuildScheduledEvent jsonModel, RestClient client) : base(client)
{
_jsonModel = jsonModel;

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;
}
28 changes: 28 additions & 0 deletions NetCord/GuildScheduledEventRecurrenceRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using NetCord.JsonModels;

namespace NetCord;

public class GuildScheduledEventRecurrenceRule(JsonGuildScheduledEventRecurrenceRule jsonModel) : IJsonModel<JsonGuildScheduledEventRecurrenceRule>
{
JsonGuildScheduledEventRecurrenceRule IJsonModel<JsonGuildScheduledEventRecurrenceRule>.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<int>? ByMonthDay => jsonModel.ByMonthDay;

public IReadOnlyList<int>? ByYearDay => jsonModel.ByYearDay;

public int? Count => jsonModel.Count;
}
9 changes: 9 additions & 0 deletions NetCord/GuildScheduledEventRecurrenceRuleFrequency.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace NetCord;

public enum GuildScheduledEventRecurrenceRuleFrequency : byte
{
Yearly = 0,
Monthly = 1,
Weekly = 2,
Daily = 3,
}
17 changes: 17 additions & 0 deletions NetCord/GuildScheduledEventRecurrenceRuleMonth.cs
Original file line number Diff line number Diff line change
@@ -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,
}
12 changes: 12 additions & 0 deletions NetCord/GuildScheduledEventRecurrenceRuleNWeekday.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using NetCord.JsonModels;

namespace NetCord;

public class GuildScheduledEventRecurrenceRuleNWeekday(JsonGuildScheduledEventRecurrenceRuleNWeekday jsonModel) : IJsonModel<JsonGuildScheduledEventRecurrenceRuleNWeekday>
{
JsonGuildScheduledEventRecurrenceRuleNWeekday IJsonModel<JsonGuildScheduledEventRecurrenceRuleNWeekday>.JsonModel => jsonModel;

public int N => jsonModel.N;

public GuildScheduledEventRecurrenceRuleWeekday Day => jsonModel.Day;
}
12 changes: 12 additions & 0 deletions NetCord/GuildScheduledEventRecurrenceRuleWeekday.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NetCord;

public enum GuildScheduledEventRecurrenceRuleWeekday : byte
{
Monday = 0,
Tuesday = 1,
Wednesday = 2,
Thursday = 3,
Friday = 4,
Saturday = 5,
Sunday = 6,
}
13 changes: 7 additions & 6 deletions NetCord/GuildUser.cs
Original file line number Diff line number Diff line change
@@ -11,20 +11,21 @@ public partial class GuildUser(JsonGuildUser jsonModel, ulong guildId, RestClien
/// <summary>
/// The ID of the guild the <see cref="GuildUser"/> object belongs to.
/// </summary>
public ulong GuildId { get; } = guildId;
public ulong GuildId => guildId;

/// <summary>
/// Gets the <see cref="ImageUrl"/> of the user's guild avatar.
/// </summary>
/// <param name="format">The format of the returned <see cref="ImageUrl"/>. Defaults to <see cref="ImageFormat.Png"/> (or <see cref="ImageFormat.Gif"/> for animated avatars).</param>
/// <returns>An <see cref="ImageUrl"/> pointing to the user's guild avatar. If the user does not have one set, returns <see langword="null"/>.</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;

/// <summary>
/// Gets the <see cref="ImageUrl"/> of the user's guild avatar decoration.
/// Gets the <see cref="ImageUrl"/> of the user's guild banner.
/// </summary>
/// <returns>An <see cref="ImageUrl"/> pointing to the user's guild avatar decoration. If the user does not have one set, returns <see langword="null"/>.</returns>
public ImageUrl? GetGuildAvatarDecorationUrl() => GuildAvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null;
/// <param name="format">The format of the returned <see cref="ImageUrl"/>. Defaults to <see cref="ImageFormat.Png"/> (or <see cref="ImageFormat.Gif"/> for animated banners).</param>
/// <returns>An <see cref="ImageUrl"/> pointing to the user's guild banner. If the user does not have one set, returns <see langword="null"/>.</returns>
public ImageUrl? GetGuildBannerUrl(ImageFormat? format = null) => GuildBannerHash is string hash ? ImageUrl.GuildUserBanner(guildId, Id, hash, format) : null;

/// <summary>
/// Applies a timeout to the <see cref="GuildUser"/> for a specified <see cref="DateTimeOffset"/>.
@@ -41,7 +42,7 @@ public partial class GuildUser(JsonGuildUser jsonModel, ulong guildId, RestClien
/// <exception cref="EntityNotFoundException"/>
public async Task<GuildUserInfo> 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.");
2 changes: 1 addition & 1 deletion NetCord/ImageFormat.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NetCord;

public enum ImageFormat
public enum ImageFormat : byte
{
Jpeg,
Png,
11 changes: 6 additions & 5 deletions NetCord/ImageUrl.cs
Original file line number Diff line number Diff line change
@@ -66,7 +66,8 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan

private protected virtual bool IsSizeValid(ReadOnlySpan<char> 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)
6 changes: 6 additions & 0 deletions NetCord/JsonModels/JsonGuildScheduledEvent.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
36 changes: 36 additions & 0 deletions NetCord/JsonModels/JsonGuildScheduledEventRecurrenceRule.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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; }
}
3 changes: 3 additions & 0 deletions NetCord/JsonModels/JsonGuildUser.cs
Original file line number Diff line number Diff line change
@@ -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; }

16 changes: 16 additions & 0 deletions NetCord/PartialGuildUser.cs
Original file line number Diff line number Diff line change
@@ -30,6 +30,11 @@ public PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : base(jsonM
/// </summary>
public string? GuildAvatarHash => _jsonModel.GuildAvatarHash;

/// <summary>
/// The user's guild banner hash.
/// </summary>
public string? GuildBannerHash => _jsonModel.GuildBannerHash;

/// <summary>
/// A list of <see cref="ulong"/> IDs representing the user's current roles.
/// </summary>
@@ -85,8 +90,19 @@ public PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : base(jsonM
/// </summary>
public bool HasGuildAvatar => GuildAvatarHash is not null;

/// <summary>
/// Whether the user has a guild banner set.
/// </summary>
public bool HasGuildBanner => GuildBannerHash is not null;

/// <summary>
/// Whether the user has a set avatar decoration.
/// </summary>
public bool HasGuildAvatarDecoration => GuildAvatarDecorationData is not null;

/// <summary>
/// Gets the <see cref="ImageUrl"/> of the user's guild avatar decoration.
/// </summary>
/// <returns>An <see cref="ImageUrl"/> pointing to the user's guild avatar decoration. If the user does not have one set, returns <see langword="null"/>.</returns>
public ImageUrl? GetGuildAvatarDecorationUrl() => GuildAvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null;
}
3 changes: 2 additions & 1 deletion NetCord/PartialGuildUserExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 8 additions & 5 deletions NetCord/Rest/RestGuild.cs
Original file line number Diff line number Diff line change
@@ -104,9 +104,9 @@ int GetHighestRolePosition(PartialGuildUser user)
/// <summary>
/// Gets the <see cref="ImageUrl"/> of the <see cref="RestGuild"/>'s splash.
/// </summary>
/// <param name="format">The format of the returned <see cref="ImageUrl"/>. Defaults to <see cref="ImageFormat.Png"/>.</param>
/// <param name="format">The format of the returned <see cref="ImageUrl"/>.</param>
/// <returns>An <see cref="ImageUrl"/> pointing to the guild's splash. If the guild does not have one set, returns <see langword="null"/>.</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;

/// <summary>
/// Whether the <see cref="RestGuild"/> has a set discovery splash.
@@ -121,9 +121,9 @@ int GetHighestRolePosition(PartialGuildUser user)
/// <summary>
/// Gets the <see cref="ImageUrl"/> of the <see cref="RestGuild"/>'s discovery splash.
/// </summary>
/// <param name="format">The format of the returned <see cref="ImageUrl"/>. Defaults to <see cref="ImageFormat.Png"/>.</param>
/// <param name="format">The format of the returned <see cref="ImageUrl"/>.</param>
/// <returns>An <see cref="ImageUrl"/> pointing to the guild's discovery splash. If the guild does not have one set, returns <see langword="null"/>.</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;

/// <summary>
/// <see langword="true"/> if the user is the owner of the <see cref="RestGuild"/>.
@@ -329,5 +329,8 @@ int GetHighestRolePosition(PartialGuildUser user)
/// <summary>
/// The guild's base role, applied to all users.
/// </summary>
public Role EveryoneRole => Roles[Id];
/// <returns>The guild's base role, applied to all users or <see langword="null"/> if the guild is partial.</returns>
public Role? EveryoneRole => Roles.GetValueOrDefault(Id);

public ImageUrl GetWidgetUrl(GuildWidgetStyle? style = null, string? hostname = null, ApiVersion? version = null) => ImageUrl.GuildWidget(Id, style, hostname, version);
}
2 changes: 2 additions & 0 deletions NetCord/Role.cs
Original file line number Diff line number Diff line change
@@ -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<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null) => Mention.TryFormatRole(destination, out charsWritten, Id);
2 changes: 2 additions & 0 deletions NetCord/Sticker.cs
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 2 additions & 0 deletions NetCord/Team.cs
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 0178040

Please sign in to comment.