Proxy edited messages if the message is the last one in the channel, and the edit introduces proxy tags where there were none previously
This commit is contained in:
		@@ -131,6 +131,7 @@ namespace PluralKit.Bot
 | 
				
			|||||||
            _client.ReactionAdded += (msg, channel, reaction) => HandleEvent(eh => eh.HandleReactionAdded(msg, channel, reaction));
 | 
					            _client.ReactionAdded += (msg, channel, reaction) => HandleEvent(eh => eh.HandleReactionAdded(msg, channel, reaction));
 | 
				
			||||||
            _client.MessageDeleted += (msg, channel) => HandleEvent(eh => eh.HandleMessageDeleted(msg, channel));
 | 
					            _client.MessageDeleted += (msg, channel) => HandleEvent(eh => eh.HandleMessageDeleted(msg, channel));
 | 
				
			||||||
            _client.MessagesBulkDeleted += (msgs, channel) => HandleEvent(eh => eh.HandleMessagesBulkDelete(msgs, channel));
 | 
					            _client.MessagesBulkDeleted += (msgs, channel) => HandleEvent(eh => eh.HandleMessagesBulkDelete(msgs, channel));
 | 
				
			||||||
 | 
					            _client.MessageUpdated += (oldMessage, newMessage, channel) => HandleEvent(eh => eh.HandleMessageEdited(oldMessage, newMessage, channel)); 
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            _services.Resolve<ShardInfoService>().Init(_client);
 | 
					            _services.Resolve<ShardInfoService>().Init(_client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -244,6 +245,7 @@ namespace PluralKit.Bot
 | 
				
			|||||||
        private CommandTree _tree;
 | 
					        private CommandTree _tree;
 | 
				
			||||||
        private Scope _sentryScope;
 | 
					        private Scope _sentryScope;
 | 
				
			||||||
        private ProxyCache _cache;
 | 
					        private ProxyCache _cache;
 | 
				
			||||||
 | 
					        private LastMessageCacheService _lastMessageCache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We're defining in the Autofac module that this class is instantiated with one instance per event
 | 
					        // We're defining in the Autofac module that this class is instantiated with one instance per event
 | 
				
			||||||
        // This means that the HandleMessage function will either be called once, or not at all
 | 
					        // This means that the HandleMessage function will either be called once, or not at all
 | 
				
			||||||
@@ -251,7 +253,7 @@ namespace PluralKit.Bot
 | 
				
			|||||||
        // hence, we just store it in a local variable, ignoring it entirely if it's null.
 | 
					        // hence, we just store it in a local variable, ignoring it entirely if it's null.
 | 
				
			||||||
        private IUserMessage _msg = null;
 | 
					        private IUserMessage _msg = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PKEventHandler(ProxyService proxy, ILogger logger, IMetrics metrics, DiscordShardedClient client, DbConnectionFactory connectionFactory, ILifetimeScope services, CommandTree tree, Scope sentryScope, ProxyCache cache)
 | 
					        public PKEventHandler(ProxyService proxy, ILogger logger, IMetrics metrics, DiscordShardedClient client, DbConnectionFactory connectionFactory, ILifetimeScope services, CommandTree tree, Scope sentryScope, ProxyCache cache, LastMessageCacheService lastMessageCache)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _proxy = proxy;
 | 
					            _proxy = proxy;
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
@@ -262,6 +264,7 @@ namespace PluralKit.Bot
 | 
				
			|||||||
            _tree = tree;
 | 
					            _tree = tree;
 | 
				
			||||||
            _sentryScope = sentryScope;
 | 
					            _sentryScope = sentryScope;
 | 
				
			||||||
            _cache = cache;
 | 
					            _cache = cache;
 | 
				
			||||||
 | 
					            _lastMessageCache = lastMessageCache;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task HandleMessage(SocketMessage arg)
 | 
					        public async Task HandleMessage(SocketMessage arg)
 | 
				
			||||||
@@ -288,6 +291,9 @@ namespace PluralKit.Bot
 | 
				
			|||||||
                {"message", msg.Id.ToString()},
 | 
					                {"message", msg.Id.ToString()},
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            // Add to last message cache
 | 
				
			||||||
 | 
					            _lastMessageCache.AddMessage(arg.Channel.Id, arg.Id);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            // We fetch information about the sending account *and* guild from the cache
 | 
					            // We fetch information about the sending account *and* guild from the cache
 | 
				
			||||||
            GuildConfig cachedGuild = default; // todo: is this default correct?
 | 
					            GuildConfig cachedGuild = default; // todo: is this default correct?
 | 
				
			||||||
            if (msg.Channel is ITextChannel textChannel) cachedGuild = await _cache.GetGuildDataCached(textChannel.GuildId);
 | 
					            if (msg.Channel is ITextChannel textChannel) cachedGuild = await _cache.GetGuildDataCached(textChannel.GuildId);
 | 
				
			||||||
@@ -396,5 +402,31 @@ namespace PluralKit.Bot
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            return _proxy.HandleMessageBulkDeleteAsync(messages, channel);
 | 
					            return _proxy.HandleMessageBulkDeleteAsync(messages, channel);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task HandleMessageEdited(Cacheable<IMessage, ulong> oldMessage, SocketMessage newMessage, ISocketMessageChannel channel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _sentryScope.AddBreadcrumb(newMessage.Content ?? "", "event.messageEdit", data: new Dictionary<string, string>()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                {"channel", channel.Id.ToString()},
 | 
				
			||||||
 | 
					                {"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
 | 
				
			||||||
 | 
					                {"message", newMessage.Id.ToString()}
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // If this isn't a guild, bail
 | 
				
			||||||
 | 
					            if (!(channel is IGuildChannel gc)) return;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // If this isn't the last message in the channel, don't do anything
 | 
				
			||||||
 | 
					            if (_lastMessageCache.GetLastMessage(channel.Id) != newMessage.Id) return;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Fetch account from cache if there is any
 | 
				
			||||||
 | 
					            var account = await _cache.GetAccountDataCached(newMessage.Author.Id);
 | 
				
			||||||
 | 
					            if (account == null) return; // Again: no cache = no account = no system = no proxy
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Also fetch guild cache
 | 
				
			||||||
 | 
					            var guild = await _cache.GetGuildDataCached(gc.GuildId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Just run the normal message handling stuff
 | 
				
			||||||
 | 
					            await _proxy.HandleMessageAsync(guild, account, newMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,14 +2,11 @@ using System;
 | 
				
			|||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Autofac;
 | 
					using Autofac;
 | 
				
			||||||
using Autofac.Extensions.DependencyInjection;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Rest;
 | 
					using Discord.Rest;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using PluralKit.Bot.Commands;
 | 
					using PluralKit.Bot.Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Sentry;
 | 
					using Sentry;
 | 
				
			||||||
@@ -68,6 +65,7 @@ namespace PluralKit.Bot
 | 
				
			|||||||
            builder.RegisterType<ShardInfoService>().AsSelf().SingleInstance();
 | 
					            builder.RegisterType<ShardInfoService>().AsSelf().SingleInstance();
 | 
				
			||||||
            builder.RegisterType<CpuStatService>().AsSelf().SingleInstance();
 | 
					            builder.RegisterType<CpuStatService>().AsSelf().SingleInstance();
 | 
				
			||||||
            builder.RegisterType<PeriodicStatCollector>().AsSelf().SingleInstance();
 | 
					            builder.RegisterType<PeriodicStatCollector>().AsSelf().SingleInstance();
 | 
				
			||||||
 | 
					            builder.RegisterType<LastMessageCacheService>().AsSelf().SingleInstance();
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Sentry stuff
 | 
					            // Sentry stuff
 | 
				
			||||||
            builder.Register(_ => new Scope(null)).AsSelf().InstancePerLifetimeScope();
 | 
					            builder.Register(_ => new Scope(null)).AsSelf().InstancePerLifetimeScope();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								PluralKit.Bot/Services/LastMessageCacheService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								PluralKit.Bot/Services/LastMessageCacheService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace PluralKit.Bot
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Doing things like this instead of enabling D.NET's message cache because the message cache is, let's face it,
 | 
				
			||||||
 | 
					    // not particularly efficient? It allocates a dictionary *and* a queue for every single channel (500k in prod!)
 | 
				
			||||||
 | 
					    // whereas this is, worst case, one dictionary *entry* of a single ulong per channel, and one dictionary instance
 | 
				
			||||||
 | 
					    // on the whole instance, total. Yeah, much more efficient.
 | 
				
			||||||
 | 
					    public class LastMessageCacheService
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private IDictionary<ulong, ulong> _cache = new ConcurrentDictionary<ulong, ulong>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void AddMessage(ulong channel, ulong message)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _cache[channel] = message;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ulong? GetLastMessage(ulong channel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_cache.TryGetValue(channel, out var message)) return message;
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user