diff --git a/PluralKit.Bot/Commands/MemberCommands.cs b/PluralKit.Bot/Commands/MemberCommands.cs index 9933492e..9187df69 100644 --- a/PluralKit.Bot/Commands/MemberCommands.cs +++ b/PluralKit.Bot/Commands/MemberCommands.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -164,6 +163,20 @@ namespace PluralKit.Bot.Commands return new ProxyTag(prefixAndSuffix[0], prefixAndSuffix[1]); } + async Task WarnOnConflict(ProxyTag newTag) + { + var conflicts = (await _data.GetConflictingProxies(ctx.System, newTag)) + .Where(m => m.Id != target.Id) + .ToList(); + + if (conflicts.Count <= 0) return true; + + var conflictList = conflicts.Select(m => $"- **{m.Name}**"); + var msg = await ctx.Reply( + $"{Emojis.Warn} The following members have conflicting proxy tags:\n{string.Join('\n', conflictList)}\nDo you want to proceed anyway?"); + return await ctx.PromptYesNo(msg); + } + // "Sub"command: no arguments clearing if (!ctx.HasNext()) { @@ -175,7 +188,7 @@ namespace PluralKit.Bot.Commands if (!await ctx.PromptYesNo(msg)) throw Errors.GenericCancelled(); } - + target.ProxyTags = new ProxyTag[] { }; await _data.SaveMember(target); @@ -190,6 +203,9 @@ namespace PluralKit.Bot.Commands if (target.ProxyTags.Contains(tagToAdd)) throw Errors.ProxyTagAlreadyExists(tagToAdd, target); + if (!await WarnOnConflict(tagToAdd)) + throw Errors.GenericCancelled(); + // It's not guaranteed the list's mutable, so we force it to be target.ProxyTags = target.ProxyTags.ToList(); target.ProxyTags.Add(tagToAdd); @@ -224,6 +240,9 @@ namespace PluralKit.Bot.Commands // already more than one proxy tag. if (target.ProxyTags.Count > 1) throw Errors.LegacyAlreadyHasProxyTag(requestedTag, target); + + if (!await WarnOnConflict(requestedTag)) + throw Errors.GenericCancelled(); target.ProxyTags = new[] {requestedTag}; diff --git a/PluralKit.Core/Stores.cs b/PluralKit.Core/Stores.cs index 3a9c93d3..5015ee54 100644 --- a/PluralKit.Core/Stores.cs +++ b/PluralKit.Core/Stores.cs @@ -1,11 +1,9 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using App.Metrics.Logging; + using Dapper; using NodaTime; -using Npgsql; using Serilog; @@ -109,7 +107,17 @@ namespace PluralKit { /// Gets the member count of a system. /// Task GetSystemMemberCount(PKSystem system); - + + /// + /// Gets a list of members with proxy tags that conflict with the given tags. + /// + /// A set of proxy tags A conflict with proxy tags B if both A's prefix and suffix + /// are a "subset" of B's. In other words, if A's prefix *starts* with B's prefix + /// and A's suffix *ends* with B's suffix, the tag pairs are considered conflicting. + /// + /// The system to check in. + Task> GetConflictingProxies(PKSystem system, ProxyTag tag); + /// /// Creates a system, auto-generating its corresponding IDs. /// @@ -359,6 +367,23 @@ namespace PluralKit { _logger = logger; } + public async Task> GetConflictingProxies(PKSystem system, ProxyTag tag) + { + using (var conn = await _conn.Obtain()) + // return await conn.QueryAsync("select * from (select *, (unnest(proxy_tags)).prefix as prefix, (unnest(proxy_tags)).suffix as suffix from members where system = @System) as _ where prefix ilike @Prefix and suffix ilike @Suffix", new + // { + // System = system.Id, + // Prefix = tag.Prefix.Replace("%", "\\%") + "%", + // Suffix = "%" + tag.Suffix.Replace("%", "\\%") + // }); + return await conn.QueryAsync("select * from (select *, (unnest(proxy_tags)).prefix as prefix, (unnest(proxy_tags)).suffix as suffix from members where system = @System) as _ where prefix = @Prefix and suffix = @Suffix", new + { + System = system.Id, + Prefix = tag.Prefix, + Suffix = tag.Suffix + }); + } + public async Task CreateSystem(string systemName = null) { string hid; do