mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
feat(bot): basic webhook username templating
This commit is contained in:
parent
87196e3297
commit
614131265b
16 changed files with 78 additions and 14 deletions
|
|
@ -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 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 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 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 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 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");
|
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 =
|
public static Command[] ConfigCommands =
|
||||||
{
|
{
|
||||||
ConfigAutoproxyAccount, ConfigAutoproxyTimeout, ConfigTimezone, ConfigPing,
|
ConfigAutoproxyAccount, ConfigAutoproxyTimeout, ConfigTimezone, ConfigPing,
|
||||||
ConfigMemberDefaultPrivacy, ConfigGroupDefaultPrivacy, ConfigShowPrivate
|
ConfigMemberDefaultPrivacy, ConfigGroupDefaultPrivacy, ConfigShowPrivate, ConfigNameFormat
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Command[] AutoproxyCommands =
|
public static Command[] AutoproxyCommands =
|
||||||
|
|
|
||||||
|
|
@ -594,6 +594,8 @@ public partial class CommandTree
|
||||||
return ctx.Execute<Config>(null, m => m.HidDisplayCaps(ctx));
|
return ctx.Execute<Config>(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"))
|
if (ctx.MatchMultiple(new[] { "pad" }, new[] { "id", "ids" }) || ctx.MatchMultiple(new[] { "id" }, new[] { "pad", "padding" }) || ctx.Match("idpad", "padid", "padids"))
|
||||||
return ctx.Execute<Config>(null, m => m.HidListPadding(ctx));
|
return ctx.Execute<Config>(null, m => m.HidListPadding(ctx));
|
||||||
|
if (ctx.MatchMultiple(new[] { "name" }, new[] { "format" }) || ctx.Match("nf"))
|
||||||
|
return ctx.Execute<Config>(null, m => m.NameFormat(ctx));
|
||||||
if (ctx.MatchMultiple(new[] { "member", "group" }, new[] { "limit" }) || ctx.Match("limit"))
|
if (ctx.MatchMultiple(new[] { "member", "group" }, new[] { "limit" }) || ctx.Match("limit"))
|
||||||
return ctx.Execute<Config>(null, m => m.LimitUpdate(ctx));
|
return ctx.Execute<Config>(null, m => m.LimitUpdate(ctx));
|
||||||
if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "switch" }) || ctx.Match("proxyswitch", "ps"))
|
if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "switch" }) || ctx.Match("proxyswitch", "ps"))
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,13 @@ public class Config
|
||||||
"disabled"
|
"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<PaginatedConfigItem>(
|
await ctx.Paginate<PaginatedConfigItem>(
|
||||||
items.ToAsyncEnumerable(),
|
items.ToAsyncEnumerable(),
|
||||||
items.Count,
|
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)}.");
|
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)
|
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 " +
|
throw new PKError("You cannot update your own member or group limits. If you need a limit update, please join the " +
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
$"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(
|
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(
|
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()}");
|
$"That member already has the proxy tag {tagToAdd.ProxyString.AsCode()}. The member currently has these tags: {member.ProxyTagsString()}");
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ public class MessageContext
|
||||||
public string? SystemTag { get; }
|
public string? SystemTag { get; }
|
||||||
public string? SystemGuildTag { get; }
|
public string? SystemGuildTag { get; }
|
||||||
public bool TagEnabled { get; }
|
public bool TagEnabled { get; }
|
||||||
|
public string? NameFormat { get; }
|
||||||
public string? SystemAvatar { get; }
|
public string? SystemAvatar { get; }
|
||||||
public string? SystemGuildAvatar { get; }
|
public string? SystemGuildAvatar { get; }
|
||||||
public bool AllowAutoproxy { get; }
|
public bool AllowAutoproxy { get; }
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,23 @@ public class ProxyMember
|
||||||
public bool AllowAutoproxy { get; }
|
public bool AllowAutoproxy { get; }
|
||||||
public string? Color { 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)
|
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;
|
var memberName = ServerName ?? DisplayName ?? Name;
|
||||||
if (!ctx.TagEnabled)
|
var tag = ctx.SystemGuildTag ?? ctx.SystemTag;
|
||||||
|
if (!ctx.TagEnabled || tag == null)
|
||||||
return memberName;
|
return memberName;
|
||||||
|
|
||||||
if (ctx.SystemGuildTag != null)
|
return FormatTag(ctx.NameFormat ?? DefaultFormat, tag, memberName);
|
||||||
return $"{memberName} {ctx.SystemGuildTag}";
|
|
||||||
if (ctx.SystemTag != null)
|
|
||||||
return $"{memberName} {ctx.SystemTag}";
|
|
||||||
return memberName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? ProxyAvatar(MessageContext ctx) => ServerAvatar ?? WebhookAvatar ?? Avatar ?? ctx.SystemGuildAvatar ?? ctx.SystemAvatar;
|
public string? ProxyAvatar(MessageContext ctx) => ServerAvatar ?? WebhookAvatar ?? Avatar ?? ctx.SystemGuildAvatar ?? ctx.SystemAvatar;
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
returns table (
|
||||||
allow_autoproxy bool,
|
allow_autoproxy bool,
|
||||||
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
case_sensitive_proxy_tags bool,
|
case_sensitive_proxy_tags bool,
|
||||||
proxy_error_message_enabled bool,
|
proxy_error_message_enabled bool,
|
||||||
proxy_switch bool,
|
proxy_switch bool,
|
||||||
|
name_format text,
|
||||||
|
|
||||||
tag_enabled bool,
|
tag_enabled bool,
|
||||||
proxy_enabled bool,
|
proxy_enabled bool,
|
||||||
|
|
@ -42,6 +43,7 @@ as $$
|
||||||
system_config.case_sensitive_proxy_tags as case_sensitive_proxy_tags,
|
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_error_message_enabled as proxy_error_message_enabled,
|
||||||
system_config.proxy_switch as proxy_switch,
|
system_config.proxy_switch as proxy_switch,
|
||||||
|
system_config.name_format as name_format,
|
||||||
|
|
||||||
-- system_guild table
|
-- system_guild table
|
||||||
coalesce(system_guild.tag_enabled, true) as tag_enabled,
|
coalesce(system_guild.tag_enabled, true) as tag_enabled,
|
||||||
|
|
|
||||||
6
PluralKit.Core/Database/Migrations/47.sql
Normal file
6
PluralKit.Core/Database/Migrations/47.sql
Normal file
|
|
@ -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;
|
||||||
|
|
@ -9,7 +9,7 @@ namespace PluralKit.Core;
|
||||||
internal class DatabaseMigrator
|
internal class DatabaseMigrator
|
||||||
{
|
{
|
||||||
private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files
|
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;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public DatabaseMigrator(ILogger logger)
|
public DatabaseMigrator(ILogger logger)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public class SystemConfigPatch: PatchObject
|
||||||
public Partial<bool> ProxyErrorMessageEnabled { get; set; }
|
public Partial<bool> ProxyErrorMessageEnabled { get; set; }
|
||||||
public Partial<bool> HidDisplaySplit { get; set; }
|
public Partial<bool> HidDisplaySplit { get; set; }
|
||||||
public Partial<bool> HidDisplayCaps { get; set; }
|
public Partial<bool> HidDisplayCaps { get; set; }
|
||||||
|
public Partial<string?> NameFormat { get; set; }
|
||||||
public Partial<SystemConfig.HidPadFormat> HidListPadding { get; set; }
|
public Partial<SystemConfig.HidPadFormat> HidListPadding { get; set; }
|
||||||
public Partial<bool> ProxySwitch { get; set; }
|
public Partial<bool> ProxySwitch { get; set; }
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ public class SystemConfigPatch: PatchObject
|
||||||
.With("hid_display_caps", HidDisplayCaps)
|
.With("hid_display_caps", HidDisplayCaps)
|
||||||
.With("hid_list_padding", HidListPadding)
|
.With("hid_list_padding", HidListPadding)
|
||||||
.With("proxy_switch", ProxySwitch)
|
.With("proxy_switch", ProxySwitch)
|
||||||
|
.With("name_format", NameFormat)
|
||||||
);
|
);
|
||||||
|
|
||||||
public new void AssertIsValid()
|
public new void AssertIsValid()
|
||||||
|
|
@ -107,6 +109,9 @@ public class SystemConfigPatch: PatchObject
|
||||||
if (ProxySwitch.IsPresent)
|
if (ProxySwitch.IsPresent)
|
||||||
o.Add("proxy_switch", ProxySwitch.Value);
|
o.Add("proxy_switch", ProxySwitch.Value);
|
||||||
|
|
||||||
|
if (NameFormat.IsPresent)
|
||||||
|
o.Add("name_format", NameFormat.Value);
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,6 +152,9 @@ public class SystemConfigPatch: PatchObject
|
||||||
if (o.ContainsKey("proxy_switch"))
|
if (o.ContainsKey("proxy_switch"))
|
||||||
patch.ProxySwitch = o.Value<bool>("proxy_switch");
|
patch.ProxySwitch = o.Value<bool>("proxy_switch");
|
||||||
|
|
||||||
|
if (o.ContainsKey("name_format"))
|
||||||
|
patch.NameFormat = o.Value<string>("name_format");
|
||||||
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ public class SystemConfig
|
||||||
public bool HidDisplayCaps { get; }
|
public bool HidDisplayCaps { get; }
|
||||||
public HidPadFormat HidListPadding { get; }
|
public HidPadFormat HidListPadding { get; }
|
||||||
public bool ProxySwitch { get; }
|
public bool ProxySwitch { get; }
|
||||||
|
public string NameFormat { get; }
|
||||||
|
|
||||||
public enum HidPadFormat
|
public enum HidPadFormat
|
||||||
{
|
{
|
||||||
|
|
@ -54,6 +55,7 @@ public static class SystemConfigExt
|
||||||
o.Add("hid_display_caps", cfg.HidDisplayCaps);
|
o.Add("hid_display_caps", cfg.HidDisplayCaps);
|
||||||
o.Add("hid_list_padding", cfg.HidListPadding.ToUserString());
|
o.Add("hid_list_padding", cfg.HidListPadding.ToUserString());
|
||||||
o.Add("proxy_switch", cfg.ProxySwitch);
|
o.Add("proxy_switch", cfg.ProxySwitch);
|
||||||
|
o.Add("name_format", cfg.NameFormat);
|
||||||
|
|
||||||
o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates));
|
o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,4 +86,10 @@ public static class StringUtils
|
||||||
|
|
||||||
return output;
|
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));
|
||||||
}
|
}
|
||||||
|
|
@ -739,8 +739,8 @@
|
||||||
},
|
},
|
||||||
"Sentry": {
|
"Sentry": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.11.1",
|
"resolved": "4.12.1",
|
||||||
"contentHash": "T/NLfs6MMkUSYsPEDajB9ad0124T18I0uUod5MNOev3iwjvcnIEQBrStEX2olbIxzqfvGXzQ/QFqTfA2ElLPlA=="
|
"contentHash": "OLf7885OKHWLaTLTyw884mwOT4XKCWj2Hz5Wuz/TJemJqXwCIdIljkJBIoeHviRUPvtB7ulDgeYXf/Z7ScToSA=="
|
||||||
},
|
},
|
||||||
"Serilog": {
|
"Serilog": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
|
|
@ -1809,7 +1809,7 @@
|
||||||
"Humanizer.Core": "[2.8.26, )",
|
"Humanizer.Core": "[2.8.26, )",
|
||||||
"Myriad": "[1.0.0, )",
|
"Myriad": "[1.0.0, )",
|
||||||
"PluralKit.Core": "[1.0.0, )",
|
"PluralKit.Core": "[1.0.0, )",
|
||||||
"Sentry": "[3.11.1, )",
|
"Sentry": "[4.12.1, )",
|
||||||
"SixLabors.ImageSharp": "[3.1.5, )"
|
"SixLabors.ImageSharp": "[3.1.5, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 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 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 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
|
## Server owner commands
|
||||||
*(all commands here require Manage Server permission)*
|
*(all commands here require Manage Server permission)*
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,14 @@ Now, oth of the following will work without needing to add multiple versions of
|
||||||
John: Hello!
|
John: Hello!
|
||||||
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
|
## Interacting with proxied messages
|
||||||
|
|
||||||
### Your own messages
|
### Your own messages
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ pkgs.mkShellNoCC {
|
||||||
gcc
|
gcc
|
||||||
protobuf
|
protobuf
|
||||||
dotnet-sdk_6
|
dotnet-sdk_6
|
||||||
|
omnisharp-roslyn
|
||||||
go
|
go
|
||||||
nodejs yarn
|
nodejs yarn
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue