Merge branch 'feature/granular-privacy'

This commit is contained in:
Ske
2020-06-20 16:25:08 +02:00
39 changed files with 692 additions and 164 deletions

View File

@@ -76,7 +76,7 @@ namespace PluralKit.Bot
ctx.CheckOwnMember(member);
await UpdateAutoproxy(ctx, AutoproxyMode.Member, member.Id);
await ctx.Reply($"{Emojis.Success} Autoproxy set to **{member.Name}** in this server.");
await ctx.Reply($"{Emojis.Success} Autoproxy set to **{member.NameFor(ctx)}** in this server.");
}
private async Task<DiscordEmbed> CreateAutoproxyStatusEmbed(Context ctx)
@@ -103,14 +103,14 @@ namespace PluralKit.Bot
{
if (relevantMember == null)
throw new ArgumentException("Attempted to print member autoproxy status, but the linked member ID wasn't found in the database. Should be handled appropriately.");
eb.WithDescription($"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.Name.EscapeMarkdown()}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`.");
eb.WithDescription($"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.NameFor(ctx).EscapeMarkdown()}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`.");
}
break;
}
// AutoproxyMember is never null if Mode is Member, this is just to make the compiler shut up
case AutoproxyMode.Member when relevantMember != null: {
eb.WithDescription($"Autoproxy is active for member **{relevantMember.Name}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`.");
eb.WithDescription($"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`.");
break;
}
case AutoproxyMode.Latch:

View File

@@ -24,8 +24,8 @@ namespace PluralKit.Bot
public static Command SystemFronter = new Command("system fronter", "system [system] fronter", "Shows a system's fronter(s)");
public static Command SystemFrontHistory = new Command("system fronthistory", "system [system] fronthistory", "Shows a system's front history");
public static Command SystemFrontPercent = new Command("system frontpercent", "system [system] frontpercent [timespan]", "Shows a system's front breakdown");
public static Command SystemPrivacy = new Command("system privacy", "system privacy <description|members|fronter|fronthistory> <public|private>", "Changes your system's privacy settings");
public static Command SystemPing = new Command("system ping", "system ping <enable|disable>", "Changes your system's ping preferences");
public static Command SystemPrivacy = new Command("system privacy", "system privacy <description|members|fronter|fronthistory|all> <public|private>", "Changes your system's privacy settings");
public static Command Autoproxy = new Command("autoproxy", "autoproxy [off|front|latch|member]", "Sets your system's autoproxy mode for this server");
public static Command MemberInfo = new Command("member", "member <member>", "Looks up information about a member");
public static Command MemberNew = new Command("member new", "member new <name>", "Creates a new member");
@@ -42,7 +42,7 @@ namespace PluralKit.Bot
public static Command MemberServerName = new Command("member servername", "member <member> servername [server name]", "Changes a member's display name in the current server");
public static Command MemberKeepProxy = new Command("member keepproxy", "member <member> keepproxy [on|off]", "Sets whether to include a member's proxy tags when proxying");
public static Command MemberRandom = new Command("random", "random", "Looks up a random member from your system");
public static Command MemberPrivacy = new Command("member privacy", "member <member> privacy [on|off]", "Sets whether a member is private or public");
public static Command MemberPrivacy = new Command("member privacy", "member <member> privacy <name|description|birthday|pronouns|metadata|visibility|all> <public|private>", "Changes a members's privacy settings");
public static Command Switch = new Command("switch", "switch <member> [member 2] [member 3...]", "Registers a switch");
public static Command SwitchOut = new Command("switch out", "switch out", "Registers a switch with no members");
public static Command SwitchMove = new Command("switch move", "switch move <date/time>", "Moves the latest switch in time");
@@ -72,7 +72,7 @@ namespace PluralKit.Bot
public static Command[] MemberCommands = {
MemberInfo, MemberNew, MemberRename, MemberDisplayName, MemberServerName, MemberDesc, MemberPronouns,
MemberColor, MemberBirthday, MemberProxy, MemberKeepProxy, MemberDelete, MemberAvatar, MemberServerAvatar,
MemberColor, MemberBirthday, MemberProxy, MemberKeepProxy, MemberDelete, MemberAvatar, MemberServerAvatar, MemberPrivacy,
MemberRandom
};
@@ -297,9 +297,9 @@ namespace PluralKit.Bot
await ctx.Execute<MemberEdit>(MemberKeepProxy, m => m.KeepProxy(ctx, target));
else if (ctx.Match("privacy"))
await ctx.Execute<MemberEdit>(MemberPrivacy, m => m.Privacy(ctx, target, null));
else if (ctx.Match("private", "hidden"))
else if (ctx.Match("private", "hidden", "hide"))
await ctx.Execute<MemberEdit>(MemberPrivacy, m => m.Privacy(ctx, target, PrivacyLevel.Private));
else if (ctx.Match("public", "shown"))
else if (ctx.Match("public", "shown", "show"))
await ctx.Execute<MemberEdit>(MemberPrivacy, m => m.Privacy(ctx, target, PrivacyLevel.Public));
else if (!ctx.HasNext()) // Bare command
await ctx.Execute<Member>(MemberInfo, m => m.ViewMember(ctx, target));

View File

@@ -28,7 +28,7 @@ namespace PluralKit.Bot
// Warn if there's already a member by this name
var existingMember = await _data.GetMemberByName(ctx.System, memberName);
if (existingMember != null) {
var msg = await ctx.Reply($"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.Name.SanitizeMentions()}\" (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?");
var msg = await ctx.Reply($"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx).SanitizeMentions()}\" (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?");
if (!await ctx.PromptYesNo(msg)) throw new PKError("Member creation cancelled.");
}
@@ -59,7 +59,7 @@ namespace PluralKit.Bot
//Maybe move this somewhere else in the file structure since it doesn't need to get created at every command
// TODO: don't buffer these, find something else to do ig
var members = await _data.GetSystemMembers(ctx.System).Where(m => m.MemberPrivacy == PrivacyLevel.Public).ToListAsync();
var members = await _data.GetSystemMembers(ctx.System).Where(m => m.MemberVisibility == PrivacyLevel.Public).ToListAsync();
if (members == null || !members.Any())
throw Errors.NoMembersError;
var randInt = randGen.Next(members.Count);

View File

@@ -47,7 +47,8 @@ namespace PluralKit.Bot
var cmd = location == AvatarLocation.Server ? "serveravatar" : "avatar";
var currentValue = location == AvatarLocation.Member ? target.AvatarUrl : guildData?.AvatarUrl;
if (string.IsNullOrEmpty(currentValue))
var canAccess = location != AvatarLocation.Member || target.AvatarPrivacy.CanAccess(ctx.LookupContextFor(target));
if (string.IsNullOrEmpty(currentValue) || !canAccess)
{
if (location == AvatarLocation.Member)
{
@@ -61,7 +62,7 @@ namespace PluralKit.Bot
}
var eb = new DiscordEmbedBuilder()
.WithTitle($"{target.Name.SanitizeMentions()}'s {field}")
.WithTitle($"{target.NameFor(ctx).SanitizeMentions()}'s {field}")
.WithImageUrl(currentValue);
if (target.System == ctx.System?.Id)
eb.WithDescription($"To clear, use `pk;member {target.Hid} {cmd} clear`.");

View File

@@ -1,5 +1,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System;
using Dapper;
@@ -33,7 +35,7 @@ namespace PluralKit.Bot
// Warn if there's already a member by this name
var existingMember = await _data.GetMemberByName(ctx.System, newName);
if (existingMember != null) {
var msg = await ctx.Reply($"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.Name.SanitizeMentions()}\" (`{existingMember.Hid}`). Do you want to rename this member to that name too?");
var msg = await ctx.Reply($"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx).SanitizeMentions()}\" (`{existingMember.Hid}`). Do you want to rename this member to that name too?");
if (!await ctx.PromptYesNo(msg)) throw new PKError("Member renaming cancelled.");
}
@@ -53,12 +55,6 @@ namespace PluralKit.Bot
}
}
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;
@@ -78,7 +74,8 @@ namespace PluralKit.Bot
}
else if (!ctx.HasNext())
{
CheckReadMemberPermission(ctx, target);
if (!target.DescriptionPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
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 <description>`.");
@@ -119,14 +116,15 @@ namespace PluralKit.Bot
}
else if (!ctx.HasNext())
{
CheckReadMemberPermission(ctx, target);
if (!target.PronounPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
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 <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()}**."
await ctx.Reply($"**{target.NameFor(ctx).SanitizeMentions()}**'s pronouns are **{target.Pronouns.SanitizeMentions()}**."
+ (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Hid} pronouns -clear`." : ""));
}
else
@@ -155,7 +153,8 @@ namespace PluralKit.Bot
}
else if (!ctx.HasNext())
{
CheckReadMemberPermission(ctx, target);
// if (!target.ColorPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
// throw Errors.LookupNotAllowed;
if (target.Color == null)
if (ctx.System?.Id == target.System)
@@ -199,7 +198,8 @@ namespace PluralKit.Bot
}
else if (!ctx.HasNext())
{
CheckReadMemberPermission(ctx, target);
if (!target.BirthdayPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
throw Errors.LookupNotAllowed;
if (target.Birthday == null)
await ctx.Reply("This member does not have a birthdate set."
@@ -223,6 +223,8 @@ namespace PluralKit.Bot
private async Task<DiscordEmbedBuilder> CreateMemberNameInfoEmbed(Context ctx, PKMember target)
{
var lcx = ctx.LookupContextFor(target);
MemberGuildSettings memberGuildConfig = null;
if (ctx.Guild != null)
memberGuildConfig = await _db.Execute(c => c.QueryOrInsertMemberGuildConfig(ctx.Guild.Id, target.Id));
@@ -231,14 +233,17 @@ namespace PluralKit.Bot
.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}**");
eb.AddField("Name", $"**{target.NameFor(ctx)}**");
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)*");
eb.AddField("Name", target.NameFor(ctx));
if (target.NamePrivacy.CanAccess(lcx))
{
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)
{
@@ -272,7 +277,7 @@ namespace PluralKit.Bot
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()}\".");
await PrintSuccess($"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.NameFor(ctx).SanitizeMentions()}\".");
}
else if (!ctx.HasNext())
{
@@ -309,7 +314,7 @@ namespace PluralKit.Bot
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()}).");
await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their member name \"{target.NameFor(ctx).SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()}).");
}
else if (!ctx.HasNext())
{
@@ -360,34 +365,122 @@ namespace PluralKit.Bot
await ctx.Reply($"{Emojis.Success} Member proxy tags will now not be included in the resulting message when proxying.");
}
private DiscordEmbed CreatePrivacyEmbed(Context ctx, PKMember member)
{
string PrivacyLevelString(PrivacyLevel level) => level switch
{
PrivacyLevel.Private => "**Private** (visible only when queried by you)",
PrivacyLevel.Public => "**Public** (visible to everyone)",
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
};
var eb = new DiscordEmbedBuilder()
.WithTitle($"Current privacy settings for {member.NameFor(ctx)}")
.AddField("Name (replaces name with display name if member has one)",PrivacyLevelString(member.NamePrivacy))
.AddField("Description", PrivacyLevelString(member.DescriptionPrivacy))
.AddField("Avatar", PrivacyLevelString(member.AvatarPrivacy))
.AddField("Birthday", PrivacyLevelString(member.BirthdayPrivacy))
.AddField("Pronouns", PrivacyLevelString(member.PronounPrivacy))
// .AddField("Color", PrivacyLevelString(target.ColorPrivacy))
.AddField("Meta (message count, last front, last message)", PrivacyLevelString(member.MetadataPrivacy))
.AddField("Visibility", PrivacyLevelString(member.MemberVisibility))
.WithDescription("To edit privacy settings, use the command:\n`pk;member <member> privacy <subject> <level>`\n\n- `subject` is one of `name`, `description`, `avatar`, `birthday`, `pronouns`, `created`, `messages`, `visibility`, or `all`\n- `level` is either `public` or `private`.");
return eb.Build();
}
public async Task Privacy(Context ctx, PKMember target, PrivacyLevel? newValueFromCommand)
{
if (ctx.System == null) throw Errors.NoSystemError;
if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError;
PrivacyLevel newValue;
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 <name> private" == always private, "pk;m <name> public == always public"), use that instead of parsing
else if (newValueFromCommand != null) newValue = newValueFromCommand.Value;
else
// Display privacy settings
if (!ctx.HasNext() && newValueFromCommand == null)
{
if (target.MemberPrivacy == PrivacyLevel.Public)
await ctx.Reply("This member's privacy is currently set to **public**. This member will show up in member lists and will return all information when queried by other accounts.");
else
await ctx.Reply("This member's privacy is currently set to **private**. This member will not show up in member lists and will return limited information when queried by other accounts.");
await ctx.Reply(embed: CreatePrivacyEmbed(ctx, target));
return;
}
// Get guild settings (mostly for warnings and such)
MemberGuildSettings guildSettings = null;
if (ctx.Guild != null)
guildSettings = await _db.Execute(c => c.QueryOrInsertMemberGuildConfig(ctx.Guild.Id, target.Id));
target.MemberPrivacy = newValue;
await _data.SaveMember(target);
// Set Privacy Settings
PrivacyLevel PopPrivacyLevel(string subjectName)
{
if (ctx.Match("public", "show", "shown", "visible"))
return PrivacyLevel.Public;
if (newValue == PrivacyLevel.Private)
await ctx.Reply($"{Emojis.Success} Member privacy set to **private**. This member will no longer show up in member lists and will return limited information when queried by other accounts.");
if (ctx.Match("private", "hide", "hidden"))
return PrivacyLevel.Private;
if (!ctx.HasNext())
throw new PKSyntaxError($"You must pass a privacy level for `{subjectName}` (`public` or `private`)");
throw new PKSyntaxError($"Invalid privacy level `{ctx.PopArgument().SanitizeMentions()}` (must be `public` or `private`).");
}
// See if we have a subject given
PrivacyLevel newLevel;
if (PrivacyUtils.TryParseMemberPrivacy(ctx.PeekArgument(), out var subject))
{
// We peeked before, pop it now
ctx.PopArgument();
// Read the privacy level from args
newLevel = PopPrivacyLevel(subject.Name());
// Set the level on the given subject
target.SetPrivacy(subject, newLevel);
await _data.SaveMember(target);
// Print response
var explanation = (subject, newLevel) switch
{
(MemberPrivacySubject.Name, PrivacyLevel.Private) => "This member's name is now hidden from other systems, and will be replaced by the member's display name.",
(MemberPrivacySubject.Description, PrivacyLevel.Private) => "This member's description is now hidden from other systems.",
(MemberPrivacySubject.Avatar, PrivacyLevel.Private) => "This member's avatar is now hidden from other systems.",
(MemberPrivacySubject.Birthday, PrivacyLevel.Private) => "This member's birthday is now hidden from other systems.",
(MemberPrivacySubject.Pronouns, PrivacyLevel.Private) => "This member's pronouns are now hidden from other systems.",
(MemberPrivacySubject.Metadata, PrivacyLevel.Private) => "This member's metadata (eg. created timestamp, message count, etc) is now hidden from other systems.",
(MemberPrivacySubject.Visibility, PrivacyLevel.Private) => "This member is now hidden from member lists.",
(MemberPrivacySubject.Name, PrivacyLevel.Public) => "This member's name is no longer hidden from other systems.",
(MemberPrivacySubject.Description, PrivacyLevel.Public) => "This member's description is no longer hidden from other systems.",
(MemberPrivacySubject.Avatar, PrivacyLevel.Public) => "This member's avatar is no longer hidden from other systems.",
(MemberPrivacySubject.Birthday, PrivacyLevel.Public) => "This member's birthday is no longer hidden from other systems.",
(MemberPrivacySubject.Pronouns, PrivacyLevel.Public) => "This member's pronouns are no longer hidden other systems.",
(MemberPrivacySubject.Metadata, PrivacyLevel.Public) => "This member's metadata (eg. created timestamp, message count, etc) is no longer hidden from other systems.",
(MemberPrivacySubject.Visibility, PrivacyLevel.Public) => "This member is no longer hidden from member lists.",
_ => throw new InvalidOperationException($"Invalid subject/level tuple ({subject}, {newLevel})")
};
await ctx.Reply($"{Emojis.Success} {target.NameFor(ctx).SanitizeMentions()}'s {subject.Name()} has been set to **{newLevel.Name()}**. {explanation}");
}
else if (ctx.Match("all") || newValueFromCommand != null)
{
newLevel = newValueFromCommand ?? PopPrivacyLevel("all");
target.SetAllPrivacy(newLevel);
await _data.SaveMember(target);
if(newLevel == PrivacyLevel.Private)
await ctx.Reply($"All {target.NameFor(ctx).SanitizeMentions()}'s privacy settings have been set to **{newLevel.Name()}**. Other accounts will now see nothing on the member card.");
else
await ctx.Reply($"All {target.NameFor(ctx).SanitizeMentions()}'s privacy settings have been set to **{newLevel.Name()}**. Other accounts will now see everything on the member card.");
}
else
await ctx.Reply($"{Emojis.Success} Member privacy set to **public**. This member will now show up in member lists and will return all information when queried by other accounts.");
{
var subjectList = "`name`, `description`, `avatar`, `birthday`, `pronouns`, `metadata`, `visibility`, or `all`";
throw new PKSyntaxError($"Invalid privacy subject `{ctx.PopArgument().SanitizeMentions()}` (must be {subjectList}).");
}
// Name privacy only works given a display name
if (subject == MemberPrivacySubject.Name && newLevel == PrivacyLevel.Private && target.DisplayName == null)
await ctx.Reply($"{Emojis.Warn} This member does not have a display name set, and name privacy **will not take effect**.");
// Avatar privacy doesn't apply when proxying if no server avatar is set
if (subject == MemberPrivacySubject.Avatar && newLevel == 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`");
}
public async Task Delete(Context ctx, PKMember target)
@@ -395,7 +488,7 @@ namespace PluralKit.Bot
if (ctx.System == null) throw Errors.NoSystemError;
if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError;
await ctx.Reply($"{Emojis.Warn} Are you sure you want to delete \"{target.Name.SanitizeMentions()}\"? If so, reply to this message with the member's ID (`{target.Hid}`). __***This cannot be undone!***__");
await ctx.Reply($"{Emojis.Warn} Are you sure you want to delete \"{target.NameFor(ctx).SanitizeMentions()}\"? If so, reply to this message with the member's ID (`{target.Hid}`). __***This cannot be undone!***__");
if (!await ctx.ConfirmWithReply(target.Hid)) throw Errors.MemberDeleteCancelled;
await _data.DeleteMember(target);
await ctx.Reply($"{Emojis.Success} Member deleted.");

View File

@@ -36,7 +36,7 @@ namespace PluralKit.Bot
if (conflicts.Count <= 0) return true;
var conflictList = conflicts.Select(m => $"- **{m.Name}**");
var conflictList = conflicts.Select(m => $"- **{m.NameFor(ctx)}**");
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);

View File

@@ -62,7 +62,7 @@ namespace PluralKit.Bot
var lastSwitchMembers = _data.GetSwitchMembers(lastSwitch);
// Make sure the requested switch isn't identical to the last one
if (await lastSwitchMembers.Select(m => m.Id).SequenceEqualAsync(members.Select(m => m.Id).ToAsyncEnumerable()))
throw Errors.SameSwitch(members);
throw Errors.SameSwitch(members, ctx.LookupContextFor(ctx.System));
}
await _data.AddSwitch(ctx.System.Id, members);
@@ -70,7 +70,7 @@ namespace PluralKit.Bot
if (members.Count == 0)
await ctx.Reply($"{Emojis.Success} Switch-out registered.");
else
await ctx.Reply($"{Emojis.Success} Switch registered. Current fronter is now {string.Join(", ", members.Select(m => m.Name)).SanitizeMentions()}.");
await ctx.Reply($"{Emojis.Success} Switch registered. Current fronter is now {string.Join(", ", members.Select(m => m.NameFor(ctx))).SanitizeMentions()}.");
}
public async Task SwitchMove(Context ctx)
@@ -102,7 +102,7 @@ namespace PluralKit.Bot
// Now we can actually do the move, yay!
// But, we do a prompt to confirm.
var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]);
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.Name).ToListAsync());
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchTimeStr = DateTimeFormats.ZonedDateTimeFormat.Format(lastTwoSwitches[0].Timestamp.InZone(ctx.System.Zone));
var lastSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp);
var newSwitchTimeStr = DateTimeFormats.ZonedDateTimeFormat.Format(time);
@@ -137,7 +137,7 @@ namespace PluralKit.Bot
if (lastTwoSwitches.Count == 0) throw Errors.NoRegisteredSwitches;
var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]);
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.Name).ToListAsync());
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp);
DiscordMessage msg;
@@ -149,7 +149,7 @@ namespace PluralKit.Bot
else
{
var secondSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[1]);
var secondSwitchMemberStr = string.Join(", ", await secondSwitchMembers.Select(m => m.Name).ToListAsync());
var secondSwitchMemberStr = string.Join(", ", await secondSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var secondSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[1].Timestamp);
msg = await ctx.Reply(
$"{Emojis.Warn} This will delete the latest switch ({lastSwitchMemberStr.SanitizeMentions()}, {lastSwitchDeltaStr} ago). The next latest switch is {secondSwitchMemberStr.SanitizeMentions()} ({secondSwitchDeltaStr} ago). Is this okay?");

View File

@@ -262,7 +262,7 @@ namespace PluralKit.Bot
.AddField("Member list", PrivacyLevelString(ctx.System.MemberListPrivacy))
.AddField("Current fronter(s)", PrivacyLevelString(ctx.System.FrontPrivacy))
.AddField("Front/switch history", PrivacyLevelString(ctx.System.FrontHistoryPrivacy))
.WithDescription("To edit privacy settings, use the command:\n`pk;system privacy <subject> <level>`\n\n- `subject` is one of `description`, `list`, `front` or `fronthistory`\n- `level` is either `public` or `private`.");
.WithDescription("To edit privacy settings, use the command:\n`pk;system privacy <subject> <level>`\n\n- `subject` is one of `description`, `list`, `front`, `fronthistory`, or `all` \n- `level` is either `public` or `private`.");
await ctx.Reply(embed: eb.Build());
return;
}
@@ -289,7 +289,7 @@ namespace PluralKit.Bot
}
string levelStr, levelExplanation, subjectStr;
var subjectList = "`description`, `members`, `front` or `fronthistory`";
var subjectList = "`description`, `members`, `front`, `fronthistory`, or `all`";
if (ctx.Match("description", "desc", "text", "info"))
{
subjectStr = "description";
@@ -310,10 +310,27 @@ namespace PluralKit.Bot
subjectStr = "front history";
ctx.System.FrontHistoryPrivacy = PopPrivacyLevel("fronthistory", out levelStr, out levelExplanation);
}
else if (ctx.Match("all")){
subjectStr = "all";
PrivacyLevel level = PopPrivacyLevel("all", out levelStr, out levelExplanation);
ctx.System.DescriptionPrivacy = level;
ctx.System.MemberListPrivacy = level;
ctx.System.FrontPrivacy = level;
ctx.System.FrontHistoryPrivacy = level;
}
else
throw new PKSyntaxError($"Invalid privacy subject `{ctx.PopArgument().SanitizeMentions()}` (must be {subjectList}).");
await _data.SaveSystem(ctx.System);
if(subjectStr == "all"){
if(levelStr == "private")
await ctx.Reply($"All of your systems privacy settings have been set to **{levelStr}**. Other accounts will now see nothing on the member card.");
else
await ctx.Reply($"All of your systems privacy have been set to **{levelStr}**. Other accounts will now see everything on the member card.");
}
//Handle other subjects
else
await ctx.Reply($"System {subjectStr} privacy has been set to **{levelStr}**. Other accounts will now {levelExplanation} your system {subjectStr}.");
}

View File

@@ -39,7 +39,7 @@ namespace PluralKit.Bot
var sw = await _data.GetLatestSwitch(system.Id);
if (sw == null) throw Errors.NoRegisteredSwitches;
await ctx.Reply(embed: await _embeds.CreateFronterEmbed(sw, system.Zone));
await ctx.Reply(embed: await _embeds.CreateFronterEmbed(sw, system.Zone, ctx.LookupContextFor(system)));
}
public async Task SystemFrontHistory(Context ctx, PKSystem system)
@@ -68,7 +68,7 @@ namespace PluralKit.Bot
var sw = entry.ThisSwitch;
// Fetch member list and format
var members = await _data.GetSwitchMembers(sw).ToListAsync();
var membersStr = members.Any() ? string.Join(", ", members.Select(m => m.Name)) : "no fronter";
var membersStr = members.Any() ? string.Join(", ", members.Select(m => m.NameFor(ctx))) : "no fronter";
var switchSince = SystemClock.Instance.GetCurrentInstant() - sw.Timestamp;
@@ -113,7 +113,7 @@ namespace PluralKit.Bot
if (rangeStart.Value.ToInstant() > now) throw Errors.FrontPercentTimeInFuture;
var frontpercent = await _data.GetFrontBreakdown(system, rangeStart.Value.ToInstant(), now);
await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system.Zone));
await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system.Zone, ctx.LookupContextFor(system)));
}
}
}

View File

@@ -26,7 +26,7 @@ namespace PluralKit.Bot
var renderer = GetRendererFor(ctx);
var opts = GetOptions(ctx, target);
var members = (await _db.Execute(c => opts.Execute(c, target))).ToList();
var members = (await _db.Execute(c => opts.Execute(c, target, ctx.LookupContextFor(target)))).ToList();
await ctx.Paginate(
members.ToAsyncEnumerable(),
members.Count,
@@ -35,7 +35,7 @@ namespace PluralKit.Bot
(eb, ms) =>
{
eb.WithFooter($"{opts.CreateFilterString()}. {members.Count} results.");
renderer.RenderPage(eb, ctx.System?.Zone ?? DateTimeZone.Utc, ms);
renderer.RenderPage(eb, ctx.System?.Zone ?? DateTimeZone.Utc, ms, ctx.LookupContextFor(target));
return Task.CompletedTask;
});
}