feat: rewrite database schema for localized autoproxy
This commit is contained in:
@@ -16,25 +16,29 @@ public class Autoproxy
|
||||
// no need to check account here, it's already done at CommandTree
|
||||
ctx.CheckGuildContext();
|
||||
|
||||
// for now, just for guild
|
||||
// this also creates settings if there are none present
|
||||
var settings = await ctx.Repository.GetAutoproxySettings(ctx.System.Id, ctx.Guild.Id, null);
|
||||
|
||||
if (ctx.Match("off", "stop", "cancel", "no", "disable", "remove"))
|
||||
await AutoproxyOff(ctx);
|
||||
await AutoproxyOff(ctx, settings);
|
||||
else if (ctx.Match("latch", "last", "proxy", "stick", "sticky"))
|
||||
await AutoproxyLatch(ctx);
|
||||
await AutoproxyLatch(ctx, settings);
|
||||
else if (ctx.Match("front", "fronter", "switch"))
|
||||
await AutoproxyFront(ctx);
|
||||
await AutoproxyFront(ctx, settings);
|
||||
else if (ctx.Match("member"))
|
||||
throw new PKSyntaxError("Member-mode autoproxy must target a specific member. Use the `pk;autoproxy <member>` command, where `member` is the name or ID of a member in your system.");
|
||||
else if (await ctx.MatchMember() is PKMember member)
|
||||
await AutoproxyMember(ctx, member);
|
||||
else if (!ctx.HasNext())
|
||||
await ctx.Reply(embed: await CreateAutoproxyStatusEmbed(ctx));
|
||||
await ctx.Reply(embed: await CreateAutoproxyStatusEmbed(ctx, settings));
|
||||
else
|
||||
throw new PKSyntaxError($"Invalid autoproxy mode {ctx.PopArgument().AsCode()}.");
|
||||
}
|
||||
|
||||
private async Task AutoproxyOff(Context ctx)
|
||||
private async Task AutoproxyOff(Context ctx, AutoproxySettings settings)
|
||||
{
|
||||
if (ctx.MessageContext.AutoproxyMode == AutoproxyMode.Off)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Off)
|
||||
{
|
||||
await ctx.Reply($"{Emojis.Note} Autoproxy is already off in this server.");
|
||||
}
|
||||
@@ -45,9 +49,9 @@ public class Autoproxy
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AutoproxyLatch(Context ctx)
|
||||
private async Task AutoproxyLatch(Context ctx, AutoproxySettings settings)
|
||||
{
|
||||
if (ctx.MessageContext.AutoproxyMode == AutoproxyMode.Latch)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Latch)
|
||||
{
|
||||
await ctx.Reply($"{Emojis.Note} Autoproxy is already set to latch mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`.");
|
||||
}
|
||||
@@ -58,9 +62,9 @@ public class Autoproxy
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AutoproxyFront(Context ctx)
|
||||
private async Task AutoproxyFront(Context ctx, AutoproxySettings settings)
|
||||
{
|
||||
if (ctx.MessageContext.AutoproxyMode == AutoproxyMode.Front)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Front)
|
||||
{
|
||||
await ctx.Reply($"{Emojis.Note} Autoproxy is already set to front mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`.");
|
||||
}
|
||||
@@ -75,11 +79,13 @@ public class Autoproxy
|
||||
{
|
||||
ctx.CheckOwnMember(member);
|
||||
|
||||
// todo: why does this not throw an error if the member is already set
|
||||
|
||||
await UpdateAutoproxy(ctx, AutoproxyMode.Member, member.Id);
|
||||
await ctx.Reply($"{Emojis.Success} Autoproxy set to **{member.NameFor(ctx)}** in this server.");
|
||||
}
|
||||
|
||||
private async Task<Embed> CreateAutoproxyStatusEmbed(Context ctx)
|
||||
private async Task<Embed> CreateAutoproxyStatusEmbed(Context ctx, AutoproxySettings settings)
|
||||
{
|
||||
var commandList = "**pk;autoproxy latch** - Autoproxies as last-proxied member"
|
||||
+ "\n**pk;autoproxy front** - Autoproxies as current (first) fronter"
|
||||
@@ -88,14 +94,16 @@ public class Autoproxy
|
||||
.Title($"Current autoproxy status (for {ctx.Guild.Name.EscapeMarkdown()})");
|
||||
|
||||
var fronters = ctx.MessageContext.LastSwitchMembers;
|
||||
var relevantMember = ctx.MessageContext.AutoproxyMode switch
|
||||
var relevantMember = settings.AutoproxyMode switch
|
||||
{
|
||||
AutoproxyMode.Front => fronters.Length > 0 ? await ctx.Repository.GetMember(fronters[0]) : null,
|
||||
AutoproxyMode.Member when ctx.MessageContext.AutoproxyMember.HasValue => await ctx.Repository.GetMember(ctx.MessageContext.AutoproxyMember.Value),
|
||||
AutoproxyMode.Member when settings.AutoproxyMember.HasValue => await ctx.Repository.GetMember(settings.AutoproxyMember.Value),
|
||||
_ => null
|
||||
};
|
||||
|
||||
switch (ctx.MessageContext.AutoproxyMode)
|
||||
Console.WriteLine(settings.AutoproxyMode);
|
||||
|
||||
switch (settings.AutoproxyMode)
|
||||
{
|
||||
case AutoproxyMode.Off:
|
||||
eb.Description($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}");
|
||||
@@ -136,9 +144,7 @@ public class Autoproxy
|
||||
|
||||
private async Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember)
|
||||
{
|
||||
await ctx.Repository.GetSystemGuild(ctx.Guild.Id, ctx.System.Id);
|
||||
|
||||
var patch = new SystemGuildPatch { AutoproxyMode = autoproxyMode, AutoproxyMember = autoproxyMember };
|
||||
await ctx.Repository.UpdateSystemGuild(ctx.System.Id, ctx.Guild.Id, patch);
|
||||
var patch = new AutoproxyPatch { AutoproxyMode = autoproxyMode, AutoproxyMember = autoproxyMember };
|
||||
await ctx.Repository.UpdateAutoproxy(ctx.System.Id, ctx.Guild.Id, null, patch);
|
||||
}
|
||||
}
|
@@ -259,11 +259,16 @@ public class Checks
|
||||
var context = await ctx.Repository.GetMessageContext(msg.Author.Id, channel.GuildId.Value, msg.ChannelId);
|
||||
var members = (await ctx.Repository.GetProxyMembers(msg.Author.Id, channel.GuildId.Value)).ToList();
|
||||
|
||||
// for now this is just server
|
||||
var autoproxySettings = await ctx.Repository.GetAutoproxySettings(ctx.System.Id, channel.GuildId.Value, null);
|
||||
|
||||
// todo: match unlatch
|
||||
|
||||
// Run everything through the checks, catch the ProxyCheckFailedException, and reply with the error message.
|
||||
try
|
||||
{
|
||||
_proxy.ShouldProxy(channel, msg, context);
|
||||
_matcher.TryMatch(context, members, out var match, msg.Content, msg.Attachments.Length > 0,
|
||||
_matcher.TryMatch(context, autoproxySettings, members, out var match, msg.Content, msg.Attachments.Length > 0,
|
||||
context.AllowAutoproxy);
|
||||
|
||||
await ctx.Reply("I'm not sure why this message was not proxied, sorry.");
|
||||
|
@@ -18,12 +18,12 @@ public class ProxyMatcher
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
public bool TryMatch(MessageContext ctx, IReadOnlyCollection<ProxyMember> members, out ProxyMatch match,
|
||||
public bool TryMatch(MessageContext ctx, AutoproxySettings settings, IReadOnlyCollection<ProxyMember> members, out ProxyMatch match,
|
||||
string messageContent,
|
||||
bool hasAttachments, bool allowAutoproxy)
|
||||
{
|
||||
if (TryMatchTags(members, messageContent, hasAttachments, out match)) return true;
|
||||
if (allowAutoproxy && TryMatchAutoproxy(ctx, members, messageContent, out match)) return true;
|
||||
if (allowAutoproxy && TryMatchAutoproxy(ctx, settings, members, messageContent, out match)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ProxyMatcher
|
||||
return hasAttachments || match.Content.Trim().Length > 0;
|
||||
}
|
||||
|
||||
private bool TryMatchAutoproxy(MessageContext ctx, IReadOnlyCollection<ProxyMember> members,
|
||||
private bool TryMatchAutoproxy(MessageContext ctx, AutoproxySettings settings, IReadOnlyCollection<ProxyMember> members,
|
||||
string messageContent,
|
||||
out ProxyMatch match)
|
||||
{
|
||||
@@ -49,41 +49,41 @@ public class ProxyMatcher
|
||||
"This message matches none of your proxy tags, and it was not autoproxied because it starts with a backslash (`\\`).");
|
||||
|
||||
// Find the member we should autoproxy (null if none)
|
||||
var member = ctx.AutoproxyMode switch
|
||||
var member = settings.AutoproxyMode switch
|
||||
{
|
||||
AutoproxyMode.Member when ctx.AutoproxyMember != null =>
|
||||
members.FirstOrDefault(m => m.Id == ctx.AutoproxyMember),
|
||||
AutoproxyMode.Member when settings.AutoproxyMember != null =>
|
||||
members.FirstOrDefault(m => m.Id == settings.AutoproxyMember),
|
||||
|
||||
AutoproxyMode.Front when ctx.LastSwitchMembers.Length > 0 =>
|
||||
members.FirstOrDefault(m => m.Id == ctx.LastSwitchMembers[0]),
|
||||
|
||||
AutoproxyMode.Latch when ctx.LastMessageMember != null =>
|
||||
members.FirstOrDefault(m => m.Id == ctx.LastMessageMember.Value),
|
||||
AutoproxyMode.Latch when settings.AutoproxyMember != null =>
|
||||
members.FirstOrDefault(m => m.Id == settings.AutoproxyMember.Value),
|
||||
|
||||
_ => null
|
||||
};
|
||||
// Throw an error if the member is null, message varies depending on autoproxy mode
|
||||
if (member == null)
|
||||
{
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Front)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Front)
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"You are using autoproxy front, but no members are currently registered as fronting. Please use `pk;switch <member>` to log a new switch.");
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Member)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Member)
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"You are using member-specific autoproxy with an invalid member. Was this member deleted?");
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Latch)
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Latch)
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"You are using autoproxy latch, but have not sent any messages yet in this server. Please send a message using proxy tags first.");
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"This message matches none of your proxy tags and autoproxy is not enabled.");
|
||||
}
|
||||
|
||||
if (ctx.AutoproxyMode != AutoproxyMode.Member && !member.AllowAutoproxy)
|
||||
if (settings.AutoproxyMode != AutoproxyMode.Member && !member.AllowAutoproxy)
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"This member has autoproxy disabled. To enable it, use `pk;m <member> autoproxy on`.");
|
||||
|
||||
// Moved the IsLatchExpired() check to here, so that an expired latch and a latch without any previous messages throw different errors
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Latch && IsLatchExpired(ctx))
|
||||
if (settings.AutoproxyMode == AutoproxyMode.Latch && IsLatchExpired(ctx, settings))
|
||||
throw new ProxyService.ProxyChecksFailedException(
|
||||
"Latch-mode autoproxy has timed out. Please send a new message using proxy tags.");
|
||||
|
||||
@@ -99,16 +99,14 @@ public class ProxyMatcher
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsLatchExpired(MessageContext ctx)
|
||||
private bool IsLatchExpired(MessageContext ctx, AutoproxySettings settings)
|
||||
{
|
||||
if (ctx.LastMessage == null) return true;
|
||||
if (ctx.LatchTimeout == 0) return false;
|
||||
|
||||
var timeout = ctx.LatchTimeout.HasValue
|
||||
? Duration.FromSeconds(ctx.LatchTimeout.Value)
|
||||
: DefaultLatchExpiryTime;
|
||||
|
||||
var timestamp = DiscordUtils.SnowflakeToInstant(ctx.LastMessage.Value);
|
||||
return _clock.GetCurrentInstant() - timestamp > timeout;
|
||||
return _clock.GetCurrentInstant() - settings.LastLatchTimestamp > timeout;
|
||||
}
|
||||
}
|
@@ -32,10 +32,11 @@ public class ProxyService
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly DiscordApiClient _rest;
|
||||
private readonly WebhookExecutorService _webhookExecutor;
|
||||
private readonly NodaTime.IClock _clock;
|
||||
|
||||
public ProxyService(LogChannelService logChannel, ILogger logger, WebhookExecutorService webhookExecutor,
|
||||
DispatchService dispatch, IDatabase db, ProxyMatcher matcher, IMetrics metrics, ModelRepository repo,
|
||||
IDiscordCache cache, DiscordApiClient rest, LastMessageCacheService lastMessage)
|
||||
NodaTime.IClock clock, IDiscordCache cache, DiscordApiClient rest, LastMessageCacheService lastMessage)
|
||||
{
|
||||
_logChannel = logChannel;
|
||||
_webhookExecutor = webhookExecutor;
|
||||
@@ -47,6 +48,7 @@ public class ProxyService
|
||||
_cache = cache;
|
||||
_lastMessage = lastMessage;
|
||||
_rest = rest;
|
||||
_clock = clock;
|
||||
_logger = logger.ForContext<ProxyService>();
|
||||
}
|
||||
|
||||
@@ -56,6 +58,17 @@ public class ProxyService
|
||||
if (!ShouldProxy(channel, message, ctx))
|
||||
return false;
|
||||
|
||||
var autoproxySettings = await _repo.GetAutoproxySettings(ctx.SystemId.Value, guild.Id, null);
|
||||
|
||||
if (autoproxySettings.AutoproxyMode == AutoproxyMode.Latch && IsUnlatch(message))
|
||||
{
|
||||
// "unlatch"
|
||||
await _repo.UpdateAutoproxy(ctx.SystemId.Value, guild.Id, null, new() {
|
||||
AutoproxyMember = null
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
var rootChannel = await _cache.GetRootChannel(message.ChannelId);
|
||||
|
||||
List<ProxyMember> members;
|
||||
@@ -63,7 +76,7 @@ public class ProxyService
|
||||
using (_metrics.Measure.Timer.Time(BotMetrics.ProxyMembersQueryTime))
|
||||
members = (await _repo.GetProxyMembers(message.Author.Id, message.GuildId!.Value)).ToList();
|
||||
|
||||
if (!_matcher.TryMatch(ctx, members, out var match, message.Content, message.Attachments.Length > 0,
|
||||
if (!_matcher.TryMatch(ctx, autoproxySettings, members, out var match, message.Content, message.Attachments.Length > 0,
|
||||
allowAutoproxy)) return false;
|
||||
|
||||
// this is hopefully temporary, so not putting it into a separate method
|
||||
@@ -84,7 +97,7 @@ public class ProxyService
|
||||
var allowEmbeds = senderPermissions.HasFlag(PermissionSet.EmbedLinks);
|
||||
|
||||
// Everything's in order, we can execute the proxy!
|
||||
await ExecuteProxy(message, ctx, match, allowEveryone, allowEmbeds);
|
||||
await ExecuteProxy(message, ctx, autoproxySettings, match, allowEveryone, allowEmbeds);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,7 +142,7 @@ public class ProxyService
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ExecuteProxy(Message trigger, MessageContext ctx,
|
||||
private async Task ExecuteProxy(Message trigger, MessageContext ctx, AutoproxySettings autoproxySettings,
|
||||
ProxyMatch match, bool allowEveryone, bool allowEmbeds)
|
||||
{
|
||||
// Create reply embed
|
||||
@@ -171,7 +184,7 @@ public class ProxyService
|
||||
Stickers = trigger.StickerItems,
|
||||
AllowEveryone = allowEveryone
|
||||
});
|
||||
await HandleProxyExecutedActions(ctx, trigger, proxyMessage, match);
|
||||
await HandleProxyExecutedActions(ctx, autoproxySettings, trigger, proxyMessage, match);
|
||||
}
|
||||
|
||||
private async Task<(string?, string?)> FetchReferencedMessageAuthorInfo(Message trigger, Message referenced)
|
||||
@@ -290,7 +303,11 @@ public class ProxyService
|
||||
private string FixSameNameInner(string name)
|
||||
=> $"{name}\u17b5";
|
||||
|
||||
private async Task HandleProxyExecutedActions(MessageContext ctx, Message triggerMessage, Message proxyMessage, ProxyMatch match)
|
||||
public static bool IsUnlatch(Message message)
|
||||
=> message.Content.StartsWith(@"\\") || message.Content.StartsWith("\\\u200b\\");
|
||||
|
||||
private async Task HandleProxyExecutedActions(MessageContext ctx, AutoproxySettings autoproxySettings,
|
||||
Message triggerMessage, Message proxyMessage, ProxyMatch match)
|
||||
{
|
||||
var sentMessage = new PKMessage
|
||||
{
|
||||
@@ -308,6 +325,14 @@ public class ProxyService
|
||||
Task LogMessageToChannel() =>
|
||||
_logChannel.LogMessage(ctx, sentMessage, triggerMessage, proxyMessage).AsTask();
|
||||
|
||||
Task SaveLatchAutoproxy() => autoproxySettings.AutoproxyMode == AutoproxyMode.Latch
|
||||
? _repo.UpdateAutoproxy(ctx.SystemId.Value, triggerMessage.GuildId, null, new()
|
||||
{
|
||||
AutoproxyMember = match.Member.Id,
|
||||
LastLatchTimestamp = _clock.GetCurrentInstant(),
|
||||
})
|
||||
: Task.CompletedTask;
|
||||
|
||||
Task DispatchWebhook() => _dispatch.Dispatch(ctx.SystemId.Value, sentMessage);
|
||||
|
||||
async Task DeleteProxyTriggerMessage()
|
||||
@@ -333,6 +358,7 @@ public class ProxyService
|
||||
DeleteProxyTriggerMessage(),
|
||||
SaveMessageInDatabase(),
|
||||
LogMessageToChannel(),
|
||||
SaveLatchAutoproxy(),
|
||||
DispatchWebhook()
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user