diff --git a/PluralKit.Bot/CommandMeta/CommandTree.cs b/PluralKit.Bot/CommandMeta/CommandTree.cs index 2d5f84c6..e35f5919 100644 --- a/PluralKit.Bot/CommandMeta/CommandTree.cs +++ b/PluralKit.Bot/CommandMeta/CommandTree.cs @@ -524,6 +524,8 @@ public partial class CommandTree return ctx.Execute(null, m => m.ShowPrivateInfo(ctx)); if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "case" })) return ctx.Execute(null, m => m.CaseSensitiveProxyTags(ctx)); + if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "error" }) || ctx.Match("pe")) + return ctx.Execute(null, m => m.ProxyErrorMessageEnabled(ctx)); // todo: maybe add the list of configuration keys here? return ctx.Reply($"{Emojis.Error} Could not find a setting with that name. Please see `pk;commands config` for the list of possible config settings."); diff --git a/PluralKit.Bot/Commands/Checks.cs b/PluralKit.Bot/Commands/Checks.cs index 362e6aca..a2be32cf 100644 --- a/PluralKit.Bot/Commands/Checks.cs +++ b/PluralKit.Bot/Commands/Checks.cs @@ -248,6 +248,13 @@ public class Checks _proxy.ShouldProxy(channel, msg, context); _matcher.TryMatch(context, autoproxySettings, members, out var match, msg.Content, msg.Attachments.Length > 0, true, ctx.Config.CaseSensitiveProxyTags); + var canProxy = await _proxy.CanProxy(channel, msg, context); + if (canProxy != null) + { + await ctx.Reply(canProxy); + return; + } + await ctx.Reply("I'm not sure why this message was not proxied, sorry."); } catch (ProxyService.ProxyChecksFailedException e) diff --git a/PluralKit.Bot/Commands/Config.cs b/PluralKit.Bot/Commands/Config.cs index c69f5843..37098c97 100644 --- a/PluralKit.Bot/Commands/Config.cs +++ b/PluralKit.Bot/Commands/Config.cs @@ -95,6 +95,13 @@ public class Config "enabled" )); + items.Add(new( + "Proxy error", + "Whether to send an error message when proxying fails.", + EnabledDisabled(ctx.Config.ProxyErrorMessageEnabled), + "enabled" + )); + await ctx.Paginate( items.ToAsyncEnumerable(), items.Count, @@ -413,4 +420,27 @@ public class Config await ctx.Reply("Proxy tags are now case insensitive."); } } + + public async Task ProxyErrorMessageEnabled(Context ctx) + { + if (!ctx.HasNext()) + { + if (ctx.Config.ProxyErrorMessageEnabled) { await ctx.Reply("Proxy error messages are currently **enabled**."); } + else { await ctx.Reply("Proxy error messages are currently **disabled**. Messages that fail to proxy (due to message or attachment size) will not throw an error message."); } + return; + } + + if (ctx.MatchToggle(true)) + { + await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { ProxyErrorMessageEnabled = true }); + + await ctx.Reply("Proxy error messages are now enabled."); + } + else + { + await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { ProxyErrorMessageEnabled = false }); + + await ctx.Reply("Proxy error messages are now disabled. Messages that fail to proxy (due to message or attachment size) will not throw an error message."); + } + } } \ No newline at end of file diff --git a/PluralKit.Bot/Proxy/ProxyService.cs b/PluralKit.Bot/Proxy/ProxyService.cs index dc665d67..9e320d9c 100644 --- a/PluralKit.Bot/Proxy/ProxyService.cs +++ b/PluralKit.Bot/Proxy/ProxyService.cs @@ -82,9 +82,14 @@ public class ProxyService if (!_matcher.TryMatch(ctx, autoproxySettings, members, out var match, message.Content, message.Attachments.Length > 0, allowAutoproxy, ctx.CaseSensitiveProxyTags)) return false; - // this is hopefully temporary, so not putting it into a separate method - if (message.Content != null && message.Content.Length > 2000) - throw new PKError("PluralKit cannot proxy messages over 2000 characters in length."); + var canProxy = await CanProxy(channel, message, ctx); + if (canProxy != null) + { + if (ctx.ProxyErrorMessageEnabled) + throw new PKError(canProxy); + + return false; + } // Permission check after proxy match so we don't get spammed when not actually proxying if (!CheckBotPermissionsOrError(botPermissions, rootChannel.Id)) @@ -104,6 +109,29 @@ public class ProxyService return true; } + public async Task CanProxy(Channel channel, Message msg, MessageContext ctx) + { + // Check if the message does not go over any Discord Nitro limits + if (msg.Content != null && msg.Content.Length > 2000) + { + return "PluralKit cannot proxy messages over 2000 characters in length."; + } + + var guild = await _cache.GetGuild(channel.GuildId.Value); + var fileSizeLimit = guild.FileSizeLimit(); + var bytesThreshold = fileSizeLimit * 1024 * 1024; + + foreach (var attachment in msg.Attachments) + { + if (attachment.Size > bytesThreshold) + { + return $"PluralKit cannot proxy attachments over {fileSizeLimit} megabytes in this server (as webhooks aren't considered as having Discord Nitro) :("; + } + } + + return null; + } + public bool ShouldProxy(Channel channel, Message msg, MessageContext ctx) { // Make sure author has a system diff --git a/PluralKit.Core/Database/Functions/MessageContext.cs b/PluralKit.Core/Database/Functions/MessageContext.cs index c4eff586..7c90f13c 100644 --- a/PluralKit.Core/Database/Functions/MessageContext.cs +++ b/PluralKit.Core/Database/Functions/MessageContext.cs @@ -29,4 +29,5 @@ public class MessageContext public bool AllowAutoproxy { get; } public int? LatchTimeout { get; } public bool CaseSensitiveProxyTags { get; } + public bool ProxyErrorMessageEnabled { get; } } \ No newline at end of file diff --git a/PluralKit.Core/Database/Functions/functions.sql b/PluralKit.Core/Database/Functions/functions.sql index a49fac8f..b3fe6df6 100644 --- a/PluralKit.Core/Database/Functions/functions.sql +++ b/PluralKit.Core/Database/Functions/functions.sql @@ -15,13 +15,14 @@ create function message_context(account_id bigint, guild_id bigint, channel_id b system_avatar text, allow_autoproxy bool, latch_timeout integer, - case_sensitive_proxy_tags bool + case_sensitive_proxy_tags bool, + proxy_error_message_enabled bool ) as $$ -- CTEs to query "static" (accessible only through args) data with system as (select systems.*, system_config.latch_timeout, system_guild.tag as guild_tag, system_guild.tag_enabled as tag_enabled, - allow_autoproxy as account_autoproxy, system_config.case_sensitive_proxy_tags from accounts + allow_autoproxy as account_autoproxy, system_config.case_sensitive_proxy_tags, system_config.proxy_error_message_enabled from accounts left join systems on systems.id = accounts.system left join system_config on system_config.system = accounts.system left join system_guild on system_guild.system = accounts.system and system_guild.guild = guild_id @@ -43,7 +44,8 @@ as $$ system.avatar_url as system_avatar, system.account_autoproxy as allow_autoproxy, system.latch_timeout as latch_timeout, - system.case_sensitive_proxy_tags as case_sensitive_proxy_tags + system.case_sensitive_proxy_tags as case_sensitive_proxy_tags, + system.proxy_error_message_enabled as proxy_error_message_enabled -- We need a "from" clause, so we just use some bogus data that's always present -- This ensure we always have exactly one row going forward, so we can left join afterwards and still get data from (select 1) as _placeholder diff --git a/PluralKit.Core/Database/Migrations/34.sql b/PluralKit.Core/Database/Migrations/34.sql new file mode 100644 index 00000000..aa5ff314 --- /dev/null +++ b/PluralKit.Core/Database/Migrations/34.sql @@ -0,0 +1,6 @@ +-- database version 34 +-- add proxy_error_message_enabled to system config + +alter table system_config add column proxy_error_message_enabled bool default true; + +update info set schema_version = 34; \ No newline at end of file diff --git a/PluralKit.Core/Database/Utils/DatabaseMigrator.cs b/PluralKit.Core/Database/Utils/DatabaseMigrator.cs index ff6efb4b..4106cfb0 100644 --- a/PluralKit.Core/Database/Utils/DatabaseMigrator.cs +++ b/PluralKit.Core/Database/Utils/DatabaseMigrator.cs @@ -9,7 +9,7 @@ namespace PluralKit.Core; internal class DatabaseMigrator { private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files - private const int TargetSchemaVersion = 33; + private const int TargetSchemaVersion = 34; private readonly ILogger _logger; public DatabaseMigrator(ILogger logger) diff --git a/PluralKit.Core/Models/Patch/SystemConfigPatch.cs b/PluralKit.Core/Models/Patch/SystemConfigPatch.cs index cc3ad00d..366c8f6d 100644 --- a/PluralKit.Core/Models/Patch/SystemConfigPatch.cs +++ b/PluralKit.Core/Models/Patch/SystemConfigPatch.cs @@ -18,6 +18,7 @@ public class SystemConfigPatch: PatchObject public Partial GroupLimitOverride { get; set; } public Partial DescriptionTemplates { get; set; } public Partial CaseSensitiveProxyTags { get; set; } + public Partial ProxyErrorMessageEnabled { get; set; } public override Query Apply(Query q) => q.ApplyPatch(wrapper => wrapper @@ -31,6 +32,7 @@ public class SystemConfigPatch: PatchObject .With("group_limit_override", GroupLimitOverride) .With("description_templates", DescriptionTemplates) .With("case_sensitive_proxy_tags", CaseSensitiveProxyTags) + .With("proxy_error_message_enabled", ProxyErrorMessageEnabled) ); public new void AssertIsValid() @@ -83,6 +85,9 @@ public class SystemConfigPatch: PatchObject if (CaseSensitiveProxyTags.IsPresent) o.Add("case_sensitive_proxy_tags", CaseSensitiveProxyTags.Value); + if (ProxyErrorMessageEnabled.IsPresent) + o.Add("proxy_error_message_enabled", ProxyErrorMessageEnabled.Value); + return o; } @@ -111,6 +116,9 @@ public class SystemConfigPatch: PatchObject if (o.ContainsKey("case_sensitive_proxy_tags")) patch.CaseSensitiveProxyTags = o.Value("case_sensitive_proxy_tags"); + if (o.ContainsKey("proxy_error_message_enabled")) + patch.ProxyErrorMessageEnabled = o.Value("proxy_error_message_enabled"); + return patch; } } \ No newline at end of file diff --git a/PluralKit.Core/Models/SystemConfig.cs b/PluralKit.Core/Models/SystemConfig.cs index 22957918..1482d0d8 100644 --- a/PluralKit.Core/Models/SystemConfig.cs +++ b/PluralKit.Core/Models/SystemConfig.cs @@ -20,6 +20,7 @@ public class SystemConfig public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz); public bool CaseSensitiveProxyTags { get; set; } + public bool ProxyErrorMessageEnabled { get; } } public static class SystemConfigExt @@ -37,6 +38,7 @@ public static class SystemConfigExt o.Add("member_limit", cfg.MemberLimitOverride ?? Limits.MaxMemberCount); o.Add("group_limit", cfg.GroupLimitOverride ?? Limits.MaxGroupCount); o.Add("case_sensitive_proxy_tags", cfg.CaseSensitiveProxyTags); + o.Add("proxy_error_message_enabled", cfg.ProxyErrorMessageEnabled); o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates));