using System.Diagnostics; 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.Abstractions.Gateway.Events; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.Extensions.Embeds; using Remora.Discord.Gateway.Responders; using Remora.Rest.Core; using Remora.Results; namespace Catalogger.Backend.Bot.Responders.Channels; public class ChannelUpdateResponder( ILogger logger, DatabaseContext db, ChannelCache channelCache, RoleCache roleCache, UserCache userCache, WebhookExecutorService webhookExecutor) : IResponder { private readonly ILogger _logger = logger.ForContext(); public async Task RespondAsync(IChannelUpdate evt, CancellationToken ct = default) { try { if (!channelCache.TryGet(evt.ID, out var oldChannel)) { _logger.Debug("Updated channel {ChannelId} wasn't in the cache", evt.ID); return Result.Success; } var guildConfig = await db.GetGuildAsync(evt.GuildID.Value, ct); var builder = new EmbedBuilder() .WithTitle(evt.Type switch { ChannelType.GuildVoice => "Voice channel created", ChannelType.GuildCategory => "Category channel created", ChannelType.GuildAnnouncement or ChannelType.GuildText => "Text channel created", _ => "Channel created" }) .WithColour(DiscordUtils.Blue) .WithFooter($"ID: {evt.ID} | Name: {evt.Name}") .WithCurrentTimestamp(); if (oldChannel.ParentID != evt.ParentID) builder.AddField("Category", CategoryUpdate(oldChannel.ParentID.OrDefault(), evt.ParentID.OrDefault())); if (oldChannel.Name != evt.Name) builder.AddField("Name", $"**Before:** {oldChannel.Name}\n**After:** {evt.Name}"); if (oldChannel.Topic != evt.Topic) { var oldTopic = oldChannel.Topic.OrDefault() ?? "(none)"; var newTopic = evt.Topic.OrDefault() ?? "(none)"; var topicField = $"**Before:** {oldTopic}\n\n**After:** {newTopic}"; if (topicField.Length > 1000) topicField = topicField[..1000] + "…"; builder.AddField("Description", topicField); } var oldOverrides = oldChannel.PermissionOverwrites.OrDefault() ?? []; var newOverrides = evt.PermissionOverwrites.OrDefault() ?? []; var addedOverrides = newOverrides.Where(o => oldOverrides.All(o2 => o.ID != o2.ID)); var removedOverrides = oldOverrides.Where(o => newOverrides.All(o2 => o.ID != o2.ID)).ToList(); // Overrides filtered to ones that exist in both lists, but have different allow or deny values var editedOverrides = newOverrides.Where(o => oldOverrides.Any(o2 => o.ID == o2.ID && (o.Allow.Value != o2.Allow.Value || o.Deny.Value != o2.Deny.Value))); if (removedOverrides.Count != 0) { var removedOverrideNames = new List(); foreach (var o in removedOverrides) { if (o.Type is PermissionOverwriteType.Member) { var user = await userCache.GetUserAsync(o.ID); removedOverrideNames.Add(user != null ? $"<@{user.ID}>" : $"user {o.ID}"); } else { removedOverrideNames.Add(roleCache.TryGet(o.ID, out var role) ? role.Name : $"role {o.ID}"); break; } } builder.AddField("Removed overrides", string.Join(", ", removedOverrideNames)); } await webhookExecutor.QueueLogAsync(guildConfig, LogChannelType.ChannelUpdate, builder.Build().GetOrThrow()); } finally { channelCache.Set(evt); } throw new NotImplementedException(); } private string CategoryUpdate(Snowflake? oldCategory, Snowflake? newCategory) { var value = ""; if (oldCategory != null && channelCache.TryGet(oldCategory.Value, out var oldChannel)) value += $"**Before:** {oldChannel.Name}"; if (newCategory != null && channelCache.TryGet(newCategory.Value, out var newChannel)) value += $"\n**After:** {newChannel.Name}"; return value.Trim(); } }