diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs new file mode 100644 index 0000000..4ee63f6 --- /dev/null +++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs @@ -0,0 +1,122 @@ +// Copyright (C) 2021-present sam (starshines.gay) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using Catalogger.Backend.Cache.InMemoryCache; +using Catalogger.Backend.Database; +using Catalogger.Backend.Database.Queries; +using Catalogger.Backend.Extensions; +using Catalogger.Backend.Services; +using Remora.Discord.API; +using Remora.Discord.API.Abstractions.Gateway.Events; +using Remora.Discord.Extensions.Embeds; +using Remora.Discord.Gateway.Responders; +using Remora.Results; + +namespace Catalogger.Backend.Bot.Responders.Guilds; + +public class GuildUpdateResponder( + ILogger logger, + DatabaseContext db, + GuildCache guildCache, + UserCache userCache, + WebhookExecutorService webhookExecutor +) : IResponder +{ + private readonly ILogger _logger = logger.ForContext(); + + public async Task RespondAsync(IGuildUpdate evt, CancellationToken ct = default) + { + try + { + if (!guildCache.TryGet(evt.ID, out var oldGuild)) + { + _logger.Warning( + "Guild {GuildId} not found in cache, ignoring event and adding it to cache", + evt.ID + ); + return Result.Success; + } + + var embed = new EmbedBuilder() + .WithTitle("Server updated") + .WithColour(DiscordUtils.Blue) + .WithCurrentTimestamp(); + + if (evt.Name != oldGuild.Name) + embed.AddField("Name", $"**Before:** {oldGuild.Name}\n**After:** {evt.Name}"); + + if (!Equals(evt.Icon, oldGuild.Icon)) + { + if (evt.Icon != null) + embed.WithThumbnailUrl( + CDN.GetGuildIconUrl(evt, imageSize: 1024).GetOrThrow().ToString() + ); + + if (evt.Icon != null && oldGuild.Icon == null) + { + embed.AddField( + "Icon added", + $"[Link]({CDN.GetGuildIconUrl(evt, imageSize: 1024).GetOrThrow()})" + ); + } + else if (evt.Icon != null && oldGuild.Icon != null) + { + embed.AddField( + "Icon changed", + $"[Link]({CDN.GetGuildIconUrl(evt, imageSize: 1024).GetOrThrow()})" + ); + } + else + { + embed.AddField("Icon removed", "*(old icon no longer available, sorry)*"); + } + } + + if (evt.OwnerID != oldGuild.OwnerID) + { + embed.AddField( + "Ownership transferred", + $""" + **Before:** {userCache.TryFormatUserAsync(oldGuild.OwnerID)} + **After:** {userCache.TryFormatUserAsync(evt.OwnerID)} + """ + ); + } + + if (embed.Fields.Count != 0) + { + var guildConfig = await db.GetGuildAsync(evt.ID, ct); + webhookExecutor.QueueLog( + guildConfig, + LogChannelType.GuildUpdate, + embed.Build().GetOrThrow() + ); + } + else + { + _logger.Debug( + "Guild update event for {GuildId} had nothing we want to log, not sending embed", + evt.ID + ); + } + + return Result.Success; + } + finally + { + guildCache.Set(evt); + } + } +} diff --git a/Catalogger.Backend/Extensions/DiscordExtensions.cs b/Catalogger.Backend/Extensions/DiscordExtensions.cs index 78500b1..02dc1d0 100644 --- a/Catalogger.Backend/Extensions/DiscordExtensions.cs +++ b/Catalogger.Backend/Extensions/DiscordExtensions.cs @@ -36,6 +36,7 @@ public static class DiscordExtensions : $"{user.Username.Value}#{discriminator:0000}"; } + // TODO: replace these avatar URL methods with the built-in CDN.* methods? public static string AvatarUrl(this IUser user, int size = 256) { if (user.Avatar != null)