diff --git a/PluralKit.Bot/CommandMeta/CommandHelp.cs b/PluralKit.Bot/CommandMeta/CommandHelp.cs index 7f840854..aa4d51b0 100644 --- a/PluralKit.Bot/CommandMeta/CommandHelp.cs +++ b/PluralKit.Bot/CommandMeta/CommandHelp.cs @@ -30,6 +30,7 @@ public partial class CommandTree public static Command ConfigShowPrivate = new Command("config show private", "config show private [on|off]", "Sets whether private information is shown to linked accounts by default"); public static Command ConfigMemberDefaultPrivacy = new("config private member", "config private member [on|off]", "Sets whether member privacy is automatically set to private when creating a new member"); public static Command ConfigGroupDefaultPrivacy = new("config private group", "config private group [on|off]", "Sets whether group privacy is automatically set to private when creating a new group"); + public static Command ConfigNameFormat = new Command("config nameformat", "config nameformat [format]", "Changes your system's username formatting"); public static Command AutoproxySet = new Command("autoproxy", "autoproxy [off|front|latch|member]", "Sets your system's autoproxy mode for the current server"); public static Command AutoproxyOff = new Command("autoproxy off", "autoproxy off", "Disables autoproxying for your system in the current server"); public static Command AutoproxyFront = new Command("autoproxy front", "autoproxy front", "Sets your system's autoproxy in this server to proxy the first member currently registered as front"); @@ -145,7 +146,7 @@ public partial class CommandTree public static Command[] ConfigCommands = { ConfigAutoproxyAccount, ConfigAutoproxyTimeout, ConfigTimezone, ConfigPing, - ConfigMemberDefaultPrivacy, ConfigGroupDefaultPrivacy, ConfigShowPrivate + ConfigMemberDefaultPrivacy, ConfigGroupDefaultPrivacy, ConfigShowPrivate, ConfigNameFormat }; public static Command[] AutoproxyCommands = diff --git a/PluralKit.Bot/CommandMeta/CommandTree.cs b/PluralKit.Bot/CommandMeta/CommandTree.cs index 5b19f193..9c1b3881 100644 --- a/PluralKit.Bot/CommandMeta/CommandTree.cs +++ b/PluralKit.Bot/CommandMeta/CommandTree.cs @@ -594,6 +594,8 @@ public partial class CommandTree return ctx.Execute(null, m => m.HidDisplayCaps(ctx)); if (ctx.MatchMultiple(new[] { "pad" }, new[] { "id", "ids" }) || ctx.MatchMultiple(new[] { "id" }, new[] { "pad", "padding" }) || ctx.Match("idpad", "padid", "padids")) return ctx.Execute(null, m => m.HidListPadding(ctx)); + if (ctx.MatchMultiple(new[] { "name" }, new[] { "format" }) || ctx.Match("nf")) + return ctx.Execute(null, m => m.NameFormat(ctx)); if (ctx.MatchMultiple(new[] { "member", "group" }, new[] { "limit" }) || ctx.Match("limit")) return ctx.Execute(null, m => m.LimitUpdate(ctx)); if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "switch" }) || ctx.Match("proxyswitch", "ps")) diff --git a/PluralKit.Bot/Commands/Config.cs b/PluralKit.Bot/Commands/Config.cs index a48d73d7..49e0fbd0 100644 --- a/PluralKit.Bot/Commands/Config.cs +++ b/PluralKit.Bot/Commands/Config.cs @@ -130,6 +130,13 @@ public class Config "disabled" )); + items.Add(new( + "Name Format", + "Format string used to display a member's name https://pluralkit.me/guide/#setting-a-custom-name-format", + ctx.Config.NameFormat, + ProxyMember.DefaultFormat + )); + await ctx.Paginate( items.ToAsyncEnumerable(), items.Count, @@ -558,6 +565,19 @@ public class Config await ctx.Reply($"Logging a switch every time a proxy tag is used is now {EnabledDisabled(newVal)}."); } + public async Task NameFormat(Context ctx) + { + if (!ctx.HasNext()) + { + await ctx.Reply($"Member names are currently formatted as `{ctx.Config.NameFormat ?? ProxyMember.DefaultFormat}`"); + return; + } + + var formatString = ctx.RemainderOrNull(); + await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { NameFormat = formatString }); + await ctx.Reply($"Member names are now formatted as `{formatString}`"); + } + public Task LimitUpdate(Context ctx) { throw new PKError("You cannot update your own member or group limits. If you need a limit update, please join the " + diff --git a/PluralKit.Bot/Errors.cs b/PluralKit.Bot/Errors.cs index ca40f248..524ea65a 100644 --- a/PluralKit.Bot/Errors.cs +++ b/PluralKit.Bot/Errors.cs @@ -162,7 +162,7 @@ public static class Errors $"The webhook's name, {name.AsCode()}, is shorter than two characters, and thus cannot be proxied. Please change the member name or use a longer system tag."); public static PKError ProxyNameTooLong(string name) => new( - $"The webhook's name, {name.AsCode()}, is too long ({name.Length} > {Limits.MaxProxyNameLength} characters), and thus cannot be proxied. Please change the member name, display name or server display name, or use a shorter system tag."); + $"The webhook's name, {name.AsCode()}, is too long ({name.Length} > {Limits.MaxProxyNameLength} characters), and thus cannot be proxied. Please change the member name, display name, server display name, system tag, or use a shorter name format"); public static PKError ProxyTagAlreadyExists(ProxyTag tagToAdd, PKMember member) => new( $"That member already has the proxy tag {tagToAdd.ProxyString.AsCode()}. The member currently has these tags: {member.ProxyTagsString()}"); diff --git a/PluralKit.Core/Database/Functions/MessageContext.cs b/PluralKit.Core/Database/Functions/MessageContext.cs index df2c0a91..f3703bec 100644 --- a/PluralKit.Core/Database/Functions/MessageContext.cs +++ b/PluralKit.Core/Database/Functions/MessageContext.cs @@ -25,6 +25,7 @@ public class MessageContext public string? SystemTag { get; } public string? SystemGuildTag { get; } public bool TagEnabled { get; } + public string? NameFormat { get; } public string? SystemAvatar { get; } public string? SystemGuildAvatar { get; } public bool AllowAutoproxy { get; } diff --git a/PluralKit.Core/Database/Functions/ProxyMember.cs b/PluralKit.Core/Database/Functions/ProxyMember.cs index 5221af32..e95237ab 100644 --- a/PluralKit.Core/Database/Functions/ProxyMember.cs +++ b/PluralKit.Core/Database/Functions/ProxyMember.cs @@ -31,17 +31,23 @@ public class ProxyMember public bool AllowAutoproxy { get; } public string? Color { get; } + // If not set, this formatting will be applied to the proxy name + public static string DefaultFormat = "{name} {tag}"; + + public static string FormatTag(string template, string tag, string name) => StringUtils.SafeFormat(template, new[] { + ("{tag}", tag), + ("{name}", name) + }); + public string ProxyName(MessageContext ctx) { + // TODO: if tag is null it should still format but only if it appears in the formatting. var memberName = ServerName ?? DisplayName ?? Name; - if (!ctx.TagEnabled) + var tag = ctx.SystemGuildTag ?? ctx.SystemTag; + if (!ctx.TagEnabled || tag == null) return memberName; - if (ctx.SystemGuildTag != null) - return $"{memberName} {ctx.SystemGuildTag}"; - if (ctx.SystemTag != null) - return $"{memberName} {ctx.SystemTag}"; - return memberName; + return FormatTag(ctx.NameFormat ?? DefaultFormat, tag, memberName); } public string? ProxyAvatar(MessageContext ctx) => ServerAvatar ?? WebhookAvatar ?? Avatar ?? ctx.SystemGuildAvatar ?? ctx.SystemAvatar; diff --git a/PluralKit.Core/Database/Functions/functions.sql b/PluralKit.Core/Database/Functions/functions.sql index d417510f..f3da1c28 100644 --- a/PluralKit.Core/Database/Functions/functions.sql +++ b/PluralKit.Core/Database/Functions/functions.sql @@ -1,4 +1,4 @@ -create function message_context(account_id bigint, guild_id bigint, channel_id bigint, thread_id bigint) +create function message_context(account_id bigint, guild_id bigint, channel_id bigint, thread_id bigint) returns table ( allow_autoproxy bool, @@ -10,6 +10,7 @@ case_sensitive_proxy_tags bool, proxy_error_message_enabled bool, proxy_switch bool, + name_format text, tag_enabled bool, proxy_enabled bool, @@ -42,6 +43,7 @@ as $$ system_config.case_sensitive_proxy_tags as case_sensitive_proxy_tags, system_config.proxy_error_message_enabled as proxy_error_message_enabled, system_config.proxy_switch as proxy_switch, + system_config.name_format as name_format, -- system_guild table coalesce(system_guild.tag_enabled, true) as tag_enabled, @@ -174,4 +176,4 @@ begin if not exists (select 1 from groups where hid = new_hid) then return new_hid; end if; end loop; end -$$ language plpgsql volatile; +$$ language plpgsql volatile; \ No newline at end of file diff --git a/PluralKit.Core/Database/Migrations/47.sql b/PluralKit.Core/Database/Migrations/47.sql new file mode 100644 index 00000000..441d648e --- /dev/null +++ b/PluralKit.Core/Database/Migrations/47.sql @@ -0,0 +1,6 @@ +-- database version 47 +-- add config setting for supplying a custom tag format in names + +alter table system_config add column name_format text; + +update info set schema_version = 47; diff --git a/PluralKit.Core/Database/Utils/DatabaseMigrator.cs b/PluralKit.Core/Database/Utils/DatabaseMigrator.cs index 0a1bcef9..85f9a293 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 = 46; + private const int TargetSchemaVersion = 47; 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 da70cbbb..b8f4d27a 100644 --- a/PluralKit.Core/Models/Patch/SystemConfigPatch.cs +++ b/PluralKit.Core/Models/Patch/SystemConfigPatch.cs @@ -21,6 +21,7 @@ public class SystemConfigPatch: PatchObject public Partial ProxyErrorMessageEnabled { get; set; } public Partial HidDisplaySplit { get; set; } public Partial HidDisplayCaps { get; set; } + public Partial NameFormat { get; set; } public Partial HidListPadding { get; set; } public Partial ProxySwitch { get; set; } @@ -40,6 +41,7 @@ public class SystemConfigPatch: PatchObject .With("hid_display_caps", HidDisplayCaps) .With("hid_list_padding", HidListPadding) .With("proxy_switch", ProxySwitch) + .With("name_format", NameFormat) ); public new void AssertIsValid() @@ -107,6 +109,9 @@ public class SystemConfigPatch: PatchObject if (ProxySwitch.IsPresent) o.Add("proxy_switch", ProxySwitch.Value); + if (NameFormat.IsPresent) + o.Add("name_format", NameFormat.Value); + return o; } @@ -147,6 +152,9 @@ public class SystemConfigPatch: PatchObject if (o.ContainsKey("proxy_switch")) patch.ProxySwitch = o.Value("proxy_switch"); + if (o.ContainsKey("name_format")) + patch.NameFormat = o.Value("name_format"); + return patch; } } \ No newline at end of file diff --git a/PluralKit.Core/Models/SystemConfig.cs b/PluralKit.Core/Models/SystemConfig.cs index 10515ccd..2f8fc4cb 100644 --- a/PluralKit.Core/Models/SystemConfig.cs +++ b/PluralKit.Core/Models/SystemConfig.cs @@ -25,6 +25,7 @@ public class SystemConfig public bool HidDisplayCaps { get; } public HidPadFormat HidListPadding { get; } public bool ProxySwitch { get; } + public string NameFormat { get; } public enum HidPadFormat { @@ -54,6 +55,7 @@ public static class SystemConfigExt o.Add("hid_display_caps", cfg.HidDisplayCaps); o.Add("hid_list_padding", cfg.HidListPadding.ToUserString()); o.Add("proxy_switch", cfg.ProxySwitch); + o.Add("name_format", cfg.NameFormat); o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates)); diff --git a/PluralKit.Core/Utils/StringUtils.cs b/PluralKit.Core/Utils/StringUtils.cs index 9a4a0410..6fe1860a 100644 --- a/PluralKit.Core/Utils/StringUtils.cs +++ b/PluralKit.Core/Utils/StringUtils.cs @@ -86,4 +86,10 @@ public static class StringUtils return output; } + + // Lightweight formatting that intentionally is very basic to not have silly things like in-template for loops like other templating engines seem to have + // Currently doesn't handle escapes which might cause problems + public static string SafeFormat(string template, (string pattern, string arg)[] args) => + args + .Aggregate(template, (acc, x) => acc.Replace(x.pattern, x.arg)); } \ No newline at end of file diff --git a/PluralKit.Tests/packages.lock.json b/PluralKit.Tests/packages.lock.json index 03217513..de785aec 100644 --- a/PluralKit.Tests/packages.lock.json +++ b/PluralKit.Tests/packages.lock.json @@ -739,8 +739,8 @@ }, "Sentry": { "type": "Transitive", - "resolved": "3.11.1", - "contentHash": "T/NLfs6MMkUSYsPEDajB9ad0124T18I0uUod5MNOev3iwjvcnIEQBrStEX2olbIxzqfvGXzQ/QFqTfA2ElLPlA==" + "resolved": "4.12.1", + "contentHash": "OLf7885OKHWLaTLTyw884mwOT4XKCWj2Hz5Wuz/TJemJqXwCIdIljkJBIoeHviRUPvtB7ulDgeYXf/Z7ScToSA==" }, "Serilog": { "type": "Transitive", @@ -1809,7 +1809,7 @@ "Humanizer.Core": "[2.8.26, )", "Myriad": "[1.0.0, )", "PluralKit.Core": "[1.0.0, )", - "Sentry": "[3.11.1, )", + "Sentry": "[4.12.1, )", "SixLabors.ImageSharp": "[3.1.5, )" } }, diff --git a/docs/content/command-list.md b/docs/content/command-list.md index 9a966818..afabba42 100644 --- a/docs/content/command-list.md +++ b/docs/content/command-list.md @@ -149,6 +149,7 @@ You can have a space after `pk;`, e.g. `pk;system` and `pk; system` will do the - `pk;config capitalize IDs [on|off]` - Toggles whether to display IDs as capital letters, to ease readability. - `pk;config pad IDs [left|right|off]` - Toggles whether to pad (add a space) 5-character IDs in lists. - `pk;config proxy switch [on|off]` - Toggles whether to log a switch whenever you proxy as a different member. +- `pk;config name format [format]` - Changes your system's username formatting. ## Server owner commands *(all commands here require Manage Server permission)* diff --git a/docs/content/user-guide.md b/docs/content/user-guide.md index 7813cf20..0f624dc8 100644 --- a/docs/content/user-guide.md +++ b/docs/content/user-guide.md @@ -387,6 +387,14 @@ Now, oth of the following will work without needing to add multiple versions of John: Hello! JOHN: Hello! +### Setting a custom name format + +The default proxy username formatting is "{name} {tag}", but you can customize this value in config: + + pk;config nameformat {tag} {name} + pk;config nameformat {name}@{tag} + + ## Interacting with proxied messages ### Your own messages diff --git a/shell.nix b/shell.nix index d91d742b..fd39dd48 100644 --- a/shell.nix +++ b/shell.nix @@ -6,6 +6,7 @@ pkgs.mkShellNoCC { gcc protobuf dotnet-sdk_6 + omnisharp-roslyn go nodejs yarn ];