diff --git a/PluralKit.Bot/Commands/MemberAvatar.cs b/PluralKit.Bot/Commands/MemberAvatar.cs index e39653bb..ac84d964 100644 --- a/PluralKit.Bot/Commands/MemberAvatar.cs +++ b/PluralKit.Bot/Commands/MemberAvatar.cs @@ -20,6 +20,21 @@ namespace PluralKit.Bot { var guildData = ctx.Guild != null ? await _data.GetMemberGuildSettings(target, ctx.Guild.Id) : null; + if (ctx.Match("clear", "remove", "reset") || ctx.MatchFlag("c", "clear")) + { + if (ctx.System == null) throw Errors.NoSystemError; + if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; + + target.AvatarUrl = null; + await _data.SaveMember(target); + + if (guildData?.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."); + else + await ctx.Reply($"{Emojis.Success} Member avatar cleared."); + return; + } + if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((target.AvatarUrl?.Trim() ?? "").Length > 0) @@ -28,7 +43,7 @@ namespace PluralKit.Bot .WithTitle($"{target.Name.SanitizeMentions()}'s avatar") .WithImageUrl(target.AvatarUrl); if (target.System == ctx.System?.Id) - eb.WithDescription($"To clear, use `pk;member {target.Hid} avatar clear`."); + eb.WithDescription($"To clear, use `pk;member {target.Hid} avatar -clear`."); await ctx.Reply(embed: eb.Build()); } else @@ -44,16 +59,6 @@ namespace PluralKit.Bot if (ctx.System == null) throw Errors.NoSystemError; if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - if (ctx.Match("clear", "remove", "reset")) - { - target.AvatarUrl = null; - await _data.SaveMember(target); - - if (guildData?.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."); - else - await ctx.Reply($"{Emojis.Success} Member avatar cleared."); - } else if (await ctx.MatchUser() is IUser user) { if (user.AvatarId == null) throw Errors.UserHasNoAvatar; @@ -90,6 +95,20 @@ namespace PluralKit.Bot ctx.CheckGuildContext(); var guildData = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); + if (ctx.Match("clear", "remove", "reset") || ctx.MatchFlag("c", "clear")) + { + if (ctx.System == null) throw Errors.NoSystemError; + if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; + + guildData.AvatarUrl = null; + await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); + + if (target.AvatarUrl != null) + await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member will now use the global avatar in this server (**{ctx.Guild.Name}**)."); + else + await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member now has no avatar."); + } + if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((guildData.AvatarUrl?.Trim() ?? "").Length > 0) @@ -110,17 +129,7 @@ namespace PluralKit.Bot if (ctx.System == null) throw Errors.NoSystemError; if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - if (ctx.Match("clear", "remove", "reset")) - { - guildData.AvatarUrl = null; - await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); - - if (target.AvatarUrl != null) - await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member will now use the global avatar in this server (**{ctx.Guild.Name}**)."); - else - await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member now has no avatar."); - } - else if (await ctx.MatchUser() is IUser user) + if (await ctx.MatchUser() is IUser user) { if (user.AvatarId == null) throw Errors.UserHasNoAvatar; guildData.AvatarUrl = user.GetAvatarUrl(ImageFormat.Png, size: 256); diff --git a/PluralKit.Bot/Commands/MemberEdit.cs b/PluralKit.Bot/Commands/MemberEdit.cs index 50dcda25..96f536e4 100644 --- a/PluralKit.Bot/Commands/MemberEdit.cs +++ b/PluralKit.Bot/Commands/MemberEdit.cs @@ -1,4 +1,5 @@ -using System.Text.RegularExpressions; +using System; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Discord; @@ -50,18 +51,36 @@ namespace PluralKit.Bot await ctx.Reply($"{Emojis.Note} Note that this member has a server name set ({memberGuildConfig.DisplayName.SanitizeMentions()}) in this server ({ctx.Guild.Name.SanitizeMentions()}), and will be proxied using that name here."); } } - - public async Task Description(Context ctx, PKMember target) { - var description = ctx.RemainderOrNull()?.NormalizeLineEndSpacing(); - var lookupContext = ctx.LookupContextFor(target.System); - if (description == null) + private void CheckReadMemberPermission(Context ctx, PKMember target) + { + if (!target.MemberPrivacy.CanAccess(ctx.LookupContextFor(target.System))) + throw Errors.LookupNotAllowed; + } + + private void CheckEditMemberPermission(Context ctx, PKMember target) + { + if (target.System != ctx.System?.Id) throw Errors.NotOwnMemberError; + } + + private static bool MatchClear(Context ctx) => + ctx.Match("clear") || ctx.MatchFlag("c", "clear"); + + public async Task Description(Context ctx, PKMember target) { + if (MatchClear(ctx)) { - if (!target.MemberPrivacy.CanAccess(lookupContext)) - throw Errors.LookupNotAllowed; + CheckEditMemberPermission(ctx, target); + target.Description = null; + + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member description cleared."); + } + else if (!ctx.HasNext()) + { + CheckReadMemberPermission(ctx, target); if (target.Description == null) - if (lookupContext == LookupContext.ByOwner) - await ctx.Reply("This member does not have a description set. To set one, type `pk;s description `."); + 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 `."); else await ctx.Reply("This member does not have a description set."); else if (ctx.MatchFlag("r", "raw")) @@ -70,125 +89,245 @@ namespace PluralKit.Bot await ctx.Reply(embed: new EmbedBuilder() .WithTitle("Member description") .WithDescription(target.Description) - .WithFooter($"To print the description with formatting, type `pk;m {target.Hid} description -raw`." - + (lookupContext == LookupContext.ByOwner ? $" To clear it, type `pk;m {target.Hid} description -clear`." : "")) + .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`." : "")) .Build()); - - return; } + else + { + CheckEditMemberPermission(ctx, target); - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - - if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) - target.Description = null; - else if (description.IsLongerThan(Limits.MaxDescriptionLength)) - throw Errors.DescriptionTooLongError(description.Length); - else target.Description = description; - - await _data.SaveMember(target); - await ctx.Reply($"{Emojis.Success} Member description {(target.Description == null ? "cleared" : "changed")}."); + var description = ctx.RemainderOrNull().NormalizeLineEndSpacing(); + if (description.IsLongerThan(Limits.MaxDescriptionLength)) + throw Errors.DescriptionTooLongError(description.Length); + target.Description = description; + + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member description changed."); + } } public async Task Pronouns(Context ctx, PKMember target) { - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; + if (MatchClear(ctx)) + { + CheckEditMemberPermission(ctx, target); + target.Pronouns = null; + + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member pronouns cleared."); + } + else if (!ctx.HasNext()) + { + CheckReadMemberPermission(ctx, target); + 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 `."); + else + await ctx.Reply("This member does not have pronouns set."); + else + await ctx.Reply($"**{target.Name.SanitizeMentions()}**'s pronouns are **{target.Pronouns.SanitizeMentions()}**." + + (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Hid} pronouns -clear`." : "")); + } + else + { + CheckEditMemberPermission(ctx, target); - var pronouns = ctx.RemainderOrNull(); - if (pronouns.IsLongerThan(Limits.MaxPronounsLength)) throw Errors.MemberPronounsTooLongError(pronouns.Length); - - target.Pronouns = pronouns; - await _data.SaveMember(target); - - await ctx.Reply($"{Emojis.Success} Member pronouns {(pronouns == null ? "cleared" : "changed")}."); + var pronouns = ctx.RemainderOrNull().NormalizeLineEndSpacing(); + if (pronouns.IsLongerThan(Limits.MaxPronounsLength)) + throw Errors.MemberPronounsTooLongError(pronouns.Length); + target.Pronouns = pronouns; + + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member pronouns changed."); + } } public async Task Color(Context ctx, PKMember target) { - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - var color = ctx.RemainderOrNull(); - if (color != null) + if (MatchClear(ctx)) { + CheckEditMemberPermission(ctx, target); + target.Color = null; + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member color cleared."); + } + else if (!ctx.HasNext()) + { + CheckReadMemberPermission(ctx, target); + + 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 `."); + else + await ctx.Reply("This member does not have a color set."); + else + await ctx.Reply(embed: new EmbedBuilder() + .WithTitle("Member color") + .WithColor(target.Color.ToDiscordColor().Value) + .WithThumbnailUrl($"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`." : "")) + .Build()); + } + else + { + CheckEditMemberPermission(ctx, target); + if (color.StartsWith("#")) color = color.Substring(1); if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color); + target.Color = color.ToLower(); + await _data.SaveMember(target); + + await ctx.Reply(embed: new EmbedBuilder() + .WithTitle($"{Emojis.Success} Member color changed.") + .WithColor(target.Color.ToDiscordColor().Value) + .WithThumbnailUrl($"https://fakeimg.pl/256x256/{target.Color}/?text=%20") + .Build()); } - - target.Color = color?.ToLower(); - await _data.SaveMember(target); - - await ctx.Reply($"{Emojis.Success} Member color {(color == null ? "cleared" : "changed")}."); } - public async Task Birthday(Context ctx, PKMember target) { - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - - LocalDate? date = null; - var birthday = ctx.RemainderOrNull(); - if (birthday != null) + if (MatchClear(ctx)) { - date = DateUtils.ParseDate(birthday, true); - if (date == null) throw Errors.BirthdayParseError(birthday); + CheckEditMemberPermission(ctx, target); + target.Birthday = null; + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member birthdate cleared."); + } + else if (!ctx.HasNext()) + { + CheckReadMemberPermission(ctx, target); + + 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 `." : "")); + 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`." : "")); } - - target.Birthday = date; - await _data.SaveMember(target); - - await ctx.Reply($"{Emojis.Success} Member birthdate {(date == null ? "cleared" : $"changed to {target.BirthdayString}")}."); - } - - public async Task DisplayName(Context ctx, PKMember target) - { - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - - var newDisplayName = ctx.RemainderOrNull(); - - target.DisplayName = newDisplayName; - await _data.SaveMember(target); - - var successStr = $"{Emojis.Success} "; - if (newDisplayName != null) - successStr += $"Member display name changed. This member will now be proxied using the name \"{newDisplayName.SanitizeMentions()}\"."; else - successStr += $"Member display name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\"."; + { + CheckEditMemberPermission(ctx, target); + + var birthdayStr = ctx.RemainderOrNull(); + var birthday = DateUtils.ParseDate(birthdayStr, true); + if (birthday == null) throw Errors.BirthdayParseError(birthdayStr); + target.Birthday = birthday; + await _data.SaveMember(target); + await ctx.Reply($"{Emojis.Success} Member birthdate changed."); + } + } + + private async Task CreateMemberNameInfoEmbed(Context ctx, PKMember target) + { + MemberGuildSettings memberGuildConfig = null; + if (ctx.Guild != null) + memberGuildConfig = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); + + var eb = new EmbedBuilder().WithTitle($"Member names") + .WithFooter($"Member ID: {target.Hid} | Active name in bold. Server name overrides display name, which overrides base name."); + + if (target.DisplayName == null && memberGuildConfig?.DisplayName == null) + eb.AddField($"Name", $"**{target.Name}**"); + else + eb.AddField("Name", target.Name); + + if (target.DisplayName != null && memberGuildConfig?.DisplayName == null) + eb.AddField($"Display Name", $"**{target.DisplayName}**"); + else + eb.AddField("Display Name", target.DisplayName ?? "*(none)*"); if (ctx.Guild != null) { - var memberGuildConfig = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); - if (memberGuildConfig.DisplayName != null) - successStr += $" However, this member has a server name set in this server ({ctx.Guild.Name.SanitizeMentions()}), and will be proxied using that name, \"{memberGuildConfig.DisplayName.SanitizeMentions()}\", here."; + if (memberGuildConfig?.DisplayName != null) + eb.AddField($"Server Name (in {ctx.Guild.Name.SanitizeMentions()})", $"**{memberGuildConfig.DisplayName}**"); + else + eb.AddField($"Server Name (in {ctx.Guild.Name.SanitizeMentions()})", memberGuildConfig?.DisplayName ?? "*(none)*"); } - await ctx.Reply(successStr); + return eb; } + public async Task DisplayName(Context ctx, PKMember target) + { + async Task PrintSuccess(string text) + { + var successStr = text; + if (ctx.Guild != null) + { + var memberGuildConfig = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); + if (memberGuildConfig.DisplayName != null) + successStr += $" However, this member has a server name set in this server ({ctx.Guild.Name.SanitizeMentions()}), and will be proxied using that name, \"{memberGuildConfig.DisplayName.SanitizeMentions()}\", here."; + } + + await ctx.Reply(successStr); + } + + if (MatchClear(ctx)) + { + CheckEditMemberPermission(ctx, target); + + target.DisplayName = null; + await _data.SaveMember(target); + await PrintSuccess($"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\"."); + } + else if (!ctx.HasNext()) + { + // 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`."); + await ctx.Reply(embed: eb.Build()); + } + else + { + CheckEditMemberPermission(ctx, target); + + var newDisplayName = ctx.RemainderOrNull(); + target.DisplayName = newDisplayName; + await _data.SaveMember(target); + + await PrintSuccess($"{Emojis.Success} Member display name changed. This member will now be proxied using the name \"{newDisplayName.SanitizeMentions()}\"."); + } + } + public async Task ServerName(Context ctx, PKMember target) { - if (ctx.System == null) throw Errors.NoSystemError; - if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError; - - // TODO: allow setting server names for different servers/in DMs by ID ctx.CheckGuildContext(); - - var newServerName = ctx.RemainderOrNull(); - var guildSettings = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); - guildSettings.DisplayName = newServerName; - await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildSettings); + + if (MatchClear(ctx)) + { + CheckEditMemberPermission(ctx, target); + + guildSettings.DisplayName = null; + await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildSettings); - var successStr = $"{Emojis.Success} "; - if (newServerName != null) - successStr += $"Member server name changed. This member will now be proxied using the name \"{newServerName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."; - else if (target.DisplayName != null) - successStr += $"Member server name cleared. This member will now be proxied using their global display name \"{target.DisplayName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."; + if (target.DisplayName != null) + await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their global display name \"{target.DisplayName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."); + else + await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."); + } + else if (!ctx.HasNext()) + { + // 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`."); + await ctx.Reply(embed: eb.Build()); + } else - successStr += $"Member server name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."; + { + CheckEditMemberPermission(ctx, target); + + var newServerName = ctx.RemainderOrNull(); + guildSettings.DisplayName = newServerName; + await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildSettings); - await ctx.Reply(successStr); + await ctx.Reply($"{Emojis.Success} Member server name changed. This member will now be proxied using the name \"{newServerName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()})."); + } } public async Task KeepProxy(Context ctx, PKMember target) @@ -200,7 +339,14 @@ namespace PluralKit.Bot if (ctx.Match("on", "enabled", "true", "yes")) newValue = true; else if (ctx.Match("off", "disabled", "false", "no")) newValue = false; else if (ctx.HasNext()) throw new PKSyntaxError("You must pass either \"on\" or \"off\"."); - else newValue = !target.KeepProxy; + else + { + if (target.KeepProxy) + await ctx.Reply("This member has keepproxy **enabled**, which means proxy tags will be **included** in the resulting message when proxying."); + else + await ctx.Reply("This member has keepproxy **disabled**, which means proxy tags will **not** be included in the resulting message when proxying."); + return; + }; target.KeepProxy = newValue; await _data.SaveMember(target); @@ -220,8 +366,17 @@ namespace PluralKit.Bot if (ctx.Match("private", "hide", "hidden", "on", "enable", "yes")) newValue = PrivacyLevel.Private; else if (ctx.Match("public", "show", "shown", "displayed", "off", "disable", "no")) newValue = PrivacyLevel.Public; else if (ctx.HasNext()) throw new PKSyntaxError("You must pass either \"private\" or \"public\"."); - // If we're getting a value from command (eg. "pk;m private" == always private, "pk;m public == always public"), use that instead of parsing/toggling - else newValue = newValueFromCommand ?? (target.MemberPrivacy != PrivacyLevel.Private ? PrivacyLevel.Private : PrivacyLevel.Public); + // If we're getting a value from command (eg. "pk;m private" == always private, "pk;m public == always public"), use that instead of parsing + else if (newValueFromCommand != null) newValue = newValueFromCommand.Value; + else + { + if (target.MemberPrivacy == PrivacyLevel.Public) + await ctx.Reply("This member's privacy is currently set to **public**. This member will not show up in member lists and will return limited information when queried by other accounts."); + else + await ctx.Reply("This member's privacy is currently set to **private**. This member will show up in member lists and will return all information when queried by other accounts."); + + return; + } target.MemberPrivacy = newValue; await _data.SaveMember(target); diff --git a/PluralKit.Bot/Commands/MemberProxy.cs b/PluralKit.Bot/Commands/MemberProxy.cs index 61d059e2..1bff110e 100644 --- a/PluralKit.Bot/Commands/MemberProxy.cs +++ b/PluralKit.Bot/Commands/MemberProxy.cs @@ -42,9 +42,8 @@ namespace PluralKit.Bot return await ctx.PromptYesNo(msg); } - // "Sub"command: no arguments clearing - // Also matches the pseudo-subcommand "text" which is equivalent to empty proxy tags on both sides. - if (!ctx.HasNext(skipFlags: false) || ctx.Match("clear", "purge", "clean", "removeall")) + // "Sub"command: clear flag + if (ctx.Match("clear", "purge", "clean", "removeall", "text") || ctx.MatchFlag("c", "clear")) { // If we already have multiple tags, this would clear everything, so prompt that if (target.ProxyTags.Count > 1) @@ -60,6 +59,17 @@ namespace PluralKit.Bot await _data.SaveMember(target); await ctx.Reply($"{Emojis.Success} Proxy tags cleared."); } + // "Sub"command: no arguments; will print proxy tags + else if (!ctx.HasNext(skipFlags: false)) + { + if (target.ProxyTags.Count == 0) + await ctx.Reply("This member does not have any proxy tags."); + else + { + var tags = string.Join("\n", target.ProxyTags.Select(t => $"`{t.ProxyString}`".SanitizeMentions())); + await ctx.Reply($"This member's proxy tags are:\n{tags}"); + } + } // Subcommand: "add" else if (ctx.Match("add", "append")) { @@ -100,8 +110,6 @@ namespace PluralKit.Bot // Subcommand: bare proxy tag given else { - if (!ctx.HasNext(skipFlags: false)) throw new PKSyntaxError("You must pass an example proxy to set (eg. `[text]` or `J:text`)."); - var requestedTag = ParseProxyTags(ctx.RemainderOrNull(skipFlags: false)); if (requestedTag.IsEmpty) throw Errors.EmptyProxyTags(target); diff --git a/PluralKit.Bot/Commands/SystemEdit.cs b/PluralKit.Bot/Commands/SystemEdit.cs index 235c60b6..ef0970b6 100644 --- a/PluralKit.Bot/Commands/SystemEdit.cs +++ b/PluralKit.Bot/Commands/SystemEdit.cs @@ -27,12 +27,28 @@ namespace PluralKit.Bot { ctx.CheckSystem(); - var newSystemName = ctx.RemainderOrNull(); - if (newSystemName != null && newSystemName.Length > Limits.MaxSystemNameLength) throw Errors.SystemNameTooLongError(newSystemName.Length); + if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) + { + ctx.System.Name = null; + await _data.SaveSystem(ctx.System); + await ctx.Reply($"{Emojis.Success} System name cleared."); + return; + } + var newSystemName = ctx.RemainderOrNull(); + if (newSystemName == null) + { + if (ctx.System.Name != null) + await ctx.Reply($"Your system's name is currently **{ctx.System.Name.SanitizeMentions()}**. Type `pk;system name -clear` to clear it."); + else + await ctx.Reply("Your system currently does not have a name. Type `pk;system name ` to set one."); + return; + } + + if (newSystemName != null && newSystemName.Length > Limits.MaxSystemNameLength) throw Errors.SystemNameTooLongError(newSystemName.Length); ctx.System.Name = newSystemName; await _data.SaveSystem(ctx.System); - await ctx.Reply($"{Emojis.Success} System name {(newSystemName != null ? "changed" : "cleared")}."); + await ctx.Reply($"{Emojis.Success} System name changed."); } public async Task Description(Context ctx) { @@ -52,7 +68,7 @@ namespace PluralKit.Bot if (ctx.System.Description == null) await ctx.Reply("Your system does not have a description set. To set one, type `pk;s description `."); else if (ctx.MatchFlag("r", "raw")) - await ctx.Reply($"```\n{ctx.System.Description}\n```"); + await ctx.Reply($"```\n{ctx.System.Description.SanitizeMentions()}\n```"); else await ctx.Reply(embed: new EmbedBuilder() .WithTitle("System description") @@ -101,7 +117,14 @@ namespace PluralKit.Bot { ctx.CheckSystem(); - if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) + if (ctx.Match("clear") || ctx.MatchFlag("c", "clear")) + { + ctx.System.AvatarUrl = null; + await _data.SaveSystem(ctx.System); + await ctx.Reply($"{Emojis.Success} System avatar cleared."); + return; + } + else if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((ctx.System.AvatarUrl?.Trim() ?? "").Length > 0) { @@ -128,12 +151,6 @@ namespace PluralKit.Bot await ctx.Reply( $"{Emojis.Success} System avatar changed to {member.Username}'s avatar! {Emojis.Warn} Please note that if {member.Username} changes their avatar, the system's avatar will need to be re-set.", embed: embed); } - else if (ctx.Match("clear")) - { - ctx.System.AvatarUrl = null; - await _data.SaveSystem(ctx.System); - await ctx.Reply($"{Emojis.Success} System avatar cleared."); - } else { // They can't both be null - otherwise we would've hit the conditional at the very top @@ -194,7 +211,7 @@ namespace PluralKit.Bot { ctx.System.UiTz = "UTC"; await _data.SaveSystem(ctx.System); - await ctx.Reply($"{Emojis.Success} System time zone cleared."); + await ctx.Reply($"{Emojis.Success} System time zone cleared (set to UTC)."); return; }