using Catalogger.Backend.Cache; 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.Extensions.Embeds; using Remora.Discord.Gateway.Responders; using Remora.Results; namespace Catalogger.Backend.Bot.Responders.Guilds; public class GuildMemberRemoveResponder( ILogger logger, DatabaseContext db, IMemberCache memberCache, RoleCache roleCache, WebhookExecutorService webhookExecutor, AuditLogEnrichedResponderService auditLogEnrichedResponderService) : IResponder { private readonly ILogger _logger = logger.ForContext(); public async Task RespondAsync(IGuildMemberRemove evt, CancellationToken ct = default) { try { // spin events that Discord doesn't send us all the data for off to another responder _ = auditLogEnrichedResponderService.RespondAsync(evt); var embed = new EmbedBuilder() .WithTitle("Member left") .WithAuthor(evt.User.Tag(), iconUrl: evt.User.AvatarUrl()) .WithColour(DiscordUtils.Orange) .WithDescription($"<@{evt.User.ID}>") .WithFooter($"ID: {evt.User.ID}") .WithCurrentTimestamp(); var guildConfig = await db.GetGuildAsync(evt.GuildID, ct); var member = await memberCache.TryGetAsync(evt.GuildID, evt.User.ID); if (member == null) { _logger.Information( "Guild member {UserId} in {GuildId} left but wasn't in the cache, sending limited embed", evt.User.ID, evt.GuildID); webhookExecutor.QueueLog(guildConfig, LogChannelType.GuildMemberRemove, embed.Build().GetOrThrow()); return Result.Success; } embed.Description += $"\njoined \n({member.JoinedAt.Prettify()} ago)"; // get the member's roles, sort them, and turn them into mentions var guildRoles = roleCache.GuildRoles(evt.GuildID); var roles = guildRoles.Sorted(member.Roles).ToList(); var roleMentions = ""; foreach (var (idx, role) in roles.Select((r, i) => (i, r))) { if (roleMentions.Length > 900) { roleMentions += $"\n(too many roles to list, showing {idx}/{roles.Count})"; break; } roleMentions += $"<@&{role.ID}>"; if (idx != roles.Count - 1) roleMentions += ", "; } embed.AddField("Roles", roleMentions, inline: false); webhookExecutor.QueueLog(guildConfig, LogChannelType.GuildMemberRemove, embed.Build().GetOrThrow()); return Result.Success; } finally { await memberCache.RemoveAsync(evt.GuildID, evt.User.ID); } } }