Skip to content

Commit

Permalink
Fix guild images not updating
Browse files Browse the repository at this point in the history
  • Loading branch information
ptlthg committed Dec 20, 2024
1 parent 5bf3c03 commit 66b45cf
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 23 deletions.
18 changes: 17 additions & 1 deletion EliteAPI/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class AdminController(
IMapper mapper,
IObjectStorageService objectStorage,
IMojangService mojangService,
UserManager<ApiUser> userManager)
UserManager<ApiUser> userManager,
IDiscordService discordService)
: ControllerBase
{

Expand Down Expand Up @@ -269,4 +270,19 @@ public async Task<ActionResult> ClearPlayerCooldowns(string playerId) {

return Ok();
}

/// <summary>
/// Refresh a Discord guild
/// </summary>
/// <param name="guildId"></param>
/// <returns></returns>
[Authorize(ApiUserPolicies.Moderator)]
[HttpPost("/guild/{guildId:long}/refresh")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(string))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(string))]
public async Task<ActionResult> RefreshGuild(long guildId) {
await discordService.RefreshDiscordGuild((ulong) guildId, replaceImages: true);
return Ok();
}
}
33 changes: 17 additions & 16 deletions EliteAPI/Services/DiscordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ public async Task FetchUserRoles(GuildMember member) {
}
}

