// 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.API.Abstractions.Objects;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
public class GuildEmojisUpdateResponder(
ILogger logger,
DatabaseContext db,
EmojiCache emojiCache,
WebhookExecutorService webhookExecutor
) : IResponder
{
private readonly ILogger _logger = logger.ForContext();
public async Task RespondAsync(IGuildEmojisUpdate evt, CancellationToken ct = default)
{
try
{
if (!emojiCache.TryGet(evt.GuildID, out var oldEmoji))
{
_logger.Information(
"Previous emoji for {GuildId} were not in cache, ignoring event",
evt.GuildID
);
return Result.Success;
}
IEmbed embed;
// As far as I know, only one emoji can be added or removed at once.
var added = evt.Emojis.FirstOrDefault(e => oldEmoji.All(o => o.ID != e.ID));
var removed = oldEmoji.FirstOrDefault(o => evt.Emojis.All(e => o.ID != e.ID));
var updated = evt.Emojis.FirstOrDefault(e =>
oldEmoji.Any(o => o.ID == e.ID && o.Name != e.Name)
);
if (added != null)
{
var url = CDN.GetEmojiUrl(added).GetOrThrow().ToString();
embed = new EmbedBuilder()
.WithTitle("Emoji created")
.WithDescription($"{FormatEmoji(added)} [{added.Name}]({url})")
.WithThumbnailUrl(url)
.WithFooter($"ID: {added.ID}")
.WithColour(DiscordUtils.Green)
.WithCurrentTimestamp()
.Build()
.GetOrThrow();
}
else if (removed != null)
{
var url = CDN.GetEmojiUrl(removed).GetOrThrow().ToString();
embed = new EmbedBuilder()
.WithTitle("Emoji removed")
.WithDescription($"[{removed.Name}]({url})")
.WithThumbnailUrl(url)
.WithFooter($"ID: {removed.ID}")
.WithColour(DiscordUtils.Red)
.WithCurrentTimestamp()
.Build()
.GetOrThrow();
}
else if (updated != null)
{
var url = CDN.GetEmojiUrl(updated).GetOrThrow().ToString();
var previous = oldEmoji.First(o => o.ID == updated.ID);
embed = new EmbedBuilder()
.WithTitle("Emoji renamed")
.WithDescription(
$"""
{FormatEmoji(updated)} [{updated.Name}]({url})
{previous.Name} → {updated.Name}
"""
)
.WithThumbnailUrl(url)
.WithFooter($"ID: {updated.ID}")
.WithColour(DiscordUtils.Green)
.WithCurrentTimestamp()
.Build()
.GetOrThrow();
}
else
{
_logger.Warning(
"Received emoji update event for {GuildId} but all emoji were identical, not logging",
evt.GuildID
);
return Result.Success;
}
var guildConfig = await db.GetGuildAsync(evt.GuildID, false, ct);
webhookExecutor.QueueLog(guildConfig, LogChannelType.GuildEmojisUpdate, embed);
return Result.Success;
}
finally
{
emojiCache.Set(evt.GuildID, evt.Emojis);
}
}
private static string FormatEmoji(IEmoji emoji) =>
emoji.IsAnimated.OrDefault(false)
? $""
: $"<:{emoji.Name}:{emoji.ID}>";
}