Add support for multiple proxy tags

Tangentially closes #103.
This commit is contained in:
Ske
2019-10-28 20:15:27 +01:00
committed by Astrid
parent 96b03495a4
commit 393ee16c1b
16 changed files with 190 additions and 69 deletions

View File

@@ -28,7 +28,7 @@ namespace PluralKit.Bot.Commands
public static Command MemberPronouns = new Command("member pronouns", "member <member> pronouns [pronouns]", "uwu");
public static Command MemberColor = new Command("member color", "member <member> color [color]", "uwu");
public static Command MemberBirthday = new Command("member birthday", "member <member> birthday [birthday]", "uwu");
public static Command MemberProxy = new Command("member proxy", "member <member> proxy [example proxy]", "uwu");
public static Command MemberProxy = new Command("member proxy", "member <member> proxy [add|remove] [example proxy]", "uwu");
public static Command MemberDelete = new Command("member delete", "member <member> delete", "uwu");
public static Command MemberAvatar = new Command("member avatar", "member <member> avatar [url|@mention]", "uwu");
public static Command MemberDisplayName = new Command("member displayname", "member <member> displayname [display name]", "uwu");

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -153,32 +154,74 @@ namespace PluralKit.Bot.Commands
{
if (ctx.System == null) throw Errors.NoSystemError;
if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError;
// Handling the clear case in an if here to keep the body dedented
var exampleProxy = ctx.RemainderOrNull();
if (exampleProxy == null)
ProxyTag ParseProxyTags(string exampleProxy)
{
// Just reset and send OK message
target.Prefix = null;
target.Suffix = null;
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Member proxy tags cleared.");
await _proxyCache.InvalidateResultsForSystem(ctx.System);
return;
// // Make sure there's one and only one instance of "text" in the example proxy given
var prefixAndSuffix = exampleProxy.Split("text");
if (prefixAndSuffix.Length < 2) throw Errors.ProxyMustHaveText;
if (prefixAndSuffix.Length > 2) throw Errors.ProxyMultipleText;
return new ProxyTag(prefixAndSuffix[0], prefixAndSuffix[1]);
}
// Make sure there's one and only one instance of "text" in the example proxy given
var prefixAndSuffix = exampleProxy.Split("text");
if (prefixAndSuffix.Length < 2) throw Errors.ProxyMustHaveText;
if (prefixAndSuffix.Length > 2) throw Errors.ProxyMultipleText;
// "Sub"command: no arguments clearing
if (!ctx.HasNext())
{
// If we already have multiple tags, this would clear everything, so prompt that
var msg = await ctx.Reply(
$"{Emojis.Warn} You already have multiple proxy tags set: {target.ProxyTagsString()}\nDo you want to clear them all?");
if (!await ctx.PromptYesNo(msg))
throw Errors.GenericCancelled();
target.ProxyTags = new ProxyTag[] { };
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Proxy tags cleared.");
}
// Subcommand: "add"
else if (ctx.Match("add"))
{
var tagToAdd = ParseProxyTags(ctx.RemainderOrNull());
if (target.ProxyTags.Contains(tagToAdd))
throw Errors.ProxyTagAlreadyExists(tagToAdd, target);
// It's not guaranteed the list's mutable, so we force it to be
target.ProxyTags = target.ProxyTags.ToList();
target.ProxyTags.Add(tagToAdd);
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Added proxy tags `{tagToAdd.ProxyString.SanitizeMentions()}`.");
}
// Subcommand: "remove"
else if (ctx.Match("remove"))
{
var tagToRemove = ParseProxyTags(ctx.RemainderOrNull());
if (!target.ProxyTags.Contains(tagToRemove))
throw Errors.ProxyTagDoesNotExist(tagToRemove, target);
// It's not guaranteed the list's mutable, so we force it to be
target.ProxyTags = target.ProxyTags.ToList();
target.ProxyTags.Remove(tagToRemove);
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Removed proxy tags `{tagToRemove.ProxyString.SanitizeMentions()}`.");
}
// Subcommand: bare proxy tag given
else
{
var requestedTag = ParseProxyTags(ctx.RemainderOrNull());
// This is mostly a legacy command, so it's gonna error out if there's
// already more than one proxy tag.
if (target.ProxyTags.Count > 1)
throw Errors.LegacyAlreadyHasProxyTag(requestedTag, target);
target.ProxyTags = new[] {requestedTag};
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Member proxy tags set to `{requestedTag.ProxyString.SanitizeMentions()}`.");
}
// If the prefix/suffix is empty, use "null" instead (for DB)
target.Prefix = prefixAndSuffix[0].Length > 0 ? prefixAndSuffix[0] : null;
target.Suffix = prefixAndSuffix[1].Length > 0 ? prefixAndSuffix[1] : null;
await _data.SaveMember(target);
await ctx.Reply($"{Emojis.Success} Member proxy tags changed to `{target.ProxyString.SanitizeMentions()}`. Try proxying now!");
await _proxyCache.InvalidateResultsForSystem(ctx.System);
}

View File