public async Task<Guild?> GetGuild(ulong guildId, bool skipCache = false) {
public async Task<Guild?> GetGuild(ulong guildId, bool skipCache = false, bool replaceImages = false) {
var existing = await context.Guilds
.Include(g => g.Channels)
.Include(g => g.Roles)
Expand All @@ -398,18 +398,18 @@ public async Task FetchUserRoles(GuildMember member) {
var guild = await response.Content.ReadFromJsonAsync<FullDiscordGuild>();
if (guild is null) return existing;

return await UpdateDiscordGuild(guild);
return await UpdateDiscordGuild(guild, replaceImages);
} catch (Exception e) {
logger.LogError(e, "Failed to parse guild from Discord");
return existing;
}
}

public async Task RefreshDiscordGuild(ulong guildId) {
await GetGuild(guildId, true);
public async Task RefreshDiscordGuild(ulong guildId, bool replaceImages = false) {
await GetGuild(guildId, true, replaceImages: replaceImages);
}

private async Task<Guild?> UpdateDiscordGuild(FullDiscordGuild? incoming, bool fetchChannels = true) {
private async Task<Guild?> UpdateDiscordGuild(FullDiscordGuild? incoming, bool fetchChannels = true, bool replaceImages = true) {
if (incoming is null) return null;
if (!ulong.TryParse(incoming.Id, out var guildId)) return null;

Expand All @@ -435,19 +435,19 @@ public async Task RefreshDiscordGuild(ulong guildId) {
guild.MemberCount = incoming.MemberCount;
guild.LastUpdated = DateTimeOffset.UtcNow;

if (guild.Icon?.Hash != incoming.Icon && incoming.Icon is not null) {
if ((replaceImages || guild.Icon?.Hash != incoming.Icon) && incoming.Icon is not null) {
if (guild.Icon is null) {
guild.Icon = await UpdateGuildIcon(guildId, incoming.Icon);
guild.Icon = await UpdateGuildIcon(guildId, incoming.Icon, force: replaceImages);
} else {
await UpdateGuildIcon(guildId, incoming.Icon, guild.Icon);
await UpdateGuildIcon(guildId, incoming.Icon, guild.Icon, force: replaceImages);
}
}

if (guild.IsPublic && guild.Banner?.Hash != incoming.Splash && incoming.Splash is not null) {
if (guild.IsPublic && (replaceImages || guild.Banner?.Hash != incoming.Splash) && incoming.Splash is not null) {
if (guild.Banner is null) {
guild.Banner = await UpdateGuildBanner(guildId, incoming.Splash);
guild.Banner = await UpdateGuildBanner(guildId, incoming.Splash, force: replaceImages);
} else {
await UpdateGuildBanner(guildId, incoming.Splash, guild.Banner);
await UpdateGuildBanner(guildId, incoming.Splash, guild.Banner, force: replaceImages);
}
}

Expand Down Expand Up @@ -504,7 +504,7 @@ await context.GuildRoles
return guild;
}

public async Task<Image?> UpdateGuildIcon(ulong guildId, string iconHash, Image? image = null) {
public async Task<Image?> UpdateGuildIcon(ulong guildId, string iconHash, Image? image = null, bool force = false) {
try {
var iconType = iconHash.StartsWith("a_") ? "gif" : "webp";
var newPath = $"guilds/{guildId}/icons/{iconHash}.{iconType}";
Expand All @@ -514,21 +514,22 @@ await context.GuildRoles
return await objectStorageService.UploadImageAsync(path: newPath, remoteUrl: remoteUrl);
}

if (image.Path == newPath) {
if (image.Path == newPath && !force) {
return image; // Same path means the image is already up to date
}

await objectStorageService.UpdateImageAsync(image: image, newPath: newPath, remoteUrl: remoteUrl);
image.Hash = iconHash;

return image;
} catch {
} catch (Exception e) {
logger.LogError(e.Message);
logger.LogWarning("Failed to fetch guild icon from Discord for guild {GuildId}", guildId);
return null;
}
}

public async Task<Image?> UpdateGuildBanner(ulong guildId, string bannerHash, Image? image = null) {
public async Task<Image?> UpdateGuildBanner(ulong guildId, string bannerHash, Image? image = null, bool force = false) {
try {
var newPath = $"guilds/{guildId}/{bannerHash}.webp";
var remoteUrl = $"https://cdn.discordapp.com/splashes/{guildId}/{bannerHash}.webp?size=1280";
Expand All @@ -537,7 +538,7 @@ await context.GuildRoles
return await objectStorageService.UploadImageAsync(path: newPath, remoteUrl: remoteUrl);
}

if (image.Path == newPath) {
if (image.Path == newPath && !force) {
return image; // Same path means the image is already up to date
}

Expand Down
8 changes: 4 additions & 4 deletions EliteAPI/Services/Interfaces/IDiscordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public interface IDiscordService
Task<GuildMember?> GetGuildMember(ClaimsPrincipal user, ulong guildId);
Task<GuildMember?> GetGuildMemberIfAdmin(ClaimsPrincipal user, ulong guildId, GuildPermission permission = GuildPermission.Role);
Task FetchUserRoles(GuildMember member);
Task<Guild?> GetGuild(ulong guildId, bool skipCache = false);
Task RefreshDiscordGuild(ulong guildId);
Task<Image?> UpdateGuildBanner(ulong guildId, string bannerHash, Image? image = null);
Task<Image?> UpdateGuildIcon(ulong guildId, string iconHash, Image? image = null);
Task<Guild?> GetGuild(ulong guildId, bool skipCache = false, bool replaceImages = false);
Task RefreshDiscordGuild(ulong guildId, bool replaceImages = false);
Task<Image?> UpdateGuildBanner(ulong guildId, string bannerHash, Image? image = null, bool force = false);
Task<Image?> UpdateGuildIcon(ulong guildId, string iconHash, Image? image = null, bool force = false);
}
4 changes: 2 additions & 2 deletions EliteAPI/Services/ObjectStorageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public ObjectStorageService(IConfiguration config, ILogger<ObjectStorageService>
_bucketName = config["S3:BucketName"] ?? "elite";

if (string.IsNullOrEmpty(accessKey) || string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(endpoint)) {
_logger.LogWarning("S3 credentials not found, ObjectStorageService will not be available.");
_logger.LogWarning("S3 credentials not found, ObjectStorageService will not be available");
return;
}

Expand Down Expand Up @@ -90,7 +90,7 @@ private async Task<IFormFile> DownloadImageAsync(string remoteUrl, CancellationT
throw new InvalidOperationException($"Response from {remoteUrl} is not an image");
}

await using var stream = await response.Content.ReadAsStreamAsync(token);
var stream = await response.Content.ReadAsStreamAsync(token);
if (stream is null) {
throw new InvalidOperationException($"Failed to read image from {remoteUrl}");
}
Expand Down

0 comments on commit 66b45cf

Please sign in to comment.