Refactor proxy handling code
- Move reaction handlers to the ReactionAdded event instead of ProxyService - Split tag matching off into ProxyTagParser - Split autoproxy matching off into Autoproxier - General cleanup and simplification
This commit is contained in:
93
PluralKit.Bot/Proxy/Autoproxier.cs
Normal file
93
PluralKit.Bot/Proxy/Autoproxier.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public class Autoproxier
|
||||
{
|
||||
public static readonly string EscapeString = @"\";
|
||||
public static readonly Duration AutoproxyExpiryTime = Duration.FromHours(6);
|
||||
|
||||
private IClock _clock;
|
||||
private IDataStore _data;
|
||||
|
||||
public Autoproxier(IDataStore data, IClock clock)
|
||||
{
|
||||
_data = data;
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
public async ValueTask<ProxyMatch?> TryAutoproxy(AutoproxyContext ctx)
|
||||
{
|
||||
if (IsEscaped(ctx.Content))
|
||||
return null;
|
||||
|
||||
var member = await FindAutoproxyMember(ctx);
|
||||
if (member == null) return null;
|
||||
|
||||
return new ProxyMatch
|
||||
{
|
||||
Content = ctx.Content,
|
||||
Member = member,
|
||||
ProxyTags = ProxyTagsFor(member)
|
||||
};
|
||||
}
|
||||
|
||||
private async ValueTask<PKMember?> FindAutoproxyMember(AutoproxyContext ctx)
|
||||
{
|
||||
switch (ctx.Mode)
|
||||
{
|
||||
case AutoproxyMode.Off:
|
||||
return null;
|
||||
|
||||
case AutoproxyMode.Front:
|
||||
return await _data.GetFirstFronter(ctx.Account.System);
|
||||
|
||||
case AutoproxyMode.Latch:
|
||||
// Latch mode: find last proxied message, use *that* member
|
||||
var msg = await _data.GetLastMessageInGuild(ctx.SenderId, ctx.GuildId);
|
||||
if (msg == null) return null; // No message found
|
||||
|
||||
// If the message is older than 6 hours, ignore it and force the sender to "refresh" a proxy
|
||||
// This can be revised in the future, it's a preliminary value.
|
||||
var timestamp = DiscordUtils.SnowflakeToInstant(msg.Message.Mid);
|
||||
if (_clock.GetCurrentInstant() - timestamp > AutoproxyExpiryTime) return null;
|
||||
|
||||
return msg.Member;
|
||||
|
||||
case AutoproxyMode.Member:
|
||||
// We already have the member list cached, so:
|
||||
// O(n) lookup since n is small (max 1500 de jure) and we're more constrained by memory (for a dictionary) here
|
||||
return ctx.Account.Members.FirstOrDefault(m => m.Id == ctx.AutoproxyMember);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unknown autoproxy mode {ctx.Mode}");
|
||||
}
|
||||
}
|
||||
|
||||
private ProxyTag? ProxyTagsFor(PKMember member)
|
||||
{
|
||||
if (member.ProxyTags.Count == 0) return null;
|
||||
return member.ProxyTags.First();
|
||||
}
|
||||
|
||||
private bool IsEscaped(string message) => message.TrimStart().StartsWith(EscapeString);
|
||||
|
||||
public struct AutoproxyContext
|
||||
{
|
||||
public CachedAccount Account;
|
||||
public string Content;
|
||||
public AutoproxyMode Mode;
|
||||
public int? AutoproxyMember;
|
||||
public ulong SenderId;
|
||||
public ulong GuildId;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user