@@ -134,7 +134,7 @@ namespace PluralKit.Bot.Commands
25,
embedTitle,
(eb, ms) => eb.Description = string.Join("\n", ms.Select((m) => {
if (m.HasProxyTags) return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}** *({m.ProxyString.SanitizeMentions()})*";
if (m.HasProxyTags) return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}** *({m.ProxyTagsString().SanitizeMentions()})*";
return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}**";
}))
);
@@ -154,7 +154,7 @@ namespace PluralKit.Bot.Commands
var profile = $"**ID**: {m.Hid}";
if (m.Pronouns != null) profile += $"\n**Pronouns**: {m.Pronouns}";
if (m.Birthday != null) profile += $"\n**Birthdate**: {m.BirthdayString}";
if (m.Prefix != null || m.Suffix != null) profile += $"\n**Proxy tags**: {m.ProxyString}";
if (m.ProxyTags.Count > 0) profile += $"\n**Proxy tags:** {m.ProxyTagsString()}";
if (m.Description != null) profile += $"\n\n{m.Description}";
eb.AddField(m.Name, profile.Truncate(1024));
}

View File

@@ -80,5 +80,11 @@ namespace PluralKit.Bot {
$"Display name too long ({displayName.Length} > {maxLength} characters). Use a shorter display name, or shorten your system tag.");
public static PKError ProxyNameTooShort(string name) => new PKError($"The webhook's name, `{name.SanitizeMentions()}`, 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 PKError($"The webhook's name, {name.SanitizeMentions()}, is too long ({name.Length} > {Limits.MaxProxyNameLength} characters), and thus cannot be proxied. Please change the member name or use a shorter system tag.");
public static PKError ProxyTagAlreadyExists(ProxyTag tagToAdd, PKMember member) => new PKError($"That member already has the proxy tag `{tagToAdd.ProxyString.SanitizeMentions()}`. The member currently has these tags: {member.ProxyTagsString().SanitizeMentions()}");
public static PKError ProxyTagDoesNotExist(ProxyTag tagToRemove, PKMember member) => new PKError($"That member does not have the proxy tag `{tagToRemove.ProxyString.SanitizeMentions()}`. The member currently has these tags: {member.ProxyTagsString().SanitizeMentions()}");
public static PKError LegacyAlreadyHasProxyTag(ProxyTag requested, PKMember member) => new PKError($"This member already has more than one proxy tag set: {member.ProxyTagsString().SanitizeMentions()}\nConsider using the `pk;member {member.Hid} proxy add {requested.ProxyString.SanitizeMentions()}` command instead.");
public static PKError GenericCancelled() => new PKError("Operation cancelled.");
}
}

View File

@@ -82,6 +82,8 @@ namespace PluralKit.Bot {
var messageCount = await _data.GetMemberMessageCount(member);
var proxyTagsStr = string.Join('\n', member.ProxyTags.Select(t => $"`{t.ProxyString}`"));
var eb = new EmbedBuilder()
// TODO: add URL of website when that's up
.WithAuthor(name, member.AvatarUrl)
@@ -94,7 +96,7 @@ namespace PluralKit.Bot {
if (member.Birthday != null) eb.AddField("Birthdate", member.BirthdayString, true);
if (member.Pronouns != null) eb.AddField("Pronouns", member.Pronouns, true);
if (messageCount > 0) eb.AddField("Message Count", messageCount, true);
if (member.HasProxyTags) eb.AddField("Proxy Tags", $"{member.Prefix.EscapeMarkdown()}text{member.Suffix.EscapeMarkdown()}", true);
if (member.HasProxyTags) eb.AddField("Proxy Tags", string.Join('\n', proxyTagsStr), true);
if (member.Color != null) eb.AddField("Color", $"#{member.Color}", true);
if (member.Description != null) eb.AddField("Description", member.Description, false);

View File

@@ -44,7 +44,7 @@ namespace PluralKit.Bot
_httpClient = new HttpClient();
}
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyCacheService.ProxyDatabaseResult> potentials)
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyCacheService.ProxyDatabaseResult> potentialMembers)
{
// If the message starts with a @mention, and then proceeds to have proxy tags,
// extract the mention and place it inside the inner message
@@ -57,19 +57,19 @@ namespace PluralKit.Bot
message = message.Substring(matchStartPosition);
}
// Sort by specificity (ProxyString length desc = prefix+suffix length desc = inner message asc = more specific proxy first!)
var ordered = potentials.OrderByDescending(p => p.Member.ProxyString.Length);
foreach (var potential in ordered)
// Flatten and sort by specificity (ProxyString length desc = prefix+suffix length desc = inner message asc = more specific proxy first!)
var ordered = potentialMembers.SelectMany(m => m.Member.ProxyTags.Select(tag => (tag, m))).OrderByDescending(p => p.Item1.ProxyString);
foreach (var (tag, match) in ordered)
{
if (potential.Member.Prefix == null && potential.Member.Suffix == null) continue;
if (tag.Prefix == null && tag.Suffix == null) continue;
var prefix = potential.Member.Prefix ?? "";
var suffix = potential.Member.Suffix ?? "";
var prefix = tag.Prefix ?? "";
var suffix = tag.Suffix ?? "";
if (message.Length >= prefix.Length + suffix.Length && message.StartsWith(prefix) && message.EndsWith(suffix)) {
var inner = message.Substring(prefix.Length, message.Length - prefix.Length - suffix.Length);
if (leadingMention != null) inner = $"{leadingMention} {inner}";
return new ProxyMatch { Member = potential.Member, System = potential.System, InnerText = inner };
return new ProxyMatch { Member = match.Member, System = match.System, InnerText = inner };
}
}

View File

@@ -100,6 +100,8 @@ namespace PluralKit.Bot
if (input != null) return pattern.Replace(input, @"\$&");
else return input;
}
public static string ProxyTagsString(this PKMember member) => string.Join(", ", member.ProxyTags.Select(t => $"`{t.ProxyString.EscapeMarkdown()}`"));
public static async Task<ChannelPermissions> PermissionsIn(this IChannel channel)
{