Fix Discord merging webhook messages with same username

Closes #33.
This commit is contained in:
spiral 2021-08-03 13:44:22 -04:00
parent c691adf8c9
commit 25f96dd920
No known key found for this signature in database
GPG Key ID: A6059F0CA0E1BD31
3 changed files with 102 additions and 6 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Myriad.Gateway;
@ -17,11 +18,13 @@ namespace PluralKit.Bot
private readonly IDatabase _db;
private readonly ModelRepository _repo;
private readonly ILogger _logger;
private readonly LastMessageCacheService _lastMessage;
public MessageDeleted(ILogger logger, IDatabase db, ModelRepository repo)
public MessageDeleted(ILogger logger, IDatabase db, ModelRepository repo, LastMessageCacheService lastMessage)
{
_db = db;
_repo = repo;
_lastMessage = lastMessage;
_logger = logger.ForContext<MessageDeleted>();
}
@ -36,6 +39,8 @@ namespace PluralKit.Bot
await _db.Execute(c => _repo.DeleteMessage(c, evt.Id));
}
_lastMessage.HandleMessageDeletion(evt.ChannelId, evt.Id);
// Fork a task to delete the message after a short delay
// to allow for lookups to happen for a little while after deletion
_ = Inner();
@ -54,6 +59,7 @@ namespace PluralKit.Bot
await _db.Execute(c => _repo.DeleteMessagesBulk(c, evt.Ids));
}
_lastMessage.HandleMessageDeletion(evt.ChannelId, evt.Ids.ToList());
_ = Inner();
return Task.CompletedTask;
}

View File

@ -33,10 +33,11 @@ namespace PluralKit.Bot
private readonly ProxyMatcher _matcher;
private readonly IMetrics _metrics;
private readonly IDiscordCache _cache;
private readonly LastMessageCacheService _lastMessage;
private readonly DiscordApiClient _rest;
public ProxyService(LogChannelService logChannel, ILogger logger,
WebhookExecutorService webhookExecutor, IDatabase db, ProxyMatcher matcher, IMetrics metrics, ModelRepository repo, IDiscordCache cache, DiscordApiClient rest)
public ProxyService(LogChannelService logChannel, ILogger logger, WebhookExecutorService webhookExecutor, IDatabase db,
ProxyMatcher matcher, IMetrics metrics, ModelRepository repo, IDiscordCache cache, DiscordApiClient rest, LastMessageCacheService lastMessage)
{
_logChannel = logChannel;
_webhookExecutor = webhookExecutor;
@ -45,6 +46,7 @@ namespace PluralKit.Bot
_metrics = metrics;
_repo = repo;
_cache = cache;
_lastMessage = lastMessage;
_rest = rest;
_logger = logger.ForContext<ProxyService>();
}
@ -142,7 +144,7 @@ namespace PluralKit.Bot
GuildId = trigger.GuildId!.Value,
ChannelId = rootChannel.Id,
ThreadId = threadId,
Name = match.Member.ProxyName(ctx),
Name = await FixSameName(messageChannel.Id, ctx, match.Member),
AvatarUrl = AvatarUtils.TryRewriteCdnUrl(match.Member.ProxyAvatar(ctx)),
Content = content,
Attachments = trigger.Attachments,
@ -230,6 +232,49 @@ namespace PluralKit.Bot
};
}
private async Task<string> FixSameName(ulong channel_id, MessageContext ctx, ProxyMember member)
{
var proxyName = member.ProxyName(ctx);
Message? lastMessage = null;
var lastMessageId = _lastMessage.GetLastMessage(channel_id)?.Previous;
if (lastMessageId == null)
// cache is out of date or channel is empty.
return proxyName;
lastMessage = await _rest.GetMessage(channel_id, lastMessageId.Value);
if (lastMessage == null)
// we don't have enough information to figure out if we need to fix the name, so bail here.
return proxyName;
await using var conn = await _db.Obtain();
var message = await _repo.GetMessage(conn, lastMessage.Id);
if (lastMessage.Author.Username == proxyName)
{
// last message wasn't proxied by us, but somehow has the same name
// it's probably from a different webhook (Tupperbox?) but let's fix it anyway!
if (message == null)
return FixSameNameInner(proxyName);
// last message was proxied by a different member
if (message.Member.Id != member.Id)
return FixSameNameInner(proxyName);
}
// if we fixed the name last message and it's the same member proxying, we want to fix it again
if (lastMessage.Author.Username == FixSameNameInner(proxyName) && message?.Member.Id == member.Id)
return FixSameNameInner(proxyName);
// No issues found, current proxy name is fine.
return proxyName;
}
private string FixSameNameInner(string name)
=> $"{name}\u17b5";
private async Task HandleProxyExecutedActions(Shard shard, IPKConnection conn, MessageContext ctx,
Message triggerMessage, Message proxyMessage,
ProxyMatch match)

View File

@ -1,6 +1,7 @@
#nullable enable
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Myriad.Types;
@ -13,14 +14,58 @@ namespace PluralKit.Bot
public void AddMessage(Message msg)
{
_cache[msg.ChannelId] = new CachedMessage(msg.Id, msg.ReferencedMessage.Value?.Id);
var previous = GetLastMessage(msg.ChannelId);
_cache[msg.ChannelId] = new CachedMessage(msg.Id, msg.ReferencedMessage.Value?.Id, previous?.Id);
}
public CachedMessage? GetLastMessage(ulong channel)
{
return _cache.TryGetValue(channel, out var message) ? message : null;
}
public void HandleMessageDeletion(ulong channel, ulong message)
{
var storedMessage = GetLastMessage(channel);
if (storedMessage == null)
return;
if (message == storedMessage.Id)
if (storedMessage.Previous != null)
_cache[channel] = new CachedMessage(storedMessage.Previous.Value, null, null);
else
_cache.Remove(channel);
else if (message == storedMessage.Previous)
_cache[channel] = new CachedMessage(storedMessage.Id, storedMessage.ReferencedMessage, null);
}
public void HandleMessageDeletion(ulong channel, List<ulong> messages)
{
var storedMessage = GetLastMessage(channel);
if (storedMessage == null)
return;
if (!(messages.Contains(storedMessage.Id) || (storedMessage.Previous != null && messages.Contains(storedMessage.Previous.Value))))
// none of the deleted messages are relevant to the cache
return;
ulong? newLastMessage = null;
if (messages.Contains(storedMessage.Id))
newLastMessage = storedMessage.Previous;
if (storedMessage.Previous != null && messages.Contains(storedMessage.Previous.Value))
if (newLastMessage == storedMessage.Previous)
newLastMessage = null;
else
{
_cache[channel] = new CachedMessage(storedMessage.Id, storedMessage.ReferencedMessage, null);
return;
}
if (newLastMessage == null)
_cache.Remove(channel);
}
}
public record CachedMessage(ulong Id, ulong? ReferencedMessage);
public record CachedMessage(ulong Id, ulong? ReferencedMessage, ulong? Previous);
}