From 2794919728080bf96630377784517a58d68ba59c Mon Sep 17 00:00:00 2001 From: Ske Date: Tue, 25 Aug 2020 22:33:04 +0200 Subject: [PATCH] Use "smart references" for member commands --- PluralKit.Bot/Commands/Groups.cs | 32 ++++++++++---------------- PluralKit.Bot/Commands/MemberAvatar.cs | 6 ++--- PluralKit.Bot/Commands/MemberEdit.cs | 24 +++++++++---------- PluralKit.Bot/Utils/ModelUtils.cs | 32 +++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/PluralKit.Bot/Commands/Groups.cs b/PluralKit.Bot/Commands/Groups.cs index ecfb9eb7..926218de 100644 --- a/PluralKit.Bot/Commands/Groups.cs +++ b/PluralKit.Bot/Commands/Groups.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Dapper; @@ -52,10 +51,10 @@ namespace PluralKit.Bot var eb = new DiscordEmbedBuilder() .WithDescription($"Your new group, **{groupName}**, has been created, with the group ID **`{newGroup.Hid}`**.\nBelow are a couple of useful commands:") - .AddField("View the group card", $"> pk;group **{GroupReference(newGroup)}**") - .AddField("Add members to the group", $"> pk;group **{GroupReference(newGroup)}** add **MemberName**\n> pk;group **{GroupReference(newGroup)}** add **Member1** **Member2** **Member3** (and so on...)") - .AddField("Set the description", $"> pk;group **{GroupReference(newGroup)}** description **This is my new group, and here is the description!**") - .AddField("Set the group icon", $"> pk;group **{GroupReference(newGroup)}** icon\n*(with an image attached)*"); + .AddField("View the group card", $"> pk;group **{newGroup.Reference()}**") + .AddField("Add members to the group", $"> pk;group **{newGroup.Reference()}** add **MemberName**\n> pk;group **{newGroup.Reference()}** add **Member1** **Member2** **Member3** (and so on...)") + .AddField("Set the description", $"> pk;group **{newGroup.Reference()}** description **This is my new group, and here is the description!**") + .AddField("Set the group icon", $"> pk;group **{newGroup.Reference()}** icon\n*(with an image attached)*"); await ctx.Reply($"{Emojis.Success} Group created!", eb.Build()); } @@ -102,7 +101,7 @@ namespace PluralKit.Bot .AddField("Display Name", target.DisplayName ?? "*(none)*"); if (ctx.System?.Id == target.System) - eb.WithDescription($"To change display name, type `pk;group {GroupReference(target)} displayname `.\nTo clear it, type `pk;group {GroupReference(target)} displayname -clear`."); + eb.WithDescription($"To change display name, type `pk;group {target.Reference()} displayname `.\nTo clear it, type `pk;group {target.Reference()} displayname -clear`."); await ctx.Reply(embed: eb.Build()); } @@ -133,7 +132,7 @@ namespace PluralKit.Bot { if (target.Description == null) if (ctx.System?.Id == target.System) - await ctx.Reply($"This group does not have a description set. To set one, type `pk;group {GroupReference(target)} description `."); + await ctx.Reply($"This group does not have a description set. To set one, type `pk;group {target.Reference()} description `."); else await ctx.Reply("This group does not have a description set."); else if (ctx.MatchFlag("r", "raw")) @@ -142,8 +141,8 @@ namespace PluralKit.Bot await ctx.Reply(embed: new DiscordEmbedBuilder() .WithTitle("Group description") .WithDescription(target.Description) - .AddField("\u200B", $"To print the description with formatting, type `pk;group {GroupReference(target)} description -raw`." - + (ctx.System?.Id == target.System ? $" To clear it, type `pk;group {GroupReference(target)} description -clear`." : "")) + .AddField("\u200B", $"To print the description with formatting, type `pk;group {target.Reference()} description -raw`." + + (ctx.System?.Id == target.System ? $" To clear it, type `pk;group {target.Reference()} description -clear`." : "")) .Build()); } else @@ -206,7 +205,7 @@ namespace PluralKit.Bot if (target.System == ctx.System?.Id) { - eb.WithDescription($"To clear, use `pk;group {GroupReference(target)} icon -clear`."); + eb.WithDescription($"To clear, use `pk;group {target.Reference()} icon -clear`."); } await ctx.Reply(embed: eb.Build()); @@ -300,9 +299,9 @@ namespace PluralKit.Bot { if (memberCount == 0 && pctx == LookupContext.ByOwner) // Only suggest the add command if this is actually the owner lol - eb.AddField("Members (0)", $"Add one with `pk;group {GroupReference(target)} add `!", true); + eb.AddField("Members (0)", $"Add one with `pk;group {target.Reference()} add `!", true); else - eb.AddField($"Members ({memberCount})", $"(see `pk;group {GroupReference(target)} list`)", true); + eb.AddField($"Members ({memberCount})", $"(see `pk;group {target.Reference()} list`)", true); } if (target.DescriptionFor(pctx) is {} desc) @@ -416,7 +415,7 @@ namespace PluralKit.Bot .AddField("Icon", target.IconPrivacy.Explanation()) .AddField("Member list", target.ListPrivacy.Explanation()) .AddField("Visibility", target.Visibility.Explanation()) - .WithDescription($"To edit privacy settings, use the command:\n> pk;group **{GroupReference(target)}** privacy **** ****\n\n- `subject` is one of `description`, `icon`, `members`, `visibility`, or `all`\n- `level` is either `public` or `private`.") + .WithDescription($"To edit privacy settings, use the command:\n> pk;group **{target.Reference()}** privacy **** ****\n\n- `subject` is one of `description`, `icon`, `members`, `visibility`, or `all`\n- `level` is either `public` or `private`.") .Build()); return; } @@ -488,12 +487,5 @@ namespace PluralKit.Bot return system; return await conn.QuerySystem(target.System)!; } - - private static string GroupReference(PKGroup group) - { - if (Regex.IsMatch(group.Name, "^[A-Za-z0-9\\-_]+$")) - return group.Name; - return group.Hid; - } } } \ No newline at end of file diff --git a/PluralKit.Bot/Commands/MemberAvatar.cs b/PluralKit.Bot/Commands/MemberAvatar.cs index 4611277d..bdfb47ec 100644 --- a/PluralKit.Bot/Commands/MemberAvatar.cs +++ b/PluralKit.Bot/Commands/MemberAvatar.cs @@ -30,7 +30,7 @@ namespace PluralKit.Bot else { if (mgs?.AvatarUrl != null) - await ctx.Reply($"{Emojis.Success} Member avatar cleared. Note that this member has a server-specific avatar set here, type `pk;member {target.Hid} serveravatar clear` if you wish to clear that too."); + await ctx.Reply($"{Emojis.Success} Member avatar cleared. Note that this member has a server-specific avatar set here, type `pk;member {target.Reference()} serveravatar clear` if you wish to clear that too."); else await ctx.Reply($"{Emojis.Success} Member avatar cleared."); } @@ -50,7 +50,7 @@ namespace PluralKit.Bot } if (location == AvatarLocation.Server) - throw new PKError($"This member does not have a server avatar set. Type `pk;member {target.Hid} avatar` to see their global avatar."); + throw new PKError($"This member does not have a server avatar set. Type `pk;member {target.Reference()} avatar` to see their global avatar."); } var field = location == AvatarLocation.Server ? $"server avatar (for {ctx.Guild.Name})" : "avatar"; @@ -60,7 +60,7 @@ namespace PluralKit.Bot .WithTitle($"{target.NameFor(ctx)}'s {field}") .WithImageUrl(currentValue); if (target.System == ctx.System?.Id) - eb.WithDescription($"To clear, use `pk;member {target.Hid} {cmd} clear`."); + eb.WithDescription($"To clear, use `pk;member {target.Reference()} {cmd} clear`."); await ctx.Reply(embed: eb.Build()); } diff --git a/PluralKit.Bot/Commands/MemberEdit.cs b/PluralKit.Bot/Commands/MemberEdit.cs index a1ffda09..7fc16590 100644 --- a/PluralKit.Bot/Commands/MemberEdit.cs +++ b/PluralKit.Bot/Commands/MemberEdit.cs @@ -78,7 +78,7 @@ namespace PluralKit.Bot throw Errors.LookupNotAllowed; if (target.Description == null) if (ctx.System?.Id == target.System) - await ctx.Reply($"This member does not have a description set. To set one, type `pk;member {target.Hid} description `."); + await ctx.Reply($"This member does not have a description set. To set one, type `pk;member {target.Reference()} description `."); else await ctx.Reply("This member does not have a description set."); else if (ctx.MatchFlag("r", "raw")) @@ -87,8 +87,8 @@ namespace PluralKit.Bot await ctx.Reply(embed: new DiscordEmbedBuilder() .WithTitle("Member description") .WithDescription(target.Description) - .AddField("\u200B", $"To print the description with formatting, type `pk;member {target.Hid} description -raw`." - + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Hid} description -clear`." : "")) + .AddField("\u200B", $"To print the description with formatting, type `pk;member {target.Reference()} description -raw`." + + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Reference()} description -clear`." : "")) .Build()); } else @@ -120,12 +120,12 @@ namespace PluralKit.Bot throw Errors.LookupNotAllowed; if (target.Pronouns == null) if (ctx.System?.Id == target.System) - await ctx.Reply($"This member does not have pronouns set. To set some, type `pk;member {target.Hid} pronouns `."); + await ctx.Reply($"This member does not have pronouns set. To set some, type `pk;member {target.Reference()} pronouns `."); else await ctx.Reply("This member does not have pronouns set."); else await ctx.Reply($"**{target.NameFor(ctx)}**'s pronouns are **{target.Pronouns}**." - + (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Hid} pronouns -clear`." : "")); + + (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Reference()} pronouns -clear`." : "")); } else { @@ -162,7 +162,7 @@ namespace PluralKit.Bot if (target.Color == null) if (ctx.System?.Id == target.System) await ctx.Reply( - $"This member does not have a color set. To set one, type `pk;member {target.Hid} color `."); + $"This member does not have a color set. To set one, type `pk;member {target.Reference()} color `."); else await ctx.Reply("This member does not have a color set."); else @@ -171,7 +171,7 @@ namespace PluralKit.Bot .WithColor(target.Color.ToDiscordColor().Value) .WithThumbnail($"https://fakeimg.pl/256x256/{target.Color}/?text=%20") .WithDescription($"This member's color is **#{target.Color}**." - + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Hid} color -clear`." : "")) + + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Reference()} color -clear`." : "")) .Build()); } else @@ -209,10 +209,10 @@ namespace PluralKit.Bot if (target.Birthday == null) await ctx.Reply("This member does not have a birthdate set." - + (ctx.System?.Id == target.System ? $" To set one, type `pk;member {target.Hid} birthdate `." : "")); + + (ctx.System?.Id == target.System ? $" To set one, type `pk;member {target.Reference()} birthdate `." : "")); else await ctx.Reply($"This member's birthdate is **{target.BirthdayString}**." - + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Hid} birthdate -clear`." : "")); + + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Reference()} birthdate -clear`." : "")); } else { @@ -293,7 +293,7 @@ namespace PluralKit.Bot // No perms check, display name isn't covered by member privacy var eb = await CreateMemberNameInfoEmbed(ctx, target); if (ctx.System?.Id == target.System) - eb.WithDescription($"To change display name, type `pk;member {target.Hid} displayname `.\nTo clear it, type `pk;member {target.Hid} displayname -clear`."); + eb.WithDescription($"To change display name, type `pk;member {target.Reference()} displayname `.\nTo clear it, type `pk;member {target.Reference()} displayname -clear`."); await ctx.Reply(embed: eb.Build()); } else @@ -330,7 +330,7 @@ namespace PluralKit.Bot // No perms check, server name isn't covered by member privacy var eb = await CreateMemberNameInfoEmbed(ctx, target); if (ctx.System?.Id == target.System) - eb.WithDescription($"To change server name, type `pk;member {target.Hid} servername `.\nTo clear it, type `pk;member {target.Hid} servername -clear`."); + eb.WithDescription($"To change server name, type `pk;member {target.Reference()} servername `.\nTo clear it, type `pk;member {target.Reference()} servername -clear`."); await ctx.Reply(embed: eb.Build()); } else @@ -455,7 +455,7 @@ namespace PluralKit.Bot // Avatar privacy doesn't apply when proxying if no server avatar is set if (subject == MemberPrivacySubject.Avatar && level == PrivacyLevel.Private && guildSettings?.AvatarUrl == null) - await ctx.Reply($"{Emojis.Warn} This member does not have a server avatar set, so *proxying* will **still show the member avatar**. If you want to hide your avatar when proxying here, set a server avatar: `pk;member {target.Hid} serveravatar`"); + await ctx.Reply($"{Emojis.Warn} This member does not have a server avatar set, so *proxying* will **still show the member avatar**. If you want to hide your avatar when proxying here, set a server avatar: `pk;member {target.Reference()} serveravatar`"); } if (ctx.Match("all") || newValueFromCommand != null) diff --git a/PluralKit.Bot/Utils/ModelUtils.cs b/PluralKit.Bot/Utils/ModelUtils.cs index 28dc3927..8c29093d 100644 --- a/PluralKit.Bot/Utils/ModelUtils.cs +++ b/PluralKit.Bot/Utils/ModelUtils.cs @@ -1,4 +1,7 @@ -using PluralKit.Core; +using System.Linq; +using System.Text.RegularExpressions; + +using PluralKit.Core; namespace PluralKit.Bot { @@ -12,5 +15,32 @@ namespace PluralKit.Bot public static string DisplayName(this PKMember member) => member.DisplayName ?? member.Name; + + public static string Reference(this PKMember member) => EntityReference(member.Hid, member.Name); + public static string Reference(this PKGroup group) => EntityReference(group.Hid, group.Name); + + private static string EntityReference(string hid, string name) + { + bool IsSimple(string s) => + // No spaces, no symbols, allow single quote but not at the start + Regex.IsMatch(s, "^[\\w\\d\\-_'?]+$") && !s.StartsWith("'"); + + // If it's very long (>25 chars), always use hid + if (name.Length >= 25) + return hid; + + // If name is "simple" just use that + if (IsSimple(name)) + return name; + + // If three or fewer "words" and they're all simple individually, quote them + var words = name.Split(' '); + if (words.Length <= 3 && words.All(w => w.Length > 0 && IsSimple(w))) + // Words with double quotes are never "simple" so we're safe to naive-quote here + return $"\"{name}\""; + + // Otherwise, just use hid + return hid; + } } } \ No newline at end of file